mirror of
https://github.com/anyproto/anytype-ts.git
synced 2025-06-11 18:20:27 +09:00
merge
This commit is contained in:
commit
e579b1ebfa
163 changed files with 3956 additions and 4013 deletions
22
dist/embed/iframe.html
vendored
22
dist/embed/iframe.html
vendored
|
@ -82,6 +82,7 @@
|
|||
let cachedHtml = '';
|
||||
let height = 0;
|
||||
let blockId = '';
|
||||
let player;
|
||||
|
||||
win.off('message resize');
|
||||
|
||||
|
@ -139,6 +140,23 @@
|
|||
insertHtml(html);
|
||||
};
|
||||
|
||||
if (processor == Processor.Youtube) {
|
||||
window.onYouTubeIframeAPIReady = () => {
|
||||
player = new YT.Player('player', {
|
||||
events: {
|
||||
onReady,
|
||||
onStateChange,
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onReady = (event) => {
|
||||
};
|
||||
|
||||
const onStateChange = (event) => {
|
||||
};
|
||||
};
|
||||
|
||||
loadLibs(libs, () => {
|
||||
if (!insertBeforeLoad) {
|
||||
insertHtml(html);
|
||||
|
@ -258,6 +276,10 @@
|
|||
libs.push('https://cpwebassets.codepen.io/assets/embed/ei.js');
|
||||
break;
|
||||
};
|
||||
|
||||
case Processor.Youtube:
|
||||
libs.push('https://www.youtube.com/iframe_api');
|
||||
break;
|
||||
};
|
||||
|
||||
return {
|
||||
|
|
1
dist/workers/graph.js
vendored
1
dist/workers/graph.js
vendored
|
@ -317,7 +317,6 @@ updateSettings = (param) => {
|
|||
|
||||
updateTheme = ({ theme, colors }) => {
|
||||
data.colors = colors;
|
||||
|
||||
initTheme(theme);
|
||||
redraw();
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const { app, shell, Menu, Tray } = require('electron');
|
||||
const { is } = require('electron-util');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const ConfigManager = require('./config.js');
|
||||
const Util = require('./util.js');
|
||||
|
@ -74,6 +75,21 @@ class MenuManager {
|
|||
|
||||
Separator,
|
||||
|
||||
{
|
||||
label: Util.translate('electronMenuCustomCss'),
|
||||
click: () => {
|
||||
const fp = path.join(Util.userPath(), 'custom.css');
|
||||
|
||||
if (!fs.existsSync(fp)) {
|
||||
fs.writeFileSync(fp, '');
|
||||
};
|
||||
|
||||
shell.openPath(fp);
|
||||
},
|
||||
},
|
||||
|
||||
Separator,
|
||||
|
||||
{ role: 'close', label: Util.translate('electronMenuClose') },
|
||||
]
|
||||
},
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.37.3
|
||||
0.38.1
|
64
package-lock.json
generated
64
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "anytype",
|
||||
"version": "0.43.7",
|
||||
"version": "0.43.26-alpha",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "anytype",
|
||||
"version": "0.43.7",
|
||||
"version": "0.43.26-alpha",
|
||||
"hasInstallScript": true,
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"dependencies": {
|
||||
|
@ -22,6 +22,7 @@
|
|||
"d3": "^7.0.1",
|
||||
"d3-force": "^3.0.0",
|
||||
"d3-force-cluster": "^0.1.2",
|
||||
"date-fns": "^4.1.0",
|
||||
"diff": "^5.2.0",
|
||||
"dompurify": "3.1.6",
|
||||
"electron-dl": "^1.14.0",
|
||||
|
@ -44,7 +45,7 @@
|
|||
"lazy-val": "^1.0.4",
|
||||
"lodash": "^4.17.20",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"mermaid": "^11.4.0",
|
||||
"mermaid": "^11.4.1",
|
||||
"mime-types": "^2.1.35",
|
||||
"mobx": "^6.6.1",
|
||||
"mobx-logger": "^0.7.1",
|
||||
|
@ -92,7 +93,7 @@
|
|||
"@typescript-eslint/parser": "^6.18.1",
|
||||
"cross-env": "^7.0.2",
|
||||
"css-loader": "^3.6.0",
|
||||
"electron": "^31.0.2",
|
||||
"electron": "^33.2.1",
|
||||
"electron-builder": "^24.13.3",
|
||||
"eslint": "^8.29.0",
|
||||
"eslint-plugin-react": "^7.31.11",
|
||||
|
@ -2358,9 +2359,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/d3-scale-chromatic": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz",
|
||||
"integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw=="
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
|
||||
"integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ=="
|
||||
},
|
||||
"node_modules/@types/d3-selection": {
|
||||
"version": "3.0.11",
|
||||
|
@ -2416,15 +2417,6 @@
|
|||
"@types/ms": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/dompurify": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.2.0.tgz",
|
||||
"integrity": "sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==",
|
||||
"deprecated": "This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed.",
|
||||
"dependencies": {
|
||||
"dompurify": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/emoji-mart": {
|
||||
"version": "3.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/emoji-mart/-/emoji-mart-3.0.14.tgz",
|
||||
|
@ -2786,6 +2778,12 @@
|
|||
"tapable": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@types/verror": {
|
||||
"version": "1.10.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.10.tgz",
|
||||
|
@ -5890,6 +5888,16 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
||||
|
@ -6539,11 +6547,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/electron": {
|
||||
"version": "31.7.5",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-31.7.5.tgz",
|
||||
"integrity": "sha512-8zFzVJdhxTRmoPcRiKkEmPW0bJHAUsTQJwEX2YJ8X0BVFIJLwSvHkSlpCjEExVbNCAk+gHnkIYX+2OyCXrRwHQ==",
|
||||
"version": "33.2.1",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-33.2.1.tgz",
|
||||
"integrity": "sha512-SG/nmSsK9Qg1p6wAW+ZfqU+AV8cmXMTIklUL18NnOKfZLlum4ZsDoVdmmmlL39ZmeCaq27dr7CgslRPahfoVJg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@electron/get": "^2.0.0",
|
||||
"@types/node": "^20.9.0",
|
||||
|
@ -10824,15 +10831,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/mermaid": {
|
||||
"version": "11.4.0",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.4.0.tgz",
|
||||
"integrity": "sha512-mxCfEYvADJqOiHfGpJXLs4/fAjHz448rH0pfY5fAoxiz70rQiDSzUUy4dNET2T08i46IVpjohPd6WWbzmRHiPA==",
|
||||
"version": "11.4.1",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.4.1.tgz",
|
||||
"integrity": "sha512-Mb01JT/x6CKDWaxigwfZYuYmDZ6xtrNwNlidKZwkSrDaY9n90tdrJTV5Umk+wP1fZscGptmKFXHsXMDEVZ+Q6A==",
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "^7.0.1",
|
||||
"@iconify/utils": "^2.1.32",
|
||||
"@mermaid-js/parser": "^0.3.0",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"cytoscape": "^3.29.2",
|
||||
"cytoscape-cose-bilkent": "^4.1.0",
|
||||
"cytoscape-fcose": "^2.2.0",
|
||||
|
@ -10840,7 +10846,7 @@
|
|||
"d3-sankey": "^0.12.3",
|
||||
"dagre-d3-es": "7.0.11",
|
||||
"dayjs": "^1.11.10",
|
||||
"dompurify": "^3.0.11 <3.1.7",
|
||||
"dompurify": "^3.2.1",
|
||||
"katex": "^0.16.9",
|
||||
"khroma": "^2.1.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
|
@ -10851,6 +10857,14 @@
|
|||
"uuid": "^9.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/mermaid/node_modules/dompurify": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.2.tgz",
|
||||
"integrity": "sha512-YMM+erhdZ2nkZ4fTNRTSI94mb7VG7uVF5vj5Zde7tImgnhZE3R6YW/IACGIHb2ux+QkEXMhe591N+5jWOmL4Zw==",
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"node_modules/methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "anytype",
|
||||
"version": "0.43.7",
|
||||
"version": "0.43.26-alpha",
|
||||
"description": "Anytype",
|
||||
"main": "electron.js",
|
||||
"scripts": {
|
||||
|
@ -68,7 +68,7 @@
|
|||
"@typescript-eslint/parser": "^6.18.1",
|
||||
"cross-env": "^7.0.2",
|
||||
"css-loader": "^3.6.0",
|
||||
"electron": "^31.0.2",
|
||||
"electron": "^33.2.1",
|
||||
"electron-builder": "^24.13.3",
|
||||
"eslint": "^8.29.0",
|
||||
"eslint-plugin-react": "^7.31.11",
|
||||
|
@ -102,6 +102,7 @@
|
|||
"d3": "^7.0.1",
|
||||
"d3-force": "^3.0.0",
|
||||
"d3-force-cluster": "^0.1.2",
|
||||
"date-fns": "^4.1.0",
|
||||
"diff": "^5.2.0",
|
||||
"dompurify": "3.1.6",
|
||||
"electron-dl": "^1.14.0",
|
||||
|
@ -124,7 +125,7 @@
|
|||
"lazy-val": "^1.0.4",
|
||||
"lodash": "^4.17.20",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"mermaid": "^11.4.0",
|
||||
"mermaid": "^11.4.1",
|
||||
"mime-types": "^2.1.35",
|
||||
"mobx": "^6.6.1",
|
||||
"mobx-logger": "^0.7.1",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="9.5" y="5" width="1" height="10" fill="#949494"/>
|
||||
<path d="M6 11L10 15L14 11" stroke="#949494"/>
|
||||
<rect x="9.5" y="5" width="1" height="10" fill="#b6b6b6"/>
|
||||
<path d="M6 11L10 15L14 11" stroke="#b6b6b6"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 208 B After Width: | Height: | Size: 208 B |
|
@ -6,7 +6,6 @@ export default {
|
|||
appName: 'Anytype',
|
||||
blankRouteId: '_blank_',
|
||||
storeSpaceId: '_anytype_marketplace',
|
||||
localLoversSpaceId: 'bafyreigyfkt6rbv24sbv5aq2hko3bhmv5xxlf22b4bypdu6j7hnphm3psq.23me69r569oi1',
|
||||
anytypeProfileId: '_anytype_profile',
|
||||
fontCode: 'plex',
|
||||
popupPinIds: [ 'search' ],
|
||||
|
@ -26,6 +25,10 @@ export default {
|
|||
testing: 'N4N1wDHFpFpovXBqdbq2TDXE9tXdXbtV1eTJFpKJW4YeaJqR'
|
||||
},
|
||||
|
||||
chatSpaceId: [
|
||||
'bafyreiezhzb4ggnhjwejmh67pd5grilk6jn3jt7y2rnfpbkjwekilreola.1t123w9f2lgn5',
|
||||
],
|
||||
|
||||
platforms: {
|
||||
win32: 'Windows',
|
||||
darwin: 'Mac',
|
||||
|
@ -40,7 +43,7 @@ export default {
|
|||
graphDepth: 5,
|
||||
|
||||
chat: {
|
||||
messages: 30,
|
||||
messages: 50,
|
||||
attachments: 10,
|
||||
files: 10,
|
||||
mentions: 10,
|
||||
|
|
|
@ -160,4 +160,8 @@ export default {
|
|||
],
|
||||
|
||||
pageCover: 'pageCover',
|
||||
|
||||
key: {
|
||||
mention: 'mentions',
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,6 +4,8 @@ export default [
|
|||
'/:page/:action/:id?',
|
||||
'/:page/:action/:id?/spaceId/:spaceId?',
|
||||
'/:page/:action/:id?/spaceId/:spaceId?/viewId/:viewId?',
|
||||
'/:page/:action/:id?/spaceId/:spaceId?/relationKey/:relationKey?',
|
||||
'/:page/:action/:id?/spaceId/:spaceId?/viewId/:viewId?/relationKey/:relationKey?',
|
||||
'/object',
|
||||
'/invite',
|
||||
'/membership'
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
"commonUncategorized": "Uncategorized",
|
||||
"commonDashboard": "Dashboard",
|
||||
"commonGraph": "Graph",
|
||||
"commonChat": "Chat",
|
||||
"commonFlow": "Flow",
|
||||
"commonLibrary": "Library",
|
||||
"commonAllContent": "All Objects",
|
||||
|
@ -175,6 +176,7 @@
|
|||
"commonSignUp": "Sign Up",
|
||||
"commonNotFound": "Not found",
|
||||
"commonCalculate": "Calculate",
|
||||
"commonRelations": "Relations",
|
||||
|
||||
"pluralDay": "day|days",
|
||||
"pluralObject": "Object|Objects",
|
||||
|
@ -221,6 +223,7 @@
|
|||
"electronMenuQuit": "Quit",
|
||||
"electronMenuFile": "File",
|
||||
"electronMenuDirectory": "Show Directory",
|
||||
"electronMenuCustomCss": "Apply custom CSS",
|
||||
"electronMenuWorkDirectory": "Work",
|
||||
"electronMenuDataDirectory": "Data",
|
||||
"electronMenuConfigDirectory": "Config",
|
||||
|
@ -482,6 +485,8 @@
|
|||
"pageMainVoidText": "Looks like you’ve cleaned the house. Ready to start fresh?<br/>Create a new space to get things rolling!",
|
||||
"pageMainVoidCreateSpace": "Create space",
|
||||
|
||||
"pageMainDateEmptyText" : "There is nothing here for this date yet",
|
||||
|
||||
"pageAuthLoginInvalidPhrase": "Invalid Key",
|
||||
"pageAuthLoginShortPhrase": "Key is too short",
|
||||
|
||||
|
@ -1951,7 +1956,6 @@
|
|||
"onboardingCollaborationTitle": "Introducing the local-first collaboration",
|
||||
"onboardingCollaborationText": "Check out the new Collaboration category to explore experiences that help you co-create, share and collaborate.",
|
||||
|
||||
"libDataviewRelations": "Relations",
|
||||
"libDataviewGroups": "Groups",
|
||||
"libDataviewView": "View",
|
||||
|
||||
|
@ -2257,6 +2261,8 @@
|
|||
"formulaPercentage": "Percentage",
|
||||
"formulaMath": "Math",
|
||||
"formulaDate": "Date",
|
||||
"formulaValue": "Count values",
|
||||
"formulaValueShort": "Values",
|
||||
"formulaDistinct": "Count unique values",
|
||||
"formulaDistinctShort": "Unique",
|
||||
"formulaEmpty": "Count empty",
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
.attachment.isImage { width: 72px; height: 72px; min-width: unset; display: flex; align-items: center; justify-content: center; }
|
||||
.attachment.isImage {
|
||||
.image { display: block; object-fit: cover; aspect-ratio: 1; border-radius: 8px; width: 100%; height: 100%; }
|
||||
.loaderWrapper { width: 72px; height: 72px; }
|
||||
}
|
||||
|
||||
.attachment.isVideo { width: 72px; height: 72px; min-width: unset; display: flex; align-items: center; justify-content: center; }
|
||||
|
@ -73,8 +74,8 @@
|
|||
.attachment { width: 540px; }
|
||||
}
|
||||
|
||||
.attachments.isSingle.isBookmark {
|
||||
.attachment { width: 360px; }
|
||||
.attachments.isSingle {
|
||||
.attachment.isBookmark { width: 360px; }
|
||||
}
|
||||
|
||||
/* Layouts */
|
||||
|
|
|
@ -34,10 +34,8 @@
|
|||
content: ''; display: block; position: absolute; top: 0px; left: 0px; width: 4px; height: 100%; background-color: var(--color-text-secondary);
|
||||
}
|
||||
.reply {
|
||||
.icon,
|
||||
.iconObject { flex-shrink: 0; width: 32px; height: 32px; }
|
||||
|
||||
.iconObject:not(.noBg, .isParticipant) { border-radius: 4px !important; background-color: var(--color-shape-highlight-medium) !important; }
|
||||
> .icon, > .iconObject { flex-shrink: 0; width: 32px; height: 32px; }
|
||||
> .iconObject:not(.noBg, .isParticipant) { border-radius: 4px !important; background-color: var(--color-shape-highlight-medium) !important; }
|
||||
|
||||
.icon.isMultiple { background-image: url('~img/icon/chat/attachment/multiple.svg'); }
|
||||
|
||||
|
|
|
@ -101,9 +101,11 @@
|
|||
.filterInputWrap { overflow: hidden; width: 0px; transition: width 0.2s $easeInQuint; }
|
||||
.line { display: none !important; }
|
||||
.icon.search { background-image: url('~img/icon/dataview/button/search.svg'); }
|
||||
.icon.clear { display: none; }
|
||||
}
|
||||
.filter.isActive {
|
||||
.icon:hover { background-color: unset; }
|
||||
.icon.clear { display: block; }
|
||||
.filterInputWrap { width: 120px; }
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
.block.blockDataview {
|
||||
.viewContent.viewGraph { position: relative; height: 100%; }
|
||||
.viewContent.viewGraph {
|
||||
#graphWrapper { width: 100%; height: 100%; }
|
||||
.graphWrapper { width: 100%; height: 100%; }
|
||||
#graph { width: 100%; height: 100%; }
|
||||
|
||||
canvas { width: 100%; height: 100%; background: var(--color-bg-primary); display: block; }
|
||||
|
|
|
@ -55,11 +55,11 @@
|
|||
.cellFoot {
|
||||
.flex { justify-content: flex-end; }
|
||||
.result { display: flex; flex-direction: row; align-items: center; gap: 0px 4px; max-width: 100%; }
|
||||
.result {
|
||||
.value { @include text-overflow-nw; }
|
||||
}
|
||||
.name { width: auto !important; color: var(--color-text-secondary); }
|
||||
.cellContent { height: 48px !important; }
|
||||
|
||||
.select { border: 0px; padding-left: 0px; padding-top: 0px; padding-bottom: 0px; opacity: 0; pointer-events: none; }
|
||||
.select:hover { background: none; }
|
||||
}
|
||||
|
||||
.cellHead.isDragging { border: 0px; height: 38px; }
|
||||
|
@ -80,10 +80,6 @@
|
|||
background-color: var(--color-shape-highlight-light);
|
||||
}
|
||||
|
||||
.cellFoot.cellKeyHover, .cellFoot.hover {
|
||||
.select { opacity: 1; pointer-events: all; }
|
||||
}
|
||||
|
||||
.cellFoot.cellKeyHover::after, .cellFoot.hover::after { height: calc(100% - 1px); }
|
||||
|
||||
.cellHead.last::after, .cellHead.hover::after { background: none; }
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
.titleWrap {
|
||||
.side { height: 32px; }
|
||||
.side.left { display: flex; flex-direction: row; align-items: center; gap: 0px 8px; }
|
||||
.side.right { display: flex; flex-direction: row; align-items: center; justify-content: flex-end; }
|
||||
|
||||
.title { @include text-paragraph; font-weight: 600; flex-grow: 1; }
|
||||
.icon.back { flex-shrink: 0; background-image: url('~img/icon/widget/back.svg'); }
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
.filter {
|
||||
.inner { position: relative; width: 100%; height: 35px; display: flex; align-items: center; gap: 0px 8px; }
|
||||
.filterInputWrap { position: relative; width: 100%; }
|
||||
|
||||
.input { padding: 0px !important; height: 28px; line-height: 28px; vertical-align: top; border: 0px !important; background: none; }
|
||||
|
||||
.icon { flex-shrink: 0; cursor: default; }
|
||||
|
|
|
@ -2,22 +2,24 @@
|
|||
|
||||
.inputWithFile {
|
||||
padding: 11px 13px; border-radius: 8px; border: solid 1px var(--color-shape-secondary); color: var(--color-control-active); @include text-common;
|
||||
transition: background 0.05s ease-in-out, border $transitionCommon; white-space: nowrap; overflow: hidden; line-height: 20px; text-align: left;
|
||||
transition: border $transitionCommon; overflow: hidden; line-height: 20px; text-align: left; display: flex; align-items: center; gap: 0px 6px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.inputWithFile:hover { border-color: var(--color-shape-primary); }
|
||||
.inputWithFile {
|
||||
.txt { line-height: 20px; height: 20px; overflow: hidden; width: calc(100% - 26px); vertical-align: top; }
|
||||
.inputWithFile-inner { display: flex; align-items: center; flex-wrap: wrap; flex-grow: 1; width: calc(100% - 26px); }
|
||||
|
||||
.fileWrap { display: inline-block; vertical-align: top; }
|
||||
.fileWrap .border { border-bottom: 0.05em solid var(--color-control-active); display: inline-block; line-height: 1; transition: $transitionAllCommon; }
|
||||
.fileWrap:hover .border { color: var(--color-text-primary); }
|
||||
|
||||
.input-text { height: 20px; line-height: 20px; vertical-align: top; padding: 0px; border: 0px; color: var(--color-text-primary); }
|
||||
.input::placeholder { color: var(--color-control-active); }
|
||||
|
||||
.urlToggle { cursor: text; display: inline-block; }
|
||||
.form { display: inline-block; vertical-align: top; }
|
||||
|
||||
#form { display: inline-block; vertical-align: top; }
|
||||
#url { height: 20px; line-height: 20px; vertical-align: top; padding: 0px; border: 0px; color: var(--color-text-primary); }
|
||||
|
||||
.icon { width: 20px; height: 20px; margin: 0px 6px 0px 0px; transition: none; vertical-align: top; }
|
||||
.icon { width: 20px; height: 20px; transition: none; flex-shrink: 0; }
|
||||
.icon.image { background-image: url('~img/icon/menu/action/block/media/image0.svg'); }
|
||||
.icon.video { background-image: url('~img/icon/menu/action/block/media/video0.svg'); }
|
||||
.icon.file { background-image: url('~img/icon/menu/action/block/media/file0.svg'); }
|
||||
|
@ -39,13 +41,13 @@
|
|||
|
||||
.inputWithFile.noFile {
|
||||
.urlToggle { width: 100%; }
|
||||
#form { width: 100%; }
|
||||
.form { width: 100%; }
|
||||
}
|
||||
.inputWithFile.noFile.isSmall .txt { height: 20px; }
|
||||
|
||||
.inputWithFile.isFocused {
|
||||
.fileWrap { display: none; }
|
||||
#form { width: 100%; }
|
||||
.form { width: 100%; }
|
||||
}
|
||||
|
||||
.inputWithFile.isSmall.isFocused {
|
||||
|
@ -53,7 +55,6 @@
|
|||
.fileWrap { display: block; }
|
||||
}
|
||||
|
||||
.inputWithFile.isIcon { }
|
||||
.inputWithFile.isIcon {
|
||||
#text { display: none; }
|
||||
.inputWithFile-inner { display: none; }
|
||||
}
|
|
@ -12,14 +12,14 @@
|
|||
.sectionName.first { padding-top: 4px; }
|
||||
.sectionName.first::before { display: none; }
|
||||
|
||||
.info {
|
||||
width: 154px; @include text-overflow-nw; line-height: 20px; border-radius: 4px; transition: background $transitionCommon;
|
||||
flex-shrink: 0; margin-right: 6px;
|
||||
}
|
||||
.info { @include text-overflow-nw; line-height: 20px; border-radius: 4px; transition: background $transitionCommon; flex-shrink: 0; }
|
||||
|
||||
.item { padding: 4px 16px; }
|
||||
.item.add { padding: 6px 16px; }
|
||||
.item.sides { display: flex; padding: 6px 16px; }
|
||||
.item.sides { display: flex; padding: 6px 16px; flex-direction: row; align-items: center; gap: 0px 6px; }
|
||||
.item.sides {
|
||||
.info { width: 154px; }
|
||||
}
|
||||
.item.empty { padding: 16px; @include text-small; }
|
||||
|
||||
.item {
|
||||
|
|
|
@ -150,7 +150,7 @@
|
|||
.descr { @include text-small; @include text-overflow-nw; line-height: 20px; height: 20px; color: var(--color-text-secondary); }
|
||||
.descr:empty { display: none; }
|
||||
|
||||
.info { line-height: 40px; width: calc(100% - 16px); }
|
||||
.info { line-height: 40px; }
|
||||
.txt { width: 100%; }
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,11 @@
|
|||
|
||||
.day.th { color: var(--color-control-active); @include text-small; }
|
||||
|
||||
.day {
|
||||
.inner { width: 100%; max-width: 28px; height: 28px; border-radius: 4px; transition: $transitionAllCommon; position: relative; }
|
||||
.bullet { width: 3px; height: 3px; border-radius: 50%; position: absolute; bottom: 2px; left: 50%; margin-left: -1.5px; background: var(--color-control-active); }
|
||||
}
|
||||
|
||||
.day.today, .day.active { font-weight: 600; }
|
||||
.day.today { color: var(--color-system-accent-125); }
|
||||
.day.active { background: var(--color-system-accent-100); color: var(--color-text-inversion); }
|
||||
|
|
|
@ -32,7 +32,8 @@ html.bodyIndex, html.bodyAuth {
|
|||
#sidebarToggle,
|
||||
#notifications,
|
||||
.sidebar,
|
||||
.sidebarDummy { display: none !important; }
|
||||
.sidebarDummy,
|
||||
.shareTooltip { display: none !important; }
|
||||
|
||||
.popup {
|
||||
.innerWrap { background: var(--color-popup) !important;; box-shadow: var(--shadow) !important; color: var(--color-text-primary) !important; }
|
||||
|
@ -89,7 +90,7 @@ html.bodyIndex, html.bodyAuth {
|
|||
ul { list-style-position: inside; padding-left: 0.75em; }
|
||||
}
|
||||
|
||||
.tooltip.menuNote { white-space: nowrap; background-color: var(--color-bg-secondary); color: var(--color-text-inversion); padding: 4px 8px; @include text-small; }
|
||||
.tooltip.menuNote { white-space: nowrap; background-color: var(--color-bg-secondary); color: var(--color-text-primary); padding: 4px 8px; @include text-small; }
|
||||
.tooltip.menuNote {
|
||||
.txt { line-height: 18px; }
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
@import "~scss/_mixins";
|
||||
|
||||
.pageMainDate { min-height: 100vh; }
|
||||
.pageMainDate {
|
||||
.wrapper { width: 704px; margin: 0px auto; padding: 40px 0px 80px 0px; user-select: none; }
|
||||
.wrapper { width: 704px; margin: 0px auto; padding: 52px 0px 80px 0px; user-select: none; }
|
||||
|
||||
.headSimple { align-items: center; height: 32px; }
|
||||
.headSimple { align-items: center; height: 32px; margin-bottom: 20px; }
|
||||
.headSimple {
|
||||
.side.right { gap: 0px; }
|
||||
.side.right {
|
||||
|
@ -18,11 +19,17 @@
|
|||
display: flex; flex-direction: row; gap: 8px; margin: 0px 0px 12px 0px; align-items: center; justify-content: flex-start; flex-wrap: wrap;
|
||||
}
|
||||
.categories {
|
||||
.button { border-radius: 8px; }
|
||||
.icon.mention { width: 20px; height: 20px; margin: 0px 6px 0px 0px; background-image: url('~img/icon/mention.svg'); }
|
||||
.separator { content: ''; background-color: var(--color-shape-secondary); width: 1px; height: 24px; }
|
||||
}
|
||||
|
||||
.cell.c-type {
|
||||
.iconObject { display: none; }
|
||||
}
|
||||
|
||||
.emptyContainer {
|
||||
text-align: center; align-content: center; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
#loader { position: fixed; top: 50% !important; transform: translateY(-50%); }
|
||||
}
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
.wrapper { display: flex; height: 100%; overflow: hidden; }
|
||||
|
||||
#graphWrapper { width: 100%; height: 100%; }
|
||||
.graphWrapper { width: 100%; height: 100%; }
|
||||
#graph { width: 100%; height: 100%; }
|
||||
|
||||
canvas { width: 100%; height: 100%; background: var(--color-bg-primary); display: block; }
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
.container { text-align: center; }
|
||||
.iconWrapper {
|
||||
display: flex; width: 320px; height: 104px; border-radius: 320px; margin: 0px auto 10px; align-items: center; justify-content: space-around;
|
||||
background: radial-gradient(50% 50% at 50% 50%, #FFBCBC 26.04%, rgba(255, 230, 230, 0.00) 100%);
|
||||
background: radial-gradient(50% 50% at 50% 50%, #ffbcbc 26.04%, rgba(255, 230, 230, 0.00) 100%);
|
||||
}
|
||||
.iconWrapper {
|
||||
.icon { width: 72px; height: 72px; background-image: url('~img/icon/popup/confirm/error.svg'); }
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
.iconObject { margin-bottom: 12px; }
|
||||
|
||||
.iconWrapper { display: flex; width: 320px; height: 104px; border-radius: 320px; margin: -16px auto 10px; align-items: center; justify-content: space-around; }
|
||||
.iconWrapper.green { background: radial-gradient(50% 50% at 50% 50%, #A9F496 26.04%, rgba(188, 242, 175, 0.00) 100%); }
|
||||
.iconWrapper.red { background: radial-gradient(50% 50% at 50% 50%, #FFBCBC 26.04%, rgba(255, 230, 230, 0.00) 100%); }
|
||||
.iconWrapper.yellow { background: radial-gradient(50% 50% at 50% 50%, #FFF0C8 0%, rgba(255, 240, 200, 0.00) 100%); }
|
||||
.iconWrapper.green { background: radial-gradient(50% 50% at 50% 50%, #a9f496 26.04%, rgba(188, 242, 175, 0.00) 100%); }
|
||||
.iconWrapper.red { background: radial-gradient(50% 50% at 50% 50%, #ffbcbc 26.04%, rgba(255, 230, 230, 0.00) 100%); }
|
||||
.iconWrapper.yellow { background: radial-gradient(50% 50% at 50% 50%, #fff0c8 0%, rgba(255, 240, 200, 0.00) 100%); }
|
||||
.iconWrapper.blue { background: radial-gradient(50% 50% at 50% 50%, #80d1ff 0%, rgba(187, 231, 255, 0.00) 100%); }
|
||||
|
||||
.iconWrapper {
|
||||
|
@ -41,6 +41,8 @@
|
|||
}
|
||||
|
||||
ul { padding-left: 1.25em; }
|
||||
|
||||
.error { margin-bottom: 0px; }
|
||||
}
|
||||
|
||||
.popup.popupConfirm.isWide {
|
||||
|
|
|
@ -203,11 +203,10 @@
|
|||
.icon.messageReply:hover, .icon.messageReply.hover { background-image: url('#{$themePath}/icon/chat/buttons/reply1.svg'); }
|
||||
.icon.more:hover, .icon.more.hover { background-image: url('#{$themePath}/icon/menu/action/more1.svg'); }
|
||||
}
|
||||
|
||||
.reply { color: var(--color-text-inversion); }
|
||||
}
|
||||
.message.isSelf {
|
||||
.side.right { background: #162908; }
|
||||
.reply { color: var(--color-text-inversion); }
|
||||
}
|
||||
|
||||
.form {
|
||||
|
|
|
@ -15,16 +15,19 @@
|
|||
.name { @include text-paragraph; @include text-overflow-nw; font-weight: 600; }
|
||||
}
|
||||
|
||||
.side.right { flex-shrink: 0; }
|
||||
.side.right { flex-shrink: 0; display: flex; flex-direction: row; align-items: center; justify-content: flex-end; }
|
||||
.side.right {
|
||||
.cnt {
|
||||
@include text-very-small; background-color: var(--color-control-accent); color: var(--color-control-bg); border-radius: 50%; min-width: 18px;
|
||||
text-align: center; font-weight: 500; height: 18px; line-height: 18px;
|
||||
text-align: center; font-weight: 500; height: 18px; line-height: 18px; display: none; padding: 0px 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.body.withCnt {
|
||||
.side.left { width: calc(100% - 30px); }
|
||||
.side.right {
|
||||
.cnt { display: block; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
.viewGraph { padding: 0px; }
|
||||
.viewGraph {
|
||||
#graphWrapper { height: 240px; }
|
||||
.graphWrapper { height: 240px; }
|
||||
canvas { width: 100%; height: 100%; }
|
||||
}
|
|
@ -173,6 +173,7 @@ class App extends React.Component<object, State> {
|
|||
render () {
|
||||
const { isLoading } = this.state;
|
||||
const platform = U.Common.getPlatform();
|
||||
const { shareTooltip } = S.Common
|
||||
|
||||
let drag = null;
|
||||
if (platform == I.Platform.Mac) {
|
||||
|
@ -204,7 +205,7 @@ class App extends React.Component<object, State> {
|
|||
<Progress />
|
||||
<Toast />
|
||||
<ListNotification key="listNotification" />
|
||||
<ShareTooltip showOnce={true} />
|
||||
<ShareTooltip showOnce={true} route={analytics.route.onboarding} />
|
||||
<Vault ref={ref => S.Common.refSet('vault', ref)} />
|
||||
|
||||
<Switch>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import $ from 'jquery';
|
||||
import raf from 'raf';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Label, Icon } from 'Component';
|
||||
import { I, C, S, U, J, keyboard, translate, Storage, Preview, Mark } from 'Lib';
|
||||
|
@ -9,7 +8,6 @@ import Message from './chat/message';
|
|||
import Form from './chat/form';
|
||||
|
||||
interface State {
|
||||
threadId: string;
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
|
@ -23,18 +21,20 @@ const BlockChat = observer(class BlockChat extends React.Component<I.BlockCompon
|
|||
refForm = null;
|
||||
deps: string[] = null;
|
||||
replies: string[] = null;
|
||||
isLoaded = false;
|
||||
isLoading = false;
|
||||
isBottom = true;
|
||||
messageRefs: any = {};
|
||||
timeoutInterface = 0;
|
||||
timeoutScroll = 0;
|
||||
top = 0;
|
||||
state = {
|
||||
threadId: '',
|
||||
isLoading: false,
|
||||
};
|
||||
|
||||
constructor (props: I.BlockComponent) {
|
||||
super(props);
|
||||
|
||||
this.onThread = this.onThread.bind(this);
|
||||
this.onScroll = this.onScroll.bind(this);
|
||||
this.onDragOver = this.onDragOver.bind(this);
|
||||
this.onDragLeave = this.onDragLeave.bind(this);
|
||||
|
@ -42,13 +42,13 @@ const BlockChat = observer(class BlockChat extends React.Component<I.BlockCompon
|
|||
this.onContextMenu = this.onContextMenu.bind(this);
|
||||
this.scrollToMessage = this.scrollToMessage.bind(this);
|
||||
this.scrollToBottom = this.scrollToBottom.bind(this);
|
||||
this.scrollToBottomCheck = this.scrollToBottomCheck.bind(this);
|
||||
this.getMessages = this.getMessages.bind(this);
|
||||
this.getReplyContent = this.getReplyContent.bind(this);
|
||||
};
|
||||
|
||||
render () {
|
||||
const { showRelativeDates } = S.Common;
|
||||
const { threadId } = this.state;
|
||||
const rootId = this.getRootId();
|
||||
const blockId = this.getBlockId();
|
||||
const messages = this.getMessages();
|
||||
|
@ -76,8 +76,7 @@ const BlockChat = observer(class BlockChat extends React.Component<I.BlockCompon
|
|||
rootId={rootId}
|
||||
blockId={blockId}
|
||||
isNew={item.id == lastId}
|
||||
isThread={!!threadId}
|
||||
onThread={this.onThread}
|
||||
scrollToBottom={this.scrollToBottomCheck}
|
||||
onContextMenu={e => this.onContextMenu(e, item)}
|
||||
onMore={e => this.onContextMenu(e, item, true)}
|
||||
onReplyEdit={e => this.onReplyEdit(e, item)}
|
||||
|
@ -133,7 +132,7 @@ const BlockChat = observer(class BlockChat extends React.Component<I.BlockCompon
|
|||
this.rebind();
|
||||
this.setState({ isLoading: true });
|
||||
|
||||
this.loadMessages(true, () => {
|
||||
this.loadMessages(-1, true, () => {
|
||||
this.loadReplies(() => {
|
||||
this.replies = this.getReplies();
|
||||
|
||||
|
@ -205,37 +204,88 @@ const BlockChat = observer(class BlockChat extends React.Component<I.BlockCompon
|
|||
U.Common.getScrollContainer(isPopup).on(`scroll.${ns}`, e => this.onScroll(e));
|
||||
};
|
||||
|
||||
loadMessages (clear: boolean, callBack?: () => void) {
|
||||
subscribeMessages (clear: boolean, callBack?: () => void) {
|
||||
const rootId = this.getRootId();
|
||||
const list = this.getMessages();
|
||||
|
||||
if (!rootId) {
|
||||
C.ChatSubscribeLastMessages(rootId, J.Constant.limit.chat.messages, (message: any) => {
|
||||
if (message.error.code) {
|
||||
if (callBack) {
|
||||
callBack();
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
const messages = message.messages || [];
|
||||
|
||||
if (messages.length && clear) {
|
||||
S.Chat.set(rootId, messages);
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
if (callBack) {
|
||||
callBack();
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
loadMessages (dir: number, clear: boolean, callBack?: () => void) {
|
||||
const rootId = this.getRootId();
|
||||
|
||||
if (!rootId || this.isLoading) {
|
||||
return;
|
||||
};
|
||||
|
||||
if (!clear && (dir > 0) && this.isLoaded) {
|
||||
return;
|
||||
};
|
||||
|
||||
this.isLoading = true;
|
||||
|
||||
if (clear) {
|
||||
C.ChatSubscribeLastMessages(rootId, J.Constant.limit.chat.messages, (message: any) => {
|
||||
if (!message.error.code) {
|
||||
S.Chat.set(rootId, message.messages);
|
||||
this.forceUpdate();
|
||||
};
|
||||
this.subscribeMessages(clear, () => {
|
||||
this.isLoading = false;
|
||||
|
||||
if (callBack) {
|
||||
callBack();
|
||||
};
|
||||
});
|
||||
} else {
|
||||
const list = this.getMessages();
|
||||
if (!list.length) {
|
||||
return;
|
||||
};
|
||||
|
||||
const first = list[0];
|
||||
const before = dir < 0 ? list[0].orderId : '';
|
||||
const after = dir > 0 ? list[list.length - 1].orderId : '';
|
||||
|
||||
C.ChatGetMessages(rootId, first.orderId, J.Constant.limit.chat.messages, (message: any) => {
|
||||
if (!message.error.code && message.messages.length) {
|
||||
S.Chat.prepend(rootId, message.messages);
|
||||
if (!before && !after) {
|
||||
return;
|
||||
};
|
||||
|
||||
this.scrollToMessage(first.id);
|
||||
C.ChatGetMessages(rootId, before, after, J.Constant.limit.chat.messages, (message: any) => {
|
||||
this.isLoading = false;
|
||||
|
||||
if (message.error.code) {
|
||||
this.isLoaded = true;
|
||||
|
||||
if (callBack) {
|
||||
callBack();
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
const messages = message.messages || [];
|
||||
|
||||
if ((dir > 0) && !messages.length) {
|
||||
this.isLoaded = true;
|
||||
this.subscribeMessages(false);
|
||||
};
|
||||
|
||||
if (messages.length) {
|
||||
const scrollTo = dir < 0 ? messages[0].id : messages[messages.length - 1].id;
|
||||
|
||||
S.Chat[(dir < 0 ? 'prepend' : 'append')](rootId, messages);
|
||||
this.scrollToMessage(scrollTo);
|
||||
};
|
||||
|
||||
if (callBack) {
|
||||
|
@ -339,7 +389,7 @@ const BlockChat = observer(class BlockChat extends React.Component<I.BlockCompon
|
|||
};
|
||||
|
||||
getBlockId () {
|
||||
return this.state.threadId || this.props.block.id;
|
||||
return this.props.block.id;
|
||||
};
|
||||
|
||||
getSections () {
|
||||
|
@ -447,13 +497,16 @@ const BlockChat = observer(class BlockChat extends React.Component<I.BlockCompon
|
|||
onScroll (e: any) {
|
||||
const { isPopup } = this.props;
|
||||
const node = $(this.node);
|
||||
const scrollWrapper = node.find('#scrollWrapper');
|
||||
const formWrapper = node.find('#formWrapper');
|
||||
const rootId = this.getRootId();
|
||||
const container = U.Common.getScrollContainer(isPopup);
|
||||
const st = container.scrollTop();
|
||||
const co = isPopup ? container.offset().top : 0;
|
||||
const ch = container.outerHeight();
|
||||
const messages = this.getMessages();
|
||||
const dates = node.find('.section > .date');
|
||||
const fh = formWrapper.outerHeight();
|
||||
const ch = container.outerHeight();
|
||||
const hh = J.Size.header;
|
||||
const lastId = Storage.getChat(rootId).lastId;
|
||||
|
||||
|
@ -475,8 +528,15 @@ const BlockChat = observer(class BlockChat extends React.Component<I.BlockCompon
|
|||
});
|
||||
};
|
||||
|
||||
this.isBottom = false;
|
||||
|
||||
if (st <= 0) {
|
||||
this.loadMessages(false);
|
||||
this.loadMessages(-1, false);
|
||||
};
|
||||
|
||||
if (st - fh >= scrollWrapper.outerHeight() - ch) {
|
||||
this.isBottom = true;
|
||||
//this.loadMessages(1, false);
|
||||
};
|
||||
|
||||
dates.each((i, item: any) => {
|
||||
|
@ -512,28 +572,30 @@ const BlockChat = observer(class BlockChat extends React.Component<I.BlockCompon
|
|||
};
|
||||
|
||||
scrollToMessage (id: string) {
|
||||
window.setTimeout(() => {
|
||||
const container = U.Common.getScrollContainer(this.props.isPopup);
|
||||
const top = this.getMessageScrollOffset(id);
|
||||
if (!id) {
|
||||
return;
|
||||
};
|
||||
|
||||
container.get(0).scrollTo({ top });
|
||||
}, 50);
|
||||
const container = U.Common.getScrollContainer(this.props.isPopup);
|
||||
const top = this.getMessageScrollOffset(id);
|
||||
|
||||
container.get(0).scrollTo({ top });
|
||||
};
|
||||
|
||||
scrollToBottom () {
|
||||
window.setTimeout(() => {
|
||||
const { isPopup } = this.props;
|
||||
const container = U.Common.getScrollContainer(isPopup);
|
||||
const height = isPopup ? container.get(0).scrollHeight : document.body.scrollHeight;
|
||||
const { isPopup } = this.props;
|
||||
const container = U.Common.getScrollContainer(isPopup);
|
||||
const node = $(this.node);
|
||||
const wrapper = node.find('#scrollWrapper');
|
||||
|
||||
container.get(0).scrollTo({ top: height + 10000 });
|
||||
}, 50);
|
||||
container.scrollTop(wrapper.outerHeight());
|
||||
};
|
||||
|
||||
onThread (id: string) {
|
||||
this.setState({ threadId: id }, () => {
|
||||
this.scrollToBottom();
|
||||
});
|
||||
scrollToBottomCheck () {
|
||||
if (this.isBottom) {
|
||||
window.clearTimeout(this.timeoutScroll);
|
||||
this.timeoutScroll = window.setTimeout(() => this.scrollToBottom(), 10);
|
||||
};
|
||||
};
|
||||
|
||||
onReplyEdit (e: React.MouseEvent, message: any) {
|
||||
|
@ -545,18 +607,29 @@ const BlockChat = observer(class BlockChat extends React.Component<I.BlockCompon
|
|||
return;
|
||||
};
|
||||
|
||||
this.isLoaded = false;
|
||||
|
||||
const rootId = this.getRootId();
|
||||
const reply = S.Chat.getReply(rootId, message.replyToMessageId);
|
||||
const limit = Math.ceil(J.Constant.limit.chat.messages / 2);
|
||||
|
||||
let messages = [];
|
||||
|
||||
C.ChatGetMessages(rootId, reply.orderId, limit, (message: any) => {
|
||||
S.Chat.clear(rootId);
|
||||
|
||||
C.ChatGetMessages(rootId, reply.orderId, '', limit, (message: any) => {
|
||||
if (!message.error.code && message.messages.length) {
|
||||
messages = messages.concat(message.messages);
|
||||
};
|
||||
|
||||
S.Chat.set(rootId, messages);
|
||||
C.ChatGetMessages(rootId, '', reply.orderId, limit, (message: any) => {
|
||||
if (!message.error.code && message.messages.length) {
|
||||
messages = messages.concat(message.messages);
|
||||
};
|
||||
|
||||
S.Chat.set(rootId, messages);
|
||||
this.scrollToMessage(reply.id);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1,21 +1,29 @@
|
|||
import * as React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { IconObject, Icon, ObjectName, ObjectDescription, ObjectType, MediaVideo, MediaAudio } from 'Component';
|
||||
import { IconObject, Icon, ObjectName, ObjectDescription, ObjectType, MediaVideo, MediaAudio, Loader } from 'Component';
|
||||
import { I, U, S, J, Action } from 'Lib';
|
||||
|
||||
interface Props {
|
||||
object: any;
|
||||
showAsFile?: boolean;
|
||||
bookmarkAsDefault?: boolean;
|
||||
scrollToBottom?: () => void;
|
||||
onRemove: (id: string) => void;
|
||||
onPreview?: (data: any) => void;
|
||||
};
|
||||
|
||||
const ChatAttachment = observer(class ChatAttachment extends React.Component<Props> {
|
||||
interface State {
|
||||
isLoaded: boolean;
|
||||
};
|
||||
|
||||
const ChatAttachment = observer(class ChatAttachment extends React.Component<Props, State> {
|
||||
|
||||
node = null;
|
||||
src = '';
|
||||
previewItem: any = null;
|
||||
state = {
|
||||
isLoaded: false,
|
||||
};
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
@ -176,13 +184,14 @@ const ChatAttachment = observer(class ChatAttachment extends React.Component<Pro
|
|||
};
|
||||
|
||||
renderImage () {
|
||||
const { object } = this.props;
|
||||
const { object, scrollToBottom } = this.props;
|
||||
const { isLoaded } = this.state;
|
||||
|
||||
this.previewItem = { type: I.FileType.Image, object };
|
||||
|
||||
if (!this.src) {
|
||||
if (object.isTmp && object.file) {
|
||||
U.File.loadPreviewBase64(object.file, { type: 'jpg', quality: 99, maxWidth: J.Size.image }, (image: string, param: any) => {
|
||||
U.File.loadPreviewBase64(object.file, { type: 'jpg', quality: 99, maxWidth: J.Size.image }, (image: string) => {
|
||||
this.src = image;
|
||||
this.previewItem.src = image;
|
||||
$(this.node).find('#image').attr({ 'src': image });
|
||||
|
@ -195,15 +204,23 @@ const ChatAttachment = observer(class ChatAttachment extends React.Component<Pro
|
|||
|
||||
this.previewItem.src = this.src;
|
||||
|
||||
return (
|
||||
if (!isLoaded) {
|
||||
const img = new Image();
|
||||
img.onload = () => this.setState({ isLoaded: true });
|
||||
img.src = this.src;
|
||||
};
|
||||
|
||||
return isLoaded ? (
|
||||
<img
|
||||
id="image"
|
||||
className="image"
|
||||
src={this.src}
|
||||
onClick={this.onPreview}
|
||||
onLoad={scrollToBottom}
|
||||
onDragStart={e => e.preventDefault()}
|
||||
style={{ aspectRatio: `${object.widthInPixels} / ${object.heightInPixels}` }}
|
||||
/>
|
||||
);
|
||||
) : <Loader />;
|
||||
};
|
||||
|
||||
renderVideo () {
|
||||
|
@ -230,6 +247,14 @@ const ChatAttachment = observer(class ChatAttachment extends React.Component<Pro
|
|||
return <MediaAudio playlist={playlist} />;
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps: Readonly<Props>, prevState: Readonly<State>): void {
|
||||
const { scrollToBottom } = this.props;
|
||||
|
||||
if (!prevState.isLoaded && this.state.isLoaded && scrollToBottom) {
|
||||
scrollToBottom();
|
||||
};
|
||||
};
|
||||
|
||||
onOpen () {
|
||||
const { object } = this.props;
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ const ChatMessage = observer(class ChatMessage extends React.Component<I.ChatMes
|
|||
};
|
||||
|
||||
render () {
|
||||
const { rootId, id, isThread, isNew, readonly, onThread, onContextMenu, onMore, onReplyEdit } = this.props;
|
||||
const { rootId, id, isNew, readonly, scrollToBottom, onContextMenu, onMore, onReplyEdit } = this.props;
|
||||
const { space } = S.Common;
|
||||
const { account } = S.Auth;
|
||||
const message = S.Chat.getMessage(rootId, id);
|
||||
|
@ -121,10 +121,13 @@ const ChatMessage = observer(class ChatMessage extends React.Component<I.ChatMes
|
|||
>
|
||||
<div className="flex">
|
||||
<div className="side left">
|
||||
<IconObject object={author} size={40} onClick={e => U.Object.openConfig(author)} />
|
||||
<IconObject
|
||||
object={{ ...author, layout: I.ObjectLayout.Participant }}
|
||||
size={40}
|
||||
onClick={e => U.Object.openConfig(author)}
|
||||
/>
|
||||
</div>
|
||||
<div className="side right">
|
||||
|
||||
<div className="author" onClick={e => U.Object.openConfig(author)}>
|
||||
<ObjectName object={author} />
|
||||
<div className="time">{U.Date.date('H:i', createdAt)}</div>
|
||||
|
@ -151,6 +154,7 @@ const ChatMessage = observer(class ChatMessage extends React.Component<I.ChatMes
|
|||
ref={ref => this.attachmentRefs[item.id] = ref}
|
||||
key={i}
|
||||
object={item}
|
||||
scrollToBottom={scrollToBottom}
|
||||
onRemove={() => this.onAttachmentRemove(item.id)}
|
||||
onPreview={(preview) => this.onPreview(preview)}
|
||||
showAsFile={!attachmentsLayout}
|
||||
|
@ -170,10 +174,6 @@ const ChatMessage = observer(class ChatMessage extends React.Component<I.ChatMes
|
|||
) : ''}
|
||||
</div>
|
||||
) : ''}
|
||||
|
||||
<div className="sub" onClick={() => onThread(id)}>
|
||||
{!isThread ? <div className="item">0 replies</div> : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!readonly ? (
|
||||
|
|
|
@ -174,9 +174,10 @@ const Item = observer(class Item extends React.Component<Props> {
|
|||
};
|
||||
|
||||
onOpenDate () {
|
||||
const { d, m, y } = this.props;
|
||||
const { d, m, y, getView } = this.props;
|
||||
const view = getView();
|
||||
|
||||
U.Object.openDateByTimestamp(U.Date.timestamp(y, m, d, 12, 0, 0), 'config');
|
||||
U.Object.openDateByTimestamp(view.groupRelationKey, U.Date.timestamp(y, m, d, 12, 0, 0), 'config');
|
||||
};
|
||||
|
||||
canCreate (): boolean {
|
||||
|
|
|
@ -278,6 +278,7 @@ const ViewGrid = observer(class ViewGrid extends React.Component<I.ViewComponent
|
|||
});
|
||||
|
||||
node.find('.rowHead').css({ gridTemplateColumns: str });
|
||||
node.find('.rowFoot').css({ gridTemplateColumns: str });
|
||||
node.find('.row .selectionTarget').css({ gridTemplateColumns: str });
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import * as React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Icon } from 'Component';
|
||||
import { I, S, C, U, keyboard, Relation, Dataview, analytics, translate } from 'Lib';
|
||||
import { I, S, C, U, keyboard, Relation, Dataview, analytics, Preview } from 'Lib';
|
||||
|
||||
interface Props extends I.ViewComponent, I.ViewRelation {
|
||||
rootId?: string;
|
||||
|
@ -9,7 +8,6 @@ interface Props extends I.ViewComponent, I.ViewRelation {
|
|||
};
|
||||
|
||||
interface State {
|
||||
isEditing: boolean;
|
||||
result: any;
|
||||
};
|
||||
|
||||
|
@ -19,16 +17,13 @@ const FootCell = observer(class FootCell extends React.Component<Props, State> {
|
|||
menuContext = null;
|
||||
|
||||
state = {
|
||||
isEditing: false,
|
||||
result: null,
|
||||
};
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.onClick = this.onClick.bind(this);
|
||||
this.onOpen = this.onOpen.bind(this);
|
||||
this.onClose = this.onClose.bind(this);
|
||||
this.onOver = this.onOver.bind(this);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onMouseEnter = this.onMouseEnter.bind(this);
|
||||
|
@ -37,24 +32,18 @@ const FootCell = observer(class FootCell extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { relationKey, rootId, block, getView } = this.props;
|
||||
const { isEditing, result } = this.state;
|
||||
const { relationKey, rootId, block } = this.props;
|
||||
const { result } = this.state;
|
||||
const relation = S.Record.getRelationByKey(relationKey);
|
||||
const view = getView();
|
||||
|
||||
if (!relation || !view) {
|
||||
if (!relation) {
|
||||
return <div />;
|
||||
};
|
||||
|
||||
// Subscriptions
|
||||
const viewRelation = view.getRelation(relationKey);
|
||||
if (!viewRelation) {
|
||||
return <div />;
|
||||
};
|
||||
|
||||
const cn = [ 'cellFoot', `cell-key-${relationKey}` ];
|
||||
const option = Relation.formulaByType(relation.format).find(it => it.id == String(viewRelation.formulaType));
|
||||
const name = option.short || option.name;
|
||||
const cn = [ 'cellFoot' ];
|
||||
const formulaType = this.getFormulaType();
|
||||
const option: any = this.getOption() || {};
|
||||
const name = option.short || option.name || '';
|
||||
const subId = S.Record.getSubId(rootId, block.id);
|
||||
const records = S.Record.getRecords(subId, [ relationKey ], true);
|
||||
|
||||
|
@ -67,22 +56,15 @@ const FootCell = observer(class FootCell extends React.Component<Props, State> {
|
|||
ref={ref => this.node = ref}
|
||||
id={Relation.cellId('foot', relationKey, '')}
|
||||
className={cn.join(' ')}
|
||||
onClick={this.onClick}
|
||||
onMouseEnter={this.onMouseEnter}
|
||||
onMouseLeave={this.onMouseLeave}
|
||||
>
|
||||
<div className="cellContent">
|
||||
<div className="cellContent" onClick={this.onSelect}>
|
||||
<div className="flex">
|
||||
{isEditing || (result === null) ? (
|
||||
<div className="select" onClick={this.onSelect}>
|
||||
<div className="name">{viewRelation.formulaType ? name : translate('commonCalculate')}</div>
|
||||
<Icon className="arrow light" />
|
||||
</div>
|
||||
) : ''}
|
||||
{!isEditing && option && (result !== null) ? (
|
||||
{formulaType != I.FormulaType.None ? (
|
||||
<div className="result">
|
||||
<span className="name">{name}</span>
|
||||
{result}
|
||||
<span className="value">{result}</span>
|
||||
</div>
|
||||
) : ''}
|
||||
</div>
|
||||
|
@ -107,6 +89,10 @@ const FootCell = observer(class FootCell extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
const viewRelation = view.getRelation(relationKey);
|
||||
if (!viewRelation) {
|
||||
return;
|
||||
};
|
||||
|
||||
const subId = isInline ? [ rootId, block.id, 'total' ].join('-') : S.Record.getSubId(rootId, block.id);
|
||||
const result = Dataview.getFormulaResult(subId, viewRelation);
|
||||
|
||||
|
@ -116,38 +102,53 @@ const FootCell = observer(class FootCell extends React.Component<Props, State> {
|
|||
};
|
||||
};
|
||||
|
||||
setEditing (v: boolean): void {
|
||||
this.setState({ isEditing: v });
|
||||
};
|
||||
getOption (): any {
|
||||
const { relationKey, getView } = this.props;
|
||||
const view = getView();
|
||||
|
||||
onClick (e: any) {
|
||||
this.setState({ isEditing: true });
|
||||
if (!view) {
|
||||
return null;
|
||||
};
|
||||
|
||||
const formulaType = this.getFormulaType();
|
||||
const relation = S.Record.getRelationByKey(relationKey);
|
||||
|
||||
if (!relation) {
|
||||
return null;
|
||||
};
|
||||
|
||||
return Relation.formulaByType(relationKey, relation.format).find(it => it.id == String(formulaType));
|
||||
};
|
||||
|
||||
onSelect (e: any) {
|
||||
const { relationKey } = this.props;
|
||||
const { relationKey, getView } = this.props;
|
||||
const id = Relation.cellId('foot', relationKey, '');
|
||||
const options = U.Menu.getFormulaSections(relationKey);
|
||||
const formulaType = this.getFormulaType();
|
||||
|
||||
if (formulaType == I.FormulaType.None) {
|
||||
return;
|
||||
};
|
||||
|
||||
S.Menu.closeAll([], () => {
|
||||
S.Menu.open('select', {
|
||||
element: `#${id} .select`,
|
||||
element: `#${id}`,
|
||||
horizontal: I.MenuDirection.Center,
|
||||
onOpen: this.onOpen,
|
||||
onClose: this.onClose,
|
||||
subIds: [ 'select2' ],
|
||||
data: {
|
||||
options,
|
||||
options: U.Menu.prepareForSelect(options),
|
||||
noScroll: true,
|
||||
noVirtualisation: true,
|
||||
onOver: this.onOver,
|
||||
onSelect: (e: any, item: any) => {
|
||||
this.onChange(item.id);
|
||||
this.setEditing(false);
|
||||
},
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Preview.tooltipHide();
|
||||
};
|
||||
|
||||
onOpen (context: any): void {
|
||||
|
@ -161,10 +162,6 @@ const FootCell = observer(class FootCell extends React.Component<Props, State> {
|
|||
analytics.event('ClickGridFormula', { format: relation.format, objectType: object.type });
|
||||
};
|
||||
|
||||
onClose () {
|
||||
$(`.cellKeyHover`).removeClass('cellKeyHover');
|
||||
};
|
||||
|
||||
onOver (e: any, item: any) {
|
||||
if (!this.menuContext) {
|
||||
return;
|
||||
|
@ -177,7 +174,7 @@ const FootCell = observer(class FootCell extends React.Component<Props, State> {
|
|||
|
||||
const { rootId, relationKey } = this.props;
|
||||
const relation = S.Record.getRelationByKey(relationKey);
|
||||
const options = Relation.formulaByType(relation.format).filter(it => it.section == item.id);
|
||||
const options = Relation.formulaByType(relationKey, relation.format).filter(it => it.section == item.id);
|
||||
|
||||
S.Menu.closeAll([ 'select2' ], () => {
|
||||
S.Menu.open('select2', {
|
||||
|
@ -193,7 +190,6 @@ const FootCell = observer(class FootCell extends React.Component<Props, State> {
|
|||
onSelect: (e: any, item: any) => {
|
||||
this.onChange(item.id);
|
||||
this.menuContext.close();
|
||||
this.setEditing(false);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
@ -216,13 +212,49 @@ const FootCell = observer(class FootCell extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
onMouseEnter (): void {
|
||||
if (!keyboard.isDragging) {
|
||||
$(this.node).addClass('hover');
|
||||
if (keyboard.isDragging) {
|
||||
return;
|
||||
};
|
||||
|
||||
const formulaType = this.getFormulaType();
|
||||
|
||||
if (formulaType == I.FormulaType.None) {
|
||||
return;
|
||||
};
|
||||
|
||||
const node = $(this.node);
|
||||
|
||||
node.addClass('hover');
|
||||
|
||||
const { result } = this.state;
|
||||
if ((result === null) || S.Menu.isOpen()) {
|
||||
return;
|
||||
};
|
||||
|
||||
const option: any = this.getOption() || {};
|
||||
const name = option.short || option.name || '';
|
||||
|
||||
const t = Preview.tooltipCaption(name, result);
|
||||
if (t) {
|
||||
Preview.tooltipShow({ text: t, element: node, typeY: I.MenuDirection.Top });
|
||||
};
|
||||
};
|
||||
|
||||
onMouseLeave () {
|
||||
$(this.node).removeClass('hover');
|
||||
Preview.tooltipHide();
|
||||
};
|
||||
|
||||
getFormulaType (): I.FormulaType {
|
||||
const { relationKey, getView } = this.props;
|
||||
const view = getView();
|
||||
|
||||
if (!view) {
|
||||
return I.FormulaType.None;
|
||||
};
|
||||
|
||||
const viewRelation = view.getRelation(relationKey);
|
||||
return viewRelation ? viewRelation.formulaType : I.FormulaType.None;
|
||||
};
|
||||
|
||||
});
|
||||
|
|
|
@ -58,13 +58,15 @@ const HeadCell = observer(class HeadCell extends React.Component<Props> {
|
|||
onMouseEnter (): void {
|
||||
const { block, relationKey } = this.props;
|
||||
|
||||
if (!keyboard.isDragging) {
|
||||
if (!keyboard.isDragging && !keyboard.isResizing) {
|
||||
$(`#block-${block.id} .cell-key-${relationKey}`).addClass('cellKeyHover');
|
||||
};
|
||||
};
|
||||
|
||||
onMouseLeave () {
|
||||
$('.cellKeyHover').removeClass('cellKeyHover');
|
||||
if (!keyboard.isDragging && !keyboard.isResizing) {
|
||||
$('.cellKeyHover').removeClass('cellKeyHover');
|
||||
};
|
||||
};
|
||||
|
||||
onEdit (e: any) {
|
||||
|
|
|
@ -661,7 +661,7 @@ const BlockFeatured = observer(class BlockFeatured extends React.Component<Props
|
|||
};
|
||||
|
||||
if (!this.canEdit(relation)) {
|
||||
U.Object.openDateByTimestamp(value, 'config');
|
||||
U.Object.openDateByTimestamp(relationKey, value, 'config');
|
||||
ret = true;
|
||||
break;
|
||||
};
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
import * as React from 'react';
|
||||
import React, { forwardRef } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { IconObject } from 'Component';
|
||||
import { I, S } from 'Lib';
|
||||
|
||||
const BlockIconPage = observer(class BlockIconPage extends React.Component<I.BlockComponent> {
|
||||
|
||||
render (): any {
|
||||
const { rootId, readonly } = this.props;
|
||||
const BlockIconPage = observer(forwardRef<{}, I.BlockComponent>(({
|
||||
rootId = '',
|
||||
readonly = false,
|
||||
}, ref) => {
|
||||
|
||||
return (
|
||||
<IconObject
|
||||
id={`block-icon-${rootId}`}
|
||||
canEdit={!readonly}
|
||||
getObject={() => S.Detail.get(rootId, rootId, [])}
|
||||
size={96}
|
||||
/>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<IconObject
|
||||
id={`block-icon-${rootId}`}
|
||||
canEdit={!readonly}
|
||||
getObject={() => S.Detail.get(rootId, rootId, [])}
|
||||
size={96}
|
||||
/>
|
||||
);
|
||||
|
||||
});
|
||||
}));
|
||||
|
||||
export default BlockIconPage;
|
|
@ -1,63 +1,50 @@
|
|||
import * as React from 'react';
|
||||
import React, { forwardRef, useState, useImperativeHandle } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { IconObject, Loader } from 'Component';
|
||||
import { I, S, U } from 'Lib';
|
||||
|
||||
interface State {
|
||||
isLoading: boolean;
|
||||
interface BlockIconUserRefProps {
|
||||
setLoading: (v: boolean) => void;
|
||||
};
|
||||
|
||||
const BlockIconUser = observer(class BlockIconUser extends React.Component<I.BlockComponent, State> {
|
||||
const BlockIconUser = observer(forwardRef<BlockIconUserRefProps, I.BlockComponent>(({
|
||||
rootId = '',
|
||||
readonly = false,
|
||||
}, ref) => {
|
||||
|
||||
state = {
|
||||
isLoading: false,
|
||||
const [ isLoading, setIsLoading ] = useState(false);
|
||||
|
||||
const onSelect = () => {
|
||||
setIsLoading(true);
|
||||
U.Object.setIcon(rootId, '', '', () => setIsLoading(false));
|
||||
};
|
||||
|
||||
constructor (props: I.BlockComponent) {
|
||||
super(props);
|
||||
|
||||
this.onSelect = this.onSelect.bind(this);
|
||||
this.onUpload = this.onUpload.bind(this);
|
||||
const onUpload = (objectId: string) => {
|
||||
setIsLoading(true);
|
||||
U.Object.setIcon(rootId, '', objectId, () => setIsLoading(false));
|
||||
};
|
||||
|
||||
render (): any {
|
||||
const { isLoading } = this.state;
|
||||
const { rootId, readonly } = this.props;
|
||||
useImperativeHandle(ref, () => ({
|
||||
setLoading: (v: boolean) => setIsLoading(v),
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="wrap">
|
||||
{isLoading ? <Loader /> : ''}
|
||||
<IconObject
|
||||
id={`block-icon-${rootId}`}
|
||||
getObject={() => S.Detail.get(rootId, rootId, [])}
|
||||
className={readonly ? 'isReadonly' : ''}
|
||||
canEdit={!readonly}
|
||||
onSelect={this.onSelect}
|
||||
onUpload={this.onUpload}
|
||||
size={128}
|
||||
iconSize={128}
|
||||
menuParam={{
|
||||
horizontal: I.MenuDirection.Center,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
onSelect () {
|
||||
this.setLoading(true);
|
||||
U.Object.setIcon(this.props.rootId, '', '', () => this.setLoading(false));
|
||||
};
|
||||
|
||||
onUpload (objectId: string) {
|
||||
this.setLoading(true);
|
||||
U.Object.setIcon(this.props.rootId, '', objectId, () => this.setLoading(false));
|
||||
};
|
||||
|
||||
setLoading (v: boolean) {
|
||||
this.setState({ isLoading: v });
|
||||
};
|
||||
|
||||
});
|
||||
return (
|
||||
<div className="wrap">
|
||||
{isLoading ? <Loader /> : ''}
|
||||
<IconObject
|
||||
id={`block-icon-${rootId}`}
|
||||
getObject={() => S.Detail.get(rootId, rootId, [])}
|
||||
className={readonly ? 'isReadonly' : ''}
|
||||
canEdit={!readonly}
|
||||
onSelect={onSelect}
|
||||
onUpload={onUpload}
|
||||
iconSize={128}
|
||||
menuParam={{
|
||||
horizontal: I.MenuDirection.Center,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}));
|
||||
|
||||
export default BlockIconUser;
|
|
@ -22,7 +22,7 @@ const BlockLink = observer(class BlockLink extends React.Component<I.BlockCompon
|
|||
|
||||
render () {
|
||||
const { rootId, block } = this.props;
|
||||
const object = S.Detail.get(rootId, block.getTargetObjectId(), J.Relation.cover);
|
||||
const object = S.Detail.get(rootId, block.getTargetObjectId(), J.Relation.cover, false, [ I.ObjectLayout.Date ]);
|
||||
const { _empty_, isArchived, isDeleted, done, layout, coverId, coverType, coverX, coverY, coverScale } = object;
|
||||
const content = U.Data.checkLinkSettings(block.content, layout);
|
||||
const readonly = this.props.readonly || !S.Block.isAllowed(object.restrictions, [ I.RestrictionObject.Details ]);
|
||||
|
|
|
@ -305,15 +305,14 @@ const BlockText = observer(class BlockText extends React.Component<Props> {
|
|||
const tag = Mark.getTag(I.MarkType.Latex);
|
||||
const code = Mark.getTag(I.MarkType.Code);
|
||||
const value = this.refEditable.getHtmlValue();
|
||||
const reg = /(^|[^\d<]+)?\$((?:[^$<]|\.)*?)\$([^\d]|$)/gi;
|
||||
const regCode = new RegExp(`^${code}`, 'i');
|
||||
const reg = /(^|[^\d<\$]+)?\$((?:[^$<]|\.)*?)\$([^\d>\$]+|$)/gi;
|
||||
const regCode = new RegExp(`^${code}|${code}$`, 'i');
|
||||
|
||||
if (!/\$((?:[^$<]|\.)*?)\$/.test(value)) {
|
||||
return;
|
||||
};
|
||||
|
||||
const match = value.matchAll(reg);
|
||||
|
||||
const render = (s: string) => {
|
||||
s = U.Common.fromHtmlSpecialChars(s);
|
||||
|
||||
|
@ -340,7 +339,17 @@ const BlockText = observer(class BlockText extends React.Component<Props> {
|
|||
const m3 = String(m[3] || '');
|
||||
|
||||
// Skip inline code marks
|
||||
if (regCode.test(m1)) {
|
||||
if (regCode.test(m1) || regCode.test(m3)) {
|
||||
return;
|
||||
};
|
||||
|
||||
// Skip Brazilian Real
|
||||
if (/R$/.test(m1) || /R$/.test(m2)) {
|
||||
return;
|
||||
};
|
||||
|
||||
// Escaped $ sign
|
||||
if (/\\$/.test(m1) || /\\$/.test(m2)) {
|
||||
return;
|
||||
};
|
||||
|
||||
|
|
|
@ -189,7 +189,7 @@ const Cell = observer(class Cell extends React.Component<Props> {
|
|||
};
|
||||
|
||||
if (relation.format == I.RelationType.Date) {
|
||||
U.Object.openDateByTimestamp(value, 'config');
|
||||
U.Object.openDateByTimestamp(relation.relationKey, value, 'config');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -277,6 +277,7 @@ const Cell = observer(class Cell extends React.Component<Props> {
|
|||
blockId: block.id,
|
||||
value,
|
||||
relation: observable.box(relation),
|
||||
relationKey: relation.relationKey,
|
||||
record,
|
||||
placeholder,
|
||||
canEdit,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React, { FC } from 'react';
|
||||
import { I, U } from 'Lib';
|
||||
|
||||
interface Props {
|
||||
|
@ -20,75 +20,62 @@ interface Props {
|
|||
onContextMenu?(e: any): void;
|
||||
};
|
||||
|
||||
class DropTarget extends React.Component<Props> {
|
||||
const DropTarget: FC<Props> = ({
|
||||
id = '',
|
||||
rootId = '',
|
||||
cacheKey = '',
|
||||
targetContextId = '',
|
||||
dropType = I.DropType.None,
|
||||
type,
|
||||
style = 0,
|
||||
className = '',
|
||||
canDropMiddle = false,
|
||||
isTargetTop = false,
|
||||
isTargetBottom = false,
|
||||
isTargetColumn = false,
|
||||
isReversed = false,
|
||||
children,
|
||||
onClick,
|
||||
onContextMenu,
|
||||
}) => {
|
||||
|
||||
public static defaultProps: Props = {
|
||||
id: '',
|
||||
rootId: '',
|
||||
dropType: I.DropType.None,
|
||||
const key = [ dropType, cacheKey || id ];
|
||||
const cn = [ 'dropTarget', 'isDroppable', `root-${rootId}`, `drop-target-${id}`, className ];
|
||||
|
||||
if (isTargetTop) {
|
||||
cn.push('targetTop');
|
||||
key.push('top');
|
||||
};
|
||||
if (isTargetBottom) {
|
||||
cn.push('targetBot');
|
||||
key.push('bot');
|
||||
};
|
||||
if (isTargetColumn) {
|
||||
cn.push('targetCol');
|
||||
key.push('col');
|
||||
};
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.onClick = this.onClick.bind(this);
|
||||
};
|
||||
|
||||
render () {
|
||||
const {
|
||||
id, rootId, cacheKey, targetContextId, dropType, type, style, children, className, canDropMiddle, isTargetTop, isTargetBottom, isTargetColumn,
|
||||
isReversed, onContextMenu,
|
||||
} = this.props;
|
||||
const key = [ dropType, cacheKey || id ];
|
||||
const cn = [ 'dropTarget', 'isDroppable', 'root-' + rootId, 'drop-target-' + id ];
|
||||
|
||||
if (className) {
|
||||
cn.push(className);
|
||||
};
|
||||
if (isTargetTop) {
|
||||
cn.push('targetTop');
|
||||
key.push('top');
|
||||
};
|
||||
if (isTargetBottom) {
|
||||
cn.push('targetBot');
|
||||
key.push('bot');
|
||||
};
|
||||
if (isTargetColumn) {
|
||||
cn.push('targetCol');
|
||||
key.push('col');
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
key={'drop-target-' + id}
|
||||
className={cn.join(' ')}
|
||||
onClick={this.onClick}
|
||||
onContextMenu={onContextMenu}
|
||||
{...U.Common.dataProps({
|
||||
id,
|
||||
type,
|
||||
style: Number(style) || 0,
|
||||
reversed: isReversed,
|
||||
'root-id': rootId,
|
||||
'cache-key': key.join('-'),
|
||||
'drop-type': dropType,
|
||||
'context-id': targetContextId,
|
||||
'drop-middle': Number(canDropMiddle) || 0,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
onClick (e: any) {
|
||||
const { onClick } = this.props;
|
||||
|
||||
if (onClick) {
|
||||
onClick(e);
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
key={`drop-target-${id}`}
|
||||
className={cn.join(' ')}
|
||||
onClick={onClick}
|
||||
onContextMenu={onContextMenu}
|
||||
{...U.Common.dataProps({
|
||||
id,
|
||||
type,
|
||||
style: Number(style) || 0,
|
||||
reversed: isReversed,
|
||||
'root-id': rootId,
|
||||
'cache-key': key.join('-'),
|
||||
'drop-type': dropType,
|
||||
'context-id': targetContextId,
|
||||
'drop-middle': Number(canDropMiddle) || 0,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DropTarget;
|
|
@ -3,9 +3,8 @@ import $ from 'jquery';
|
|||
import raf from 'raf';
|
||||
import { observer } from 'mobx-react';
|
||||
import { throttle } from 'lodash';
|
||||
import { Icon, Loader, Deleted, DropTarget } from 'Component';
|
||||
import { Icon, Loader, Deleted, DropTarget, EditorControls } from 'Component';
|
||||
import { I, C, S, U, J, Key, Preview, Mark, focus, keyboard, Storage, Action, translate, analytics, Renderer, sidebar } from 'Lib';
|
||||
import Controls from 'Component/page/elements/head/controls';
|
||||
import PageHeadEditor from 'Component/page/elements/head/editor';
|
||||
import Children from 'Component/page/elements/children';
|
||||
|
||||
|
@ -98,7 +97,7 @@ const EditorPage = observer(class EditorPage extends React.Component<Props, Stat
|
|||
id="editorWrapper"
|
||||
className="editorWrapper"
|
||||
>
|
||||
<Controls
|
||||
<EditorControls
|
||||
ref={ref => this.refControls = ref}
|
||||
key="editorControls"
|
||||
{...this.props}
|
||||
|
@ -274,7 +273,7 @@ const EditorPage = observer(class EditorPage extends React.Component<Props, Stat
|
|||
const { isPopup, match } = this.props;
|
||||
|
||||
let close = true;
|
||||
if (isPopup && (match.params.id == this.id)) {
|
||||
if (isPopup && (match?.params?.id == this.id)) {
|
||||
close = false;
|
||||
};
|
||||
if (keyboard.isCloseDisabled) {
|
||||
|
@ -560,6 +559,7 @@ const EditorPage = observer(class EditorPage extends React.Component<Props, Stat
|
|||
Preview.previewHide(true);
|
||||
|
||||
const ids = selection.get(I.SelectType.Block);
|
||||
const idsWithChildren = selection.get(I.SelectType.Block, true);
|
||||
const cmd = keyboard.cmdKey();
|
||||
const readonly = this.isReadonly();
|
||||
const styleParam = this.getStyleParam();
|
||||
|
@ -620,15 +620,7 @@ const EditorPage = observer(class EditorPage extends React.Component<Props, Stat
|
|||
ret = true;
|
||||
});
|
||||
|
||||
if (ids.length) {
|
||||
keyboard.shortcut('escape', e, () => {
|
||||
if (!menuOpen) {
|
||||
selection.clear();
|
||||
};
|
||||
|
||||
ret = true;
|
||||
});
|
||||
|
||||
if (idsWithChildren.length) {
|
||||
// Mark-up
|
||||
|
||||
let type = null;
|
||||
|
@ -653,18 +645,28 @@ const EditorPage = observer(class EditorPage extends React.Component<Props, Stat
|
|||
data: {
|
||||
filter: '',
|
||||
onChange: (newType: I.MarkType, param: string) => {
|
||||
C.BlockTextListSetMark(rootId, ids, { type: newType, param, range: { from: 0, to: 0 } }, () => {
|
||||
analytics.event('ChangeTextStyle', { type: newType, count: ids.length });
|
||||
C.BlockTextListSetMark(rootId, idsWithChildren, { type: newType, param, range: { from: 0, to: 0 } }, () => {
|
||||
analytics.event('ChangeTextStyle', { type: newType, count: idsWithChildren.length });
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
C.BlockTextListSetMark(rootId, ids, { type, param, range: { from: 0, to: 0 } }, () => {
|
||||
analytics.event('ChangeTextStyle', { type, count: ids.length });
|
||||
C.BlockTextListSetMark(rootId, idsWithChildren, { type, param, range: { from: 0, to: 0 } }, () => {
|
||||
analytics.event('ChangeTextStyle', { type, count: idsWithChildren.length });
|
||||
});
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (ids.length) {
|
||||
keyboard.shortcut('escape', e, () => {
|
||||
if (!menuOpen) {
|
||||
selection.clear();
|
||||
};
|
||||
|
||||
ret = true;
|
||||
});
|
||||
|
||||
// Duplicate
|
||||
keyboard.shortcut(`${cmd}+d`, e, () => {
|
||||
|
|
|
@ -38,18 +38,13 @@ const DragVertical = forwardRef<DragVerticalRefProps, Props>(({
|
|||
$(trackRef.current).css({ height: `${Math.round(v * 72)}px` });
|
||||
};
|
||||
|
||||
const setValue = (v: number) => {
|
||||
inputRef.current.setValue(v);
|
||||
setHeight(v);
|
||||
};
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>, value: string) => {
|
||||
const v = 1 - Number(value) || 0;
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
setValue(v);
|
||||
setHeight(v);
|
||||
|
||||
if (onChange) {
|
||||
onChange(e, v);
|
||||
|
@ -57,8 +52,8 @@ const DragVertical = forwardRef<DragVerticalRefProps, Props>(({
|
|||
};
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getValue: () => inputRef?.current.getValue(),
|
||||
setValue,
|
||||
getValue: () => inputRef.current?.getValue(),
|
||||
setValue: (v: number) => inputRef.current?.setValue(v),
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
|
|
293
src/ts/component/form/emailCollection.tsx
Normal file
293
src/ts/component/form/emailCollection.tsx
Normal file
|
@ -0,0 +1,293 @@
|
|||
import React, { FC, useState, useRef, useEffect } from 'react';
|
||||
import { Label, Checkbox, Input, Button, Icon, Pin } from 'Component';
|
||||
import { analytics, C, J, S, translate, U } from 'Lib';
|
||||
|
||||
interface Props {
|
||||
onStepChange: () => void;
|
||||
onComplete: () => void;
|
||||
};
|
||||
|
||||
const EmailCollection: FC<Props> = ({
|
||||
onStepChange,
|
||||
onComplete,
|
||||
}) => {
|
||||
|
||||
const nodeRef = useRef(null);
|
||||
const checkboxTipsRef = useRef(null);
|
||||
const checkboxNewsRef = useRef(null);
|
||||
const emailRef = useRef(null);
|
||||
const buttonRef = useRef(null);
|
||||
const codeRef = useRef(null);
|
||||
const interval = useRef(0);
|
||||
const timeout = useRef(0);
|
||||
const [ step, setStep ] = useState(0);
|
||||
const [ status, setStatus ] = useState('');
|
||||
const [ statusText, setStatusText ] = useState('');
|
||||
const [ countdown, setCountdown ] = useState(0);
|
||||
const [ email, setEmail ] = useState('');
|
||||
const [ subscribeNews, setSubscribeNews ] = useState(false);
|
||||
const [ subscribeTips, setSubscribeTips ] = useState(false);
|
||||
const [ pinDisabled, setPinDisabled ] = useState(false);
|
||||
const [ showCodeSent, setShowCodeSent ] = useState(false);
|
||||
|
||||
let content = null;
|
||||
let descriptionSuffix = 'Description';
|
||||
|
||||
const onCheck = (ref, type: string) => {
|
||||
if (!ref.current) {
|
||||
return;
|
||||
};
|
||||
|
||||
const val = ref.current.getValue();
|
||||
|
||||
ref.current.toggle();
|
||||
|
||||
if (!val) {
|
||||
analytics.event('ClickEmailCollection', { route: 'OnboardingTooltip', step: 1, type });
|
||||
};
|
||||
};
|
||||
|
||||
const setStepHandler = (newStep: number) => {
|
||||
if (step == newStep) {
|
||||
return;
|
||||
};
|
||||
|
||||
setStep(newStep);
|
||||
onStepChange();
|
||||
analytics.event('EmailCollection', { route: 'OnboardingTooltip', step: newStep });
|
||||
};
|
||||
|
||||
const setStatusAndText = (status: string, statusText: string) => {
|
||||
setStatus(status);
|
||||
setStatusText(statusText);
|
||||
};
|
||||
|
||||
const clearStatus = () => {
|
||||
setStatusAndText('', '');
|
||||
};
|
||||
|
||||
const validateEmail = () => {
|
||||
clearStatus();
|
||||
|
||||
window.clearTimeout(timeout.current);
|
||||
timeout.current = window.setTimeout(() => {
|
||||
const value = emailRef.current?.getValue();
|
||||
const isValid = U.Common.checkEmail(value);
|
||||
|
||||
if (value && !isValid) {
|
||||
setStatusAndText('error', translate('errorIncorrectEmail'));
|
||||
};
|
||||
|
||||
buttonRef.current?.setDisabled(!isValid);
|
||||
}, J.Constant.delay.keyboard);
|
||||
};
|
||||
|
||||
const onSubmitEmail = (e: any) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!buttonRef.current || !emailRef.current) {
|
||||
return;
|
||||
};
|
||||
|
||||
if (buttonRef.current.isDisabled()) {
|
||||
return;
|
||||
};
|
||||
|
||||
analytics.event('ClickEmailCollection', { route: 'OnboardingTooltip', step: 1, type: 'SignUp' });
|
||||
|
||||
setEmail(emailRef.current?.getValue());
|
||||
setSubscribeNews(checkboxNewsRef.current?.getValue());
|
||||
setSubscribeTips(checkboxTipsRef.current?.getValue());
|
||||
};
|
||||
|
||||
const verifyEmail = () => {
|
||||
buttonRef.current?.setLoading(true);
|
||||
|
||||
C.MembershipGetVerificationEmail(email, subscribeNews, subscribeTips, true, (message) => {
|
||||
buttonRef.current?.setLoading(false);
|
||||
|
||||
if (message.error.code) {
|
||||
setStatusAndText('error', message.error.description);
|
||||
return;
|
||||
};
|
||||
|
||||
setStepHandler(1);
|
||||
startCountdown(60);
|
||||
});
|
||||
};
|
||||
|
||||
const onConfirmEmailCode = () => {
|
||||
const code = codeRef.current?.getValue();
|
||||
|
||||
setPinDisabled(true);
|
||||
|
||||
C.MembershipVerifyEmailCode(code, (message) => {
|
||||
if (message.error.code) {
|
||||
setStatusAndText('error', message.error.description);
|
||||
setPinDisabled(false);
|
||||
codeRef.current?.reset();
|
||||
return;
|
||||
};
|
||||
|
||||
setStepHandler(2);
|
||||
});
|
||||
};
|
||||
|
||||
const onResend = (e: any) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (countdown) {
|
||||
return;
|
||||
};
|
||||
|
||||
verifyEmail();
|
||||
analytics.event('ClickEmailCollection', { route: 'OnboardingTooltip', step: 2, type: 'Resend' });
|
||||
};
|
||||
|
||||
const startCountdown = (s: number) => {
|
||||
const { emailConfirmationTime } = S.Common;
|
||||
|
||||
if (!emailConfirmationTime) {
|
||||
S.Common.emailConfirmationTimeSet(U.Date.now());
|
||||
};
|
||||
|
||||
setCountdown(s);
|
||||
setShowCodeSent(true);
|
||||
window.setTimeout(() => setShowCodeSent(false), 2000);
|
||||
};
|
||||
|
||||
const tick = () => {
|
||||
window.clearTimeout(interval.current);
|
||||
interval.current = window.setTimeout(() => setCountdown(countdown => countdown - 1), 1000);
|
||||
};
|
||||
|
||||
const clear = () => {
|
||||
window.clearTimeout(timeout.current);
|
||||
timeout.current = window.setTimeout(() => clearStatus(), 4000);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
buttonRef.current?.setDisabled(true);
|
||||
analytics.event('EmailCollection', { route: 'OnboardingTooltip', step: 1 });
|
||||
|
||||
return () => {
|
||||
window.clearTimeout(timeout.current);
|
||||
window.clearTimeout(interval.current);
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (interval.current) {
|
||||
tick();
|
||||
};
|
||||
}, [ showCodeSent ]);
|
||||
|
||||
useEffect(() => {
|
||||
if (status || statusText) {
|
||||
clear();
|
||||
};
|
||||
}, [ status, statusText ]);
|
||||
|
||||
useEffect(() => {
|
||||
if (timeout.current) {
|
||||
clear();
|
||||
};
|
||||
}, [ pinDisabled ]);
|
||||
|
||||
useEffect(() => {
|
||||
if (email) {
|
||||
verifyEmail();
|
||||
};
|
||||
}, [ email, subscribeNews, subscribeTips ]);
|
||||
|
||||
useEffect(() => {
|
||||
if (countdown) {
|
||||
tick();
|
||||
} else {
|
||||
window.clearTimeout(interval.current);
|
||||
S.Common.emailConfirmationTimeSet(0);
|
||||
};
|
||||
}, [ countdown ]);
|
||||
|
||||
switch (step) {
|
||||
case 0: {
|
||||
content = (
|
||||
<div className="step step0">
|
||||
<form onSubmit={onSubmitEmail}>
|
||||
<div className="check" onClick={() => onCheck(checkboxTipsRef, 'Tips')}>
|
||||
<Checkbox ref={checkboxTipsRef} value={false} /> {translate('emailCollectionCheckboxTipsLabel')}
|
||||
</div>
|
||||
<div className="check" onClick={() => onCheck(checkboxNewsRef, 'Updates')}>
|
||||
<Checkbox ref={checkboxNewsRef} value={false} /> {translate('emailCollectionCheckboxNewsLabel')}
|
||||
</div>
|
||||
|
||||
<div className="inputWrapper">
|
||||
<Input ref={emailRef} onKeyUp={validateEmail} placeholder={translate(`emailCollectionEnterEmail`)} />
|
||||
</div>
|
||||
|
||||
{status ? <div className={[ 'statusBar', status ].join(' ')}>{statusText}</div> : ''}
|
||||
|
||||
<div className="buttonWrapper">
|
||||
<Button ref={buttonRef} onClick={onSubmitEmail} className="c36" text={translate('commonSignUp')} />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
};
|
||||
|
||||
case 1: {
|
||||
content = (
|
||||
<div className="step step1">
|
||||
<Pin
|
||||
ref={codeRef}
|
||||
pinLength={4}
|
||||
isVisible={true}
|
||||
onSuccess={onConfirmEmailCode}
|
||||
readonly={pinDisabled}
|
||||
/>
|
||||
|
||||
{status ? <div className={[ 'statusBar', status ].join(' ')}>{statusText}</div> : ''}
|
||||
|
||||
<div onClick={onResend} className={[ 'resend', (countdown ? 'countdown' : '') ].join(' ')}>
|
||||
{showCodeSent ? translate('emailCollectionCodeSent') : translate('popupMembershipResend')}
|
||||
{countdown && !showCodeSent ? U.Common.sprintf(translate('popupMembershipCountdown'), countdown) : ''}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
};
|
||||
|
||||
case 2: {
|
||||
descriptionSuffix = 'News';
|
||||
if (subscribeTips) {
|
||||
descriptionSuffix = 'Tips';
|
||||
};
|
||||
if (subscribeTips && subscribeNews) {
|
||||
descriptionSuffix = 'NewsAndTips';
|
||||
};
|
||||
|
||||
content = (
|
||||
<div className="step step2">
|
||||
<Icon />
|
||||
|
||||
<div className="buttonWrapper">
|
||||
<Button onClick={onComplete} className="c36" text={translate('emailCollectionGreat')} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="emailCollectionForm">
|
||||
<Label className="category" text={translate(`emailCollectionStep${step}Title`)} />
|
||||
<Label className="descr" text={translate(`emailCollectionStep${step}${descriptionSuffix}`)} />
|
||||
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmailCollection;
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React, { forwardRef, useImperativeHandle, useEffect, useState, useRef } from 'react';
|
||||
import $ from 'jquery';
|
||||
import { Input, Icon } from 'Component';
|
||||
import { I, keyboard, translate } from 'Lib';
|
||||
|
@ -27,134 +27,81 @@ interface Props {
|
|||
onIconClick?(e: any): void;
|
||||
};
|
||||
|
||||
interface State {
|
||||
isActive: boolean;
|
||||
interface FilterRefProps {
|
||||
focus(): void;
|
||||
blur(): void;
|
||||
setActive(v: boolean): void;
|
||||
setValue(v: string): void;
|
||||
getValue(): string;
|
||||
getRange(): I.TextRange;
|
||||
setRange(range: I.TextRange): void;
|
||||
};
|
||||
|
||||
class Filter extends React.Component<Props, State> {
|
||||
const Filter = forwardRef<FilterRefProps, Props>(({
|
||||
id = '',
|
||||
className = '',
|
||||
inputClassName = '',
|
||||
icon = '',
|
||||
value = '',
|
||||
placeholder = translate('commonFilterClick'),
|
||||
placeholderFocus = '',
|
||||
tooltip = '',
|
||||
tooltipCaption = '',
|
||||
tooltipX = I.MenuDirection.Center,
|
||||
tooltipY = I.MenuDirection.Bottom,
|
||||
focusOnMount = false,
|
||||
onClick,
|
||||
onFocus,
|
||||
onBlur,
|
||||
onKeyDown,
|
||||
onKeyUp,
|
||||
onChange,
|
||||
onSelect,
|
||||
onClear,
|
||||
onIconClick,
|
||||
}, ref) => {
|
||||
const nodeRef = useRef(null);
|
||||
const inputRef = useRef(null);
|
||||
const placeholderRef = useRef(null);
|
||||
const [ isFocused, setIsFocused ] = useState(false);
|
||||
const [ isActive, setIsActive ] = useState(false);
|
||||
const cn = [ 'filter', className ];
|
||||
|
||||
public static defaultProps = {
|
||||
className: '',
|
||||
inputClassName: '',
|
||||
tooltipY: I.MenuDirection.Bottom,
|
||||
if (isFocused) {
|
||||
cn.push('isFocused');
|
||||
};
|
||||
|
||||
state = {
|
||||
isActive: false,
|
||||
if (isActive) {
|
||||
cn.push('isActive');
|
||||
};
|
||||
|
||||
node: any = null;
|
||||
isFocused = false;
|
||||
placeholder: any = null;
|
||||
ref = null;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.onFocus = this.onFocus.bind(this);
|
||||
this.onBlur = this.onBlur.bind(this);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onClear = this.onClear.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
this.onKeyUp = this.onKeyUp.bind(this);
|
||||
this.onInput = this.onInput.bind(this);
|
||||
};
|
||||
|
||||
render () {
|
||||
const { isActive } = this.state;
|
||||
const { id, value, icon, tooltip, tooltipCaption, tooltipX, tooltipY, placeholder = translate('commonFilterClick'), className, inputClassName, focusOnMount, onKeyDown, onKeyUp, onClick, onIconClick } = this.props;
|
||||
const cn = [ 'filter' ];
|
||||
|
||||
if (className) {
|
||||
cn.push(className);
|
||||
};
|
||||
|
||||
if (isActive) {
|
||||
cn.push('isActive');
|
||||
};
|
||||
|
||||
let iconObj = null;
|
||||
if (icon) {
|
||||
iconObj = (
|
||||
<Icon
|
||||
className={icon}
|
||||
tooltip={tooltip}
|
||||
tooltipCaption={tooltipCaption}
|
||||
tooltipX={tooltipX}
|
||||
tooltipY={tooltipY}
|
||||
onClick={onIconClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={node => this.node = node}
|
||||
id={id}
|
||||
className={cn.join(' ')}
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="inner">
|
||||
{iconObj}
|
||||
|
||||
<div className="filterInputWrap">
|
||||
<Input
|
||||
ref={ref => this.ref = ref}
|
||||
id="input"
|
||||
className={inputClassName}
|
||||
value={value}
|
||||
focusOnMount={focusOnMount}
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onKeyUp={this.onKeyUp}
|
||||
onInput={() => this.placeholderCheck()}
|
||||
onCompositionStart={() => this.placeholderCheck()}
|
||||
onCompositionEnd={() => this.placeholderCheck()}
|
||||
/>
|
||||
<div id="placeholder" className="placeholder">{placeholder}</div>
|
||||
</div>
|
||||
|
||||
<Icon className="clear" onClick={this.onClear} />
|
||||
</div>
|
||||
<div className="line" />
|
||||
</div>
|
||||
let iconObj = null;
|
||||
if (icon) {
|
||||
iconObj = (
|
||||
<Icon
|
||||
className={icon}
|
||||
tooltip={tooltip}
|
||||
tooltipCaption={tooltipCaption}
|
||||
tooltipX={tooltipX}
|
||||
tooltipY={tooltipY}
|
||||
onClick={onIconClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const node = $(this.node);
|
||||
|
||||
this.ref.setValue(this.props.value);
|
||||
this.placeholder = node.find('#placeholder');
|
||||
|
||||
this.checkButton();
|
||||
this.resize();
|
||||
const focus = () => {
|
||||
inputRef.current.focus();
|
||||
};
|
||||
|
||||
componentDidUpdate () {
|
||||
this.checkButton();
|
||||
this.resize();
|
||||
const blur = () => {
|
||||
inputRef.current.blur();
|
||||
};
|
||||
|
||||
focus () {
|
||||
this.ref.focus();
|
||||
this.checkButton();
|
||||
};
|
||||
|
||||
blur () {
|
||||
this.ref.blur();
|
||||
};
|
||||
|
||||
onFocus (e: any) {
|
||||
const { placeholderFocus, onFocus } = this.props;
|
||||
|
||||
this.isFocused = true;
|
||||
this.addFocusedClass();
|
||||
const onFocusHandler = (e: any) => {
|
||||
setIsFocused(true);
|
||||
|
||||
if (placeholderFocus) {
|
||||
this.placeholderSet(placeholderFocus);
|
||||
placeholderSet(placeholderFocus);
|
||||
};
|
||||
|
||||
if (onFocus) {
|
||||
|
@ -162,14 +109,11 @@ class Filter extends React.Component<Props, State> {
|
|||
};
|
||||
};
|
||||
|
||||
onBlur (e: any) {
|
||||
const { placeholderFocus, placeholder, onBlur } = this.props;
|
||||
|
||||
this.isFocused = false;
|
||||
this.removeFocusedClass();
|
||||
const onBlurHandler = (e: any) => {
|
||||
setIsFocused(false);
|
||||
|
||||
if (placeholderFocus) {
|
||||
this.placeholderSet(placeholder);
|
||||
placeholderSet(placeholder);
|
||||
};
|
||||
|
||||
if (onBlur) {
|
||||
|
@ -177,122 +121,158 @@ class Filter extends React.Component<Props, State> {
|
|||
};
|
||||
};
|
||||
|
||||
onInput () {
|
||||
this.placeholderCheck();
|
||||
const onSelectHandler = (e: any) => {
|
||||
if (onSelect) {
|
||||
onSelect(e);
|
||||
};
|
||||
};
|
||||
|
||||
addFocusedClass () {
|
||||
this.addClass('isFocused');
|
||||
};
|
||||
|
||||
removeFocusedClass () {
|
||||
this.removeClass('isFocused');
|
||||
};
|
||||
|
||||
addClass (c: string) {
|
||||
$(this.node).addClass(c);
|
||||
};
|
||||
|
||||
removeClass (c: string) {
|
||||
$(this.node).removeClass(c);
|
||||
};
|
||||
|
||||
setActive (v: boolean) {
|
||||
this.setState({ isActive: v });
|
||||
};
|
||||
|
||||
onClear (e: any) {
|
||||
const onClearHandler = (e: any) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const { onClear } = this.props;
|
||||
|
||||
this.ref.setValue('');
|
||||
this.ref.focus();
|
||||
this.onChange(e, '');
|
||||
|
||||
inputRef.current.setValue('');
|
||||
inputRef.current.focus();
|
||||
|
||||
onChangeHandler(e, '');
|
||||
if (onClear) {
|
||||
onClear();
|
||||
};
|
||||
};
|
||||
|
||||
onChange (e: any, v: string) {
|
||||
const onChangeHandler = (e: any, v: string) => {
|
||||
// Chinese IME is open
|
||||
if (keyboard.isComposition) {
|
||||
return;
|
||||
};
|
||||
|
||||
this.checkButton();
|
||||
resize();
|
||||
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(v);
|
||||
if (onChange) {
|
||||
onChange(v);
|
||||
};
|
||||
};
|
||||
|
||||
onKeyDown (e: any, v: string): void {
|
||||
const onKeyDownHandler = (e: any, v: string): void => {
|
||||
// Chinese IME is open
|
||||
if (keyboard.isComposition) {
|
||||
return;
|
||||
};
|
||||
|
||||
if (this.props.onKeyDown) {
|
||||
this.props.onKeyDown(e, v);
|
||||
if (onKeyDown) {
|
||||
onKeyDown(e, v);
|
||||
};
|
||||
};
|
||||
|
||||
onKeyUp (e: any, v: string): void {
|
||||
const onKeyUpHandler = (e: any, v: string): void => {
|
||||
// Chinese IME is open
|
||||
if (keyboard.isComposition) {
|
||||
return;
|
||||
};
|
||||
|
||||
if (this.props.onKeyUp) {
|
||||
this.props.onKeyUp(e, v);
|
||||
if (onKeyUp) {
|
||||
onKeyUp(e, v);
|
||||
};
|
||||
};
|
||||
|
||||
checkButton () {
|
||||
$(this.node).toggleClass('active', !!this.getValue());
|
||||
this.placeholderCheck();
|
||||
const buttonCheck = () => {
|
||||
$(nodeRef.current).toggleClass('active', Boolean(getValue()));
|
||||
placeholderCheck();
|
||||
};
|
||||
|
||||
setValue (v: string) {
|
||||
this.ref.setValue(v);
|
||||
this.checkButton();
|
||||
const getValue = () => {
|
||||
return inputRef.current?.getValue();
|
||||
};
|
||||
|
||||
getValue () {
|
||||
return this.ref.getValue();
|
||||
const getRange = (): I.TextRange => {
|
||||
return inputRef.current.getRange();
|
||||
};
|
||||
|
||||
getRange () {
|
||||
return this.ref.getRange();
|
||||
const setRange = (range: I.TextRange) => {
|
||||
inputRef.current.setRange(range);
|
||||
};
|
||||
|
||||
setRange (range: I.TextRange) {
|
||||
this.ref.setRange(range);
|
||||
const placeholderCheck = () => {
|
||||
getValue() ? placeholderHide() : placeholderShow();
|
||||
};
|
||||
|
||||
placeholderCheck () {
|
||||
this.getValue() ? this.placeholderHide() : this.placeholderShow();
|
||||
};
|
||||
|
||||
placeholderSet (v: string) {
|
||||
this.placeholder.text(v);
|
||||
const placeholderSet = (v: string) => {
|
||||
$(placeholderRef.current).text(v);
|
||||
};
|
||||
|
||||
placeholderHide () {
|
||||
this.placeholder.hide();
|
||||
const placeholderHide = () => {
|
||||
$(placeholderRef.current).hide();
|
||||
};
|
||||
|
||||
placeholderShow () {
|
||||
this.placeholder.show();
|
||||
const placeholderShow = () => {
|
||||
$(placeholderRef.current).show();
|
||||
};
|
||||
|
||||
resize () {
|
||||
this.placeholder.css({ lineHeight: this.placeholder.height() + 'px' });
|
||||
const resize = () => {
|
||||
const ref = $(placeholderRef.current);
|
||||
ref.css({ lineHeight: ref.height() + 'px' });
|
||||
};
|
||||
|
||||
};
|
||||
const init = () => {
|
||||
buttonCheck();
|
||||
resize();
|
||||
};
|
||||
|
||||
useEffect(() => init(), []);
|
||||
useEffect(() => init(), [ value ]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
focus,
|
||||
blur,
|
||||
setActive: v => setIsActive(v),
|
||||
isFocused: () => isFocused,
|
||||
setValue: (v: string) => inputRef.current.setValue(v),
|
||||
getValue,
|
||||
getRange,
|
||||
setRange,
|
||||
}));
|
||||
|
||||
const val = getValue();
|
||||
|
||||
if (val) {
|
||||
cn.push('active');
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={nodeRef}
|
||||
id={id}
|
||||
className={cn.join(' ')}
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="inner">
|
||||
{iconObj}
|
||||
|
||||
<div className="filterInputWrap">
|
||||
<Input
|
||||
ref={inputRef}
|
||||
id="input"
|
||||
className={inputClassName}
|
||||
value={value}
|
||||
focusOnMount={focusOnMount}
|
||||
onFocus={onFocusHandler}
|
||||
onBlur={onBlurHandler}
|
||||
onChange={onChangeHandler}
|
||||
onKeyDown={onKeyDownHandler}
|
||||
onKeyUp={onKeyUpHandler}
|
||||
onSelect={onSelectHandler}
|
||||
onInput={() => placeholderCheck()}
|
||||
onCompositionStart={() => placeholderCheck()}
|
||||
onCompositionEnd={() => placeholderCheck()}
|
||||
/>
|
||||
<div ref={placeholderRef} className="placeholder">{placeholder}</div>
|
||||
</div>
|
||||
|
||||
<Icon className="clear" onClick={onClearHandler} />
|
||||
</div>
|
||||
<div className="line" />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default Filter;
|
|
@ -1,4 +1,6 @@
|
|||
import React, { FC, useEffect, useRef, useState, forwardRef, useImperativeHandle } from 'react';
|
||||
import React, {
|
||||
useEffect, useRef, useState, forwardRef, useImperativeHandle, ChangeEvent, SyntheticEvent, KeyboardEvent, FormEvent, FocusEvent, ClipboardEvent
|
||||
} from 'react';
|
||||
import $ from 'jquery';
|
||||
import Inputmask from 'inputmask';
|
||||
import { I, keyboard } from 'Lib';
|
||||
|
@ -103,81 +105,33 @@ const Input = forwardRef<InputRef, Props>(({
|
|||
inputRef.current?.focus({ preventScroll: true })
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (maskOptions && inputRef.current) {
|
||||
new Inputmask(maskOptions.mask, maskOptions).mask($(inputRef.current).get(0));
|
||||
};
|
||||
|
||||
if (focusOnMount && inputRef.current) {
|
||||
focus();
|
||||
};
|
||||
|
||||
return () => {
|
||||
if (isFocused.current) {
|
||||
keyboard.setFocus(false);
|
||||
keyboard.disableSelection(false);
|
||||
};
|
||||
};
|
||||
}, [ maskOptions, focusOnMount ]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
focus,
|
||||
blur: () => inputRef.current?.blur(),
|
||||
select: () => inputRef.current?.select(),
|
||||
setValue: (v: string) => setValue(String(v || '')),
|
||||
getValue: () => String(value || ''),
|
||||
setType: (v: string) => setInputType(v),
|
||||
setError: (hasError: boolean) => $(inputRef.current).toggleClass('withError', hasError),
|
||||
getSelectionRect,
|
||||
setPlaceholder: (placeholder: string) => $(inputRef.current).attr({ placeholder }),
|
||||
setRange: (range: I.TextRange) => {
|
||||
callWithTimeout(() => {
|
||||
focus();
|
||||
inputRef.current?.setSelectionRange(range.from, range.to);
|
||||
});
|
||||
},
|
||||
getRange: (): I.TextRange | null => rangeRef.current,
|
||||
}));
|
||||
|
||||
const handleEvent = (
|
||||
handler: ((e: any, value: string) => void) | undefined,
|
||||
e: React.SyntheticEvent<HTMLInputElement>
|
||||
e: SyntheticEvent<HTMLInputElement>
|
||||
) => {
|
||||
let val = null;
|
||||
if (e.currentTarget) {
|
||||
val = e.currentTarget.value;
|
||||
} else
|
||||
if (e.target) {
|
||||
val = String($(e.target).val());
|
||||
};
|
||||
|
||||
if (val === null) {
|
||||
console.log('[Input Event] No value to handle!');
|
||||
return;
|
||||
};
|
||||
handler?.(e, val);
|
||||
handler?.(e, String($(e.target || e.currentTarget).val() || ''));
|
||||
};
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setValue(e.target.value);
|
||||
handleEvent(onChange, e);
|
||||
};
|
||||
|
||||
const handleKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
const handleKeyUp = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if ($(inputRef.current).hasClass('disabled')) return;
|
||||
handleEvent(onKeyUp, e);
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if ($(inputRef.current).hasClass('disabled')) return;
|
||||
handleEvent(onKeyDown, e);
|
||||
};
|
||||
|
||||
const handleInput = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
const handleInput = (e: FormEvent<HTMLInputElement>) => {
|
||||
handleEvent(onInput, e);
|
||||
};
|
||||
|
||||
const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
|
||||
const handleFocus = (e: FocusEvent<HTMLInputElement>) => {
|
||||
isFocused.current = true;
|
||||
addClass('isFocused');
|
||||
keyboard.setFocus(true);
|
||||
|
@ -185,7 +139,7 @@ const Input = forwardRef<InputRef, Props>(({
|
|||
handleEvent(onFocus, e);
|
||||
};
|
||||
|
||||
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
|
||||
const handleBlur = (e: FocusEvent<HTMLInputElement>) => {
|
||||
isFocused.current = false;
|
||||
removeClass('isFocused');
|
||||
keyboard.setFocus(false);
|
||||
|
@ -193,7 +147,7 @@ const Input = forwardRef<InputRef, Props>(({
|
|||
handleEvent(onBlur, e);
|
||||
};
|
||||
|
||||
const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {
|
||||
const handlePaste = (e: ClipboardEvent<HTMLInputElement>) => {
|
||||
e.persist();
|
||||
callWithTimeout(() => {
|
||||
updateRange(e);
|
||||
|
@ -201,7 +155,7 @@ const Input = forwardRef<InputRef, Props>(({
|
|||
});
|
||||
};
|
||||
|
||||
const handleCut = (e: React.ClipboardEvent<HTMLInputElement>) => {
|
||||
const handleCut = (e: ClipboardEvent<HTMLInputElement>) => {
|
||||
e.persist();
|
||||
callWithTimeout(() => {
|
||||
updateRange(e);
|
||||
|
@ -209,7 +163,7 @@ const Input = forwardRef<InputRef, Props>(({
|
|||
});
|
||||
};
|
||||
|
||||
const handleSelect = (e: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
const handleSelect = (e: SyntheticEvent<HTMLInputElement>) => {
|
||||
updateRange(e);
|
||||
handleEvent(onSelect, e);
|
||||
};
|
||||
|
@ -277,6 +231,44 @@ const Input = forwardRef<InputRef, Props>(({
|
|||
return rect;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (maskOptions && inputRef.current) {
|
||||
new Inputmask(maskOptions.mask, maskOptions).mask($(inputRef.current).get(0));
|
||||
};
|
||||
|
||||
if (focusOnMount && inputRef.current) {
|
||||
focus();
|
||||
};
|
||||
|
||||
return () => {
|
||||
if (isFocused.current) {
|
||||
keyboard.setFocus(false);
|
||||
keyboard.disableSelection(false);
|
||||
};
|
||||
};
|
||||
}, [ maskOptions, focusOnMount ]);
|
||||
|
||||
useEffect(() => onChange?.($.Event('change'), value), [ value ]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
focus,
|
||||
blur: () => inputRef.current?.blur(),
|
||||
select: () => inputRef.current?.select(),
|
||||
setValue: (v: string) => setValue(String(v || '')),
|
||||
getValue: () => String(value || ''),
|
||||
setType: (v: string) => setInputType(v),
|
||||
setError: (hasError: boolean) => $(inputRef.current).toggleClass('withError', hasError),
|
||||
getSelectionRect,
|
||||
setPlaceholder: (placeholder: string) => $(inputRef.current).attr({ placeholder }),
|
||||
setRange: (range: I.TextRange) => {
|
||||
callWithTimeout(() => {
|
||||
focus();
|
||||
inputRef.current?.setSelectionRange(range.from, range.to);
|
||||
});
|
||||
},
|
||||
getRange: (): I.TextRange | null => rangeRef.current,
|
||||
}));
|
||||
|
||||
return (
|
||||
<input
|
||||
ref={inputRef}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React, { FC, useRef, useState, useEffect } from 'react';
|
||||
import $ from 'jquery';
|
||||
import raf from 'raf';
|
||||
import { Icon, Input, Button } from 'Component';
|
||||
|
@ -17,229 +17,122 @@ interface Props {
|
|||
onChangeFile? (e: any, path: string): void;
|
||||
};
|
||||
|
||||
interface State {
|
||||
focused: boolean;
|
||||
size: Size;
|
||||
};
|
||||
|
||||
const SMALL_WIDTH = 248;
|
||||
const ICON_WIDTH = 60;
|
||||
|
||||
enum Size { Icon = 0, Small = 1, Full = 2 };
|
||||
|
||||
class InputWithFile extends React.Component<Props, State> {
|
||||
const InputWithFile: FC<Props> = ({
|
||||
icon = '',
|
||||
textUrl = translate('inputWithFileTextUrl'),
|
||||
textFile = '',
|
||||
withFile = true,
|
||||
accept,
|
||||
block,
|
||||
readonly = false,
|
||||
canResize = true,
|
||||
onChangeUrl,
|
||||
onChangeFile,
|
||||
}) => {
|
||||
|
||||
public static defaultProps = {
|
||||
withFile: true,
|
||||
canResize: true,
|
||||
const [ isFocused, setIsFocused ] = useState(false);
|
||||
const [ size, setSize ] = useState(Size.Full);
|
||||
const nodeRef = useRef(null);
|
||||
const urlRef = useRef(null);
|
||||
const timeout = useRef(0);
|
||||
const cn = [ 'inputWithFile', 'resizable' ];
|
||||
const or = ` ${translate('commonOr')} `;
|
||||
const isSmall = size == Size.Small;
|
||||
const isIcon = size == Size.Icon;
|
||||
|
||||
let placeholder = textUrl;
|
||||
let onClick = null;
|
||||
|
||||
if (!withFile) {
|
||||
cn.push('noFile');
|
||||
};
|
||||
|
||||
if (isSmall) {
|
||||
cn.push('isSmall');
|
||||
};
|
||||
|
||||
_isMounted = false;
|
||||
node: any = null;
|
||||
state = {
|
||||
focused: false,
|
||||
size: Size.Full,
|
||||
if (readonly) {
|
||||
cn.push('isReadonly');
|
||||
};
|
||||
|
||||
if (isIcon) {
|
||||
cn.push('isIcon');
|
||||
onClick = e => onClickFile(e);
|
||||
};
|
||||
|
||||
if (isFocused) {
|
||||
cn.push('isFocused');
|
||||
};
|
||||
|
||||
if (withFile && isFocused) {
|
||||
placeholder += or + (!isSmall ? textFile : '');
|
||||
};
|
||||
t = 0;
|
||||
refUrl: any = null;
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.onSubmit = this.onSubmit.bind(this);
|
||||
this.onFocus = this.onFocus.bind(this);
|
||||
this.onBlur = this.onBlur.bind(this);
|
||||
this.onClickFile = this.onClickFile.bind(this);
|
||||
};
|
||||
|
||||
render () {
|
||||
const { focused, size } = this.state;
|
||||
const { icon, textUrl = translate('inputWithFileTextUrl'), textFile, withFile, readonly } = this.props;
|
||||
const cn = [ 'inputWithFile', 'resizable' ];
|
||||
const or = ` ${translate('commonOr')} `;
|
||||
const onBlur = focused ? this.onBlur : null;
|
||||
const onFocus = !focused ? this.onFocus : null;
|
||||
const isSmall = size == Size.Small;
|
||||
const isIcon = size == Size.Icon;
|
||||
|
||||
let placeholder = textUrl;
|
||||
let onClick = null;
|
||||
|
||||
if (!withFile) {
|
||||
cn.push('noFile');
|
||||
};
|
||||
|
||||
if (isSmall) {
|
||||
cn.push('isSmall');
|
||||
};
|
||||
|
||||
if (readonly) {
|
||||
cn.push('isReadonly');
|
||||
};
|
||||
|
||||
if (isIcon) {
|
||||
cn.push('isIcon');
|
||||
onClick = e => this.onClickFile(e);
|
||||
};
|
||||
|
||||
if (focused) {
|
||||
cn.push('isFocused');
|
||||
};
|
||||
|
||||
if (withFile && focused) {
|
||||
placeholder += or + (!isSmall ? textFile : '');
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={node => this.node = node}
|
||||
className={cn.join(' ')}
|
||||
onClick={onClick}
|
||||
>
|
||||
{icon ? <Icon className={icon} /> : ''}
|
||||
|
||||
<div id="text" className="txt">
|
||||
<form id="form" onSubmit={this.onSubmit}>
|
||||
{focused ? (
|
||||
<React.Fragment>
|
||||
<Input
|
||||
id="url"
|
||||
ref={ref => this.refUrl = ref}
|
||||
placeholder={placeholder}
|
||||
onPaste={e => this.onChangeUrl(e, true)}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
<Button type="input" className="dn" />
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<span className="urlToggle" onClick={this.onFocus}>{textUrl + (withFile && isSmall ? or : '')}</span>
|
||||
)}
|
||||
</form>
|
||||
|
||||
{withFile ? (
|
||||
<span className="fileWrap" onMouseDown={this.onClickFile}>
|
||||
{!isSmall ? <span> {translate('commonOr')} </span> : ''}
|
||||
<span className="border">{textFile}</span>
|
||||
</span>
|
||||
) : ''}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
this._isMounted = true;
|
||||
this.resize();
|
||||
this.rebind();
|
||||
};
|
||||
|
||||
componentDidUpdate () {
|
||||
const { focused } = this.state;
|
||||
const { block } = this.props;
|
||||
|
||||
this.resize();
|
||||
this.rebind();
|
||||
|
||||
if (focused) {
|
||||
if (this.refUrl) {
|
||||
this.refUrl.focus();
|
||||
};
|
||||
focus.set(block.id, { from: 0, to: 0 });
|
||||
const rebind = () => {
|
||||
if (canResize) {
|
||||
$(nodeRef.current).off('resizeMove').on('resizeMove', () => resize());
|
||||
};
|
||||
};
|
||||
|
||||
componentWillUnmount () {
|
||||
const { focused } = focus.state;
|
||||
const { block } = this.props;
|
||||
|
||||
this._isMounted = false;
|
||||
this.unbind();
|
||||
|
||||
if (focused == block.id) {
|
||||
keyboard.setFocus(false);
|
||||
const unbind = () => {
|
||||
if (canResize) {
|
||||
$(nodeRef.current).off('resizeMove');
|
||||
};
|
||||
};
|
||||
|
||||
rebind () {
|
||||
const { canResize } = this.props;
|
||||
if (!this._isMounted || !canResize) {
|
||||
return;
|
||||
};
|
||||
|
||||
$(this.node).off('resizeMove').on('resizeMove', (e: any) => this.resize());
|
||||
};
|
||||
|
||||
unbind () {
|
||||
const { canResize } = this.props;
|
||||
if (!this._isMounted || !canResize) {
|
||||
return;
|
||||
};
|
||||
|
||||
$(this.node).off('resizeMove');
|
||||
};
|
||||
|
||||
resize () {
|
||||
const { canResize } = this.props;
|
||||
const resize = () => {
|
||||
if (!canResize) {
|
||||
return;
|
||||
};
|
||||
|
||||
raf(() => {
|
||||
if (!this._isMounted) {
|
||||
return;
|
||||
};
|
||||
|
||||
const node = $(this.node);
|
||||
const node = $(nodeRef.current);
|
||||
const rect = (node.get(0) as HTMLInputElement).getBoundingClientRect();
|
||||
|
||||
let size = Size.Full;
|
||||
|
||||
let s = Size.Full;
|
||||
if (rect.width <= SMALL_WIDTH) {
|
||||
size = Size.Small;
|
||||
s = Size.Small;
|
||||
};
|
||||
if (rect.width <= ICON_WIDTH) {
|
||||
size = Size.Icon;
|
||||
s = Size.Icon;
|
||||
};
|
||||
|
||||
if (size != this.state.size) {
|
||||
this.setState({ size });
|
||||
|
||||
if (s != size) {
|
||||
setSize(s);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
onFocus (e: any) {
|
||||
const onFocusHandler = (e: any) => {
|
||||
e.stopPropagation();
|
||||
|
||||
const { readonly } = this.props;
|
||||
if (readonly) {
|
||||
return;
|
||||
if (!readonly) {
|
||||
setIsFocused(true);
|
||||
};
|
||||
this.setState({ focused: true });
|
||||
};
|
||||
|
||||
onBlur (e: any) {
|
||||
const onBlurHandler = (e: any) => {
|
||||
e.stopPropagation();
|
||||
this.setState({ focused: false });
|
||||
setIsFocused(false);
|
||||
};
|
||||
|
||||
focus () {
|
||||
this.setState({ focused: true });
|
||||
};
|
||||
|
||||
onChangeUrl (e: any, force: boolean) {
|
||||
const { onChangeUrl, readonly } = this.props;
|
||||
|
||||
const onChangeUrlHandler = (e: any, force: boolean) => {
|
||||
if (readonly) {
|
||||
return;
|
||||
};
|
||||
|
||||
window.clearTimeout(this.t);
|
||||
this.t = window.setTimeout(() => {
|
||||
if (!this.refUrl) {
|
||||
window.clearTimeout(timeout.current);
|
||||
timeout.current = window.setTimeout(() => {
|
||||
if (!urlRef.current) {
|
||||
return;
|
||||
};
|
||||
|
||||
const url = this.refUrl.getValue() || '';
|
||||
const url = String(urlRef.current.getValue() || '');
|
||||
if (!url) {
|
||||
return;
|
||||
};
|
||||
|
@ -250,9 +143,7 @@ class InputWithFile extends React.Component<Props, State> {
|
|||
}, force ? 50 : J.Constant.delay.keyboard);
|
||||
};
|
||||
|
||||
onClickFile (e: any) {
|
||||
const { onChangeFile, accept, readonly } = this.props;
|
||||
|
||||
const onClickFile = (e: any) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
|
@ -267,11 +158,77 @@ class InputWithFile extends React.Component<Props, State> {
|
|||
});
|
||||
};
|
||||
|
||||
onSubmit (e: any) {
|
||||
const onSubmit = (e: any) => {
|
||||
e.preventDefault();
|
||||
this.onChangeUrl(e, true);
|
||||
onChangeUrlHandler(e, true);
|
||||
};
|
||||
|
||||
const onBlur = isFocused ? onBlurHandler : null;
|
||||
const onFocus = !isFocused ? onFocusHandler : null;
|
||||
|
||||
useEffect(() => {
|
||||
resize();
|
||||
rebind();
|
||||
|
||||
return () => {
|
||||
const { focused } = focus.state;
|
||||
|
||||
unbind();
|
||||
|
||||
if (focused == block.id) {
|
||||
keyboard.setFocus(false);
|
||||
};
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
resize();
|
||||
rebind();
|
||||
|
||||
if (isFocused) {
|
||||
keyboard.setFocus(true);
|
||||
urlRef.current?.focus();
|
||||
focus.set(block.id, { from: 0, to: 0 });
|
||||
};
|
||||
|
||||
}, [ isFocused, size ]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={nodeRef}
|
||||
className={cn.join(' ')}
|
||||
onClick={onClick}
|
||||
>
|
||||
{icon ? <Icon className={icon} /> : ''}
|
||||
|
||||
<div className="inputWithFile-inner">
|
||||
<form className="form" onSubmit={onSubmit}>
|
||||
{isFocused ? (
|
||||
<>
|
||||
<Input
|
||||
ref={urlRef}
|
||||
placeholder={placeholder}
|
||||
onPaste={e => onChangeUrlHandler(e, true)}
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
<Button type="input" className="dn" />
|
||||
</>
|
||||
) : (
|
||||
<span className="urlToggle" onClick={onFocusHandler}>{textUrl + (withFile && isSmall ? or : '')}</span>
|
||||
)}
|
||||
</form>
|
||||
|
||||
{withFile ? (
|
||||
<span className="fileWrap" onMouseDown={onClickFile}>
|
||||
{!isSmall ? <span> {translate('commonOr')} </span> : ''}
|
||||
<span className="border">{textFile}</span>
|
||||
</span>
|
||||
) : ''}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InputWithFile;
|
|
@ -1,7 +1,6 @@
|
|||
import * as React from 'react';
|
||||
import $ from 'jquery';
|
||||
import { Input, Icon, Label } from 'Component';
|
||||
import { I, translate } from 'Lib';
|
||||
import React, { forwardRef, useRef, useEffect, useState, useImperativeHandle } from 'react';
|
||||
import { Input, Label } from 'Component';
|
||||
import { I } from 'Lib';
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
|
@ -16,76 +15,56 @@ interface Props {
|
|||
onMouseLeave?(e: any): void;
|
||||
};
|
||||
|
||||
class InputWithLabel extends React.Component<Props> {
|
||||
|
||||
node: any = null;
|
||||
isFocused = false;
|
||||
placeholder: any = null;
|
||||
ref = null;
|
||||
interface InputWithLabelRefProps {
|
||||
focus(): void;
|
||||
blur(): void;
|
||||
setValue(v: string): void;
|
||||
getValue(): string;
|
||||
setRange(range: I.TextRange): void;
|
||||
isFocused(): boolean;
|
||||
};
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
const InputWithLabel = forwardRef<InputWithLabelRefProps, Props>((props, ref) => {
|
||||
const {
|
||||
label = '',
|
||||
value = '',
|
||||
readonly = false,
|
||||
onFocus,
|
||||
onBlur,
|
||||
} = props;
|
||||
|
||||
this.onFocus = this.onFocus.bind(this);
|
||||
this.onBlur = this.onBlur.bind(this);
|
||||
};
|
||||
|
||||
render () {
|
||||
const { label } = this.props;
|
||||
const nodeRef = useRef(null);
|
||||
const inputRef = useRef(null);
|
||||
const [ isFocused, setIsFocused ] = useState(false);
|
||||
const cn = [ 'inputWithLabel' ];
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={node => this.node = node}
|
||||
onClick={() => this.ref.focus()}
|
||||
className="inputWithLabel"
|
||||
>
|
||||
<div className="inner">
|
||||
<Label text={label} />
|
||||
|
||||
<Input
|
||||
ref={ref => this.ref = ref}
|
||||
{...this.props}
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
if (isFocused) {
|
||||
cn.push('isFocused');
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const node = $(this.node);
|
||||
|
||||
this.ref.setValue(this.props.value);
|
||||
const focus = () => {
|
||||
inputRef.current?.focus();
|
||||
};
|
||||
|
||||
focus () {
|
||||
this.ref.focus();
|
||||
const blur = () => {
|
||||
inputRef.current?.blur();
|
||||
};
|
||||
|
||||
blur () {
|
||||
this.ref.blur();
|
||||
const setValue = (v: string) => {
|
||||
inputRef.current?.setValue(v);
|
||||
};
|
||||
|
||||
setValue (v: string) {
|
||||
this.ref.setValue(v);
|
||||
const getValue = () => {
|
||||
return inputRef.current?.getValue();
|
||||
};
|
||||
|
||||
getValue () {
|
||||
return this.ref.getValue();
|
||||
const setRange = (range: I.TextRange) => {
|
||||
inputRef.current?.setRange(range);
|
||||
};
|
||||
|
||||
setRange (range: I.TextRange) {
|
||||
this.ref.setRange(range);
|
||||
};
|
||||
|
||||
onFocus (e: any) {
|
||||
const { onFocus, readonly } = this.props;
|
||||
|
||||
const onFocusHandler = (e: any) => {
|
||||
if (!readonly) {
|
||||
this.isFocused = true;
|
||||
this.addFocusedClass();
|
||||
setIsFocused(true);
|
||||
};
|
||||
|
||||
if (onFocus) {
|
||||
|
@ -93,12 +72,9 @@ class InputWithLabel extends React.Component<Props> {
|
|||
};
|
||||
};
|
||||
|
||||
onBlur (e: any) {
|
||||
const { onBlur, readonly } = this.props;
|
||||
|
||||
const onBlurHandler = (e: any) => {
|
||||
if (!readonly) {
|
||||
this.isFocused = false;
|
||||
this.removeFocusedClass();
|
||||
setIsFocused(false);
|
||||
};
|
||||
|
||||
if (onBlur) {
|
||||
|
@ -106,16 +82,37 @@ class InputWithLabel extends React.Component<Props> {
|
|||
};
|
||||
};
|
||||
|
||||
addFocusedClass () {
|
||||
const node = $(this.node);
|
||||
node.addClass('isFocused');
|
||||
};
|
||||
useEffect(() => inputRef.current.setValue(value), []);
|
||||
|
||||
removeFocusedClass () {
|
||||
const node = $(this.node);
|
||||
node.removeClass('isFocused');
|
||||
};
|
||||
useImperativeHandle(ref, () => ({
|
||||
focus,
|
||||
blur,
|
||||
setValue,
|
||||
getValue,
|
||||
setRange,
|
||||
isFocused: () => isFocused,
|
||||
}));
|
||||
|
||||
};
|
||||
return (
|
||||
<div
|
||||
ref={nodeRef}
|
||||
onClick={focus}
|
||||
className={cn.join(' ')}
|
||||
>
|
||||
<div className="inner">
|
||||
<Label text={label} />
|
||||
|
||||
export default InputWithLabel;
|
||||
<Input
|
||||
ref={inputRef}
|
||||
{...props}
|
||||
onFocus={onFocusHandler}
|
||||
onBlur={onBlurHandler}
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
export default InputWithLabel;
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React, { forwardRef, useState, useEffect, useImperativeHandle, MouseEvent } from 'react';
|
||||
import $ from 'jquery';
|
||||
import { I, S, U, Relation } from 'Lib';
|
||||
import { Icon, MenuItemVertical } from 'Component';
|
||||
|
@ -11,7 +11,7 @@ interface Props {
|
|||
element?: string;
|
||||
value: any;
|
||||
options: I.Option[];
|
||||
noFilter: boolean;
|
||||
noFilter?: boolean;
|
||||
isMultiple?: boolean;
|
||||
showOn?: string;
|
||||
readonly?: boolean;
|
||||
|
@ -19,159 +19,82 @@ interface Props {
|
|||
onChange? (id: any): void;
|
||||
};
|
||||
|
||||
interface State {
|
||||
value: string[];
|
||||
options: I.Option[];
|
||||
interface SelectRefProps {
|
||||
getValue: () => any;
|
||||
setValue: (v: any) => void;
|
||||
setOptions: (options: I.Option[]) => void;
|
||||
};
|
||||
|
||||
class Select extends React.Component<Props, State> {
|
||||
|
||||
public static defaultProps = {
|
||||
initial: '',
|
||||
noFilter: true,
|
||||
showOn: 'click',
|
||||
};
|
||||
|
||||
_isMounted = false;
|
||||
state = {
|
||||
value: [],
|
||||
options: [] as I.Option[]
|
||||
};
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.show = this.show.bind(this);
|
||||
this.hide = this.hide.bind(this);
|
||||
};
|
||||
|
||||
render () {
|
||||
const { id, className, arrowClassName, readonly, showOn } = this.props;
|
||||
const { options } = this.state;
|
||||
const cn = [ 'select' ];
|
||||
const acn = [ 'arrow', (arrowClassName ? arrowClassName : '') ];
|
||||
const value = Relation.getArrayValue(this.state.value);
|
||||
const current: any[] = [];
|
||||
const Select = forwardRef<SelectRefProps, Props>(({
|
||||
id = '',
|
||||
initial = '',
|
||||
className = '',
|
||||
arrowClassName = '',
|
||||
element = '',
|
||||
value: initialValue = [],
|
||||
options: initialOptions = [],
|
||||
noFilter = true,
|
||||
isMultiple = false,
|
||||
showOn = 'click',
|
||||
readonly = false,
|
||||
menuParam = {},
|
||||
onChange,
|
||||
}, ref) => {
|
||||
const [ value, setValue ] = useState(initialValue);
|
||||
const [ options, setOptions ] = useState(initialOptions);
|
||||
const cn = [ 'select', className ];
|
||||
const acn = [ 'arrow', arrowClassName ];
|
||||
const current: any[] = [];
|
||||
|
||||
if (className) {
|
||||
cn.push(className);
|
||||
};
|
||||
|
||||
if (readonly) {
|
||||
cn.push('isReadonly');
|
||||
};
|
||||
|
||||
value.forEach((id: string) => {
|
||||
const option = options.find(item => item.id == id);
|
||||
if (option) {
|
||||
current.push(option);
|
||||
};
|
||||
});
|
||||
|
||||
if (!current.length && options.length) {
|
||||
current.push(options[0]);
|
||||
};
|
||||
|
||||
let onClick = null;
|
||||
let onMouseDown = null;
|
||||
let onMouseEnter = null;
|
||||
|
||||
if (showOn == 'mouseDown') {
|
||||
onMouseDown = this.show;
|
||||
};
|
||||
|
||||
if (showOn == 'click') {
|
||||
onClick = this.show;
|
||||
};
|
||||
|
||||
if (showOn == 'mouseEnter') {
|
||||
onMouseEnter = this.show;
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
id={`select-${id}`}
|
||||
className={cn.join(' ')}
|
||||
onClick={onClick}
|
||||
onMouseDown={onMouseDown}
|
||||
onMouseEnter={onMouseEnter}
|
||||
>
|
||||
{current ? (
|
||||
<React.Fragment>
|
||||
{current.map((item: any, i: number) => (
|
||||
<MenuItemVertical key={i} {...item} />
|
||||
))}
|
||||
<Icon className={acn.join(' ')} />
|
||||
</React.Fragment>
|
||||
) : ''}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
this._isMounted = true;
|
||||
|
||||
const options = this.getOptions();
|
||||
|
||||
let value = Relation.getArrayValue(this.props.value);
|
||||
if (!value.length && options.length) {
|
||||
value = [ options[0].id ];
|
||||
};
|
||||
|
||||
this.setState({ value, options });
|
||||
if (className) {
|
||||
cn.push(className);
|
||||
};
|
||||
|
||||
componentWillUnmount () {
|
||||
this._isMounted = false;
|
||||
if (readonly) {
|
||||
cn.push('isReadonly');
|
||||
};
|
||||
|
||||
getOptions () {
|
||||
const { initial } = this.props;
|
||||
const options = [];
|
||||
let val = Relation.getArrayValue(value);
|
||||
val.forEach((id: string) => {
|
||||
const option = options.find(item => item.id == id);
|
||||
if (option) {
|
||||
current.push(option);
|
||||
};
|
||||
});
|
||||
|
||||
if (!current.length && options.length) {
|
||||
current.push(options[0]);
|
||||
};
|
||||
|
||||
const getOptions = () => {
|
||||
const ret = [];
|
||||
|
||||
if (initial) {
|
||||
options.push({ id: '', name: initial, isInitial: true });
|
||||
ret.push({ id: '', name: initial, isInitial: true });
|
||||
};
|
||||
for (const option of this.props.options) {
|
||||
options.push(option);
|
||||
for (const option of initialOptions) {
|
||||
ret.push(option);
|
||||
};
|
||||
|
||||
return options;
|
||||
return ret;
|
||||
};
|
||||
|
||||
setOptions (options: any[]) {
|
||||
this.setState({ options });
|
||||
const getValue = (val: any): any => {
|
||||
return isMultiple ? val : (val.length ? val[0] : '');
|
||||
};
|
||||
|
||||
getValue (): any {
|
||||
const { isMultiple } = this.props;
|
||||
const value = Relation.getArrayValue(this.state.value);
|
||||
|
||||
return isMultiple ? value : value[0];
|
||||
const setValueHandler = (v: any) => {
|
||||
setValue(Relation.getArrayValue(v));
|
||||
};
|
||||
|
||||
setValue (v: any) {
|
||||
const value = Relation.getArrayValue(v);
|
||||
|
||||
if (this._isMounted) {
|
||||
this.state.value = value;
|
||||
this.setState({ value });
|
||||
};
|
||||
};
|
||||
|
||||
show (e: React.MouseEvent) {
|
||||
const show = (e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
|
||||
const { id, onChange, noFilter, isMultiple, readonly } = this.props;
|
||||
const { value, options } = this.state;
|
||||
const elementId = `#select-${id}`;
|
||||
const element = this.props.element || elementId;
|
||||
|
||||
if (readonly) {
|
||||
return;
|
||||
};
|
||||
|
||||
const mp = this.props.menuParam || {};
|
||||
const el = element || `#select-${id}`;
|
||||
const mp = menuParam || {};
|
||||
|
||||
let onOpen = null;
|
||||
let onClose = null;
|
||||
|
@ -185,18 +108,18 @@ class Select extends React.Component<Props, State> {
|
|||
delete(mp.onClose);
|
||||
};
|
||||
|
||||
const menuParam = Object.assign({
|
||||
element,
|
||||
const param = Object.assign({
|
||||
element: el,
|
||||
noFlipX: true,
|
||||
onOpen: (context: any) => {
|
||||
window.setTimeout(() => $(element).addClass('isFocused'));
|
||||
window.setTimeout(() => $(el).addClass('isFocused'));
|
||||
|
||||
if (onOpen) {
|
||||
onOpen(context);
|
||||
};
|
||||
},
|
||||
onClose: () => {
|
||||
window.setTimeout(() => $(element).removeClass('isFocused'));
|
||||
window.setTimeout(() => $(el).removeClass('isFocused'));
|
||||
|
||||
if (onClose) {
|
||||
onClose();
|
||||
|
@ -204,47 +127,97 @@ class Select extends React.Component<Props, State> {
|
|||
},
|
||||
}, mp);
|
||||
|
||||
menuParam.data = Object.assign({
|
||||
param.data = Object.assign({
|
||||
noFilter,
|
||||
noClose: true,
|
||||
value,
|
||||
options: U.Menu.prepareForSelect(options),
|
||||
onSelect: (e: any, item: any) => {
|
||||
let { value } = this.state;
|
||||
|
||||
if (item.id !== '') {
|
||||
if (isMultiple) {
|
||||
value = value.includes(item.id) ? value.filter(it => it != item.id) : [ ...value, item.id ];
|
||||
val = val.includes(item.id) ? val.filter(it => it != item.id) : [ ...val, item.id ];
|
||||
} else {
|
||||
value = [ item.id ];
|
||||
val = [ item.id ];
|
||||
};
|
||||
} else {
|
||||
value = [];
|
||||
val = [];
|
||||
};
|
||||
|
||||
this.setValue(value);
|
||||
setValueHandler(val);
|
||||
|
||||
if (onChange) {
|
||||
onChange(this.getValue());
|
||||
onChange(getValue(val));
|
||||
};
|
||||
|
||||
if (!isMultiple) {
|
||||
this.hide();
|
||||
hide();
|
||||
} else {
|
||||
S.Menu.updateData('select', { value });
|
||||
S.Menu.updateData('select', { value: val });
|
||||
};
|
||||
},
|
||||
}, mp.data || {});
|
||||
|
||||
S.Menu.closeAll([ 'select' ], () => {
|
||||
S.Menu.open('select', menuParam);
|
||||
S.Menu.open('select', param);
|
||||
});
|
||||
};
|
||||
|
||||
hide () {
|
||||
const hide = () => {
|
||||
S.Menu.close('select');
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
let onClick = null;
|
||||
let onMouseDown = null;
|
||||
let onMouseEnter = null;
|
||||
|
||||
if (showOn == 'mouseDown') {
|
||||
onMouseDown = show;
|
||||
};
|
||||
|
||||
if (showOn == 'click') {
|
||||
onClick = show;
|
||||
};
|
||||
|
||||
if (showOn == 'mouseEnter') {
|
||||
onMouseEnter = show;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const options = getOptions();
|
||||
|
||||
let val = Relation.getArrayValue(initialValue);
|
||||
if (!val.length && options.length) {
|
||||
val = [ options[0].id ];
|
||||
};
|
||||
|
||||
setValue(val);
|
||||
setOptions(options);
|
||||
}, []);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getValue: () => getValue(val),
|
||||
setValue: setValueHandler,
|
||||
setOptions: (options: I.Option[]) => setOptions(options),
|
||||
}));
|
||||
|
||||
return (
|
||||
<div
|
||||
id={`select-${id}`}
|
||||
className={cn.join(' ')}
|
||||
onClick={onClick}
|
||||
onMouseDown={onMouseDown}
|
||||
onMouseEnter={onMouseEnter}
|
||||
>
|
||||
{current ? (
|
||||
<>
|
||||
{current.map((item: any, i: number) => (
|
||||
<MenuItemVertical key={i} {...item} />
|
||||
))}
|
||||
<Icon className={acn.join(' ')} />
|
||||
</>
|
||||
) : ''}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default Select;
|
||||
|
|
|
@ -46,7 +46,7 @@ const Switch = forwardRef<SwitchRefProps, Props>(({
|
|||
};
|
||||
};
|
||||
|
||||
useEffect(() => setValue(initialValue));
|
||||
useEffect(() => setValue(initialValue), []);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getValue: () => value,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React, { forwardRef, useRef, useState, useEffect, useImperativeHandle } from 'react';
|
||||
import $ from 'jquery';
|
||||
import { keyboard } from 'Lib';
|
||||
|
||||
|
@ -22,182 +22,150 @@ interface Props {
|
|||
onPaste?(e: any): void;
|
||||
};
|
||||
|
||||
interface State {
|
||||
value: string;
|
||||
interface TextareaRefProps {
|
||||
focus(): void;
|
||||
select(): void;
|
||||
getValue(): string;
|
||||
setError(v: boolean): void;
|
||||
addClass(v: string): void;
|
||||
removeClass(v: string): void;
|
||||
};
|
||||
|
||||
class Textarea extends React.Component<Props, State> {
|
||||
const Textarea = forwardRef<TextareaRefProps, Props>(({
|
||||
id = '',
|
||||
name = '',
|
||||
placeholder = '',
|
||||
className = '',
|
||||
rows = null,
|
||||
value: initialValue = '',
|
||||
autoComplete = null,
|
||||
maxLength = null,
|
||||
readonly = false,
|
||||
onChange,
|
||||
onKeyDown,
|
||||
onKeyUp,
|
||||
onInput,
|
||||
onFocus,
|
||||
onBlur,
|
||||
onCopy,
|
||||
onPaste,
|
||||
}, ref) => {
|
||||
const [ value, setValue ] = useState(initialValue);
|
||||
const nodeRef = useRef(null);
|
||||
const cn = [ 'textarea' ];
|
||||
|
||||
public static defaultProps = {
|
||||
value: ''
|
||||
if (className) {
|
||||
cn.push(className);
|
||||
};
|
||||
|
||||
_isMounted = false;
|
||||
node: any = null;
|
||||
textAreaElement: HTMLTextAreaElement;
|
||||
|
||||
state = {
|
||||
value: '',
|
||||
};
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
this.onKeyUp = this.onKeyUp.bind(this);
|
||||
this.onInput = this.onInput.bind(this);
|
||||
this.onFocus = this.onFocus.bind(this);
|
||||
this.onBlur = this.onBlur.bind(this);
|
||||
this.onCopy = this.onCopy.bind(this);
|
||||
this.onPaste = this.onPaste.bind(this);
|
||||
};
|
||||
|
||||
render () {
|
||||
const { id, name, className, placeholder, rows, autoComplete, readonly, maxLength } = this.props;
|
||||
const { value } = this.state;
|
||||
const cn = [ 'textarea' ];
|
||||
|
||||
if (className) {
|
||||
cn.push(className);
|
||||
};
|
||||
|
||||
return (
|
||||
<textarea
|
||||
ref={node => this.node = node}
|
||||
name={name}
|
||||
id={id}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
rows={rows}
|
||||
className={cn.join(' ')}
|
||||
autoComplete={autoComplete}
|
||||
readOnly={readonly}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onKeyUp={this.onKeyUp}
|
||||
onInput={this.onInput}
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
onCopy={this.onCopy}
|
||||
onPaste={this.onPaste}
|
||||
maxLength={maxLength ? maxLength : undefined}
|
||||
spellCheck={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
this._isMounted = true;
|
||||
this.textAreaElement = $(this.node).get(0) as HTMLTextAreaElement;
|
||||
this.setValue(this.props.value ? this.props.value : '');
|
||||
};
|
||||
|
||||
componentWillUnmount () {
|
||||
this._isMounted = false;
|
||||
};
|
||||
|
||||
onChange (e: any) {
|
||||
this.setValue(e.target.value);
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(e, e.target.value);
|
||||
const onChangeHandler = (e: any) => {
|
||||
setValue(e.target.value);
|
||||
if (onChange) {
|
||||
onChange(e, e.target.value);
|
||||
};
|
||||
};
|
||||
|
||||
onKeyDown (e: any) {
|
||||
this.setValue(e.target.value);
|
||||
if (this.props.onKeyDown) {
|
||||
this.props.onKeyDown(e, e.target.value);
|
||||
};
|
||||
};
|
||||
|
||||
onKeyUp (e: any) {
|
||||
this.setValue(e.target.value);
|
||||
if (this.props.onKeyUp) {
|
||||
this.props.onKeyUp(e, e.target.value);
|
||||
const onKeyDownHandler = (e: any) => {
|
||||
setValue(e.target.value);
|
||||
if (onKeyDown) {
|
||||
onKeyDown(e, e.target.value);
|
||||
};
|
||||
};
|
||||
|
||||
onInput (e: any) {
|
||||
if (this.props.onInput) {
|
||||
this.props.onInput(e, e.target.value);
|
||||
const onKeyUpHandler = (e: any) => {
|
||||
setValue(e.target.value);
|
||||
if (onKeyUp) {
|
||||
onKeyUp(e, e.target.value);
|
||||
};
|
||||
};
|
||||
|
||||
onFocus (e: any) {
|
||||
if (this.props.onFocus) {
|
||||
this.props.onFocus(e, this.state.value);
|
||||
|
||||
const onInputHandler = (e: any) => {
|
||||
if (onInput) {
|
||||
onInput(e, e.target.value);
|
||||
};
|
||||
};
|
||||
|
||||
const onFocusHandler = (e: any) => {
|
||||
if (onFocus) {
|
||||
onFocus(e, value);
|
||||
};
|
||||
|
||||
keyboard.setFocus(true);
|
||||
this.addClass('isFocused');
|
||||
$(nodeRef.current).addClass('isFocused');
|
||||
};
|
||||
|
||||
onBlur (e: any) {
|
||||
if (this.props.onBlur) {
|
||||
this.props.onBlur(e, this.state.value);
|
||||
|
||||
const onBlurHandler = (e: any) => {
|
||||
if (onBlur) {
|
||||
onBlur(e, value);
|
||||
};
|
||||
|
||||
keyboard.setFocus(false);
|
||||
this.removeClass('isFocused');
|
||||
$(nodeRef.current).removeClass('isFocused');
|
||||
};
|
||||
|
||||
onCopy (e: any) {
|
||||
if (this.props.onCopy) {
|
||||
this.props.onCopy(e, this.state.value);
|
||||
};
|
||||
};
|
||||
|
||||
onPaste (e: any) {
|
||||
if (this.props.onPaste) {
|
||||
this.props.onPaste(e);
|
||||
const onCopyHandler = (e: any) => {
|
||||
if (onCopy) {
|
||||
onCopy(e, value);
|
||||
};
|
||||
};
|
||||
|
||||
focus () {
|
||||
window.setTimeout(() => {
|
||||
if (!this._isMounted) {
|
||||
return;
|
||||
};
|
||||
|
||||
this.textAreaElement.focus({ preventScroll: true });
|
||||
});
|
||||
};
|
||||
|
||||
select () {
|
||||
window.setTimeout(() => {
|
||||
if (!this._isMounted) {
|
||||
return;
|
||||
};
|
||||
|
||||
this.textAreaElement.select();
|
||||
});
|
||||
};
|
||||
|
||||
setValue (v: string) {
|
||||
this.setState({ value: v });
|
||||
};
|
||||
|
||||
getValue () {
|
||||
return this.state.value;
|
||||
};
|
||||
|
||||
setError (v: boolean) {
|
||||
$(this.node).toggleClass('withError', v);
|
||||
};
|
||||
|
||||
addClass (v: string) {
|
||||
if (this._isMounted) {
|
||||
$(this.node).addClass(v);
|
||||
const onPasteHandler = (e: any) => {
|
||||
if (onPaste) {
|
||||
onPaste(e);
|
||||
};
|
||||
};
|
||||
|
||||
removeClass (v: string) {
|
||||
if (this._isMounted) {
|
||||
$(this.node).removeClass(v);
|
||||
};
|
||||
|
||||
const focus = () => {
|
||||
window.setTimeout(() => nodeRef.current.focus({ preventScroll: true }));
|
||||
};
|
||||
|
||||
};
|
||||
const select = () => {
|
||||
window.setTimeout(() => nodeRef.current.select());
|
||||
};
|
||||
|
||||
const setError = (v: boolean) => {
|
||||
$(nodeRef.current).toggleClass('withError', v);
|
||||
};
|
||||
|
||||
const addClass = (v: string) => {
|
||||
$(nodeRef.current).addClass(v);
|
||||
};
|
||||
|
||||
const removeClass = (v: string) => {
|
||||
$(nodeRef.current).removeClass(v);
|
||||
};
|
||||
|
||||
useEffect(() => setValue(initialValue));
|
||||
useImperativeHandle(ref, () => ({
|
||||
focus,
|
||||
select,
|
||||
getValue: () => value,
|
||||
setError,
|
||||
addClass,
|
||||
removeClass
|
||||
}));
|
||||
|
||||
return (
|
||||
<textarea
|
||||
ref={nodeRef}
|
||||
name={name}
|
||||
id={id}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
rows={rows}
|
||||
className={cn.join(' ')}
|
||||
autoComplete={autoComplete}
|
||||
readOnly={readonly}
|
||||
onChange={onChangeHandler}
|
||||
onKeyDown={onKeyDownHandler}
|
||||
onKeyUp={onKeyUpHandler}
|
||||
onInput={onInputHandler}
|
||||
onFocus={onFocusHandler}
|
||||
onBlur={onBlurHandler}
|
||||
onCopy={onCopyHandler}
|
||||
onPaste={onPasteHandler}
|
||||
maxLength={maxLength ? maxLength : undefined}
|
||||
spellCheck={false}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export default Textarea;
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React, { forwardRef, useImperativeHandle, useRef, useEffect } from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import $ from 'jquery';
|
||||
import * as d3 from 'd3';
|
||||
|
@ -14,116 +14,82 @@ interface Props {
|
|||
storageKey: string;
|
||||
};
|
||||
|
||||
const Graph = observer(class Graph extends React.Component<Props> {
|
||||
interface GraphRefProps {
|
||||
init: () => void;
|
||||
resize: () => void;
|
||||
};
|
||||
|
||||
node: any = null;
|
||||
canvas: any = null;
|
||||
edges: any[] = [];
|
||||
nodes: any[] = [];
|
||||
worker: any = null;
|
||||
images: any = {};
|
||||
subject: any = null;
|
||||
isDragging = false;
|
||||
isPreviewDisabled = false;
|
||||
ids: string[] = [];
|
||||
timeoutPreview = 0;
|
||||
zoom: any = null;
|
||||
previewId = '';
|
||||
const Graph = observer(forwardRef<GraphRefProps, Props>(({
|
||||
id = '',
|
||||
isPopup = false,
|
||||
rootId = '',
|
||||
data = {},
|
||||
storageKey = '',
|
||||
}, ref) => {
|
||||
|
||||
constructor (props: Props) {
|
||||
super(props);
|
||||
const nodeRef = useRef(null);
|
||||
const worker = useRef(null);
|
||||
const theme = S.Common.getThemeClass();
|
||||
const elementId = [ 'graph', id ].join('-') + U.Common.getEventNamespace(isPopup);
|
||||
const previewId = useRef('');
|
||||
const canvas = useRef(null);
|
||||
const edges = useRef([]);
|
||||
const nodes = useRef([]);
|
||||
const images = useRef({});
|
||||
const subject = useRef(null);
|
||||
const isDragging = useRef(false);
|
||||
const isPreviewDisabled = useRef(false);
|
||||
const ids = useRef([]);
|
||||
const zoom = useRef(null);
|
||||
|
||||
this.onMessage = this.onMessage.bind(this);
|
||||
this.nodeMapper = this.nodeMapper.bind(this);
|
||||
this.setRootId = this.setRootId.bind(this);
|
||||
};
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div
|
||||
ref={node => this.node = node}
|
||||
id="graphWrapper"
|
||||
>
|
||||
<div id={this.getId()} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
this.rebind();
|
||||
};
|
||||
|
||||
componentWillUnmount () {
|
||||
if (this.worker) {
|
||||
this.worker.terminate();
|
||||
const send = (id: string, param: any, transfer?: any[]) => {
|
||||
if (worker.current) {
|
||||
worker.current.postMessage({ id, ...param }, transfer);
|
||||
};
|
||||
|
||||
this.unbind();
|
||||
this.onPreviewHide();
|
||||
};
|
||||
|
||||
rebind () {
|
||||
const rebind = () => {
|
||||
const win = $(window);
|
||||
|
||||
this.unbind();
|
||||
win.on('updateGraphSettings.graph', () => this.updateSettings());
|
||||
win.on('updateGraphRoot.graph', (e: any, data: any) => this.setRootId(data.id));
|
||||
win.on('removeGraphNode.graph', (e: any, data: any) => this.send('onRemoveNode', { ids: U.Common.objectCopy(data.ids) }));
|
||||
win.on(`keydown.graph`, e => this.onKeyDown(e));
|
||||
win.on('updateTheme.graph', () => {
|
||||
const theme = S.Common.getThemeClass();
|
||||
this.send('updateTheme', { theme, colors: J.Theme[theme].graph || {} });
|
||||
});
|
||||
unbind();
|
||||
win.on('updateGraphSettings.graph', () => updateSettings());
|
||||
win.on('updateGraphRoot.graph', (e: any, data: any) => setRootId(data.id));
|
||||
win.on('removeGraphNode.graph', (e: any, data: any) => send('onRemoveNode', { ids: U.Common.objectCopy(data.ids) }));
|
||||
win.on(`keydown.graph`, e => onKeyDown(e));
|
||||
};
|
||||
|
||||
unbind () {
|
||||
const events = [ 'updateGraphSettings', 'updateGraphRoot', 'updateTheme', 'removeGraphNode', 'keydown' ];
|
||||
const unbind = () => {
|
||||
const events = [ 'updateGraphSettings', 'updateGraphRoot', 'removeGraphNode', 'keydown' ];
|
||||
|
||||
$(window).off(events.map(it => `${it}.graph`).join(' '));
|
||||
};
|
||||
|
||||
getId (): string {
|
||||
const { id, isPopup } = this.props;
|
||||
const ret = [ 'graph' ];
|
||||
|
||||
if (id) {
|
||||
ret.push(id);
|
||||
};
|
||||
if (isPopup) {
|
||||
ret.push('popup');
|
||||
};
|
||||
return ret.join('-');
|
||||
};
|
||||
|
||||
init () {
|
||||
const { data, rootId, storageKey } = this.props;
|
||||
const node = $(this.node);
|
||||
const init = () => {
|
||||
const node = $(nodeRef.current);
|
||||
const density = window.devicePixelRatio;
|
||||
const elementId = `#${this.getId()}`;
|
||||
const width = node.width();
|
||||
const height = node.height();
|
||||
const theme = S.Common.getThemeClass();
|
||||
const settings = S.Common.getGraph(storageKey);
|
||||
|
||||
this.images = {};
|
||||
this.zoom = d3.zoom().scaleExtent([ 0.05, 10 ]).on('zoom', e => this.onZoom(e));
|
||||
this.edges = (data.edges || []).map(this.edgeMapper);
|
||||
this.nodes = (data.nodes || []).map(this.nodeMapper);
|
||||
images.current = {};
|
||||
zoom.current = d3.zoom().scaleExtent([ 0.05, 10 ]).on('zoom', e => onZoom(e));
|
||||
edges.current = (data.edges || []).map(edgeMapper);
|
||||
nodes.current = (data.nodes || []).map(nodeMapper);
|
||||
|
||||
node.find('canvas').remove();
|
||||
|
||||
this.canvas = d3.select(elementId).append('canvas')
|
||||
canvas.current = d3.select(`#${elementId}`).append('canvas')
|
||||
.attr('width', (width * density) + 'px')
|
||||
.attr('height', (height * density) + 'px')
|
||||
.node();
|
||||
|
||||
const transfer = node.find('canvas').get(0).transferControlToOffscreen();
|
||||
const transfer = canvas.current.transferControlToOffscreen();
|
||||
|
||||
this.worker = new Worker('workers/graph.js');
|
||||
this.worker.onerror = (e: any) => console.log(e);
|
||||
this.worker.addEventListener('message', this.onMessage);
|
||||
worker.current = new Worker('workers/graph.js');
|
||||
worker.current.onerror = (e: any) => console.log(e);
|
||||
worker.current.addEventListener('message', onMessage);
|
||||
|
||||
this.send('init', {
|
||||
send('init', {
|
||||
canvas: transfer,
|
||||
width,
|
||||
height,
|
||||
|
@ -131,20 +97,20 @@ const Graph = observer(class Graph extends React.Component<Props> {
|
|||
theme,
|
||||
settings,
|
||||
rootId,
|
||||
nodes: this.nodes,
|
||||
edges: this.edges,
|
||||
nodes: nodes.current,
|
||||
edges: edges.current,
|
||||
colors: J.Theme[theme].graph || {},
|
||||
}, [ transfer ]);
|
||||
|
||||
d3.select(this.canvas)
|
||||
d3.select(canvas.current)
|
||||
.call(d3.drag().
|
||||
subject(() => this.subject).
|
||||
on('start', (e: any, d: any) => this.onDragStart(e)).
|
||||
on('drag', (e: any, d: any) => this.onDragMove(e)).
|
||||
on('end', (e: any, d: any) => this.onDragEnd(e))
|
||||
subject(() => subject.current).
|
||||
on('start', (e: any, d: any) => onDragStart(e)).
|
||||
on('drag', (e: any, d: any) => onDragMove(e)).
|
||||
on('end', (e: any, d: any) => onDragEnd(e))
|
||||
)
|
||||
.call(this.zoom)
|
||||
.call(this.zoom.transform, d3.zoomIdentity.translate(0, 0).scale(1))
|
||||
.call(zoom.current)
|
||||
.call(zoom.current.transform, d3.zoomIdentity.translate(0, 0).scale(1))
|
||||
.on('click', (e: any) => {
|
||||
const { local } = S.Common.getGraph(storageKey);
|
||||
const [ x, y ] = d3.pointer(e);
|
||||
|
@ -156,25 +122,25 @@ const Graph = observer(class Graph extends React.Component<Props> {
|
|||
event = e.shiftKey ? 'onSelect' : 'onClick';
|
||||
};
|
||||
|
||||
this.send(event, { x, y });
|
||||
send(event, { x, y });
|
||||
})
|
||||
.on('dblclick', (e: any) => {
|
||||
if (e.shiftKey) {
|
||||
const [ x, y ] = d3.pointer(e);
|
||||
this.send('onSelect', { x, y, selectRelated: true });
|
||||
send('onSelect', { x, y, selectRelated: true });
|
||||
};
|
||||
})
|
||||
.on('contextmenu', (e: any) => {
|
||||
const [ x, y ] = d3.pointer(e);
|
||||
this.send('onContextMenu', { x, y });
|
||||
send('onContextMenu', { x, y });
|
||||
})
|
||||
.on('mousemove', (e: any) => {
|
||||
const [ x, y ] = d3.pointer(e);
|
||||
this.send('onMouseMove', { x, y });
|
||||
send('onMouseMove', { x, y });
|
||||
});
|
||||
};
|
||||
|
||||
nodeMapper (d: any) {
|
||||
const nodeMapper = (d: any) => {
|
||||
d = d || {};
|
||||
d.layout = Number(d.layout) || 0;
|
||||
d.radius = 4;
|
||||
|
@ -197,21 +163,21 @@ const Graph = observer(class Graph extends React.Component<Props> {
|
|||
d.iconEmoji = '';
|
||||
};
|
||||
|
||||
if (!this.images[d.src]) {
|
||||
if (!images.current[d.src]) {
|
||||
const img = new Image();
|
||||
|
||||
img.onload = () => {
|
||||
if (this.images[d.src]) {
|
||||
if (images.current[d.src]) {
|
||||
return;
|
||||
};
|
||||
|
||||
createImageBitmap(img, { resizeWidth: 160, resizeQuality: 'high' }).then((res: any) => {
|
||||
if (this.images[d.src]) {
|
||||
if (images.current[d.src]) {
|
||||
return;
|
||||
};
|
||||
|
||||
this.images[d.src] = true;
|
||||
this.send('image', { src: d.src, bitmap: res });
|
||||
images.current[d.src] = true;
|
||||
send('image', { src: d.src, bitmap: res });
|
||||
});
|
||||
};
|
||||
img.crossOrigin = '';
|
||||
|
@ -221,24 +187,24 @@ const Graph = observer(class Graph extends React.Component<Props> {
|
|||
return d;
|
||||
};
|
||||
|
||||
edgeMapper (d: any) {
|
||||
const edgeMapper = (d: any) => {
|
||||
d.type = Number(d.type) || 0;
|
||||
d.typeName = translate('edgeType' + d.type);
|
||||
return d;
|
||||
};
|
||||
|
||||
updateSettings () {
|
||||
this.send('updateSettings', S.Common.getGraph(this.props.storageKey));
|
||||
const updateSettings = () => {
|
||||
send('updateSettings', S.Common.getGraph(storageKey));
|
||||
};
|
||||
|
||||
onDragStart (e: any) {
|
||||
this.isDragging = true;
|
||||
this.send('onDragStart', { active: e.active });
|
||||
const onDragStart = (e: any) => {
|
||||
isDragging.current = true;
|
||||
send('onDragStart', { active: e.active });
|
||||
};
|
||||
|
||||
onDragMove (e: any) {
|
||||
const p = d3.pointer(e, d3.select(this.canvas));
|
||||
const node = $(this.node);
|
||||
const onDragMove = (e: any) => {
|
||||
const p = d3.pointer(e, d3.select(canvas.current));
|
||||
const node = $(nodeRef.current);
|
||||
|
||||
if (!node || !node.length) {
|
||||
return;
|
||||
|
@ -246,36 +212,36 @@ const Graph = observer(class Graph extends React.Component<Props> {
|
|||
|
||||
const { left, top } = node.offset();
|
||||
|
||||
this.send('onDragMove', {
|
||||
subjectId: this.subject.id,
|
||||
send('onDragMove', {
|
||||
subjectId: subject.current?.id,
|
||||
active: e.active,
|
||||
x: p[0] - left,
|
||||
y: p[1] - top,
|
||||
});
|
||||
};
|
||||
|
||||
onDragEnd (e: any) {
|
||||
this.isDragging = false;
|
||||
this.subject = null;
|
||||
this.send('onDragEnd', { active: e.active });
|
||||
const onDragEnd = (e: any) => {
|
||||
isDragging.current = false;
|
||||
subject.current = null;
|
||||
send('onDragEnd', { active: e.active });
|
||||
};
|
||||
|
||||
onZoom ({ transform }) {
|
||||
this.send('onZoom', { transform });
|
||||
const onZoom = ({ transform }) => {
|
||||
send('onZoom', { transform });
|
||||
};
|
||||
|
||||
onPreviewShow (data: any) {
|
||||
if (this.isPreviewDisabled || !this.subject) {
|
||||
const onPreviewShow = (data: any) => {
|
||||
if (isPreviewDisabled.current || !subject.current) {
|
||||
return;
|
||||
};
|
||||
|
||||
const win = $(window);
|
||||
const body = $('body');
|
||||
const node = $(this.node);
|
||||
const node = $(nodeRef.current);
|
||||
const { left, top } = node.offset();
|
||||
const render = this.previewId != this.subject.id;
|
||||
const render = previewId != subject.current.id;
|
||||
|
||||
this.previewId = this.subject.id;
|
||||
previewId.current = subject.current.id;
|
||||
|
||||
let el = $('#graphPreview');
|
||||
|
||||
|
@ -293,22 +259,21 @@ const Graph = observer(class Graph extends React.Component<Props> {
|
|||
body.find('#graphPreview').remove();
|
||||
body.append(el);
|
||||
|
||||
ReactDOM.render(<PreviewDefault object={this.subject} className="previewGraph" />, el.get(0), position);
|
||||
analytics.event('SelectGraphNode', { objectType: this.subject.type, layout: this.subject.layout });
|
||||
ReactDOM.render(<PreviewDefault object={subject.current} className="previewGraph" />, el.get(0), position);
|
||||
analytics.event('SelectGraphNode', { objectType: subject.current.type, layout: subject.current.layout });
|
||||
} else {
|
||||
position();
|
||||
};
|
||||
};
|
||||
|
||||
onPreviewHide () {
|
||||
const onPreviewHide = () => {
|
||||
$('#graphPreview').remove();
|
||||
};
|
||||
|
||||
onMessage (e) {
|
||||
const { storageKey } = this.props;
|
||||
const onMessage = (e) => {
|
||||
const settings = S.Common.getGraph(storageKey);
|
||||
const { id, data } = e.data;
|
||||
const node = $(this.node);
|
||||
const node = $(nodeRef.current);
|
||||
|
||||
if (!node || !node.length) {
|
||||
return;
|
||||
|
@ -316,8 +281,8 @@ const Graph = observer(class Graph extends React.Component<Props> {
|
|||
|
||||
const { left, top } = node.offset();
|
||||
const menuParam = {
|
||||
onOpen: () => this.isPreviewDisabled = true,
|
||||
onClose: () => this.isPreviewDisabled = false,
|
||||
onOpen: () => isPreviewDisabled.current = true,
|
||||
onClose: () => isPreviewDisabled.current = false,
|
||||
recalcRect: () => ({
|
||||
width: 0,
|
||||
height: 0,
|
||||
|
@ -328,30 +293,30 @@ const Graph = observer(class Graph extends React.Component<Props> {
|
|||
|
||||
switch (id) {
|
||||
case 'onClick': {
|
||||
this.onClickObject(data.node);
|
||||
onClickObject(data.node);
|
||||
break;
|
||||
};
|
||||
|
||||
case 'onSelect': {
|
||||
this.onSelect(data.node, data.related);
|
||||
onSelect(data.node, data.related);
|
||||
break;
|
||||
};
|
||||
|
||||
case 'onMouseMove': {
|
||||
if (this.isDragging) {
|
||||
if (isDragging.current) {
|
||||
break;
|
||||
};
|
||||
|
||||
this.subject = this.nodes.find(d => d.id == data.node);
|
||||
subject.current = getNode(data.node);
|
||||
|
||||
if (settings.preview) {
|
||||
this.subject ? this.onPreviewShow(data) : this.onPreviewHide();
|
||||
subject.current ? onPreviewShow(data) : onPreviewHide();
|
||||
};
|
||||
break;
|
||||
};
|
||||
|
||||
case 'onDragMove': {
|
||||
this.onPreviewHide();
|
||||
onPreviewHide();
|
||||
break;
|
||||
};
|
||||
|
||||
|
@ -360,91 +325,90 @@ const Graph = observer(class Graph extends React.Component<Props> {
|
|||
break;
|
||||
};
|
||||
|
||||
this.onPreviewHide();
|
||||
this.onContextMenu(data.node.id, menuParam);
|
||||
onPreviewHide();
|
||||
onContextMenu(data.node.id, menuParam);
|
||||
break;
|
||||
};
|
||||
|
||||
case 'onContextSpaceClick': {
|
||||
this.onPreviewHide();
|
||||
this.onContextSpaceClick(menuParam, data);
|
||||
onPreviewHide();
|
||||
onContextSpaceClick(menuParam, data);
|
||||
break;
|
||||
};
|
||||
|
||||
case 'onTransform': {
|
||||
d3.select(this.canvas)
|
||||
.call(this.zoom)
|
||||
.call(this.zoom.transform, d3.zoomIdentity.translate(data.x, data.y).scale(data.k));
|
||||
d3.select(canvas.current)
|
||||
.call(zoom.current)
|
||||
.call(zoom.current.transform, d3.zoomIdentity.translate(data.x, data.y).scale(data.k));
|
||||
break;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
onKeyDown (e: any) {
|
||||
const onKeyDown = (e: any) => {
|
||||
const cmd = keyboard.cmdKey();
|
||||
const length = this.ids.length;
|
||||
const length = ids.current.length;
|
||||
|
||||
keyboard.shortcut(`${cmd}+f`, e, () => $('#button-header-search').trigger('click'));
|
||||
|
||||
if (length) {
|
||||
keyboard.shortcut('escape', e, () => {
|
||||
this.ids = [];
|
||||
this.send('onSetSelected', { ids: [] });
|
||||
});
|
||||
|
||||
keyboard.shortcut('backspace, delete', e, () => {
|
||||
Action.archive(this.ids, analytics.route.graph, () => {
|
||||
this.nodes = this.nodes.filter(d => !this.ids.includes(d.id));
|
||||
this.send('onRemoveNode', { ids: this.ids });
|
||||
});
|
||||
});
|
||||
if (!length) {
|
||||
return;
|
||||
};
|
||||
|
||||
keyboard.shortcut('escape', e, () => setSelected([]));
|
||||
|
||||
keyboard.shortcut('backspace, delete', e, () => {
|
||||
Action.archive(ids.current, analytics.route.graph, () => {
|
||||
nodes.current = nodes.current.filter(d => !ids.current.includes(d.id));
|
||||
send('onRemoveNode', { ids: ids });
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
onContextMenu (id: string, param: any) {
|
||||
const ids = this.ids.length ? this.ids : [ id ];
|
||||
const onContextMenu = (id: string, param: any) => {
|
||||
const selected = ids.current.length ? ids.current : [ id ];
|
||||
|
||||
S.Menu.open('dataviewContext', {
|
||||
...param,
|
||||
data: {
|
||||
route: analytics.route.graph,
|
||||
objectIds: ids,
|
||||
getObject: id => this.getNode(id),
|
||||
objectIds: selected,
|
||||
getObject: id => getNode(id),
|
||||
allowedLinkTo: true,
|
||||
allowedOpen: true,
|
||||
onLinkTo: (sourceId: string, targetId: string) => {
|
||||
const target = this.getNode(targetId);
|
||||
const target = getNode(targetId);
|
||||
if (target) {
|
||||
this.edges.push(this.edgeMapper({ type: I.EdgeType.Link, source: sourceId, target: targetId }));
|
||||
this.send('onSetEdges', { edges: this.edges });
|
||||
edges.current.push(edgeMapper({ type: I.EdgeType.Link, source: sourceId, target: targetId }));
|
||||
send('onSetEdges', { edges: edges.current });
|
||||
} else {
|
||||
this.addNewNode(targetId, sourceId, null);
|
||||
addNewNode(targetId, sourceId, null);
|
||||
};
|
||||
},
|
||||
onSelect: (itemId: string) => {
|
||||
switch (itemId) {
|
||||
case 'archive': {
|
||||
this.nodes = this.nodes.filter(d => !ids.includes(d.id));
|
||||
this.send('onRemoveNode', { ids });
|
||||
nodes.current = nodes.current.filter(d => !selected.includes(d.id));
|
||||
send('onRemoveNode', { ids: selected });
|
||||
break;
|
||||
};
|
||||
|
||||
case 'fav': {
|
||||
ids.forEach((id: string) => {
|
||||
const node = this.getNode(id);
|
||||
selected.forEach(id => {
|
||||
const node = getNode(id);
|
||||
|
||||
if (node) {
|
||||
node.isFavorite = true;
|
||||
};
|
||||
});
|
||||
this.send('onSetEdges', { edges: this.edges });
|
||||
send('onSetEdges', { edges: edges.current });
|
||||
break;
|
||||
};
|
||||
|
||||
case 'unfav': {
|
||||
ids.forEach((id: string) => {
|
||||
const node = this.getNode(id);
|
||||
selected.forEach(id => {
|
||||
const node = getNode(id);
|
||||
|
||||
if (node) {
|
||||
node.isFavorite = false;
|
||||
|
@ -454,14 +418,13 @@ const Graph = observer(class Graph extends React.Component<Props> {
|
|||
};
|
||||
};
|
||||
|
||||
this.ids = [];
|
||||
this.send('onSetSelected', { ids: this.ids });
|
||||
setSelected(ids.current);
|
||||
},
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onContextSpaceClick (param: any, data: any) {
|
||||
const onContextSpaceClick = (param: any, data: any) => {
|
||||
if (!U.Space.canMyParticipantWrite()) {
|
||||
return;
|
||||
};
|
||||
|
@ -478,7 +441,7 @@ const Graph = observer(class Graph extends React.Component<Props> {
|
|||
const flags = [ I.ObjectFlag.SelectType, I.ObjectFlag.SelectTemplate ];
|
||||
|
||||
U.Object.create('', '', {}, I.BlockPosition.Bottom, '', flags, analytics.route.graph, (message: any) => {
|
||||
U.Object.openConfig(message.details, { onClose: () => this.addNewNode(message.targetId, '', data) });
|
||||
U.Object.openConfig(message.details, { onClose: () => addNewNode(message.targetId, '', data) });
|
||||
});
|
||||
break;
|
||||
};
|
||||
|
@ -488,48 +451,46 @@ const Graph = observer(class Graph extends React.Component<Props> {
|
|||
});
|
||||
};
|
||||
|
||||
onSelect (id: string, related?: string[]) {
|
||||
const isSelected = this.ids.includes(id);
|
||||
const onSelect = (id: string, related?: string[]) => {
|
||||
const isSelected = ids.current.includes(id);
|
||||
|
||||
let ids = [ id ];
|
||||
let ret = [ id ];
|
||||
|
||||
if (related && related.length) {
|
||||
if (!isSelected) {
|
||||
this.ids = [];
|
||||
ret = [];
|
||||
};
|
||||
|
||||
ids = ids.concat(related);
|
||||
ret = ret.concat(related);
|
||||
};
|
||||
|
||||
ids.forEach((id) => {
|
||||
ret.forEach(id => {
|
||||
if (isSelected) {
|
||||
this.ids = this.ids.filter(it => it != id);
|
||||
ids.current = ids.current.filter(it => it != id);
|
||||
return;
|
||||
};
|
||||
|
||||
this.ids = this.ids.includes(id) ? this.ids.filter(it => it != id) : this.ids.concat([ id ]);
|
||||
ids.current = ids.current.includes(id) ? ids.current.filter(it => it != id) : ids.current.concat([ id ]);
|
||||
});
|
||||
|
||||
this.send('onSetSelected', { ids: this.ids });
|
||||
setSelected(ids.current);
|
||||
};
|
||||
|
||||
onClickObject (id: string) {
|
||||
this.ids = [];
|
||||
this.send('onSetSelected', { ids: [] });
|
||||
|
||||
U.Object.openAuto(this.nodes.find(d => d.id == id));
|
||||
const onClickObject = (id: string) => {
|
||||
setSelected([]);
|
||||
U.Object.openAuto(getNode(id));
|
||||
};
|
||||
|
||||
addNewNode (id: string, sourceId?: string, param?: any, callBack?: (object: any) => void) {
|
||||
const addNewNode = (id: string, sourceId?: string, param?: any, callBack?: (object: any) => void) => {
|
||||
U.Object.getById(id, {}, (object: any) => {
|
||||
object = this.nodeMapper(object);
|
||||
object = nodeMapper(object);
|
||||
|
||||
if (param) {
|
||||
object = Object.assign(object, param);
|
||||
};
|
||||
|
||||
this.nodes.push(object);
|
||||
this.send('onAddNode', { target: object, sourceId });
|
||||
nodes.current.push(object);
|
||||
send('onAddNode', { target: object, sourceId });
|
||||
|
||||
if (callBack) {
|
||||
callBack(object);
|
||||
|
@ -537,30 +498,59 @@ const Graph = observer(class Graph extends React.Component<Props> {
|
|||
});
|
||||
};
|
||||
|
||||
getNode (id: string) {
|
||||
return this.nodes.find(d => d.id == id);
|
||||
const getNode = (id: string) => {
|
||||
return nodes.current.find(d => d.id == id);
|
||||
};
|
||||
|
||||
setRootId (id: string) {
|
||||
this.send('setRootId', { rootId: id });
|
||||
const setRootId = (id: string) => {
|
||||
send('setRootId', { rootId: id });
|
||||
};
|
||||
|
||||
send (id: string, param: any, transfer?: any[]) {
|
||||
if (this.worker) {
|
||||
this.worker.postMessage({ id, ...param }, transfer);
|
||||
};
|
||||
const setSelected = (selected: string[]) => {
|
||||
ids.current = selected;
|
||||
send('onSetSelected', { ids: ids.current });
|
||||
};
|
||||
|
||||
resize () {
|
||||
const node = $(this.node);
|
||||
const resize = () => {
|
||||
const node = $(nodeRef.current);
|
||||
|
||||
this.send('resize', {
|
||||
send('resize', {
|
||||
width: node.width(),
|
||||
height: node.height(),
|
||||
height: node.height(),
|
||||
density: window.devicePixelRatio,
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
useEffect(() => {
|
||||
rebind();
|
||||
|
||||
return () => {
|
||||
unbind();
|
||||
onPreviewHide();
|
||||
|
||||
if (worker.current) {
|
||||
worker.current.terminate();
|
||||
};
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
send('updateTheme', { theme, colors: J.Theme[theme].graph || {} });
|
||||
}, [ theme ]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
init,
|
||||
resize,
|
||||
}));
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={nodeRef}
|
||||
className="graphWrapper"
|
||||
>
|
||||
<div id={elementId} />
|
||||
</div>
|
||||
);
|
||||
}));
|
||||
|
||||
export default Graph;
|
|
@ -1,31 +1,18 @@
|
|||
import * as React from 'react';
|
||||
import React, { forwardRef } from 'react';
|
||||
import { Icon } from 'Component';
|
||||
import { S } from 'Lib';
|
||||
|
||||
class HeaderAuthIndex extends React.Component {
|
||||
|
||||
constructor (props: any) {
|
||||
super(props);
|
||||
const HeaderAuthIndex = forwardRef(() => {
|
||||
|
||||
this.onSettings = this.onSettings.bind(this);
|
||||
};
|
||||
|
||||
render () {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="side left" />
|
||||
<div className="side center" />
|
||||
<div className="side right">
|
||||
<Icon className="settings withBackground" onClick={this.onSettings} />
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
onSettings () {
|
||||
S.Popup.open('settingsOnboarding', {});
|
||||
};
|
||||
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className="side left" />
|
||||
<div className="side center" />
|
||||
<div className="side right">
|
||||
<Icon className="settings withBackground" onClick={() => S.Popup.open('settingsOnboarding', {})} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default HeaderAuthIndex;
|
|
@ -81,7 +81,7 @@ class Header extends React.Component<Props> {
|
|||
|
||||
componentDidUpdate () {
|
||||
sidebar.resizePage(null, null, false);
|
||||
this.refChild.forceUpdate();
|
||||
this.refChild?.forceUpdate();
|
||||
};
|
||||
|
||||
renderLeftIcons (onOpen?: () => void) {
|
||||
|
|
|
@ -1,64 +1,49 @@
|
|||
import * as React from 'react';
|
||||
import React, { forwardRef, useState, useImperativeHandle } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Icon } from 'Component';
|
||||
import { I, S, U, keyboard } from 'Lib';
|
||||
import { I, S, U, keyboard, translate } from 'Lib';
|
||||
|
||||
interface State {
|
||||
version: I.HistoryVersion;
|
||||
interface HeaderMainHistoryRefProps {
|
||||
setVersion: (version: I.HistoryVersion) => void;
|
||||
};
|
||||
|
||||
const HeaderMainHistory = observer(class HeaderMainHistory extends React.Component<I.HeaderComponent, State> {
|
||||
const HeaderMainHistory = observer(forwardRef<HeaderMainHistoryRefProps, I.HeaderComponent>((props, ref) => {
|
||||
const { rootId, renderLeftIcons, onRelation } = props;
|
||||
const [ version, setVersion ] = useState<I.HistoryVersion | null>(null);
|
||||
const [ dummyState, setDummyState ] = useState(0);
|
||||
const cmd = keyboard.cmdSymbol();
|
||||
const object = S.Detail.get(rootId, rootId, []);
|
||||
const showMenu = !U.Object.isTypeOrRelationLayout(object.layout);
|
||||
|
||||
state = {
|
||||
version: null,
|
||||
};
|
||||
useImperativeHandle(ref, () => ({
|
||||
setVersion,
|
||||
forceUpdate: () => setDummyState(dummyState + 1),
|
||||
}));
|
||||
|
||||
constructor (props: I.HeaderComponent) {
|
||||
super(props);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="side left">{renderLeftIcons()}</div>
|
||||
|
||||
this.onBack = this.onBack.bind(this);
|
||||
this.onRelation = this.onRelation.bind(this);
|
||||
};
|
||||
|
||||
render () {
|
||||
const { rootId, renderLeftIcons } = this.props;
|
||||
const { version } = this.state;
|
||||
const cmd = keyboard.cmdSymbol();
|
||||
const object = S.Detail.get(rootId, rootId, []);
|
||||
const showMenu = !U.Object.isTypeOrRelationLayout(object.layout);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="side left">{renderLeftIcons()}</div>
|
||||
|
||||
<div className="side center">
|
||||
<div className="txt">
|
||||
{version ? U.Date.date('M d, Y g:i:s A', version.time) : ''}
|
||||
</div>
|
||||
<div className="side center">
|
||||
<div className="txt">
|
||||
{version ? U.Date.date('M d, Y g:i:s A', version.time) : ''}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="side right">
|
||||
{showMenu ? <Icon id="button-header-relation" tooltip="Relations" tooltipCaption={`${cmd} + Shift + R`} className="relation withBackground" onClick={this.onRelation} /> : ''}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
<div className="side right">
|
||||
{showMenu ? (
|
||||
<Icon
|
||||
id="button-header-relation"
|
||||
tooltip={translate('commonRelations')}
|
||||
tooltipCaption={`${cmd} + Shift + R`}
|
||||
className="relation withBackground"
|
||||
onClick={() => onRelation({}, { readonly: true })}
|
||||
/>
|
||||
) : ''}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
onBack (e: any) {
|
||||
const { rootId } = this.props;
|
||||
const object = S.Detail.get(rootId, rootId, []);
|
||||
|
||||
U.Object.openEvent(e, object);
|
||||
};
|
||||
|
||||
onRelation () {
|
||||
this.props.onRelation({}, { readonly: true });
|
||||
};
|
||||
|
||||
setVersion (version: I.HistoryVersion) {
|
||||
this.setState({ version });
|
||||
};
|
||||
|
||||
});
|
||||
}));
|
||||
|
||||
export default HeaderMainHistory;
|
|
@ -98,8 +98,24 @@ const HeaderMainObject = observer(class HeaderMainObject extends React.Component
|
|||
</div>
|
||||
|
||||
<div className="side right">
|
||||
{showRelations ? <Icon id="button-header-relation" tooltip="Relations" tooltipCaption={`${cmd} + Shift + R`} className="relation withBackground" onClick={this.onRelation} /> : ''}
|
||||
{showMenu ? <Icon id="button-header-more" tooltip="Menu" className="more withBackground" onClick={this.onMore} /> : ''}
|
||||
{showRelations ? (
|
||||
<Icon
|
||||
id="button-header-relation"
|
||||
tooltip={translate('commonRelations')}
|
||||
tooltipCaption={`${cmd} + Shift + R`}
|
||||
className="relation withBackground"
|
||||
onClick={this.onRelation}
|
||||
/>
|
||||
) : ''}
|
||||
|
||||
{showMenu ? (
|
||||
<Icon
|
||||
id="button-header-more"
|
||||
tooltip={translate('commonMenu')}
|
||||
className="more withBackground"
|
||||
onClick={this.onMore}
|
||||
/>
|
||||
) : ''}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
|
|
@ -19,6 +19,7 @@ import ListNotification from './list/notification';
|
|||
import ListChildren from './list/children';
|
||||
import ListObject from './list/object';
|
||||
import ListObjectManager from './list/objectManager';
|
||||
import ListObjectPreview from './list/objectPreview';
|
||||
|
||||
import Header from './header';
|
||||
import Footer from './footer';
|
||||
|
@ -60,6 +61,9 @@ import Pin from './form/pin';
|
|||
import Filter from './form/filter';
|
||||
import Phrase from './form/phrase';
|
||||
import TabSwitch from './form/tabSwitch';
|
||||
import EmailCollection from './form/emailCollection';
|
||||
import HeadSimple from './page/elements/head/simple';
|
||||
import EditorControls from './page/elements/head/controls';
|
||||
|
||||
import Pager from './util/pager';
|
||||
import Dimmer from './util/dimmer';
|
||||
|
@ -96,7 +100,7 @@ import ShareTooltip from './util/share/tooltip';
|
|||
import ShareBanner from './util/share/banner';
|
||||
import FooterAuthDisclaimer from './footer/auth/disclaimer';
|
||||
|
||||
import EmailCollectionForm from './util/emailCollectionForm';
|
||||
import Floater from './util/floater';
|
||||
|
||||
export {
|
||||
Page,
|
||||
|
@ -118,6 +122,7 @@ export {
|
|||
ListChildren,
|
||||
ListObject,
|
||||
ListObjectManager,
|
||||
ListObjectPreview,
|
||||
ListNotification,
|
||||
|
||||
Header,
|
||||
|
@ -194,5 +199,8 @@ export {
|
|||
ShareBanner,
|
||||
FooterAuthDisclaimer,
|
||||
|
||||
EmailCollectionForm,
|
||||
EmailCollection,
|
||||
Floater,
|
||||
HeadSimple,
|
||||
EditorControls,
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React, { FC } from 'react';
|
||||
import { Menu } from 'Component';
|
||||
import { observer } from 'mobx-react';
|
||||
import { I, S } from 'Lib';
|
||||
|
@ -7,21 +7,17 @@ interface Props {
|
|||
history: any;
|
||||
};
|
||||
|
||||
const ListMenu = observer(class ListMenu extends React.Component<Props> {
|
||||
const ListMenu: FC<Props> = observer(() => {
|
||||
const { list } = S.Menu;
|
||||
|
||||
render () {
|
||||
const { list } = S.Menu;
|
||||
|
||||
return (
|
||||
<div className="menus">
|
||||
{list.map((item: I.Menu, i: number) => (
|
||||
<Menu {...this.props} key={item.id} {...item} />
|
||||
))}
|
||||
<div id="menu-polygon" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="menus">
|
||||
{list.map((item: I.Menu) => (
|
||||
<Menu key={item.id} {...item} />
|
||||
))}
|
||||
<div id="menu-polygon" />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default ListMenu;
|
|
@ -45,7 +45,7 @@ const ListObjectManager = observer(class ListObjectManager extends React.Compone
|
|||
offset = 0;
|
||||
cache: any = null;
|
||||
refList: List = null;
|
||||
refFilter: Filter = null;
|
||||
refFilter = null;
|
||||
refCheckbox: Map<string, any> = new Map();
|
||||
selected: string[] = [];
|
||||
timeout = 0;
|
||||
|
|
|
@ -53,7 +53,7 @@ class ListObjectPreview extends React.Component<Props> {
|
|||
|
||||
<div className="scroller">
|
||||
<div className="heading">
|
||||
<div className="name">Blank</div>
|
||||
<div className="name">{translate('commonBlank')}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border" />
|
||||
|
|
|
@ -1,27 +1,23 @@
|
|||
import * as React from 'react';
|
||||
import React, { FC, useEffect } from 'react';
|
||||
import $ from 'jquery';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Popup } from 'Component';
|
||||
import { I, S } from 'Lib';
|
||||
|
||||
const ListPopup = observer(class ListPopup extends React.Component<I.PageComponent> {
|
||||
const ListPopup: FC<I.PageComponent> = observer(() => {
|
||||
const { list } = S.Popup;
|
||||
|
||||
render () {
|
||||
const { list } = S.Popup;
|
||||
|
||||
return (
|
||||
<div className="popups">
|
||||
{list.map((item: I.Popup, i: number) => (
|
||||
<Popup {...this.props} key={i} {...item} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
componentDidUpdate () {
|
||||
useEffect(() => {
|
||||
$('body').toggleClass('overPopup', S.Popup.list.length > 0);
|
||||
};
|
||||
|
||||
}, [ list.length ]);
|
||||
|
||||
return (
|
||||
<div className="popups">
|
||||
{list.map((item: I.Popup, i: number) => (
|
||||
<Popup key={i} {...item} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default ListPopup;
|
|
@ -64,13 +64,6 @@ const MenuBlockLink = observer(class MenuBlockLink extends React.Component<I.Men
|
|||
|
||||
if (item.isSection) {
|
||||
content = <div className={[ 'sectionName', (param.index == 0 ? 'first' : '') ].join(' ')} style={param.style}>{item.name}</div>;
|
||||
} else
|
||||
if (item.isDiv) {
|
||||
content = (
|
||||
<div className="separator" style={param.style}>
|
||||
<div className="inner" />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
if ([ 'add', 'link' ].indexOf(item.itemId) >= 0) {
|
||||
cn.push(item.itemId);
|
||||
|
@ -96,6 +89,7 @@ const MenuBlockLink = observer(class MenuBlockLink extends React.Component<I.Men
|
|||
description={type ? type.name : undefined}
|
||||
style={param.style}
|
||||
iconSize={40}
|
||||
isDiv={item.isDiv}
|
||||
className={cn.join(' ')}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -51,41 +51,14 @@ const MenuBlockMention = observer(class MenuBlockMention extends React.Component
|
|||
};
|
||||
|
||||
const type = S.Record.getTypeById(item.type);
|
||||
const object = ![ 'add', 'selectDate' ].includes(item.id) ? item : null;
|
||||
const cn = [];
|
||||
|
||||
let content = null;
|
||||
if (item.isDiv) {
|
||||
content = (
|
||||
<div className="separator" style={param.style}>
|
||||
<div className="inner" />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
if (item.id == 'add') {
|
||||
cn.push('add');
|
||||
};
|
||||
if (item.isHidden) {
|
||||
cn.push('isHidden');
|
||||
};
|
||||
|
||||
let object = null;
|
||||
if (![ 'add', 'selectDate' ].includes(item.id)) {
|
||||
object = item;
|
||||
};
|
||||
|
||||
content = (
|
||||
<MenuItemVertical
|
||||
id={item.id}
|
||||
object={object}
|
||||
icon={item.icon}
|
||||
name={<ObjectName object={item} />}
|
||||
onMouseEnter={e => this.onOver(e, item)}
|
||||
onClick={e => this.onClick(e, item)}
|
||||
caption={type ? type.name : undefined}
|
||||
style={param.style}
|
||||
className={cn.join(' ')}
|
||||
/>
|
||||
);
|
||||
if (item.id == 'add') {
|
||||
cn.push('add');
|
||||
};
|
||||
if (item.isHidden) {
|
||||
cn.push('isHidden');
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -96,7 +69,18 @@ const MenuBlockMention = observer(class MenuBlockMention extends React.Component
|
|||
columnIndex={0}
|
||||
rowIndex={param.index}
|
||||
>
|
||||
{content}
|
||||
<MenuItemVertical
|
||||
id={item.id}
|
||||
object={object}
|
||||
icon={item.icon}
|
||||
name={<ObjectName object={item} />}
|
||||
onMouseEnter={e => this.onOver(e, item)}
|
||||
onClick={e => this.onClick(e, item)}
|
||||
caption={type?.name}
|
||||
style={param.style}
|
||||
isDiv={item.isDiv}
|
||||
className={cn.join(' ')}
|
||||
/>
|
||||
</CellMeasurer>
|
||||
);
|
||||
};
|
||||
|
@ -281,7 +265,6 @@ const MenuBlockMention = observer(class MenuBlockMention extends React.Component
|
|||
fullText: filter,
|
||||
offset: this.offset,
|
||||
limit: J.Constant.limit.menuRecords,
|
||||
skipLayoutFormat: [ I.ObjectLayout.Date ],
|
||||
}, (message: any) => {
|
||||
if (message.error.code) {
|
||||
this.setState({ isLoading: false });
|
||||
|
@ -370,6 +353,7 @@ const MenuBlockMention = observer(class MenuBlockMention extends React.Component
|
|||
canEdit: true,
|
||||
canClear: false,
|
||||
value: U.Date.now(),
|
||||
relationKey: J.Relation.key.mention,
|
||||
onChange: (value: number) => {
|
||||
C.ObjectDateByTimestamp(space, value, (message: any) => {
|
||||
if (!message.error.code) {
|
||||
|
|
|
@ -1,59 +1,18 @@
|
|||
import * as React from 'react';
|
||||
import React, { forwardRef } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { MenuItemVertical } from 'Component';
|
||||
import { I } from 'Lib';
|
||||
|
||||
const MenuButton = observer(class MenuButton extends React.Component<I.Menu> {
|
||||
const MenuButton = observer(forwardRef<I.MenuRef, I.Menu>((props, ref) => {
|
||||
const { param, close } = props;
|
||||
const { data } = param;
|
||||
const { disabled, onSelect, noClose } = data;
|
||||
|
||||
_isMounted = false;
|
||||
|
||||
constructor (props: I.Menu) {
|
||||
super(props);
|
||||
|
||||
this.onSelect = this.onSelect.bind(this);
|
||||
const getItems = () => {
|
||||
return props.param.data.options || [];
|
||||
};
|
||||
|
||||
render () {
|
||||
const { param } = this.props;
|
||||
const { data } = param;
|
||||
const { disabled } = data;
|
||||
const items = this.getItems();
|
||||
|
||||
return (
|
||||
<div className="items">
|
||||
{items.map((item: any, i: number) => (
|
||||
<MenuItemVertical
|
||||
key={i}
|
||||
{...item}
|
||||
className={disabled ? 'disabled' : ''}
|
||||
onClick={e => this.onSelect(e, item)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
this._isMounted = true;
|
||||
};
|
||||
|
||||
componentWillUnmount () {
|
||||
this._isMounted = false;
|
||||
};
|
||||
|
||||
getItems () {
|
||||
const { param } = this.props;
|
||||
const { data } = param;
|
||||
const { options } = data;
|
||||
|
||||
return options || [];
|
||||
};
|
||||
|
||||
onSelect (e: any, item: any) {
|
||||
const { param, close } = this.props;
|
||||
const { data } = param;
|
||||
const { disabled, onSelect, noClose } = data;
|
||||
|
||||
const onClick = (e: any, item: any) => {
|
||||
if (!noClose) {
|
||||
close();
|
||||
};
|
||||
|
@ -63,6 +22,20 @@ const MenuButton = observer(class MenuButton extends React.Component<I.Menu> {
|
|||
};
|
||||
};
|
||||
|
||||
});
|
||||
const items = getItems();
|
||||
|
||||
return (
|
||||
<div className="items">
|
||||
{items.map((item: any, i: number) => (
|
||||
<MenuItemVertical
|
||||
key={i}
|
||||
{...item}
|
||||
className={disabled ? 'disabled' : ''}
|
||||
onClick={e => onClick(e, item)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}));
|
||||
|
||||
export default MenuButton;
|
|
@ -1,18 +1,27 @@
|
|||
import * as React from 'react';
|
||||
import { I, S, U, translate } from 'Lib';
|
||||
import { I, S, U, J, translate } from 'Lib';
|
||||
import { Select } from 'Component';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
const MenuCalendar = observer(class MenuCalendar extends React.Component<I.Menu> {
|
||||
interface State {
|
||||
dotMap: Map<string, boolean>;
|
||||
};
|
||||
|
||||
const MenuCalendar = observer(class MenuCalendar extends React.Component<I.Menu, State> {
|
||||
|
||||
originalValue = 0;
|
||||
refMonth: any = null;
|
||||
refYear: any = null;
|
||||
|
||||
state: Readonly<State> = {
|
||||
dotMap: new Map(),
|
||||
};
|
||||
|
||||
render () {
|
||||
const { param } = this.props;
|
||||
const { data, classNameWrap } = param;
|
||||
const { value, isEmpty, canEdit, canClear = true } = data;
|
||||
const { dotMap } = this.state;
|
||||
const items = this.getData();
|
||||
const { m, y } = U.Date.getCalendarDateParam(value);
|
||||
const todayParam = U.Date.getCalendarDateParam(this.originalValue);
|
||||
|
@ -91,15 +100,20 @@ const MenuCalendar = observer(class MenuCalendar extends React.Component<I.Menu>
|
|||
if (!isEmpty && (todayParam.d == item.d) && (todayParam.m == item.m) && (todayParam.y == item.y)) {
|
||||
cn.push('active');
|
||||
};
|
||||
|
||||
const check = dotMap.get([ item.d, item.m, item.y ].join('-'));
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
key={i}
|
||||
id={[ 'day', item.d, item.m, item.y ].join('-')}
|
||||
className={cn.join(' ')}
|
||||
onClick={e => this.onClick(e, item)}
|
||||
onContextMenu={e => this.onContextMenu(e, item)}
|
||||
>
|
||||
{item.d}
|
||||
<div className="inner">
|
||||
{item.d}
|
||||
{check ? <div className="bullet" /> : ''}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
@ -130,6 +144,7 @@ const MenuCalendar = observer(class MenuCalendar extends React.Component<I.Menu>
|
|||
const { value } = data;
|
||||
|
||||
this.originalValue = value;
|
||||
this.initDotMap();
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
|
@ -145,17 +160,35 @@ const MenuCalendar = observer(class MenuCalendar extends React.Component<I.Menu>
|
|||
this.props.position();
|
||||
};
|
||||
|
||||
initDotMap () {
|
||||
const { param } = this.props;
|
||||
const { data } = param;
|
||||
const { getDotMap } = data;
|
||||
const items = this.getData();
|
||||
|
||||
if (!getDotMap || !items.length) {
|
||||
return;
|
||||
};
|
||||
|
||||
const first = items[0];
|
||||
const last = items[items.length - 1];
|
||||
const start = U.Date.timestamp(first.y, first.m, first.d);
|
||||
const end = U.Date.timestamp(last.y, last.m, last.d);
|
||||
|
||||
getDotMap(start, end, dotMap => this.setState({ dotMap }));
|
||||
};
|
||||
|
||||
onClick (e: any, item: any) {
|
||||
e.stopPropagation();
|
||||
|
||||
const { param } = this.props;
|
||||
const { data } = param;
|
||||
const { canEdit } = data;
|
||||
const { canEdit, relationKey } = data;
|
||||
|
||||
if (canEdit) {
|
||||
this.setValue(U.Date.timestamp(item.y, item.m, item.d), true, true);
|
||||
} else {
|
||||
U.Object.openDateByTimestamp(U.Date.timestamp(item.y, item.m, item.d));
|
||||
U.Object.openDateByTimestamp(relationKey, U.Date.timestamp(item.y, item.m, item.d));
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -163,7 +196,8 @@ const MenuCalendar = observer(class MenuCalendar extends React.Component<I.Menu>
|
|||
e.preventDefault();
|
||||
|
||||
const { getId, param } = this.props;
|
||||
const { className, classNameWrap } = param;
|
||||
const { className, classNameWrap, data } = param;
|
||||
const { relationKey } = data;
|
||||
|
||||
S.Menu.open('select', {
|
||||
element: `#${getId()} #${[ 'day', item.d, item.m, item.y ].join('-')}`,
|
||||
|
@ -176,7 +210,7 @@ const MenuCalendar = observer(class MenuCalendar extends React.Component<I.Menu>
|
|||
{ id: 'open', icon: 'expand', name: translate('commonOpenObject') },
|
||||
],
|
||||
onSelect: () => {
|
||||
U.Object.openDateByTimestamp(U.Date.timestamp(item.y, item.m, item.d));
|
||||
U.Object.openDateByTimestamp(relationKey, U.Date.timestamp(item.y, item.m, item.d));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -10,7 +10,8 @@ const MenuCalendarDay = observer(class MenuCalendarDay extends React.Component<I
|
|||
render () {
|
||||
const { param, getId } = this.props;
|
||||
const { data } = param;
|
||||
const { y, m, d, hideIcon, className, fromWidget, readonly, onCreate } = data;
|
||||
const { y, m, d, hideIcon, className, fromWidget, relationKey, readonly, onCreate } = data;
|
||||
const timestamp = U.Date.timestamp(y, m, d);
|
||||
const items = this.getItems();
|
||||
const cn = [ 'wrap' ];
|
||||
const menuId = getId();
|
||||
|
@ -19,7 +20,7 @@ const MenuCalendarDay = observer(class MenuCalendarDay extends React.Component<I
|
|||
let size = 16;
|
||||
|
||||
if (fromWidget) {
|
||||
const w = Number(U.Date.date('N', U.Date.timestamp(y, m, d))) + 1;
|
||||
const w = Number(U.Date.date('N', timestamp)) + 1;
|
||||
label = `${translate(`day${w}`)} ${d}`;
|
||||
size = 18;
|
||||
};
|
||||
|
@ -60,7 +61,7 @@ const MenuCalendarDay = observer(class MenuCalendarDay extends React.Component<I
|
|||
|
||||
return (
|
||||
<div className={cn.join(' ')}>
|
||||
<div className="number">
|
||||
<div className="number" onClick={() => U.Object.openDateByTimestamp(relationKey, timestamp, 'config')}>
|
||||
<div className="inner">{label}</div>
|
||||
</div>
|
||||
<div className="items">
|
||||
|
|
|
@ -21,25 +21,15 @@ class MenuContext extends React.Component<I.Menu> {
|
|||
<div id={'section-' + item.id} className="section">
|
||||
{item.name ? <div className="name">{item.name}</div> : ''}
|
||||
<div className="items">
|
||||
{item.children.map((action: any, i: number) => {
|
||||
if (action.isDiv) {
|
||||
return (
|
||||
<div key={i} className="separator">
|
||||
<div className="inner" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<MenuItemVertical
|
||||
key={i}
|
||||
{...action}
|
||||
icon={action.icon || action.id}
|
||||
onMouseEnter={e => this.onMouseEnter(e, action)}
|
||||
onClick={e => this.onClick(e, action)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{item.children.map((action: any, i: number) => (
|
||||
<MenuItemVertical
|
||||
key={i}
|
||||
{...action}
|
||||
icon={action.icon || action.id}
|
||||
onMouseEnter={e => this.onMouseEnter(e, action)}
|
||||
onClick={e => this.onClick(e, action)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -58,27 +58,6 @@ const MenuDataviewFileList = observer(class MenuDataviewFileList extends React.C
|
|||
|
||||
const type = S.Record.getTypeById(item.type);
|
||||
|
||||
let content = null;
|
||||
if (item.isDiv) {
|
||||
content = (
|
||||
<div className="separator" style={param.style}>
|
||||
<div className="inner" />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<MenuItemVertical
|
||||
id={item.id}
|
||||
object={item}
|
||||
name={item.name}
|
||||
onMouseEnter={e => this.onOver(e, item)}
|
||||
onClick={e => this.onClick(e, item)}
|
||||
caption={type ? type.name : undefined}
|
||||
style={param.style}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<CellMeasurer
|
||||
key={param.key}
|
||||
|
@ -87,7 +66,15 @@ const MenuDataviewFileList = observer(class MenuDataviewFileList extends React.C
|
|||
columnIndex={0}
|
||||
rowIndex={param.index}
|
||||
>
|
||||
{content}
|
||||
<MenuItemVertical
|
||||
{...item}
|
||||
object={item}
|
||||
name={item.name}
|
||||
onMouseEnter={e => this.onOver(e, item)}
|
||||
onClick={e => this.onClick(e, item)}
|
||||
caption={type?.name}
|
||||
style={param.style}
|
||||
/>
|
||||
</CellMeasurer>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -629,7 +629,10 @@ const MenuDataviewFilterValues = observer(class MenuDataviewFilterValues extends
|
|||
};
|
||||
|
||||
onCalendar (value: number) {
|
||||
const { getId } = this.props;
|
||||
const { getId, param } = this.props;
|
||||
const { data } = param;
|
||||
const { getView, itemId } = data;
|
||||
const item = getView().getFilter(itemId);
|
||||
|
||||
S.Menu.open('dataviewCalendar', {
|
||||
element: `#${getId()} #value`,
|
||||
|
@ -638,6 +641,7 @@ const MenuDataviewFilterValues = observer(class MenuDataviewFilterValues extends
|
|||
rebind: this.rebind,
|
||||
value,
|
||||
canEdit: true,
|
||||
relationKey: item.relationKey,
|
||||
onChange: (value: number) => {
|
||||
this.onChange('value', value);
|
||||
},
|
||||
|
|
|
@ -341,7 +341,7 @@ const MenuRelationEdit = observer(class MenuRelationEdit extends React.Component
|
|||
return;
|
||||
};
|
||||
|
||||
const options = Relation.formulaByType(relation.format).filter(it => it.section == item.id);
|
||||
const options = Relation.formulaByType(relation.relationKey, relation.format).filter(it => it.section == item.id);
|
||||
|
||||
S.Menu.closeAll([ 'select2' ], () => {
|
||||
S.Menu.open('select2', {
|
||||
|
|
|
@ -45,8 +45,8 @@ const MenuSort = observer(class MenuSort extends React.Component<I.Menu> {
|
|||
const sortCnt = items.length;
|
||||
|
||||
const typeOptions = [
|
||||
{ id: String(I.SortType.Asc), name: translate('commonAscending') },
|
||||
{ id: String(I.SortType.Desc), name: translate('commonDescending') },
|
||||
{ id: I.SortType.Asc, name: translate('commonAscending') },
|
||||
{ id: I.SortType.Desc, name: translate('commonDescending') },
|
||||
];
|
||||
|
||||
const Handle = SortableHandle(() => (
|
||||
|
|
|
@ -243,7 +243,7 @@ const MenuViewSettings = observer(class MenuViewSettings extends React.Component
|
|||
const layoutSettings = [
|
||||
{ id: 'layout', name: translate('menuDataviewObjectTypeEditLayout'), subComponent: 'dataviewViewLayout', caption: Dataview.defaultViewName(type) },
|
||||
isBoard ? { id: 'group', name: translate('libDataviewGroups'), subComponent: 'dataviewGroupList' } : null,
|
||||
{ id: 'relations', name: translate('libDataviewRelations'), subComponent: 'dataviewRelationList', caption: relationCnt.join(', ') },
|
||||
{ id: 'relations', name: translate('commonRelations'), subComponent: 'dataviewRelationList', caption: relationCnt.join(', ') },
|
||||
];
|
||||
const tools = [
|
||||
{ id: 'filter', name: translate('menuDataviewViewFilter'), subComponent: 'dataviewFilterList', caption: filterCnt ? U.Common.sprintf(translate('menuDataviewViewApplied'), filterCnt) : '' },
|
||||
|
|
|
@ -1,72 +1,22 @@
|
|||
import * as React from 'react';
|
||||
import React, { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
|
||||
import { MenuItemVertical, Button, ShareTooltip } from 'Component';
|
||||
import { I, S, U, J, Onboarding, keyboard, analytics, Action, Highlight, Storage, translate, Preview } from 'Lib';
|
||||
import { I, S, U, J, keyboard, analytics, Action, Highlight, translate } from 'Lib';
|
||||
|
||||
class MenuHelp extends React.Component<I.Menu> {
|
||||
const MenuHelp = forwardRef<I.MenuRef, I.Menu>((props, ref) => {
|
||||
const { setActive, close, getId, onKeyDown } = props;
|
||||
const n = useRef(-1);
|
||||
|
||||
n = -1;
|
||||
|
||||
constructor (props: I.Menu) {
|
||||
super(props);
|
||||
|
||||
this.onClick = this.onClick.bind(this);
|
||||
};
|
||||
|
||||
render () {
|
||||
const items = this.getItems();
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="items">
|
||||
{items.map((item: any, i: number) => {
|
||||
let content = null;
|
||||
|
||||
if (item.isDiv) {
|
||||
content = (
|
||||
<div key={i} className="separator">
|
||||
<div className="inner" />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<MenuItemVertical
|
||||
key={i}
|
||||
{...item}
|
||||
onMouseEnter={e => this.onMouseEnter(e, item)}
|
||||
onClick={e => this.onClick(e, item)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return content;
|
||||
})}
|
||||
</div>
|
||||
<ShareTooltip />
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
this.rebind();
|
||||
Preview.shareTooltipHide();
|
||||
Highlight.showAll();
|
||||
};
|
||||
|
||||
componentWillUnmount () {
|
||||
this.unbind();
|
||||
};
|
||||
|
||||
rebind () {
|
||||
this.unbind();
|
||||
$(window).on('keydown.menu', e => this.props.onKeyDown(e));
|
||||
window.setTimeout(() => this.props.setActive(), 15);
|
||||
const rebind = () => {
|
||||
unbind();
|
||||
$(window).on('keydown.menu', e => onKeyDown(e));
|
||||
window.setTimeout(() => setActive(), 15);
|
||||
};
|
||||
|
||||
unbind () {
|
||||
const unbind = () => {
|
||||
$(window).off('keydown.menu');
|
||||
};
|
||||
|
||||
getItems () {
|
||||
const getItems = () => {
|
||||
const btn = <Button className="c16" text={U.Common.getElectron().version.app} />;
|
||||
|
||||
return [
|
||||
|
@ -84,17 +34,13 @@ class MenuHelp extends React.Component<I.Menu> {
|
|||
].map(it => ({ ...it, name: translate(U.Common.toCamelCase(`menuHelp-${it.id}`)) }));
|
||||
};
|
||||
|
||||
onMouseEnter (e: any, item: any) {
|
||||
const onMouseEnter = (e: any, item: any) => {
|
||||
if (!keyboard.isMouseDisabled) {
|
||||
this.props.setActive(item, false);
|
||||
setActive(item, false);
|
||||
};
|
||||
};
|
||||
|
||||
onClick (e: any, item: any) {
|
||||
const { getId, close } = this.props;
|
||||
const isGraph = keyboard.isMainGraph();
|
||||
const home = U.Space.getDashboard();
|
||||
|
||||
const onClick = (e: any, item: any) => {
|
||||
close();
|
||||
analytics.event(U.Common.toUpperCamelCase([ getId(), item.id ].join('-')), { route: analytics.route.menuHelp });
|
||||
|
||||
|
@ -134,6 +80,40 @@ class MenuHelp extends React.Component<I.Menu> {
|
|||
|
||||
};
|
||||
|
||||
};
|
||||
const items = getItems();
|
||||
|
||||
export default MenuHelp;
|
||||
useEffect(() => {
|
||||
rebind();
|
||||
S.Common.shareTooltipSet(true);
|
||||
Highlight.showAll();
|
||||
return () => unbind();
|
||||
}, []);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
rebind,
|
||||
unbind,
|
||||
getItems,
|
||||
getIndex: () => n.current,
|
||||
setIndex: (i: number) => n.current = i,
|
||||
}), []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="items">
|
||||
{items.map((item: any, i: number) => (
|
||||
<MenuItemVertical
|
||||
key={i}
|
||||
{...item}
|
||||
onMouseEnter={e => onMouseEnter(e, item)}
|
||||
onClick={e => onClick(e, item)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<ShareTooltip route={analytics.route.menuHelp} />
|
||||
</>
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
export default MenuHelp;
|
|
@ -745,6 +745,22 @@ const Menu = observer(class Menu extends React.Component<I.Menu, State> {
|
|||
};
|
||||
};
|
||||
|
||||
getIndex (): number {
|
||||
if (!this.ref) {
|
||||
return -1;
|
||||
};
|
||||
|
||||
return this.ref.getIndex ? this.ref.getIndex() : this.ref.n;
|
||||
};
|
||||
|
||||
setIndex (n: number) {
|
||||
if (!this.ref) {
|
||||
return;
|
||||
};
|
||||
|
||||
this.ref.setIndex ? this.ref.setIndex(n) : this.ref.n = n;
|
||||
};
|
||||
|
||||
onKeyDown (e: any) {
|
||||
if (!this.ref || !this.ref.getItems || keyboard.isComposition) {
|
||||
return;
|
||||
|
@ -759,17 +775,18 @@ const Menu = observer(class Menu extends React.Component<I.Menu, State> {
|
|||
const refInput = this.ref.refFilter || this.ref.refName;
|
||||
const shortcutClose = [ 'escape' ];
|
||||
const shortcutSelect = [ 'tab', 'enter' ];
|
||||
|
||||
|
||||
let index = this.getIndex();
|
||||
let ret = false;
|
||||
|
||||
if (refInput) {
|
||||
if (refInput.isFocused && (this.ref.n < 0)) {
|
||||
if (refInput.isFocused && (index < 0)) {
|
||||
keyboard.shortcut('arrowleft, arrowright', e, () => ret = true);
|
||||
|
||||
keyboard.shortcut('arrowdown', e, () => {
|
||||
refInput.blur();
|
||||
|
||||
this.ref.n = 0;
|
||||
this.setIndex(0);
|
||||
this.setActive(null, true);
|
||||
|
||||
ret = true;
|
||||
|
@ -793,7 +810,7 @@ const Menu = observer(class Menu extends React.Component<I.Menu, State> {
|
|||
return;
|
||||
};
|
||||
|
||||
this.ref.n = this.ref.getItems().length - 1;
|
||||
this.setIndex(this.ref.getItems().length - 1);
|
||||
this.setActive(null, true);
|
||||
|
||||
refInput.blur();
|
||||
|
@ -801,9 +818,10 @@ const Menu = observer(class Menu extends React.Component<I.Menu, State> {
|
|||
});
|
||||
} else {
|
||||
keyboard.shortcut('arrowup', e, () => {
|
||||
if (!this.ref.n) {
|
||||
this.ref.n = -1;
|
||||
if (index < 0) {
|
||||
refInput.focus();
|
||||
|
||||
this.setIndex(-1);
|
||||
this.setActive(null, true);
|
||||
|
||||
ret = true;
|
||||
|
@ -832,25 +850,29 @@ const Menu = observer(class Menu extends React.Component<I.Menu, State> {
|
|||
|
||||
const items = this.ref.getItems();
|
||||
const l = items.length;
|
||||
const item = items[this.ref.n];
|
||||
|
||||
index = this.getIndex();
|
||||
|
||||
const item = items[index];
|
||||
const onArrow = (dir: number) => {
|
||||
this.ref.n += dir;
|
||||
index += dir;
|
||||
|
||||
if (this.ref.n < 0) {
|
||||
if ((this.ref.n == -1) && refInput) {
|
||||
this.ref.n = -1;
|
||||
if (index < 0) {
|
||||
if ((index == -1) && refInput) {
|
||||
index = -1;
|
||||
refInput.focus();
|
||||
} else {
|
||||
this.ref.n = l - 1;
|
||||
index = l - 1;
|
||||
};
|
||||
};
|
||||
|
||||
if (this.ref.n > l - 1) {
|
||||
this.ref.n = 0;
|
||||
if (index > l - 1) {
|
||||
index = 0;
|
||||
};
|
||||
|
||||
const item = items[this.ref.n];
|
||||
this.setIndex(index);
|
||||
|
||||
const item = items[index];
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
|
@ -899,7 +921,7 @@ const Menu = observer(class Menu extends React.Component<I.Menu, State> {
|
|||
keyboard.shortcut('backspace', e, () => {
|
||||
e.preventDefault();
|
||||
|
||||
this.ref.n--;
|
||||
this.setIndex(index - 1);
|
||||
this.checkIndex();
|
||||
this.ref.onRemove(e, item);
|
||||
this.setActive(null, true);
|
||||
|
@ -917,18 +939,21 @@ const Menu = observer(class Menu extends React.Component<I.Menu, State> {
|
|||
};
|
||||
|
||||
onSortMove (dir: number) {
|
||||
const n = this.ref.n;
|
||||
const index = this.getIndex();
|
||||
|
||||
this.ref.n = n + dir;
|
||||
this.setIndex(index + dir);
|
||||
this.checkIndex();
|
||||
this.ref.onSortEnd({ oldIndex: n, newIndex: this.ref.n });
|
||||
this.ref.onSortEnd({ oldIndex: index, newIndex: index + dir });
|
||||
};
|
||||
|
||||
checkIndex () {
|
||||
const items = this.ref.getItems();
|
||||
|
||||
let index = this.getIndex();
|
||||
index = Math.max(0, index);
|
||||
index = Math.min(items.length - 1, index);
|
||||
|
||||
this.ref.n = Math.max(0, this.ref.n);
|
||||
this.ref.n = Math.min(items.length - 1, this.ref.n);
|
||||
this.setIndex(index);
|
||||
};
|
||||
|
||||
setActive (item?: any, scroll?: boolean) {
|
||||
|
@ -937,36 +962,39 @@ const Menu = observer(class Menu extends React.Component<I.Menu, State> {
|
|||
};
|
||||
|
||||
const refInput = this.ref.refFilter || this.ref.refName;
|
||||
if ((this.ref.n == -1) && refInput) {
|
||||
|
||||
let index = this.getIndex();
|
||||
if ((index < 0) && refInput) {
|
||||
refInput.focus();
|
||||
};
|
||||
|
||||
const items = this.ref.getItems();
|
||||
if (item && (undefined !== item.id)) {
|
||||
this.ref.n = items.findIndex(it => it.id == item.id);
|
||||
index = items.findIndex(it => it.id == item.id);
|
||||
};
|
||||
|
||||
if (this.ref.refList && scroll) {
|
||||
let idx = this.ref.n;
|
||||
if (this.ref.recalcIndex) {
|
||||
idx = this.ref.recalcIndex();
|
||||
index = this.ref.recalcIndex();
|
||||
};
|
||||
this.ref.refList.scrollToRow(Math.max(0, idx));
|
||||
this.ref.refList.scrollToRow(Math.max(0, index));
|
||||
};
|
||||
|
||||
const next = items[this.ref.n];
|
||||
const next = items[index];
|
||||
if (!next) {
|
||||
return;
|
||||
};
|
||||
|
||||
if (next.isDiv || next.isSection || next.isEmpty) {
|
||||
this.ref.n++;
|
||||
if (items[this.ref.n]) {
|
||||
this.setActive(items[this.ref.n], scroll);
|
||||
index++;
|
||||
if (items[index]) {
|
||||
this.setActive(items[index], scroll);
|
||||
};
|
||||
} else {
|
||||
this.setHover(next, scroll);
|
||||
};
|
||||
|
||||
this.setIndex(index);
|
||||
};
|
||||
|
||||
setHover (item?: any, scroll?: boolean) {
|
||||
|
|
|
@ -9,13 +9,16 @@ class MenuItemVertical extends React.Component<I.MenuItem> {
|
|||
|
||||
render () {
|
||||
const {
|
||||
id, icon, object, inner, name, description, caption, color, arrow, checkbox, isActive, withDescription, withSwitch, withSelect, withMore,
|
||||
icon, object, inner, name, description, caption, color, arrow, checkbox, isActive, withDescription, withSwitch, withSelect, withMore,
|
||||
className, style, iconSize, switchValue, selectValue, options, readonly, onClick, onSwitch, onSelect, onMouseEnter, onMouseLeave, onMore,
|
||||
selectMenuParam, subComponent, note, sortArrow
|
||||
selectMenuParam, subComponent, note, sortArrow, isDiv,
|
||||
} = this.props;
|
||||
const cn = [ 'item' ];
|
||||
const id = this.props.id || '';
|
||||
const cn = [];
|
||||
const withArrow = arrow || subComponent;
|
||||
|
||||
isDiv ? cn.push('separator') : cn.push('item');
|
||||
|
||||
let hasClick = true;
|
||||
let iconMainElement = null;
|
||||
let iconSideElement = null;
|
||||
|
@ -93,6 +96,9 @@ class MenuItemVertical extends React.Component<I.MenuItem> {
|
|||
};
|
||||
|
||||
let content = null;
|
||||
if (isDiv) {
|
||||
content = <div className="inner" />;
|
||||
} else
|
||||
if (withDescription) {
|
||||
content = (
|
||||
<React.Fragment>
|
||||
|
@ -158,7 +164,7 @@ class MenuItemVertical extends React.Component<I.MenuItem> {
|
|||
return (
|
||||
<div
|
||||
ref={node => this.node = node}
|
||||
id={'item-' + id}
|
||||
id={`item-${id}`}
|
||||
className={cn.join(' ')}
|
||||
onMouseDown={hasClick ? onClick : undefined}
|
||||
onMouseEnter={onMouseEnter}
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react';
|
|||
import $ from 'jquery';
|
||||
import raf from 'raf';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Button, Icon, Label, EmailCollectionForm } from 'Component';
|
||||
import { Button, Icon, Label, EmailCollection } from 'Component';
|
||||
import { I, C, S, U, J, Onboarding, analytics, keyboard, translate } from 'Lib';
|
||||
import ReactCanvasConfetti from 'react-canvas-confetti';
|
||||
|
||||
|
@ -90,7 +90,7 @@ const MenuOnboarding = observer(class MenuSelect extends React.Component<I.Menu,
|
|||
/>
|
||||
) : ''}
|
||||
{withEmailForm ? (
|
||||
<EmailCollectionForm onStepChange={position} onComplete={() => close()} />
|
||||
<EmailCollection onStepChange={position} onComplete={() => close()} />
|
||||
) : ''}
|
||||
|
||||
<div className={[ 'bottom', withSteps ? 'withSteps' : '' ].join(' ')}>
|
||||
|
|
|
@ -1,50 +1,44 @@
|
|||
import * as React from 'react';
|
||||
import React, { forwardRef, useEffect } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { ObjectName, ObjectDescription, Label, IconObject, EmptySearch } from 'Component';
|
||||
import { I, U, translate } from 'Lib';
|
||||
|
||||
const MenuParticipant = observer(class MenuParticipant extends React.Component<I.Menu> {
|
||||
const MenuParticipant = observer(forwardRef<I.MenuRef, I.Menu>((props: I.Menu, ref: any) => {
|
||||
useEffect(() => load(), []);
|
||||
|
||||
render () {
|
||||
const object = this.getObject();
|
||||
|
||||
if (!object) {
|
||||
return <EmptySearch text={translate('commonNotFound')} />;
|
||||
};
|
||||
|
||||
const relationKey = object.globalName ? 'globalName': 'identity';
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<IconObject object={object} size={96} />
|
||||
<ObjectName object={object} />
|
||||
<Label text={U.Common.shorten(object[relationKey], 150)} />
|
||||
<ObjectDescription object={object} />
|
||||
</React.Fragment>
|
||||
);
|
||||
const getObject = () => {
|
||||
return props.param.data.object;
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
this.load();
|
||||
const setObject = (object: any) => {
|
||||
props.param.data.object = object;
|
||||
};
|
||||
|
||||
getObject () {
|
||||
return this.props.param.data.object;
|
||||
};
|
||||
|
||||
load () {
|
||||
const object = this.getObject();
|
||||
const load = () => {
|
||||
const object = getObject();
|
||||
if (!object) {
|
||||
return;
|
||||
};
|
||||
|
||||
U.Object.getById(object.id, { keys: U.Data.participantRelationKeys() }, (object: any) => {
|
||||
if (object) {
|
||||
this.props.param.data.object = object;
|
||||
setObject(object);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
const object = getObject();
|
||||
const relationKey = object.globalName ? 'globalName': 'identity';
|
||||
|
||||
return object ? (
|
||||
<>
|
||||
<IconObject object={object} size={96} />
|
||||
<ObjectName object={object} />
|
||||
<Label text={U.Common.shorten(object[relationKey], 150)} />
|
||||
<ObjectDescription object={object} />
|
||||
</>
|
||||
) : <EmptySearch text={translate('commonNotFound')} />;
|
||||
|
||||
}));
|
||||
|
||||
export default MenuParticipant;
|
|
@ -68,13 +68,6 @@ const MenuRelationSuggest = observer(class MenuRelationSuggest extends React.Com
|
|||
</div>
|
||||
);
|
||||
} else
|
||||
if (item.isDiv) {
|
||||
content = (
|
||||
<div className="separator" style={param.style}>
|
||||
<div className="inner" />
|
||||
</div>
|
||||
);
|
||||
} else
|
||||
if (item.isSection) {
|
||||
content = <div className={[ 'sectionName', (param.index == 0 ? 'first' : '') ].join(' ')} style={param.style}>{item.name}</div>;
|
||||
} else {
|
||||
|
|
|
@ -70,13 +70,6 @@ const MenuSearchObject = observer(class MenuSearchObject extends React.Component
|
|||
|
||||
if (item.isSection) {
|
||||
content = <div className={[ 'sectionName', (param.index == 0 ? 'first' : '') ].join(' ')} style={param.style}>{item.name}</div>;
|
||||
} else
|
||||
if (item.isDiv) {
|
||||
content = (
|
||||
<div className="separator" style={param.style}>
|
||||
<div className="inner" />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
const props = {
|
||||
...item,
|
||||
|
|
|
@ -55,13 +55,6 @@ const MenuSelect = observer(class MenuSelect extends React.Component<I.Menu> {
|
|||
|
||||
content = <div className={cn.join(' ')} style={item.style}>{item.name}</div>;
|
||||
} else
|
||||
if (item.isDiv) {
|
||||
content = (
|
||||
<div className="separator" style={item.style}>
|
||||
<div className="inner" />
|
||||
</div>
|
||||
);
|
||||
} else
|
||||
if (item.id == 'add') {
|
||||
content = (
|
||||
<div
|
||||
|
|
|
@ -640,7 +640,7 @@ const MenuSmile = observer(class MenuSmile extends React.Component<I.Menu, State
|
|||
|
||||
const { close } = this.props;
|
||||
const { tab } = this.state;
|
||||
const checkFilter = () => this.refFilter && this.refFilter.isFocused;
|
||||
const checkFilter = () => this.refFilter && this.refFilter.isFocused();
|
||||
|
||||
e.stopPropagation();
|
||||
keyboard.disableMouse(true);
|
||||
|
|
|
@ -68,13 +68,6 @@ const MenuTypeSuggest = observer(class MenuTypeSuggest extends React.Component<I
|
|||
</div>
|
||||
);
|
||||
} else
|
||||
if (item.isDiv) {
|
||||
content = (
|
||||
<div className="separator" style={param.style}>
|
||||
<div className="inner" />
|
||||
</div>
|
||||
);
|
||||
} else
|
||||
if (item.isSection) {
|
||||
content = <div className={[ 'sectionName', (param.index == 0 ? 'first' : '') ].join(' ')} style={param.style}>{item.name}</div>;
|
||||
} else {
|
||||
|
|
|
@ -282,7 +282,7 @@ const MenuWidget = observer(class MenuWidget extends React.Component<I.Menu> {
|
|||
menuId = 'select';
|
||||
menuParam.width = 320;
|
||||
menuParam.data = Object.assign(menuParam.data, {
|
||||
options: U.Menu.getWidgetLayoutOptions(this.target?.id, this.target?.layout),
|
||||
options: U.Menu.prepareForSelect(U.Menu.getWidgetLayoutOptions(this.target?.id, this.target?.layout)),
|
||||
value: this.layout,
|
||||
onSelect: (e, option) => {
|
||||
this.layout = Number(option.id);
|
||||
|
|
|
@ -117,6 +117,7 @@ const PageAuthLogin = observer(class PageAuthLogin extends React.Component<I.Pag
|
|||
|
||||
const { mode, path } = networkConfig;
|
||||
const account = accounts[0];
|
||||
const { shareTooltip } = S.Common;
|
||||
|
||||
S.Auth.accountSet(account);
|
||||
Renderer.send('keytarSet', account.id, this.refPhrase.getValue());
|
||||
|
@ -131,12 +132,12 @@ const PageAuthLogin = observer(class PageAuthLogin extends React.Component<I.Pag
|
|||
S.Common.configSet(message.account.config, false);
|
||||
|
||||
const spaceId = Storage.get('spaceId');
|
||||
const shareTooltip = Storage.get('shareTooltip');
|
||||
const routeParam = {
|
||||
replace: true,
|
||||
onRouteChange: () => {
|
||||
if (!shareTooltip) {
|
||||
Preview.shareTooltipShow();
|
||||
S.Common.shareTooltipSet(false);
|
||||
analytics.event('OnboardingTooltip', { id: 'ShareApp' });
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
|
@ -211,8 +211,8 @@ const PageAuthOnboard = observer(class PageAuthOnboard extends React.Component<I
|
|||
if (stage == Stage.Vault) {
|
||||
const cb = () => {
|
||||
Animation.from(() => {
|
||||
this.setState({ stage: stage + 1 });
|
||||
this.refNext?.setLoading(false);
|
||||
this.setState({ stage: stage + 1 });
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -9,8 +9,10 @@ interface Props {
|
|||
isContextMenuDisabled?: boolean;
|
||||
readonly?: boolean;
|
||||
noIcon?: boolean;
|
||||
relationKey?: string;
|
||||
onCreate?: () => void;
|
||||
onEdit?: () => void;
|
||||
getDotMap?: (start: number, end: number, callback: (res: Map<string, boolean>) => void) => void;
|
||||
};
|
||||
|
||||
const EDITORS = [
|
||||
|
@ -305,7 +307,7 @@ const HeadSimple = observer(class Controls extends React.Component<Props> {
|
|||
};
|
||||
|
||||
onCalendar = () => {
|
||||
const { rootId } = this.props;
|
||||
const { rootId, getDotMap, relationKey } = this.props;
|
||||
const object = S.Detail.get(rootId, rootId);
|
||||
|
||||
S.Menu.open('dataviewCalendar', {
|
||||
|
@ -315,7 +317,9 @@ const HeadSimple = observer(class Controls extends React.Component<Props> {
|
|||
value: object.timestamp,
|
||||
canEdit: true,
|
||||
canClear: false,
|
||||
onChange: (value: number) => U.Object.openDateByTimestamp(value),
|
||||
relationKey,
|
||||
onChange: (value: number) => U.Object.openDateByTimestamp(relationKey, value),
|
||||
getDotMap,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -323,10 +327,10 @@ const HeadSimple = observer(class Controls extends React.Component<Props> {
|
|||
};
|
||||
|
||||
changeDate = (dir: number) => {
|
||||
const { rootId } = this.props;
|
||||
const { rootId, relationKey } = this.props;
|
||||
const object = S.Detail.get(rootId, rootId);
|
||||
|
||||
U.Object.openDateByTimestamp(object.timestamp + dir * 86400);
|
||||
U.Object.openDateByTimestamp(relationKey, object.timestamp + dir * 86400);
|
||||
analytics.event(dir > 0 ? 'ClickDateForward' : 'ClickDateBack');
|
||||
};
|
||||
|
||||
|
|
|
@ -105,7 +105,8 @@ const PageMainChat = observer(class PageMainChat extends React.Component<I.PageC
|
|||
};
|
||||
|
||||
unbind () {
|
||||
const namespace = this.getNamespace();
|
||||
const { isPopup } = this.props;
|
||||
const namespace = U.Common.getEventNamespace(isPopup);
|
||||
const events = [ 'keydown', 'scroll' ];
|
||||
|
||||
$(window).off(events.map(it => `${it}.set${namespace}`).join(' '));
|
||||
|
@ -114,7 +115,7 @@ const PageMainChat = observer(class PageMainChat extends React.Component<I.PageC
|
|||
rebind () {
|
||||
const { isPopup } = this.props;
|
||||
const win = $(window);
|
||||
const namespace = this.getNamespace();
|
||||
const namespace = U.Common.getEventNamespace(isPopup);
|
||||
const container = U.Common.getScrollContainer(isPopup);
|
||||
|
||||
this.unbind();
|
||||
|
@ -137,10 +138,6 @@ const PageMainChat = observer(class PageMainChat extends React.Component<I.PageC
|
|||
};
|
||||
};
|
||||
|
||||
getNamespace () {
|
||||
return this.props.isPopup ? '-popup' : '';
|
||||
};
|
||||
|
||||
open () {
|
||||
const rootId = this.getRootId();
|
||||
|
||||
|
@ -177,11 +174,8 @@ const PageMainChat = observer(class PageMainChat extends React.Component<I.PageC
|
|||
};
|
||||
|
||||
const { isPopup, match } = this.props;
|
||||
|
||||
let close = true;
|
||||
if (isPopup && (match.params.id == this.id)) {
|
||||
close = false;
|
||||
};
|
||||
const close = !(isPopup && (match?.params?.id == this.id));
|
||||
|
||||
if (close) {
|
||||
Action.pageClose(this.id, true);
|
||||
};
|
||||
|
@ -189,7 +183,7 @@ const PageMainChat = observer(class PageMainChat extends React.Component<I.PageC
|
|||
|
||||
getRootId () {
|
||||
const { rootId, match } = this.props;
|
||||
return rootId ? rootId : match.params.id;
|
||||
return rootId ? rootId : match?.params?.id;
|
||||
};
|
||||
|
||||
onScroll () {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import * as React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Header, Footer, Deleted, ListObject, Button } from 'Component';
|
||||
import { Header, Footer, Deleted, ListObject, Button, Icon, Label, Loader, HeadSimple } from 'Component';
|
||||
import { I, C, S, U, J, Action, translate, analytics } from 'Lib';
|
||||
import HeadSimple from 'Component/page/elements/head/simple';
|
||||
import { eachDayOfInterval, isEqual, format, fromUnixTime } from 'date-fns';
|
||||
|
||||
interface State {
|
||||
isDeleted: boolean;
|
||||
isLoading: boolean;
|
||||
relations: any[];
|
||||
relationKey: string;
|
||||
};
|
||||
|
@ -22,18 +23,18 @@ const PageMainDate = observer(class PageMainDate extends React.Component<I.PageC
|
|||
refHead: any = null;
|
||||
refList: any = null;
|
||||
refCalIcon: any = null;
|
||||
loading = false;
|
||||
timeout = 0;
|
||||
|
||||
state = {
|
||||
isDeleted: false,
|
||||
isLoading: false,
|
||||
relations: [],
|
||||
relationKey: RELATION_KEY_MENTION,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { space } = S.Common;
|
||||
const { isDeleted, relations, relationKey } = this.state;
|
||||
const { isLoading, isDeleted, relations, relationKey } = this.state;
|
||||
const rootId = this.getRootId();
|
||||
const object = S.Detail.get(rootId, rootId, []);
|
||||
|
||||
|
@ -42,87 +43,99 @@ const PageMainDate = observer(class PageMainDate extends React.Component<I.PageC
|
|||
};
|
||||
|
||||
const relation = S.Record.getRelationByKey(relationKey);
|
||||
if (!relation) {
|
||||
return null;
|
||||
};
|
||||
|
||||
const columns: any[] = [
|
||||
{ relationKey: 'type', name: translate('commonObjectType'), isObject: true },
|
||||
{ relationKey: 'creator', name: translate('relationCreator'), isObject: true },
|
||||
];
|
||||
const keys = relations.map(it => it.relationKey);
|
||||
|
||||
const filters: I.Filter[] = [];
|
||||
|
||||
if (relation.format == I.RelationType.Object) {
|
||||
filters.push({ relationKey: RELATION_KEY_MENTION, condition: I.FilterCondition.In, value: [ object.id ] });
|
||||
let content = null;
|
||||
if (isLoading) {
|
||||
content = <Loader id="loader" />
|
||||
} else
|
||||
if (!relations.length || !relation) {
|
||||
content = (
|
||||
<div className="emptyContainer">
|
||||
<Label text={translate('pageMainDateEmptyText')} />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
filters.push({ relationKey: relationKey, condition: I.FilterCondition.Equal, value: object.timestamp, format: I.RelationType.Date });
|
||||
};
|
||||
const columns: any[] = [
|
||||
{ relationKey: 'type', name: translate('commonObjectType'), isObject: true },
|
||||
{ relationKey: 'creator', name: translate('relationCreator'), isObject: true },
|
||||
];
|
||||
|
||||
if ([ 'createdDate' ].includes(relationKey)) {
|
||||
const map = {
|
||||
createdDate: 'creator',
|
||||
const keys = relations.map(it => it.relationKey);
|
||||
const filters: I.Filter[] = [];
|
||||
|
||||
if (relation.format == I.RelationType.Object) {
|
||||
filters.push({ relationKey: RELATION_KEY_MENTION, condition: I.FilterCondition.In, value: [ object.id ] });
|
||||
} else {
|
||||
filters.push({ relationKey: relationKey, condition: I.FilterCondition.Equal, value: object.timestamp, format: I.RelationType.Date });
|
||||
};
|
||||
|
||||
filters.push({ relationKey: map[relationKey], condition: I.FilterCondition.NotEqual, value: J.Constant.anytypeProfileId });
|
||||
keys.push(map[relationKey]);
|
||||
};
|
||||
if ([ 'createdDate' ].includes(relationKey)) {
|
||||
const map = {
|
||||
createdDate: 'creator',
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={node => this.node = node}>
|
||||
<Header
|
||||
{...this.props}
|
||||
component="mainObject"
|
||||
ref={ref => this.refHeader = ref}
|
||||
rootId={rootId}
|
||||
/>
|
||||
|
||||
<div className="blocks wrapper">
|
||||
<HeadSimple
|
||||
{...this.props}
|
||||
noIcon={true}
|
||||
ref={ref => this.refHead = ref}
|
||||
rootId={rootId}
|
||||
readonly={true}
|
||||
/>
|
||||
filters.push({ relationKey: map[relationKey], condition: I.FilterCondition.NotEqual, value: J.Constant.anytypeProfileId });
|
||||
keys.push(map[relationKey]);
|
||||
};
|
||||
|
||||
content = (
|
||||
<React.Fragment>
|
||||
<div className="categories">
|
||||
{relations.map((item) => {
|
||||
const isMention = item.relationKey == RELATION_KEY_MENTION;
|
||||
const icon = isMention ? 'mention' : '';
|
||||
const separator = isMention ? <div className="separator" /> : '';
|
||||
|
||||
return (
|
||||
<React.Fragment key={item.relationKey}>
|
||||
<Button
|
||||
id={`category-${item.relationKey}`}
|
||||
active={relationKey == item.relationKey}
|
||||
color="blank"
|
||||
className="c36"
|
||||
onClick={() => this.onCategory(item.relationKey)}
|
||||
icon={icon}
|
||||
text={item.name}
|
||||
/>
|
||||
{relations.length > 1 ? separator : ''}
|
||||
</React.Fragment>
|
||||
<Button
|
||||
id={`category-${item.relationKey}`}
|
||||
key={item.relationKey}
|
||||
active={relationKey == item.relationKey}
|
||||
color="blank"
|
||||
className="c36"
|
||||
onClick={() => this.onCategoryClick(item.relationKey)}
|
||||
icon={icon}
|
||||
text={item.name}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="dateList">
|
||||
<ListObject
|
||||
ref={ref => this.refList = ref}
|
||||
{...this.props}
|
||||
spaceId={space}
|
||||
subId={SUB_ID}
|
||||
rootId={rootId}
|
||||
columns={columns}
|
||||
filters={filters}
|
||||
route={analytics.route.screenDate}
|
||||
relationKeys={keys}
|
||||
/>
|
||||
</div>
|
||||
<ListObject
|
||||
ref={ref => this.refList = ref}
|
||||
{...this.props}
|
||||
spaceId={space}
|
||||
subId={SUB_ID}
|
||||
rootId={rootId}
|
||||
columns={columns}
|
||||
filters={filters}
|
||||
route={analytics.route.screenDate}
|
||||
relationKeys={keys}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={node => this.node = node}>
|
||||
<Header
|
||||
{...this.props}
|
||||
component="mainObject"
|
||||
ref={ref => this.refHeader = ref}
|
||||
rootId={rootId}
|
||||
/>
|
||||
|
||||
<div className="blocks wrapper">
|
||||
<HeadSimple
|
||||
{...this.props}
|
||||
noIcon={true}
|
||||
ref={ref => this.refHead = ref}
|
||||
rootId={rootId}
|
||||
readonly={true}
|
||||
relationKey={relationKey}
|
||||
getDotMap={this.getDotMap}
|
||||
/>
|
||||
|
||||
{content}
|
||||
</div>
|
||||
|
||||
<Footer component="mainObject" {...this.props} />
|
||||
|
@ -130,9 +143,69 @@ const PageMainDate = observer(class PageMainDate extends React.Component<I.PageC
|
|||
);
|
||||
};
|
||||
|
||||
getFilters = (start: number, end: number): I.Filter[] => {
|
||||
const { relationKey } = this.state;
|
||||
|
||||
if (!relationKey) {
|
||||
return [];
|
||||
};
|
||||
|
||||
return [
|
||||
|
||||
{
|
||||
relationKey,
|
||||
condition: I.FilterCondition.GreaterOrEqual,
|
||||
value: start,
|
||||
quickOption: I.FilterQuickOption.ExactDate,
|
||||
format: I.RelationType.Date,
|
||||
},
|
||||
{
|
||||
relationKey,
|
||||
condition: I.FilterCondition.LessOrEqual,
|
||||
value: end,
|
||||
quickOption: I.FilterQuickOption.ExactDate,
|
||||
format: I.RelationType.Date,
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
getDotMap = (start: number, end: number, callBack: (res: Map<string, boolean>) => void): void => {
|
||||
const { relationKey } = this.state;
|
||||
const res = new Map();
|
||||
|
||||
if (!relationKey) {
|
||||
callBack(res);
|
||||
return;
|
||||
};
|
||||
|
||||
U.Data.search({
|
||||
filters: this.getFilters(start, end),
|
||||
keys: [ relationKey ],
|
||||
}, (message: any) => {
|
||||
eachDayOfInterval({
|
||||
start: fromUnixTime(start),
|
||||
end: fromUnixTime(end)
|
||||
}).forEach(date => {
|
||||
if (message.records.find(rec => isEqual(date, fromUnixTime(rec[relationKey]).setHours(0, 0, 0, 0)))) {
|
||||
res.set(format(date, 'dd-MM-yyyy'), true);
|
||||
};
|
||||
});
|
||||
|
||||
callBack(res);
|
||||
});
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
const { match } = this.props;
|
||||
const { relationKey } = match?.params || {};
|
||||
|
||||
this._isMounted = true;
|
||||
this.open();
|
||||
|
||||
if (relationKey) {
|
||||
this.setState({ relationKey }, () => this.open());
|
||||
} else {
|
||||
this.open();
|
||||
};
|
||||
};
|
||||
|
||||
componentDidUpdate () {
|
||||
|
@ -168,7 +241,7 @@ const PageMainDate = observer(class PageMainDate extends React.Component<I.PageC
|
|||
|
||||
this.close();
|
||||
this.id = rootId;
|
||||
this.setState({ isDeleted: false });
|
||||
this.setState({ isDeleted: false, isLoading: true });
|
||||
|
||||
C.ObjectOpen(rootId, '', U.Router.getRouteSpaceId(), (message: any) => {
|
||||
if (!U.Common.checkErrorOnOpen(rootId, message.error.code, this)) {
|
||||
|
@ -194,11 +267,8 @@ const PageMainDate = observer(class PageMainDate extends React.Component<I.PageC
|
|||
};
|
||||
|
||||
const { isPopup, match } = this.props;
|
||||
|
||||
let close = true;
|
||||
if (isPopup && (match.params.id == this.id)) {
|
||||
close = false;
|
||||
};
|
||||
const close = !(isPopup && (match?.params?.id == this.id));
|
||||
|
||||
if (close) {
|
||||
Action.pageClose(this.id, true);
|
||||
};
|
||||
|
@ -209,6 +279,8 @@ const PageMainDate = observer(class PageMainDate extends React.Component<I.PageC
|
|||
const { relationKey } = this.state;
|
||||
const rootId = this.getRootId();
|
||||
|
||||
this.setState({ isLoading: true });
|
||||
|
||||
C.RelationListWithValue(space, rootId, (message: any) => {
|
||||
const relations = (message.relations || []).map(it => S.Record.getRelationByKey(it.relationKey)).filter(it => {
|
||||
if ([ RELATION_KEY_MENTION ].includes(it.relationKey)) {
|
||||
|
@ -231,9 +303,9 @@ const PageMainDate = observer(class PageMainDate extends React.Component<I.PageC
|
|||
return 0;
|
||||
});
|
||||
|
||||
if (relations.length) {
|
||||
this.setState({ relations });
|
||||
this.setState({ relations, isLoading: false });
|
||||
|
||||
if (relations.length) {
|
||||
if (!relationKey || !relations.find(it => it.relationKey == relationKey)) {
|
||||
this.onCategory(relations[0].relationKey);
|
||||
} else {
|
||||
|
@ -247,6 +319,10 @@ const PageMainDate = observer(class PageMainDate extends React.Component<I.PageC
|
|||
|
||||
onCategory (relationKey: string) {
|
||||
this.setState({ relationKey }, () => this.reload());
|
||||
};
|
||||
|
||||
onCategoryClick (relationKey: string) {
|
||||
this.onCategory(relationKey);
|
||||
analytics.event('SwitchRelationDate', { relationKey });
|
||||
};
|
||||
|
||||
|
@ -256,9 +332,9 @@ const PageMainDate = observer(class PageMainDate extends React.Component<I.PageC
|
|||
|
||||
getRootId () {
|
||||
const { rootId, match } = this.props;
|
||||
return rootId ? rootId : match.params.id;
|
||||
return rootId ? rootId : match?.params?.id;
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
export default PageMainDate;
|
||||
export default PageMainDate;
|
|
@ -62,7 +62,7 @@ class PageMainEdit extends React.Component<I.PageComponent> {
|
|||
|
||||
getRootId () {
|
||||
const { rootId, match } = this.props;
|
||||
return rootId ? rootId : match.params.id;
|
||||
return rootId ? rootId : match?.params?.id;
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -171,7 +171,7 @@ const PageMainGraph = observer(class PageMainGraph extends React.Component<I.Pag
|
|||
|
||||
getRootId () {
|
||||
const { rootId, match } = this.props;
|
||||
return this.rootId || (rootId ? rootId : match.params.id);
|
||||
return this.rootId || (rootId ? rootId : match?.params?.id);
|
||||
};
|
||||
|
||||
onTab (id: string) {
|
||||
|
|
|
@ -445,7 +445,7 @@ const PageMainHistory = observer(class PageMainHistory extends React.Component<I
|
|||
|
||||
getRootId () {
|
||||
const { rootId, match } = this.props;
|
||||
return rootId ? rootId : match.params.id;
|
||||
return rootId ? rootId : match?.params?.id;
|
||||
};
|
||||
|
||||
isSetOrCollection (): boolean {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import * as React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Header, Block } from 'Component';
|
||||
import { Header, Block, HeadSimple } from 'Component';
|
||||
import { I, M, S, U, translate } from 'Lib';
|
||||
import HeadSimple from 'Component/page/elements/head/simple';
|
||||
|
||||
interface Props extends I.PageComponent {
|
||||
rootId: string;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import * as React from 'react';
|
||||
import $ from 'jquery';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Header, Footer, Loader, Block, Button, Icon, IconObject, Deleted } from 'Component';
|
||||
import { Header, Footer, Loader, Block, Button, Icon, IconObject, Deleted, HeadSimple } from 'Component';
|
||||
import { I, C, S, M, U, Action, translate, Relation, analytics } from 'Lib';
|
||||
import HeadSimple from 'Component/page/elements/head/simple';
|
||||
|
||||
interface State {
|
||||
isLoading: boolean;
|
||||
|
@ -237,11 +236,7 @@ const PageMainMedia = observer(class PageMainMedia extends React.Component<I.Pag
|
|||
};
|
||||
|
||||
const { isPopup, match } = this.props;
|
||||
|
||||
let close = true;
|
||||
if (isPopup && (match.params.id == this.id)) {
|
||||
close = false;
|
||||
};
|
||||
const close = !(isPopup && (match?.params?.id == this.id));
|
||||
|
||||
if (close) {
|
||||
Action.pageClose(this.id, true);
|
||||
|
@ -321,7 +316,7 @@ const PageMainMedia = observer(class PageMainMedia extends React.Component<I.Pag
|
|||
|
||||
getRootId () {
|
||||
const { rootId, match } = this.props;
|
||||
return rootId ? rootId : match.params.id;
|
||||
return rootId ? rootId : match?.params?.id;
|
||||
};
|
||||
|
||||
resize () {
|
||||
|
|
|
@ -399,8 +399,9 @@ const PageMainNavigation = observer(class PageMainNavigation extends React.Compo
|
|||
|
||||
loadPage (id: string) {
|
||||
const { loading } = this.state;
|
||||
const skipIds = U.Space.getSystemDashboardIds();
|
||||
|
||||
if (!id || [ I.HomePredefinedId.Graph, I.HomePredefinedId.Last ].includes(id as any)) {
|
||||
if (!id || skipIds.includes(id as any)) {
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -466,7 +467,7 @@ const PageMainNavigation = observer(class PageMainNavigation extends React.Compo
|
|||
getRootId () {
|
||||
const { rootId, match } = this.props;
|
||||
|
||||
let root = rootId ? rootId : match.params.id;
|
||||
let root = rootId ? rootId : match?.params?.id;
|
||||
if (root == I.HomePredefinedId.Graph) {
|
||||
root = U.Space.getLastOpened()?.id;
|
||||
};
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import * as React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Header, Footer, Loader, ListObject, Deleted, Icon } from 'Component';
|
||||
import { Header, Footer, Loader, ListObject, Deleted, Icon, HeadSimple } from 'Component';
|
||||
import { I, C, S, U, Action, translate, analytics } from 'Lib';
|
||||
import HeadSimple from 'Component/page/elements/head/simple';
|
||||
|
||||
interface State {
|
||||
isDeleted: boolean;
|
||||
|
@ -183,11 +182,8 @@ const PageMainRelation = observer(class PageMainRelation extends React.Component
|
|||
};
|
||||
|
||||
const { isPopup, match } = this.props;
|
||||
|
||||
let close = true;
|
||||
if (isPopup && (match.params.id == this.id)) {
|
||||
close = false;
|
||||
};
|
||||
const close = !(isPopup && (match?.params?.id == this.id));
|
||||
|
||||
if (close) {
|
||||
Action.pageClose(this.id, true);
|
||||
};
|
||||
|
@ -195,7 +191,7 @@ const PageMainRelation = observer(class PageMainRelation extends React.Component
|
|||
|
||||
getRootId () {
|
||||
const { rootId, match } = this.props;
|
||||
return rootId ? rootId : match.params.id;
|
||||
return rootId ? rootId : match?.params?.id;
|
||||
};
|
||||
|
||||
getObject () {
|
||||
|
|
|
@ -2,10 +2,8 @@ import * as React from 'react';
|
|||
import $ from 'jquery';
|
||||
import raf from 'raf';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Header, Footer, Loader, Block, Deleted } from 'Component';
|
||||
import { Header, Footer, Loader, Block, Deleted, HeadSimple, EditorControls } from 'Component';
|
||||
import { I, M, C, S, U, J, Action, keyboard, translate, analytics } from 'Lib';
|
||||
import Controls from 'Component/page/elements/head/controls';
|
||||
import HeadSimple from 'Component/page/elements/head/simple';
|
||||
|
||||
interface State {
|
||||
isLoading: boolean;
|
||||
|
@ -60,7 +58,7 @@ const PageMainSet = observer(class PageMainSet extends React.Component<I.PageCom
|
|||
{check.withCover ? <Block {...this.props} key={cover.id} rootId={rootId} block={cover} /> : ''}
|
||||
|
||||
<div className="blocks wrapper">
|
||||
<Controls ref={ref => this.refControls = ref} key="editorControls" {...this.props} rootId={rootId} resize={this.resize} />
|
||||
<EditorControls ref={ref => this.refControls = ref} key="editorControls" {...this.props} rootId={rootId} resize={this.resize} />
|
||||
<HeadSimple
|
||||
{...this.props}
|
||||
ref={ref => this.refHead = ref}
|
||||
|
@ -195,11 +193,8 @@ const PageMainSet = observer(class PageMainSet extends React.Component<I.PageCom
|
|||
};
|
||||
|
||||
const { isPopup, match } = this.props;
|
||||
|
||||
let close = true;
|
||||
if (isPopup && (match.params.id == this.id)) {
|
||||
close = false;
|
||||
};
|
||||
const close = !(isPopup && (match?.params?.id == this.id));
|
||||
|
||||
if (close) {
|
||||
Action.pageClose(this.id, true);
|
||||
};
|
||||
|
@ -207,7 +202,7 @@ const PageMainSet = observer(class PageMainSet extends React.Component<I.PageCom
|
|||
|
||||
getRootId () {
|
||||
const { rootId, match } = this.props;
|
||||
return rootId ? rootId : match.params.id;
|
||||
return rootId ? rootId : match?.params?.id;
|
||||
};
|
||||
|
||||
onScroll () {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue