mirror of
https://github.com/laurent22/joplin.git
synced 2026-10-25 18:55:41 +00:00
Compare commits
78
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8390d75293 | ||
|
|
75e62b86a1 | ||
|
|
840d3e9c60 | ||
|
|
a2b747c63d | ||
|
|
374ee3baf5 | ||
|
|
58574a2d0e | ||
|
|
96ac6887ce | ||
|
|
d4cc7ba85b | ||
|
|
6919be2908 | ||
|
|
cc6d7cebfa | ||
|
|
7ac224df52 | ||
|
|
a2861e476b | ||
|
|
76f44fe09f | ||
|
|
6f1e8fba2c | ||
|
|
fa65e29fb0 | ||
|
|
277fa0827e | ||
|
|
0907cf6e88 | ||
|
|
85296a8c09 | ||
|
|
b9a88c9c44 | ||
|
|
e4ffebbbc4 | ||
|
|
427e90fbf7 | ||
|
|
204be519c1 | ||
|
|
20bd001878 | ||
|
|
f016d6d6e5 | ||
|
|
96960a7407 | ||
|
|
7b233084fe | ||
|
|
74355d5320 | ||
|
|
fbfd5942ce | ||
|
|
eaca80d6c8 | ||
|
|
68f5494e3c | ||
|
|
cf462b7e03 | ||
|
|
73a7b683d1 | ||
|
|
1cc348d620 | ||
|
|
25ab1704c6 | ||
|
|
cb12c54eb7 | ||
|
|
3959a441da | ||
|
|
ab26949ad4 | ||
|
|
af4a93c9d1 | ||
|
|
e364ae9133 | ||
|
|
a66c40e999 | ||
|
|
016943f7eb | ||
|
|
2dee827bb8 | ||
|
|
27700028ee | ||
|
|
56ba7663fd | ||
|
|
64413405d7 | ||
|
|
f2469c9bf7 | ||
|
|
81140debdd | ||
|
|
274861fdeb | ||
|
|
8b76699949 | ||
|
|
1f301cc841 | ||
|
|
8bcf096840 | ||
|
|
d2a31f0ef2 | ||
|
|
02a8b22163 | ||
|
|
15d55d537e | ||
|
|
488ba01f60 | ||
|
|
d7efc4aaca | ||
|
|
19ed685a36 | ||
|
|
184e22937a | ||
|
|
5b81aa1fdb | ||
|
|
7bb3ba9f3e | ||
|
|
54ef47120b | ||
|
|
f83129cf2f | ||
|
|
9ec5030830 | ||
|
|
4758035b45 | ||
|
|
33895bbc4e | ||
|
|
3b36cac7f5 | ||
|
|
3acb1a2836 | ||
|
|
9348499b63 | ||
|
|
9c61cfd39b | ||
|
|
6445361bd9 | ||
|
|
7ad0c0b9bd | ||
|
|
a405a67367 | ||
|
|
fc6f29636a | ||
|
|
ac4e885748 | ||
|
|
c120c6095a | ||
|
|
af762f7839 | ||
|
|
31a44ac319 | ||
|
|
3ff8fa87a8 |
No files matched your search
+40
-2
@@ -69,8 +69,12 @@ ElectronClient/commands/focusElement.js
|
||||
ElectronClient/commands/startExternalEditing.js
|
||||
ElectronClient/commands/stopExternalEditing.js
|
||||
ElectronClient/global.d.js
|
||||
ElectronClient/gui/Button/Button.js
|
||||
ElectronClient/gui/ConfigScreen/ButtonBar.js
|
||||
ElectronClient/gui/ConfigScreen/ConfigScreen.js
|
||||
ElectronClient/gui/ConfigScreen/SideBar.js
|
||||
ElectronClient/gui/DropboxLoginScreen.js
|
||||
ElectronClient/gui/ErrorBoundary.js
|
||||
ElectronClient/gui/Header/commands/focusSearch.js
|
||||
ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js
|
||||
ElectronClient/gui/KeymapConfig/ShortcutRecorder.js
|
||||
ElectronClient/gui/KeymapConfig/styles/index.js
|
||||
@@ -81,8 +85,8 @@ ElectronClient/gui/MainScreen/commands/editAlarm.js
|
||||
ElectronClient/gui/MainScreen/commands/exportPdf.js
|
||||
ElectronClient/gui/MainScreen/commands/hideModalMessage.js
|
||||
ElectronClient/gui/MainScreen/commands/moveToFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newNote.js
|
||||
ElectronClient/gui/MainScreen/commands/newNotebook.js
|
||||
ElectronClient/gui/MainScreen/commands/newTodo.js
|
||||
ElectronClient/gui/MainScreen/commands/print.js
|
||||
ElectronClient/gui/MainScreen/commands/renameFolder.js
|
||||
@@ -94,9 +98,11 @@ ElectronClient/gui/MainScreen/commands/showModalMessage.js
|
||||
ElectronClient/gui/MainScreen/commands/showNoteContentProperties.js
|
||||
ElectronClient/gui/MainScreen/commands/showNoteProperties.js
|
||||
ElectronClient/gui/MainScreen/commands/showShareNoteDialog.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleEditors.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleNoteList.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleSidebar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
|
||||
ElectronClient/gui/MainScreen/MainScreen.js
|
||||
ElectronClient/gui/MultiNoteActions.js
|
||||
ElectronClient/gui/NoteContentPropertiesDialog.js
|
||||
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
|
||||
@@ -116,6 +122,7 @@ ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinMode.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useLineSorting.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
|
||||
ElectronClient/gui/NoteEditor/NoteEditor.js
|
||||
@@ -125,19 +132,40 @@ ElectronClient/gui/NoteEditor/utils/index.js
|
||||
ElectronClient/gui/NoteEditor/utils/resourceHandling.js
|
||||
ElectronClient/gui/NoteEditor/utils/types.js
|
||||
ElectronClient/gui/NoteEditor/utils/useDropHandler.js
|
||||
ElectronClient/gui/NoteEditor/utils/useFolder.js
|
||||
ElectronClient/gui/NoteEditor/utils/useFormNote.js
|
||||
ElectronClient/gui/NoteEditor/utils/useMarkupToHtml.js
|
||||
ElectronClient/gui/NoteEditor/utils/useMessageHandler.js
|
||||
ElectronClient/gui/NoteEditor/utils/useNoteSearchBar.js
|
||||
ElectronClient/gui/NoteEditor/utils/useNoteToolbarButtons.js
|
||||
ElectronClient/gui/NoteEditor/utils/useSearchMarkers.js
|
||||
ElectronClient/gui/NoteEditor/utils/useWindowCommandHandler.js
|
||||
ElectronClient/gui/NoteList/commands/focusElementNoteList.js
|
||||
ElectronClient/gui/NoteList/NoteList.js
|
||||
ElectronClient/gui/NoteListControls/commands/focusSearch.js
|
||||
ElectronClient/gui/NoteListControls/NoteListControls.js
|
||||
ElectronClient/gui/NoteListItem.js
|
||||
ElectronClient/gui/NoteToolbar/NoteToolbar.js
|
||||
ElectronClient/gui/OneDriveLoginScreen.js
|
||||
ElectronClient/gui/ResizableLayout/hooks/useLayoutItemSizes.js
|
||||
ElectronClient/gui/ResizableLayout/hooks/useWindowResizeEvent.js
|
||||
ElectronClient/gui/ResizableLayout/ResizableLayout.js
|
||||
ElectronClient/gui/ResourceScreen.js
|
||||
ElectronClient/gui/Root_UpgradeSyncTarget.js
|
||||
ElectronClient/gui/SearchBar/hooks/useSearch.js
|
||||
ElectronClient/gui/SearchBar/SearchBar.js
|
||||
ElectronClient/gui/SearchBar/styles/index.js
|
||||
ElectronClient/gui/ShareNoteDialog.js
|
||||
ElectronClient/gui/SideBar/commands/focusElementSideBar.js
|
||||
ElectronClient/gui/SideBar/SideBar.js
|
||||
ElectronClient/gui/SideBar/styles/index.js
|
||||
ElectronClient/gui/StatusScreen/StatusScreen.js
|
||||
ElectronClient/gui/style/StyledInput.js
|
||||
ElectronClient/gui/style/StyledTextInput.js
|
||||
ElectronClient/gui/ToggleEditorsButton/styles/index.js
|
||||
ElectronClient/gui/ToggleEditorsButton/ToggleEditorsButton.js
|
||||
ElectronClient/gui/ToolbarButton/styles/index.js
|
||||
ElectronClient/gui/ToolbarButton/ToolbarButton.js
|
||||
ReactNativeClient/lib/AsyncActionQueue.js
|
||||
ReactNativeClient/lib/checkPermissions.js
|
||||
ReactNativeClient/lib/commands/historyBackward.js
|
||||
@@ -176,6 +204,16 @@ ReactNativeClient/lib/services/synchronizer/utils/types.js
|
||||
ReactNativeClient/lib/services/UndoRedoService.js
|
||||
ReactNativeClient/lib/ShareExtension.js
|
||||
ReactNativeClient/lib/shareHandler.js
|
||||
ReactNativeClient/lib/theme.js
|
||||
ReactNativeClient/lib/themes/aritimDark.js
|
||||
ReactNativeClient/lib/themes/dark.js
|
||||
ReactNativeClient/lib/themes/dracula.js
|
||||
ReactNativeClient/lib/themes/light.js
|
||||
ReactNativeClient/lib/themes/nord.js
|
||||
ReactNativeClient/lib/themes/oledDark.js
|
||||
ReactNativeClient/lib/themes/solarizedDark.js
|
||||
ReactNativeClient/lib/themes/solarizedLight.js
|
||||
ReactNativeClient/lib/themes/type.js
|
||||
ReactNativeClient/lib/versionInfo.js
|
||||
ReactNativeClient/PluginAssetsLoader.js
|
||||
ReactNativeClient/setUpQuickActions.js
|
||||
|
||||
+40
-2
@@ -62,8 +62,12 @@ ElectronClient/commands/focusElement.js
|
||||
ElectronClient/commands/startExternalEditing.js
|
||||
ElectronClient/commands/stopExternalEditing.js
|
||||
ElectronClient/global.d.js
|
||||
ElectronClient/gui/Button/Button.js
|
||||
ElectronClient/gui/ConfigScreen/ButtonBar.js
|
||||
ElectronClient/gui/ConfigScreen/ConfigScreen.js
|
||||
ElectronClient/gui/ConfigScreen/SideBar.js
|
||||
ElectronClient/gui/DropboxLoginScreen.js
|
||||
ElectronClient/gui/ErrorBoundary.js
|
||||
ElectronClient/gui/Header/commands/focusSearch.js
|
||||
ElectronClient/gui/KeymapConfig/KeymapConfigScreen.js
|
||||
ElectronClient/gui/KeymapConfig/ShortcutRecorder.js
|
||||
ElectronClient/gui/KeymapConfig/styles/index.js
|
||||
@@ -74,8 +78,8 @@ ElectronClient/gui/MainScreen/commands/editAlarm.js
|
||||
ElectronClient/gui/MainScreen/commands/exportPdf.js
|
||||
ElectronClient/gui/MainScreen/commands/hideModalMessage.js
|
||||
ElectronClient/gui/MainScreen/commands/moveToFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newFolder.js
|
||||
ElectronClient/gui/MainScreen/commands/newNote.js
|
||||
ElectronClient/gui/MainScreen/commands/newNotebook.js
|
||||
ElectronClient/gui/MainScreen/commands/newTodo.js
|
||||
ElectronClient/gui/MainScreen/commands/print.js
|
||||
ElectronClient/gui/MainScreen/commands/renameFolder.js
|
||||
@@ -87,9 +91,11 @@ ElectronClient/gui/MainScreen/commands/showModalMessage.js
|
||||
ElectronClient/gui/MainScreen/commands/showNoteContentProperties.js
|
||||
ElectronClient/gui/MainScreen/commands/showNoteProperties.js
|
||||
ElectronClient/gui/MainScreen/commands/showShareNoteDialog.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleEditors.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleNoteList.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleSidebar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
|
||||
ElectronClient/gui/MainScreen/MainScreen.js
|
||||
ElectronClient/gui/MultiNoteActions.js
|
||||
ElectronClient/gui/NoteContentPropertiesDialog.js
|
||||
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
|
||||
@@ -109,6 +115,7 @@ ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinMode.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useLineSorting.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js
|
||||
ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/utils/useScroll.js
|
||||
ElectronClient/gui/NoteEditor/NoteEditor.js
|
||||
@@ -118,19 +125,40 @@ ElectronClient/gui/NoteEditor/utils/index.js
|
||||
ElectronClient/gui/NoteEditor/utils/resourceHandling.js
|
||||
ElectronClient/gui/NoteEditor/utils/types.js
|
||||
ElectronClient/gui/NoteEditor/utils/useDropHandler.js
|
||||
ElectronClient/gui/NoteEditor/utils/useFolder.js
|
||||
ElectronClient/gui/NoteEditor/utils/useFormNote.js
|
||||
ElectronClient/gui/NoteEditor/utils/useMarkupToHtml.js
|
||||
ElectronClient/gui/NoteEditor/utils/useMessageHandler.js
|
||||
ElectronClient/gui/NoteEditor/utils/useNoteSearchBar.js
|
||||
ElectronClient/gui/NoteEditor/utils/useNoteToolbarButtons.js
|
||||
ElectronClient/gui/NoteEditor/utils/useSearchMarkers.js
|
||||
ElectronClient/gui/NoteEditor/utils/useWindowCommandHandler.js
|
||||
ElectronClient/gui/NoteList/commands/focusElementNoteList.js
|
||||
ElectronClient/gui/NoteList/NoteList.js
|
||||
ElectronClient/gui/NoteListControls/commands/focusSearch.js
|
||||
ElectronClient/gui/NoteListControls/NoteListControls.js
|
||||
ElectronClient/gui/NoteListItem.js
|
||||
ElectronClient/gui/NoteToolbar/NoteToolbar.js
|
||||
ElectronClient/gui/OneDriveLoginScreen.js
|
||||
ElectronClient/gui/ResizableLayout/hooks/useLayoutItemSizes.js
|
||||
ElectronClient/gui/ResizableLayout/hooks/useWindowResizeEvent.js
|
||||
ElectronClient/gui/ResizableLayout/ResizableLayout.js
|
||||
ElectronClient/gui/ResourceScreen.js
|
||||
ElectronClient/gui/Root_UpgradeSyncTarget.js
|
||||
ElectronClient/gui/SearchBar/hooks/useSearch.js
|
||||
ElectronClient/gui/SearchBar/SearchBar.js
|
||||
ElectronClient/gui/SearchBar/styles/index.js
|
||||
ElectronClient/gui/ShareNoteDialog.js
|
||||
ElectronClient/gui/SideBar/commands/focusElementSideBar.js
|
||||
ElectronClient/gui/SideBar/SideBar.js
|
||||
ElectronClient/gui/SideBar/styles/index.js
|
||||
ElectronClient/gui/StatusScreen/StatusScreen.js
|
||||
ElectronClient/gui/style/StyledInput.js
|
||||
ElectronClient/gui/style/StyledTextInput.js
|
||||
ElectronClient/gui/ToggleEditorsButton/styles/index.js
|
||||
ElectronClient/gui/ToggleEditorsButton/ToggleEditorsButton.js
|
||||
ElectronClient/gui/ToolbarButton/styles/index.js
|
||||
ElectronClient/gui/ToolbarButton/ToolbarButton.js
|
||||
ReactNativeClient/lib/AsyncActionQueue.js
|
||||
ReactNativeClient/lib/checkPermissions.js
|
||||
ReactNativeClient/lib/commands/historyBackward.js
|
||||
@@ -169,6 +197,16 @@ ReactNativeClient/lib/services/synchronizer/utils/types.js
|
||||
ReactNativeClient/lib/services/UndoRedoService.js
|
||||
ReactNativeClient/lib/ShareExtension.js
|
||||
ReactNativeClient/lib/shareHandler.js
|
||||
ReactNativeClient/lib/theme.js
|
||||
ReactNativeClient/lib/themes/aritimDark.js
|
||||
ReactNativeClient/lib/themes/dark.js
|
||||
ReactNativeClient/lib/themes/dracula.js
|
||||
ReactNativeClient/lib/themes/light.js
|
||||
ReactNativeClient/lib/themes/nord.js
|
||||
ReactNativeClient/lib/themes/oledDark.js
|
||||
ReactNativeClient/lib/themes/solarizedDark.js
|
||||
ReactNativeClient/lib/themes/solarizedLight.js
|
||||
ReactNativeClient/lib/themes/type.js
|
||||
ReactNativeClient/lib/versionInfo.js
|
||||
ReactNativeClient/PluginAssetsLoader.js
|
||||
ReactNativeClient/setUpQuickActions.js
|
||||
|
||||
@@ -11,6 +11,7 @@ Note that all the applications share the same library, which, for historical rea
|
||||
- macOS, Linux: Install rsync - https://nodejs.org/en/
|
||||
- macOS: Install Cocoapods - `brew install cocoapods`
|
||||
- Windows: Install Windows Build Tools - `npm install -g windows-build-tools`
|
||||
- Linux: Install dependencies - `sudo apt install libnss3 libsecret-1-dev`
|
||||
|
||||
## Building
|
||||
|
||||
@@ -25,6 +26,8 @@ Then you can test the various applications:
|
||||
cd ElectronClient
|
||||
npm start
|
||||
|
||||
You can also run it under WSL 2. To do so, [follow these instructions](https://www.beekeeperstudio.io/blog/building-electron-windows-ubuntu-wsl2) to setup your environment.
|
||||
|
||||
## Testing the Terminal application
|
||||
|
||||
cd CliClient
|
||||
|
||||
@@ -26,10 +26,11 @@ describe('timeUtils', function() {
|
||||
startDate = new Date('3 Aug 2020 07:30:20');
|
||||
expect(time.goBackInTime(startDate, 1, 'day')).toBe(endDate.getTime().toString());
|
||||
|
||||
// Note: this test randomly fails - https://github.com/laurent22/joplin/issues/3722
|
||||
|
||||
startDate = new Date('11 Aug 2020');
|
||||
endDate = new Date('9 Aug 2020'); // week start;
|
||||
expect(time.goBackInTime(startDate, 0, 'week')).toBe(endDate.getTime().toString());
|
||||
// startDate = new Date('11 Aug 2020');
|
||||
// endDate = new Date('9 Aug 2020'); // week start;
|
||||
// expect(time.goBackInTime(startDate, 0, 'week')).toBe(endDate.getTime().toString());
|
||||
|
||||
startDate = new Date('02 Feb 2020');
|
||||
endDate = new Date('01 Jan 2020');
|
||||
|
||||
@@ -37,13 +37,13 @@ const resourceEditWatcherReducer = require('lib/services/ResourceEditWatcher/red
|
||||
const versionInfo = require('lib/versionInfo').default;
|
||||
|
||||
const commands = [
|
||||
require('./gui/Header/commands/focusSearch'),
|
||||
require('./gui/NoteListControls/commands/focusSearch'),
|
||||
require('./gui/MainScreen/commands/editAlarm'),
|
||||
require('./gui/MainScreen/commands/exportPdf'),
|
||||
require('./gui/MainScreen/commands/hideModalMessage'),
|
||||
require('./gui/MainScreen/commands/moveToFolder'),
|
||||
require('./gui/MainScreen/commands/newNote'),
|
||||
require('./gui/MainScreen/commands/newNotebook'),
|
||||
require('./gui/MainScreen/commands/newFolder'),
|
||||
require('./gui/MainScreen/commands/newTodo'),
|
||||
require('./gui/MainScreen/commands/print'),
|
||||
require('./gui/MainScreen/commands/renameFolder'),
|
||||
@@ -58,6 +58,7 @@ const commands = [
|
||||
require('./gui/MainScreen/commands/toggleNoteList'),
|
||||
require('./gui/MainScreen/commands/toggleSidebar'),
|
||||
require('./gui/MainScreen/commands/toggleVisiblePanes'),
|
||||
require('./gui/MainScreen/commands/toggleEditors'),
|
||||
require('./gui/NoteEditor/commands/focusElementNoteBody'),
|
||||
require('./gui/NoteEditor/commands/focusElementNoteTitle'),
|
||||
require('./gui/NoteEditor/commands/showLocalSearch'),
|
||||
@@ -286,10 +287,11 @@ class Application extends BaseApplication {
|
||||
}
|
||||
|
||||
newState = resourceEditWatcherReducer(newState, action);
|
||||
newState = super.reducer(newState, action);
|
||||
|
||||
CommandService.instance().scheduleMapStateToProps(newState);
|
||||
|
||||
return super.reducer(newState, action);
|
||||
return newState;
|
||||
}
|
||||
|
||||
toggleDevTools(visible) {
|
||||
@@ -519,7 +521,7 @@ class Application extends BaseApplication {
|
||||
|
||||
const newNoteItem = cmdService.commandToMenuItem('newNote');
|
||||
const newTodoItem = cmdService.commandToMenuItem('newTodo');
|
||||
const newNotebookItem = cmdService.commandToMenuItem('newNotebook');
|
||||
const newFolderItem = cmdService.commandToMenuItem('newFolder');
|
||||
const printItem = cmdService.commandToMenuItem('print');
|
||||
|
||||
toolsItemsFirst.push(syncStatusItem, {
|
||||
@@ -650,7 +652,7 @@ class Application extends BaseApplication {
|
||||
},
|
||||
shim.isMac() ? noItem : newNoteItem,
|
||||
shim.isMac() ? noItem : newTodoItem,
|
||||
shim.isMac() ? noItem : newNotebookItem, {
|
||||
shim.isMac() ? noItem : newFolderItem, {
|
||||
type: 'separator',
|
||||
visible: shim.isMac() ? false : true,
|
||||
}, {
|
||||
@@ -699,7 +701,7 @@ class Application extends BaseApplication {
|
||||
submenu: [
|
||||
newNoteItem,
|
||||
newTodoItem,
|
||||
newNotebookItem, {
|
||||
newFolderItem, {
|
||||
label: _('Close Window'),
|
||||
platforms: ['darwin'],
|
||||
accelerator: shim.isMac() && keymapService.getAccelerator('closeWindow'),
|
||||
|
||||
@@ -11,7 +11,7 @@ interface Props {
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'startExternalEditing',
|
||||
label: () => _('Edit in external editor'),
|
||||
iconName: 'fa-share-square',
|
||||
iconName: 'icon-share',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
import * as React from 'react';
|
||||
const styled = require('styled-components').default;
|
||||
const { space } = require('styled-system');
|
||||
|
||||
export enum ButtonLevel {
|
||||
Primary = 'primary',
|
||||
Secondary = 'secondary',
|
||||
Tertiary = 'tertiary',
|
||||
SideBarSecondary = 'sideBarSecondary',
|
||||
}
|
||||
|
||||
interface Props {
|
||||
title?: string,
|
||||
iconName?: string,
|
||||
level?: ButtonLevel,
|
||||
className?:string,
|
||||
onClick:Function,
|
||||
color?: string,
|
||||
iconAnimation?: string,
|
||||
tooltip?: string,
|
||||
disabled?: boolean,
|
||||
style?:any,
|
||||
}
|
||||
|
||||
const StyledTitle = styled.span`
|
||||
|
||||
`;
|
||||
|
||||
const StyledButtonBase = styled.button`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
height: ${(props:any) => `${props.theme.toolbarHeight}px`};
|
||||
min-height: ${(props:any) => `${props.theme.toolbarHeight}px`};
|
||||
max-height: ${(props:any) => `${props.theme.toolbarHeight}px`};
|
||||
width: ${(props:any) => props.iconOnly ? `${props.theme.toolbarHeight}px` : 'auto'};
|
||||
${(props:any) => props.iconOnly ? `min-width: ${props.theme.toolbarHeight}px;` : ''}
|
||||
${(props:any) => !props.iconOnly ? 'min-width: 100px;' : ''}
|
||||
${(props:any) => props.iconOnly ? `max-width: ${props.theme.toolbarHeight}px;` : ''}
|
||||
box-sizing: border-box;
|
||||
border-radius: 3px;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
font-size: ${(props:any) => props.theme.fontSize}px;
|
||||
padding: 0 ${(props:any) => props.iconOnly ? 4 : 8}px;
|
||||
justify-content: center;
|
||||
opacity: ${(props:any) => props.disabled ? 0.5 : 1};
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
const StyledIcon = styled(styled.span(space))`
|
||||
font-size: ${(props:any) => props.theme.toolbarIconSize}px;
|
||||
${(props:any) => props.animation ? `animation: ${props.animation}` : ''};
|
||||
`;
|
||||
|
||||
const StyledButtonPrimary = styled(StyledButtonBase)`
|
||||
border: none;
|
||||
background-color: ${(props:any) => props.theme.backgroundColor5};
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props:any) => props.theme.backgroundColorHover5};
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: ${(props:any) => props.theme.backgroundColorActive5};
|
||||
}
|
||||
|
||||
${StyledIcon} {
|
||||
color: ${(props:any) => props.theme.color5};
|
||||
}
|
||||
|
||||
${StyledTitle} {
|
||||
color: ${(props:any) => props.theme.color5};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledButtonSecondary = styled(StyledButtonBase)`
|
||||
border: 1px solid ${(props:any) => props.theme.borderColor4};
|
||||
background-color: ${(props:any) => props.theme.backgroundColor4};
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props:any) => props.theme.backgroundColorHover4};
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: ${(props:any) => props.theme.backgroundColorActive4};
|
||||
}
|
||||
|
||||
${StyledIcon} {
|
||||
color: ${(props:any) => props.theme.color4};
|
||||
}
|
||||
|
||||
${StyledTitle} {
|
||||
color: ${(props:any) => props.theme.color4};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledButtonTertiary = styled(StyledButtonBase)`
|
||||
border: 1px solid ${(props:any) => props.theme.color3};
|
||||
background-color: ${(props:any) => props.theme.backgroundColor3};
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props:any) => props.theme.backgroundColorHoverDim3};
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: ${(props:any) => props.theme.backgroundColorActive3};
|
||||
}
|
||||
|
||||
${StyledIcon} {
|
||||
color: ${(props:any) => props.theme.color};
|
||||
}
|
||||
|
||||
${StyledTitle} {
|
||||
color: ${(props:any) => props.theme.color};
|
||||
opacity: 0.9;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledButtonSideBarSecondary = styled(StyledButtonBase)`
|
||||
background: none;
|
||||
border-color: ${(props:any) => props.theme.color2};
|
||||
color: ${(props:any) => props.theme.color2};
|
||||
|
||||
&:hover {
|
||||
color: ${(props:any) => props.theme.colorHover2};
|
||||
border-color: ${(props:any) => props.theme.colorHover2};
|
||||
background: none;
|
||||
|
||||
${StyledTitle} {
|
||||
color: ${(props:any) => props.theme.colorHover2};
|
||||
}
|
||||
|
||||
${StyledIcon} {
|
||||
color: ${(props:any) => props.theme.colorHover2};
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: ${(props:any) => props.theme.colorActive2};
|
||||
border-color: ${(props:any) => props.theme.colorActive2};
|
||||
background: none;
|
||||
|
||||
${StyledTitle} {
|
||||
color: ${(props:any) => props.theme.colorActive2};
|
||||
}
|
||||
|
||||
${StyledIcon} {
|
||||
color: ${(props:any) => props.theme.colorActive2};
|
||||
}
|
||||
}
|
||||
|
||||
${StyledTitle} {
|
||||
color: ${(props:any) => props.theme.color2};
|
||||
}
|
||||
|
||||
${StyledIcon} {
|
||||
color: ${(props:any) => props.theme.color2};
|
||||
}
|
||||
`;
|
||||
|
||||
function buttonClass(level:ButtonLevel) {
|
||||
if (level === ButtonLevel.Primary) return StyledButtonPrimary;
|
||||
if (level === ButtonLevel.Tertiary) return StyledButtonTertiary;
|
||||
if (level === ButtonLevel.SideBarSecondary) return StyledButtonSideBarSecondary;
|
||||
return StyledButtonSecondary;
|
||||
}
|
||||
|
||||
export default function Button(props:Props) {
|
||||
const iconOnly = props.iconName && !props.title;
|
||||
|
||||
const StyledButton = buttonClass(props.level);
|
||||
|
||||
function renderIcon() {
|
||||
if (!props.iconName) return null;
|
||||
return <StyledIcon animation={props.iconAnimation} mr={iconOnly ? '0' : '6px'} color={props.color} className={props.iconName}/>;
|
||||
}
|
||||
|
||||
function renderTitle() {
|
||||
if (!props.title) return null;
|
||||
return <StyledTitle color={props.color}>{props.title}</StyledTitle>;
|
||||
}
|
||||
|
||||
function onClick() {
|
||||
if (props.disabled) return;
|
||||
props.onClick();
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledButton style={props.style} disabled={props.disabled} title={props.tooltip} className={props.className} iconOnly={iconOnly} onClick={onClick}>
|
||||
{renderIcon()}
|
||||
{renderTitle()}
|
||||
</StyledButton>
|
||||
);
|
||||
}
|
||||
@@ -40,10 +40,12 @@ class ClipperConfigScreenComponent extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const containerStyle = Object.assign({}, theme.containerStyle, {
|
||||
overflowY: 'scroll',
|
||||
padding: theme.configScreenPadding,
|
||||
backgroundColor: theme.backgroundColor3,
|
||||
});
|
||||
|
||||
const buttonStyle = Object.assign({}, theme.buttonStyle, { marginRight: 10 });
|
||||
@@ -106,8 +108,8 @@ class ClipperConfigScreenComponent extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
<div style={containerStyle}>
|
||||
<div style={{ padding: theme.margin }}>
|
||||
<p style={theme.textStyle}>{_('Joplin Web Clipper allows saving web pages and screenshots from your browser to Joplin.')}</p>
|
||||
<div>
|
||||
<p style={Object.assign({}, theme.textStyle, { marginTop: 0 })}>{_('Joplin Web Clipper allows saving web pages and screenshots from your browser to Joplin.')}</p>
|
||||
<p style={theme.textStyle}>{_('In order to use the web clipper, you need to do the following:')}</p>
|
||||
|
||||
<div style={stepBoxStyle}>
|
||||
@@ -120,8 +122,8 @@ class ClipperConfigScreenComponent extends React.Component {
|
||||
<p style={theme.h1Style}>{_('Step 2: Install the extension')}</p>
|
||||
<p style={theme.textStyle}>{_('Download and install the relevant extension for your browser:')}</p>
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
<ExtensionBadge theme={this.props.theme} type="firefox" url="https://addons.mozilla.org/en-US/firefox/addon/joplin-web-clipper/"/>
|
||||
<ExtensionBadge style={{ marginLeft: 10 }} theme={this.props.theme} type="chrome" url="https://chrome.google.com/webstore/detail/joplin-web-clipper/alofnhikmmkdbbbgpnglcpdollgjjfek"/>
|
||||
<ExtensionBadge themeId={this.props.themeId} type="firefox" url="https://addons.mozilla.org/en-US/firefox/addon/joplin-web-clipper/"/>
|
||||
<ExtensionBadge style={{ marginLeft: 10 }} themeId={this.props.themeId} type="chrome" url="https://chrome.google.com/webstore/detail/joplin-web-clipper/alofnhikmmkdbbbgpnglcpdollgjjfek"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -145,7 +147,7 @@ class ClipperConfigScreenComponent extends React.Component {
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
clipperServer: state.clipperServer,
|
||||
clipperServerAutoStart: state.settings['clipperServer.autoStart'],
|
||||
apiToken: state.settings['api.token'],
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
const React = require('react');
|
||||
const styleSelector = require('./style/ConfigMenuBar');
|
||||
const Setting = require('lib/models/Setting');
|
||||
|
||||
function ConfigMenuBarButton(props) {
|
||||
const style = styleSelector(null, props);
|
||||
|
||||
const iconStyle = props.selected ? style.buttonIconSelected : style.buttonIcon;
|
||||
const labelStyle = props.selected ? style.buttonLabelSelected : style.buttonLabel;
|
||||
|
||||
return (
|
||||
<button style={style.button} onClick={props.onClick}>
|
||||
<i style={iconStyle} className={props.iconName}></i>
|
||||
<span style={labelStyle}>{props.label}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function ConfigMenuBar(props) {
|
||||
const buttons = [];
|
||||
|
||||
const style = styleSelector(null, props);
|
||||
|
||||
for (const section of props.sections) {
|
||||
buttons.push(<ConfigMenuBarButton
|
||||
selected={props.selection === section.name}
|
||||
theme={props.theme}
|
||||
key={section.name}
|
||||
iconName={Setting.sectionNameToIcon(section.name)}
|
||||
label={Setting.sectionNameToLabel(section.name)}
|
||||
onClick={() => { props.onSelectionChange({ section: section }); }}
|
||||
/>);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={style.root} className="config-menu-bar">
|
||||
<div style={style.barButtons}>
|
||||
{buttons}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = ConfigMenuBar;
|
||||
@@ -0,0 +1,52 @@
|
||||
import * as React from 'react';
|
||||
import Button, { ButtonLevel } from '../Button/Button';
|
||||
const styled = require('styled-components').default;
|
||||
const { _ } = require('lib/locale.js');
|
||||
|
||||
interface Props {
|
||||
backButtonTitle?: string,
|
||||
hasChanges?: boolean,
|
||||
onCancelClick: Function,
|
||||
onSaveClick?: Function,
|
||||
onApplyClick?: Function,
|
||||
}
|
||||
|
||||
export const StyledRoot = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
background-color: ${(props:any) => props.theme.backgroundColor3};
|
||||
padding-left: ${(props:any) => props.theme.configScreenPadding}px;
|
||||
border-top-width: 1px;
|
||||
border-top-style: solid;
|
||||
border-top-color: ${(props:any) => props.theme.dividerColor};
|
||||
`;
|
||||
|
||||
export default function ButtonBar(props:Props) {
|
||||
function renderOkButton() {
|
||||
if (!props.onSaveClick) return null;
|
||||
return <Button style={{ marginRight: 10 }} level={ButtonLevel.Primary} disabled={!props.hasChanges} onClick={props.onSaveClick} title={_('OK')}/>;
|
||||
}
|
||||
|
||||
function renderApplyButton() {
|
||||
if (!props.onApplyClick) return null;
|
||||
return <Button level={ButtonLevel.Primary} disabled={!props.hasChanges} onClick={props.onApplyClick} title={_('Apply')}/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledRoot>
|
||||
<Button
|
||||
onClick={props.onCancelClick}
|
||||
level={ButtonLevel.Secondary}
|
||||
iconName="fa fa-chevron-left"
|
||||
title={props.backButtonTitle ? props.backButtonTitle : _('Back')}
|
||||
/>
|
||||
{ (props.onApplyClick || props.onSaveClick) && (
|
||||
<div style={{ display: 'flex', flexDirection: 'row', marginLeft: 30 }}>
|
||||
{renderOkButton()}
|
||||
{renderApplyButton()}
|
||||
</div>
|
||||
)}
|
||||
</StyledRoot>
|
||||
);
|
||||
}
|
||||
+192
-175
@@ -1,48 +1,63 @@
|
||||
const React = require('react');
|
||||
import * as React from 'react';
|
||||
import SideBar from './SideBar';
|
||||
import ButtonBar from './ButtonBar';
|
||||
import Button, { ButtonLevel } from '../Button/Button';
|
||||
const { connect } = require('react-redux');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const pathUtils = require('lib/path-utils.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry');
|
||||
const shared = require('lib/components/shared/config-shared.js');
|
||||
const ConfigMenuBar = require('./ConfigMenuBar.min.js');
|
||||
const { EncryptionConfigScreen } = require('./EncryptionConfigScreen.min');
|
||||
const { ClipperConfigScreen } = require('./ClipperConfigScreen.min');
|
||||
const { KeymapConfigScreen } = require('./KeymapConfig/KeymapConfigScreen');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const { EncryptionConfigScreen } = require('../EncryptionConfigScreen.min');
|
||||
const { ClipperConfigScreen } = require('../ClipperConfigScreen.min');
|
||||
const { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen');
|
||||
|
||||
class ConfigScreenComponent extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
class ConfigScreenComponent extends React.Component<any, any> {
|
||||
|
||||
rowStyle_:any = null;
|
||||
|
||||
constructor(props:any) {
|
||||
super(props);
|
||||
|
||||
shared.init(this);
|
||||
|
||||
this.state.selectedSectionName = 'general';
|
||||
this.state.screenName = '';
|
||||
|
||||
this.checkSyncConfig_ = async () => {
|
||||
await shared.checkSyncConfig(this, this.state.settings);
|
||||
};
|
||||
|
||||
this.checkNextcloudAppButton_click = async () => {
|
||||
this.setState({ showNextcloudAppLog: true });
|
||||
await shared.checkNextcloudApp(this, this.state.settings);
|
||||
};
|
||||
|
||||
this.showLogButton_click = () => {
|
||||
this.setState({ showNextcloudAppLog: true });
|
||||
};
|
||||
|
||||
this.nextcloudAppHelpLink_click = () => {
|
||||
bridge().openExternal('https://joplinapp.org/nextcloud_app');
|
||||
this.state = {
|
||||
selectedSectionName: 'general',
|
||||
screenName: '',
|
||||
changedSettingKeys: [],
|
||||
};
|
||||
|
||||
this.rowStyle_ = {
|
||||
marginBottom: 10,
|
||||
};
|
||||
|
||||
this.configMenuBar_selectionChange = this.configMenuBar_selectionChange.bind(this);
|
||||
this.sideBar_selectionChange = this.sideBar_selectionChange.bind(this);
|
||||
this.checkSyncConfig_ = this.checkSyncConfig_.bind(this);
|
||||
this.checkNextcloudAppButton_click = this.checkNextcloudAppButton_click.bind(this);
|
||||
this.showLogButton_click = this.showLogButton_click.bind(this);
|
||||
this.nextcloudAppHelpLink_click = this.nextcloudAppHelpLink_click.bind(this);
|
||||
this.onCancelClick = this.onCancelClick.bind(this);
|
||||
this.onSaveClick = this.onSaveClick.bind(this);
|
||||
this.onApplyClick = this.onApplyClick.bind(this);
|
||||
}
|
||||
|
||||
async checkSyncConfig_() {
|
||||
await shared.checkSyncConfig(this, this.state.settings);
|
||||
}
|
||||
|
||||
async checkNextcloudAppButton_click() {
|
||||
this.setState({ showNextcloudAppLog: true });
|
||||
await shared.checkNextcloudApp(this, this.state.settings);
|
||||
}
|
||||
|
||||
showLogButton_click() {
|
||||
this.setState({ showNextcloudAppLog: true });
|
||||
}
|
||||
|
||||
nextcloudAppHelpLink_click() {
|
||||
bridge().openExternal('https://joplinapp.org/nextcloud_app');
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
@@ -57,7 +72,7 @@ class ConfigScreenComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
sectionByName(name) {
|
||||
sectionByName(name:string) {
|
||||
const sections = shared.settingsSections({ device: 'desktop', settings: this.state.settings });
|
||||
for (const section of sections) {
|
||||
if (section.name === name) return section;
|
||||
@@ -66,15 +81,15 @@ class ConfigScreenComponent extends React.Component {
|
||||
throw new Error(`Invalid section name: ${name}`);
|
||||
}
|
||||
|
||||
screenFromName(screenName) {
|
||||
if (screenName === 'encryption') return <EncryptionConfigScreen theme={this.props.theme}/>;
|
||||
if (screenName === 'server') return <ClipperConfigScreen theme={this.props.theme}/>;
|
||||
if (screenName === 'keymap') return <KeymapConfigScreen themeId={this.props.theme}/>;
|
||||
screenFromName(screenName:string) {
|
||||
if (screenName === 'encryption') return <EncryptionConfigScreen themeId={this.props.themeId}/>;
|
||||
if (screenName === 'server') return <ClipperConfigScreen themeId={this.props.themeId}/>;
|
||||
if (screenName === 'keymap') return <KeymapConfigScreen themeId={this.props.themeId}/>;
|
||||
|
||||
throw new Error(`Invalid screen name: ${screenName}`);
|
||||
}
|
||||
|
||||
switchSection(name) {
|
||||
switchSection(name:string) {
|
||||
const section = this.sectionByName(name);
|
||||
let screenName = '';
|
||||
if (section.isScreen) {
|
||||
@@ -89,11 +104,11 @@ class ConfigScreenComponent extends React.Component {
|
||||
this.setState({ selectedSectionName: section.name, screenName: screenName });
|
||||
}
|
||||
|
||||
configMenuBar_selectionChange(event) {
|
||||
sideBar_selectionChange(event:any) {
|
||||
this.switchSection(event.section.name);
|
||||
}
|
||||
|
||||
keyValueToArray(kv) {
|
||||
keyValueToArray(kv:any) {
|
||||
const output = [];
|
||||
for (const k in kv) {
|
||||
if (!kv.hasOwnProperty(k)) continue;
|
||||
@@ -106,11 +121,11 @@ class ConfigScreenComponent extends React.Component {
|
||||
return output;
|
||||
}
|
||||
|
||||
renderSectionDescription(section) {
|
||||
renderSectionDescription(section:any) {
|
||||
const description = Setting.sectionDescription(section.name);
|
||||
if (!description) return null;
|
||||
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
return (
|
||||
<div style={Object.assign({}, theme.textStyle, { marginBottom: 15 })}>
|
||||
{description}
|
||||
@@ -118,10 +133,10 @@ class ConfigScreenComponent extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
sectionToComponent(key, section, settings, selected) {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
sectionToComponent(key:string, section:any, settings:any, selected:boolean) {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const createSettingComponents = (advanced) => {
|
||||
const createSettingComponents = (advanced:boolean) => {
|
||||
const output = [];
|
||||
for (let i = 0; i < section.metadatas.length; i++) {
|
||||
const md = section.metadatas[i];
|
||||
@@ -135,9 +150,10 @@ class ConfigScreenComponent extends React.Component {
|
||||
const settingComps = createSettingComponents(false);
|
||||
const advancedSettingComps = createSettingComponents(true);
|
||||
|
||||
const sectionStyle = {
|
||||
const sectionStyle:any = {
|
||||
marginTop: 20,
|
||||
marginBottom: 20,
|
||||
maxWidth: 640,
|
||||
};
|
||||
|
||||
if (!selected) sectionStyle.display = 'none';
|
||||
@@ -161,9 +177,12 @@ class ConfigScreenComponent extends React.Component {
|
||||
|
||||
settingComps.push(
|
||||
<div key="check_sync_config_button" style={this.rowStyle_}>
|
||||
<button disabled={this.state.checkSyncConfigResult === 'checking'} style={theme.buttonStyle} onClick={this.checkSyncConfig_}>
|
||||
{_('Check synchronisation configuration')}
|
||||
</button>
|
||||
<Button
|
||||
title={_('Check synchronisation configuration')}
|
||||
level={ButtonLevel.Secondary}
|
||||
disabled={this.state.checkSyncConfigResult === 'checking'}
|
||||
onClick={this.checkSyncConfig_}
|
||||
/>
|
||||
{statusComp}
|
||||
</div>
|
||||
);
|
||||
@@ -204,9 +223,7 @@ class ConfigScreenComponent extends React.Component {
|
||||
|
||||
{showLogButton}
|
||||
|
||||
<button disabled={this.state.checkNextcloudAppResult === 'checking'} style={theme.buttonStyle} onClick={this.checkNextcloudAppButton_click}>
|
||||
{_('Check Status')}
|
||||
</button>
|
||||
<Button level={ButtonLevel.Secondary} style={{ display: 'inline-block' }} title={_('Check Status')} disabled={this.state.checkNextcloudAppResult === 'checking'} onClick={this.checkNextcloudAppButton_click}/>
|
||||
|
||||
<a style={theme.urlStyle} href="#" onClick={this.nextcloudAppHelpLink_click}>[{_('Help')}]</a>
|
||||
{statusComp}
|
||||
@@ -220,8 +237,17 @@ class ConfigScreenComponent extends React.Component {
|
||||
|
||||
if (advancedSettingComps.length) {
|
||||
const iconName = this.state.showAdvancedSettings ? 'fa fa-angle-down' : 'fa fa-angle-right';
|
||||
const advancedSettingsButtonStyle = Object.assign({}, theme.buttonStyle, { marginBottom: 10 });
|
||||
advancedSettingsButton = <button onClick={() => shared.advancedSettingsButton_click(this)} style={advancedSettingsButtonStyle}><i style={{ fontSize: 14 }} className={iconName}></i> {_('Show Advanced Settings')}</button>;
|
||||
// const advancedSettingsButtonStyle = Object.assign({}, theme.buttonStyle, { marginBottom: 10 });
|
||||
advancedSettingsButton = (
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<Button
|
||||
level={ButtonLevel.Secondary}
|
||||
onClick={() => shared.advancedSettingsButton_click(this)}
|
||||
iconName={iconName}
|
||||
title={_('Show Advanced Settings')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
advancedSettingsSectionStyle.display = this.state.showAdvancedSettings ? 'block' : 'none';
|
||||
}
|
||||
|
||||
@@ -235,35 +261,39 @@ class ConfigScreenComponent extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
settingToComponent(key, value) {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
settingToComponent(key:string, value:any) {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const output = null;
|
||||
const output:any = null;
|
||||
|
||||
const rowStyle = this.rowStyle_;
|
||||
const rowStyle = {
|
||||
marginBottom: theme.mainPadding,
|
||||
};
|
||||
|
||||
const labelStyle = Object.assign({}, theme.textStyle, {
|
||||
display: 'inline-block',
|
||||
marginRight: 10,
|
||||
display: 'block',
|
||||
color: theme.color,
|
||||
fontSize: theme.fontSize * 1.083333,
|
||||
fontWeight: 500,
|
||||
marginBottom: theme.mainPadding / 4,
|
||||
});
|
||||
|
||||
const subLabel = Object.assign({}, labelStyle, {
|
||||
display: 'block',
|
||||
opacity: 0.7,
|
||||
marginBottom: Math.round(rowStyle.marginBottom * 0.7),
|
||||
});
|
||||
|
||||
const invisibleLabel = Object.assign({}, labelStyle, {
|
||||
opacity: 0,
|
||||
marginBottom: labelStyle.marginBottom,
|
||||
});
|
||||
|
||||
const checkboxLabelStyle = Object.assign({}, labelStyle, {
|
||||
marginLeft: 8,
|
||||
display: 'inline',
|
||||
backgroundColor: 'transparent',
|
||||
});
|
||||
|
||||
const controlStyle = {
|
||||
display: 'inline-block',
|
||||
color: theme.color,
|
||||
fontFamily: theme.fontFamily,
|
||||
backgroundColor: theme.backgroundColor,
|
||||
};
|
||||
|
||||
@@ -275,13 +305,19 @@ class ConfigScreenComponent extends React.Component {
|
||||
});
|
||||
|
||||
const textInputBaseStyle = Object.assign({}, controlStyle, {
|
||||
fontFamily: theme.fontFamily,
|
||||
border: '1px solid',
|
||||
padding: '4px 6px',
|
||||
borderColor: theme.dividerColor,
|
||||
borderRadius: 4,
|
||||
boxSizing: 'border-box',
|
||||
borderColor: theme.borderColor4,
|
||||
borderRadius: 3,
|
||||
paddingLeft: 6,
|
||||
paddingRight: 6,
|
||||
paddingTop: 4,
|
||||
paddingBottom: 4,
|
||||
});
|
||||
|
||||
const updateSettingValue = (key, value) => {
|
||||
const updateSettingValue = (key:string, value:any) => {
|
||||
// console.info(key + ' = ' + value);
|
||||
return shared.updateSettingValue(this, key, value);
|
||||
};
|
||||
@@ -306,7 +342,14 @@ class ConfigScreenComponent extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
const selectStyle = Object.assign({}, controlStyle, { height: 22, borderColor: theme.dividerColor });
|
||||
const selectStyle = Object.assign({}, controlStyle, {
|
||||
paddingLeft: 6,
|
||||
paddingRight: 6,
|
||||
paddingTop: 4,
|
||||
paddingBottom: 4,
|
||||
borderColor: theme.borderColor4,
|
||||
borderRadius: 3,
|
||||
});
|
||||
|
||||
return (
|
||||
<div key={key} style={rowStyle}>
|
||||
@@ -316,7 +359,7 @@ class ConfigScreenComponent extends React.Component {
|
||||
<select
|
||||
value={value}
|
||||
style={selectStyle}
|
||||
onChange={event => {
|
||||
onChange={(event:any) => {
|
||||
updateSettingValue(key, event.target.value);
|
||||
}}
|
||||
>
|
||||
@@ -330,35 +373,38 @@ class ConfigScreenComponent extends React.Component {
|
||||
updateSettingValue(key, !value);
|
||||
};
|
||||
|
||||
const checkboxSize = theme.fontSize * 1.1666666666666;
|
||||
|
||||
// Hack: The {key+value.toString()} is needed as otherwise the checkbox doesn't update when the state changes.
|
||||
// There's probably a better way to do this but can't figure it out.
|
||||
|
||||
return (
|
||||
<div key={key + value.toString()} style={rowStyle}>
|
||||
<div style={controlStyle}>
|
||||
<div style={{ ...controlStyle, backgroundColor: 'transparent', display: 'flex', alignItems: 'center' }}>
|
||||
<input
|
||||
id={`setting_checkbox_${key}`}
|
||||
type="checkbox"
|
||||
checked={!!value}
|
||||
onChange={event => {
|
||||
onCheckboxClick(event);
|
||||
onChange={() => {
|
||||
onCheckboxClick();
|
||||
}}
|
||||
style={{ marginLeft: 0, width: checkboxSize, height: checkboxSize }}
|
||||
/>
|
||||
<label
|
||||
onClick={event => {
|
||||
onCheckboxClick(event);
|
||||
onClick={() => {
|
||||
onCheckboxClick();
|
||||
}}
|
||||
style={checkboxLabelStyle}
|
||||
style={{ ...checkboxLabelStyle, marginLeft: 5, marginBottom: 0 }}
|
||||
htmlFor={`setting_checkbox_${key}`}
|
||||
>
|
||||
{md.label()}
|
||||
</label>
|
||||
{descriptionComp}
|
||||
</div>
|
||||
{descriptionComp}
|
||||
</div>
|
||||
);
|
||||
} else if (md.type === Setting.TYPE_STRING) {
|
||||
const inputStyle = Object.assign({}, textInputBaseStyle, {
|
||||
const inputStyle:any = Object.assign({}, textInputBaseStyle, {
|
||||
width: '50%',
|
||||
minWidth: '20em',
|
||||
});
|
||||
@@ -367,13 +413,13 @@ class ConfigScreenComponent extends React.Component {
|
||||
if (md.subType === 'file_path_and_args') {
|
||||
inputStyle.marginBottom = subLabel.marginBottom;
|
||||
|
||||
const splitCmd = cmdString => {
|
||||
const splitCmd = (cmdString:string) => {
|
||||
const path = pathUtils.extractExecutablePath(cmdString);
|
||||
const args = cmdString.substr(path.length + 1);
|
||||
return [pathUtils.unquotePath(path), args];
|
||||
};
|
||||
|
||||
const joinCmd = cmdArray => {
|
||||
const joinCmd = (cmdArray:string[]) => {
|
||||
if (!cmdArray[0] && !cmdArray[1]) return '';
|
||||
let cmdString = pathUtils.quotePath(cmdArray[0]);
|
||||
if (!cmdString) cmdString = '""';
|
||||
@@ -381,13 +427,13 @@ class ConfigScreenComponent extends React.Component {
|
||||
return cmdString;
|
||||
};
|
||||
|
||||
const onPathChange = event => {
|
||||
const onPathChange = (event:any) => {
|
||||
const cmd = splitCmd(this.state.settings[key]);
|
||||
cmd[0] = event.target.value;
|
||||
updateSettingValue(key, joinCmd(cmd));
|
||||
};
|
||||
|
||||
const onArgsChange = event => {
|
||||
const onArgsChange = (event:any) => {
|
||||
const cmd = splitCmd(this.state.settings[key]);
|
||||
cmd[1] = event.target.value;
|
||||
updateSettingValue(key, joinCmd(cmd));
|
||||
@@ -405,53 +451,51 @@ class ConfigScreenComponent extends React.Component {
|
||||
|
||||
return (
|
||||
<div key={key} style={rowStyle}>
|
||||
<div style={labelStyle}>
|
||||
<label>{md.label()}</label>
|
||||
</div>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<div style={{ flex: 0, whiteSpace: 'nowrap' }}>
|
||||
<div style={labelStyle}>
|
||||
<label>{md.label()}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ flex: 0 }}>
|
||||
<div style={subLabel}>Path:</div>
|
||||
<div style={subLabel}>Arguments:</div>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', marginBottom: inputStyle.marginBottom }}>
|
||||
<div style={{ ...rowStyle, marginBottom: 5 }}>
|
||||
<div style={subLabel}>Path:</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', marginBottom: inputStyle.marginBottom }}>
|
||||
<input
|
||||
type={inputType}
|
||||
style={Object.assign({}, inputStyle, { marginBottom: 0, marginRight: 5 })}
|
||||
onChange={(event:any) => {
|
||||
onPathChange(event);
|
||||
}}
|
||||
value={cmd[0]}
|
||||
/>
|
||||
<Button
|
||||
level={ButtonLevel.Secondary}
|
||||
title={_('Browse...')}
|
||||
onClick={browseButtonClick}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ ...rowStyle, marginBottom: 5 }}>
|
||||
<div style={subLabel}>Arguments:</div>
|
||||
<input
|
||||
type={inputType}
|
||||
style={Object.assign({}, inputStyle, { marginBottom: 0 })}
|
||||
onChange={event => {
|
||||
onPathChange(event);
|
||||
style={inputStyle}
|
||||
onChange={(event:any) => {
|
||||
onArgsChange(event);
|
||||
}}
|
||||
value={cmd[0]}
|
||||
value={cmd[1]}
|
||||
/>
|
||||
<button onClick={browseButtonClick} style={Object.assign({}, theme.buttonStyle, { marginLeft: 5 })}>
|
||||
{_('Browse...')}
|
||||
</button>
|
||||
<div style={{ width: inputStyle.width }}>
|
||||
{descriptionComp}
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
type={inputType}
|
||||
style={inputStyle}
|
||||
onChange={event => {
|
||||
onArgsChange(event);
|
||||
}}
|
||||
value={cmd[1]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex' }}>
|
||||
<div style={{ flex: 0, whiteSpace: 'nowrap' }}>
|
||||
<div style={invisibleLabel}>
|
||||
<label>{md.label()}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>{descriptionComp}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
const onTextChange = event => {
|
||||
const onTextChange = (event:any) => {
|
||||
updateSettingValue(key, event.target.value);
|
||||
};
|
||||
|
||||
@@ -464,16 +508,18 @@ class ConfigScreenComponent extends React.Component {
|
||||
type={inputType}
|
||||
style={inputStyle}
|
||||
value={this.state.settings[key]}
|
||||
onChange={event => {
|
||||
onChange={(event:any) => {
|
||||
onTextChange(event);
|
||||
}}
|
||||
/>
|
||||
{descriptionComp}
|
||||
<div style={{ width: inputStyle.width }}>
|
||||
{descriptionComp}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
} else if (md.type === Setting.TYPE_INT) {
|
||||
const onNumChange = event => {
|
||||
const onNumChange = (event:any) => {
|
||||
updateSettingValue(key, event.target.value);
|
||||
};
|
||||
|
||||
@@ -491,7 +537,7 @@ class ConfigScreenComponent extends React.Component {
|
||||
type="number"
|
||||
style={inputStyle}
|
||||
value={this.state.settings[key]}
|
||||
onChange={event => {
|
||||
onChange={(event:any) => {
|
||||
onNumChange(event);
|
||||
}}
|
||||
min={md.minimum}
|
||||
@@ -502,20 +548,12 @@ class ConfigScreenComponent extends React.Component {
|
||||
</div>
|
||||
);
|
||||
} else if (md.type === Setting.TYPE_BUTTON) {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const buttonStyle = Object.assign({}, theme.buttonStyle, {
|
||||
display: 'inline-block',
|
||||
marginRight: 10,
|
||||
});
|
||||
|
||||
return (
|
||||
<div key={key} style={rowStyle}>
|
||||
<div style={labelStyle}>
|
||||
<label>{md.label()}</label>
|
||||
</div>
|
||||
<button style={buttonStyle} onClick={md.onClick}>
|
||||
{_('Edit')}
|
||||
</button>
|
||||
<Button level={ButtonLevel.Secondary} title={_('Edit')} onClick={md.onClick}/>
|
||||
{descriptionComp}
|
||||
</div>
|
||||
);
|
||||
@@ -544,46 +582,35 @@ class ConfigScreenComponent extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const style = Object.assign(
|
||||
{
|
||||
backgroundColor: theme.backgroundColor,
|
||||
},
|
||||
const style = Object.assign({},
|
||||
this.props.style,
|
||||
{
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
backgroundColor: theme.backgroundColor3,
|
||||
}
|
||||
);
|
||||
|
||||
const settings = this.state.settings;
|
||||
|
||||
const containerStyle = Object.assign({}, theme.containerStyle, { padding: 10, paddingTop: 0, display: 'flex', flex: 1 });
|
||||
const containerStyle = {
|
||||
overflow: 'auto',
|
||||
padding: theme.configScreenPadding,
|
||||
paddingTop: 0,
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
};
|
||||
|
||||
const hasChanges = this.hasChanges();
|
||||
|
||||
const buttonStyle = Object.assign({}, theme.buttonStyle, {
|
||||
display: 'inline-block',
|
||||
marginRight: 10,
|
||||
});
|
||||
|
||||
const buttonStyleApprove = Object.assign({}, buttonStyle, {
|
||||
opacity: hasChanges ? 1 : theme.disabledOpacity,
|
||||
});
|
||||
|
||||
const settingComps = shared.settingsToComponents2(this, 'desktop', settings, this.state.selectedSectionName);
|
||||
|
||||
const buttonBarStyle = {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
padding: 10,
|
||||
borderTopWidth: 1,
|
||||
borderTopStyle: 'solid',
|
||||
borderTopColor: theme.dividerColor,
|
||||
};
|
||||
|
||||
// screenComp is a custom config screen, such as the encryption config screen or keymap config screen.
|
||||
// These screens handle their own loading/saving of settings and have bespoke rendering.
|
||||
// When screenComp is null, it means we are viewing the regular settings.
|
||||
const screenComp = this.state.screenName ? <div style={{ overflow: 'scroll', flex: 1 }}>{this.screenFromName(this.state.screenName)}</div> : null;
|
||||
|
||||
if (screenComp) containerStyle.display = 'none';
|
||||
@@ -591,45 +618,35 @@ class ConfigScreenComponent extends React.Component {
|
||||
const sections = shared.settingsSections({ device: 'desktop', settings });
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<ConfigMenuBar
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
<SideBar
|
||||
selection={this.state.selectedSectionName}
|
||||
onSelectionChange={this.configMenuBar_selectionChange}
|
||||
onSelectionChange={this.sideBar_selectionChange}
|
||||
sections={sections}
|
||||
theme={this.props.theme}
|
||||
/>
|
||||
{screenComp}
|
||||
<div style={containerStyle}>{settingComps}</div>
|
||||
<div style={buttonBarStyle}>
|
||||
<button
|
||||
onClick={() => {
|
||||
this.onCancelClick();
|
||||
}}
|
||||
style={buttonStyle}
|
||||
>
|
||||
<i style={theme.buttonIconStyle} className={'fa fa-chevron-left'}></i>
|
||||
{hasChanges && !screenComp ? _('Cancel') : _('Back')}
|
||||
</button>
|
||||
{ !screenComp && (
|
||||
<div>
|
||||
<button disabled={!hasChanges} onClick={() => { this.onSaveClick(); }} style={buttonStyleApprove}>{_('OK')}</button>
|
||||
<button disabled={!hasChanges} onClick={() => { this.onApplyClick(); }} style={buttonStyleApprove}>{_('Apply')}</button>
|
||||
</div>
|
||||
)}
|
||||
<div style={style}>
|
||||
{screenComp}
|
||||
<div style={containerStyle}>{settingComps}</div>
|
||||
<ButtonBar
|
||||
hasChanges={hasChanges}
|
||||
backButtonTitle={hasChanges && !screenComp ? _('Cancel') : _('Back')}
|
||||
onCancelClick={this.onCancelClick}
|
||||
onSaveClick={screenComp ? null : this.onSaveClick}
|
||||
onApplyClick={screenComp ? null : this.onApplyClick}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const mapStateToProps = (state:any) => {
|
||||
return {
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
settings: state.settings,
|
||||
locale: state.settings.locale,
|
||||
};
|
||||
};
|
||||
|
||||
const ConfigScreen = connect(mapStateToProps)(ConfigScreenComponent);
|
||||
export default connect(mapStateToProps)(ConfigScreenComponent);
|
||||
|
||||
module.exports = { ConfigScreen };
|
||||
@@ -0,0 +1,74 @@
|
||||
import * as React from 'react';
|
||||
const styled = require('styled-components').default;
|
||||
const Setting = require('lib/models/Setting');
|
||||
|
||||
interface Props {
|
||||
selection: string,
|
||||
onSelectionChange: Function,
|
||||
sections: any[],
|
||||
}
|
||||
|
||||
export const StyledRoot = styled.div`
|
||||
display: flex;
|
||||
background-color: ${(props:any) => props.theme.backgroundColor2};
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
export const StyledListItem = styled.a`
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: ${(props:any) => props.theme.mainPadding}px;
|
||||
background: ${(props:any) => props.selected ? props.theme.selectedColor2 : 'none'};
|
||||
transition: 0.1s;
|
||||
text-decoration: none;
|
||||
cursor: default;
|
||||
opacity: ${(props:any) => props.selected ? 1 : 0.8};
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props:any) => props.theme.backgroundColorHover2};
|
||||
}
|
||||
`;
|
||||
|
||||
export const StyledListItemLabel = styled.span`
|
||||
font-size: ${(props:any) => Math.round(props.theme.fontSize * 1.2)}px;
|
||||
font-weight: 500;
|
||||
color: ${(props:any) => props.theme.color2};
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
export const StyledListItemIcon = styled.i`
|
||||
font-size: ${(props:any) => Math.round(props.theme.fontSize * 1.4)}px;
|
||||
color: ${(props:any) => props.theme.color2};
|
||||
margin-right: ${(props:any) => props.theme.mainPadding / 1.5}px;
|
||||
`;
|
||||
|
||||
export default function SideBar(props:Props) {
|
||||
const buttons:any[] = [];
|
||||
|
||||
function renderButton(section:any) {
|
||||
const selected = props.selection === section.name;
|
||||
return (
|
||||
<StyledListItem key={section.name} selected={selected} onClick={() => { props.onSelectionChange({ section: section }); }}>
|
||||
<StyledListItemIcon className={Setting.sectionNameToIcon(section.name)} />
|
||||
<StyledListItemLabel>
|
||||
{Setting.sectionNameToLabel(section.name)}
|
||||
</StyledListItemLabel>
|
||||
</StyledListItem>
|
||||
);
|
||||
}
|
||||
|
||||
for (const section of props.sections) {
|
||||
buttons.push(renderButton(section));
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledRoot>
|
||||
{buttons}
|
||||
</StyledRoot>
|
||||
);
|
||||
}
|
||||
@@ -3,7 +3,7 @@ const { _ } = require('lib/locale.js');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
|
||||
function DialogButtonRow(props) {
|
||||
const theme = themeStyle(props.theme);
|
||||
const theme = themeStyle(props.themeId);
|
||||
|
||||
const okButton_click = () => {
|
||||
if (props.onClick) props.onClick({ buttonName: 'ok' });
|
||||
|
||||
+25
-17
@@ -1,16 +1,24 @@
|
||||
const React = require('react');
|
||||
import * as React from 'react';
|
||||
import ButtonBar from './ConfigScreen/ButtonBar';
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const { Header } = require('./Header/Header.min.js');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Shared = require('lib/components/shared/dropbox-login-shared');
|
||||
|
||||
class DropboxLoginScreenComponent extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
interface Props {
|
||||
themeId: string,
|
||||
}
|
||||
|
||||
this.shared_ = new Shared(this, msg => bridge().showInfoMessageBox(msg), msg => bridge().showErrorMessageBox(msg));
|
||||
class DropboxLoginScreenComponent extends React.Component<any, any> {
|
||||
|
||||
shared_:any;
|
||||
|
||||
constructor(props:Props) {
|
||||
super(props);
|
||||
|
||||
this.shared_ = new Shared(this, (msg:string) => bridge().showInfoMessageBox(msg), (msg:string) => bridge().showErrorMessageBox(msg));
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
@@ -19,19 +27,18 @@ class DropboxLoginScreenComponent extends React.Component {
|
||||
|
||||
render() {
|
||||
const style = this.props.style;
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const headerStyle = Object.assign({}, theme.headerStyle, { width: style.width });
|
||||
const containerStyle = Object.assign({}, theme.containerStyle, {
|
||||
padding: theme.margin,
|
||||
height: style.height - theme.headerHeight - theme.margin * 2,
|
||||
padding: theme.configScreenPadding,
|
||||
height: style.height - theme.margin * 2,
|
||||
flex: 1,
|
||||
});
|
||||
|
||||
const inputStyle = Object.assign({}, theme.inputStyle, { width: 500 });
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Header style={headerStyle} />
|
||||
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
||||
<div style={containerStyle}>
|
||||
<p style={theme.textStyle}>{_('To allow Joplin to synchronise with Dropbox, please follow the steps below:')}</p>
|
||||
<p style={theme.textStyle}>{_('Step 1: Open this URL in your browser to authorise the application:')}</p>
|
||||
@@ -46,17 +53,18 @@ class DropboxLoginScreenComponent extends React.Component {
|
||||
{_('Submit')}
|
||||
</button>
|
||||
</div>
|
||||
<ButtonBar
|
||||
onCancelClick={() => this.props.dispatch({ type: 'NAV_BACK' })}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const mapStateToProps = (state:any) => {
|
||||
return {
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
};
|
||||
};
|
||||
|
||||
const DropboxLoginScreen = connect(mapStateToProps)(DropboxLoginScreenComponent);
|
||||
|
||||
module.exports = { DropboxLoginScreen };
|
||||
export default connect(mapStateToProps)(DropboxLoginScreenComponent);
|
||||
@@ -35,7 +35,7 @@ class EncryptionConfigScreenComponent extends React.Component {
|
||||
}
|
||||
|
||||
renderMasterKey(mk) {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const passwordStyle = {
|
||||
color: theme.color,
|
||||
@@ -80,7 +80,7 @@ class EncryptionConfigScreenComponent extends React.Component {
|
||||
const needUpgradeMasterKeys = EncryptionService.instance().masterKeysThatNeedUpgrading(this.props.masterKeys);
|
||||
if (!needUpgradeMasterKeys.length) return null;
|
||||
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const rows = [];
|
||||
const comp = this;
|
||||
@@ -114,7 +114,7 @@ class EncryptionConfigScreenComponent extends React.Component {
|
||||
renderReencryptData() {
|
||||
if (!shim.isElectron()) return null;
|
||||
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const buttonLabel = _('Re-encrypt data');
|
||||
|
||||
const intro = this.props.shouldReencrypt ? _('The default encryption method has been changed to a more secure one and it is recommended that you apply it to your data.') : _('You may use the tool below to re-encrypt your data, for example if you know that some of your notes are encrypted with an obsolete encryption method.');
|
||||
@@ -139,13 +139,13 @@ class EncryptionConfigScreenComponent extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const masterKeys = this.props.masterKeys;
|
||||
const containerPadding = 10;
|
||||
|
||||
const containerStyle = Object.assign({}, theme.containerStyle, {
|
||||
padding: containerPadding,
|
||||
padding: theme.configScreenPadding,
|
||||
overflow: 'auto',
|
||||
backgroundColor: theme.backgroundColor3,
|
||||
});
|
||||
|
||||
const mkComps = [];
|
||||
@@ -289,7 +289,7 @@ class EncryptionConfigScreenComponent extends React.Component {
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
masterKeys: state.masterKeys,
|
||||
passwords: state.settings['encryption.passwordCache'],
|
||||
encryptionEnabled: state.settings['encryption.enabled'],
|
||||
|
||||
@@ -1,329 +0,0 @@
|
||||
const React = require('react');
|
||||
const { connect } = require('react-redux');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const CommandService = require('lib/services/CommandService').default;
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
|
||||
const commands = [
|
||||
require('./commands/focusSearch'),
|
||||
];
|
||||
|
||||
class HeaderComponent extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
searchQuery: '',
|
||||
showSearchUsageLink: false,
|
||||
showButtonLabels: true,
|
||||
};
|
||||
|
||||
for (const command of commands) {
|
||||
CommandService.instance().registerRuntime(command.declaration.name, command.runtime(this));
|
||||
}
|
||||
|
||||
this.scheduleSearchChangeEventIid_ = null;
|
||||
this.searchOnQuery_ = null;
|
||||
this.searchElement_ = null;
|
||||
|
||||
const triggerOnQuery = query => {
|
||||
clearTimeout(this.scheduleSearchChangeEventIid_);
|
||||
if (this.searchOnQuery_) this.searchOnQuery_(query, Setting.value('db.fuzzySearchEnabled'));
|
||||
this.scheduleSearchChangeEventIid_ = null;
|
||||
};
|
||||
|
||||
this.search_onChange = event => {
|
||||
this.setState({ searchQuery: event.target.value });
|
||||
|
||||
if (this.scheduleSearchChangeEventIid_) clearTimeout(this.scheduleSearchChangeEventIid_);
|
||||
|
||||
this.scheduleSearchChangeEventIid_ = setTimeout(() => {
|
||||
triggerOnQuery(this.state.searchQuery);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
this.search_onClear = () => {
|
||||
this.resetSearch();
|
||||
if (this.searchElement_) this.searchElement_.focus();
|
||||
};
|
||||
|
||||
this.search_onFocus = () => {
|
||||
if (this.hideSearchUsageLinkIID_) {
|
||||
clearTimeout(this.hideSearchUsageLinkIID_);
|
||||
this.hideSearchUsageLinkIID_ = null;
|
||||
}
|
||||
|
||||
this.setState({ showSearchUsageLink: true });
|
||||
};
|
||||
|
||||
this.search_onBlur = () => {
|
||||
if (this.hideSearchUsageLinkIID_) return;
|
||||
|
||||
this.hideSearchUsageLinkIID_ = setTimeout(() => {
|
||||
this.setState({ showSearchUsageLink: false });
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
this.search_keyDown = event => {
|
||||
if (event.keyCode === 27) {
|
||||
// ESCAPE
|
||||
this.resetSearch();
|
||||
}
|
||||
};
|
||||
|
||||
this.resetSearch = () => {
|
||||
this.setState({ searchQuery: '' });
|
||||
triggerOnQuery('');
|
||||
};
|
||||
|
||||
this.searchUsageLink_click = () => {
|
||||
bridge().openExternal('https://joplinapp.org/#searching');
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.notesParentType !== this.props.notesParentType && this.props.notesParentType !== 'Search' && this.state.searchQuery) {
|
||||
this.resetSearch();
|
||||
}
|
||||
|
||||
if (this.props.zoomFactor !== prevProps.zoomFactor || this.props.size !== prevProps.size) {
|
||||
this.determineButtonLabelState();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.determineButtonLabelState();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.hideSearchUsageLinkIID_) {
|
||||
clearTimeout(this.hideSearchUsageLinkIID_);
|
||||
this.hideSearchUsageLinkIID_ = null;
|
||||
}
|
||||
|
||||
for (const command of commands) {
|
||||
CommandService.instance().unregisterRuntime(command.declaration.name);
|
||||
}
|
||||
}
|
||||
|
||||
determineButtonLabelState() {
|
||||
const mediaQuery = window.matchMedia(`(max-width: ${780 * this.props.zoomFactor}px)`);
|
||||
const showButtonLabels = !mediaQuery.matches;
|
||||
|
||||
if (this.state.showButtonLabels !== showButtonLabels) {
|
||||
this.setState({
|
||||
showButtonLabels: !mediaQuery.matches,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
back_click() {
|
||||
this.props.dispatch({ type: 'NAV_BACK' });
|
||||
}
|
||||
|
||||
makeButton(key, style, options) {
|
||||
// TODO: "tab" type is not finished
|
||||
if (options.type === 'tab') {
|
||||
const buttons = [];
|
||||
for (let i = 0; i < options.items.length; i++) {
|
||||
const item = options.items[i];
|
||||
buttons.push(this.makeButton(key + item.title, style, Object.assign({}, options, {
|
||||
title: item.title,
|
||||
type: 'button',
|
||||
})));
|
||||
}
|
||||
|
||||
return <span style={{ display: 'flex', flexDirection: 'row' }}>{buttons}</span>;
|
||||
}
|
||||
|
||||
const theme = themeStyle(this.props.theme);
|
||||
|
||||
let icon = null;
|
||||
if (options.iconName) {
|
||||
const iconStyle = {
|
||||
fontSize: Math.round(style.fontSize * 1.1),
|
||||
color: theme.iconColor,
|
||||
};
|
||||
if (options.title) iconStyle.marginRight = 5;
|
||||
if ('undefined' != typeof options.iconRotation) {
|
||||
iconStyle.transition = 'transform 0.15s ease-in-out';
|
||||
iconStyle.transform = `rotate(${options.iconRotation}deg)`;
|
||||
}
|
||||
icon = <i style={iconStyle} className={`fas ${options.iconName}`}></i>;
|
||||
}
|
||||
|
||||
const isEnabled = !('enabled' in options) || options.enabled;
|
||||
const classes = ['button'];
|
||||
if (!isEnabled) classes.push('disabled');
|
||||
|
||||
const finalStyle = Object.assign({}, style, {
|
||||
opacity: isEnabled ? 1 : 0.4,
|
||||
});
|
||||
|
||||
const title = options.title ? options.title : '';
|
||||
|
||||
if (options.type === 'checkbox' && options.checked) {
|
||||
finalStyle.backgroundColor = theme.selectedColor;
|
||||
finalStyle.borderWidth = 1;
|
||||
finalStyle.borderTopColor = theme.selectedDividerColor;
|
||||
finalStyle.borderLeftColor = theme.selectedDividerColor;
|
||||
finalStyle.borderTopStyle = 'solid';
|
||||
finalStyle.borderLeftStyle = 'solid';
|
||||
finalStyle.paddingLeft++;
|
||||
finalStyle.paddingTop++;
|
||||
finalStyle.paddingBottom--;
|
||||
finalStyle.paddingRight--;
|
||||
finalStyle.boxSizing = 'border-box';
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
className={classes.join(' ')}
|
||||
style={finalStyle}
|
||||
key={key}
|
||||
href="#"
|
||||
title={title}
|
||||
onClick={() => {
|
||||
if (isEnabled) options.onClick();
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
<span className="title" style={{
|
||||
display: this.state.showButtonLabels ? 'inline-block' : 'none',
|
||||
}}>{title}</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
makeSearch(key, style, options, state) {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
|
||||
const inputStyle = {
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
marginLeft: 10,
|
||||
paddingLeft: 6,
|
||||
paddingRight: 6,
|
||||
paddingTop: 1, // vertical alignment with buttons
|
||||
paddingBottom: 0, // vertical alignment with buttons
|
||||
height: style.fontSize * 2,
|
||||
maxWidth: 300,
|
||||
color: style.color,
|
||||
fontSize: style.fontSize,
|
||||
fontFamily: style.fontFamily,
|
||||
backgroundColor: style.searchColor,
|
||||
border: '1px solid',
|
||||
borderColor: style.dividerColor,
|
||||
};
|
||||
|
||||
const searchButton = {
|
||||
paddingLeft: 4,
|
||||
paddingRight: 4,
|
||||
paddingTop: 2,
|
||||
paddingBottom: 2,
|
||||
textDecoration: 'none',
|
||||
};
|
||||
|
||||
const iconStyle = {
|
||||
display: 'flex',
|
||||
fontSize: Math.round(style.fontSize) * 1.2,
|
||||
color: style.color,
|
||||
};
|
||||
|
||||
const containerStyle = {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexGrow: 1,
|
||||
alignItems: 'center',
|
||||
};
|
||||
|
||||
const iconName = state.searchQuery ? 'fa-times' : 'fa-search';
|
||||
const icon = <i style={iconStyle} className={`fas ${iconName}`}></i>;
|
||||
if (options.onQuery) this.searchOnQuery_ = options.onQuery;
|
||||
|
||||
const usageLink = !this.state.showSearchUsageLink ? null : (
|
||||
<a onClick={this.searchUsageLink_click} style={theme.urlStyle} href="#">
|
||||
{_('Usage')}
|
||||
</a>
|
||||
);
|
||||
|
||||
return (
|
||||
<div key={key} style={containerStyle}>
|
||||
<input type="text" style={inputStyle} placeholder={options.title} value={state.searchQuery} onChange={this.search_onChange} ref={elem => (this.searchElement_ = elem)} onFocus={this.search_onFocus} onBlur={this.search_onBlur} onKeyDown={this.search_keyDown} />
|
||||
<a href="#" style={searchButton} onClick={this.search_onClear}>
|
||||
{icon}
|
||||
</a>
|
||||
{usageLink}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const style = Object.assign({}, this.props.style);
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const showBackButton = this.props.showBackButton === undefined || this.props.showBackButton === true;
|
||||
style.height = theme.headerHeight;
|
||||
style.display = 'flex';
|
||||
style.flexDirection = 'row';
|
||||
style.borderBottom = `1px solid ${theme.dividerColor}`;
|
||||
style.boxSizing = 'border-box';
|
||||
|
||||
const items = [];
|
||||
|
||||
const itemStyle = {
|
||||
height: theme.headerHeight,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
paddingTop: 1,
|
||||
paddingBottom: 1,
|
||||
paddingLeft: theme.headerButtonHPadding,
|
||||
paddingRight: theme.headerButtonHPadding,
|
||||
color: theme.color,
|
||||
searchColor: theme.backgroundColor,
|
||||
dividerColor: theme.dividerColor,
|
||||
textDecoration: 'none',
|
||||
fontFamily: theme.fontFamily,
|
||||
fontSize: theme.fontSize,
|
||||
boxSizing: 'border-box',
|
||||
cursor: 'default',
|
||||
whiteSpace: 'nowrap',
|
||||
userSelect: 'none',
|
||||
};
|
||||
|
||||
if (showBackButton) {
|
||||
items.push(this.makeButton('back', itemStyle, { title: _('Back'), onClick: () => this.back_click(), iconName: 'fa-chevron-left ' }));
|
||||
}
|
||||
|
||||
if (this.props.items) {
|
||||
for (let i = 0; i < this.props.items.length; i++) {
|
||||
const item = this.props.items[i];
|
||||
|
||||
if (item.type === 'search') {
|
||||
items.push(this.makeSearch(`item_${i}_search`, itemStyle, item, this.state));
|
||||
} else {
|
||||
items.push(this.makeButton(`item_${i}_${item.title}`, itemStyle, item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="header" style={style}>
|
||||
{items}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
theme: state.settings.theme,
|
||||
notesParentType: state.notesParentType,
|
||||
size: state.windowContentSize,
|
||||
zoomFactor: state.settings.windowContentZoomFactor / 100,
|
||||
};
|
||||
};
|
||||
|
||||
const Header = connect(mapStateToProps)(HeaderComponent);
|
||||
|
||||
module.exports = { Header };
|
||||
@@ -14,7 +14,7 @@ class HelpButtonComponent extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const style = Object.assign({}, this.props.style, { color: theme.color, textDecoration: 'none' });
|
||||
const helpIconStyle = { flex: 0, width: 16, height: 16, marginLeft: 10 };
|
||||
const extraProps = {};
|
||||
@@ -29,7 +29,7 @@ class HelpButtonComponent extends React.Component {
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ const { themeStyle } = require('lib/theme');
|
||||
class IconButton extends React.Component {
|
||||
render() {
|
||||
const style = this.props.style;
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const iconStyle = {
|
||||
color: theme.color,
|
||||
fontSize: theme.fontSize * 1.4,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const React = require('react');
|
||||
const { connect } = require('react-redux');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const { Header } = require('./Header/Header.min.js');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { filename, basename } = require('lib/path-utils.js');
|
||||
@@ -94,8 +93,7 @@ class ImportScreenComponent extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const style = this.props.style;
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const messages = this.uniqueMessages();
|
||||
|
||||
const messagesStyle = {
|
||||
@@ -105,10 +103,6 @@ class ImportScreenComponent extends React.Component {
|
||||
backgroundColor: theme.backgroundColor,
|
||||
};
|
||||
|
||||
const headerStyle = {
|
||||
width: style.width,
|
||||
};
|
||||
|
||||
const messageComps = [];
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
messageComps.push(<div key={messages[i].key}>{messages[i].text}</div>);
|
||||
@@ -116,7 +110,6 @@ class ImportScreenComponent extends React.Component {
|
||||
|
||||
return (
|
||||
<div style={{}}>
|
||||
<Header style={headerStyle} />
|
||||
<div style={messagesStyle}>{messageComps}</div>
|
||||
</div>
|
||||
);
|
||||
@@ -125,7 +118,7 @@ class ImportScreenComponent extends React.Component {
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -36,6 +36,10 @@ class ItemList extends React.Component {
|
||||
return this.listRef.current ? this.listRef.current.offsetTop : 0;
|
||||
}
|
||||
|
||||
offsetScroll() {
|
||||
return this.scrollTop_;
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this.updateStateItemIndexes();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ export default function styles(themeId: number) {
|
||||
return {
|
||||
container: {
|
||||
...theme.containerStyle,
|
||||
padding: 16,
|
||||
padding: theme.configScreenPadding,
|
||||
backgroundColor: theme.backgroundColor3,
|
||||
},
|
||||
actionsContainer: {
|
||||
display: 'flex',
|
||||
|
||||
+190
-79
@@ -1,24 +1,26 @@
|
||||
const React = require('react');
|
||||
import * as React from 'react';
|
||||
import ResizableLayout, { findItemByKey, LayoutItem, LayoutItemDirection } from '../ResizableLayout/ResizableLayout';
|
||||
import NoteList from '../NoteList/NoteList.js';
|
||||
import NoteEditor from '../NoteEditor/NoteEditor.js';
|
||||
import NoteContentPropertiesDialog from '../NoteContentPropertiesDialog.js';
|
||||
import ShareNoteDialog from '../ShareNoteDialog.js';
|
||||
import NoteListControls from '../NoteListControls/NoteListControls.js';
|
||||
import CommandService from 'lib/services/CommandService';
|
||||
|
||||
const produce = require('immer').default;
|
||||
const { connect } = require('react-redux');
|
||||
const { Header } = require('../Header/Header.min.js');
|
||||
const { SideBar } = require('../SideBar/SideBar.min.js');
|
||||
const { NoteList } = require('../NoteList/NoteList.min.js');
|
||||
const NoteEditor = require('../NoteEditor/NoteEditor.js').default;
|
||||
const { SideBar } = require('../SideBar/SideBar.js');
|
||||
const { stateUtils } = require('lib/reducer.js');
|
||||
const { PromptDialog } = require('../PromptDialog.min.js');
|
||||
const NoteContentPropertiesDialog = require('../NoteContentPropertiesDialog.js').default;
|
||||
const NotePropertiesDialog = require('../NotePropertiesDialog.min.js');
|
||||
const ShareNoteDialog = require('../ShareNoteDialog.js').default;
|
||||
const InteropServiceHelper = require('../../InteropServiceHelper.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const { themeStyle } = require('lib/theme.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const VerticalResizer = require('../VerticalResizer.min');
|
||||
const PluginManager = require('lib/services/PluginManager');
|
||||
const EncryptionService = require('lib/services/EncryptionService');
|
||||
const CommandService = require('lib/services/CommandService').default;
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
const { time } = require('lib/time-utils.js');
|
||||
|
||||
@@ -28,7 +30,7 @@ const commands = [
|
||||
require('./commands/hideModalMessage'),
|
||||
require('./commands/moveToFolder'),
|
||||
require('./commands/newNote'),
|
||||
require('./commands/newNotebook'),
|
||||
require('./commands/newFolder'),
|
||||
require('./commands/newTodo'),
|
||||
require('./commands/print'),
|
||||
require('./commands/renameFolder'),
|
||||
@@ -40,14 +42,76 @@ const commands = [
|
||||
require('./commands/showNoteContentProperties'),
|
||||
require('./commands/showNoteProperties'),
|
||||
require('./commands/showShareNoteDialog'),
|
||||
require('./commands/toggleEditors'),
|
||||
require('./commands/toggleNoteList'),
|
||||
require('./commands/toggleSidebar'),
|
||||
require('./commands/toggleVisiblePanes'),
|
||||
];
|
||||
|
||||
class MainScreenComponent extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
class MainScreenComponent extends React.Component<any, any> {
|
||||
|
||||
waitForNotesSavedIID_:any;
|
||||
isPrinting_:boolean;
|
||||
styleKey_:string;
|
||||
styles_:any;
|
||||
promptOnClose_:Function;
|
||||
|
||||
constructor(props:any) {
|
||||
super(props);
|
||||
|
||||
const rootLayoutSize = this.rootLayoutSize();
|
||||
const theme = themeStyle(props.themeId);
|
||||
const sideBarMinWidth = 200;
|
||||
|
||||
const layout:LayoutItem = {
|
||||
key: 'root',
|
||||
direction: LayoutItemDirection.Row,
|
||||
resizable: false,
|
||||
width: rootLayoutSize.width,
|
||||
height: rootLayoutSize.height,
|
||||
children: [
|
||||
{
|
||||
key: 'sidebarColumn',
|
||||
direction: LayoutItemDirection.Column,
|
||||
resizable: true,
|
||||
width: Setting.value('style.sidebar.width') < sideBarMinWidth ? sideBarMinWidth : Setting.value('style.sidebar.width'),
|
||||
visible: Setting.value('sidebarVisibility'),
|
||||
minWidth: sideBarMinWidth,
|
||||
children: [
|
||||
{
|
||||
key: 'sideBar',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'noteListColumn',
|
||||
direction: LayoutItemDirection.Column,
|
||||
resizable: true,
|
||||
width: Setting.value('style.noteList.width') < sideBarMinWidth ? sideBarMinWidth : Setting.value('style.noteList.width'),
|
||||
visible: Setting.value('noteListVisibility'),
|
||||
minWidth: sideBarMinWidth,
|
||||
children: [
|
||||
{
|
||||
height: theme.topRowHeight,
|
||||
key: 'noteListControls',
|
||||
},
|
||||
{
|
||||
key: 'noteList',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'editorColumn',
|
||||
direction: LayoutItemDirection.Column,
|
||||
resizable: false,
|
||||
children: [
|
||||
{
|
||||
key: 'editor',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
this.state = {
|
||||
promptOptions: null,
|
||||
@@ -58,6 +122,7 @@ class MainScreenComponent extends React.Component {
|
||||
notePropertiesDialogOptions: {},
|
||||
noteContentPropertiesDialogOptions: {},
|
||||
shareNoteDialogOptions: {},
|
||||
layout: layout,
|
||||
};
|
||||
|
||||
this.registerCommands();
|
||||
@@ -70,6 +135,16 @@ class MainScreenComponent extends React.Component {
|
||||
this.shareNoteDialog_close = this.shareNoteDialog_close.bind(this);
|
||||
this.sidebar_onDrag = this.sidebar_onDrag.bind(this);
|
||||
this.noteList_onDrag = this.noteList_onDrag.bind(this);
|
||||
this.resizableLayout_resize = this.resizableLayout_resize.bind(this);
|
||||
this.resizableLayout_renderItem = this.resizableLayout_renderItem.bind(this);
|
||||
this.window_resize = this.window_resize.bind(this);
|
||||
this.rowHeight = this.rowHeight.bind(this);
|
||||
|
||||
window.addEventListener('resize', this.window_resize);
|
||||
}
|
||||
|
||||
window_resize() {
|
||||
this.updateRootLayoutSize();
|
||||
}
|
||||
|
||||
setupAppCloseHandling() {
|
||||
@@ -103,11 +178,11 @@ class MainScreenComponent extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
sidebar_onDrag(event) {
|
||||
sidebar_onDrag(event:any) {
|
||||
Setting.setValue('style.sidebar.width', this.props.sidebarWidth + event.deltaX);
|
||||
}
|
||||
|
||||
noteList_onDrag(event) {
|
||||
noteList_onDrag(event:any) {
|
||||
Setting.setValue('style.noteList.width', Setting.value('style.noteList.width') + event.deltaX);
|
||||
}
|
||||
|
||||
@@ -123,13 +198,13 @@ class MainScreenComponent extends React.Component {
|
||||
this.setState({ shareNoteDialogOptions: {} });
|
||||
}
|
||||
|
||||
commandService_commandsEnabledStateChange(event) {
|
||||
commandService_commandsEnabledStateChange(event:any) {
|
||||
const buttonCommandNames = [
|
||||
'toggleSidebar',
|
||||
'toggleNoteList',
|
||||
'newNote',
|
||||
'newTodo',
|
||||
'newNotebook',
|
||||
'newFolder',
|
||||
'toggleVisiblePanes',
|
||||
];
|
||||
|
||||
@@ -141,13 +216,40 @@ class MainScreenComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
updateRootLayoutSize() {
|
||||
this.setState({ layout: produce(this.state.layout, (draftState:any) => {
|
||||
const s = this.rootLayoutSize();
|
||||
draftState.width = s.width;
|
||||
draftState.height = s.height;
|
||||
}) });
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps:any) {
|
||||
if (this.props.noteListVisibility !== prevProps.noteListVisibility || this.props.sidebarVisibility !== prevProps.sidebarVisibility) {
|
||||
this.setState({ layout: produce(this.state.layout, (draftState:any) => {
|
||||
const noteListColumn = findItemByKey(draftState, 'noteListColumn');
|
||||
noteListColumn.visible = this.props.noteListVisibility;
|
||||
|
||||
const sidebarColumn = findItemByKey(draftState, 'sidebarColumn');
|
||||
sidebarColumn.visible = this.props.sidebarVisibility;
|
||||
}) });
|
||||
}
|
||||
|
||||
if (prevProps.style.width !== this.props.style.width || prevProps.style.height !== this.props.style.height) {
|
||||
this.updateRootLayoutSize();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
CommandService.instance().on('commandsEnabledStateChange', this.commandService_commandsEnabledStateChange);
|
||||
this.updateRootLayoutSize();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
CommandService.instance().off('commandsEnabledStateChange', this.commandService_commandsEnabledStateChange);
|
||||
this.unregisterCommands();
|
||||
|
||||
window.removeEventListener('resize', this.window_resize);
|
||||
}
|
||||
|
||||
toggleSidebar() {
|
||||
@@ -162,14 +264,14 @@ class MainScreenComponent extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
async waitForNoteToSaved(noteId) {
|
||||
async waitForNoteToSaved(noteId:string) {
|
||||
while (noteId && this.props.editorNoteStatuses[noteId] === 'saving') {
|
||||
console.info('Waiting for note to be saved...', this.props.editorNoteStatuses);
|
||||
await time.msleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
async printTo_(target, options) {
|
||||
async printTo_(target:string, options:any) {
|
||||
// Concurrent print calls are disallowed to avoid incorrect settings being restored upon completion
|
||||
if (this.isPrinting_) {
|
||||
console.info(`Printing ${options.path} to ${target} disallowed, already printing.`);
|
||||
@@ -208,7 +310,23 @@ class MainScreenComponent extends React.Component {
|
||||
this.isPrinting_ = false;
|
||||
}
|
||||
|
||||
styles(themeId, width, height, messageBoxVisible, isSidebarVisible, isNoteListVisible, sidebarWidth, noteListWidth) {
|
||||
rootLayoutSize() {
|
||||
return {
|
||||
width: window.innerWidth,
|
||||
height: this.rowHeight(),
|
||||
};
|
||||
}
|
||||
|
||||
rowHeight() {
|
||||
if (!this.props) return 0;
|
||||
return this.props.style.height - (this.messageBoxVisible() ? this.messageBoxHeight() : 0);
|
||||
}
|
||||
|
||||
messageBoxHeight() {
|
||||
return 50;
|
||||
}
|
||||
|
||||
styles(themeId:number, width:number, height:number, messageBoxVisible:boolean, isSidebarVisible:any, isNoteListVisible:any, sidebarWidth:number, noteListWidth:number) {
|
||||
const styleKey = [themeId, width, height, messageBoxVisible, +isSidebarVisible, +isNoteListVisible, sidebarWidth, noteListWidth].join('_');
|
||||
if (styleKey === this.styleKey_) return this.styles_;
|
||||
|
||||
@@ -224,14 +342,16 @@ class MainScreenComponent extends React.Component {
|
||||
|
||||
this.styles_.messageBox = {
|
||||
width: width,
|
||||
height: 50,
|
||||
height: this.messageBoxHeight(),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
paddingLeft: 10,
|
||||
backgroundColor: theme.warningBackgroundColor,
|
||||
};
|
||||
|
||||
const rowHeight = height - theme.headerHeight - (messageBoxVisible ? this.styles_.messageBox.height : 0);
|
||||
const rowHeight = height - (messageBoxVisible ? this.styles_.messageBox.height : 0);
|
||||
|
||||
this.styles_.rowHeight = rowHeight;
|
||||
|
||||
this.styles_.verticalResizerSidebar = {
|
||||
width: 5,
|
||||
@@ -241,6 +361,10 @@ class MainScreenComponent extends React.Component {
|
||||
display: 'inline-block',
|
||||
};
|
||||
|
||||
this.styles_.resizableLayout = {
|
||||
height: rowHeight,
|
||||
};
|
||||
|
||||
this.styles_.verticalResizerNotelist = Object.assign({}, this.styles_.verticalResizerSidebar);
|
||||
|
||||
this.styles_.sideBar = {
|
||||
@@ -295,7 +419,7 @@ class MainScreenComponent extends React.Component {
|
||||
return this.styles_;
|
||||
}
|
||||
|
||||
renderNotification(theme, styles) {
|
||||
renderNotification(theme:any, styles:any) {
|
||||
if (!this.messageBoxVisible()) return null;
|
||||
|
||||
const onViewStatusScreen = () => {
|
||||
@@ -401,8 +525,34 @@ class MainScreenComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
resizableLayout_resize(event:any) {
|
||||
this.setState({ layout: event.layout });
|
||||
|
||||
const col1 = findItemByKey(event.layout, 'sidebarColumn');
|
||||
const col2 = findItemByKey(event.layout, 'noteListColumn');
|
||||
Setting.setValue('style.sidebar.width', col1.width);
|
||||
Setting.setValue('style.noteList.width', col2.width);
|
||||
}
|
||||
|
||||
resizableLayout_renderItem(key:string, event:any) {
|
||||
const eventEmitter = event.eventEmitter;
|
||||
|
||||
if (key === 'sideBar') {
|
||||
return <SideBar key={key} />;
|
||||
} else if (key === 'noteList') {
|
||||
return <NoteList key={key} resizableLayoutEventEmitter={eventEmitter} size={event.size} visible={event.visible}/>;
|
||||
} else if (key === 'editor') {
|
||||
const bodyEditor = this.props.settingEditorCodeView ? 'CodeMirror' : 'TinyMCE';
|
||||
return <NoteEditor key={key} bodyEditor={bodyEditor} />;
|
||||
} else if (key === 'noteListControls') {
|
||||
return <NoteListControls key={key} />;
|
||||
}
|
||||
|
||||
throw new Error(`Invalid layout component: ${key}`);
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const style = Object.assign(
|
||||
{
|
||||
color: theme.color,
|
||||
@@ -411,48 +561,12 @@ class MainScreenComponent extends React.Component {
|
||||
this.props.style
|
||||
);
|
||||
const promptOptions = this.state.promptOptions;
|
||||
const notes = this.props.notes;
|
||||
const sidebarVisibility = this.props.sidebarVisibility;
|
||||
const noteListVisibility = this.props.noteListVisibility;
|
||||
const styles = this.styles(this.props.theme, style.width, style.height, this.messageBoxVisible(), sidebarVisibility, noteListVisibility, this.props.sidebarWidth, this.props.noteListWidth);
|
||||
|
||||
const headerItems = [];
|
||||
|
||||
headerItems.push(CommandService.instance().commandToToolbarButton('toggleSidebar', { iconRotation: sidebarVisibility ? 0 : 90 }));
|
||||
headerItems.push(CommandService.instance().commandToToolbarButton('toggleNoteList', { iconRotation: noteListVisibility ? 0 : 90 }));
|
||||
headerItems.push(CommandService.instance().commandToToolbarButton('newNote'));
|
||||
headerItems.push(CommandService.instance().commandToToolbarButton('newTodo'));
|
||||
headerItems.push(CommandService.instance().commandToToolbarButton('newNotebook'));
|
||||
|
||||
headerItems.push({
|
||||
title: _('Code View'),
|
||||
iconName: 'fa-file-code ',
|
||||
enabled: !!notes.length,
|
||||
type: 'checkbox',
|
||||
checked: this.props.settingEditorCodeView,
|
||||
onClick: () => {
|
||||
// A bit of a hack, but for now don't allow changing code view
|
||||
// while a note is being saved as it will cause a problem with
|
||||
// TinyMCE because it won't have time to send its content before
|
||||
// being switch to the Code Editor.
|
||||
if (this.props.hasNotesBeingSaved) return;
|
||||
Setting.toggle('editor.codeView');
|
||||
},
|
||||
});
|
||||
|
||||
headerItems.push(CommandService.instance().commandToToolbarButton('toggleVisiblePanes'));
|
||||
|
||||
headerItems.push({
|
||||
title: _('Search...'),
|
||||
iconName: 'fa-search',
|
||||
onQuery: (query, fuzzy = false) => {
|
||||
CommandService.instance().execute('search', { query, fuzzy });
|
||||
},
|
||||
type: 'search',
|
||||
});
|
||||
const styles = this.styles(this.props.themeId, style.width, style.height, this.messageBoxVisible(), sidebarVisibility, noteListVisibility, this.props.sidebarWidth, this.props.noteListWidth);
|
||||
|
||||
if (!this.promptOnClose_) {
|
||||
this.promptOnClose_ = (answer, buttonType) => {
|
||||
this.promptOnClose_ = (answer:any, buttonType:any) => {
|
||||
return this.state.promptOptions.onClose(answer, buttonType);
|
||||
};
|
||||
}
|
||||
@@ -468,34 +582,33 @@ class MainScreenComponent extends React.Component {
|
||||
const noteContentPropertiesDialogOptions = this.state.noteContentPropertiesDialogOptions;
|
||||
const shareNoteDialogOptions = this.state.shareNoteDialogOptions;
|
||||
|
||||
const bodyEditor = this.props.settingEditorCodeView ? 'CodeMirror' : 'TinyMCE';
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<div style={modalLayerStyle}>{this.state.modalLayer.message}</div>
|
||||
|
||||
{noteContentPropertiesDialogOptions.visible && <NoteContentPropertiesDialog theme={this.props.theme} onClose={this.noteContentPropertiesDialog_close} text={noteContentPropertiesDialogOptions.text}/>}
|
||||
{notePropertiesDialogOptions.visible && <NotePropertiesDialog theme={this.props.theme} noteId={notePropertiesDialogOptions.noteId} onClose={this.notePropertiesDialog_close} onRevisionLinkClick={notePropertiesDialogOptions.onRevisionLinkClick} />}
|
||||
{shareNoteDialogOptions.visible && <ShareNoteDialog theme={this.props.theme} noteIds={shareNoteDialogOptions.noteIds} onClose={this.shareNoteDialog_close} />}
|
||||
{noteContentPropertiesDialogOptions.visible && <NoteContentPropertiesDialog markupLanguage={noteContentPropertiesDialogOptions.markupLanguage} themeId={this.props.themeId} onClose={this.noteContentPropertiesDialog_close} text={noteContentPropertiesDialogOptions.text}/>}
|
||||
{notePropertiesDialogOptions.visible && <NotePropertiesDialog themeId={this.props.themeId} noteId={notePropertiesDialogOptions.noteId} onClose={this.notePropertiesDialog_close} onRevisionLinkClick={notePropertiesDialogOptions.onRevisionLinkClick} />}
|
||||
{shareNoteDialogOptions.visible && <ShareNoteDialog themeId={this.props.themeId} noteIds={shareNoteDialogOptions.noteIds} onClose={this.shareNoteDialog_close} />}
|
||||
|
||||
<PromptDialog autocomplete={promptOptions && 'autocomplete' in promptOptions ? promptOptions.autocomplete : null} defaultValue={promptOptions && promptOptions.value ? promptOptions.value : ''} theme={this.props.theme} style={styles.prompt} onClose={this.promptOnClose_} label={promptOptions ? promptOptions.label : ''} description={promptOptions ? promptOptions.description : null} visible={!!this.state.promptOptions} buttons={promptOptions && 'buttons' in promptOptions ? promptOptions.buttons : null} inputType={promptOptions && 'inputType' in promptOptions ? promptOptions.inputType : null} />
|
||||
<PromptDialog autocomplete={promptOptions && 'autocomplete' in promptOptions ? promptOptions.autocomplete : null} defaultValue={promptOptions && promptOptions.value ? promptOptions.value : ''} themeId={this.props.themeId} style={styles.prompt} onClose={this.promptOnClose_} label={promptOptions ? promptOptions.label : ''} description={promptOptions ? promptOptions.description : null} visible={!!this.state.promptOptions} buttons={promptOptions && 'buttons' in promptOptions ? promptOptions.buttons : null} inputType={promptOptions && 'inputType' in promptOptions ? promptOptions.inputType : null} />
|
||||
|
||||
<Header style={styles.header} showBackButton={false} items={headerItems} />
|
||||
{messageComp}
|
||||
<SideBar style={styles.sideBar} />
|
||||
<VerticalResizer style={styles.verticalResizerSidebar} onDrag={this.sidebar_onDrag} />
|
||||
<NoteList style={styles.noteList} />
|
||||
<VerticalResizer style={styles.verticalResizerNotelist} onDrag={this.noteList_onDrag} />
|
||||
<NoteEditor bodyEditor={bodyEditor} style={styles.noteText} />
|
||||
<ResizableLayout
|
||||
width={this.state.width}
|
||||
height={styles.rowHeight}
|
||||
layout={this.state.layout}
|
||||
onResize={this.resizableLayout_resize}
|
||||
renderItem={this.resizableLayout_renderItem}
|
||||
/>
|
||||
{pluginDialog}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const mapStateToProps = (state:any) => {
|
||||
return {
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
settingEditorCodeView: state.settings['editor.codeView'],
|
||||
sidebarVisibility: state.sidebarVisibility,
|
||||
noteListVisibility: state.noteListVisibility,
|
||||
@@ -519,6 +632,4 @@ const mapStateToProps = state => {
|
||||
};
|
||||
};
|
||||
|
||||
const MainScreen = connect(mapStateToProps)(MainScreenComponent);
|
||||
|
||||
module.exports = { MainScreen };
|
||||
export default connect(mapStateToProps)(MainScreenComponent);
|
||||
@@ -8,7 +8,7 @@ const { time } = require('lib/time-utils');
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'editAlarm',
|
||||
label: () => _('Set alarm'),
|
||||
iconName: 'fa-clock',
|
||||
iconName: 'icon-alarm',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ const Folder = require('lib/models/Folder');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'newNotebook',
|
||||
name: 'newFolder',
|
||||
label: () => _('New notebook'),
|
||||
iconName: 'fa-book',
|
||||
};
|
||||
@@ -6,13 +6,12 @@ const { uuid } = require('lib/uuid.js');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'search',
|
||||
iconName: 'icon-search',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async ({ query, fuzzy }:any) => {
|
||||
console.info('RUNTIME', query);
|
||||
|
||||
execute: async ({ query }:any) => {
|
||||
if (!comp.searchId_) comp.searchId_ = uuid.create();
|
||||
|
||||
comp.props.dispatch({
|
||||
@@ -23,7 +22,6 @@ export const runtime = (comp:any):CommandRuntime => {
|
||||
query_pattern: query,
|
||||
query_folder_id: null,
|
||||
type_: BaseModel.TYPE_SEARCH,
|
||||
fuzzy: fuzzy,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ const { _ } = require('lib/locale');
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'setTags',
|
||||
label: () => _('Tags'),
|
||||
iconName: 'fa-tags',
|
||||
iconName: 'icon-tags',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
|
||||
@@ -4,7 +4,7 @@ const { _ } = require('lib/locale');
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'showNoteProperties',
|
||||
label: () => _('Note properties'),
|
||||
iconName: 'fa-info-circle',
|
||||
iconName: 'icon-info',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { CommandDeclaration, CommandRuntime } from '../../../lib/services/CommandService';
|
||||
const { _ } = require('lib/locale');
|
||||
const { stateUtils } = require('lib/reducer.js');
|
||||
const Setting = require('lib/models/Setting');
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'toggleEditors',
|
||||
label: () => _('Toggle editors'),
|
||||
iconName: 'fa-columns',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async (props:any) => {
|
||||
// A bit of a hack, but for now don't allow changing code view
|
||||
// while a note is being saved as it will cause a problem with
|
||||
// TinyMCE because it won't have time to send its content before
|
||||
// being switch to Ace Editor.
|
||||
if (props.hasNotesBeingSaved) return;
|
||||
Setting.toggle('editor.codeView');
|
||||
},
|
||||
isEnabled: (props:any):boolean => {
|
||||
return !props.hasNotesBeingSaved && props.selectedNoteIds.length === 1;
|
||||
},
|
||||
mapStateToProps: (state:any):any => {
|
||||
return {
|
||||
hasNotesBeingSaved: stateUtils.hasNotesBeingSaved(state),
|
||||
selectedNoteIds: state.selectedNoteIds,
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -4,7 +4,7 @@ const { _ } = require('lib/locale');
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'toggleVisiblePanes',
|
||||
label: () => _('Toggle editor layout'),
|
||||
iconName: 'fa-columns',
|
||||
iconName: 'icon-layout ',
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
|
||||
@@ -5,22 +5,21 @@ const { bridge } = require('electron').remote.require('./bridge');
|
||||
const NoteListUtils = require('./utils/NoteListUtils');
|
||||
|
||||
interface MultiNoteActionsProps {
|
||||
theme: number,
|
||||
themeId: number,
|
||||
selectedNoteIds: string[],
|
||||
notes: any[],
|
||||
dispatch: Function,
|
||||
watchedNoteFiles: string[],
|
||||
style: any,
|
||||
}
|
||||
|
||||
function styles_(props:MultiNoteActionsProps) {
|
||||
return buildStyle('MultiNoteActions', props.theme, (theme:any) => {
|
||||
return buildStyle('MultiNoteActions', props.themeId, (theme:any) => {
|
||||
return {
|
||||
root: {
|
||||
...props.style,
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'center',
|
||||
paddingTop: theme.marginTop,
|
||||
width: '100%',
|
||||
},
|
||||
itemList: {
|
||||
display: 'flex',
|
||||
|
||||
@@ -7,7 +7,7 @@ const Countable = require('countable');
|
||||
const markupLanguageUtils = require('lib/markupLanguageUtils');
|
||||
|
||||
interface NoteContentPropertiesDialogProps {
|
||||
theme: number,
|
||||
themeId: number,
|
||||
text: string,
|
||||
markupLanguage: number,
|
||||
onClose: Function,
|
||||
@@ -46,7 +46,9 @@ function formatReadTime(readTimeMinutes: number) {
|
||||
}
|
||||
|
||||
export default function NoteContentPropertiesDialog(props:NoteContentPropertiesDialogProps) {
|
||||
const theme = themeStyle(props.theme);
|
||||
|
||||
console.info('MMMMMMMMMMMM', props.markupLanguage);
|
||||
const theme = themeStyle(props.themeId);
|
||||
const tableBodyComps: JSX.Element[] = [];
|
||||
// For the source Markdown
|
||||
const [lines, setLines] = useState<number>(0);
|
||||
@@ -165,7 +167,7 @@ export default function NoteContentPropertiesDialog(props:NoteContentPropertiesD
|
||||
<div style={labelCompStyle}>
|
||||
{_('Read time: %s min', formatReadTime(strippedReadTime))}
|
||||
</div>
|
||||
<DialogButtonRow theme={props.theme} onClick={buttonRow_click} okButtonShow={false} cancelButtonLabel={_('Close')}/>
|
||||
<DialogButtonRow themeId={props.themeId} onClick={buttonRow_click} okButtonShow={false} cancelButtonLabel={_('Close')}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -48,7 +48,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
props_onChangeRef.current = props.onChange;
|
||||
const contentKeyHasChangedRef = useRef(false);
|
||||
contentKeyHasChangedRef.current = previousContentKey !== props.contentKey;
|
||||
const theme = themeStyle(props.theme);
|
||||
|
||||
const rootSize = useRootSize({ rootRef });
|
||||
|
||||
@@ -351,6 +350,8 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
}, [styles.editor.codeMirrorTheme]);
|
||||
|
||||
useEffect(() => {
|
||||
const theme = themeStyle(props.themeId);
|
||||
|
||||
const element = document.createElement('style');
|
||||
element.setAttribute('id', 'codemirrorStyle');
|
||||
document.head.appendChild(element);
|
||||
@@ -420,7 +421,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
return () => {
|
||||
document.head.removeChild(element);
|
||||
};
|
||||
}, [props.theme]);
|
||||
}, [props.themeId]);
|
||||
|
||||
const webview_domReady = useCallback(() => {
|
||||
setWebviewReady(true);
|
||||
@@ -549,7 +550,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
value={props.content}
|
||||
ref={editorRef}
|
||||
mode={props.contentMarkupLanguage === Note.MARKUP_LANGUAGE_HTML ? 'xml' : 'joplin-markdown'}
|
||||
theme={styles.editor.codeMirrorTheme}
|
||||
codeMirrorTheme={styles.editor.codeMirrorTheme}
|
||||
style={styles.editor}
|
||||
readOnly={props.visiblePanes.indexOf('editor') < 0}
|
||||
autoMatchBraces={Setting.value('editor.autoMatchingBraces')}
|
||||
@@ -580,7 +581,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
<div style={styles.root} ref={rootRef}>
|
||||
<div style={styles.rowToolbar}>
|
||||
<Toolbar
|
||||
theme={props.theme}
|
||||
themeId={props.themeId}
|
||||
dispatch={props.dispatch}
|
||||
disabled={editorReadOnly}
|
||||
/>
|
||||
|
||||
@@ -69,7 +69,7 @@ export interface EditorProps {
|
||||
value: string,
|
||||
mode: string,
|
||||
style: any,
|
||||
theme: any,
|
||||
codeMirrorTheme: any,
|
||||
readOnly: boolean,
|
||||
autoMatchBraces: boolean,
|
||||
keyMap: string,
|
||||
@@ -216,7 +216,7 @@ function Editor(props: EditorProps, ref: any) {
|
||||
const cmOptions = {
|
||||
value: props.value,
|
||||
screenReaderLabel: props.value,
|
||||
theme: props.theme,
|
||||
theme: props.codeMirrorTheme,
|
||||
mode: props.mode,
|
||||
readOnly: props.readOnly,
|
||||
autoCloseBrackets: props.autoMatchBraces,
|
||||
@@ -265,9 +265,9 @@ function Editor(props: EditorProps, ref: any) {
|
||||
|
||||
useEffect(() => {
|
||||
if (editor) {
|
||||
editor.setOption('theme', props.theme);
|
||||
editor.setOption('theme', props.codeMirrorTheme);
|
||||
}
|
||||
}, [props.theme]);
|
||||
}, [props.codeMirrorTheme]);
|
||||
|
||||
useEffect(() => {
|
||||
if (editor) {
|
||||
|
||||
@@ -2,22 +2,20 @@ import * as React from 'react';
|
||||
import CommandService from '../../../../lib/services/CommandService';
|
||||
|
||||
const ToolbarBase = require('../../../Toolbar.min.js');
|
||||
const { buildStyle, themeStyle } = require('lib/theme');
|
||||
const { buildStyle } = require('lib/theme');
|
||||
|
||||
interface ToolbarProps {
|
||||
theme: number,
|
||||
themeId: number,
|
||||
dispatch: Function,
|
||||
disabled: boolean,
|
||||
}
|
||||
|
||||
function styles_(props:ToolbarProps) {
|
||||
return buildStyle('CodeMirrorToolbar', props.theme, (/* theme:any*/) => {
|
||||
const theme = themeStyle(props.theme);
|
||||
return buildStyle('CodeMirrorToolbar', props.themeId, () => {
|
||||
return {
|
||||
root: {
|
||||
flex: 1,
|
||||
marginBottom: 0,
|
||||
borderTop: `1px solid ${theme.dividerColor}`,
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -29,6 +27,11 @@ export default function Toolbar(props:ToolbarProps) {
|
||||
const cmdService = CommandService.instance();
|
||||
|
||||
const toolbarItems = [
|
||||
cmdService.commandToToolbarButton('historyBackward'),
|
||||
cmdService.commandToToolbarButton('historyForward'),
|
||||
cmdService.commandToToolbarButton('startExternalEditing'),
|
||||
|
||||
{ type: 'separator' },
|
||||
cmdService.commandToToolbarButton('textBold'),
|
||||
cmdService.commandToToolbarButton('textItalic'),
|
||||
{ type: 'separator' },
|
||||
@@ -42,6 +45,8 @@ export default function Toolbar(props:ToolbarProps) {
|
||||
cmdService.commandToToolbarButton('textHeading'),
|
||||
cmdService.commandToToolbarButton('textHorizontalRule'),
|
||||
cmdService.commandToToolbarButton('insertDateTime'),
|
||||
|
||||
cmdService.commandToToolbarButton('toggleEditors'),
|
||||
];
|
||||
|
||||
return <ToolbarBase disabled={props.disabled} style={styles.root} items={toolbarItems} />;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { NoteBodyEditorProps } from '../../../utils/types';
|
||||
const { buildStyle } = require('lib/theme');
|
||||
|
||||
export default function styles(props: NoteBodyEditorProps) {
|
||||
return buildStyle('CodeMirror', props.theme, (theme: any) => {
|
||||
return buildStyle('CodeMirror', props.themeId, (theme: any) => {
|
||||
return {
|
||||
root: {
|
||||
position: 'relative',
|
||||
|
||||
@@ -3,15 +3,18 @@ import { useState, useEffect, useCallback, useRef, forwardRef, useImperativeHand
|
||||
import { ScrollOptions, ScrollOptionTypes, EditorCommand, NoteBodyEditorProps } from '../../utils/types';
|
||||
import { resourcesStatus, commandAttachFileToBody, handlePasteEvent } from '../../utils/resourceHandling';
|
||||
import useScroll from './utils/useScroll';
|
||||
import styles_ from './styles';
|
||||
import { menuItems, ContextMenuOptions, ContextMenuItemType } from '../../utils/contextMenu';
|
||||
import CommandService from '../../../../lib/services/CommandService';
|
||||
import CommandService, { ToolbarButtonInfo } from 'lib/services/CommandService';
|
||||
import ToggleEditorsButton, { Value as ToggleEditorsButtonValue } from '../../../ToggleEditorsButton/ToggleEditorsButton';
|
||||
import ToolbarButton from '../../../../gui/ToolbarButton/ToolbarButton';
|
||||
const { MarkupToHtml } = require('lib/joplin-renderer');
|
||||
const taboverride = require('taboverride');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { _, closestSupportedLocale } = require('lib/locale');
|
||||
const BaseItem = require('lib/models/BaseItem');
|
||||
const Resource = require('lib/models/Resource');
|
||||
const { themeStyle, buildStyle } = require('lib/theme');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { clipboard } = require('electron');
|
||||
const supportedLocales = require('./supportedLocales');
|
||||
|
||||
@@ -112,31 +115,6 @@ const joplinCommandToTinyMceCommands:JoplinCommandToTinyMceCommands = {
|
||||
'search': { name: 'SearchReplace' },
|
||||
};
|
||||
|
||||
function styles_(props:NoteBodyEditorProps) {
|
||||
return buildStyle('TinyMCE', props.theme, (/* theme:any */) => {
|
||||
return {
|
||||
disabledOverlay: {
|
||||
zIndex: 10,
|
||||
position: 'absolute',
|
||||
backgroundColor: 'white',
|
||||
opacity: 0.7,
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
padding: 20,
|
||||
paddingTop: 50,
|
||||
textAlign: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
rootStyle: {
|
||||
position: 'relative',
|
||||
...props.style,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
let loadedCssFiles_:string[] = [];
|
||||
let loadedJsFiles_:string[] = [];
|
||||
let dispatchDidUpdateIID_:any = null;
|
||||
@@ -170,7 +148,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
||||
editorRef.current = editor;
|
||||
|
||||
const styles = styles_(props);
|
||||
const theme = themeStyle(props.theme);
|
||||
// const theme = themeStyle(props.themeId);
|
||||
|
||||
const { scrollToPercent } = useScroll({ editor, onScroll: props.onScroll });
|
||||
|
||||
@@ -368,10 +346,17 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
||||
useEffect(() => {
|
||||
if (!editorReady) return () => {};
|
||||
|
||||
const theme = themeStyle(props.themeId);
|
||||
|
||||
const element = document.createElement('style');
|
||||
element.setAttribute('id', 'tinyMceStyle');
|
||||
document.head.appendChild(element);
|
||||
element.appendChild(document.createTextNode(`
|
||||
.joplin-tinymce .tox-editor-header {
|
||||
padding-left: ${styles.leftExtraToolbarContainer.width + styles.leftExtraToolbarContainer.padding * 2}px;
|
||||
padding-right: ${styles.rightExtraToolbarContainer.width + styles.rightExtraToolbarContainer.padding * 2}px;
|
||||
}
|
||||
|
||||
.tox .tox-toolbar,
|
||||
.tox .tox-toolbar__overflow,
|
||||
.tox .tox-toolbar__primary,
|
||||
@@ -388,8 +373,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
||||
}
|
||||
|
||||
.tox .tox-editor-header {
|
||||
border-top: 1px solid ${theme.dividerColor};
|
||||
border-bottom: 1px solid ${theme.dividerColor};
|
||||
border: none;
|
||||
}
|
||||
|
||||
.tox .tox-tbtn,
|
||||
@@ -401,8 +385,8 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
||||
.tox input,
|
||||
.tox .tox-label,
|
||||
.tox .tox-toolbar-label {
|
||||
color: ${theme.iconColor} !important;
|
||||
fill: ${theme.iconColor} !important;
|
||||
color: ${theme.color3} !important;
|
||||
fill: ${theme.color3} !important;
|
||||
}
|
||||
|
||||
.tox .tox-statusbar a,
|
||||
@@ -424,32 +408,59 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
||||
}
|
||||
|
||||
.tox .tox-tbtn:hover {
|
||||
background-color: ${theme.backgroundHover};
|
||||
color: ${theme.colorHover};
|
||||
fill: ${theme.colorHover};
|
||||
color: ${theme.colorHover3} !important;
|
||||
fill: ${theme.colorHover3} !important;
|
||||
background-color: ${theme.backgroundColorHover3}
|
||||
}
|
||||
|
||||
.tox .tox-tbtn {
|
||||
width: ${theme.toolbarHeight}px;
|
||||
height: ${theme.toolbarHeight}px;
|
||||
min-width: ${theme.toolbarHeight}px;
|
||||
min-height: ${theme.toolbarHeight}px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
.tox .tox-tbtn[aria-haspopup=true] {
|
||||
width: ${theme.toolbarHeight + 15}px;
|
||||
min-width: ${theme.toolbarHeight + 15}px;
|
||||
}
|
||||
|
||||
.tox .tox-tbtn > span,
|
||||
.tox .tox-tbtn:active > span,
|
||||
.tox .tox-tbtn:hover > span {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
.tox .tox-toolbar__primary,
|
||||
.tox .tox-toolbar__overflow {
|
||||
background: none;
|
||||
background-color: ${theme.backgroundColor3} !important;
|
||||
}
|
||||
|
||||
.tox-tinymce,
|
||||
.tox .tox-toolbar__group,
|
||||
.tox.tox-tinymce-aux .tox-toolbar__overflow,
|
||||
.tox .tox-dialog__footer {
|
||||
border-color: ${theme.dividerColor} !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.tox-tinymce {
|
||||
border-top: none !important;
|
||||
}
|
||||
|
||||
.joplin-tinymce .tox-toolbar__group {
|
||||
background-color: ${theme.backgroundColor3};
|
||||
padding-top: ${theme.toolbarPadding}px;
|
||||
padding-bottom: ${theme.toolbarPadding}px;
|
||||
}
|
||||
`));
|
||||
|
||||
return () => {
|
||||
document.head.removeChild(element);
|
||||
};
|
||||
}, [editorReady, props.theme]);
|
||||
}, [editorReady, props.themeId]);
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Enable or disable the editor
|
||||
@@ -499,6 +510,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
||||
menubar: false,
|
||||
relative_urls: false,
|
||||
branding: false,
|
||||
statusbar: false,
|
||||
target_list: false,
|
||||
table_resize_bars: false,
|
||||
language: ['en_US', 'en_GB'].includes(language) ? undefined : language,
|
||||
@@ -706,6 +718,8 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
||||
// The fix would be to make allAssets() return a name and a version for each asset. Then the loading
|
||||
// code would check this and either append the CSS or replace.
|
||||
|
||||
const theme = themeStyle(props.themeId);
|
||||
|
||||
let docHead_:any = null;
|
||||
|
||||
function docHead() {
|
||||
@@ -1039,12 +1053,64 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
function renderExtraToolbarButton(key:string, info:ToolbarButtonInfo) {
|
||||
return <ToolbarButton
|
||||
key={key}
|
||||
themeId={props.themeId}
|
||||
toolbarButtonInfo={info}
|
||||
/>;
|
||||
}
|
||||
|
||||
const leftButtonCommandNames = ['historyBackward', 'historyForward', 'startExternalEditing'];
|
||||
|
||||
function renderLeftExtraToolbarButtons() {
|
||||
const buttons = [];
|
||||
for (const buttonName in props.noteToolbarButtonInfos) {
|
||||
if (!leftButtonCommandNames.includes(buttonName)) continue;
|
||||
const info = props.noteToolbarButtonInfos[buttonName];
|
||||
buttons.push(renderExtraToolbarButton(buttonName, info));
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={styles.leftExtraToolbarContainer}>
|
||||
{buttons}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderRightExtraToolbarButtons() {
|
||||
const buttons = [];
|
||||
for (const buttonName in props.noteToolbarButtonInfos) {
|
||||
if (leftButtonCommandNames.includes(buttonName)) continue;
|
||||
const info = props.noteToolbarButtonInfos[buttonName];
|
||||
|
||||
if (buttonName === 'toggleEditors') {
|
||||
buttons.push(<ToggleEditorsButton
|
||||
key={buttonName}
|
||||
value={ToggleEditorsButtonValue.RichText}
|
||||
themeId={props.themeId}
|
||||
toolbarButtonInfo={info}
|
||||
/>);
|
||||
} else {
|
||||
buttons.push(renderExtraToolbarButton(buttonName, info));
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={styles.rightExtraToolbarContainer}>
|
||||
{buttons}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Currently we don't handle resource "auto" and "manual" mode with TinyMCE
|
||||
// as it is quite complex and probably rarely used.
|
||||
function renderDisabledOverlay() {
|
||||
const status = resourcesStatus(props.resourceInfos);
|
||||
if (status === 'ready' && !draggingStarted) return null;
|
||||
|
||||
const theme = themeStyle(props.themeId);
|
||||
|
||||
const message = draggingStarted ? _('Drop notes or files here') : _('Please wait for all attachments to be downloaded and decrypted. You may also switch to %s to edit the note.', _('Code View'));
|
||||
const statusComp = draggingStarted ? null : <p style={theme.textStyleMinor}>{`Status: ${status}`}</p>;
|
||||
return (
|
||||
@@ -1056,8 +1122,10 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={styles.rootStyle}>
|
||||
<div style={styles.rootStyle} className="joplin-tinymce">
|
||||
{renderDisabledOverlay()}
|
||||
{renderLeftExtraToolbarButtons()}
|
||||
{renderRightExtraToolbarButtons()}
|
||||
<div style={{ width: '100%', height: '100%' }} id={rootIdRef.current}/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { NoteBodyEditorProps } from '../../../utils/types';
|
||||
const { buildStyle } = require('lib/theme');
|
||||
|
||||
export default function styles(props:NoteBodyEditorProps) {
|
||||
return buildStyle(['TinyMCE', props.style.width, props.style.height], props.themeId, (theme:any) => {
|
||||
const extraToolbarContainer = {
|
||||
backgroundColor: theme.backgroundColor3,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
position: 'absolute',
|
||||
height: theme.toolbarHeight,
|
||||
zIndex: 2,
|
||||
top: 0,
|
||||
padding: theme.toolbarPadding,
|
||||
};
|
||||
|
||||
return {
|
||||
disabledOverlay: {
|
||||
zIndex: 10,
|
||||
position: 'absolute',
|
||||
backgroundColor: 'white',
|
||||
opacity: 0.7,
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
padding: 20,
|
||||
paddingTop: 50,
|
||||
textAlign: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
rootStyle: {
|
||||
position: 'relative',
|
||||
width: props.style.width,
|
||||
height: props.style.height,
|
||||
},
|
||||
leftExtraToolbarContainer: {
|
||||
...extraToolbarContainer,
|
||||
width: 80,
|
||||
left: 0,
|
||||
},
|
||||
rightExtraToolbarContainer: {
|
||||
...extraToolbarContainer,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
width: 70,
|
||||
right: 0,
|
||||
paddingRight: theme.mainPadding,
|
||||
},
|
||||
extraToolbarButton: {
|
||||
display: 'flex',
|
||||
border: 'none',
|
||||
background: 'none',
|
||||
},
|
||||
extraToolbarButtonIcon: {
|
||||
fontSize: theme.toolbarIconSize,
|
||||
color: theme.color3,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -13,13 +13,18 @@ import useMessageHandler from './utils/useMessageHandler';
|
||||
import useWindowCommandHandler from './utils/useWindowCommandHandler';
|
||||
import useDropHandler from './utils/useDropHandler';
|
||||
import useMarkupToHtml from './utils/useMarkupToHtml';
|
||||
import useNoteToolbarButtons from './utils/useNoteToolbarButtons';
|
||||
import useFormNote, { OnLoadEvent } from './utils/useFormNote';
|
||||
import useFolder from './utils/useFolder';
|
||||
import styles_ from './styles';
|
||||
import { NoteEditorProps, FormNote, ScrollOptions, ScrollOptionTypes, OnChangeEvent, NoteBodyEditorProps } from './utils/types';
|
||||
import ResourceEditWatcher from '../../lib/services/ResourceEditWatcher/index';
|
||||
import CommandService from '../../lib/services/CommandService';
|
||||
import CommandService from 'lib/services/CommandService';
|
||||
import ToolbarButton from '../ToolbarButton/ToolbarButton';
|
||||
import Button, { ButtonLevel } from '../Button/Button';
|
||||
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { substrWithEllipsis } = require('lib/string-utils');
|
||||
const NoteSearchBar = require('../NoteSearchBar.min.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
@@ -70,6 +75,8 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
const formNoteRef = useRef<FormNote>();
|
||||
formNoteRef.current = { ...formNote };
|
||||
|
||||
const formNoteFolder = useFolder({ folderId: formNote.parent_id });
|
||||
|
||||
const {
|
||||
localSearch,
|
||||
onChange: localSearch_change,
|
||||
@@ -133,17 +140,17 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
return formNote.saveActionQueue.waitForAllDone();
|
||||
}
|
||||
|
||||
const markupToHtml = useMarkupToHtml({ themeId: props.theme, customCss: props.customCss });
|
||||
const markupToHtml = useMarkupToHtml({ themeId: props.themeId, customCss: props.customCss });
|
||||
|
||||
const allAssets = useCallback(async (markupLanguage: number): Promise<any[]> => {
|
||||
const theme = themeStyle(props.theme);
|
||||
const theme = themeStyle(props.themeId);
|
||||
|
||||
const markupToHtml = markupLanguageUtils.newMarkupToHtml({
|
||||
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
|
||||
});
|
||||
|
||||
return markupToHtml.allAssets(markupLanguage, theme);
|
||||
}, [props.theme]);
|
||||
}, [props.themeId]);
|
||||
|
||||
const handleProvisionalFlag = useCallback(() => {
|
||||
if (props.isProvisional) {
|
||||
@@ -331,25 +338,49 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
}
|
||||
|
||||
function renderNoteToolbar() {
|
||||
// const theme = themeStyle(props.themeId);
|
||||
|
||||
const toolbarStyle = {
|
||||
marginBottom: 0,
|
||||
// paddingTop: theme.mainPadding,
|
||||
// paddingBottom: theme.mainPadding,
|
||||
};
|
||||
|
||||
return <NoteToolbar
|
||||
theme={props.theme}
|
||||
themeId={props.themeId}
|
||||
note={formNote}
|
||||
style={toolbarStyle}
|
||||
/>;
|
||||
}
|
||||
|
||||
function renderTagButton() {
|
||||
const info = CommandService.instance().commandToToolbarButton('setTags');
|
||||
return <ToolbarButton
|
||||
themeId={props.themeId}
|
||||
toolbarButtonInfo={info}
|
||||
/>;
|
||||
}
|
||||
|
||||
function renderTagBar() {
|
||||
return props.selectedNoteTags.length ? <TagList items={props.selectedNoteTags} /> : null;
|
||||
const theme = themeStyle(props.themeId);
|
||||
let control = null;
|
||||
if (!props.selectedNoteTags.length) {
|
||||
const noteIds = [formNote.id];
|
||||
control = <span onClick={() => { CommandService.instance().execute('setTags', { noteIds }); }} style={theme.clickableTextStyle}>Click to add some tags...</span>;
|
||||
} else {
|
||||
control = <TagList items={props.selectedNoteTags} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ paddingLeft: 8 }}>{control}</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderTitleBar() {
|
||||
const theme = themeStyle(props.themeId);
|
||||
const titleBarDate = <span style={styles.titleDate}>{time.formatMsToLocal(formNote.user_updated_time)}</span>;
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', height: theme.topRowHeight }}>
|
||||
<input
|
||||
type="text"
|
||||
ref={titleInputRef}
|
||||
@@ -360,6 +391,7 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
value={formNote.title}
|
||||
/>
|
||||
{titleBarDate}
|
||||
{renderNoteToolbar()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -381,7 +413,7 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
markupToHtml: markupToHtml,
|
||||
allAssets: allAssets,
|
||||
disabled: false,
|
||||
theme: props.theme,
|
||||
themeId: props.themeId,
|
||||
dispatch: props.dispatch,
|
||||
noteToolbar: null,// renderNoteToolbar(),
|
||||
onScroll: onScroll,
|
||||
@@ -391,6 +423,7 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
keyboardMode: Setting.value('editor.keyboardMode'),
|
||||
locale: Setting.value('locale'),
|
||||
onDrop: onDrop,
|
||||
noteToolbarButtonInfos: useNoteToolbarButtons(),
|
||||
};
|
||||
|
||||
let editor = null;
|
||||
@@ -414,10 +447,10 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
}, []);
|
||||
|
||||
if (showRevisions) {
|
||||
const theme = themeStyle(props.theme);
|
||||
const theme = themeStyle(props.themeId);
|
||||
|
||||
const revStyle = {
|
||||
...props.style,
|
||||
const revStyle:any = {
|
||||
// ...props.style,
|
||||
display: 'inline-flex',
|
||||
padding: theme.margin,
|
||||
verticalAlign: 'top',
|
||||
@@ -433,19 +466,18 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
|
||||
if (props.selectedNoteIds.length > 1) {
|
||||
return <MultiNoteActions
|
||||
theme={props.theme}
|
||||
themeId={props.themeId}
|
||||
selectedNoteIds={props.selectedNoteIds}
|
||||
notes={props.notes}
|
||||
dispatch={props.dispatch}
|
||||
watchedNoteFiles={props.watchedNoteFiles}
|
||||
style={props.style}
|
||||
/>;
|
||||
}
|
||||
|
||||
function renderSearchBar() {
|
||||
if (!showLocalSearch) return false;
|
||||
|
||||
const theme = themeStyle(props.theme);
|
||||
const theme = themeStyle(props.themeId);
|
||||
|
||||
return (
|
||||
<NoteSearchBar
|
||||
@@ -479,6 +511,30 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function renderSearchInfo() {
|
||||
if (formNoteFolder && ['Search', 'Tag', 'SmartFilter'].includes(props.notesParentType)) {
|
||||
return (
|
||||
<div style={{ paddingTop: 10, paddingBottom: 10 }}>
|
||||
<Button
|
||||
iconName="icon-notebooks"
|
||||
level={ButtonLevel.Primary}
|
||||
title={_('In: %s', substrWithEllipsis(formNoteFolder.title, 0, 100))}
|
||||
onClick={() => {
|
||||
props.dispatch({
|
||||
type: 'FOLDER_AND_NOTE_SELECT',
|
||||
folderId: formNoteFolder.id,
|
||||
noteId: formNote.id,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<div style={{ flex: 1 }}></div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (formNote.encryption_applied || !formNote.id || !props.noteId) {
|
||||
return renderNoNotes(styles.root);
|
||||
}
|
||||
@@ -488,15 +544,17 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
||||
{renderResourceWatchingNotification()}
|
||||
{renderTitleBar()}
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
{renderNoteToolbar()}{renderTagBar()}
|
||||
</div>
|
||||
{renderSearchInfo()}
|
||||
<div style={{ display: 'flex', flex: 1 }}>
|
||||
{editor}
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
{renderSearchBar()}
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', height: 40 }}>
|
||||
{renderTagButton()}
|
||||
{renderTagBar()}
|
||||
</div>
|
||||
{wysiwygBanner}
|
||||
</div>
|
||||
</div>
|
||||
@@ -518,7 +576,7 @@ const mapStateToProps = (state: any) => {
|
||||
isProvisional: state.provisionalNoteIds.includes(noteId),
|
||||
editorNoteStatuses: state.editorNoteStatuses,
|
||||
syncStarted: state.syncStarted,
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
watchedNoteFiles: state.watchedNoteFiles,
|
||||
notesParentType: state.notesParentType,
|
||||
selectedNoteTags: state.selectedNoteTags,
|
||||
|
||||
@@ -28,57 +28,57 @@ const declarations:CommandDeclaration[] = [
|
||||
{
|
||||
name: 'textBold',
|
||||
label: () => _('Bold'),
|
||||
iconName: 'fa-bold',
|
||||
iconName: 'icon-bold',
|
||||
},
|
||||
{
|
||||
name: 'textItalic',
|
||||
label: () => _('Italic'),
|
||||
iconName: 'fa-italic',
|
||||
iconName: 'icon-italic',
|
||||
},
|
||||
{
|
||||
name: 'textLink',
|
||||
label: () => _('Hyperlink'),
|
||||
iconName: 'fa-link',
|
||||
iconName: 'icon-link',
|
||||
},
|
||||
{
|
||||
name: 'textCode',
|
||||
label: () => _('Code'),
|
||||
iconName: 'fa-code',
|
||||
iconName: 'icon-code',
|
||||
},
|
||||
{
|
||||
name: 'attachFile',
|
||||
label: () => _('Attach file'),
|
||||
iconName: 'fa-paperclip',
|
||||
iconName: 'icon-attachment',
|
||||
},
|
||||
{
|
||||
name: 'textNumberedList',
|
||||
label: () => _('Numbered List'),
|
||||
iconName: 'fa-list-ol',
|
||||
iconName: 'icon-numbered-list',
|
||||
},
|
||||
{
|
||||
name: 'textBulletedList',
|
||||
label: () => _('Bulleted List'),
|
||||
iconName: 'fa-list-ul',
|
||||
iconName: 'icon-bulleted-list',
|
||||
},
|
||||
{
|
||||
name: 'textCheckbox',
|
||||
label: () => _('Checkbox'),
|
||||
iconName: 'fa-check-square',
|
||||
iconName: 'icon-to-do-list',
|
||||
},
|
||||
{
|
||||
name: 'textHeading',
|
||||
label: () => _('Heading'),
|
||||
iconName: 'fa-heading',
|
||||
iconName: 'icon-heading',
|
||||
},
|
||||
{
|
||||
name: 'textHorizontalRule',
|
||||
label: () => _('Horizontal Rule'),
|
||||
iconName: 'fa-ellipsis-h',
|
||||
iconName: 'fas fa-ellipsis-h',
|
||||
},
|
||||
{
|
||||
name: 'insertDateTime',
|
||||
label: () => _('Insert Date Time'),
|
||||
iconName: 'fa-calendar-plus',
|
||||
iconName: 'icon-add-date',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -3,16 +3,18 @@ import { NoteEditorProps } from '../utils/types';
|
||||
const { buildStyle } = require('lib/theme');
|
||||
|
||||
export default function styles(props: NoteEditorProps) {
|
||||
return buildStyle(['NoteEditor', props.style.width, props.style.height], props.theme, (theme: any) => {
|
||||
return buildStyle(['NoteEditor'], props.themeId, (theme: any) => {
|
||||
return {
|
||||
root: {
|
||||
...props.style,
|
||||
// ...props.style,
|
||||
boxSizing: 'border-box',
|
||||
paddingLeft: 10,
|
||||
paddingTop: 5,
|
||||
paddingLeft: theme.mainPadding,
|
||||
paddingTop: 0,
|
||||
borderLeftWidth: 1,
|
||||
borderLeftColor: theme.dividerColor,
|
||||
borderLeftStyle: 'solid',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
titleInput: {
|
||||
flex: 1,
|
||||
@@ -20,16 +22,15 @@ export default function styles(props: NoteEditorProps) {
|
||||
paddingTop: 5,
|
||||
minHeight: 35,
|
||||
boxSizing: 'border-box',
|
||||
fontWeight: 'bold',
|
||||
paddingBottom: 5,
|
||||
paddingLeft: 8,
|
||||
paddingLeft: 0,
|
||||
paddingRight: 8,
|
||||
marginLeft: 5,
|
||||
// marginRight: theme.paddingLeft,
|
||||
color: theme.textStyle.color,
|
||||
fontSize: theme.textStyle.fontSize * 1.25,
|
||||
fontSize: Math.round(theme.textStyle.fontSize * 1.5),
|
||||
backgroundColor: theme.backgroundColor,
|
||||
border: '1px solid',
|
||||
borderColor: theme.dividerColor,
|
||||
border: 'none',
|
||||
},
|
||||
warningBanner: {
|
||||
background: theme.warningBackgroundColor,
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import AsyncActionQueue from '../../../lib/AsyncActionQueue';
|
||||
import { ToolbarButtonInfo } from 'lib/services/CommandService';
|
||||
|
||||
export interface ToolbarButtonInfos {
|
||||
[key:string]: ToolbarButtonInfo;
|
||||
}
|
||||
|
||||
export interface NoteEditorProps {
|
||||
style: any;
|
||||
// style: any;
|
||||
noteId: string;
|
||||
theme: number;
|
||||
themeId: number;
|
||||
dispatch: Function;
|
||||
selectedNoteIds: string[];
|
||||
notes: any[];
|
||||
@@ -29,7 +34,7 @@ export interface NoteEditorProps {
|
||||
export interface NoteBodyEditorProps {
|
||||
style: any;
|
||||
ref: any,
|
||||
theme: number;
|
||||
themeId: number;
|
||||
content: string,
|
||||
contentKey: string,
|
||||
contentMarkupLanguage: number,
|
||||
@@ -51,6 +56,7 @@ export interface NoteBodyEditorProps {
|
||||
resourceInfos: ResourceInfos,
|
||||
locale: string,
|
||||
onDrop: Function,
|
||||
noteToolbarButtonInfos: ToolbarButtonInfos,
|
||||
}
|
||||
|
||||
export interface FormNote {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
const Folder = require('lib/models/Folder');
|
||||
|
||||
interface HookDependencies {
|
||||
folderId: string,
|
||||
}
|
||||
|
||||
export default function(dependencies:HookDependencies) {
|
||||
const { folderId } = dependencies;
|
||||
const [folder, setFolder] = useState(null);
|
||||
|
||||
useEffect(function() {
|
||||
let cancelled = false;
|
||||
|
||||
async function loadFolder() {
|
||||
const f = await Folder.load(folderId);
|
||||
if (cancelled) return;
|
||||
setFolder(f);
|
||||
}
|
||||
|
||||
loadFolder();
|
||||
|
||||
return function() {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [folderId]);
|
||||
|
||||
return folder;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import CommandService, { ToolbarButtonInfo } from 'lib/services/CommandService';
|
||||
|
||||
interface ToolbarButtonInfos {
|
||||
[key:string]: ToolbarButtonInfo;
|
||||
}
|
||||
|
||||
export default function useNoteToolbarButtons():ToolbarButtonInfos {
|
||||
const [noteToolbarButtons, setNoteToolbarButtons] = useState<ToolbarButtonInfos>({});
|
||||
|
||||
function update() {
|
||||
const buttonNames = ['historyBackward', 'historyForward', 'toggleEditors', 'startExternalEditing'];
|
||||
const output:ToolbarButtonInfos = {};
|
||||
|
||||
for (const buttonName of buttonNames) {
|
||||
output[buttonName] = CommandService.instance().commandToToolbarButton(buttonName);
|
||||
}
|
||||
|
||||
setNoteToolbarButtons(output);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
update();
|
||||
|
||||
CommandService.instance().on('commandsEnabledStateChange', update);
|
||||
|
||||
return () => {
|
||||
CommandService.instance().off('commandsEnabledStateChange', update);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return noteToolbarButtons;
|
||||
}
|
||||
+100
-56
@@ -12,11 +12,18 @@ const Setting = require('lib/models/Setting');
|
||||
const NoteListUtils = require('../utils/NoteListUtils');
|
||||
const NoteListItem = require('../NoteListItem').default;
|
||||
const CommandService = require('lib/services/CommandService.js').default;
|
||||
const styled = require('styled-components').default;
|
||||
|
||||
const commands = [
|
||||
require('./commands/focusElementNoteList'),
|
||||
];
|
||||
|
||||
const StyledRoot = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: ${(props:any) => props.theme.backgroundColor3};
|
||||
`;
|
||||
|
||||
class NoteListComponent extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
@@ -27,12 +34,15 @@ class NoteListComponent extends React.Component {
|
||||
|
||||
this.state = {
|
||||
dragOverTargetNoteIndex: null,
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
|
||||
this.noteListRef = React.createRef();
|
||||
this.itemListRef = React.createRef();
|
||||
this.itemAnchorRefs_ = {};
|
||||
|
||||
this.itemRenderer = this.itemRenderer.bind(this);
|
||||
this.renderItem = this.renderItem.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
this.noteItem_titleClick = this.noteItem_titleClick.bind(this);
|
||||
this.noteItem_noteDragOver = this.noteItem_noteDragOver.bind(this);
|
||||
@@ -43,12 +53,13 @@ class NoteListComponent extends React.Component {
|
||||
this.registerGlobalDragEndEvent_ = this.registerGlobalDragEndEvent_.bind(this);
|
||||
this.unregisterGlobalDragEndEvent_ = this.unregisterGlobalDragEndEvent_.bind(this);
|
||||
this.itemContextMenu = this.itemContextMenu.bind(this);
|
||||
this.resizableLayout_resize = this.resizableLayout_resize.bind(this);
|
||||
}
|
||||
|
||||
style() {
|
||||
if (this.styleCache_ && this.styleCache_[this.props.theme]) return this.styleCache_[this.props.theme];
|
||||
if (this.styleCache_ && this.styleCache_[this.props.themeId]) return this.styleCache_[this.props.themeId];
|
||||
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const style = {
|
||||
root: {
|
||||
@@ -85,12 +96,12 @@ class NoteListComponent extends React.Component {
|
||||
};
|
||||
|
||||
this.styleCache_ = {};
|
||||
this.styleCache_[this.props.theme] = style;
|
||||
this.styleCache_[this.props.themeId] = style;
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
itemContextMenu(event) {
|
||||
itemContextMenu(event:any) {
|
||||
const currentItemId = event.currentTarget.getAttribute('data-id');
|
||||
if (!currentItemId) return;
|
||||
|
||||
@@ -128,11 +139,11 @@ class NoteListComponent extends React.Component {
|
||||
document.removeEventListener('dragend', this.onGlobalDrop_);
|
||||
}
|
||||
|
||||
dragTargetNoteIndex_(event) {
|
||||
return Math.abs(Math.round((event.clientY - this.itemListRef.current.offsetTop()) / this.itemHeight));
|
||||
dragTargetNoteIndex_(event:any) {
|
||||
return Math.abs(Math.round((event.clientY - this.itemListRef.current.offsetTop() + this.itemListRef.current.offsetScroll()) / this.itemHeight));
|
||||
}
|
||||
|
||||
noteItem_noteDragOver(event) {
|
||||
noteItem_noteDragOver(event:any) {
|
||||
if (this.props.notesParentType !== 'Folder') return;
|
||||
|
||||
const dt = event.dataTransfer;
|
||||
@@ -146,7 +157,7 @@ class NoteListComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
async noteItem_noteDrop(event) {
|
||||
async noteItem_noteDrop(event:any) {
|
||||
if (this.props.notesParentType !== 'Folder') return;
|
||||
|
||||
if (this.props.noteSortOrder !== 'order') {
|
||||
@@ -172,7 +183,7 @@ class NoteListComponent extends React.Component {
|
||||
}
|
||||
|
||||
|
||||
async noteItem_checkboxClick(event, item) {
|
||||
async noteItem_checkboxClick(event:any, item:any) {
|
||||
const checked = event.target.checked;
|
||||
const newNote = {
|
||||
id: item.id,
|
||||
@@ -182,7 +193,7 @@ class NoteListComponent extends React.Component {
|
||||
eventManager.emit('todoToggle', { noteId: item.id, note: newNote });
|
||||
}
|
||||
|
||||
async noteItem_titleClick(event, item) {
|
||||
async noteItem_titleClick(event:any, item:any) {
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
event.preventDefault();
|
||||
this.props.dispatch({
|
||||
@@ -203,7 +214,7 @@ class NoteListComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
noteItem_dragStart(event) {
|
||||
noteItem_dragStart(event:any) {
|
||||
let noteIds = [];
|
||||
|
||||
// Here there is two cases:
|
||||
@@ -223,7 +234,7 @@ class NoteListComponent extends React.Component {
|
||||
event.dataTransfer.setData('text/x-jop-note-ids', JSON.stringify(noteIds));
|
||||
}
|
||||
|
||||
itemRenderer(item, index) {
|
||||
renderItem(item:any, index:number) {
|
||||
const highlightedWords = () => {
|
||||
if (this.props.notesParentType === 'Search') {
|
||||
const query = BaseModel.byId(this.props.searches, this.props.selectedSearchId);
|
||||
@@ -240,11 +251,12 @@ class NoteListComponent extends React.Component {
|
||||
return <NoteListItem
|
||||
ref={ref}
|
||||
key={item.id}
|
||||
style={this.style(this.props.theme)}
|
||||
style={this.style()}
|
||||
item={item}
|
||||
index={index}
|
||||
theme={this.props.theme}
|
||||
width={this.props.style.width}
|
||||
themeId={this.props.themeId}
|
||||
width={this.state.width}
|
||||
height={this.itemHeight}
|
||||
dragItemIndex={this.state.dragOverTargetNoteIndex}
|
||||
highlightedWords={highlightedWords()}
|
||||
isProvisional={this.props.provisionalNoteIds.includes(item.id)}
|
||||
@@ -260,12 +272,12 @@ class NoteListComponent extends React.Component {
|
||||
/>;
|
||||
}
|
||||
|
||||
itemAnchorRef(itemId) {
|
||||
itemAnchorRef(itemId:string) {
|
||||
if (this.itemAnchorRefs_[itemId] && this.itemAnchorRefs_[itemId].current) return this.itemAnchorRefs_[itemId].current;
|
||||
return null;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
componentDidUpdate(prevProps:any) {
|
||||
if (prevProps.selectedNoteIds !== this.props.selectedNoteIds && this.props.selectedNoteIds.length === 1) {
|
||||
const id = this.props.selectedNoteIds[0];
|
||||
const doRefocus = this.props.notes.length < prevProps.notes.length;
|
||||
@@ -281,9 +293,13 @@ class NoteListComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prevProps.visible !== this.props.visible) {
|
||||
this.updateSizeState();
|
||||
}
|
||||
}
|
||||
|
||||
scrollNoteIndex_(keyCode, ctrlKey, metaKey, noteIndex) {
|
||||
scrollNoteIndex_(keyCode:any, ctrlKey:any, metaKey:any, noteIndex:any) {
|
||||
|
||||
if (keyCode === 33) {
|
||||
// Page Up
|
||||
@@ -314,7 +330,7 @@ class NoteListComponent extends React.Component {
|
||||
return noteIndex;
|
||||
}
|
||||
|
||||
async onKeyDown(event) {
|
||||
async onKeyDown(event:any) {
|
||||
const keyCode = event.keyCode;
|
||||
const noteIds = this.props.selectedNoteIds;
|
||||
|
||||
@@ -350,7 +366,7 @@ class NoteListComponent extends React.Component {
|
||||
event.preventDefault();
|
||||
|
||||
const notes = BaseModel.modelsByIds(this.props.notes, noteIds);
|
||||
const todos = notes.filter(n => !!n.is_todo);
|
||||
const todos = notes.filter((n:any) => !!n.is_todo);
|
||||
if (!todos.length) return;
|
||||
|
||||
for (let i = 0; i < todos.length; i++) {
|
||||
@@ -382,7 +398,7 @@ class NoteListComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
focusNoteId_(noteId) {
|
||||
focusNoteId_(noteId:string) {
|
||||
// - We need to focus the item manually otherwise focus might be lost when the
|
||||
// list is scrolled and items within it are being rebuilt.
|
||||
// - We need to use an interval because when leaving the arrow pressed, the rendering
|
||||
@@ -401,56 +417,86 @@ class NoteListComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
updateSizeState() {
|
||||
this.setState({
|
||||
width: this.noteListRef.current.clientWidth,
|
||||
height: this.noteListRef.current.clientHeight,
|
||||
});
|
||||
}
|
||||
|
||||
resizableLayout_resize() {
|
||||
this.updateSizeState();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.resizableLayoutEventEmitter.on('resize', this.resizableLayout_resize);
|
||||
this.updateSizeState();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.focusItemIID_) {
|
||||
clearInterval(this.focusItemIID_);
|
||||
this.focusItemIID_ = null;
|
||||
}
|
||||
|
||||
this.props.resizableLayoutEventEmitter.off('resize', this.resizableLayout_resize);
|
||||
|
||||
CommandService.instance().componentUnregisterCommands(commands);
|
||||
}
|
||||
|
||||
renderEmptyList() {
|
||||
if (this.props.notes.length) return null;
|
||||
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const padding = 10;
|
||||
const emptyDivStyle = {
|
||||
padding: `${padding}px`,
|
||||
fontSize: theme.fontSize,
|
||||
color: theme.color,
|
||||
backgroundColor: theme.backgroundColor,
|
||||
fontFamily: theme.fontFamily,
|
||||
};
|
||||
// emptyDivStyle.width = emptyDivStyle.width - padding * 2;
|
||||
// emptyDivStyle.height = emptyDivStyle.height - padding * 2;
|
||||
return <div style={emptyDivStyle}>{this.props.folders.length ? _('No notes in here. Create one by clicking on "New note".') : _('There is currently no notebook. Create one by clicking on "New notebook".')}</div>;
|
||||
}
|
||||
|
||||
renderItemList(style:any) {
|
||||
if (!this.props.notes.length) return null;
|
||||
|
||||
return (
|
||||
<ItemList
|
||||
ref={this.itemListRef}
|
||||
disabled={this.props.isInsertingNotes}
|
||||
itemHeight={this.style().listItem.height}
|
||||
className={'note-list'}
|
||||
items={this.props.notes}
|
||||
style={style}
|
||||
itemRenderer={this.renderItem}
|
||||
onKeyDown={this.onKeyDown}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const style = this.props.style;
|
||||
if (!this.props.size) throw new Error('props.size is required');
|
||||
|
||||
if (!this.props.notes.length) {
|
||||
const padding = 10;
|
||||
const emptyDivStyle = Object.assign(
|
||||
{
|
||||
padding: `${padding}px`,
|
||||
fontSize: theme.fontSize,
|
||||
color: theme.color,
|
||||
backgroundColor: theme.backgroundColor,
|
||||
fontFamily: theme.fontFamily,
|
||||
},
|
||||
style
|
||||
);
|
||||
emptyDivStyle.width = emptyDivStyle.width - padding * 2;
|
||||
emptyDivStyle.height = emptyDivStyle.height - padding * 2;
|
||||
return <div style={emptyDivStyle}>{this.props.folders.length ? _('No notes in here. Create one by clicking on "New note".') : _('There is currently no notebook. Create one by clicking on "New notebook".')}</div>;
|
||||
}
|
||||
|
||||
return <ItemList
|
||||
ref={this.itemListRef}
|
||||
disabled={this.props.isInsertingNotes}
|
||||
itemHeight={this.style(this.props.theme).listItem.height}
|
||||
className={'note-list'}
|
||||
items={this.props.notes}
|
||||
style={style}
|
||||
itemRenderer={this.itemRenderer}
|
||||
onKeyDown={this.onKeyDown}
|
||||
/>;
|
||||
return (
|
||||
<StyledRoot ref={this.noteListRef}>
|
||||
{this.renderEmptyList()}
|
||||
{this.renderItemList(this.props.size)}
|
||||
</StyledRoot>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const mapStateToProps = (state:any) => {
|
||||
return {
|
||||
notes: state.notes,
|
||||
folders: state.folders,
|
||||
selectedNoteIds: state.selectedNoteIds,
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
notesParentType: state.notesParentType,
|
||||
searches: state.searches,
|
||||
selectedSearchId: state.selectedSearchId,
|
||||
@@ -462,6 +508,4 @@ const mapStateToProps = state => {
|
||||
};
|
||||
};
|
||||
|
||||
const NoteList = connect(mapStateToProps)(NoteListComponent);
|
||||
|
||||
module.exports = { NoteList };
|
||||
export default connect(mapStateToProps)(NoteListComponent);
|
||||
@@ -0,0 +1,58 @@
|
||||
import * as React from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import SearchBar from '../SearchBar/SearchBar';
|
||||
import Button, { ButtonLevel } from '../Button/Button';
|
||||
import CommandService from 'lib/services/CommandService';
|
||||
import { runtime as focusSearchRuntime } from './commands/focusSearch';
|
||||
const styled = require('styled-components').default;
|
||||
|
||||
const StyledRoot = styled.div`
|
||||
width: 100%;
|
||||
/*height: 100%;*/
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: ${(props:any) => props.theme.mainPadding}px;
|
||||
background-color: ${(props:any) => props.theme.backgroundColor3};
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
margin-left: 8px;
|
||||
`;
|
||||
|
||||
export default function NoteListControls() {
|
||||
const searchBarRef = useRef(null);
|
||||
|
||||
useEffect(function() {
|
||||
CommandService.instance().registerRuntime('focusSearch', focusSearchRuntime(searchBarRef));
|
||||
|
||||
return function() {
|
||||
CommandService.instance().unregisterRuntime('focusSearch');
|
||||
};
|
||||
}, []);
|
||||
|
||||
function onNewTodoButtonClick() {
|
||||
CommandService.instance().execute('newTodo');
|
||||
}
|
||||
|
||||
function onNewNoteButtonClick() {
|
||||
CommandService.instance().execute('newNote');
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledRoot>
|
||||
<SearchBar inputRef={searchBarRef}/>
|
||||
<StyledButton
|
||||
tooltip={CommandService.instance().title('newTodo')}
|
||||
iconName="far fa-check-square"
|
||||
level={ButtonLevel.Primary}
|
||||
onClick={onNewTodoButtonClick}
|
||||
/>
|
||||
<StyledButton
|
||||
tooltip={CommandService.instance().title('newNote')}
|
||||
iconName="icon-note"
|
||||
level={ButtonLevel.Primary}
|
||||
onClick={onNewNoteButtonClick}
|
||||
/>
|
||||
</StyledRoot>
|
||||
);
|
||||
}
|
||||
+2
-2
@@ -6,10 +6,10 @@ export const declaration:CommandDeclaration = {
|
||||
label: () => _('Search in all the notes'),
|
||||
};
|
||||
|
||||
export const runtime = (comp:any):CommandRuntime => {
|
||||
export const runtime = (searchBarRef:any):CommandRuntime => {
|
||||
return {
|
||||
execute: async () => {
|
||||
if (comp.searchElement_) comp.searchElement_.focus();
|
||||
if (searchBarRef.current) searchBarRef.current.focus();
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -5,10 +5,45 @@ const Mark = require('mark.js/dist/mark.min.js');
|
||||
const markJsUtils = require('lib/markJsUtils');
|
||||
const Note = require('lib/models/Note');
|
||||
const { replaceRegexDiacritics, pregQuote } = require('lib/string-utils');
|
||||
const styled = require('styled-components').default;
|
||||
|
||||
const StyledRoot = styled.div`
|
||||
width: ${(props:any) => props.width}px;
|
||||
height: ${(props:any) => props.height}px;
|
||||
opacity: ${(props:any) => props.isProvisional ? '0.5' : '1'};
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
position: relative;
|
||||
background-color: ${(props:any) => props.selected ? props.theme.selectedColor : 'none'};
|
||||
|
||||
border-style: solid;
|
||||
border-color: ${(props:any) => props.theme.color};
|
||||
border-top-width: ${(props:any) => props.dragItemPosition === 'top' ? 2 : 0}px;
|
||||
border-bottom-width: ${(props:any) => props.dragItemPosition === 'bottom' ? 2 : 0}px;
|
||||
border-right: none;
|
||||
border-left: none;
|
||||
|
||||
// https://stackoverflow.com/questions/50174448/css-how-to-add-white-space-before-elements-border
|
||||
&::before {
|
||||
content: '';
|
||||
border-bottom: 1px solid ${(props:any) => props.theme.dividerColor};
|
||||
width: ${(props:any) => props.width - 32}px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 16px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props:any) => props.theme.backgroundColorHover3};
|
||||
}
|
||||
`;
|
||||
|
||||
interface NoteListItemProps {
|
||||
theme: number,
|
||||
themeId: number,
|
||||
width: number,
|
||||
height: number,
|
||||
style: any,
|
||||
dragItemIndex: number,
|
||||
highlightedWords: string[],
|
||||
@@ -28,8 +63,8 @@ interface NoteListItemProps {
|
||||
|
||||
function NoteListItem(props:NoteListItemProps, ref:any) {
|
||||
const item = props.item;
|
||||
const theme = themeStyle(props.theme);
|
||||
const hPadding = 10;
|
||||
const theme = themeStyle(props.themeId);
|
||||
const hPadding = 16;
|
||||
|
||||
const anchorRef = useRef(null);
|
||||
|
||||
@@ -41,14 +76,11 @@ function NoteListItem(props:NoteListItemProps, ref:any) {
|
||||
};
|
||||
});
|
||||
|
||||
let rootStyle = Object.assign({ width: props.width, opacity: props.isProvisional ? 0.5 : 1 }, props.style.listItem);
|
||||
|
||||
if (props.isSelected) rootStyle = Object.assign(rootStyle, props.style.listItemSelected);
|
||||
|
||||
let dragItemPosition = '';
|
||||
if (props.dragItemIndex === props.index) {
|
||||
rootStyle.borderTop = `2px solid ${theme.color}`;
|
||||
dragItemPosition = 'top';
|
||||
} else if (props.index === props.itemCount - 1 && props.dragItemIndex >= props.itemCount) {
|
||||
rootStyle.borderBottom = `2px solid ${theme.color}`;
|
||||
dragItemPosition = 'bottom';
|
||||
}
|
||||
|
||||
const onTitleClick = useCallback((event) => {
|
||||
@@ -65,7 +97,7 @@ function NoteListItem(props:NoteListItemProps, ref:any) {
|
||||
if (!item.is_todo) return null;
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', height: rootStyle.height, alignItems: 'center', paddingLeft: hPadding }}>
|
||||
<div style={{ display: 'flex', height: props.height, alignItems: 'center', paddingLeft: hPadding }}>
|
||||
<input
|
||||
style={{ margin: 0, marginBottom: 1, marginRight: 5 }}
|
||||
type="checkbox"
|
||||
@@ -118,12 +150,19 @@ function NoteListItem(props:NoteListItemProps, ref:any) {
|
||||
};
|
||||
const watchedIcon = props.isWatched ? null : <i style={watchedIconStyle} className={'fa fa-share-square'}></i>;
|
||||
|
||||
// key={`${item.id}_${item.todo_completed}`}
|
||||
|
||||
// Need to include "todo_completed" in key so that checkbox is updated when
|
||||
// item is changed via sync.
|
||||
return (
|
||||
<div className="list-item-container" style={rootStyle} onDragOver={props.onNoteDragOver} onDrop={props.onNoteDrop}>
|
||||
<StyledRoot
|
||||
className="list-item-container"
|
||||
onDragOver={props.onNoteDragOver}
|
||||
onDrop={props.onNoteDrop}
|
||||
width={props.width}
|
||||
height={props.height}
|
||||
isProvisional={props.isProvisional}
|
||||
selected={props.isSelected}
|
||||
dragItemPosition={dragItemPosition}
|
||||
>
|
||||
{renderCheckbox()}
|
||||
<a
|
||||
ref={anchorRef}
|
||||
@@ -138,7 +177,7 @@ function NoteListItem(props:NoteListItemProps, ref:any) {
|
||||
{watchedIcon}
|
||||
{titleComp}
|
||||
</a>
|
||||
</div>
|
||||
</StyledRoot>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -224,8 +224,8 @@ class NotePropertiesDialog extends React.Component {
|
||||
}
|
||||
|
||||
createNoteField(key, value) {
|
||||
const styles = this.styles(this.props.theme);
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const styles = this.styles(this.props.themeId);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const labelComp = <label style={Object.assign({}, theme.textStyle, theme.controlBoxLabel)}>{this.formatLabel(key)}</label>;
|
||||
let controlComp = null;
|
||||
let editComp = null;
|
||||
@@ -356,7 +356,7 @@ class NotePropertiesDialog extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const formNote = this.state.formNote;
|
||||
|
||||
const noteComps = [];
|
||||
@@ -374,7 +374,7 @@ class NotePropertiesDialog extends React.Component {
|
||||
<div style={theme.dialogBox}>
|
||||
<div style={theme.dialogTitle}>{_('Note properties')}</div>
|
||||
<div>{noteComps}</div>
|
||||
<DialogButtonRow theme={this.props.theme} okButtonRef={this.okButton} onClick={this.buttonRow_click}/>
|
||||
<DialogButtonRow themeId={this.props.themeId} okButtonRef={this.okButton} onClick={this.buttonRow_click}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -38,7 +38,7 @@ class NoteRevisionViewerComponent extends React.PureComponent {
|
||||
}
|
||||
|
||||
style() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const style = {
|
||||
root: {
|
||||
@@ -114,7 +114,7 @@ class NoteRevisionViewerComponent extends React.PureComponent {
|
||||
this.setState({ note: note });
|
||||
}
|
||||
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const markupToHtml = markupLanguageUtils.newMarkupToHtml({
|
||||
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
|
||||
@@ -164,7 +164,7 @@ class NoteRevisionViewerComponent extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const style = this.style();
|
||||
|
||||
const revisionListItems = [];
|
||||
@@ -213,7 +213,7 @@ class NoteRevisionViewerComponent extends React.PureComponent {
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class NoteSearchBarComponent extends React.Component {
|
||||
}
|
||||
|
||||
style() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const style = {
|
||||
root: Object.assign({}, theme.textStyle, {
|
||||
@@ -34,7 +34,7 @@ class NoteSearchBarComponent extends React.Component {
|
||||
}
|
||||
|
||||
buttonIconComponent(iconName, clickHandler, isEnabled) {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const searchButton = {
|
||||
paddingLeft: 4,
|
||||
@@ -119,7 +119,7 @@ class NoteSearchBarComponent extends React.Component {
|
||||
// backgroundColor needs to cached to a local variable to prevent the
|
||||
// colour from blinking.
|
||||
// For more info: https://github.com/laurent22/joplin/pull/2329#issuecomment-578376835
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
if (!this.props.searching) {
|
||||
if (this.props.resultCount === 0 && query.length > 0) {
|
||||
this.backgroundColor = theme.warningBackgroundColor;
|
||||
@@ -181,7 +181,7 @@ class NoteSearchBarComponent extends React.Component {
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ const { themeStyle } = require('lib/theme');
|
||||
|
||||
class NoteStatusBarComponent extends React.Component {
|
||||
style() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const style = {
|
||||
root: Object.assign({}, theme.textStyle, {
|
||||
@@ -28,7 +28,7 @@ const mapStateToProps = state => {
|
||||
// notes: state.notes,
|
||||
// folders: state.folders,
|
||||
// selectedNoteIds: state.selectedNoteIds,
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ class NoteTextViewerComponent extends React.Component {
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import * as React from 'react';
|
||||
import { useEffect, useCallback, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import CommandService from '../../lib/services/CommandService';
|
||||
const { connect } = require('react-redux');
|
||||
const { buildStyle } = require('lib/theme');
|
||||
const Toolbar = require('../Toolbar.min.js');
|
||||
const Folder = require('lib/models/Folder');
|
||||
const { _ } = require('lib/locale');
|
||||
const { substrWithEllipsis } = require('lib/string-utils');
|
||||
// const Folder = require('lib/models/Folder');
|
||||
// const { _ } = require('lib/locale');
|
||||
// const { substrWithEllipsis } = require('lib/string-utils');
|
||||
|
||||
interface ButtonClickEvent {
|
||||
name: string,
|
||||
}
|
||||
|
||||
interface NoteToolbarProps {
|
||||
theme: number,
|
||||
themeId: number,
|
||||
style: any,
|
||||
folders: any[],
|
||||
watchedNoteFiles: string[],
|
||||
@@ -26,11 +26,12 @@ interface NoteToolbarProps {
|
||||
}
|
||||
|
||||
function styles_(props:NoteToolbarProps) {
|
||||
return buildStyle('NoteToolbar', props.theme, (/* theme:any*/) => {
|
||||
return buildStyle('NoteToolbar', props.themeId, (theme:any) => {
|
||||
return {
|
||||
root: {
|
||||
...props.style,
|
||||
borderBottom: 'none',
|
||||
backgroundColor: theme.backgroundColor,
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -39,52 +40,27 @@ function styles_(props:NoteToolbarProps) {
|
||||
function NoteToolbar(props:NoteToolbarProps) {
|
||||
const styles = styles_(props);
|
||||
const [toolbarItems, setToolbarItems] = useState([]);
|
||||
const selectedNoteFolder = Folder.byId(props.folders, props.note.parent_id);
|
||||
const folderId = selectedNoteFolder ? selectedNoteFolder.id : '';
|
||||
const folderTitle = selectedNoteFolder && selectedNoteFolder.title ? selectedNoteFolder.title : '';
|
||||
// const selectedNoteFolder = Folder.byId(props.folders, props.note.parent_id);
|
||||
// const folderId = selectedNoteFolder ? selectedNoteFolder.id : '';
|
||||
// const folderTitle = selectedNoteFolder && selectedNoteFolder.title ? selectedNoteFolder.title : '';
|
||||
|
||||
const cmdService = CommandService.instance();
|
||||
|
||||
const updateToolbarItems = useCallback(() => {
|
||||
function updateToolbarItems() {
|
||||
const output = [];
|
||||
|
||||
output.push(
|
||||
cmdService.commandToToolbarButton('historyBackward')
|
||||
);
|
||||
|
||||
output.push(
|
||||
cmdService.commandToToolbarButton('historyForward')
|
||||
);
|
||||
|
||||
if (folderId && ['Search', 'Tag', 'SmartFilter'].includes(props.notesParentType)) {
|
||||
output.push({
|
||||
title: _('In: %s', substrWithEllipsis(folderTitle, 0, 16)),
|
||||
tooltip: folderTitle,
|
||||
iconName: 'fa-book',
|
||||
onClick: () => {
|
||||
props.dispatch({
|
||||
type: 'FOLDER_AND_NOTE_SELECT',
|
||||
folderId: folderId,
|
||||
noteId: props.note.id,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
output.push(cmdService.commandToToolbarButton('showNoteProperties'));
|
||||
|
||||
if (props.watchedNoteFiles.indexOf(props.note.id) >= 0) {
|
||||
output.push(cmdService.commandToToolbarButton('stopExternalEditing'));
|
||||
} else {
|
||||
output.push(cmdService.commandToToolbarButton('startExternalEditing'));
|
||||
}
|
||||
// if (props.watchedNoteFiles.indexOf(props.note.id) >= 0) {
|
||||
// output.push(cmdService.commandToToolbarButton('stopExternalEditing'));
|
||||
// } else {
|
||||
// output.push(cmdService.commandToToolbarButton('startExternalEditing'));
|
||||
// }
|
||||
|
||||
output.push(cmdService.commandToToolbarButton('editAlarm'));
|
||||
|
||||
output.push(cmdService.commandToToolbarButton('setTags'));
|
||||
output.push(cmdService.commandToToolbarButton('toggleVisiblePanes'));
|
||||
output.push(cmdService.commandToToolbarButton('showNoteProperties'));
|
||||
|
||||
setToolbarItems(output);
|
||||
}, [props.note.id, folderId, folderTitle, props.watchedNoteFiles, props.notesParentType]);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
updateToolbarItems();
|
||||
@@ -92,7 +68,7 @@ function NoteToolbar(props:NoteToolbarProps) {
|
||||
return () => {
|
||||
cmdService.off('commandsEnabledStateChange', updateToolbarItems);
|
||||
};
|
||||
}, [updateToolbarItems]);
|
||||
}, []);
|
||||
|
||||
return <Toolbar style={styles.root} items={toolbarItems} />;
|
||||
}
|
||||
|
||||
+22
-18
@@ -1,16 +1,21 @@
|
||||
const React = require('react');
|
||||
import * as React from 'react';
|
||||
import ButtonBar from './ConfigScreen/ButtonBar';
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const Setting = require('lib/models/Setting');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const { Header } = require('./Header/Header.min.js');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { OneDriveApiNodeUtils } = require('lib/onedrive-api-node-utils.js');
|
||||
|
||||
class OneDriveLoginScreenComponent extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
interface Props {
|
||||
themeId: string,
|
||||
}
|
||||
|
||||
class OneDriveLoginScreenComponent extends React.Component<any, any> {
|
||||
constructor(props:Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
authLog: [],
|
||||
@@ -18,8 +23,8 @@ class OneDriveLoginScreenComponent extends React.Component {
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const log = (s) => {
|
||||
this.setState(state => {
|
||||
const log = (s:any) => {
|
||||
this.setState((state:any) => {
|
||||
const authLog = state.authLog.slice();
|
||||
authLog.push({ key: (Date.now() + Math.random()).toString(), text: s });
|
||||
return { authLog: authLog };
|
||||
@@ -30,7 +35,7 @@ class OneDriveLoginScreenComponent extends React.Component {
|
||||
const syncTarget = reg.syncTarget(syncTargetId);
|
||||
const oneDriveApiUtils = new OneDriveApiNodeUtils(syncTarget.api());
|
||||
const auth = await oneDriveApiUtils.oauthDance({
|
||||
log: (s) => log(s),
|
||||
log: (s:any) => log(s),
|
||||
});
|
||||
|
||||
Setting.setValue(`sync.${syncTargetId}.auth`, auth ? JSON.stringify(auth) : null);
|
||||
@@ -52,9 +57,7 @@ class OneDriveLoginScreenComponent extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const style = this.props.style;
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const headerStyle = Object.assign({}, theme.headerStyle, { width: style.width });
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const logComps = [];
|
||||
for (const l of this.state.authLog) {
|
||||
@@ -66,22 +69,23 @@ class OneDriveLoginScreenComponent extends React.Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Header style={headerStyle}/>
|
||||
<div style={{ padding: 10 }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
|
||||
<div style={{ padding: theme.configScreenPadding, flex: 1 }}>
|
||||
{logComps}
|
||||
</div>
|
||||
<ButtonBar
|
||||
onCancelClick={() => this.props.dispatch({ type: 'NAV_BACK' })}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const mapStateToProps = (state:any) => {
|
||||
return {
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
};
|
||||
};
|
||||
|
||||
const OneDriveLoginScreen = connect(mapStateToProps)(OneDriveLoginScreenComponent);
|
||||
export default connect(mapStateToProps)(OneDriveLoginScreenComponent);
|
||||
|
||||
module.exports = { OneDriveLoginScreen };
|
||||
@@ -164,10 +164,10 @@ class PromptDialog extends React.Component {
|
||||
|
||||
render() {
|
||||
const style = this.props.style;
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const buttonTypes = this.props.buttons ? this.props.buttons : ['ok', 'cancel'];
|
||||
|
||||
const styles = this.styles(this.props.theme, style.width, style.height, this.state.visible);
|
||||
const styles = this.styles(this.props.themeId, style.width, style.height, this.state.visible);
|
||||
|
||||
const onClose = (accept, buttonType) => {
|
||||
if (this.props.onClose) {
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
import * as React from 'react';
|
||||
import { useRef, useState } from 'react';
|
||||
import produce from 'immer';
|
||||
import useWindowResizeEvent from './hooks/useWindowResizeEvent';
|
||||
import useLayoutItemSizes, { LayoutItemSizes, itemSize } from './hooks/useLayoutItemSizes';
|
||||
const { Resizable } = require('re-resizable');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
export enum LayoutItemDirection {
|
||||
Row = 'row',
|
||||
Column = 'column',
|
||||
}
|
||||
|
||||
export interface Size {
|
||||
width: number,
|
||||
height: number,
|
||||
}
|
||||
|
||||
export interface LayoutItem {
|
||||
key: string,
|
||||
width?: number,
|
||||
height?: number,
|
||||
minWidth?: number,
|
||||
minHeight?: number,
|
||||
children?: LayoutItem[]
|
||||
direction?: LayoutItemDirection,
|
||||
resizable?: boolean,
|
||||
visible?: boolean,
|
||||
}
|
||||
|
||||
interface onResizeEvent {
|
||||
layout: LayoutItem
|
||||
}
|
||||
|
||||
interface Props {
|
||||
layout: LayoutItem,
|
||||
renderItem(key:string, event:any):JSX.Element;
|
||||
onResize(event:onResizeEvent):void;
|
||||
width?: number,
|
||||
height?: number,
|
||||
}
|
||||
|
||||
export function findItemByKey(layout:LayoutItem, key:string):LayoutItem {
|
||||
function recurseFind(item:LayoutItem):LayoutItem {
|
||||
if (item.key === key) return item;
|
||||
|
||||
if (item.children) {
|
||||
for (const child of item.children) {
|
||||
const found = recurseFind(child);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const output = recurseFind(layout);
|
||||
if (!output) throw new Error(`Invalid item key: ${key}`);
|
||||
return output;
|
||||
}
|
||||
|
||||
function updateLayoutItem(layout:LayoutItem, key:string, props:any) {
|
||||
return produce(layout, (draftState:LayoutItem) => {
|
||||
function recurseFind(item:LayoutItem) {
|
||||
if (item.key === key) {
|
||||
for (const n in props) {
|
||||
(item as any)[n] = props[n];
|
||||
}
|
||||
} else {
|
||||
if (item.children) {
|
||||
for (const child of item.children) {
|
||||
recurseFind(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recurseFind(draftState);
|
||||
});
|
||||
}
|
||||
|
||||
function renderContainer(item:LayoutItem, sizes:LayoutItemSizes, onResizeStart:Function, onResize:Function, onResizeStop:Function, children:JSX.Element[]):JSX.Element {
|
||||
const style:any = {
|
||||
display: item.visible !== false ? 'flex' : 'none',
|
||||
flexDirection: item.direction,
|
||||
};
|
||||
|
||||
const size:Size = itemSize(item, sizes);
|
||||
|
||||
const className = `resizableLayoutItem rli-${item.key}`;
|
||||
if (item.resizable) {
|
||||
const enable = { top: false, right: true, bottom: false, left: false, topRight: false, bottomRight: false, bottomLeft: false, topLeft: false };
|
||||
|
||||
return (
|
||||
<Resizable
|
||||
key={item.key}
|
||||
className={className}
|
||||
style={style}
|
||||
size={size}
|
||||
onResizeStart={onResizeStart}
|
||||
onResize={onResize}
|
||||
onResizeStop={onResizeStop}
|
||||
enable={enable}
|
||||
minWidth={item.minWidth}
|
||||
minHeight={item.minHeight}
|
||||
>
|
||||
{children}
|
||||
</Resizable>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div key={item.key} className={className} style={{ ...style, ...size }}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function ResizableLayout(props:Props) {
|
||||
const eventEmitter = useRef(new EventEmitter());
|
||||
|
||||
const [resizedItem, setResizedItem] = useState<any>(null);
|
||||
|
||||
function renderLayoutItem(item:LayoutItem, sizes:LayoutItemSizes, isVisible:boolean):JSX.Element {
|
||||
|
||||
function onResizeStart() {
|
||||
setResizedItem({
|
||||
key: item.key,
|
||||
initialWidth: sizes[item.key].width,
|
||||
initialHeight: sizes[item.key].height,
|
||||
});
|
||||
}
|
||||
|
||||
function onResize(_event:any, _direction:any, _refToElement: HTMLDivElement, delta:any) {
|
||||
const newLayout = updateLayoutItem(props.layout, item.key, {
|
||||
width: resizedItem.initialWidth + delta.width,
|
||||
height: resizedItem.initialHeight + delta.height,
|
||||
});
|
||||
|
||||
props.onResize({ layout: newLayout });
|
||||
eventEmitter.current.emit('resize');
|
||||
}
|
||||
|
||||
function onResizeStop(_event:any, _direction:any, _refToElement: HTMLDivElement, delta:any) {
|
||||
onResize(_event, _direction, _refToElement, delta);
|
||||
setResizedItem(null);
|
||||
}
|
||||
|
||||
if (!item.children) {
|
||||
const comp = props.renderItem(item.key, {
|
||||
item: item,
|
||||
eventEmitter: eventEmitter.current,
|
||||
size: sizes[item.key],
|
||||
visible: isVisible,
|
||||
});
|
||||
|
||||
return renderContainer(item, sizes, onResizeStart, onResize, onResizeStop, [comp]);
|
||||
} else {
|
||||
const childrenComponents = [];
|
||||
for (const child of item.children) {
|
||||
childrenComponents.push(renderLayoutItem(child, sizes, isVisible && child.visible !== false));
|
||||
}
|
||||
|
||||
return renderContainer(item, sizes, onResizeStart, onResize, onResizeStop, childrenComponents);
|
||||
}
|
||||
}
|
||||
|
||||
useWindowResizeEvent(eventEmitter);
|
||||
const sizes = useLayoutItemSizes(props.layout);
|
||||
|
||||
return renderLayoutItem(props.layout, sizes, props.layout.visible !== false);
|
||||
}
|
||||
|
||||
export default ResizableLayout;
|
||||
@@ -0,0 +1,79 @@
|
||||
import { useMemo } from 'react';
|
||||
import { LayoutItem, Size } from '../ResizableLayout';
|
||||
|
||||
export interface LayoutItemSizes {
|
||||
[key:string]: Size,
|
||||
}
|
||||
|
||||
export function itemSize(item:LayoutItem, sizes:LayoutItemSizes):Size {
|
||||
return {
|
||||
width: 'width' in item ? item.width : sizes[item.key].width,
|
||||
height: 'height' in item ? item.height : sizes[item.key].height,
|
||||
};
|
||||
}
|
||||
|
||||
function calculateChildrenSizes(item:LayoutItem, sizes:LayoutItemSizes):LayoutItemSizes {
|
||||
if (!item.children) return sizes;
|
||||
|
||||
const parentSize = itemSize(item, sizes);
|
||||
|
||||
const remainingSize:Size = {
|
||||
width: parentSize.width,
|
||||
height: parentSize.height,
|
||||
};
|
||||
|
||||
const noWidthChildren:LayoutItem[] = [];
|
||||
const noHeightChildren:LayoutItem[] = [];
|
||||
|
||||
for (const child of item.children) {
|
||||
let w = 'width' in child ? child.width : null;
|
||||
let h = 'height' in child ? child.height : null;
|
||||
if (child.visible === false) {
|
||||
w = 0;
|
||||
h = 0;
|
||||
}
|
||||
sizes[child.key] = { width: w, height: h };
|
||||
if (w !== null) remainingSize.width -= w;
|
||||
if (h !== null) remainingSize.height -= h;
|
||||
if (w === null) noWidthChildren.push(child);
|
||||
if (h === null) noHeightChildren.push(child);
|
||||
}
|
||||
|
||||
if (noWidthChildren.length) {
|
||||
const w = item.direction === 'row' ? remainingSize.width / noWidthChildren.length : parentSize.width;
|
||||
for (const child of noWidthChildren) {
|
||||
sizes[child.key].width = w;
|
||||
}
|
||||
}
|
||||
|
||||
if (noHeightChildren.length) {
|
||||
const h = item.direction === 'column' ? remainingSize.height / noHeightChildren.length : parentSize.height;
|
||||
for (const child of noHeightChildren) {
|
||||
sizes[child.key].height = h;
|
||||
}
|
||||
}
|
||||
|
||||
for (const child of item.children) {
|
||||
const childrenSizes = calculateChildrenSizes(child, sizes);
|
||||
sizes = { ...sizes, ...childrenSizes };
|
||||
}
|
||||
|
||||
return sizes;
|
||||
}
|
||||
|
||||
export default function useLayoutItemSizes(layout:LayoutItem) {
|
||||
return useMemo(() => {
|
||||
let sizes:LayoutItemSizes = {};
|
||||
|
||||
if (!('width' in layout) || !('height' in layout)) throw new Error('width and height are required on layout root');
|
||||
|
||||
sizes[layout.key] = {
|
||||
width: layout.width,
|
||||
height: layout.height,
|
||||
};
|
||||
|
||||
sizes = calculateChildrenSizes(layout, sizes);
|
||||
|
||||
return sizes;
|
||||
}, [layout]);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { useEffect } from 'react';
|
||||
const debounce = require('debounce');
|
||||
|
||||
export default function useWindowResizeEvent(eventEmitter:any) {
|
||||
useEffect(() => {
|
||||
const window_resize = debounce(() => {
|
||||
eventEmitter.current.emit('resize');
|
||||
}, 500);
|
||||
|
||||
window.addEventListener('resize', window_resize);
|
||||
|
||||
return () => {
|
||||
window_resize.clear();
|
||||
window.removeEventListener('resize', window_resize);
|
||||
};
|
||||
}, []);
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import ButtonBar from './ConfigScreen/ButtonBar';
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const { Header } = require('./Header/Header.min.js');
|
||||
const prettyBytes = require('pretty-bytes');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
|
||||
@@ -14,8 +14,9 @@ interface Style {
|
||||
}
|
||||
|
||||
interface Props {
|
||||
theme: any;
|
||||
style: Style
|
||||
themeId: number;
|
||||
style: Style,
|
||||
dispatch: Function,
|
||||
}
|
||||
|
||||
interface Resource {
|
||||
@@ -37,7 +38,7 @@ interface ResourceTable {
|
||||
onResourceClick: (resource: Resource) => any
|
||||
onResourceDelete: (resource: Resource) => any
|
||||
onToggleSorting: (order: SortingOrder) => any
|
||||
theme: any
|
||||
themeId: number
|
||||
style: Style
|
||||
}
|
||||
|
||||
@@ -50,17 +51,19 @@ interface ActiveSorting {
|
||||
}
|
||||
|
||||
const ResourceTable: React.FC<ResourceTable> = (props: ResourceTable) => {
|
||||
const theme = themeStyle(props.themeId);
|
||||
|
||||
const sortOrderEngagedMarker = (s: SortingOrder) => {
|
||||
return (
|
||||
<a href="#"
|
||||
style={{ color: props.theme.urlColor }}
|
||||
style={{ color: theme.urlColor }}
|
||||
onClick={() => props.onToggleSorting(s)}>{
|
||||
(props.sorting.order === s && props.sorting.type === 'desc') ? '▾' : '▴'}</a>
|
||||
);
|
||||
};
|
||||
|
||||
const titleCellStyle = {
|
||||
...props.theme.textStyle,
|
||||
...theme.textStyle,
|
||||
textOverflow: 'ellipsis',
|
||||
overflowX: 'hidden',
|
||||
maxWidth: 1,
|
||||
@@ -69,14 +72,14 @@ const ResourceTable: React.FC<ResourceTable> = (props: ResourceTable) => {
|
||||
};
|
||||
|
||||
const cellStyle = {
|
||||
...props.theme.textStyle,
|
||||
...theme.textStyle,
|
||||
whiteSpace: 'nowrap',
|
||||
color: props.theme.colorFaded,
|
||||
color: theme.colorFaded,
|
||||
width: 1,
|
||||
};
|
||||
|
||||
const headerStyle = {
|
||||
...props.theme.textStyle,
|
||||
...theme.textStyle,
|
||||
whiteSpace: 'nowrap',
|
||||
width: 1,
|
||||
fontWeight: 'bold',
|
||||
@@ -97,7 +100,7 @@ const ResourceTable: React.FC<ResourceTable> = (props: ResourceTable) => {
|
||||
<tr key={index}>
|
||||
<td style={titleCellStyle} className="titleCell">
|
||||
<a
|
||||
style={{ color: props.theme.urlColor }}
|
||||
style={{ color: theme.urlColor }}
|
||||
href="#"
|
||||
onClick={() => props.onResourceClick(resource)}>{resource.title || `(${_('Untitled')})`}
|
||||
</a>
|
||||
@@ -105,7 +108,7 @@ const ResourceTable: React.FC<ResourceTable> = (props: ResourceTable) => {
|
||||
<td style={cellStyle} className="dataCell">{prettyBytes(resource.size)}</td>
|
||||
<td style={cellStyle} className="dataCell">{resource.id}</td>
|
||||
<td style={cellStyle} className="dataCell">
|
||||
<button style={props.theme.buttonStyle} onClick={() => props.onResourceDelete(resource)}>{_('Delete')}</button>
|
||||
<button style={theme.buttonStyle} onClick={() => props.onResourceDelete(resource)}>{_('Delete')}</button>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
@@ -202,8 +205,7 @@ class ResourceScreenComponent extends React.Component<Props, State> {
|
||||
|
||||
render() {
|
||||
const style = this.props.style;
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const headerStyle = Object.assign({}, theme.headerStyle, { width: style.width });
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const rootStyle:any = {
|
||||
...style,
|
||||
@@ -211,13 +213,16 @@ class ResourceScreenComponent extends React.Component<Props, State> {
|
||||
color: theme.color,
|
||||
padding: 20,
|
||||
boxSizing: 'border-box',
|
||||
flex: 1,
|
||||
};
|
||||
rootStyle.height = style.height - 35; // Minus the header height
|
||||
// rootStyle.height = style.height - 35; // Minus the header height
|
||||
delete rootStyle.height;
|
||||
delete rootStyle.width;
|
||||
|
||||
const containerHeight = style.height;
|
||||
|
||||
return (
|
||||
<div style={{ ...theme.containerStyle, fontFamily: theme.fontFamily }}>
|
||||
<Header style={headerStyle} />
|
||||
<div style={{ ...theme.containerStyle, fontFamily: theme.fontFamily, height: containerHeight, display: 'flex', flexDirection: 'column' }}>
|
||||
<div style={rootStyle}>
|
||||
<div style={{ ...theme.notificationBox, marginBottom: 10 }}>{
|
||||
_('This is an advanced tool to show the attachments that are linked to your notes. Please be careful when deleting one of them as they cannot be restored afterwards.')
|
||||
@@ -232,7 +237,7 @@ class ResourceScreenComponent extends React.Component<Props, State> {
|
||||
<div>{_('Warning: not all resources shown for performance reasons (limit: %s).', MAX_RESOURCES)}</div>
|
||||
}
|
||||
{this.state.resources && <ResourceTable
|
||||
theme={theme}
|
||||
themeId={this.props.themeId}
|
||||
style={style}
|
||||
resources={this.state.resources}
|
||||
sorting={this.state.sorting}
|
||||
@@ -243,13 +248,16 @@ class ResourceScreenComponent extends React.Component<Props, State> {
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<ButtonBar
|
||||
onCancelClick={() => this.props.dispatch({ type: 'NAV_BACK' })}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: any) => ({
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
});
|
||||
|
||||
const ResourceScreen = connect(mapStateToProps)(ResourceScreenComponent);
|
||||
|
||||
@@ -5,20 +5,30 @@ const { connect, Provider } = require('react-redux');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
|
||||
const { MainScreen } = require('.//MainScreen/MainScreen.min.js');
|
||||
const MainScreen = require('./MainScreen/MainScreen').default;
|
||||
const ConfigScreen = require('./ConfigScreen/ConfigScreen').default;
|
||||
const StatusScreen = require('./StatusScreen/StatusScreen').default;
|
||||
const OneDriveLoginScreen = require('./OneDriveLoginScreen').default;
|
||||
const DropboxLoginScreen = require('./DropboxLoginScreen').default;
|
||||
const ErrorBoundary = require('./ErrorBoundary').default;
|
||||
const { OneDriveLoginScreen } = require('./OneDriveLoginScreen.min.js');
|
||||
const { DropboxLoginScreen } = require('./DropboxLoginScreen.min.js');
|
||||
const { StatusScreen } = require('./StatusScreen.min.js');
|
||||
const { ImportScreen } = require('./ImportScreen.min.js');
|
||||
const { ConfigScreen } = require('./ConfigScreen.min.js');
|
||||
const { ResourceScreen } = require('./ResourceScreen.js');
|
||||
const { Navigator } = require('./Navigator.min.js');
|
||||
const WelcomeUtils = require('lib/WelcomeUtils');
|
||||
const { app } = require('../app');
|
||||
const { ThemeProvider, StyleSheetManager, createGlobalStyle } = require('styled-components');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
|
||||
const GlobalStyle = createGlobalStyle`
|
||||
div, span, a {
|
||||
color: ${(props) => props.theme.color};
|
||||
font-size: ${(props) => props.theme.fontSize}px;
|
||||
font-family: ${(props) => props.theme.fontFamily};
|
||||
}
|
||||
`;
|
||||
|
||||
async function initialize() {
|
||||
this.wcsTimeoutId_ = null;
|
||||
|
||||
@@ -84,6 +94,8 @@ class RootComponent extends React.Component {
|
||||
height: this.props.size.height / this.props.zoomFactor,
|
||||
};
|
||||
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const screens = {
|
||||
Main: { screen: MainScreen },
|
||||
OneDriveLogin: { screen: OneDriveLoginScreen, title: () => _('OneDrive Login') },
|
||||
@@ -94,7 +106,14 @@ class RootComponent extends React.Component {
|
||||
Status: { screen: StatusScreen, title: () => _('Synchronisation Status') },
|
||||
};
|
||||
|
||||
return <Navigator style={navigatorStyle} screens={screens} />;
|
||||
return (
|
||||
<StyleSheetManager disableVendorPrefixes>
|
||||
<ThemeProvider theme={theme}>
|
||||
<GlobalStyle/>
|
||||
<Navigator style={navigatorStyle} screens={screens} />
|
||||
</ThemeProvider>
|
||||
</StyleSheetManager>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +122,7 @@ const mapStateToProps = state => {
|
||||
size: state.windowContentSize,
|
||||
zoomFactor: state.settings.windowContentZoomFactor / 100,
|
||||
appState: state.appState,
|
||||
themeId: state.settings.theme,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import * as React from 'react';
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
import CommandService from 'lib/services/CommandService';
|
||||
import useSearch from './hooks/useSearch';
|
||||
import { Root, SearchInput, SearchButton, SearchButtonIcon } from './styles';
|
||||
const { connect } = require('react-redux');
|
||||
|
||||
const { _ } = require('lib/locale.js');
|
||||
|
||||
interface Props {
|
||||
inputRef?: any,
|
||||
notesParentType: string,
|
||||
}
|
||||
|
||||
function SearchBar(props:Props) {
|
||||
const [query, setQuery] = useState('');
|
||||
const iconName = !query ? CommandService.instance().iconName('search') : 'fa fa-times';
|
||||
|
||||
const onChange = (event:any) => {
|
||||
setQuery(event.currentTarget.value);
|
||||
};
|
||||
|
||||
const onSearchButtonClick = useCallback(() => {
|
||||
setQuery('');
|
||||
}, []);
|
||||
|
||||
useSearch(query);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.notesParentType !== 'Search') {
|
||||
setQuery('');
|
||||
}
|
||||
}, [props.notesParentType]);
|
||||
|
||||
return (
|
||||
<Root>
|
||||
<SearchInput ref={props.inputRef} value={query} type="text" placeholder={_('Search...')} onChange={onChange}/>
|
||||
<SearchButton onClick={onSearchButtonClick}>
|
||||
<SearchButtonIcon className={iconName}/>
|
||||
</SearchButton>
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state:any) => {
|
||||
return {
|
||||
notesParentType: state.notesParentType,
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(SearchBar);
|
||||
@@ -0,0 +1,17 @@
|
||||
import { useEffect } from 'react';
|
||||
import CommandService from 'lib/services/CommandService';
|
||||
const debounce = require('debounce');
|
||||
|
||||
export default function useSearch(query:string) {
|
||||
useEffect(() => {
|
||||
const search = debounce((query:string) => {
|
||||
CommandService.instance().execute('search', { query });
|
||||
}, 500);
|
||||
|
||||
search(query);
|
||||
|
||||
return () => {
|
||||
search.clear();
|
||||
};
|
||||
}, [query]);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import StyledInput from '../../style/StyledInput';
|
||||
const styled = require('styled-components').default;
|
||||
|
||||
export const Root = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const SearchButton = styled.button`
|
||||
position: absolute;
|
||||
right: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
height: 100%;
|
||||
opacity: ${(props:any) => props.disabled ? 0.5 : 1};
|
||||
`;
|
||||
|
||||
export const SearchButtonIcon = styled.span`
|
||||
font-size: ${(props:any) => props.theme.toolbarIconSize}px;
|
||||
color: ${(props:any) => props.theme.color4};
|
||||
`;
|
||||
|
||||
export const SearchInput = styled(StyledInput)`
|
||||
padding-right: 20px;
|
||||
flex: 1;
|
||||
width: 10px;
|
||||
`;
|
||||
@@ -12,7 +12,7 @@ const { reg } = require('lib/registry.js');
|
||||
const { clipboard } = require('electron');
|
||||
|
||||
interface ShareNoteDialogProps {
|
||||
theme: number,
|
||||
themeId: number,
|
||||
noteIds: Array<string>,
|
||||
onClose: Function,
|
||||
}
|
||||
@@ -22,7 +22,7 @@ interface SharesMap {
|
||||
}
|
||||
|
||||
function styles_(props:ShareNoteDialogProps) {
|
||||
return buildStyle('ShareNoteDialog', props.theme, (theme:any) => {
|
||||
return buildStyle('ShareNoteDialog', props.themeId, (theme:any) => {
|
||||
return {
|
||||
noteList: {
|
||||
marginBottom: 10,
|
||||
@@ -67,7 +67,7 @@ export default function ShareNoteDialog(props:ShareNoteDialogProps) {
|
||||
const [shares, setShares] = useState<SharesMap>({});
|
||||
|
||||
const noteCount = notes.length;
|
||||
const theme = themeStyle(props.theme);
|
||||
const theme = themeStyle(props.themeId);
|
||||
const styles = styles_(props);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -206,7 +206,7 @@ export default function ShareNoteDialog(props:ShareNoteDialogProps) {
|
||||
<button disabled={['creating', 'synchronizing'].indexOf(sharesState) >= 0} style={styles.copyShareLinkButton} onClick={shareLinkButton_click}>{_n('Copy Shareable Link', 'Copy Shareable Links', noteCount)}</button>
|
||||
<div style={theme.textStyle}>{statusMessage(sharesState)}</div>
|
||||
{encryptionWarningMessage}
|
||||
<DialogButtonRow theme={props.theme} onClick={buttonRow_click} okButtonShow={false} cancelButtonLabel={_('Close')}/>
|
||||
<DialogButtonRow themeId={props.themeId} onClick={buttonRow_click} okButtonShow={false} cancelButtonLabel={_('Close')}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,766 +0,0 @@
|
||||
const React = require('react');
|
||||
const { connect } = require('react-redux');
|
||||
const shared = require('lib/components/shared/side-menu-shared.js');
|
||||
const { Synchronizer } = require('lib/synchronizer.js');
|
||||
const CommandService = require('lib/services/CommandService.js').default;
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const Menu = bridge().Menu;
|
||||
const MenuItem = bridge().MenuItem;
|
||||
const InteropServiceHelper = require('../../InteropServiceHelper.js');
|
||||
const { substrWithEllipsis } = require('lib/string-utils');
|
||||
const { ALL_NOTES_FILTER_ID } = require('lib/reserved-ids');
|
||||
|
||||
const commands = [
|
||||
require('./commands/focusElementSideBar'),
|
||||
];
|
||||
|
||||
class SideBarComponent extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
CommandService.instance().componentRegisterCommands(this, commands);
|
||||
|
||||
this.onFolderDragStart_ = event => {
|
||||
const folderId = event.currentTarget.getAttribute('folderid');
|
||||
if (!folderId) return;
|
||||
|
||||
event.dataTransfer.setDragImage(new Image(), 1, 1);
|
||||
event.dataTransfer.clearData();
|
||||
event.dataTransfer.setData('text/x-jop-folder-ids', JSON.stringify([folderId]));
|
||||
};
|
||||
|
||||
this.onFolderDragOver_ = event => {
|
||||
if (event.dataTransfer.types.indexOf('text/x-jop-note-ids') >= 0) event.preventDefault();
|
||||
if (event.dataTransfer.types.indexOf('text/x-jop-folder-ids') >= 0) event.preventDefault();
|
||||
};
|
||||
|
||||
this.onFolderDrop_ = async event => {
|
||||
const folderId = event.currentTarget.getAttribute('folderid');
|
||||
const dt = event.dataTransfer;
|
||||
if (!dt) return;
|
||||
|
||||
// folderId can be NULL when dropping on the sidebar Notebook header. In that case, it's used
|
||||
// to put the dropped folder at the root. But for notes, folderId needs to always be defined
|
||||
// since there's no such thing as a root note.
|
||||
|
||||
if (dt.types.indexOf('text/x-jop-note-ids') >= 0) {
|
||||
event.preventDefault();
|
||||
|
||||
if (!folderId) return;
|
||||
|
||||
const noteIds = JSON.parse(dt.getData('text/x-jop-note-ids'));
|
||||
for (let i = 0; i < noteIds.length; i++) {
|
||||
await Note.moveToFolder(noteIds[i], folderId);
|
||||
}
|
||||
} else if (dt.types.indexOf('text/x-jop-folder-ids') >= 0) {
|
||||
event.preventDefault();
|
||||
|
||||
const folderIds = JSON.parse(dt.getData('text/x-jop-folder-ids'));
|
||||
for (let i = 0; i < folderIds.length; i++) {
|
||||
await Folder.moveToFolder(folderIds[i], folderId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.onTagDrop_ = async event => {
|
||||
const tagId = event.currentTarget.getAttribute('tagid');
|
||||
const dt = event.dataTransfer;
|
||||
if (!dt) return;
|
||||
|
||||
if (dt.types.indexOf('text/x-jop-note-ids') >= 0) {
|
||||
event.preventDefault();
|
||||
|
||||
const noteIds = JSON.parse(dt.getData('text/x-jop-note-ids'));
|
||||
for (let i = 0; i < noteIds.length; i++) {
|
||||
await Tag.addNote(tagId, noteIds[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.onFolderToggleClick_ = async event => {
|
||||
const folderId = event.currentTarget.getAttribute('folderid');
|
||||
|
||||
this.props.dispatch({
|
||||
type: 'FOLDER_TOGGLE',
|
||||
id: folderId,
|
||||
});
|
||||
};
|
||||
|
||||
this.folderItemsOrder_ = [];
|
||||
this.tagItemsOrder_ = [];
|
||||
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
this.onAllNotesClick_ = this.onAllNotesClick_.bind(this);
|
||||
|
||||
this.rootRef = React.createRef();
|
||||
|
||||
this.anchorItemRefs = {};
|
||||
|
||||
this.state = {
|
||||
tagHeaderIsExpanded: Setting.value('tagHeaderIsExpanded'),
|
||||
folderHeaderIsExpanded: Setting.value('folderHeaderIsExpanded'),
|
||||
};
|
||||
}
|
||||
|
||||
style() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
|
||||
const itemHeight = 25;
|
||||
|
||||
const style = {
|
||||
root: {
|
||||
backgroundColor: theme.backgroundColor2,
|
||||
},
|
||||
listItemContainer: {
|
||||
boxSizing: 'border-box',
|
||||
height: itemHeight,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
listItem: {
|
||||
fontFamily: theme.fontFamily,
|
||||
fontSize: theme.fontSize,
|
||||
textDecoration: 'none',
|
||||
color: theme.color2,
|
||||
cursor: 'default',
|
||||
opacity: 0.8,
|
||||
whiteSpace: 'nowrap',
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
userSelect: 'none',
|
||||
},
|
||||
listItemSelected: {
|
||||
backgroundColor: theme.selectedColor2,
|
||||
},
|
||||
listItemExpandIcon: {
|
||||
color: theme.color2,
|
||||
cursor: 'default',
|
||||
opacity: 0.8,
|
||||
fontSize: theme.fontSize,
|
||||
textDecoration: 'none',
|
||||
paddingRight: 5,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: 12,
|
||||
},
|
||||
conflictFolder: {
|
||||
color: theme.colorError2,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
header: {
|
||||
height: itemHeight * 1.8,
|
||||
fontFamily: theme.fontFamily,
|
||||
fontSize: theme.fontSize * 1.16,
|
||||
textDecoration: 'none',
|
||||
boxSizing: 'border-box',
|
||||
color: theme.color2,
|
||||
paddingLeft: 8,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
userSelect: 'none',
|
||||
},
|
||||
button: {
|
||||
padding: 6,
|
||||
fontFamily: theme.fontFamily,
|
||||
fontSize: theme.fontSize,
|
||||
textDecoration: 'none',
|
||||
boxSizing: 'border-box',
|
||||
color: theme.color2,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
border: '1px solid rgba(255,255,255,0.2)',
|
||||
marginTop: 10,
|
||||
marginLeft: 5,
|
||||
marginRight: 5,
|
||||
cursor: 'default',
|
||||
userSelect: 'none',
|
||||
},
|
||||
syncReport: {
|
||||
fontFamily: theme.fontFamily,
|
||||
fontSize: Math.round(theme.fontSize * 0.9),
|
||||
color: theme.color2,
|
||||
opacity: 0.5,
|
||||
display: 'flex',
|
||||
alignItems: 'left',
|
||||
justifyContent: 'top',
|
||||
flexDirection: 'column',
|
||||
marginTop: 10,
|
||||
marginLeft: 5,
|
||||
marginRight: 5,
|
||||
marginBottom: 10,
|
||||
wordWrap: 'break-word',
|
||||
},
|
||||
noteCount: {
|
||||
paddingLeft: 5,
|
||||
opacity: 0.5,
|
||||
userSelect: 'none',
|
||||
},
|
||||
};
|
||||
|
||||
style.tagItem = Object.assign({}, style.listItem);
|
||||
style.tagItem.paddingLeft = 23;
|
||||
style.tagItem.height = itemHeight;
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
clearForceUpdateDuringSync() {
|
||||
if (this.forceUpdateDuringSyncIID_) {
|
||||
clearInterval(this.forceUpdateDuringSyncIID_);
|
||||
this.forceUpdateDuringSyncIID_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.clearForceUpdateDuringSync();
|
||||
|
||||
CommandService.instance().componentUnregisterCommands(commands);
|
||||
}
|
||||
|
||||
async itemContextMenu(event) {
|
||||
const itemId = event.currentTarget.getAttribute('data-id');
|
||||
if (itemId === Folder.conflictFolderId()) return;
|
||||
|
||||
const itemType = Number(event.currentTarget.getAttribute('data-type'));
|
||||
if (!itemId || !itemType) throw new Error('No data on element');
|
||||
|
||||
let deleteMessage = '';
|
||||
let buttonLabel = _('Remove');
|
||||
if (itemType === BaseModel.TYPE_FOLDER) {
|
||||
const folder = await Folder.load(itemId);
|
||||
deleteMessage = _('Delete notebook "%s"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.', substrWithEllipsis(folder.title, 0, 32));
|
||||
buttonLabel = _('Delete');
|
||||
} else if (itemType === BaseModel.TYPE_TAG) {
|
||||
const tag = await Tag.load(itemId);
|
||||
deleteMessage = _('Remove tag "%s" from all notes?', substrWithEllipsis(tag.title, 0, 32));
|
||||
} else if (itemType === BaseModel.TYPE_SEARCH) {
|
||||
deleteMessage = _('Remove this search from the sidebar?');
|
||||
}
|
||||
|
||||
const menu = new Menu();
|
||||
|
||||
let item = null;
|
||||
if (itemType === BaseModel.TYPE_FOLDER) {
|
||||
item = BaseModel.byId(this.props.folders, itemId);
|
||||
}
|
||||
|
||||
if (itemType === BaseModel.TYPE_FOLDER && !item.encryption_applied) {
|
||||
menu.append(
|
||||
new MenuItem(CommandService.instance().commandToMenuItem('newNotebook', { parentId: itemId }))
|
||||
);
|
||||
}
|
||||
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
label: buttonLabel,
|
||||
click: async () => {
|
||||
const ok = bridge().showConfirmMessageBox(deleteMessage, {
|
||||
buttons: [buttonLabel, _('Cancel')],
|
||||
defaultId: 1,
|
||||
});
|
||||
if (!ok) return;
|
||||
|
||||
if (itemType === BaseModel.TYPE_FOLDER) {
|
||||
await Folder.delete(itemId);
|
||||
} else if (itemType === BaseModel.TYPE_TAG) {
|
||||
await Tag.untagAll(itemId);
|
||||
} else if (itemType === BaseModel.TYPE_SEARCH) {
|
||||
this.props.dispatch({
|
||||
type: 'SEARCH_DELETE',
|
||||
id: itemId,
|
||||
});
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
if (itemType === BaseModel.TYPE_FOLDER && !item.encryption_applied) {
|
||||
menu.append(new MenuItem(CommandService.instance().commandToMenuItem('renameFolder', { folderId: itemId })));
|
||||
|
||||
menu.append(new MenuItem({ type: 'separator' }));
|
||||
|
||||
const InteropService = require('lib/services/InteropService.js');
|
||||
|
||||
const exportMenu = new Menu();
|
||||
const ioService = new InteropService();
|
||||
const ioModules = ioService.modules();
|
||||
for (let i = 0; i < ioModules.length; i++) {
|
||||
const module = ioModules[i];
|
||||
if (module.type !== 'exporter') continue;
|
||||
|
||||
exportMenu.append(
|
||||
new MenuItem({
|
||||
label: module.fullLabel(),
|
||||
click: async () => {
|
||||
await InteropServiceHelper.export(this.props.dispatch.bind(this), module, { sourceFolderIds: [itemId] });
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
label: _('Export'),
|
||||
submenu: exportMenu,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (itemType === BaseModel.TYPE_TAG) {
|
||||
menu.append(new MenuItem(
|
||||
CommandService.instance().commandToMenuItem('renameTag', { tagId: itemId })
|
||||
));
|
||||
}
|
||||
|
||||
menu.popup(bridge().window());
|
||||
}
|
||||
|
||||
folderItem_click(folder) {
|
||||
this.props.dispatch({
|
||||
type: 'FOLDER_SELECT',
|
||||
id: folder ? folder.id : null,
|
||||
});
|
||||
}
|
||||
|
||||
tagItem_click(tag) {
|
||||
this.props.dispatch({
|
||||
type: 'TAG_SELECT',
|
||||
id: tag ? tag.id : null,
|
||||
});
|
||||
}
|
||||
|
||||
// async sync_click() {
|
||||
// await shared.synchronize_press(this);
|
||||
// }
|
||||
|
||||
anchorItemRef(type, id) {
|
||||
if (!this.anchorItemRefs[type]) this.anchorItemRefs[type] = {};
|
||||
if (this.anchorItemRefs[type][id]) return this.anchorItemRefs[type][id];
|
||||
this.anchorItemRefs[type][id] = React.createRef();
|
||||
return this.anchorItemRefs[type][id];
|
||||
}
|
||||
|
||||
firstAnchorItemRef(type) {
|
||||
const refs = this.anchorItemRefs[type];
|
||||
if (!refs) return null;
|
||||
|
||||
const n = `${type}s`;
|
||||
const item = this.props[n] && this.props[n].length ? this.props[n][0] : null;
|
||||
console.info('props', this.props[n], item);
|
||||
if (!item) return null;
|
||||
|
||||
return refs[item.id];
|
||||
}
|
||||
|
||||
noteCountElement(count) {
|
||||
return <div style={this.style().noteCount}>({count})</div>;
|
||||
}
|
||||
|
||||
folderItem(folder, selected, hasChildren, depth) {
|
||||
let style = Object.assign({}, this.style().listItem);
|
||||
if (folder.id === Folder.conflictFolderId()) style = Object.assign(style, this.style().conflictFolder);
|
||||
|
||||
const itemTitle = Folder.displayTitle(folder);
|
||||
|
||||
let containerStyle = Object.assign({}, this.style().listItemContainer);
|
||||
if (selected) containerStyle = Object.assign(containerStyle, this.style().listItemSelected);
|
||||
|
||||
containerStyle.paddingLeft = 8 + depth * 15;
|
||||
|
||||
const expandLinkStyle = Object.assign({}, this.style().listItemExpandIcon);
|
||||
const expandIconStyle = {
|
||||
visibility: hasChildren ? 'visible' : 'hidden',
|
||||
};
|
||||
|
||||
const iconName = this.props.collapsedFolderIds.indexOf(folder.id) >= 0 ? 'fa-chevron-right' : 'fa-chevron-down';
|
||||
const expandIcon = <i style={expandIconStyle} className={`fas ${iconName}`}></i>;
|
||||
const expandLink = hasChildren ? (
|
||||
<a style={expandLinkStyle} href="#" folderid={folder.id} onClick={this.onFolderToggleClick_}>
|
||||
{expandIcon}
|
||||
</a>
|
||||
) : (
|
||||
<span style={expandLinkStyle}>{expandIcon}</span>
|
||||
);
|
||||
|
||||
const anchorRef = this.anchorItemRef('folder', folder.id);
|
||||
const noteCount = folder.note_count ? this.noteCountElement(folder.note_count) : '';
|
||||
|
||||
return (
|
||||
<div className={`list-item-container list-item-depth-${depth}`} style={containerStyle} key={folder.id} onDragStart={this.onFolderDragStart_} onDragOver={this.onFolderDragOver_} onDrop={this.onFolderDrop_} draggable={true} folderid={folder.id}>
|
||||
{expandLink}
|
||||
<a
|
||||
ref={anchorRef}
|
||||
className="list-item"
|
||||
href="#"
|
||||
data-id={folder.id}
|
||||
data-type={BaseModel.TYPE_FOLDER}
|
||||
onContextMenu={event => this.itemContextMenu(event)}
|
||||
style={style}
|
||||
folderid={folder.id}
|
||||
onClick={() => {
|
||||
this.folderItem_click(folder);
|
||||
}}
|
||||
onDoubleClick={this.onFolderToggleClick_}
|
||||
>
|
||||
{itemTitle} {noteCount}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
tagItem(tag, selected) {
|
||||
let style = Object.assign({}, this.style().tagItem);
|
||||
if (selected) style = Object.assign(style, this.style().listItemSelected);
|
||||
|
||||
const anchorRef = this.anchorItemRef('tag', tag.id);
|
||||
const noteCount = Setting.value('showNoteCounts') ? this.noteCountElement(tag.note_count) : '';
|
||||
|
||||
return (
|
||||
<a
|
||||
className="list-item"
|
||||
href="#"
|
||||
ref={anchorRef}
|
||||
data-id={tag.id}
|
||||
data-type={BaseModel.TYPE_TAG}
|
||||
onContextMenu={event => this.itemContextMenu(event)}
|
||||
tagid={tag.id}
|
||||
key={tag.id}
|
||||
style={style}
|
||||
onDrop={this.onTagDrop_}
|
||||
onClick={() => {
|
||||
this.tagItem_click(tag);
|
||||
}}
|
||||
>
|
||||
{Tag.displayTitle(tag)} {noteCount}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
// searchItem(search, selected) {
|
||||
// let style = Object.assign({}, this.style().listItem);
|
||||
// if (selected) style = Object.assign(style, this.style().listItemSelected);
|
||||
// return (
|
||||
// <a
|
||||
// className="list-item"
|
||||
// href="#"
|
||||
// data-id={search.id}
|
||||
// data-type={BaseModel.TYPE_SEARCH}
|
||||
// onContextMenu={event => this.itemContextMenu(event)}
|
||||
// key={search.id}
|
||||
// style={style}
|
||||
// onClick={() => {
|
||||
// this.searchItem_click(search);
|
||||
// }}
|
||||
// >
|
||||
// {search.title}
|
||||
// </a>
|
||||
// );
|
||||
// }
|
||||
|
||||
makeDivider(key) {
|
||||
return <div style={{ height: 2, backgroundColor: 'blue' }} key={key} />;
|
||||
}
|
||||
|
||||
makeHeader(key, label, iconName, extraProps = {}) {
|
||||
const style = this.style().header;
|
||||
const icon = <i style={{ fontSize: style.fontSize, marginRight: 5 }} className={`fas ${iconName}`} />;
|
||||
|
||||
if (extraProps.toggleblock || extraProps.onClick) {
|
||||
style.cursor = 'pointer';
|
||||
}
|
||||
|
||||
const headerClick = extraProps.onClick || null;
|
||||
delete extraProps.onClick;
|
||||
|
||||
// check if toggling option is set.
|
||||
let toggleIcon = null;
|
||||
const toggleKey = `${key}IsExpanded`;
|
||||
if (extraProps.toggleblock) {
|
||||
const isExpanded = this.state[toggleKey];
|
||||
toggleIcon = <i className={`fas ${isExpanded ? 'fa-chevron-down' : 'fa-chevron-right'}`} style={{ fontSize: style.fontSize * 0.75, marginRight: 12, marginLeft: 5, marginTop: style.fontSize * 0.125 }}></i>;
|
||||
}
|
||||
if (extraProps.selected) {
|
||||
style.backgroundColor = this.style().listItemSelected.backgroundColor;
|
||||
}
|
||||
|
||||
const ref = this.anchorItemRef('headers', key);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
style={style}
|
||||
key={key}
|
||||
{...extraProps}
|
||||
onClick={event => {
|
||||
// if a custom click event is attached, trigger that.
|
||||
if (headerClick) {
|
||||
headerClick(key, event);
|
||||
}
|
||||
this.onHeaderClick_(key, event);
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
<span style={{ flex: 1 }}>{label}</span>
|
||||
{toggleIcon}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
selectedItem() {
|
||||
if (this.props.notesParentType === 'Folder' && this.props.selectedFolderId) {
|
||||
return { type: 'folder', id: this.props.selectedFolderId };
|
||||
} else if (this.props.notesParentType === 'Tag' && this.props.selectedTagId) {
|
||||
return { type: 'tag', id: this.props.selectedTagId };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
onKeyDown(event) {
|
||||
const keyCode = event.keyCode;
|
||||
const selectedItem = this.selectedItem();
|
||||
|
||||
if (keyCode === 40 || keyCode === 38) {
|
||||
// DOWN / UP
|
||||
event.preventDefault();
|
||||
|
||||
const focusItems = [];
|
||||
|
||||
for (let i = 0; i < this.folderItemsOrder_.length; i++) {
|
||||
const id = this.folderItemsOrder_[i];
|
||||
focusItems.push({ id: id, ref: this.anchorItemRefs['folder'][id], type: 'folder' });
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.tagItemsOrder_.length; i++) {
|
||||
const id = this.tagItemsOrder_[i];
|
||||
focusItems.push({ id: id, ref: this.anchorItemRefs['tag'][id], type: 'tag' });
|
||||
}
|
||||
|
||||
let currentIndex = 0;
|
||||
for (let i = 0; i < focusItems.length; i++) {
|
||||
if (!selectedItem || focusItems[i].id === selectedItem.id) {
|
||||
currentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const inc = keyCode === 38 ? -1 : +1;
|
||||
let newIndex = currentIndex + inc;
|
||||
|
||||
if (newIndex < 0) newIndex = 0;
|
||||
if (newIndex > focusItems.length - 1) newIndex = focusItems.length - 1;
|
||||
|
||||
const focusItem = focusItems[newIndex];
|
||||
|
||||
const actionName = `${focusItem.type.toUpperCase()}_SELECT`;
|
||||
|
||||
this.props.dispatch({
|
||||
type: actionName,
|
||||
id: focusItem.id,
|
||||
});
|
||||
|
||||
focusItem.ref.current.focus();
|
||||
}
|
||||
|
||||
if (keyCode === 9) {
|
||||
// TAB
|
||||
event.preventDefault();
|
||||
|
||||
if (event.shiftKey) {
|
||||
CommandService.instance().execute('focusElement', { target: 'noteBody' });
|
||||
} else {
|
||||
CommandService.instance().execute('focusElement', { target: 'noteList' });
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedItem && selectedItem.type === 'folder' && keyCode === 32) {
|
||||
// SPACE
|
||||
event.preventDefault();
|
||||
|
||||
this.props.dispatch({
|
||||
type: 'FOLDER_TOGGLE',
|
||||
id: selectedItem.id,
|
||||
});
|
||||
}
|
||||
|
||||
if (keyCode === 65 && (event.ctrlKey || event.metaKey)) {
|
||||
// Ctrl+A key
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
onHeaderClick_(key, event) {
|
||||
const currentHeader = event.currentTarget;
|
||||
const toggleBlock = +currentHeader.getAttribute('toggleblock');
|
||||
if (toggleBlock) {
|
||||
const toggleKey = `${key}IsExpanded`;
|
||||
const isExpanded = this.state[toggleKey];
|
||||
this.setState({ [toggleKey]: !isExpanded });
|
||||
Setting.setValue(toggleKey, !isExpanded);
|
||||
}
|
||||
}
|
||||
|
||||
onAllNotesClick_() {
|
||||
this.props.dispatch({
|
||||
type: 'SMART_FILTER_SELECT',
|
||||
id: ALL_NOTES_FILTER_ID,
|
||||
});
|
||||
}
|
||||
|
||||
synchronizeButton(type) {
|
||||
const style = Object.assign({}, this.style().button, { marginBottom: 5 });
|
||||
const iconName = 'fa-sync-alt';
|
||||
const label = type === 'sync' ? _('Synchronise') : _('Cancel');
|
||||
const iconStyle = { fontSize: style.fontSize, marginRight: 5 };
|
||||
|
||||
if (type !== 'sync') {
|
||||
iconStyle.animation = 'icon-infinite-rotation 1s linear infinite';
|
||||
}
|
||||
|
||||
const icon = <i style={iconStyle} className={`fas ${iconName}`} />;
|
||||
return (
|
||||
<a
|
||||
className="synchronize-button"
|
||||
style={style}
|
||||
href="#"
|
||||
key="sync_button"
|
||||
onClick={() => {
|
||||
CommandService.instance().execute('synchronize');
|
||||
// this.sync_click();
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
{label}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const style = Object.assign({}, this.style().root, this.props.style, {
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'hidden',
|
||||
display: 'inline-flex',
|
||||
flexDirection: 'column',
|
||||
});
|
||||
|
||||
const items = [];
|
||||
items.push(
|
||||
this.makeHeader('allNotesHeader', _('All notes'), 'fa-clone', {
|
||||
onClick: this.onAllNotesClick_,
|
||||
selected: this.props.notesParentType === 'SmartFilter' && this.props.selectedSmartFilterId === ALL_NOTES_FILTER_ID,
|
||||
})
|
||||
);
|
||||
|
||||
items.push(
|
||||
this.makeHeader('folderHeader', _('Notebooks'), 'fa-book', {
|
||||
onDrop: this.onFolderDrop_,
|
||||
folderid: '',
|
||||
toggleblock: 1,
|
||||
})
|
||||
);
|
||||
|
||||
if (this.props.folders.length) {
|
||||
const result = shared.renderFolders(this.props, this.folderItem.bind(this));
|
||||
const folderItems = result.items;
|
||||
this.folderItemsOrder_ = result.order;
|
||||
items.push(
|
||||
<div className="folders" key="folder_items" style={{ display: this.state.folderHeaderIsExpanded ? 'block' : 'none' }}>
|
||||
{folderItems}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
items.push(
|
||||
this.makeHeader('tagHeader', _('Tags'), 'fa-tags', {
|
||||
toggleblock: 1,
|
||||
})
|
||||
);
|
||||
|
||||
if (this.props.tags.length) {
|
||||
const result = shared.renderTags(this.props, this.tagItem.bind(this));
|
||||
const tagItems = result.items;
|
||||
this.tagItemsOrder_ = result.order;
|
||||
|
||||
items.push(
|
||||
<div className="tags" key="tag_items" style={{ display: this.state.tagHeaderIsExpanded ? 'block' : 'none' }}>
|
||||
{tagItems}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let decryptionReportText = '';
|
||||
if (this.props.decryptionWorker && this.props.decryptionWorker.state !== 'idle' && this.props.decryptionWorker.itemCount) {
|
||||
decryptionReportText = _('Decrypting items: %d/%d', this.props.decryptionWorker.itemIndex + 1, this.props.decryptionWorker.itemCount);
|
||||
}
|
||||
|
||||
let resourceFetcherText = '';
|
||||
if (this.props.resourceFetcher && this.props.resourceFetcher.toFetchCount) {
|
||||
resourceFetcherText = _('Fetching resources: %d/%d', this.props.resourceFetcher.fetchingCount, this.props.resourceFetcher.toFetchCount);
|
||||
}
|
||||
|
||||
const lines = Synchronizer.reportToLines(this.props.syncReport);
|
||||
if (resourceFetcherText) lines.push(resourceFetcherText);
|
||||
if (decryptionReportText) lines.push(decryptionReportText);
|
||||
const syncReportText = [];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
syncReportText.push(
|
||||
<div key={i} style={{ wordWrap: 'break-word', width: '100%' }}>
|
||||
{lines[i]}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const syncButton = this.synchronizeButton(this.props.syncStarted ? 'cancel' : 'sync');
|
||||
|
||||
const syncReportComp = !syncReportText.length ? null : (
|
||||
<div style={this.style().syncReport} key="sync_report">
|
||||
{syncReportText}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div ref={this.rootRef} onKeyDown={this.onKeyDown} className="side-bar" style={style}>
|
||||
<div style={{ flex: 1, overflowX: 'hidden', overflowY: 'auto' }}>{items}</div>
|
||||
<div style={{ flex: 0 }}>
|
||||
{syncReportComp}
|
||||
{syncButton}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
folders: state.folders,
|
||||
tags: state.tags,
|
||||
searches: state.searches,
|
||||
syncStarted: state.syncStarted,
|
||||
syncReport: state.syncReport,
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
selectedTagId: state.selectedTagId,
|
||||
selectedSearchId: state.selectedSearchId,
|
||||
selectedSmartFilterId: state.selectedSmartFilterId,
|
||||
notesParentType: state.notesParentType,
|
||||
locale: state.settings.locale,
|
||||
theme: state.settings.theme,
|
||||
collapsedFolderIds: state.collapsedFolderIds,
|
||||
decryptionWorker: state.decryptionWorker,
|
||||
resourceFetcher: state.resourceFetcher,
|
||||
sidebarVisibility: state.sidebarVisibility,
|
||||
noteListVisibility: state.noteListVisibility,
|
||||
};
|
||||
};
|
||||
|
||||
const SideBar = connect(mapStateToProps)(SideBarComponent);
|
||||
|
||||
module.exports = { SideBar };
|
||||
@@ -0,0 +1,646 @@
|
||||
import * as React from 'react';
|
||||
import { StyledRoot, StyledAddButton, StyledHeader, StyledHeaderIcon, StyledHeaderLabel, StyledListItem, StyledListItemAnchor, StyledExpandLink, StyledNoteCount, StyledSyncReportText, StyledSyncReport, StyledSynchronizeButton } from './styles';
|
||||
import { ButtonLevel } from '../Button/Button';
|
||||
import CommandService from 'lib/services/CommandService';
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
const shared = require('lib/components/shared/side-menu-shared.js');
|
||||
const { Synchronizer } = require('lib/synchronizer.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const Menu = bridge().Menu;
|
||||
const MenuItem = bridge().MenuItem;
|
||||
const InteropServiceHelper = require('../../InteropServiceHelper.js');
|
||||
const { substrWithEllipsis } = require('lib/string-utils');
|
||||
const { ALL_NOTES_FILTER_ID } = require('lib/reserved-ids');
|
||||
|
||||
interface Props {
|
||||
themeId: number,
|
||||
dispatch: Function,
|
||||
folders: any[],
|
||||
collapsedFolderIds: string[],
|
||||
notesParentType: string,
|
||||
selectedFolderId: string,
|
||||
selectedTagId: string,
|
||||
selectedSmartFilterId:string,
|
||||
decryptionWorker: any,
|
||||
resourceFetcher: any,
|
||||
syncReport: any,
|
||||
tags: any[],
|
||||
syncStarted: boolean,
|
||||
}
|
||||
|
||||
interface State {
|
||||
tagHeaderIsExpanded: boolean,
|
||||
folderHeaderIsExpanded: boolean,
|
||||
}
|
||||
|
||||
const commands = [
|
||||
require('./commands/focusElementSideBar'),
|
||||
];
|
||||
|
||||
class SideBarComponent extends React.Component<Props, State> {
|
||||
|
||||
private folderItemsOrder_:any[] = [];
|
||||
private tagItemsOrder_:any[] = [];
|
||||
private rootRef:any = null;
|
||||
private anchorItemRefs:any = {};
|
||||
private forceUpdateDuringSyncIID_:any = null;
|
||||
|
||||
constructor(props:any) {
|
||||
super(props);
|
||||
|
||||
CommandService.instance().componentRegisterCommands(this, commands);
|
||||
|
||||
this.state = {
|
||||
tagHeaderIsExpanded: Setting.value('tagHeaderIsExpanded'),
|
||||
folderHeaderIsExpanded: Setting.value('folderHeaderIsExpanded'),
|
||||
};
|
||||
|
||||
this.onFolderToggleClick_ = this.onFolderToggleClick_.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
this.onAllNotesClick_ = this.onAllNotesClick_.bind(this);
|
||||
this.header_contextMenu = this.header_contextMenu.bind(this);
|
||||
this.onAddFolderButtonClick = this.onAddFolderButtonClick.bind(this);
|
||||
}
|
||||
|
||||
onFolderDragStart_(event:any) {
|
||||
const folderId = event.currentTarget.getAttribute('data-folder-id');
|
||||
if (!folderId) return;
|
||||
|
||||
event.dataTransfer.setDragImage(new Image(), 1, 1);
|
||||
event.dataTransfer.clearData();
|
||||
event.dataTransfer.setData('text/x-jop-folder-ids', JSON.stringify([folderId]));
|
||||
}
|
||||
|
||||
onFolderDragOver_(event:any) {
|
||||
if (event.dataTransfer.types.indexOf('text/x-jop-note-ids') >= 0) event.preventDefault();
|
||||
if (event.dataTransfer.types.indexOf('text/x-jop-folder-ids') >= 0) event.preventDefault();
|
||||
}
|
||||
|
||||
async onFolderDrop_(event:any) {
|
||||
const folderId = event.currentTarget.getAttribute('data-folder-id');
|
||||
const dt = event.dataTransfer;
|
||||
if (!dt) return;
|
||||
|
||||
// folderId can be NULL when dropping on the sidebar Notebook header. In that case, it's used
|
||||
// to put the dropped folder at the root. But for notes, folderId needs to always be defined
|
||||
// since there's no such thing as a root note.
|
||||
|
||||
if (dt.types.indexOf('text/x-jop-note-ids') >= 0) {
|
||||
event.preventDefault();
|
||||
|
||||
if (!folderId) return;
|
||||
|
||||
const noteIds = JSON.parse(dt.getData('text/x-jop-note-ids'));
|
||||
for (let i = 0; i < noteIds.length; i++) {
|
||||
await Note.moveToFolder(noteIds[i], folderId);
|
||||
}
|
||||
} else if (dt.types.indexOf('text/x-jop-folder-ids') >= 0) {
|
||||
event.preventDefault();
|
||||
|
||||
const folderIds = JSON.parse(dt.getData('text/x-jop-folder-ids'));
|
||||
for (let i = 0; i < folderIds.length; i++) {
|
||||
await Folder.moveToFolder(folderIds[i], folderId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async onTagDrop_(event:any) {
|
||||
const tagId = event.currentTarget.getAttribute('data-tag-id');
|
||||
const dt = event.dataTransfer;
|
||||
if (!dt) return;
|
||||
|
||||
if (dt.types.indexOf('text/x-jop-note-ids') >= 0) {
|
||||
event.preventDefault();
|
||||
|
||||
const noteIds = JSON.parse(dt.getData('text/x-jop-note-ids'));
|
||||
for (let i = 0; i < noteIds.length; i++) {
|
||||
await Tag.addNote(tagId, noteIds[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async onFolderToggleClick_(event:any) {
|
||||
const folderId = event.currentTarget.getAttribute('data-folder-id');
|
||||
|
||||
this.props.dispatch({
|
||||
type: 'FOLDER_TOGGLE',
|
||||
id: folderId,
|
||||
});
|
||||
}
|
||||
|
||||
clearForceUpdateDuringSync() {
|
||||
if (this.forceUpdateDuringSyncIID_) {
|
||||
clearInterval(this.forceUpdateDuringSyncIID_);
|
||||
this.forceUpdateDuringSyncIID_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.clearForceUpdateDuringSync();
|
||||
|
||||
CommandService.instance().componentUnregisterCommands(commands);
|
||||
}
|
||||
|
||||
async header_contextMenu() {
|
||||
const menu = new Menu();
|
||||
|
||||
menu.append(
|
||||
new MenuItem(CommandService.instance().commandToMenuItem('newFolder'))
|
||||
);
|
||||
|
||||
menu.popup(bridge().window());
|
||||
}
|
||||
|
||||
async itemContextMenu(event:any) {
|
||||
const itemId = event.currentTarget.getAttribute('data-id');
|
||||
if (itemId === Folder.conflictFolderId()) return;
|
||||
|
||||
const itemType = Number(event.currentTarget.getAttribute('data-type'));
|
||||
if (!itemId || !itemType) throw new Error('No data on element');
|
||||
|
||||
let deleteMessage = '';
|
||||
let buttonLabel = _('Remove');
|
||||
if (itemType === BaseModel.TYPE_FOLDER) {
|
||||
const folder = await Folder.load(itemId);
|
||||
deleteMessage = _('Delete notebook "%s"?\n\nAll notes and sub-notebooks within this notebook will also be deleted.', substrWithEllipsis(folder.title, 0, 32));
|
||||
buttonLabel = _('Delete');
|
||||
} else if (itemType === BaseModel.TYPE_TAG) {
|
||||
const tag = await Tag.load(itemId);
|
||||
deleteMessage = _('Remove tag "%s" from all notes?', substrWithEllipsis(tag.title, 0, 32));
|
||||
} else if (itemType === BaseModel.TYPE_SEARCH) {
|
||||
deleteMessage = _('Remove this search from the sidebar?');
|
||||
}
|
||||
|
||||
const menu = new Menu();
|
||||
|
||||
let item = null;
|
||||
if (itemType === BaseModel.TYPE_FOLDER) {
|
||||
item = BaseModel.byId(this.props.folders, itemId);
|
||||
}
|
||||
|
||||
if (itemType === BaseModel.TYPE_FOLDER && !item.encryption_applied) {
|
||||
menu.append(
|
||||
new MenuItem(CommandService.instance().commandToMenuItem('newFolder', { parentId: itemId }))
|
||||
);
|
||||
}
|
||||
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
label: buttonLabel,
|
||||
click: async () => {
|
||||
const ok = bridge().showConfirmMessageBox(deleteMessage, {
|
||||
buttons: [buttonLabel, _('Cancel')],
|
||||
defaultId: 1,
|
||||
});
|
||||
if (!ok) return;
|
||||
|
||||
if (itemType === BaseModel.TYPE_FOLDER) {
|
||||
await Folder.delete(itemId);
|
||||
} else if (itemType === BaseModel.TYPE_TAG) {
|
||||
await Tag.untagAll(itemId);
|
||||
} else if (itemType === BaseModel.TYPE_SEARCH) {
|
||||
this.props.dispatch({
|
||||
type: 'SEARCH_DELETE',
|
||||
id: itemId,
|
||||
});
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
if (itemType === BaseModel.TYPE_FOLDER && !item.encryption_applied) {
|
||||
menu.append(new MenuItem(CommandService.instance().commandToMenuItem('renameFolder', { folderId: itemId })));
|
||||
|
||||
menu.append(new MenuItem({ type: 'separator' }));
|
||||
|
||||
const InteropService = require('lib/services/InteropService.js');
|
||||
|
||||
const exportMenu = new Menu();
|
||||
const ioService = new InteropService();
|
||||
const ioModules = ioService.modules();
|
||||
for (let i = 0; i < ioModules.length; i++) {
|
||||
const module = ioModules[i];
|
||||
if (module.type !== 'exporter') continue;
|
||||
|
||||
exportMenu.append(
|
||||
new MenuItem({
|
||||
label: module.fullLabel(),
|
||||
click: async () => {
|
||||
await InteropServiceHelper.export(this.props.dispatch.bind(this), module, { sourceFolderIds: [itemId] });
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
label: _('Export'),
|
||||
submenu: exportMenu,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (itemType === BaseModel.TYPE_TAG) {
|
||||
menu.append(new MenuItem(
|
||||
CommandService.instance().commandToMenuItem('renameTag', { tagId: itemId })
|
||||
));
|
||||
}
|
||||
|
||||
menu.popup(bridge().window());
|
||||
}
|
||||
|
||||
folderItem_click(folder:any) {
|
||||
this.props.dispatch({
|
||||
type: 'FOLDER_SELECT',
|
||||
id: folder ? folder.id : null,
|
||||
});
|
||||
}
|
||||
|
||||
tagItem_click(tag:any) {
|
||||
this.props.dispatch({
|
||||
type: 'TAG_SELECT',
|
||||
id: tag ? tag.id : null,
|
||||
});
|
||||
}
|
||||
|
||||
anchorItemRef(type:string, id:string) {
|
||||
if (!this.anchorItemRefs[type]) this.anchorItemRefs[type] = {};
|
||||
if (this.anchorItemRefs[type][id]) return this.anchorItemRefs[type][id];
|
||||
this.anchorItemRefs[type][id] = React.createRef();
|
||||
return this.anchorItemRefs[type][id];
|
||||
}
|
||||
|
||||
firstAnchorItemRef(type:string) {
|
||||
const refs = this.anchorItemRefs[type];
|
||||
if (!refs) return null;
|
||||
|
||||
const n = `${type}s`;
|
||||
const p = this.props as any;
|
||||
const item = p[n] && p[n].length ? p[n][0] : null;
|
||||
if (!item) return null;
|
||||
|
||||
return refs[item.id];
|
||||
}
|
||||
|
||||
renderNoteCount(count:number) {
|
||||
return <StyledNoteCount>{count}</StyledNoteCount>;
|
||||
}
|
||||
|
||||
renderExpandIcon(isExpanded:boolean, isVisible:boolean = true) {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const style:any = { width: 16, maxWidth: 16, opacity: 0.5, fontSize: Math.round(theme.toolbarIconSize * 0.8), display: 'flex', justifyContent: 'center' };
|
||||
if (!isVisible) style.visibility = 'hidden';
|
||||
return <i className={isExpanded ? 'fas fa-caret-down' : 'fas fa-caret-right'} style={style}></i>;
|
||||
}
|
||||
|
||||
renderAllNotesItem(selected:boolean) {
|
||||
return (
|
||||
<StyledListItem key="allNotesHeader" selected={selected} className={'list-item-container list-item-depth-0'} isSpecialItem={true}>
|
||||
<StyledExpandLink>{this.renderExpandIcon(false, false)}</StyledExpandLink>
|
||||
<StyledListItemAnchor
|
||||
className="list-item"
|
||||
isSpecialItem={true}
|
||||
href="#"
|
||||
selected={selected}
|
||||
onClick={() => {
|
||||
this.onAllNotesClick_();
|
||||
}}
|
||||
>
|
||||
({_('All notes')})
|
||||
</StyledListItemAnchor>
|
||||
</StyledListItem>
|
||||
);
|
||||
}
|
||||
|
||||
renderFolderItem(folder:any, selected:boolean, hasChildren:boolean, depth:number) {
|
||||
const isExpanded = this.props.collapsedFolderIds.indexOf(folder.id) < 0;
|
||||
const expandIcon = this.renderExpandIcon(isExpanded, hasChildren);
|
||||
const expandLink = hasChildren ? (
|
||||
<StyledExpandLink href="#" data-folder-id={folder.id} onClick={this.onFolderToggleClick_}>
|
||||
{expandIcon}
|
||||
</StyledExpandLink>
|
||||
) : (
|
||||
<StyledExpandLink>{expandIcon}</StyledExpandLink>
|
||||
);
|
||||
|
||||
const anchorRef = this.anchorItemRef('folder', folder.id);
|
||||
const noteCount = folder.note_count ? this.renderNoteCount(folder.note_count) : '';
|
||||
|
||||
return (
|
||||
<StyledListItem depth={depth} selected={selected} className={`list-item-container list-item-depth-${depth}`} key={folder.id} onDragStart={this.onFolderDragStart_} onDragOver={this.onFolderDragOver_} onDrop={this.onFolderDrop_} draggable={true} data-folder-id={folder.id}>
|
||||
{expandLink}
|
||||
<StyledListItemAnchor
|
||||
ref={anchorRef}
|
||||
className="list-item"
|
||||
isConflictFolder={folder.id === Folder.conflictFolderId()}
|
||||
href="#"
|
||||
selected={selected}
|
||||
data-id={folder.id}
|
||||
data-type={BaseModel.TYPE_FOLDER}
|
||||
onContextMenu={(event:any) => this.itemContextMenu(event)}
|
||||
data-folder-id={folder.id}
|
||||
onClick={() => {
|
||||
this.folderItem_click(folder);
|
||||
}}
|
||||
onDoubleClick={this.onFolderToggleClick_}
|
||||
>
|
||||
{Folder.displayTitle(folder)} {noteCount}
|
||||
</StyledListItemAnchor>
|
||||
</StyledListItem>
|
||||
);
|
||||
}
|
||||
|
||||
renderTag(tag:any, selected:boolean) {
|
||||
const anchorRef = this.anchorItemRef('tag', tag.id);
|
||||
const noteCount = Setting.value('showNoteCounts') ? this.renderNoteCount(tag.note_count) : '';
|
||||
|
||||
return (
|
||||
<StyledListItem selected={selected} className={'list-item-container'} key={tag.id} onDrop={this.onTagDrop_} data-tag-id={tag.id}>
|
||||
<StyledExpandLink>{this.renderExpandIcon(false, false)}</StyledExpandLink>
|
||||
<StyledListItemAnchor
|
||||
ref={anchorRef}
|
||||
className="list-item"
|
||||
href="#"
|
||||
selected={selected}
|
||||
data-id={tag.id}
|
||||
data-type={BaseModel.TYPE_TAG}
|
||||
onContextMenu={(event:any) => this.itemContextMenu(event)}
|
||||
onClick={() => {
|
||||
this.tagItem_click(tag);
|
||||
}}
|
||||
>
|
||||
{Tag.displayTitle(tag)} {noteCount}
|
||||
</StyledListItemAnchor>
|
||||
</StyledListItem>
|
||||
);
|
||||
}
|
||||
|
||||
makeDivider(key:string) {
|
||||
return <div style={{ height: 2, backgroundColor: 'blue' }} key={key} />;
|
||||
}
|
||||
|
||||
renderHeader(key:string, label:string, iconName:string, contextMenuHandler:Function = null, onPlusButtonClick:Function = null, extraProps:any = {}) {
|
||||
const headerClick = extraProps.onClick || null;
|
||||
delete extraProps.onClick;
|
||||
const ref = this.anchorItemRef('headers', key);
|
||||
|
||||
return (
|
||||
<div key={key} style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
<StyledHeader
|
||||
ref={ref}
|
||||
{...extraProps}
|
||||
onContextMenu={contextMenuHandler}
|
||||
onClick={(event:any) => {
|
||||
// if a custom click event is attached, trigger that.
|
||||
if (headerClick) {
|
||||
headerClick(key, event);
|
||||
}
|
||||
this.onHeaderClick_(key);
|
||||
}}
|
||||
>
|
||||
<StyledHeaderIcon className={iconName}/>
|
||||
<StyledHeaderLabel>{label}</StyledHeaderLabel>
|
||||
</StyledHeader>
|
||||
{ onPlusButtonClick && <StyledAddButton onClick={onPlusButtonClick} iconName="fas fa-plus" level={ButtonLevel.SideBarSecondary}/> }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
selectedItem() {
|
||||
if (this.props.notesParentType === 'Folder' && this.props.selectedFolderId) {
|
||||
return { type: 'folder', id: this.props.selectedFolderId };
|
||||
} else if (this.props.notesParentType === 'Tag' && this.props.selectedTagId) {
|
||||
return { type: 'tag', id: this.props.selectedTagId };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
onKeyDown(event:any) {
|
||||
const keyCode = event.keyCode;
|
||||
const selectedItem = this.selectedItem();
|
||||
|
||||
if (keyCode === 40 || keyCode === 38) {
|
||||
// DOWN / UP
|
||||
event.preventDefault();
|
||||
|
||||
const focusItems = [];
|
||||
|
||||
for (let i = 0; i < this.folderItemsOrder_.length; i++) {
|
||||
const id = this.folderItemsOrder_[i];
|
||||
focusItems.push({ id: id, ref: this.anchorItemRefs['folder'][id], type: 'folder' });
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.tagItemsOrder_.length; i++) {
|
||||
const id = this.tagItemsOrder_[i];
|
||||
focusItems.push({ id: id, ref: this.anchorItemRefs['tag'][id], type: 'tag' });
|
||||
}
|
||||
|
||||
let currentIndex = 0;
|
||||
for (let i = 0; i < focusItems.length; i++) {
|
||||
if (!selectedItem || focusItems[i].id === selectedItem.id) {
|
||||
currentIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const inc = keyCode === 38 ? -1 : +1;
|
||||
let newIndex = currentIndex + inc;
|
||||
|
||||
if (newIndex < 0) newIndex = 0;
|
||||
if (newIndex > focusItems.length - 1) newIndex = focusItems.length - 1;
|
||||
|
||||
const focusItem = focusItems[newIndex];
|
||||
|
||||
const actionName = `${focusItem.type.toUpperCase()}_SELECT`;
|
||||
|
||||
this.props.dispatch({
|
||||
type: actionName,
|
||||
id: focusItem.id,
|
||||
});
|
||||
|
||||
focusItem.ref.current.focus();
|
||||
}
|
||||
|
||||
if (keyCode === 9) {
|
||||
// TAB
|
||||
event.preventDefault();
|
||||
|
||||
if (event.shiftKey) {
|
||||
CommandService.instance().execute('focusElement', { target: 'noteBody' });
|
||||
} else {
|
||||
CommandService.instance().execute('focusElement', { target: 'noteList' });
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedItem && selectedItem.type === 'folder' && keyCode === 32) {
|
||||
// SPACE
|
||||
event.preventDefault();
|
||||
|
||||
this.props.dispatch({
|
||||
type: 'FOLDER_TOGGLE',
|
||||
id: selectedItem.id,
|
||||
});
|
||||
}
|
||||
|
||||
if (keyCode === 65 && (event.ctrlKey || event.metaKey)) {
|
||||
// Ctrl+A key
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
onHeaderClick_(key:string) {
|
||||
const toggleKey = `${key}IsExpanded`;
|
||||
const isExpanded = (this.state as any)[toggleKey];
|
||||
const newState:any = { [toggleKey]: !isExpanded };
|
||||
this.setState(newState);
|
||||
Setting.setValue(toggleKey, !isExpanded);
|
||||
}
|
||||
|
||||
onAllNotesClick_() {
|
||||
this.props.dispatch({
|
||||
type: 'SMART_FILTER_SELECT',
|
||||
id: ALL_NOTES_FILTER_ID,
|
||||
});
|
||||
}
|
||||
|
||||
renderSynchronizeButton(type:string) {
|
||||
const label = type === 'sync' ? _('Synchronise') : _('Cancel');
|
||||
const iconAnimation = type !== 'sync' ? 'icon-infinite-rotation 1s linear infinite' : '';
|
||||
|
||||
return (
|
||||
<StyledSynchronizeButton
|
||||
level={ButtonLevel.SideBarSecondary}
|
||||
iconName="icon-sync"
|
||||
key="sync_button"
|
||||
iconAnimation={iconAnimation}
|
||||
title={label}
|
||||
onClick={() => {
|
||||
CommandService.instance().execute('synchronize', { syncStarted: type !== 'sync' });
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
onAddFolderButtonClick() {
|
||||
CommandService.instance().execute('newFolder');
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const items = [];
|
||||
|
||||
items.push(
|
||||
this.renderHeader('folderHeader', _('Notebooks'), 'icon-notebooks', this.header_contextMenu, this.onAddFolderButtonClick, {
|
||||
onDrop: this.onFolderDrop_,
|
||||
['data-folder-id']: '',
|
||||
toggleblock: 1,
|
||||
})
|
||||
);
|
||||
|
||||
if (this.props.folders.length) {
|
||||
const allNotesSelected = this.props.notesParentType === 'SmartFilter' && this.props.selectedSmartFilterId === ALL_NOTES_FILTER_ID;
|
||||
const result = shared.renderFolders(this.props, this.renderFolderItem.bind(this));
|
||||
const folderItems = [this.renderAllNotesItem(allNotesSelected)].concat(result.items);
|
||||
this.folderItemsOrder_ = result.order;
|
||||
items.push(
|
||||
<div className="folders" key="folder_items" style={{ display: this.state.folderHeaderIsExpanded ? 'block' : 'none', paddingBottom: 10 }}>
|
||||
{folderItems}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
items.push(
|
||||
this.renderHeader('tagHeader', _('Tags'), 'icon-tags', null, null, {
|
||||
toggleblock: 1,
|
||||
})
|
||||
);
|
||||
|
||||
if (this.props.tags.length) {
|
||||
const result = shared.renderTags(this.props, this.renderTag.bind(this));
|
||||
const tagItems = result.items;
|
||||
this.tagItemsOrder_ = result.order;
|
||||
|
||||
items.push(
|
||||
<div className="tags" key="tag_items" style={{ display: this.state.tagHeaderIsExpanded ? 'block' : 'none' }}>
|
||||
{tagItems}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let decryptionReportText = '';
|
||||
if (this.props.decryptionWorker && this.props.decryptionWorker.state !== 'idle' && this.props.decryptionWorker.itemCount) {
|
||||
decryptionReportText = _('Decrypting items: %d/%d', this.props.decryptionWorker.itemIndex + 1, this.props.decryptionWorker.itemCount);
|
||||
}
|
||||
|
||||
let resourceFetcherText = '';
|
||||
if (this.props.resourceFetcher && this.props.resourceFetcher.toFetchCount) {
|
||||
resourceFetcherText = _('Fetching resources: %d/%d', this.props.resourceFetcher.fetchingCount, this.props.resourceFetcher.toFetchCount);
|
||||
}
|
||||
|
||||
const lines = Synchronizer.reportToLines(this.props.syncReport);
|
||||
if (resourceFetcherText) lines.push(resourceFetcherText);
|
||||
if (decryptionReportText) lines.push(decryptionReportText);
|
||||
const syncReportText = [];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
syncReportText.push(
|
||||
<StyledSyncReportText key={i}>
|
||||
{lines[i]}
|
||||
</StyledSyncReportText>
|
||||
);
|
||||
}
|
||||
|
||||
const syncButton = this.renderSynchronizeButton(this.props.syncStarted ? 'cancel' : 'sync');
|
||||
|
||||
const syncReportComp = !syncReportText.length ? null : (
|
||||
<StyledSyncReport key="sync_report">
|
||||
{syncReportText}
|
||||
</StyledSyncReport>
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledRoot ref={this.rootRef} onKeyDown={this.onKeyDown} className="side-bar">
|
||||
<div style={{ flex: 1, overflowX: 'hidden', overflowY: 'auto' }}>{items}</div>
|
||||
<div style={{ flex: 0, padding: theme.mainPadding }}>
|
||||
{syncReportComp}
|
||||
{syncButton}
|
||||
</div>
|
||||
</StyledRoot>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state:any) => {
|
||||
return {
|
||||
folders: state.folders,
|
||||
tags: state.tags,
|
||||
searches: state.searches,
|
||||
syncStarted: state.syncStarted,
|
||||
syncReport: state.syncReport,
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
selectedTagId: state.selectedTagId,
|
||||
selectedSearchId: state.selectedSearchId,
|
||||
selectedSmartFilterId: state.selectedSmartFilterId,
|
||||
notesParentType: state.notesParentType,
|
||||
locale: state.settings.locale,
|
||||
themeId: state.settings.theme,
|
||||
collapsedFolderIds: state.collapsedFolderIds,
|
||||
decryptionWorker: state.decryptionWorker,
|
||||
resourceFetcher: state.resourceFetcher,
|
||||
sidebarVisibility: state.sidebarVisibility,
|
||||
noteListVisibility: state.noteListVisibility,
|
||||
};
|
||||
};
|
||||
|
||||
const SideBar = connect(mapStateToProps)(SideBarComponent);
|
||||
|
||||
module.exports = { SideBar };
|
||||
@@ -0,0 +1,122 @@
|
||||
import Button from '../../Button/Button';
|
||||
const styled = require('styled-components').default;
|
||||
|
||||
export const StyledRoot = styled.div`
|
||||
background-color: ${(props:any) => props.theme.backgroundColor2};
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
export const StyledHeader = styled.div`
|
||||
//height: ${(props:any) => props.theme.topRowHeight}px;
|
||||
//text-decoration: none;
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
padding: ${(props:any) => props.theme.mainPadding}px;
|
||||
padding-bottom: ${(props:any) => props.theme.mainPadding / 2}px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
text-transform: uppercase;
|
||||
//cursor: pointer;
|
||||
`;
|
||||
|
||||
export const StyledHeaderIcon = styled.i`
|
||||
font-size: ${(props:any) => props.theme.toolbarIconSize}px;
|
||||
color: ${(props:any) => props.theme.color2};
|
||||
margin-right: 8px;
|
||||
`;
|
||||
|
||||
export const StyledHeaderLabel = styled.span`
|
||||
flex: 1;
|
||||
color: ${(props:any) => props.theme.color2};
|
||||
font-size: ${(props:any) => Math.round(props.theme.fontSize * 1.1)}px;
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
export const StyledListItem = styled.div`
|
||||
box-sizing: border-box;
|
||||
height: 25px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding-left: ${(props:any) => props.theme.mainPadding + ('depth' in props ? props.depth : 0) * 16}px;
|
||||
background: ${(props:any) => props.selected ? props.theme.selectedColor2 : 'none'};
|
||||
text-transform: ${(props:any) => props.isSpecialItem ? 'uppercase' : 'none'};
|
||||
transition: 0.1s;
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props:any) => props.theme.backgroundColorHover2};
|
||||
}
|
||||
`;
|
||||
|
||||
function listItemTextColor(props:any) {
|
||||
if (props.isConflictFolder) return props.theme.colorError2;
|
||||
if (props.isSpecialItem) return props.theme.colorFaded2;
|
||||
return props.theme.color2;
|
||||
}
|
||||
|
||||
export const StyledListItemAnchor = styled.a`
|
||||
font-size: ${(props:any) => Math.round(props.theme.fontSize * 1.0833333)}px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
color: ${(props:any) => listItemTextColor(props)};
|
||||
cursor: default;
|
||||
opacity: ${(props:any) => props.selected ? 1 : 0.8};
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
export const StyledExpandLink = styled.a`
|
||||
color: ${(props:any) => props.theme.color2};
|
||||
cursor: default;
|
||||
opacity: 0.8;
|
||||
text-decoration: none;
|
||||
padding-right: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 16px;
|
||||
max-width: 16px;
|
||||
min-width: 16px;
|
||||
`;
|
||||
|
||||
export const StyledNoteCount = styled.div`
|
||||
color: ${(props:any) => props.theme.color2};
|
||||
padding-left: 8px;
|
||||
opacity: 0.5;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
export const StyledSynchronizeButton = styled(Button)`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const StyledAddButton = styled(Button)`
|
||||
border: none;
|
||||
padding-right: 15px;
|
||||
padding-top: 4px;
|
||||
`;
|
||||
|
||||
export const StyledSyncReport = styled.div`
|
||||
font-size: ${(props:any) => Math.round(props.theme.fontSize * 0.9)}px;
|
||||
color: ${(props:any) => props.theme.color2};
|
||||
opacity: 0.5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 10px;
|
||||
word-wrap: break-word;
|
||||
`;
|
||||
|
||||
export const StyledSyncReportText = styled.div`
|
||||
color: ${(props:any) => props.theme.color2};
|
||||
word-wrap: break-word;
|
||||
width: 100%;
|
||||
`;
|
||||
@@ -1,159 +0,0 @@
|
||||
const React = require('react');
|
||||
const { connect } = require('react-redux');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const { Header } = require('./Header/Header.min.js');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { ReportService } = require('lib/services/report.js');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
class StatusScreenComponent extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
report: [],
|
||||
};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this.resfreshScreen();
|
||||
}
|
||||
|
||||
async resfreshScreen() {
|
||||
const service = new ReportService();
|
||||
const report = await service.status(Setting.value('sync.target'));
|
||||
this.setState({ report: report });
|
||||
}
|
||||
|
||||
async exportDebugReportClick() {
|
||||
const filename = `syncReport-${new Date().getTime()}.csv`;
|
||||
|
||||
const filePath = bridge().showSaveDialog({
|
||||
title: _('Please select where the sync status should be exported to'),
|
||||
defaultPath: filename,
|
||||
});
|
||||
|
||||
if (!filePath) return;
|
||||
|
||||
const service = new ReportService();
|
||||
const csv = await service.basicItemList({ format: 'csv' });
|
||||
await fs.writeFileSync(filePath, csv);
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const style = this.props.style;
|
||||
|
||||
const headerStyle = Object.assign({}, theme.headerStyle, { width: style.width });
|
||||
const retryStyle = Object.assign({}, theme.urlStyle, { marginLeft: 5 });
|
||||
const retryAllStyle = Object.assign({}, theme.urlStyle, { marginTop: 5, display: 'inline-block' });
|
||||
|
||||
const containerPadding = 10;
|
||||
|
||||
const containerStyle = Object.assign({}, theme.containerStyle, {
|
||||
padding: containerPadding,
|
||||
height: style.height - theme.headerHeight - containerPadding * 2,
|
||||
});
|
||||
|
||||
function renderSectionTitleHtml(key, title) {
|
||||
return (
|
||||
<h2 key={`section_${key}`} style={theme.h2Style}>
|
||||
{title}
|
||||
</h2>
|
||||
);
|
||||
}
|
||||
|
||||
function renderSectionRetryAllHtml(key, retryAllHandler) {
|
||||
return (
|
||||
<a key={`retry_all_${key}`} href="#" onClick={retryAllHandler} style={retryAllStyle}>
|
||||
{_('Retry All')}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
const renderSectionHtml = (key, section) => {
|
||||
const itemsHtml = [];
|
||||
|
||||
itemsHtml.push(renderSectionTitleHtml(section.title, section.title));
|
||||
|
||||
for (const n in section.body) {
|
||||
if (!section.body.hasOwnProperty(n)) continue;
|
||||
const item = section.body[n];
|
||||
let text = '';
|
||||
|
||||
let retryLink = null;
|
||||
if (typeof item === 'object') {
|
||||
if (item.canRetry) {
|
||||
const onClick = async () => {
|
||||
await item.retryHandler();
|
||||
this.resfreshScreen();
|
||||
};
|
||||
|
||||
retryLink = (
|
||||
<a href="#" onClick={onClick} style={retryStyle}>
|
||||
{_('Retry')}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
text = item.text;
|
||||
} else {
|
||||
text = item;
|
||||
}
|
||||
|
||||
if (!text) text = '\xa0';
|
||||
|
||||
itemsHtml.push(
|
||||
<div style={theme.textStyle} key={`item_${n}`}>
|
||||
<span>{text}</span>
|
||||
{retryLink}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (section.canRetryAll) {
|
||||
itemsHtml.push(renderSectionRetryAllHtml(section.title, section.retryAllHandler));
|
||||
}
|
||||
|
||||
return <div key={key}>{itemsHtml}</div>;
|
||||
};
|
||||
|
||||
function renderBodyHtml(report) {
|
||||
const sectionsHtml = [];
|
||||
|
||||
for (let i = 0; i < report.length; i++) {
|
||||
const section = report[i];
|
||||
if (!section.body.length) continue;
|
||||
sectionsHtml.push(renderSectionHtml(i, section));
|
||||
}
|
||||
|
||||
return <div>{sectionsHtml}</div>;
|
||||
}
|
||||
|
||||
const body = renderBodyHtml(this.state.report);
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<Header style={headerStyle} />
|
||||
<div style={containerStyle}>
|
||||
<a style={theme.textStyle} onClick={() => this.exportDebugReportClick()} href="#">
|
||||
Export debug report
|
||||
</a>
|
||||
{body}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
theme: state.settings.theme,
|
||||
settings: state.settings,
|
||||
locale: state.settings.locale,
|
||||
};
|
||||
};
|
||||
|
||||
const StatusScreen = connect(mapStateToProps)(StatusScreenComponent);
|
||||
|
||||
module.exports = { StatusScreen };
|
||||
@@ -0,0 +1,163 @@
|
||||
import * as React from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import ButtonBar from '../ConfigScreen/ButtonBar';
|
||||
|
||||
const { connect } = require('react-redux');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { ReportService } = require('lib/services/report.js');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
interface Props {
|
||||
themeId: string,
|
||||
style: any,
|
||||
dispatch: Function,
|
||||
}
|
||||
|
||||
async function exportDebugReportClick() {
|
||||
const filename = `syncReport-${new Date().getTime()}.csv`;
|
||||
|
||||
const filePath = bridge().showSaveDialog({
|
||||
title: _('Please select where the sync status should be exported to'),
|
||||
defaultPath: filename,
|
||||
});
|
||||
|
||||
if (!filePath) return;
|
||||
|
||||
const service = new ReportService();
|
||||
const csv = await service.basicItemList({ format: 'csv' });
|
||||
await fs.writeFileSync(filePath, csv);
|
||||
}
|
||||
|
||||
function StatusScreen(props:Props) {
|
||||
const [report, setReport] = useState<any[]>([]);
|
||||
|
||||
async function resfreshScreen() {
|
||||
const service = new ReportService();
|
||||
const r = await service.status(Setting.value('sync.target'));
|
||||
setReport(r);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
resfreshScreen();
|
||||
}, []);
|
||||
|
||||
const theme = themeStyle(props.themeId);
|
||||
const style = { ...props.style,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
};
|
||||
|
||||
const retryStyle = Object.assign({}, theme.urlStyle, { marginLeft: 5 });
|
||||
const retryAllStyle = Object.assign({}, theme.urlStyle, { marginTop: 5, display: 'inline-block' });
|
||||
|
||||
const containerPadding = theme.configScreenPadding;
|
||||
|
||||
const containerStyle = Object.assign({}, theme.containerStyle, {
|
||||
padding: containerPadding,
|
||||
flex: 1,
|
||||
});
|
||||
|
||||
function renderSectionTitleHtml(key:string, title:string) {
|
||||
return (
|
||||
<h2 key={`section_${key}`} style={theme.h2Style}>
|
||||
{title}
|
||||
</h2>
|
||||
);
|
||||
}
|
||||
|
||||
function renderSectionRetryAllHtml(key:string, retryAllHandler:any) {
|
||||
return (
|
||||
<a key={`retry_all_${key}`} href="#" onClick={retryAllHandler} style={retryAllStyle}>
|
||||
{_('Retry All')}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
const renderSectionHtml = (key:string, section:any) => {
|
||||
const itemsHtml = [];
|
||||
|
||||
itemsHtml.push(renderSectionTitleHtml(section.title, section.title));
|
||||
|
||||
for (const n in section.body) {
|
||||
if (!section.body.hasOwnProperty(n)) continue;
|
||||
const item = section.body[n];
|
||||
let text = '';
|
||||
|
||||
let retryLink = null;
|
||||
if (typeof item === 'object') {
|
||||
if (item.canRetry) {
|
||||
const onClick = async () => {
|
||||
await item.retryHandler();
|
||||
resfreshScreen();
|
||||
};
|
||||
|
||||
retryLink = (
|
||||
<a href="#" onClick={onClick} style={retryStyle}>
|
||||
{_('Retry')}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
text = item.text;
|
||||
} else {
|
||||
text = item;
|
||||
}
|
||||
|
||||
if (!text) text = '\xa0';
|
||||
|
||||
itemsHtml.push(
|
||||
<div style={theme.textStyle} key={`item_${n}`}>
|
||||
<span>{text}</span>
|
||||
{retryLink}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (section.canRetryAll) {
|
||||
itemsHtml.push(renderSectionRetryAllHtml(section.title, section.retryAllHandler));
|
||||
}
|
||||
|
||||
return <div key={key}>{itemsHtml}</div>;
|
||||
};
|
||||
|
||||
function renderBodyHtml(report:any) {
|
||||
const sectionsHtml = [];
|
||||
|
||||
for (let i = 0; i < report.length; i++) {
|
||||
const section = report[i];
|
||||
if (!section.body.length) continue;
|
||||
sectionsHtml.push(renderSectionHtml(`${i}`, section));
|
||||
}
|
||||
|
||||
return <div>{sectionsHtml}</div>;
|
||||
}
|
||||
|
||||
const body = renderBodyHtml(report);
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<div style={containerStyle}>
|
||||
<a style={theme.textStyle} onClick={() => exportDebugReportClick()} href="#">
|
||||
Export debug report
|
||||
</a>
|
||||
{body}
|
||||
</div>
|
||||
<ButtonBar
|
||||
onCancelClick={() => props.dispatch({ type: 'NAV_BACK' })}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state:any) => {
|
||||
return {
|
||||
themeId: state.settings.theme,
|
||||
settings: state.settings,
|
||||
locale: state.settings.locale,
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(StatusScreen);
|
||||
|
||||
@@ -4,7 +4,7 @@ const { themeStyle } = require('lib/theme');
|
||||
|
||||
class TagItemComponent extends React.Component {
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const style = Object.assign({}, theme.tagStyle);
|
||||
const title = this.props.title;
|
||||
|
||||
@@ -13,7 +13,7 @@ class TagItemComponent extends React.Component {
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return { theme: state.settings.theme };
|
||||
return { themeId: state.settings.theme };
|
||||
};
|
||||
|
||||
const TagItem = connect(mapStateToProps)(TagItemComponent);
|
||||
|
||||
@@ -6,7 +6,7 @@ const TagItem = require('./TagItem.min.js');
|
||||
class TagListComponent extends React.Component {
|
||||
render() {
|
||||
const style = Object.assign({}, this.props.style);
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const tags = this.props.items;
|
||||
|
||||
style.display = 'flex';
|
||||
@@ -15,11 +15,13 @@ class TagListComponent extends React.Component {
|
||||
style.boxSizing = 'border-box';
|
||||
style.fontSize = theme.fontSize;
|
||||
style.whiteSpace = 'nowrap';
|
||||
style.height = 25;
|
||||
// style.height = 40;
|
||||
style.paddingTop = 8;
|
||||
style.paddingBottom = 8;
|
||||
|
||||
const tagItems = [];
|
||||
if (tags && tags.length > 0) {
|
||||
// Sort by id for now, but probably needs to be changed in the future.
|
||||
|
||||
tags.sort((a, b) => {
|
||||
return a.title < b.title ? -1 : +1;
|
||||
});
|
||||
@@ -42,7 +44,7 @@ class TagListComponent extends React.Component {
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return { theme: state.settings.theme };
|
||||
return { themeId: state.settings.theme };
|
||||
};
|
||||
|
||||
const TagList = connect(mapStateToProps)(TagListComponent);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import * as React from 'react';
|
||||
import styles_ from './styles';
|
||||
import { ToolbarButtonInfo } from 'lib/services/CommandService';
|
||||
|
||||
export enum Value {
|
||||
Markdown = 'markdown',
|
||||
RichText = 'richText',
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
themeId: number,
|
||||
value: Value,
|
||||
toolbarButtonInfo: ToolbarButtonInfo,
|
||||
}
|
||||
|
||||
export default function ToggleEditorsButton(props:Props) {
|
||||
const style = styles_(props);
|
||||
|
||||
return (
|
||||
<button style={style.button} disabled={!props.toolbarButtonInfo.enabled} aria-label={props.toolbarButtonInfo.title} title={props.toolbarButtonInfo.title} type="button" className="tox-tbtn" aria-pressed="false" onClick={props.toolbarButtonInfo.onClick}>
|
||||
<div style={style.leftInnerButton}>
|
||||
<i style={style.leftIcon} className="fab fa-markdown"></i>
|
||||
</div>
|
||||
<div style={style.rightInnerButton}>
|
||||
<i style={style.rightIcon} className="fas fa-edit"></i>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Props, Value } from '../ToggleEditorsButton';
|
||||
const { buildStyle } = require('lib/theme');
|
||||
|
||||
export default function styles(props:Props) {
|
||||
return buildStyle(['ToggleEditorsButton', props.value], props.themeId, (theme: any) => {
|
||||
const iconSize = 15;
|
||||
const mdIconWidth = iconSize * 1.25;
|
||||
const buttonHeight = theme.toolbarHeight - 8;
|
||||
const mdIconPadding = Math.round((buttonHeight - iconSize) / 2) + 3;
|
||||
|
||||
const innerButton:any = {
|
||||
borderStyle: 'solid',
|
||||
borderColor: theme.color3,
|
||||
borderWidth: 1,
|
||||
borderRadius: 0,
|
||||
width: mdIconWidth + mdIconPadding * 2,
|
||||
height: buttonHeight,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
};
|
||||
|
||||
const output:any = {
|
||||
button: {
|
||||
border: 'none',
|
||||
padding: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
background: 'none',
|
||||
},
|
||||
leftInnerButton: {
|
||||
...innerButton,
|
||||
borderTopLeftRadius: 4,
|
||||
borderBottomLeftRadius: 4,
|
||||
},
|
||||
rightInnerButton: {
|
||||
...innerButton,
|
||||
borderTopRightRadius: 4,
|
||||
borderBottomRightRadius: 4,
|
||||
},
|
||||
leftIcon: {
|
||||
fontSize: iconSize,
|
||||
position: 'relative',
|
||||
top: 1,
|
||||
color: theme.color3,
|
||||
},
|
||||
rightIcon: {
|
||||
fontSize: iconSize - 1,
|
||||
borderLeft: 'none',
|
||||
position: 'relative',
|
||||
top: 1,
|
||||
color: theme.color3,
|
||||
},
|
||||
};
|
||||
|
||||
if (props.value === Value.Markdown) {
|
||||
output.leftInnerButton.backgroundColor = theme.color3;
|
||||
output.leftIcon.color = theme.backgroundColor3;
|
||||
output.rightInnerButton.opacity = 0.5;
|
||||
} else if (props.value === Value.RichText) {
|
||||
output.rightInnerButton.backgroundColor = theme.color3;
|
||||
output.rightIcon.color = theme.backgroundColor3;
|
||||
output.leftInnerButton.opacity = 0.5;
|
||||
}
|
||||
|
||||
return output;
|
||||
});
|
||||
}
|
||||
@@ -1,22 +1,32 @@
|
||||
const React = require('react');
|
||||
const { connect } = require('react-redux');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
const ToolbarButton = require('./ToolbarButton.min.js');
|
||||
const ToolbarButton = require('./ToolbarButton/ToolbarButton.js').default;
|
||||
const ToolbarSpace = require('./ToolbarSpace.min.js');
|
||||
const ToggleEditorsButton = require('./ToggleEditorsButton/ToggleEditorsButton.js').default;
|
||||
|
||||
class ToolbarComponent extends React.Component {
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const style = Object.assign({
|
||||
// height: theme.toolbarHeight,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
borderBottom: `1px solid ${theme.dividerColor}`,
|
||||
boxSizing: 'border-box',
|
||||
backgroundColor: theme.backgroundColor3,
|
||||
padding: theme.toolbarPadding,
|
||||
paddingRight: theme.mainPadding,
|
||||
}, this.props.style);
|
||||
|
||||
const itemComps = [];
|
||||
const groupStyle = {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
boxSizing: 'border-box',
|
||||
};
|
||||
|
||||
const leftItemComps = [];
|
||||
const centerItemComps = [];
|
||||
const rightItemComps = [];
|
||||
|
||||
if (this.props.items) {
|
||||
for (let i = 0; i < this.props.items.length; i++) {
|
||||
@@ -30,31 +40,47 @@ class ToolbarComponent extends React.Component {
|
||||
const props = Object.assign(
|
||||
{
|
||||
key: key,
|
||||
theme: this.props.theme,
|
||||
themeId: this.props.themeId,
|
||||
},
|
||||
o
|
||||
);
|
||||
|
||||
if (this.props.disabled) props.disabled = true;
|
||||
|
||||
if (itemType === 'button') {
|
||||
itemComps.push(<ToolbarButton {...props} />);
|
||||
if (o.name === 'toggleEditors') {
|
||||
rightItemComps.push(<ToggleEditorsButton
|
||||
key={o.name}
|
||||
value={'markdown'}
|
||||
themeId={this.props.themeId}
|
||||
toolbarButtonInfo={o}
|
||||
/>);
|
||||
} else if (itemType === 'button') {
|
||||
const target = ['historyForward', 'historyBackward', 'startExternalEditing'].includes(o.name) ? leftItemComps : centerItemComps;
|
||||
target.push(<ToolbarButton {...props} />);
|
||||
} else if (itemType === 'separator') {
|
||||
itemComps.push(<ToolbarSpace {...props} />);
|
||||
centerItemComps.push(<ToolbarSpace {...props} />);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="editor-toolbar" style={style}>
|
||||
{itemComps}
|
||||
<div style={groupStyle}>
|
||||
{leftItemComps}
|
||||
</div>
|
||||
<div style={groupStyle}>
|
||||
{centerItemComps}
|
||||
</div>
|
||||
<div style={Object.assign({}, groupStyle, { flex: 1, justifyContent: 'flex-end' })}>
|
||||
{rightItemComps}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return { theme: state.settings.theme };
|
||||
return { themeId: state.settings.theme };
|
||||
};
|
||||
|
||||
const Toolbar = connect(mapStateToProps)(ToolbarComponent);
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
const React = require('react');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
|
||||
class ToolbarButton extends React.Component {
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
|
||||
const style = Object.assign({}, theme.toolbarStyle);
|
||||
|
||||
const title = this.props.title ? this.props.title : '';
|
||||
const tooltip = this.props.tooltip ? this.props.tooltip : title;
|
||||
|
||||
let icon = null;
|
||||
if (this.props.iconName) {
|
||||
const iconStyle = {
|
||||
fontSize: Math.round(theme.fontSize * 1.5),
|
||||
color: theme.iconColor,
|
||||
};
|
||||
if (title) iconStyle.marginRight = 5;
|
||||
icon = <i style={iconStyle} className={`fas ${this.props.iconName}`}></i>;
|
||||
}
|
||||
|
||||
// Keep this for legacy compatibility but for consistency we should use "disabled" prop
|
||||
let isEnabled = !('enabled' in this.props) || this.props.enabled === true;
|
||||
if (this.props.disabled) isEnabled = false;
|
||||
|
||||
const classes = ['button'];
|
||||
if (!isEnabled) classes.push('disabled');
|
||||
|
||||
const finalStyle = Object.assign({}, style, {
|
||||
opacity: isEnabled ? 1 : 0.4,
|
||||
});
|
||||
|
||||
return (
|
||||
<a
|
||||
className={classes.join(' ')}
|
||||
style={finalStyle}
|
||||
title={tooltip}
|
||||
href="#"
|
||||
onClick={() => {
|
||||
if (isEnabled && this.props.onClick) this.props.onClick();
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
{title}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ToolbarButton;
|
||||
@@ -0,0 +1,63 @@
|
||||
import * as React from 'react';
|
||||
import { ToolbarButtonInfo } from 'lib/services/CommandService';
|
||||
import { StyledRoot, StyledIconSpan, StyledIconI } from './styles';
|
||||
|
||||
interface Props {
|
||||
readonly themeId: number,
|
||||
readonly toolbarButtonInfo?: ToolbarButtonInfo,
|
||||
readonly title?: string,
|
||||
readonly tooltip?: string,
|
||||
readonly iconName?: string,
|
||||
readonly disabled?: boolean,
|
||||
readonly backgroundHover?: boolean,
|
||||
}
|
||||
|
||||
function isFontAwesomeIcon(iconName:string) {
|
||||
const s = iconName.split(' ');
|
||||
return s.length === 2 && ['fa', 'fas'].includes(s[0]);
|
||||
}
|
||||
|
||||
function getProp(props:Props, name:string, defaultValue:any = null) {
|
||||
if (props.toolbarButtonInfo && (name in props.toolbarButtonInfo)) return (props.toolbarButtonInfo as any)[name];
|
||||
if (!(name in props)) return defaultValue;
|
||||
return (props as any)[name];
|
||||
}
|
||||
|
||||
export default function ToolbarButton(props:Props) {
|
||||
const title = getProp(props, 'title', '');
|
||||
const tooltip = getProp(props, 'tooltip', title);
|
||||
|
||||
let icon = null;
|
||||
const iconName = getProp(props, 'iconName');
|
||||
if (iconName) {
|
||||
const IconClass = isFontAwesomeIcon(iconName) ? StyledIconI : StyledIconSpan;
|
||||
icon = <IconClass className={iconName} title={title}/>;
|
||||
}
|
||||
|
||||
// Keep this for legacy compatibility but for consistency we should use "disabled" prop
|
||||
let isEnabled = getProp(props, 'enabled', null);
|
||||
if (isEnabled === null) isEnabled = true;
|
||||
if (props.disabled) isEnabled = false;
|
||||
|
||||
const classes = ['button'];
|
||||
if (!isEnabled) classes.push('disabled');
|
||||
|
||||
const onClick = getProp(props, 'onClick');
|
||||
|
||||
return (
|
||||
<StyledRoot
|
||||
className={classes.join(' ')}
|
||||
disabled={!isEnabled}
|
||||
title={tooltip}
|
||||
href="#"
|
||||
hasTitle={!!title}
|
||||
onClick={() => {
|
||||
if (isEnabled && onClick) onClick();
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
{title}
|
||||
</StyledRoot>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
const styled = require('styled-components').default;
|
||||
const { css } = require('styled-components');
|
||||
|
||||
interface RootProps {
|
||||
readonly theme: any;
|
||||
readonly disabled: boolean;
|
||||
readonly hasTitle: boolean;
|
||||
}
|
||||
|
||||
export const StyledRoot = styled.a<RootProps>`
|
||||
opacity: ${(props:RootProps) => props.disabled ? 0.3 : 1};
|
||||
height: ${(props:RootProps) => props.theme.toolbarHeight}px;
|
||||
min-height: ${(props:RootProps) => props.theme.toolbarHeight}px;
|
||||
width: ${(props:RootProps) => props.hasTitle ? 'auto' : `${props.theme.toolbarHeight}px`};
|
||||
max-width: ${(props:RootProps) => props.hasTitle ? 'auto' : `${props.theme.toolbarHeight}px`};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: default;
|
||||
border-radius: 3px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props:RootProps) => props.disabled ? 'none' : props.theme.backgroundColorHover3};
|
||||
}
|
||||
`;
|
||||
|
||||
interface IconProps {
|
||||
readonly theme: any;
|
||||
readonly title: string;
|
||||
}
|
||||
|
||||
const iconStyle = css<IconProps>`
|
||||
font-size: ${(props:IconProps) => props.theme.toolbarIconSize}px;
|
||||
color: ${(props:IconProps) => props.theme.color3};
|
||||
margin-right: ${(props:IconProps) => props.title ? 5 : 0}px;
|
||||
`;
|
||||
|
||||
export const StyledIconI = styled.i`${iconStyle}`;
|
||||
export const StyledIconSpan = styled.span`${iconStyle}`;
|
||||
@@ -3,7 +3,7 @@ const { themeStyle } = require('lib/theme');
|
||||
|
||||
class ToolbarSpace extends React.Component {
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const style = Object.assign({}, theme.toolbarStyle);
|
||||
style.minWidth = style.height / 2;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { createSelector } = require('reselect');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
|
||||
const themeSelector = (state, props) => themeStyle(props.theme);
|
||||
const themeSelector = (state, props) => themeStyle(props.themeId);
|
||||
|
||||
const style = createSelector(
|
||||
themeSelector,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { createSelector } = require('reselect');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
|
||||
const themeSelector = (state, props) => themeStyle(props.theme);
|
||||
const themeSelector = (state, props) => themeStyle(props.themeId);
|
||||
|
||||
const style = createSelector(
|
||||
themeSelector,
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
const styled = require('styled-components').default;
|
||||
const Color = require('color');
|
||||
|
||||
const StyledInput = styled.input`
|
||||
border: 1px solid ${(props:any) => Color(props.theme.color3).alpha(0.6)};
|
||||
border-radius: 3px;
|
||||
font-size: ${(props:any) => props.theme.fontSize}px;
|
||||
color: ${(props:any) => props.theme.color};
|
||||
padding: 0 8px;
|
||||
height: ${(props:any) => `${props.theme.toolbarHeight}px`};
|
||||
max-height: ${(props:any) => `${props.theme.toolbarHeight}px`};
|
||||
box-sizing: border-box;
|
||||
background-color: ${(props:any) => Color(props.theme.backgroundColor4).alpha(0.5)};
|
||||
|
||||
&::placeholder {
|
||||
color: ${(props:any) => props.theme.colorFaded};
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: ${(props:any) => props.theme.backgroundColor4};
|
||||
border: 1px solid ${(props:any) => props.theme.color3};
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledInput;
|
||||
@@ -0,0 +1,7 @@
|
||||
const styled = require('styled-components').default;
|
||||
|
||||
const StyledInput = styled.input`
|
||||
|
||||
`;
|
||||
|
||||
export default StyledInput;
|
||||
@@ -9,9 +9,12 @@
|
||||
-->
|
||||
<title>Joplin</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="stylesheet" href="style/icons/style.css">
|
||||
<!-- TODO: Remove once all icons have been swapped -->
|
||||
<link rel="stylesheet" href="node_modules/@fortawesome/fontawesome-free/css/all.min.css">
|
||||
<link rel="stylesheet" href="node_modules/react-datetime/css/react-datetime.css">
|
||||
<link rel="stylesheet" href="node_modules/smalltalk/css/smalltalk.css">
|
||||
<link rel="stylesheet" href="node_modules/roboto-fontface/css/roboto/roboto-fontface.css">
|
||||
<link rel="stylesheet" href="node_modules/codemirror/lib/codemirror.css">
|
||||
|
||||
<style>
|
||||
|
||||
Generated
+539
-11
@@ -28,6 +28,123 @@
|
||||
"integrity": "sha512-uPHXapEmUtlUKTBx4asWMlxtFUWXzEY0KVEgU7QKhgO2LJzzM3kYxM6yOyUZTtYE6mhK4dDn3FDut9SCQWHzgg==",
|
||||
"optional": true
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
|
||||
"integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
|
||||
"requires": {
|
||||
"@babel/highlight": "^7.10.4"
|
||||
}
|
||||
},
|
||||
"@babel/generator": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz",
|
||||
"integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.11.0",
|
||||
"jsesc": "^2.5.1",
|
||||
"source-map": "^0.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/types": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
|
||||
"integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.10.4",
|
||||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"jsesc": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
|
||||
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
|
||||
},
|
||||
"to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/helper-annotate-as-pure": {
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz",
|
||||
"integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.10.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/types": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
|
||||
"integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.10.4",
|
||||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/helper-function-name": {
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
|
||||
"integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
|
||||
"requires": {
|
||||
"@babel/helper-get-function-arity": "^7.10.4",
|
||||
"@babel/template": "^7.10.4",
|
||||
"@babel/types": "^7.10.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/types": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
|
||||
"integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.10.4",
|
||||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/helper-get-function-arity": {
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
|
||||
"integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.10.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/types": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
|
||||
"integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.10.4",
|
||||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/helper-module-imports": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz",
|
||||
@@ -36,6 +153,84 @@
|
||||
"@babel/types": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"@babel/helper-split-export-declaration": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
|
||||
"integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
|
||||
"requires": {
|
||||
"@babel/types": "^7.11.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/types": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
|
||||
"integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.10.4",
|
||||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/helper-validator-identifier": {
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
|
||||
"integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw=="
|
||||
},
|
||||
"@babel/highlight": {
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
|
||||
"integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.10.4",
|
||||
"chalk": "^2.0.0",
|
||||
"js-tokens": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.0.tgz",
|
||||
"integrity": "sha512-qvRvi4oI8xii8NllyEc4MDJjuZiNaRzyb7Y7lup1NqJV8TZHF4O27CcP+72WPn/k1zkgJ6WJfnIbk4jTsVAZHw=="
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz",
|
||||
@@ -51,6 +246,84 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/template": {
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
|
||||
"integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
"@babel/parser": "^7.10.4",
|
||||
"@babel/types": "^7.10.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/types": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
|
||||
"integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.10.4",
|
||||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/traverse": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz",
|
||||
"integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==",
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
"@babel/generator": "^7.11.0",
|
||||
"@babel/helper-function-name": "^7.10.4",
|
||||
"@babel/helper-split-export-declaration": "^7.11.0",
|
||||
"@babel/parser": "^7.11.0",
|
||||
"@babel/types": "^7.11.0",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0",
|
||||
"lodash": "^4.17.19"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/types": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
|
||||
"integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.10.4",
|
||||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.4.tgz",
|
||||
@@ -172,6 +445,21 @@
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.6.6.tgz",
|
||||
"integrity": "sha512-ojhgxzUHZ7am3D2jHkMzPpsBAiB005GF5YU4ea+8DNPybMk01JJUM9V9YRlF/GE95tcOm8DxQvWA2jq19bGalQ=="
|
||||
},
|
||||
"@emotion/is-prop-valid": {
|
||||
"version": "0.8.8",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
|
||||
"integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
|
||||
"requires": {
|
||||
"@emotion/memoize": "0.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/memoize": {
|
||||
"version": "0.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
|
||||
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@emotion/memoize": {
|
||||
"version": "0.6.6",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.6.6.tgz",
|
||||
@@ -214,6 +502,108 @@
|
||||
"integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@styled-system/background": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@styled-system/background/-/background-5.1.2.tgz",
|
||||
"integrity": "sha512-jtwH2C/U6ssuGSvwTN3ri/IyjdHb8W9X/g8Y0JLcrH02G+BW3OS8kZdHphF1/YyRklnrKrBT2ngwGUK6aqqV3A==",
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/border": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@styled-system/border/-/border-5.1.5.tgz",
|
||||
"integrity": "sha512-JvddhNrnhGigtzWRCVuAHepniyVi6hBlimxWDVAdcTuk7aRn9BYJUwfHslURtwYFsF5FoEs8Zmr1oZq2M1AP0A==",
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/color": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@styled-system/color/-/color-5.1.2.tgz",
|
||||
"integrity": "sha512-1kCkeKDZkt4GYkuFNKc7vJQMcOmTl3bJY3YBUs7fCNM6mMYJeT1pViQ2LwBSBJytj3AB0o4IdLBoepgSgGl5MA==",
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/core": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@styled-system/core/-/core-5.1.2.tgz",
|
||||
"integrity": "sha512-XclBDdNIy7OPOsN4HBsawG2eiWfCcuFt6gxKn1x4QfMIgeO6TOlA2pZZ5GWZtIhCUqEPTgIBta6JXsGyCkLBYw==",
|
||||
"requires": {
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"@styled-system/css": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@styled-system/css/-/css-5.1.5.tgz",
|
||||
"integrity": "sha512-XkORZdS5kypzcBotAMPBoeckDs9aSZVkvrAlq5K3xP8IMAUek+x2O4NtwoSgkYkWWzVBu6DGdFZLR790QWGG+A=="
|
||||
},
|
||||
"@styled-system/flexbox": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@styled-system/flexbox/-/flexbox-5.1.2.tgz",
|
||||
"integrity": "sha512-6hHV52+eUk654Y1J2v77B8iLeBNtc+SA3R4necsu2VVinSD7+XY5PCCEzBFaWs42dtOEDIa2lMrgL0YBC01mDQ==",
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/grid": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@styled-system/grid/-/grid-5.1.2.tgz",
|
||||
"integrity": "sha512-K3YiV1KyHHzgdNuNlaw8oW2ktMuGga99o1e/NAfTEi5Zsa7JXxzwEnVSDSBdJC+z6R8WYTCYRQC6bkVFcvdTeg==",
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/layout": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@styled-system/layout/-/layout-5.1.2.tgz",
|
||||
"integrity": "sha512-wUhkMBqSeacPFhoE9S6UF3fsMEKFv91gF4AdDWp0Aym1yeMPpqz9l9qS/6vjSsDPF7zOb5cOKC3tcKKOMuDCPw==",
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/position": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@styled-system/position/-/position-5.1.2.tgz",
|
||||
"integrity": "sha512-60IZfMXEOOZe3l1mCu6sj/2NAyUmES2kR9Kzp7s2D3P4qKsZWxD1Se1+wJvevb+1TP+ZMkGPEYYXRyU8M1aF5A==",
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/shadow": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@styled-system/shadow/-/shadow-5.1.2.tgz",
|
||||
"integrity": "sha512-wqniqYb7XuZM7K7C0d1Euxc4eGtqEe/lvM0WjuAFsQVImiq6KGT7s7is+0bNI8O4Dwg27jyu4Lfqo/oIQXNzAg==",
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/space": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@styled-system/space/-/space-5.1.2.tgz",
|
||||
"integrity": "sha512-+zzYpR8uvfhcAbaPXhH8QgDAV//flxqxSjHiS9cDFQQUSznXMQmxJegbhcdEF7/eNnJgHeIXv1jmny78kipgBA==",
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/typography": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@styled-system/typography/-/typography-5.1.2.tgz",
|
||||
"integrity": "sha512-BxbVUnN8N7hJ4aaPOd7wEsudeT7CxarR+2hns8XCX1zp0DFfbWw4xYa/olA0oQaqx7F1hzDg+eRaGzAJbF+jOg==",
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"@styled-system/variant": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@styled-system/variant/-/variant-5.1.5.tgz",
|
||||
"integrity": "sha512-Yn8hXAFoWIro8+Q5J8YJd/mP85Teiut3fsGVR9CAxwgNfIAiqlYxsk5iHU7VHJks/0KjL4ATSjmbtCDC/4l1qw==",
|
||||
"requires": {
|
||||
"@styled-system/core": "^5.1.2",
|
||||
"@styled-system/css": "^5.1.5"
|
||||
}
|
||||
},
|
||||
"@szmarczak/http-timer": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
|
||||
@@ -1145,7 +1535,8 @@
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
@@ -1310,7 +1701,8 @@
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
@@ -1425,7 +1817,8 @@
|
||||
},
|
||||
"tar": {
|
||||
"version": "4.4.8",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
@@ -1652,6 +2045,17 @@
|
||||
"resolve": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"babel-plugin-styled-components": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.11.1.tgz",
|
||||
"integrity": "sha512-YwrInHyKUk1PU3avIRdiLyCpM++18Rs1NgyMXEAQC33rIXs/vro0A+stf4sT0Gf22Got+xRWB8Cm0tw+qkRzBA==",
|
||||
"requires": {
|
||||
"@babel/helper-annotate-as-pure": "^7.0.0",
|
||||
"@babel/helper-module-imports": "^7.0.0",
|
||||
"babel-plugin-syntax-jsx": "^6.18.0",
|
||||
"lodash": "^4.17.11"
|
||||
}
|
||||
},
|
||||
"babel-plugin-syntax-flow": {
|
||||
"version": "6.18.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz",
|
||||
@@ -2432,6 +2836,11 @@
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||
"dev": true
|
||||
},
|
||||
"camelize": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
|
||||
"integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
@@ -3123,6 +3532,21 @@
|
||||
"resolved": "https://registry.npmjs.org/css-b64-images/-/css-b64-images-0.2.5.tgz",
|
||||
"integrity": "sha1-QgBdgyBLK0pdk7axpWRBM7WSegI="
|
||||
},
|
||||
"css-color-keywords": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
|
||||
"integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU="
|
||||
},
|
||||
"css-to-react-native": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz",
|
||||
"integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==",
|
||||
"requires": {
|
||||
"camelize": "^1.0.0",
|
||||
"css-color-keywords": "^1.0.0",
|
||||
"postcss-value-parser": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"cssom": {
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
|
||||
@@ -3465,6 +3889,11 @@
|
||||
"whatwg-url": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"debounce": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz",
|
||||
"integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
@@ -4312,8 +4741,7 @@
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"dev": true
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
||||
},
|
||||
"escaper": {
|
||||
"version": "2.5.3",
|
||||
@@ -4652,6 +5080,11 @@
|
||||
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
|
||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
|
||||
},
|
||||
"fast-memoize": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz",
|
||||
"integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw=="
|
||||
},
|
||||
"fbjs": {
|
||||
"version": "0.8.16",
|
||||
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz",
|
||||
@@ -5440,7 +5873,9 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.9.0",
|
||||
@@ -5464,9 +5899,14 @@
|
||||
}
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.5",
|
||||
"version": "0.5.1",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
@@ -5636,7 +6076,8 @@
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "",
|
||||
"resolved": false,
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
@@ -6194,8 +6635,7 @@
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
||||
"dev": true
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
|
||||
},
|
||||
"has-symbols": {
|
||||
"version": "1.0.1",
|
||||
@@ -9021,6 +9461,11 @@
|
||||
"integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
|
||||
"dev": true
|
||||
},
|
||||
"postcss-value-parser": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
|
||||
"integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ=="
|
||||
},
|
||||
"prebuild-install": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz",
|
||||
@@ -9260,6 +9705,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"re-resizable": {
|
||||
"version": "6.5.4",
|
||||
"resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.5.4.tgz",
|
||||
"integrity": "sha512-7T3L1lexB2zkZIDmzRJbwdq+xGFuRkrEVQIf5hBPnh7JuS9kG9Yc8XgIaxTWic1kU7jVlDgqzfId/gvmpBCjpA==",
|
||||
"requires": {
|
||||
"fast-memoize": "^2.5.1"
|
||||
}
|
||||
},
|
||||
"react": {
|
||||
"version": "16.9.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-16.9.0.tgz",
|
||||
@@ -9906,6 +10359,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"roboto-fontface": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.10.0.tgz",
|
||||
"integrity": "sha512-OlwfYEgA2RdboZohpldlvJ1xngOins5d7ejqnIBWr9KaMxsnBqotpptRXTyfNRLnFpqzX6sTDt+X+a+6udnU8g=="
|
||||
},
|
||||
"rw": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
|
||||
@@ -10078,6 +10536,11 @@
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
|
||||
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
|
||||
},
|
||||
"shallowequal": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
|
||||
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
|
||||
@@ -10559,6 +11022,71 @@
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
|
||||
},
|
||||
"styled-components": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.1.1.tgz",
|
||||
"integrity": "sha512-1ps8ZAYu2Husx+Vz8D+MvXwEwvMwFv+hqqUwhNlDN5ybg6A+3xyW1ECrAgywhvXapNfXiz79jJyU0x22z0FFTg==",
|
||||
"requires": {
|
||||
"@babel/helper-module-imports": "^7.0.0",
|
||||
"@babel/traverse": "^7.4.5",
|
||||
"@emotion/is-prop-valid": "^0.8.8",
|
||||
"@emotion/stylis": "^0.8.4",
|
||||
"@emotion/unitless": "^0.7.4",
|
||||
"babel-plugin-styled-components": ">= 1",
|
||||
"css-to-react-native": "^3.0.0",
|
||||
"hoist-non-react-statics": "^3.0.0",
|
||||
"shallowequal": "^1.1.0",
|
||||
"supports-color": "^5.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/stylis": {
|
||||
"version": "0.8.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
|
||||
"integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ=="
|
||||
},
|
||||
"@emotion/unitless": {
|
||||
"version": "0.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
|
||||
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
|
||||
},
|
||||
"hoist-non-react-statics": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||
"requires": {
|
||||
"react-is": "^16.7.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"styled-system": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/styled-system/-/styled-system-5.1.5.tgz",
|
||||
"integrity": "sha512-7VoD0o2R3RKzOzPK0jYrVnS8iJdfkKsQJNiLRDjikOpQVqQHns/DXWaPZOH4tIKkhAT7I6wIsy9FWTWh2X3q+A==",
|
||||
"requires": {
|
||||
"@styled-system/background": "^5.1.2",
|
||||
"@styled-system/border": "^5.1.5",
|
||||
"@styled-system/color": "^5.1.2",
|
||||
"@styled-system/core": "^5.1.2",
|
||||
"@styled-system/flexbox": "^5.1.2",
|
||||
"@styled-system/grid": "^5.1.2",
|
||||
"@styled-system/layout": "^5.1.2",
|
||||
"@styled-system/position": "^5.1.2",
|
||||
"@styled-system/shadow": "^5.1.2",
|
||||
"@styled-system/space": "^5.1.2",
|
||||
"@styled-system/typography": "^5.1.2",
|
||||
"@styled-system/variant": "^5.1.5",
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"stylis": {
|
||||
"version": "3.5.4",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
|
||||
|
||||
@@ -125,6 +125,7 @@
|
||||
"color": "^3.1.2",
|
||||
"compare-versions": "^3.2.1",
|
||||
"countable": "^3.0.1",
|
||||
"debounce": "^1.2.0",
|
||||
"diacritics": "^1.3.0",
|
||||
"diff-match-patch": "^1.0.4",
|
||||
"electron-context-menu": "^0.15.0",
|
||||
@@ -177,6 +178,7 @@
|
||||
"pretty-bytes": "^5.3.0",
|
||||
"promise": "^8.0.1",
|
||||
"query-string": "^5.1.1",
|
||||
"re-resizable": "^6.5.4",
|
||||
"react": "^16.9.0",
|
||||
"react-datetime": "^2.14.0",
|
||||
"react-dom": "^16.9.0",
|
||||
@@ -188,6 +190,7 @@
|
||||
"redux": "^3.7.2",
|
||||
"relative": "^3.0.2",
|
||||
"reselect": "^4.0.0",
|
||||
"roboto-fontface": "^0.10.0",
|
||||
"sax": "^1.2.4",
|
||||
"server-destroy": "^1.0.1",
|
||||
"smalltalk": "^2.5.1",
|
||||
@@ -195,6 +198,8 @@
|
||||
"sqlite3": "^4.1.1",
|
||||
"string-padding": "^1.0.2",
|
||||
"string-to-stream": "^1.1.1",
|
||||
"styled-components": "^5.1.1",
|
||||
"styled-system": "^5.1.5",
|
||||
"syswide-cas": "^5.1.0",
|
||||
"taboverride": "^4.0.3",
|
||||
"tar": "^4.4.4",
|
||||
|
||||
@@ -33,6 +33,8 @@ class Dialog extends React.PureComponent {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.fuzzy_ = false;
|
||||
|
||||
this.state = {
|
||||
query: '',
|
||||
results: [],
|
||||
@@ -58,11 +60,11 @@ class Dialog extends React.PureComponent {
|
||||
}
|
||||
|
||||
style() {
|
||||
const styleKey = [this.props.theme, this.state.resultsInBody ? '1' : '0'].join('-');
|
||||
const styleKey = [this.props.themeId, this.state.resultsInBody ? '1' : '0'].join('-');
|
||||
|
||||
if (this.styles_[styleKey]) return this.styles_[styleKey];
|
||||
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const itemHeight = this.state.resultsInBody ? 84 : 64;
|
||||
|
||||
@@ -178,7 +180,7 @@ class Dialog extends React.PureComponent {
|
||||
}
|
||||
|
||||
async keywords(searchQuery) {
|
||||
const parsedQuery = await SearchEngine.instance().parseQuery(searchQuery, false);
|
||||
const parsedQuery = await SearchEngine.instance().parseQuery(searchQuery, this.fuzzy_);
|
||||
return SearchEngine.instance().allParsedQueryTerms(parsedQuery);
|
||||
}
|
||||
|
||||
@@ -215,7 +217,7 @@ class Dialog extends React.PureComponent {
|
||||
} else { // Note TITLE or BODY
|
||||
listType = BaseModel.TYPE_NOTE;
|
||||
searchQuery = this.makeSearchQuery(this.state.query);
|
||||
results = await SearchEngine.instance().search(searchQuery);
|
||||
results = await SearchEngine.instance().search(searchQuery, { fuzzy: this.fuzzy_ });
|
||||
|
||||
resultsInBody = !!results.find(row => row.fields.includes('body'));
|
||||
|
||||
@@ -341,7 +343,7 @@ class Dialog extends React.PureComponent {
|
||||
}
|
||||
|
||||
listItemRenderer(item) {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const style = this.style();
|
||||
const rowStyle = item.id === this.state.selectedItemId ? style.rowSelected : style.row;
|
||||
const titleHtml = item.fragments
|
||||
@@ -430,7 +432,7 @@ class Dialog extends React.PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const style = this.style();
|
||||
const helpComp = !this.state.showHelp ? null : <div style={style.help}>{_('Type a note title or part of its content to jump to it. Or type # followed by a tag name, or @ followed by a notebook name.')}</div>;
|
||||
|
||||
@@ -453,7 +455,7 @@ class Dialog extends React.PureComponent {
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
folders: state.folders,
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
showCompletedTodos: state.settings.showCompletedTodos,
|
||||
highlightedWords: state.highlightedWords,
|
||||
};
|
||||
|
||||
+17
-14
@@ -38,18 +38,18 @@ a {
|
||||
::-webkit-scrollbar-track {
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(100, 100, 100, 0.3);
|
||||
background: rgba(100, 100, 100, 0.3);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track:hover {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(100, 100, 100, 0.7);
|
||||
background: rgba(100, 100, 100, 0.7);
|
||||
}
|
||||
|
||||
.fade_out {
|
||||
@@ -64,20 +64,13 @@ a {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/*
|
||||
.note-list .list-item-container:hover {
|
||||
background-color: rgba(0,160,255,0.1) !important;
|
||||
}
|
||||
*/
|
||||
|
||||
/*.side-bar .list-item:hover,
|
||||
.side-bar .synchronize-button:hover {
|
||||
background-color: #01427B;
|
||||
}
|
||||
|
||||
.side-bar .list-item:active,
|
||||
.side-bar .synchronize-button:active {
|
||||
background-color: #0465BB;
|
||||
}*/
|
||||
|
||||
/*
|
||||
.editor-toolbar .button:not(.disabled):hover,
|
||||
.header .button:not(.disabled):hover {
|
||||
background-color: rgba(0,160,255,0.1);
|
||||
@@ -91,6 +84,7 @@ a {
|
||||
border: 1px solid rgba(0,160,255,0.7);
|
||||
box-sizing: 'border-box';
|
||||
}
|
||||
*/
|
||||
|
||||
.editor-toolbar .button,
|
||||
.header .button {
|
||||
@@ -163,3 +157,12 @@ a {
|
||||
from {transform: rotate(0deg);}
|
||||
to {transform: rotate(360deg);}
|
||||
}
|
||||
|
||||
/* .joplin-tinymce .tox-editor-header {
|
||||
padding-left: 88px;
|
||||
padding-right: 150px;
|
||||
} */
|
||||
|
||||
*:focus {
|
||||
outline: none;
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
Executable
+55
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Generated by IcoMoon</metadata>
|
||||
<defs>
|
||||
<font id="icomoon" horiz-adv-x="1024">
|
||||
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
||||
<missing-glyph horiz-adv-x="1024" />
|
||||
<glyph unicode=" " horiz-adv-x="512" d="" />
|
||||
<glyph unicode="" glyph-name="error" d="M949.76 149.333v0 0l-374.187 660.907c-9.93 26.213-34.828 44.511-64 44.511s-54.070-18.297-63.844-44.042l-0.156-0.469-373.333-660.907c-6.431-10.419-10.242-23.048-10.242-36.568 0-0.194 0.001-0.388 0.002-0.582v0.030c0.78-18.935 8.791-35.859 21.324-48.205l0.009-0.009c12.521-12.639 29.879-20.468 49.064-20.48h756.482c18.721 0.373 35.56 8.133 47.781 20.474l0.006 0.006c12.409 12.869 20.054 30.405 20.054 49.727 0 0.068 0 0.136 0 0.204v-0.010c0.005 0.306 0.008 0.668 0.008 1.030 0 12.63-3.326 24.483-9.15 34.732l0.182-0.348zM480.427 593.067h64v-259.413h-64zM512 172.373c-0.127-0.001-0.277-0.002-0.427-0.002-26.863 0-48.64 21.777-48.64 48.64s21.777 48.64 48.64 48.64c26.862 0 48.639-21.776 48.64-48.638v0c0.001-0.127 0.002-0.277 0.002-0.427 0-26.627-21.586-48.213-48.213-48.213-0.001 0-0.001 0-0.002 0v0z" />
|
||||
<glyph unicode="" glyph-name="attention" d="M942.080 490.667l-387.413 387.413c-11.036 10.661-26.084 17.231-42.667 17.231s-31.631-6.57-42.684-17.248l0.018 0.017-387.413-387.413c-10.661-11.036-17.231-26.084-17.231-42.667s6.57-31.631 17.248-42.684l-0.017 0.018 387.413-387.413c11.036-10.661 26.084-17.231 42.667-17.231s31.631 6.57 42.684 17.248l-0.018-0.017 387.413 387.413c10.661 11.036 17.231 26.084 17.231 42.667s-6.57 31.631-17.248 42.684l0.017-0.018zM512 192c-35.346 0-64 28.654-64 64s28.654 64 64 64c35.346 0 64-28.654 64-64v0c0-35.346-28.654-64-64-64v0zM544 384h-64l-32 320h128z" />
|
||||
<glyph unicode="" glyph-name="collapse" d="M492.373 718.933l-81.493 49.067-190.72-317.013c-4.222-6.944-6.721-15.34-6.721-24.32s2.499-17.376 6.84-24.531l-0.119 0.211 190.72-317.013 81.493 49.067-175.787 292.267zM746.667 718.933l-81.92 49.067-190.293-317.013c-4.402-6.895-7.017-15.302-7.017-24.32s2.615-17.425 7.127-24.504l-0.11 0.184 190.293-317.013 81.92 49.067-175.787 292.267z" />
|
||||
<glyph unicode="" glyph-name="cancel" d="M512 853.333c-224.491-2.868-405.371-185.522-405.371-410.422 0-1.961 0.014-3.919 0.041-5.874l-0.003 0.295c-0.024-1.659-0.038-3.617-0.038-5.578 0-224.9 180.88-407.553 405.1-410.419l0.271-0.003c224.491 2.868 405.371 185.522 405.371 410.422 0 1.961-0.014 3.919-0.041 5.874l0.003-0.295c0.024 1.659 0.038 3.617 0.038 5.578 0 224.9-180.88 407.553-405.1 410.419l-0.271 0.003zM512 85.333c-189.334 3.818-341.355 158.203-341.355 348.093 0 1.374 0.008 2.745 0.024 4.115l-0.002-0.209c-0.014 1.161-0.022 2.533-0.022 3.907 0 189.891 152.021 344.275 340.999 348.088l0.356 0.006c189.334-3.818 341.355-158.203 341.355-348.093 0-1.374-0.008-2.745-0.024-4.115l0.002 0.209c0.014-1.161 0.022-2.533 0.022-3.907 0-189.891-152.021-344.275-340.999-348.088l-0.356-0.006zM656.213 619.947l-144.213-137.387-144.213 137.387-47.787-45.227 144.213-137.387-144.213-137.387 47.787-45.227 144.213 137.387 144.213-137.387 47.787 45.227-144.64 137.387 144.64 137.387-47.787 45.227z" />
|
||||
<glyph unicode="" glyph-name="expand" d="M512 298.667l192 256h-384l192-256z" />
|
||||
<glyph unicode="" glyph-name="notebooks" d="M320.427 896c-70.692 0-128-57.308-128-128v0-672c0-53.019 42.981-96 96-96v0h544v64h-544c-17.673 0-32 14.327-32 32s14.327 32 32 32v0h544v768zM768 192h-512v576c0 0 0 0.001 0 0.001 0 35.346 28.654 64 64 64 0.15 0 0.3-0.001 0.45-0.002h447.55zM320.427 642.56v-128h384v192h-384zM384.427 642.56h256v-64h-256.427z" />
|
||||
<glyph unicode="" glyph-name="plus" d="M832 495.787h-271.787v272.213h-96v-272.213h-272.213v-96h272.213v-271.787h96v271.787h271.787v96z" />
|
||||
<glyph unicode="" glyph-name="update" d="M896 576l-128-192h90.88c-33.282-159.369-172.609-277.367-339.495-277.367-1.697 0-3.39 0.012-5.081 0.037l0.256-0.003c-80.581 0.131-154.81 27.22-214.176 72.727l0.843-0.621-45.227-46.080c70.066-56.099 159.999-90.027 257.857-90.027 0.247 0 0.494 0 0.741 0.001h-0.039c1.555-0.021 3.392-0.033 5.231-0.033 202.029 0 369.86 146.419 403.167 338.929l0.349 2.438h100.693zM159.147 448c0 0.136 0 0.296 0 0.457 0 197.703 158.943 358.282 356.017 360.927l0.25 0.003c0.014 0 0.031 0 0.047 0 97.827 0 186.231-40.347 249.478-105.309l0.075-0.077 45.653 45.653c-74.965 76.931-179.5 124.736-295.201 125.013h-0.052c-233.058-3.367-420.693-193.084-420.693-426.625 0-0.015 0-0.029 0-0.044v0.002h-94.72l128-192 128 192z" />
|
||||
<glyph unicode="" glyph-name="tags" d="M896 768h-64v64c0 35.346-28.654 64-64 64v0h-256c-17.667-0.016-33.655-7.188-45.226-18.773l-384.001-384.001c-11.669-11.531-18.895-27.535-18.895-45.227s7.227-33.696 18.889-45.221l384.006-384.006c11.579-11.565 27.568-18.717 45.227-18.717s33.648 7.152 45.227 18.718l383.999 383.999c11.586 11.572 18.757 27.56 18.773 45.224v256.003c0 35.346-28.654 64-64 64v0zM128 448l384 384h256v-256l-384-384zM896 448l-384-384-82.773 82.773 384 384c11.586 11.572 18.757 27.56 18.773 45.224v128.003h64zM640 768c-35.346 0-64-28.654-64-64s28.654-64 64-64c35.346 0 64 28.654 64 64v0c0 35.346-28.654 64-64 64v0z" />
|
||||
<glyph unicode="" glyph-name="search" d="M917.333 98.56l-222.293 221.44c40.74 52.69 65.302 119.689 65.302 192.426 0 174.61-141.55 316.16-316.16 316.16s-316.16-141.55-316.16-316.16c0-174.61 141.55-316.16 316.16-316.16 0.292 0 0.584 0 0.876 0.001h-0.045c74.116 0.383 142.086 26.382 195.584 69.587l-0.597-0.467 222.72-222.72zM207.36 514.987c0 0.127 0 0.276 0 0.426 0 131.488 106.592 238.080 238.080 238.080s238.080-106.592 238.080-238.080c0-131.488-106.592-238.080-238.080-238.080-0.15 0-0.3 0-0.45 0h0.023c-131.304 0.243-237.653 106.742-237.653 238.080 0 0 0 0 0 0v0z" />
|
||||
<glyph unicode="" glyph-name="notes" d="M960 770.133h-192v128c-1.184 34.416-29.368 61.867-63.964 61.867-0.012 0-0.025 0-0.037 0h-639.998c-0.011 0-0.023 0-0.036 0-34.596 0-62.781-27.45-63.961-61.759l-0.003-0.108v-640c-0.023-0.636-0.036-1.383-0.036-2.133 0-35.346 28.654-64 64-64 0.013 0 0.025 0 0.038 0h127.998v-192c0-35.346 28.654-64 64-64v0h704c35.346 0 64 28.654 64 64v0 704c0 35.346-28.654 64-64 64v0zM64 896h640v-640h-640zM960 0h-704v192h448c35.346 0 64 28.654 64 64v0 448h192zM128 706.133h512v-64h-512v64zM128 514.56h320v-64h-320v64z" />
|
||||
<glyph unicode="" glyph-name="sync" d="M512 853.333c-0.372 0.001-0.813 0.002-1.253 0.002-100.776 0-192.953-36.816-263.816-97.732l0.536 0.45 42.667-52.053c59.39 49.89 136.682 80.198 221.053 80.198 175.504 0 320.378-131.145 341.97-300.782l0.178-1.709h-102.4l136.533-204.373 136.533 204.373h-102.4c-20.69 209.397-195.9 371.627-409.001 371.627-0.211 0-0.421 0-0.631 0h0.032zM136.533 618.667l-136.533-204.373h102.4c20.405-209.431 195.557-371.784 408.628-371.784 101.519 0 194.429 36.856 266.082 97.917l-0.576-0.479-42.667 52.053c-59.39-49.89-136.682-80.198-221.053-80.198-175.504 0-320.378 131.145-341.97 300.782l-0.178 1.709h102.4z" />
|
||||
<glyph unicode="" glyph-name="general" d="M545.707 277.333c-43.070-1.026-79.361-28.943-92.799-67.56l-0.214-0.707h-346.027v-68.267h346.027c13.041-39.66 49.742-67.788 93.013-67.788s79.972 28.128 92.816 67.098l0.197 0.69h278.613v68.267h-277.333c-13.8 39.717-50.707 67.803-94.238 68.266h-0.055zM545.707 140.8c-0.001 0-0.002 0-0.003 0-18.616 0-33.707 15.091-33.707 33.707 0 0.15 0.001 0.3 0.003 0.449v-0.023 4.267c1.251 18.181 15.573 32.65 33.575 34.125l0.131 0.009c18.838-0.617 33.875-16.037 33.875-34.969 0-1.208-0.061-2.402-0.181-3.579l0.012 0.148c-1.064-18.261-15.49-32.835-33.591-34.127l-0.116-0.007zM410.453 550.4c-42.942-1.081-79.090-28.999-92.375-67.562l-0.211-0.705h-211.2v-68.267h213.333c13.497-39.268 49.645-67.186 92.46-68.264l0.127-0.003c43.114 0.895 79.467 28.869 92.802 67.562l0.211 0.705h413.867v68.267h-416c-13.546 39.398-49.899 67.372-92.908 68.265l-0.105 0.002zM410.453 413.867c-0.001 0-0.002 0-0.003 0-18.616 0-33.707 15.091-33.707 33.707 0 0.15 0.001 0.3 0.003 0.449v-0.023 4.267c1.251 18.181 15.573 32.65 33.575 34.125l0.131 0.009c19.221-1.8 34.147-17.853 34.147-37.392 0-0.354-0.005-0.707-0.015-1.059l0.001 0.052c-1.276-18.315-15.818-32.857-34.018-34.127l-0.115-0.006zM777.813 755.2c-15.994 38.443-52.098 65.535-94.827 68.25l-0.32 0.016c-43.070-1.026-79.361-28.943-92.799-67.56l-0.214-0.707h-482.987v-68.267h481.28c13.041-39.66 49.742-67.788 93.013-67.788s79.972 28.128 92.816 67.098l0.197 0.69h143.36v68.267zM682.667 686.933c-0.13-0.002-0.284-0.003-0.438-0.003-18.38 0-33.28 14.9-33.28 33.28 0 0.301 0.004 0.601 0.012 0.9l-0.001-0.044v4.267c1.064 18.261 15.49 32.835 33.591 34.127l0.116 0.007c18.838-0.617 33.875-16.037 33.875-34.969 0-1.208-0.061-2.402-0.181-3.579l0.012 0.148c-1.251-18.181-15.573-32.65-33.575-34.125l-0.131-0.009z" />
|
||||
<glyph unicode="" glyph-name="note" d="M785.067 823.467h-546.133c-56.265-0.712-101.688-46.135-102.399-102.332l-0.001-0.068v-546.133c0.712-56.265 46.135-101.688 102.332-102.399l0.068-0.001h426.667c9.575 0.958 18.273 4.007 25.862 8.683l-0.262-0.15 187.733 187.733c5.432 5.6 8.781 13.247 8.781 21.677 0 1.383-0.090 2.745-0.265 4.080l0.017-0.157v426.667c-0.712 56.265-46.135 101.688-102.332 102.399l-0.068 0.001zM819.2 311.467h-170.667v-170.667h-409.6c-18.315 1.276-32.857 15.818-34.127 34.018l-0.006 0.115v546.133c1.276 18.315 15.818 32.857 34.018 34.127l0.115 0.006h546.133c18.315-1.276 32.857-15.818 34.127-34.018l0.006-0.115zM580.267 584.533h-273.067v-68.267h409.6v68.267h-136.533zM648.533 448h-341.333v-68.267h341.333z" />
|
||||
<glyph unicode="" glyph-name="note-history" d="M742.4 789.333l38.4 51.2-166.4 4.267 46.933-157.867 38.4 46.933c84.382-55.852 142.177-146.055 153.466-250.209l0.134-1.525h-34.133v-68.267h34.133c-17.537-162.117-145.083-289.663-305.67-307.065l-1.53-0.135v34.133h-68.267v-34.133c-162.117 17.537-289.663 145.083-307.065 305.67l-0.135 1.53h34.133v68.267h-34.133c17.922 173.251 163.129 307.204 339.623 307.204 0.601 0 1.202-0.002 1.803-0.005h-0.092v68.267c-226.216 0-409.6-183.384-409.6-409.6s183.384-409.6 409.6-409.6c226.216 0 409.6 183.384 409.6 409.6v0c-1.314 140.66-71.248 264.713-177.895 340.454l-1.305 0.88zM477.867 465.067l-149.333-110.933 42.667-55.467 162.133 123.733c4.267 4.267 8.533 8.533 8.533 12.8s4.267 8.533 4.267 17.067v234.667h-68.267z" />
|
||||
<glyph unicode="" glyph-name="application" d="M512 896c-19.087 0-34.56-15.473-34.56-34.56v0-5.973c0-0.053 0-0.116 0-0.179 0-16.588-11.833-30.412-27.518-33.493l-0.216-0.035-23.467-4.267c-2.13-0.479-4.576-0.753-7.086-0.753-14.123 0-26.218 8.686-31.233 21.008l-0.081 0.225-3.84 5.12c-5.336 12.668-17.648 21.399-32 21.399-19.123 0-34.626-15.502-34.626-34.626 0-4.772 0.965-9.318 2.711-13.454l-0.085 0.227 2.56-5.12c1.941-4.127 3.074-8.963 3.074-14.064 0-12.009-6.28-22.551-15.735-28.521l-0.139-0.082-19.627-13.227c-5.26-3.643-11.776-5.819-18.801-5.819-9.357 0-17.812 3.862-23.859 10.078l-0.007 0.008-4.267 3.84c-5.299 3.472-11.79 5.538-18.764 5.538-19.087 0-34.56-15.473-34.56-34.56 0-6.974 2.066-13.466 5.619-18.896l-0.081 0.132 3.84-4.267c6.224-6.054 10.086-14.509 10.086-23.866 0-7.025-2.176-13.541-5.892-18.911l0.072 0.111c-4.267-6.4-8.96-12.8-13.227-19.627-6.052-9.594-16.594-15.874-28.603-15.874-5.101 0-9.937 1.133-14.27 3.161l0.207-0.087h-5.12c-6.020 5.22-13.932 8.402-22.586 8.402-19.087 0-34.56-15.473-34.56-34.56 0-18.026 13.801-32.829 31.413-34.419l0.134-0.010 5.12-2.133c12.547-5.096 21.233-17.191 21.233-31.314 0-2.51-0.274-4.956-0.795-7.31l0.042 0.224c0-7.68-2.987-15.36-4.267-23.467-3.116-15.901-16.94-27.734-33.528-27.734-0.063 0-0.126 0-0.188 0.001h-5.963c-19.087 0-34.56-15.473-34.56-34.56s15.473-34.56 34.56-34.56h5.973c0.053 0 0.116 0 0.179 0 16.588 0 30.412-11.833 33.493-27.518l0.035-0.216c0-8.107 2.56-15.787 4.267-23.467 0.479-2.13 0.753-4.576 0.753-7.086 0-14.123-8.686-26.218-21.008-31.233l-0.225-0.081-5.12-5.547c-12.668-5.336-21.399-17.648-21.399-32 0-19.123 15.502-34.626 34.626-34.626 4.772 0 9.318 0.965 13.454 2.711l-0.227-0.085 5.12 2.56c4.009 1.734 8.677 2.743 13.58 2.743 12.074 0 22.72-6.116 29.008-15.419l0.079-0.124c3.84-6.827 8.533-13.227 12.8-20.053 3.672-5.318 5.866-11.903 5.866-19 0-9.225-3.706-17.584-9.709-23.67l0.004 0.004-4.267-3.84c-5.89-6.186-9.513-14.576-9.513-23.813 0-19.087 15.473-34.56 34.56-34.56 9.131 0 17.434 3.541 23.613 9.324l-0.019-0.018 4.267 4.267c6.108 6.152 14.57 9.96 23.921 9.96 6.989 0 13.481-2.127 18.864-5.769l-0.119 0.076 19.627-13.227c9.594-6.052 15.874-16.594 15.874-28.603 0-5.101-1.133-9.937-3.161-14.27l0.087 0.207v-5.547c-0.023-0.468-0.036-1.017-0.036-1.569 0-19.087 15.473-34.56 34.56-34.56 10.155 0 19.287 4.38 25.61 11.353l0.026 0.029 2.133 5.547c5.242 12.528 17.4 21.169 31.576 21.169 2.416 0 4.773-0.251 7.047-0.728l-0.222 0.039 23.467-4.267c15.901-3.116 27.734-16.94 27.734-33.528 0-0.063 0-0.126-0.001-0.188v0.010-5.973c0-19.087 15.473-34.56 34.56-34.56s34.56 15.473 34.56 34.56v5.973c0 0.053 0 0.116 0 0.179 0 16.588 11.833 30.412 27.518 33.493l0.216 0.035 23.467 4.267c2.13 0.479 4.576 0.753 7.086 0.753 14.123 0 26.218-8.686 31.233-21.008l0.081-0.225 2.133-5.12c5.336-12.668 17.648-21.399 32-21.399 19.123 0 34.626 15.502 34.626 34.626 0 4.772-0.965 9.318-2.711 13.454l0.085-0.227-2.56 5.12c-1.734 4.009-2.743 8.677-2.743 13.58 0 12.074 6.116 22.72 15.419 29.008l0.124 0.079c6.827 3.84 13.227 8.533 20.053 12.8 5.318 3.672 11.903 5.866 19 5.866 9.225 0 17.584-3.706 23.67-9.709l-0.004 0.004 3.84-4.267c6.186-5.89 14.576-9.513 23.813-9.513 19.087 0 34.56 15.473 34.56 34.56 0 9.131-3.541 17.434-9.324 23.613l0.018-0.019-4.267 4.267c-6.152 6.108-9.96 14.57-9.96 23.921 0 6.989 2.127 13.481 5.769 18.864l-0.076-0.119c4.267 6.4 8.96 12.8 13.227 19.627 6.052 9.594 16.594 15.874 28.603 15.874 5.101 0 9.937-1.133 14.27-3.161l-0.207 0.087 5.547-2.133c2.826-0.833 6.072-1.312 9.431-1.312 19.087 0 34.56 15.473 34.56 34.56 0 12.128-6.247 22.796-15.698 28.964l-0.133 0.081-5.547 2.133c-12.528 5.242-21.169 17.4-21.169 31.576 0 2.416 0.251 4.773 0.728 7.047l-0.039-0.222c0 7.68 2.987 15.787 4.267 23.467 3.116 15.901 16.94 27.734 33.528 27.734 0.063 0 0.126 0 0.188-0.001h5.963c19.087 0 34.56 15.473 34.56 34.56s-15.473 34.56-34.56 34.56h-5.973c-0.053 0-0.116 0-0.179 0-16.588 0-30.412 11.833-33.493 27.518l-0.035 0.216c0 8.107-2.56 15.787-4.267 23.467-0.479 2.13-0.753 4.576-0.753 7.086 0 14.123 8.686 26.218 21.008 31.233l0.225 0.081 5.12 5.547c12.668 5.336 21.399 17.648 21.399 32 0 19.123-15.502 34.626-34.626 34.626-4.772 0-9.318-0.965-13.454-2.711l0.227 0.085-5.12-2.56c-4.009-1.734-8.677-2.743-13.58-2.743-12.074 0-22.72 6.116-29.008 15.419l-0.079 0.124c-3.84 6.827-8.533 13.227-12.8 20.053-3.672 5.318-5.866 11.903-5.866 19 0 9.225 3.706 17.584 9.709 23.67l-0.004-0.004 4.267 3.84c6.756 6.324 10.967 15.297 10.967 25.254 0 19.087-15.473 34.56-34.56 34.56-9.85 0-18.738-4.121-25.033-10.733l-0.013-0.014-4.267-4.267c-6.054-6.224-14.509-10.086-23.866-10.086-7.025 0-13.541 2.176-18.911 5.892l0.111-0.072-19.627 13.227c-9.594 6.052-15.874 16.594-15.874 28.603 0 5.101 1.133 9.937 3.161 14.27l-0.087-0.207 2.133 5.12c4.080 5.603 6.528 12.623 6.528 20.214 0 19.087-15.473 34.56-34.56 34.56-17.489 0-31.943-12.99-34.241-29.848l-0.020-0.179-2.133-5.12c-5.096-12.547-17.191-21.233-31.314-21.233-2.51 0-4.956 0.274-7.31 0.795l0.224-0.042-23.467 4.267c-15.901 3.116-27.734 16.94-27.734 33.528 0 0.063 0 0.126 0.001 0.188Line truncated
|
||||
<glyph unicode="" glyph-name="encryption" d="M630.187 853.333c-0.237 0.001-0.518 0.001-0.799 0.001-158.115 0-286.293-128.178-286.293-286.293 0-36.178 6.71-70.788 18.954-102.654l-0.662 1.96-256-256v-170.667h170.667v104.107h100.693v101.547h101.547l52.053 52.053c30.908-12.4 66.739-19.593 104.25-19.593 158.587 0 287.147 128.56 287.147 287.147s-128.56 287.147-287.147 287.147c-1.551 0-3.099-0.012-4.644-0.037l0.233 0.003zM697.6 718.080c47.128 0 85.333-38.205 85.333-85.333s-38.205-85.333-85.333-85.333c-47.128 0-85.333 38.205-85.333 85.333v0c0 47.128 38.205 85.333 85.333 85.333v0z" />
|
||||
<glyph unicode="" glyph-name="plugins" d="M512 857.6c0 0-0.001 0-0.001 0-56.404 0-102.157-45.603-102.399-101.95v-0.023c2.013-30.391 13.094-57.858 30.532-80.103l-0.239 0.316c3.108-4.764 4.956-10.595 4.956-16.858 0-17.202-13.945-31.147-31.147-31.147-1.296 0-2.573 0.079-3.827 0.233l0.15-0.015c-77.653 9.387-156.587 24.32-156.587 24.32v0c-2.173 0.197-4.7 0.309-7.253 0.309s-5.080-0.112-7.577-0.332l0.323 0.023c-18.829-0.083-35.857-7.729-48.215-20.055l0.001 0.001c-12.013-11.914-19.592-28.283-20.052-46.42l-0.002-0.086v-163.84c0.251-21.723 17.919-39.236 39.678-39.236 7.743 0 14.968 2.218 21.074 6.053l-0.165-0.097c21.476 16.868 48.898 27.052 78.699 27.052 2.334 0 4.653-0.062 6.956-0.186l-0.321 0.014c53.997-2.69 96.758-47.124 96.758-101.547 0-56.148-45.517-101.665-101.665-101.665-1.726 0-3.442 0.043-5.146 0.128l0.24-0.010c-28.63 1.429-54.666 11.467-75.858 27.553l0.338-0.246c-5.943 3.741-13.17 5.96-20.916 5.96-21.61 0-39.185-17.275-39.67-38.769l-0.001-0.045v-81.493c0-52.48 30.72-82.773 68.267-82.773h546.133c0 0 0.001 0 0.001 0 37.553 0 68.023 30.321 68.265 67.817v475.33c0 0.018 0 0.039 0 0.060 0 18.825-7.667 35.858-20.049 48.149l-0.004 0.004c-12.357 12.324-29.384 19.97-48.197 20.053h-14.523s-78.933-14.933-156.587-24.32c-1.104-0.139-2.381-0.218-3.676-0.218-17.202 0-31.147 13.945-31.147 31.147 0 6.263 1.849 12.094 5.030 16.978l-0.073-0.12c18.657 23.151 29.948 52.921 29.948 85.328 0 1.652-0.029 3.298-0.088 4.936l0.007-0.237c-4.793 43.464-35.95 78.506-76.938 89.018l-0.716 0.156c-4.963 0.698-10.696 1.097-16.523 1.097-2.743 0-5.466-0.088-8.165-0.262l0.368 0.019z" />
|
||||
<glyph unicode="" glyph-name="web-clipper" d="M64.853 442.453c1.868 23.717 19.51 42.814 42.354 46.887l0.312 0.046 336.64 42.667-2.987 34.56v18.773l-265.813 51.2c-24.766 3.655-43.559 24.762-43.559 50.258 0 3.351 0.325 6.626 0.944 9.796l-0.052-0.321c4.068 24.799 25.342 43.489 50.981 43.489 3.244 0 6.419-0.299 9.497-0.872l-0.319 0.049 354.56-69.12h58.88l-75.947-87.893c-13.548-15.556-21.808-36.027-21.808-58.428 0-26.66 11.699-50.587 30.243-66.928l0.098-0.085c16.259-14.678 37.907-23.659 61.653-23.659s45.395 8.981 61.734 23.731l-0.081-0.072 85.333 80.213c3.144 3.097 5.091 7.401 5.091 12.16s-1.948 9.063-5.089 12.158l-0.002 0.002c-3.143 3.042-7.433 4.918-12.16 4.918s-9.017-1.875-12.165-4.922l0.005 0.005-85.333-79.787c-10.143-9.102-23.622-14.667-38.4-14.667s-28.257 5.566-38.454 14.715l0.054-0.047c-11.549 10.214-18.793 25.067-18.793 41.613 0 13.992 5.181 26.773 13.728 36.531l-0.055-0.064 104.96 122.027 21.76 22.613c22.126 21.964 52.608 35.539 86.259 35.539 25.069 0 48.379-7.533 67.789-20.46l-0.449 0.281 46.933-31.147c46.421-30.896 76.637-82.942 76.8-142.055v-251.758c-0.484-75.509-61.807-136.534-137.384-136.534-0.151 0-0.302 0-0.453 0.001h-155.71c10.138 13.862 16.221 31.247 16.221 50.053 0 0.403-0.003 0.806-0.008 1.208l0.001-0.061c-0.107 27.889-13.577 52.61-34.335 68.106l-0.225 0.16c20.82 15.733 34.135 40.446 34.135 68.268 0 46.979-37.963 85.091-84.885 85.332h-141.25l-333.227-42.667c-2.114-0.309-4.555-0.486-7.036-0.486-25.436 0-46.539 18.548-50.524 42.856l-0.040 0.296c-0.388 2.236-0.61 4.81-0.61 7.436 0 1.438 0.066 2.86 0.196 4.264l-0.013-0.18zM408.747 345.6c0.242 28.329 23.262 51.2 51.625 51.2 0.001 0 0.001 0 0.002 0h136.96c28.277 0 51.2-22.923 51.2-51.2s-22.923-51.2-51.2-51.2h-136.96c0 0-0.001 0-0.002 0-28.363 0-51.383 22.871-51.625 51.177v0.023zM443.307 209.067c0.242 28.329 23.262 51.2 51.625 51.2 0.001 0 0.001 0 0.002 0h102.4c28.277 0 51.2-22.923 51.2-51.2s-22.923-51.2-51.2-51.2h-102.4c0 0-0.001 0-0.002 0-28.363 0-51.383 22.871-51.625 51.177v0.023z" />
|
||||
<glyph unicode="" glyph-name="appearance" d="M290.987 896c-9.525-0.016-18.133-3.931-24.315-10.235l-192.432-192.432c-6.288-6.195-10.183-14.802-10.183-24.32s3.895-18.125 10.179-24.316l199.684-199.684 48.64 48.64-175.36 175.36 143.787 143.787 42.667-42.667-42.667-42.667 50.347-52.48 42.667 42.667 37.12-37.12-71.253-70.4 42.667-42.667 120.747 120.32-197.973 197.973c-6.187 6.309-14.795 10.224-24.317 10.24h-0.003zM816.213 861.44c-18.821-0.132-35.833-7.767-48.218-20.058l-49.062-48.636 137.813-137.813 48.64 49.067c12.471 12.438 20.186 29.638 20.186 48.64s-7.716 36.202-20.185 48.638l-0.002 0.002-40.533 42.667c-12.276 10.884-28.525 17.532-46.326 17.532-0.814 0-1.624-0.014-2.431-0.041l0.117 0.003zM649.813 723.627l-474.453-474.453 42.667-25.173 17.493-51.627 51.627-17.493 25.173-42.667 475.307 473.6zM750.080 450.987l-120.32-120.747 42.667-42.667 72.107 71.68 37.12-37.12-42.667-42.667 48.64-48.64 44.8 42.667 42.667-42.667-143.787-143.787-175.36 175.36-48.64-48.64 199.68-199.68c6.195-6.288 14.802-10.183 24.32-10.183s18.125 3.895 24.316 10.179l192.431 192.431c6.288 6.195 10.183 14.802 10.183 24.32s-3.895 18.125-10.179 24.316l-0.004 0.004-153.173 150.187zM128 141.653l-29.44-83.2c-0.195-1.026-0.306-2.207-0.306-3.413s0.111-2.387 0.324-3.532l-0.018 0.119c0.232-9.331 7.736-16.835 17.045-17.066h6.422l85.333 29.867z" />
|
||||
<glyph unicode="" glyph-name="code" d="M434.773 64l58.453 5.547c35.361 65.828 66.743 142.554 90.037 222.722l2.123 8.531-93.013-9.387c-14.561-86.991-34.574-163.501-60.604-237.168l3.004 9.755zM532.48 624.64c-0.166 0.002-0.362 0.003-0.559 0.003-30.869 0-55.893-25.024-55.893-55.893 0-1.352 0.048-2.693 0.142-4.021l-0.010 0.178c-0.136-1.473-0.213-3.185-0.213-4.916 0-31.34 25.406-56.747 56.747-56.747s56.747 25.406 56.747 56.747c0 1.731-0.077 3.443-0.229 5.134l0.016-0.218c0.129 1.416 0.203 3.063 0.203 4.726 0 30.398-24.642 55.040-55.040 55.040-0.672 0-1.341-0.012-2.006-0.036l0.096 0.003zM190.293 707.413c0.743-28.46 3.507-55.685 8.162-82.259l-0.482 3.325c4.217-23.539 6.983-51.065 7.667-79.107l0.013-0.679c0-33.28-9.387-70.4-77.653-70.4v-48.213c68.267 0 77.653-42.667 77.653-70.4-0.747-27.85-3.51-54.477-8.158-80.45l0.478 3.224c-4.192-22.894-6.956-49.672-7.666-76.962l-0.014-0.692c0-96 59.733-130.56 139.093-130.56h20.48v52.48h-17.493c-53.76 0-76.373 29.44-76.373 82.773 0.636 24.415 3.247 47.784 7.686 70.499l-0.433-2.659c4.083 21.534 6.555 46.438 6.825 71.871l0.002 0.236c0.77 4.454 1.21 9.583 1.21 14.816 0 42.986-29.704 79.033-69.706 88.737l-0.624 0.128c40.531 9.715 70.19 45.641 70.19 88.494 0 4.909-0.389 9.726-1.138 14.424l0.068-0.518c-0.293 25.835-2.765 50.886-7.239 75.237l0.412-2.703c-4.183 20.81-6.803 44.938-7.248 69.596l-0.005 0.377c0 51.2 19.2 81.493 75.947 81.493h17.92v52.48h-20.48c-81.493 0-139.093-37.12-139.093-124.587zM896 430.080v48.213c-68.267 0-77.653 37.12-77.653 70.4 0.697 28.722 3.463 56.248 8.161 83.109l-0.481-3.322c4.173 23.249 6.937 50.473 7.665 78.205l0.015 0.728c0 85.333-58.453 124.587-139.947 124.587h-19.2v-52.48h17.067c55.467 0 75.947-30.293 75.947-81.493-0.933-24.605-3.38-47.828-7.272-70.557l0.445 3.144c-4.289-21.478-7.056-46.471-7.67-71.997l-0.010-0.536c-0.55-3.79-0.863-8.166-0.863-12.615 0-43.106 29.458-79.331 69.341-89.644l0.643-0.141c-40.585-10.412-70.1-46.666-70.1-89.815 0-4.742 0.356-9.4 1.044-13.95l-0.064 0.512c0.548-26.006 3.319-51.004 8.132-75.266l-0.452 2.733c3.447-19.828 5.894-43.35 6.797-67.267l0.030-1c0-53.333-23.040-81.92-76.8-82.773h-16.213v-52.48h20.053c78.507 0 139.093 34.56 139.093 130.56-0.724 27.981-3.488 54.759-8.158 80.877l0.478-3.223c-4.17 22.749-6.933 49.376-7.665 76.51l-0.015 0.717c0 27.307 9.387 68.267 77.653 68.267z" />
|
||||
<glyph unicode="" glyph-name="asterisks" d="M917.333 457.813l-13.227 46.080-85.76-33.28 2.987 105.387h-44.373l2.56-104.107-87.467 34.133-13.227-45.653 87.467-27.307-58.027-83.2 35.84-27.733 52.053 84.907 53.76-87.040 35.84 28.587-57.173 81.92 88.747 27.307zM331.947 503.893l-86.187-33.28 2.987 105.387h-43.947l2.56-104.107-87.467 34.133-13.227-45.653 87.467-27.307-58.453-83.2 36.267-27.733 52.053 84.907 53.76-87.040 35.84 28.587-57.173 81.92 88.747 27.307-13.227 46.080zM617.813 503.893l-85.76-33.28 2.987 105.387h-44.373l2.56-104.107-87.040 34.133-13.227-45.653 87.040-27.307-58.027-83.2 36.267-27.733 52.053 84.907 53.76-87.040 35.84 28.587-57.6 81.92 88.747 27.307-13.227 46.080z" />
|
||||
<glyph unicode="" glyph-name="bold" d="M832 285.44c0.001 0.23 0.002 0.501 0.002 0.773 0 84.44-55.37 155.947-131.783 180.194l-1.339 0.367-24.747 9.387 23.040 12.8c51.178 27.492 85.386 80.648 85.386 141.794 0 1.451-0.019 2.897-0.058 4.338l0.004-0.213c0 102.827-85.333 174.507-203.093 174.507h-344.747v-721.493h370.347c131.413 0 226.987 85.333 226.987 197.547zM378.453 693.76h178.773c45.243 0 81.92-36.677 81.92-81.92s-36.677-81.92-81.92-81.92v0h-178.773zM378.453 203.52v210.347h202.667c2.004 0.136 4.344 0.213 6.702 0.213 58.203 0 105.387-47.183 105.387-105.387s-47.183-105.387-105.387-105.387c-2.358 0-4.698 0.077-7.017 0.23l0.315-0.017z" />
|
||||
<glyph unicode="" glyph-name="bulleted-list" d="M427.947 774.4h468.053v-93.013h-468.053v93.013zM427.947 494.933h468.053v-93.013h-468.053v93.013zM427.947 215.467h468.053v-93.013h-468.053v93.013zM241.067 238.933c-38.645 0-69.973-31.328-69.973-69.973s31.328-69.973 69.973-69.973c38.645 0 69.973 31.328 69.973 69.973v0c0 38.645-31.328 69.973-69.973 69.973v0zM241.067 149.333c-9.661 0-17.493 7.832-17.493 17.493v0c0 9.661 7.832 17.493 17.493 17.493v0c9.661 0 17.493-7.832 17.493-17.493v0c-0.232-9.566-7.927-17.261-17.471-17.493h-0.022zM241.067 518.4c-38.645-0.001-69.972-31.329-69.972-69.973s31.328-69.973 69.973-69.973c38.495 0 69.73 31.085 69.972 69.524v0.023c-0.24 38.548-31.425 69.733-69.95 69.973h-0.023zM241.067 797.44c-38.645 0-69.973-31.328-69.973-69.973s31.328-69.973 69.973-69.973c38.645 0 69.973 31.328 69.973 69.973v0c0 38.645-31.328 69.973-69.973 69.973v0z" />
|
||||
<glyph unicode="" glyph-name="embed-code" d="M386.987 64h83.2l166.827 768h-83.2l-166.827-768zM302.080 665.173l-185.173-192c-6.313-6.58-10.2-15.529-10.2-25.387s3.887-18.807 10.212-25.399l-0.012 0.013 185.173-192 49.067 50.773-160.427 166.827 160.427 166.4zM907.093 473.173l-185.173 192-49.067-50.773 160.853-166.4-160.853-166.827 49.067-50.773 185.173 192c6.313 6.58 10.2 15.529 10.2 25.387s-3.887 18.807-10.212 25.399l0.012-0.013z" />
|
||||
<glyph unicode="" glyph-name="back" d="M618.24 85.333l-325.973 329.387c-8.347 8.594-13.494 20.336-13.494 33.28s5.146 24.686 13.505 33.292l-0.011-0.012 325.973 329.387 64.427-66.56-291.413-296.107 291.413-296.107z" />
|
||||
<glyph unicode="" glyph-name="forward" d="M405.333 85.333l-64 66.56 290.987 296.107-290.987 296.107 64 66.56 325.547-329.387c8.549-8.502 13.84-20.273 13.84-33.28s-5.291-24.778-13.838-33.278l-0.002-0.002z" />
|
||||
<glyph unicode="" glyph-name="h1" d="M425.813 476.16h-207.36v279.040h-69.12v-628.053h69.12v279.040h207.36v-279.040h69.12v628.053h-69.12v-279.040zM770.987 197.547v571.307l-183.467-61.867 21.76-66.133 92.587 31.147v-474.453h-103.68v-69.547h276.48v69.547h-103.68z" />
|
||||
<glyph unicode="" glyph-name="h2" d="M434.347 480.427h-213.333v282.027h-70.827v-634.453h70.827v282.027h213.333v-282.027h70.827v634.453h-70.827v-282.027zM669.867 199.253c67.84 79.36 213.333 264.533 213.333 386.987s-77.227 176.213-153.6 176.213-154.027-54.613-154.027-175.787h70.827c0 66.987 30.293 104.96 85.333 104.96s82.773-39.253 82.773-105.387c0-104.533-164.267-306.347-227.413-375.040l-9.387-10.24v-72.107h318.293v70.4z" />
|
||||
<glyph unicode="" glyph-name="h3" d="M430.080 478.293h-210.773v280.32h-69.973v-630.613h69.973v280.32h210.773v-280.32h69.973v630.613h-69.973v-280.32zM774.827 481.28c58.969 23.632 99.858 80.307 99.858 146.532 0 0.835-0.006 1.668-0.019 2.5l0.002-0.126c0 183.893-304.213 183.893-304.213 0h69.547c1.884 39.263 34.174 70.382 73.734 70.382 2.733 0 5.432-0.149 8.088-0.438l-0.329 0.029c0.626 0.015 1.364 0.023 2.104 0.023 21.39 0 41.132-7.059 57.023-18.974l-0.247 0.177c14.142-11.512 23.102-28.92 23.102-48.42 0-0.978-0.023-1.95-0.067-2.917l0.005 0.137c0-103.253-145.92-105.387-151.893-105.387l-4.267-69.547c6.4 0 156.16-20.907 156.16-151.040 0-66.133-30.293-105.387-81.92-105.387s-81.493 35.84-81.493 104.533h-69.547c0-120.747 76.373-174.507 151.893-174.507s152.32 54.187 152.32 175.36c0.009 0.718 0.014 1.565 0.014 2.414 0 74.018-39.68 138.768-98.928 174.14l-0.927 0.513z" />
|
||||
<glyph unicode="" glyph-name="heading" d="M678.4 808.96v-314.027h-332.8v314.027h-110.933v-721.493h110.933v314.453h332.8v-314.453h110.933v721.493h-110.933z" />
|
||||
<glyph unicode="" glyph-name="info" d="M478.293 517.973h69.973v-279.467h-69.973v279.467zM512 657.493c-25.685 0-46.507-20.822-46.507-46.507s20.822-46.507 46.507-46.507c25.685 0 46.507 20.822 46.507 46.507v0c0 25.685-20.822 46.507-46.507 46.507v0zM512 64c-212.077 0-384 171.923-384 384s171.923 384 384 384c212.077 0 384-171.923 384-384v0c0-212.077-171.923-384-384-384v0zM512 762.027c-172.881-0.727-312.747-141.042-312.747-314.024 0-173.432 140.595-314.027 314.027-314.027s314.025 140.593 314.027 314.024v0c-0.242 173.485-140.935 314.028-314.453 314.028-0.3 0-0.6 0-0.9-0.001h0.046z" />
|
||||
<glyph unicode="" glyph-name="italic" d="M736.427 738.987l10.24 69.547h-343.040l-9.813-69.547h124.16l-83.627-581.973h-125.44l-10.24-69.547h340.48l10.24 69.547h-120.32l84.053 581.973h123.307z" />
|
||||
<glyph unicode="" glyph-name="layout" d="M768 808.533h-512c-57.452-0.482-103.867-47.047-104.107-104.51v-512.023c0.236-57.574 46.603-104.24 104.038-104.959l0.068-0.001h512c0.127-0.001 0.277-0.001 0.427-0.001 57.968 0 104.96 46.992 104.96 104.96 0 0 0 0.001 0 0.001v0 512c0 0.001 0 0.002 0 0.003 0 57.732-46.801 104.533-104.533 104.533-0.3 0-0.6-0.001-0.899-0.004h0.046zM221.867 192v489.387h256v-524.373h-221.867c-18.952 0.477-34.133 15.954-34.133 34.976 0 0.004 0 0.007 0 0.011v-0.001zM768 157.013h-220.587v524.373h256v-489.387c0-19.323-15.664-34.987-34.987-34.987v0z" />
|
||||
<glyph unicode="" glyph-name="line" d="M192 482.987h640v-69.973h-640v69.973z" />
|
||||
<glyph unicode="" glyph-name="link" d="M345.6 315.307c0-0.035 0-0.077 0-0.119 0-18.616 15.091-33.707 33.707-33.707 9.337 0 17.788 3.797 23.892 9.931l265.388 265.388c6.135 6.106 9.932 14.556 9.932 23.893s-3.797 17.788-9.93 23.892l-0.001 0.001c-6.189 5.981-14.596 9.702-23.872 9.813h-0.022c-9.313-0.016-17.749-3.759-23.898-9.817l-265.383-265.383c-6.065-6.094-9.814-14.497-9.814-23.775 0-0.042 0-0.083 0-0.125v0.006zM481.707 306.347c-4-15.99-12.062-29.728-23.029-40.522l-0.011-0.011-85.333-87.467c-16.257-16.088-38.625-26.027-63.315-26.027-0.091 0-0.181 0-0.272 0h0.014c-50.33 0.241-91.066 40.977-91.307 91.284v0.023c-0.004 0.306-0.006 0.668-0.006 1.031 0 24.582 9.947 46.841 26.035 62.971l-0.002-0.002 87.467 85.333c10.696 10.916 24.279 18.964 39.514 22.91l0.593 0.13 64 64c-12.663 3.523-27.203 5.547-42.216 5.547-0.158 0-0.316 0-0.475-0.001h0.024c-0.321 0.002-0.701 0.004-1.081 0.004-42.101 0-80.201-17.128-107.712-44.797l-0.007-0.007-87.467-85.333c-29.387-28.098-47.686-67.582-47.787-111.341v-0.019c0.914-87.432 72.003-157.958 159.565-157.958 42.923 0 81.887 16.947 110.568 44.515l-0.053-0.050 85.333 87.467c27.824 27.929 45.026 66.457 45.026 109.002 0 15.438-2.265 30.346-6.48 44.411l0.281-1.093zM827.733 763.733c-28.609 28.433-68.039 46.007-111.573 46.007s-82.964-17.574-111.581-46.015l0.008 0.008-85.333-87.467c-27.935-28.012-45.208-66.669-45.208-109.361 0-15.149 2.175-29.79 6.229-43.627l-0.275 1.095 64 64c4.076 15.828 12.124 29.411 23.026 40.093l0.014 0.014 85.333 87.467c15.201 11.726 34.519 18.795 55.488 18.795 50.427 0 91.307-40.879 91.307-91.307 0-20.969-7.068-40.287-18.953-55.701l0.158 0.213-87.467-85.333c-10.718-11.111-24.494-19.204-39.972-22.926l-0.562-0.114-64-63.573c12.972-3.934 27.881-6.199 43.318-6.199 42.545 0 81.073 17.202 109.007 45.030l-0.005-0.005 87.467 85.333c28.433 28.609 46.007 68.039 46.007 111.573 0 43.746-17.746 83.348-46.432 111.999l-0.002 0.002z" />
|
||||
<glyph unicode="" glyph-name="more" d="M597.333 170.667c0 47.128-38.205 85.333-85.333 85.333s-85.333-38.205-85.333-85.333c0-47.128 38.205-85.333 85.333-85.333v0c47.128 0 85.333 38.205 85.333 85.333v0zM597.333 448c0 47.128-38.205 85.333-85.333 85.333s-85.333-38.205-85.333-85.333c0-47.128 38.205-85.333 85.333-85.333v0c47.128 0 85.333 38.205 85.333 85.333v0zM597.333 725.333c0 47.128-38.205 85.333-85.333 85.333s-85.333-38.205-85.333-85.333c0-47.128 38.205-85.333 85.333-85.333v0c47.128 0 85.333 38.205 85.333 85.333v0z" />
|
||||
<glyph unicode="" glyph-name="numbered-list" d="M216.747 629.333h46.080v181.333h-5.973l-84.907-28.587v-39.68l44.8 15.36v-128.427zM256 418.133l18.773 16.213c4.41 4.189 8.462 8.647 12.172 13.386l0.202 0.268c4.539 5.277 8.4 11.325 11.333 17.879l0.187 0.468c2.143 5.31 3.394 11.466 3.413 17.912v0.008c0 0.020 0 0.043 0 0.067 0 7.863-1.729 15.322-4.828 22.018l0.135-0.325c-3.468 6.962-8.213 12.799-13.979 17.415l-0.101 0.078c-6.763 4.789-14.822 8.235-23.54 9.762l-0.353 0.051c-9.333 2.745-20.056 4.324-31.147 4.324s-21.814-1.579-31.954-4.525l0.808 0.201c-9.778-3.257-18.265-7.748-25.839-13.397l0.239 0.17c-6.594-5.232-12.022-11.613-16.050-18.879l-0.163-0.321c-3.76-6.777-5.973-14.861-5.973-23.463 0-0.001 0-0.003 0-0.004v0-5.12h52.053v5.12c-0.037 0.477-0.059 1.034-0.059 1.595 0 6.051 2.47 11.525 6.457 15.469l0.002 0.002c4.9 3.777 11.125 6.053 17.883 6.053 0.764 0 1.521-0.029 2.27-0.086l-0.1 0.006c1.411 0.226 3.037 0.356 4.693 0.356s3.283-0.129 4.869-0.378l-0.176 0.023c2.503-0.811 4.647-2.13 6.403-3.843l-0.003 0.003c2.060-1.231 3.586-3.173 4.25-5.48l0.016-0.066c0.346-1.215 0.544-2.611 0.544-4.053s-0.199-2.838-0.57-4.161l0.026 0.108c0.172-0.898 0.27-1.931 0.27-2.987s-0.098-2.089-0.286-3.090l0.016 0.103c-0.981-2.957-2.435-5.512-4.295-7.714l0.028 0.034-8.107-9.813-12.8-11.52-69.12-61.013v-29.44h154.453v36.267h-82.773zM298.667 165.973c-3.59 3.91-7.656 7.297-12.128 10.097l-0.245 0.143-5.12 2.56h3.413c4.429 3.683 8.128 8.061 10.97 12.995l0.123 0.231c2.708 3.313 5.003 7.114 6.715 11.218l0.112 0.302c0.309 1.86 0.485 4.003 0.485 6.187s-0.176 4.327-0.516 6.415l0.031-0.229c0.008 0.314 0.013 0.684 0.013 1.055 0 6.424-1.42 12.517-3.963 17.981l0.11-0.263c-4.263 6.796-9.844 12.377-16.425 16.514l-0.215 0.126c-7.405 5.47-16.092 9.724-25.486 12.25l-0.54 0.124c-8.912 2.033-19.147 3.198-29.653 3.198s-20.741-1.165-30.581-3.373l0.928 0.175c-9.078-2.512-17.019-6.010-24.303-10.473l0.41 0.233c-6.775-3.973-12.355-9.281-16.525-15.601l-0.115-0.185c-3.68-6.1-5.889-13.45-5.973-21.31v-5.144h54.613v5.12c-0.294 0.827-0.463 1.78-0.463 2.773s0.17 1.947 0.482 2.833l-0.018-0.060c1.098 1.879 2.527 3.444 4.222 4.663l0.044 0.030c2.209 1.233 4.766 2.257 7.459 2.939l0.221 0.047c3.142 0.716 6.75 1.126 10.453 1.126s7.312-0.41 10.781-1.188l-0.327 0.062c2.764-0.763 5.18-1.933 7.324-3.461l-0.070 0.048c1.714-1.301 3.036-3.035 3.813-5.041l0.027-0.079c0.214-1.026 0.336-2.205 0.336-3.413s-0.122-2.387-0.356-3.526l0.019 0.113c0.014-0.252 0.023-0.546 0.023-0.843 0-5.005-2.33-9.467-5.964-12.359l-0.032-0.025c-5.025-3.28-11.178-5.231-17.786-5.231-0.948 0-1.887 0.040-2.815 0.119l0.121-0.008h-14.933v-35.413h17.92c5.12 0 29.867 0 29.867-20.053 0-5.973-2.56-18.347-26.88-18.347-0.784-0.093-1.692-0.146-2.612-0.146-12.411 0-22.572 9.635-23.411 21.833l-0.004 0.073h-52.907v-5.12c-0.004-0.218-0.006-0.475-0.006-0.733 0-8.608 2.549-16.62 6.933-23.323l-0.1 0.163c4.985-6.692 11.082-12.245 18.066-16.482l0.281-0.158c7.124-3.941 15.375-7.167 24.069-9.25l0.677-0.137c8.045-1.903 17.281-2.995 26.772-2.995 0.488 0 0.975 0.003 1.462 0.009l-0.074-0.001c11.043 0.011 21.79 1.254 32.119 3.599l-0.972-0.186c9.68 2.186 18.234 5.72 25.974 10.453l-0.374-0.213c7.078 4.412 12.928 10.127 17.363 16.857l0.13 0.21c4.024 6.391 6.411 14.164 6.411 22.494 0 0.342-0.004 0.683-0.012 1.023l0.001-0.050c0.002 0.133 0.003 0.29 0.003 0.447 0 4.906-0.941 9.591-2.652 13.887l0.089-0.254c-1.572 4.61-3.898 8.585-6.863 11.989l0.036-0.043zM421.547 765.44h453.12v-90.88h-453.12v90.88zM421.547 493.227h453.12v-90.88h-453.12v90.88zM421.547 221.44h453.12v-90.88h-453.12v90.88z" />
|
||||
<glyph unicode="" glyph-name="quote" d="M213.333 410.027h162.987c-1.426-50.021-42.317-90.028-92.551-90.028-0.162 0-0.325 0-0.487 0.001h0.025v-91.307c0.514-0.005 1.122-0.008 1.731-0.008 100.75 0 182.62 80.834 184.294 181.184l0.002 0.157v251.307h-256zM554.667 661.333v-251.307h162.987c-1.426-50.021-42.317-90.028-92.551-90.028-0.162 0-0.325 0-0.487 0.001h0.025v-91.307c0.514-0.005 1.122-0.008 1.731-0.008 100.75 0 182.62 80.834 184.294 181.184l0.002 0.157v251.307z" />
|
||||
<glyph unicode="" glyph-name="alarm" d="M778.667 469.333c0 177.92-87.467 256-162.56 287.573v13.653c0 57.497-46.61 104.107-104.107 104.107s-104.107-46.61-104.107-104.107v0-13.653c-75.093-32.853-162.56-109.653-162.56-287.573 0-186.027-78.080-249.6-78.507-250.027-8.846-6.342-14.541-16.592-14.541-28.171 0-19.087 15.473-34.56 34.56-34.56 0.312 0 0.623 0.004 0.933 0.012l-0.046-0.001h177.92c16.582-66.515 75.8-115.018 146.347-115.018s129.765 48.503 146.127 113.98l0.219 1.038h177.92c0.036 0 0.079 0 0.122 0 15.116 0 27.966 9.705 32.658 23.224l0.073 0.243c1.142 3.328 1.801 7.162 1.801 11.149 0 11.17-5.172 21.133-13.252 27.624l-0.069 0.054s-78.933 64.427-78.933 250.453zM512 805.12c16.193-0.201 29.689-11.509 33.234-26.646l0.046-0.234c-9.59 1.85-21.024 3.221-32.664 3.815l-0.616 0.025c-12.255-0.619-23.689-1.99-34.867-4.088l1.587 0.248c3.591 15.371 17.087 26.679 33.258 26.88h0.022zM512 110.507c-0.030 0-0.066 0-0.101 0-31.979 0-59.61 18.614-72.649 45.599l-0.21 0.482h145.92c-13.249-27.466-40.88-46.080-72.859-46.080-0.036 0-0.071 0-0.107 0h0.005zM258.987 226.133c35.432 64.914 56.268 142.188 56.268 224.328 0 6.637-0.136 13.242-0.405 19.813l0.031-0.941c0 234.667 176.213 242.773 197.12 243.2s197.12-8.96 197.12-243.2c-0.239-5.63-0.375-12.235-0.375-18.872 0-82.14 20.836-159.414 57.511-226.824l-1.243 2.496zM848.213 533.333l69.12 7.68c-11.235 134.019-91.096 247.111-203.956 304.932l-2.124 0.988-28.587-61.867c93.24-49.059 157.544-142.068 165.488-250.727l0.059-1.006zM341.333 785.067l-28.587 63.573c-114.984-58.809-194.845-171.901-205.987-304.543l-0.093-1.377 69.12-9.387c8.003 109.665 72.307 202.674 163.864 250.925l1.683 0.808z" />
|
||||
<glyph unicode="" glyph-name="share" d="M804.267 169.387c0-19.323-15.664-34.987-34.987-34.987v0h-513.28c-0.127-0.002-0.277-0.003-0.427-0.003-19.323 0-34.987 15.664-34.987 34.987 0 0.001 0 0.002 0 0.003v0 514.56c0 0.001 0 0.002 0 0.003 0 19.323 15.664 34.987 34.987 34.987 0.15 0 0.3-0.001 0.449-0.003h162.537v70.4h-162.56c-0.254 0.002-0.554 0.003-0.854 0.003-58.29 0-105.572-47.133-105.812-105.367v-514.583c0.241-58.257 47.522-105.39 105.812-105.39 0.3 0 0.6 0.001 0.9 0.004h511.954c58.106 0.241 105.145 47.281 105.387 105.364v163.863h-69.12zM558.933 789.333v-69.973h195.84l-314.453-314.88 49.493-49.493 314.453 314.453v-195.84h70.4v315.733h-315.733z" />
|
||||
<glyph unicode="" glyph-name="table" d="M768 789.333h-512c-0.254 0.002-0.554 0.003-0.854 0.003-58.29 0-105.572-47.133-105.812-105.367v-514.583c0.241-58.257 47.522-105.39 105.812-105.39 0.3 0 0.6 0.001 0.9 0.004h511.954c58.106 0.241 105.145 47.281 105.387 105.364v514.583c-0.241 58.106-47.281 105.145-105.364 105.387h-0.023zM802.987 438.613h-256v222.72h256zM475.733 438.613h-256v222.72h256zM256 133.973c-0.001 0-0.002 0-0.003 0-19.323 0-34.987 15.664-34.987 34.987 0 0.15 0.001 0.3 0.003 0.449v-0.023 198.827h256v-234.24zM770.56 133.973h-223.573v234.24h256v-198.827c0.002-0.127 0.003-0.277 0.003-0.427 0-19.323-15.664-34.987-34.987-34.987-0.001 0-0.002 0-0.003 0v0z" />
|
||||
<glyph unicode="" glyph-name="to-do-list" d="M478.72 739.84h417.28v-92.587h-417.28v92.587zM478.72 462.080h417.28v-92.587h-417.28v92.587zM478.72 184.32h417.28v-92.587h-417.28v92.587zM410.453 761.6l-49.067 49.067-114.347-114.347-68.267 68.267-49.067-49.493 117.333-116.907 163.413 163.413zM247.040 418.56l-68.267 68.267-49.067-49.067 117.333-117.333 163.413 163.413-49.067 49.067-114.347-114.347zM247.040 140.8l-68.267 68.267-49.067-49.067 117.333-117.333 163.413 163.413-49.067 49.067-114.347-114.347z" />
|
||||
<glyph unicode="" glyph-name="add-date" d="M550.4 549.547h-69.973v-139.52h-139.52v-69.547h139.52v-139.947h69.973v139.947h139.52v69.547h-139.52v139.52zM768 774.4h-35.413v57.6h-69.547v-57.6h-302.507v57.6h-69.973v-57.6h-34.56c-0.255 0.002-0.556 0.004-0.857 0.004-56.962 0-103.195-45.936-103.676-102.785v-504.366c0.483-57.129 46.906-103.254 104.103-103.254 0.151 0 0.302 0 0.454 0.001h511.977c0.128-0.001 0.279-0.001 0.43-0.001 57.197 0 103.62 46.126 104.103 103.209v504.366c-0.481 56.894-46.714 102.83-103.677 102.83-0.301 0-0.602-0.001-0.903-0.004h0.046zM768 134.4h-512c-0.001 0-0.002 0-0.003 0-19.173 0-34.743 15.422-34.984 34.537v479.596h581.973v-480c-0.241-19.138-15.811-34.56-34.984-34.56-0.001 0-0.002 0-0.003 0v0z" />
|
||||
<glyph unicode="" glyph-name="attachment" d="M820.48 769.707c-27.405 24.997-64.023 40.307-104.216 40.307-47.678 0-90.324-21.543-118.735-55.427l-0.196-0.24-269.227-317.867c-16.858-19.904-27.108-45.872-27.108-74.235 0-33.567 14.357-63.781 37.265-84.837l0.083-0.075c18.671-17.104 43.654-27.584 71.085-27.584 32.612 0 61.765 14.813 81.096 38.078l0.139 0.172 222.293 263.253-48.213 42.667-221.867-263.253c-7.885-9.798-19.875-16.014-33.318-16.014-11.405 0-21.765 4.475-29.419 11.764l0.018-0.017c-9.311 8.672-15.116 21.002-15.116 34.688 0 11.498 4.097 22.039 10.912 30.243l-0.063-0.078 267.947 317.867c17.261 20.688 43.052 33.757 71.895 33.757 24.276 0 46.39-9.258 63.004-24.435l-0.072 0.065c19.938-18.661 32.363-45.139 32.363-74.518 0-24.781-8.839-47.497-23.537-65.168l0.134 0.166-318.293-375.893c-26.404-31.634-65.848-51.616-109.96-51.616-37.269 0-71.207 14.264-96.651 37.63l0.104-0.094c-30.511 28.506-49.528 68.982-49.528 113.901 0 37.919 13.552 72.673 36.077 99.682l-0.202-0.249 267.947 318.72 4.267 4.693-47.787 42.667-272.213-320.427c-32.402-38.476-52.091-88.588-52.091-143.298 0-64.722 27.554-123.008 71.572-163.768l0.146-0.133c35.963-33.874 84.38-54.878 137.697-55.466l0.117-0.001h14.507c58.091 4.22 108.944 32.078 143.51 73.895l0.277 0.345 318.293 377.173c24.403 28.93 39.234 66.627 39.234 107.788 0 4.11-0.148 8.185-0.439 12.22l0.031-0.541c-2.402 44.983-22.601 84.824-53.622 112.943l-0.138 0.123z" />
|
||||
</font></defs></svg>
|
||||
|
After Width: | Height: | Size: 41 KiB |
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
Executable
+162
@@ -0,0 +1,162 @@
|
||||
@font-face {
|
||||
font-family: 'icomoon';
|
||||
src: url('fonts/icomoon.eot?f410v3');
|
||||
src: url('fonts/icomoon.eot?f410v3#iefix') format('embedded-opentype'),
|
||||
url('fonts/icomoon.ttf?f410v3') format('truetype'),
|
||||
url('fonts/icomoon.woff?f410v3') format('woff'),
|
||||
url('fonts/icomoon.svg?f410v3#icomoon') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
[class^="icon-"], [class*=" icon-"] {
|
||||
/* use !important to prevent issues with browser extensions that change fonts */
|
||||
font-family: 'icomoon' !important;
|
||||
speak: never;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
|
||||
/* Better Font Rendering =========== */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-error:before {
|
||||
content: "\e900";
|
||||
}
|
||||
.icon-attention:before {
|
||||
content: "\e901";
|
||||
}
|
||||
.icon-collapse:before {
|
||||
content: "\e902";
|
||||
}
|
||||
.icon-cancel:before {
|
||||
content: "\e903";
|
||||
}
|
||||
.icon-expand:before {
|
||||
content: "\e904";
|
||||
}
|
||||
.icon-notebooks:before {
|
||||
content: "\e905";
|
||||
}
|
||||
.icon-plus:before {
|
||||
content: "\e906";
|
||||
}
|
||||
.icon-update:before {
|
||||
content: "\e907";
|
||||
}
|
||||
.icon-tags:before {
|
||||
content: "\e908";
|
||||
}
|
||||
.icon-search:before {
|
||||
content: "\e909";
|
||||
}
|
||||
.icon-notes:before {
|
||||
content: "\e90a";
|
||||
}
|
||||
.icon-sync:before {
|
||||
content: "\e90b";
|
||||
}
|
||||
.icon-general:before {
|
||||
content: "\e90c";
|
||||
}
|
||||
.icon-note:before {
|
||||
content: "\e90d";
|
||||
}
|
||||
.icon-note-history:before {
|
||||
content: "\e90e";
|
||||
}
|
||||
.icon-application:before {
|
||||
content: "\e90f";
|
||||
}
|
||||
.icon-encryption:before {
|
||||
content: "\e910";
|
||||
}
|
||||
.icon-plugins:before {
|
||||
content: "\e911";
|
||||
}
|
||||
.icon-web-clipper:before {
|
||||
content: "\e912";
|
||||
}
|
||||
.icon-appearance:before {
|
||||
content: "\e913";
|
||||
}
|
||||
.icon-code:before {
|
||||
content: "\e914";
|
||||
}
|
||||
.icon-asterisks:before {
|
||||
content: "\e915";
|
||||
}
|
||||
.icon-bold:before {
|
||||
content: "\e916";
|
||||
}
|
||||
.icon-bulleted-list:before {
|
||||
content: "\e917";
|
||||
}
|
||||
.icon-embed-code:before {
|
||||
content: "\e918";
|
||||
}
|
||||
.icon-back:before {
|
||||
content: "\e919";
|
||||
}
|
||||
.icon-forward:before {
|
||||
content: "\e91a";
|
||||
}
|
||||
.icon-h1:before {
|
||||
content: "\e91b";
|
||||
}
|
||||
.icon-h2:before {
|
||||
content: "\e91c";
|
||||
}
|
||||
.icon-h3:before {
|
||||
content: "\e91d";
|
||||
}
|
||||
.icon-heading:before {
|
||||
content: "\e91e";
|
||||
}
|
||||
.icon-info:before {
|
||||
content: "\e91f";
|
||||
}
|
||||
.icon-italic:before {
|
||||
content: "\e920";
|
||||
}
|
||||
.icon-layout:before {
|
||||
content: "\e921";
|
||||
}
|
||||
.icon-line:before {
|
||||
content: "\e922";
|
||||
}
|
||||
.icon-link:before {
|
||||
content: "\e923";
|
||||
}
|
||||
.icon-more:before {
|
||||
content: "\e924";
|
||||
}
|
||||
.icon-numbered-list:before {
|
||||
content: "\e925";
|
||||
}
|
||||
.icon-quote:before {
|
||||
content: "\e926";
|
||||
}
|
||||
.icon-alarm:before {
|
||||
content: "\e927";
|
||||
}
|
||||
.icon-share:before {
|
||||
content: "\e928";
|
||||
}
|
||||
.icon-table:before {
|
||||
content: "\e929";
|
||||
}
|
||||
.icon-to-do-list:before {
|
||||
content: "\e92a";
|
||||
}
|
||||
.icon-add-date:before {
|
||||
content: "\e92b";
|
||||
}
|
||||
.icon-attachment:before {
|
||||
content: "\e92c";
|
||||
}
|
||||
@@ -43,9 +43,7 @@ function convertJsx(path) {
|
||||
|
||||
module.exports = function() {
|
||||
convertJsx(`${__dirname}/../gui`);
|
||||
convertJsx(`${__dirname}/../gui/SideBar`);
|
||||
convertJsx(`${__dirname}/../gui/MainScreen`);
|
||||
convertJsx(`${__dirname}/../gui/Header`);
|
||||
convertJsx(`${__dirname}/../gui/NoteList`);
|
||||
convertJsx(`${__dirname}/../plugins`);
|
||||
|
||||
|
||||
@@ -292,8 +292,8 @@ class BaseApplication {
|
||||
notes = await Tag.notes(parentId, options);
|
||||
} else if (parentType === BaseModel.TYPE_SEARCH) {
|
||||
const search = BaseModel.byId(state.searches, parentId);
|
||||
notes = await SearchEngineUtils.notesForQuery(search.query_pattern, { fuzzy: search.fuzzy });
|
||||
const parsedQuery = await SearchEngine.instance().parseQuery(search.query_pattern, search.fuzzy);
|
||||
notes = await SearchEngineUtils.notesForQuery(search.query_pattern);
|
||||
const parsedQuery = await SearchEngine.instance().parseQuery(search.query_pattern);
|
||||
highlightedWords = SearchEngine.instance().allParsedQueryTerms(parsedQuery);
|
||||
} else if (parentType === BaseModel.TYPE_SMART_FILTER) {
|
||||
notes = await Note.previews(parentId, options);
|
||||
|
||||
@@ -4,7 +4,8 @@ const { _ } = require('lib/locale');
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'historyBackward',
|
||||
label: () => _('Back'),
|
||||
iconName: 'fa-arrow-left',
|
||||
// iconName: 'fa-arrow-left',
|
||||
iconName: 'icon-back',
|
||||
};
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -4,7 +4,7 @@ const { _ } = require('lib/locale');
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'historyForward',
|
||||
label: () => _('Forward'),
|
||||
iconName: 'fa-arrow-right',
|
||||
iconName: 'icon-forward',
|
||||
};
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -10,7 +10,7 @@ class ModalDialog extends React.Component {
|
||||
}
|
||||
|
||||
styles() {
|
||||
const themeId = this.props.theme;
|
||||
const themeId = this.props.themeId;
|
||||
const theme = themeStyle(themeId);
|
||||
|
||||
if (this.styles_[themeId]) return this.styles_[themeId];
|
||||
|
||||
@@ -63,7 +63,7 @@ class AppNavComponent extends Component {
|
||||
|
||||
this.previousRouteName_ = route.routeName;
|
||||
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const style = { flex: 1, backgroundColor: theme.backgroundColor };
|
||||
|
||||
@@ -81,7 +81,7 @@ class AppNavComponent extends Component {
|
||||
const AppNav = connect(state => {
|
||||
return {
|
||||
route: state.route,
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
};
|
||||
})(AppNavComponent);
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ class NoteBodyViewer extends Component {
|
||||
this.forceUpdate_ = false;
|
||||
|
||||
const note = this.props.note;
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const bodyToRender = note ? note.body : '';
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ class NoteItemComponent extends Component {
|
||||
}
|
||||
|
||||
styles() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
if (this.styles_[this.props.theme]) return this.styles_[this.props.theme];
|
||||
if (this.styles_[this.props.themeId]) return this.styles_[this.props.themeId];
|
||||
this.styles_ = {};
|
||||
|
||||
const styles = {
|
||||
@@ -62,8 +62,8 @@ class NoteItemComponent extends Component {
|
||||
styles.selectionWrapperSelected = Object.assign({}, styles.selectionWrapper);
|
||||
styles.selectionWrapperSelected.backgroundColor = theme.selectedColor;
|
||||
|
||||
this.styles_[this.props.theme] = StyleSheet.create(styles);
|
||||
return this.styles_[this.props.theme];
|
||||
this.styles_[this.props.themeId] = StyleSheet.create(styles);
|
||||
return this.styles_[this.props.themeId];
|
||||
}
|
||||
|
||||
async todoCheckbox_change(checked) {
|
||||
@@ -107,7 +107,7 @@ class NoteItemComponent extends Component {
|
||||
const note = this.props.note ? this.props.note : {};
|
||||
const isTodo = !!Number(note.is_todo);
|
||||
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
// IOS: display: none crashes the app
|
||||
const checkboxStyle = !isTodo ? { display: 'none' } : { color: theme.color };
|
||||
@@ -145,7 +145,7 @@ class NoteItemComponent extends Component {
|
||||
|
||||
const NoteItem = connect(state => {
|
||||
return {
|
||||
theme: state.settings.theme,
|
||||
themeId: state.settings.theme,
|
||||
noteSelectionEnabled: state.noteSelectionEnabled,
|
||||
selectedNoteIds: state.selectedNoteIds,
|
||||
};
|
||||
|
||||
Loaded 100 of 138 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user