1
0
Fork 0
mirror of https://github.com/anyproto/anytype-ts.git synced 2025-06-09 09:35:02 +09:00
This commit is contained in:
Andrew Simachev 2024-11-28 19:00:36 +01:00
commit 69dddaadbd
No known key found for this signature in database
GPG key ID: 1DFE44B21443F0EF
76 changed files with 1080 additions and 378 deletions

11
dist/img/icon/default/date.svg vendored Normal file
View file

@ -0,0 +1,11 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3.75" y="4.75" width="20.5" height="18.5" rx="4.25" stroke="#B6B6B6" stroke-width="1.5"/>
<path d="M17 12.9C17 12.6791 17.1791 12.5 17.4 12.5H19.6C19.8209 12.5 20 12.6791 20 12.9V13.6C20 13.8209 19.8209 14 19.6 14H17.4C17.1791 14 17 13.8209 17 13.6V12.9Z" fill="#B6B6B6"/>
<path d="M12.5 15.9547C12.5 15.7338 12.6791 15.5547 12.9 15.5547H15.1C15.3209 15.5547 15.5 15.7338 15.5 15.9547V16.6547C15.5 16.8756 15.3209 17.0547 15.1 17.0547H12.9C12.6791 17.0547 12.5 16.8756 12.5 16.6547V15.9547Z" fill="#B6B6B6"/>
<path d="M17 15.9547C17 15.7338 17.1791 15.5547 17.4 15.5547H19.6C19.8209 15.5547 20 15.7338 20 15.9547V16.6547C20 16.8756 19.8209 17.0547 19.6 17.0547H17.4C17.1791 17.0547 17 16.8756 17 16.6547V15.9547Z" fill="#B6B6B6"/>
<path d="M12.5 12.9C12.5 12.6791 12.6791 12.5 12.9 12.5H15.1C15.3209 12.5 15.5 12.6791 15.5 12.9V13.6C15.5 13.8209 15.3209 14 15.1 14H12.9C12.6791 14 12.5 13.8209 12.5 13.6V12.9Z" fill="#B6B6B6"/>
<path d="M8 15.9C8 15.6791 8.17909 15.5 8.4 15.5H10.6C10.8209 15.5 11 15.6791 11 15.9V16.6C11 16.8209 10.8209 17 10.6 17H8.4C8.17909 17 8 16.8209 8 16.6V15.9Z" fill="#B6B6B6"/>
<path d="M12.5 18.9C12.5 18.6791 12.6791 18.5 12.9 18.5H15.1C15.3209 18.5 15.5 18.6791 15.5 18.9V19.6C15.5 19.8209 15.3209 20 15.1 20H12.9C12.6791 20 12.5 19.8209 12.5 19.6V18.9Z" fill="#B6B6B6"/>
<path d="M8 18.9C8 18.6791 8.17909 18.5 8.4 18.5H10.6C10.8209 18.5 11 18.6791 11 18.9V19.6C11 19.8209 10.8209 20 10.6 20H8.4C8.17909 20 8 19.8209 8 19.6V18.9Z" fill="#B6B6B6"/>
<path d="M3.5 9.5C3.5 7.01472 5.51472 5 8 5H20C22.4853 5 24.5 7.01472 24.5 9.5V10H3.5V9.5Z" fill="#B6B6B6"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -199,7 +199,7 @@ class MenuManager {
config.debug[i] = !config.debug[i];
Api.setConfig(this.win, { debug: config.debug });
if ([ 'ho' ].includes(i)) {
if ([ 'hiddenObject' ].includes(i)) {
this.win.reload();
};
}

12
package-lock.json generated
View file

@ -61,7 +61,7 @@
"react-dom": "^17.0.2",
"react-minimal-pie-chart": "^8.3.0",
"react-pdf": "^6.2.2",
"react-router-dom": "^5.2.0",
"react-router-dom": "^5.3.4",
"react-sortable-hoc": "^2.0.0",
"react-virtualized": "^9.22.3",
"regedit": "^5.1.3",
@ -87,7 +87,7 @@
"@types/raf": "^3.4.0",
"@types/react": "^16.14.31",
"@types/react-dom": "^16.9.16",
"@types/react-router-dom": "^4.3.5",
"@types/react-router-dom": "^5.3.3",
"@typescript-eslint/eslint-plugin": "^6.18.1",
"@typescript-eslint/parser": "^6.18.1",
"cross-env": "^7.0.2",
@ -2691,12 +2691,12 @@
}
},
"node_modules/@types/react-router-dom": {
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-4.3.5.tgz",
"integrity": "sha512-eFajSUASYbPHg2BDM1G8Btx+YqGgvROPIg6sBhl3O4kbDdYXdFdfrgQFf/pcBuQVObjfT9AL/dd15jilR5DIEA==",
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz",
"integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==",
"dev": true,
"dependencies": {
"@types/history": "*",
"@types/history": "^4.7.11",
"@types/react": "*",
"@types/react-router": "*"
}

View file

@ -63,7 +63,7 @@
"@types/raf": "^3.4.0",
"@types/react": "^16.14.31",
"@types/react-dom": "^16.9.16",
"@types/react-router-dom": "^4.3.5",
"@types/react-router-dom": "^5.3.3",
"@typescript-eslint/eslint-plugin": "^6.18.1",
"@typescript-eslint/parser": "^6.18.1",
"cross-env": "^7.0.2",
@ -141,7 +141,7 @@
"react-dom": "^17.0.2",
"react-minimal-pie-chart": "^8.3.0",
"react-pdf": "^6.2.2",
"react-router-dom": "^5.2.0",
"react-router-dom": "^5.3.4",
"react-sortable-hoc": "^2.0.0",
"react-virtualized": "^9.22.3",
"regedit": "^5.1.3",

View file

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 14.5L12.5 10L8 5.5" stroke="#B6B6B6" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 196 B

3
src/img/icon/mention.svg Normal file
View file

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.3828 17.75C9.11699 17.75 7.99408 17.5778 7.01409 17.2335C6.03818 16.8933 5.21539 16.391 4.54573 15.7267C3.87607 15.0664 3.3677 14.2563 3.02062 13.2963C2.67354 12.3362 2.5 11.2405 2.5 10.0091C2.5 8.81011 2.67558 7.73262 3.02675 6.77666C3.37791 5.82069 3.88832 5.0065 4.55798 4.33408C5.23173 3.65762 6.05043 3.14115 7.01409 2.78469C7.97775 2.42823 9.07207 2.25 10.2971 2.25C11.469 2.25 12.502 2.43228 13.3963 2.79684C14.2905 3.16141 15.0419 3.66167 15.6503 4.29763C16.2628 4.93359 16.7242 5.66271 17.0345 6.48501C17.3448 7.3073 17.5 8.17617 17.5 9.09163C17.5 9.73164 17.4592 10.3575 17.3775 10.9691C17.2958 11.5767 17.1448 12.1276 16.9243 12.6218C16.7078 13.1119 16.3934 13.5049 15.981 13.8006C15.5727 14.0922 15.0419 14.2441 14.3885 14.2563C14.0414 14.2684 13.7046 14.2279 13.3779 14.1348C13.0512 14.0416 12.7736 13.8917 12.5449 13.6851C12.3163 13.4745 12.1774 13.2071 12.1284 12.8831H12.0549C11.9569 13.1221 11.7936 13.3449 11.5649 13.5515C11.3363 13.754 11.0443 13.916 10.6891 14.0375C10.3379 14.155 9.92956 14.2036 9.46407 14.1834C8.94957 14.1631 8.48612 14.0477 8.0737 13.837C7.66129 13.6264 7.30808 13.3347 7.01409 12.9621C6.72417 12.5894 6.50163 12.1479 6.34647 11.6375C6.1913 11.1271 6.11372 10.56 6.11372 9.9362C6.11372 9.3367 6.20151 8.79998 6.37709 8.32605C6.55267 7.84807 6.79155 7.43692 7.09371 7.09261C7.39996 6.74425 7.75112 6.4688 8.1472 6.26627C8.54328 6.06373 8.96182 5.93614 9.40282 5.88348C9.83156 5.83487 10.2256 5.84702 10.5849 5.91993C10.9443 5.99285 11.2423 6.10424 11.4792 6.25412C11.7201 6.40399 11.8712 6.56805 11.9324 6.74628H12.012V6.06576H13.5188V11.7043C13.5188 12.0243 13.6025 12.2957 13.7699 12.5185C13.9414 12.7413 14.1844 12.8527 14.4988 12.8527C14.8704 12.8527 15.1603 12.717 15.3685 12.4456C15.5808 12.1742 15.7299 11.757 15.8156 11.1939C15.9055 10.6309 15.9504 9.90987 15.9504 9.03087C15.9504 8.41516 15.8667 7.83591 15.6993 7.29312C15.5359 6.74628 15.293 6.25209 14.9704 5.81056C14.6519 5.36499 14.2599 4.9822 13.7944 4.66219C13.3289 4.34219 12.7981 4.09509 12.2019 3.92091C11.6098 3.74673 10.9545 3.65964 10.2358 3.65964C9.2599 3.65964 8.3922 3.80952 7.63271 4.10927C6.87321 4.40497 6.23214 4.83232 5.70947 5.39132C5.19089 5.95031 4.79686 6.6207 4.52736 7.40249C4.26194 8.18022 4.12924 9.05315 4.12924 10.0213C4.12924 11.0299 4.26603 11.9251 4.53961 12.7069C4.81319 13.4846 5.21744 14.1408 5.75235 14.6755C6.28726 15.2102 6.94876 15.6153 7.73683 15.8907C8.52491 16.1702 9.43344 16.31 10.4624 16.31C10.9198 16.31 11.3689 16.2695 11.8099 16.1885C12.2509 16.1115 12.6429 16.0224 12.9859 15.9211C13.333 15.8239 13.5923 15.7449 13.7638 15.6841L14.1925 17.0148C13.9394 17.1323 13.6025 17.2477 13.1819 17.3611C12.7654 17.4746 12.3122 17.5677 11.8222 17.6406C11.3363 17.7135 10.8565 17.75 10.3828 17.75ZM9.81931 12.6947C10.3501 12.6947 10.7768 12.5894 11.0994 12.3788C11.4261 12.1681 11.6609 11.8542 11.8038 11.437C11.9508 11.0157 12.0243 10.4891 12.0243 9.85721C12.0202 9.24151 11.9406 8.75137 11.7854 8.38681C11.6343 8.0182 11.3975 7.7549 11.0749 7.59692C10.7564 7.43489 10.3461 7.35388 9.84381 7.35388C9.3824 7.35388 8.98836 7.46527 8.6617 7.68806C8.33912 7.91085 8.09208 8.21263 7.92058 8.59339C7.75316 8.97416 7.66742 9.39746 7.66333 9.86329C7.66742 10.3534 7.73683 10.8152 7.87158 11.2486C8.01041 11.678 8.23499 12.0264 8.54533 12.2937C8.85974 12.5611 9.2844 12.6947 9.81931 12.6947Z" fill="#B6B6B6"/>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View file

@ -22,7 +22,8 @@ export default {
'sizeInBytes',
'restrictions',
'defaultTemplateId',
'createdDate'
'createdDate',
'timestamp',
],
sidebar: [
@ -49,7 +50,8 @@ export default {
'restrictions',
'source',
'lastModifiedDate',
'lastOpenedDate'
'lastOpenedDate',
'timestamp'
],
relation: [

View file

@ -89,6 +89,7 @@
"commonComment": "Comment",
"commonIcon": "Icon",
"commonObjects": "Objects",
"commonDates": "Dates",
"commonPreferences": "Preferences",
"commonDuplicate": "Duplicate",
"commonRemoveFromFavorites": "Remove from Favorites",
@ -173,6 +174,7 @@
"commonMenu": "Menu",
"commonSignUp": "Sign Up",
"commonNotFound": "Not found",
"commonCalculate": "Calculate",
"pluralDay": "day|days",
"pluralObject": "Object|Objects",
@ -616,8 +618,8 @@
"blockDataviewHeadMenuChange": "Change source %s",
"blockDataviewHeadMenuOpen": "Open source %s",
"blockDataviewBoardRelationDeletedTitle": "Relation has been deleted",
"blockDataviewBoardRelationDeletedDescription": "Choose another Relation to group your Kanban",
"blockDataviewBoardRelationDeletedTitle": "No Relation",
"blockDataviewBoardRelationDeletedDescription": "Choose Relation to group your Kanban",
"blockDataviewBoardOpenViewMenu": "Open View menu",
"blockDataviewBoardColumnSettings": "Column settings",
@ -730,9 +732,12 @@
"popupSettingsPersonalSidebar": "Automatically show and hide sidebar",
"popupSettingsPersonalSidebarMode": "Sidebar mode",
"popupSettingsPersonalRelativeDates": "Display relative dates",
"popupSettingsPersonalDateFormat": "Date format",
"popupSettingsPersonalSectionLanguage": "Language & Spelling",
"popupSettingsPersonalSectionEditor": "Editor Personalisation",
"popupSettingsPersonalSectionApp": "App Appearance",
"popupSettingsPersonalSectionDateTime": "Date & Time",
"popupSettingsPersonalTimeFormat": "Time format",
"popupSettingsColorMode": "Color mode",
"popupSettingsColorModeButtonLight": "Light",
@ -2217,6 +2222,9 @@
"participantPermissions2": "Owner",
"participantPermissions3": "No access",
"relationMentions": "Mentioned in",
"relationCreator": "Created by",
"spaceStatus0": "Active",
"spaceStatus1": "Loading",
"spaceStatus2": "Ok",
@ -2245,7 +2253,6 @@
"networkMode2Title": "Self-hosted",
"networkMode2Text": "Back up to your self-hosted network",
"formulaNone": "Calculate",
"formulaCount": "Count",
"formulaPercentage": "Percentage",
"formulaMath": "Math",
@ -2277,4 +2284,5 @@
"formulaCheckboxNotEmptyShort": "Checked",
"formulaCheckboxPercentEmpty": "Percentage unchecked",
"formulaCheckboxPercentNotEmpty": "Percentage checked"
}

View file

@ -60,6 +60,11 @@ export default {
text: '#b6b6b6',
bg: '#f2f2f2'
},
progress: {
bg: '#ebebeb',
fg: '#ffd15b',
}
},
dark: {
@ -109,6 +114,11 @@ export default {
text: '#9a9a9a',
bg: '#b6b6b6'
},
progress: {
bg: '#292929',
fg: '#ffd15b',
}
}
};

View file

@ -30,7 +30,10 @@
.day.active {
.number { padding: 0px; color: var(--color-text-inversion); }
.number {
.inner { background-color: var(--color-system-accent-100); border-radius: 12px; padding: 0px 8px; align-self: flex-end; min-width: 24px; text-align: center; }
.inner {
background-color: var(--color-system-accent-100); border-radius: 12px; padding: 0px 8px; align-self: flex-end; min-width: 24px; text-align: center;
transition: $transitionAllCommon;
}
}
}

View file

@ -54,7 +54,7 @@
.cellFoot { height: 48px; @include text-common; color: var(--color-text-primary); }
.cellFoot {
.flex { justify-content: flex-end; }
.result { display: flex; flex-direction: row; align-items: center; gap: 0px 2px; max-width: 100%; }
.result { display: flex; flex-direction: row; align-items: center; gap: 0px 4px; max-width: 100%; }
.name { width: auto !important; color: var(--color-text-secondary); }
.cellContent { height: 48px !important; }

View file

@ -232,10 +232,6 @@
.block.blockDataview { padding: 0px; }
.editorControls { height: 52px; width: 100%; margin-bottom: 8px; padding: 0px 14px; }
.headSimple {
.side.right { display: none; }
}
}
.block.blockCover {
@ -262,6 +258,12 @@
&.withIconAndCover {
.editorControls { display: none; }
}
.blocks {
.headSimple {
.side.right { display: none; }
}
}
}
.editorWrapper.isSet,

View file

@ -12,13 +12,13 @@
.editableWrap { flex-grow: 1; }
.editableWrap {
.editable { z-index: 1; position: relative; word-break: break-word; cursor: text; }
.editable { z-index: 1; position: relative; word-break: break-word; cursor: text; min-height: 32px; }
}
> .descr { @include text-paragraph; }
}
.side.right { flex-shrink: 0; text-align: right; gap: 0px 16px; display: flex; flex-direction: row; align-items: center; justify-content: flex-end; }
.side.right { text-align: right; display: flex; flex-direction: row; gap: 0px 16px; align-items: center; justify-content: flex-end; }
.side.right {
.button { white-space: nowrap; }
}

View file

@ -37,8 +37,9 @@
.button.dark:not(.disabled).hover { background: rgba(0,0,0,0.4); }
.button.blank { background: none; border: solid 1px var(--color-shape-primary); font-weight: 400; }
.button.blank:not(.disabled):hover,
.button.blank:not(.disabled).hover { background: var(--color-shape-highlight-medium); }
.button.blank:not(.disabled) {
&:hover, &.hover, &.active { background: var(--color-shape-highlight-medium); }
}
.button.c36 { @include text-common; height: 36px; border-radius: 6px; padding: 0px 12px; }
.button.c32 { @include text-small; height: 32px; border-radius: 6px; padding: 0px 10px; }

View file

@ -1,28 +1,28 @@
@import "~scss/_mixins";
.listObject {
.table { display: grid; border-color: var(--color-shape-secondary); border-style: solid; border-top-width: 1px; margin: 0px 0px 10px 0px; }
.table { display: grid; margin: 0px 0px 10px 0px; }
.table {
.selectionTarget { display: grid; grid-template-columns: minmax(0, 1fr) 20% 20%; }
.row.isHead { display: grid; grid-template-columns: minmax(0, 1fr) 20% 20%; color: var(--color-text-secondary); }
.row.isHead { display: grid; grid-template-columns: minmax(0, 1fr) 20% 20%; color: var(--color-control-active); }
.row.isHead {
.cell {
text-align: left; padding: 14px 0px 14px 14px; white-space: nowrap; font-weight: 400; line-height: 20px; position: relative;
text-align: left; padding: 9px 0px 9px 14px; white-space: nowrap; font-weight: 400; line-height: 20px; position: relative;
@include text-overflow-nw;
}
.cell {
.name {
display: flex; flex-direction: row; align-items: center; line-height: 20px; height: 20px; vertical-align: top; width: 100%;
@include text-overflow-nw; border-right: 1px solid var(--color-shape-secondary);
@include text-overflow-nw;
}
.icon.sortArrow { width: 20px; height: 20px; margin: 0px; background-image: url('~img/icon/sortArrow.svg'); }
.icon.sortArrow.c1 { transform: rotateZ(180deg); }
}
.cell:last-child {
.name { border: 0px; }
}
.icon.sortArrow { width: 20px; height: 20px; margin: 0px; background-image: url('~img/icon/sortArrow.svg'); }
.icon.sortArrow.c1 { transform: rotateZ(180deg); }
}
.row { border-bottom: 1px solid var(--color-shape-secondary); }

View file

@ -3,7 +3,7 @@
.menus {
.menu.menuDataviewOptionList { width: var(--menu-width-value);; }
.menu.menuDataviewOptionList {
.content { overflow: visible; transition: none; padding: 0px; max-height: unset; }
.content { overflow: hidden; transition: none; padding: 0px; max-height: unset; }
.content {
.wrap { height: 100%; display: flex; flex-direction: column; }
.wrap.noFilter {

View file

@ -14,4 +14,5 @@
@import "./main/membership";
@import "./main/onboarding";
@import "./main/chat";
@import "./main/void";
@import "./main/void";
@import "./main/date";

View file

@ -0,0 +1,28 @@
@import "~scss/_mixins";
.pageMainDate {
.wrapper { width: 704px; margin: 0px auto; padding: 40px 0px 80px 0px; user-select: none; }
.headSimple { align-items: center; height: 32px; }
.headSimple {
.side.right { gap: 0px; }
.side.right {
.icon { width: 24px !important; height: 24px !important; }
.icon.calendar { background-image: url('~img/icon/relation/date.svg'); }
.icon.arrow { background-image: url('~img/arrow/dateSelectLight.svg'); }
.icon.arrow.left { transform: rotateZ(180deg); }
}
}
.categories {
display: flex; flex-direction: row; gap: 8px; margin: 0px 0px 12px 0px; align-items: center; justify-content: flex-start; flex-wrap: wrap;
}
.categories {
.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; }
}
}

View file

@ -227,7 +227,7 @@ html.themeDark {
/* Progress */
.progress {
.inner { background: var(--color-bg-secondary); }
.inner { background: var(--color-bg-secondary); box-shadow: 0px 4px 16px rgb(0 0 0 / 20%), 0px 0px 0px 1px var(--color-shape-primary) inset; }
}
.tooltip { color: var(--color-text-primary); }

View file

@ -47,6 +47,7 @@ const BlockChat = observer(class BlockChat extends React.Component<I.BlockCompon
};
render () {
const { showRelativeDates } = S.Common;
const { threadId } = this.state;
const rootId = this.getRootId();
const blockId = this.getBlockId();
@ -57,10 +58,8 @@ const BlockChat = observer(class BlockChat extends React.Component<I.BlockCompon
const lastId = Storage.getChat(rootId).lastId;
const Section = (item: any) => {
let date = U.Date.dayString(item.createdAt);
if (!date) {
date = U.Date.dateWithFormat(I.DateFormat.MonthAbbrAfterDay, item.createdAt);
};
const day = showRelativeDates ? U.Date.dayString(item.createdAt) : null;
const date = day ? day : U.Date.dateWithFormat(S.Common.dateFormat, item.createdAt);
return (
<div className="section">
@ -383,8 +382,8 @@ const BlockChat = observer(class BlockChat extends React.Component<I.BlockCompon
});
sections.sort((c1, c2) => {
if (c1.time > c2.time) return 1;
if (c1.time < c2.time) return -1;
if (c1.createdAt > c2.createdAt) return 1;
if (c1.createdAt < c2.createdAt) return -1;
return 0;
});

View file

@ -315,7 +315,7 @@ const Head = observer(class Head extends React.Component<I.ViewComponent, State>
checkInput (isEmpty: boolean) {
if (this.ref) {
$(this.ref.node).toggleClass('isEmpty', isEmpty)
$(this.ref.node).toggleClass('isEmpty', isEmpty);
};
};

View file

@ -22,7 +22,7 @@ const Card = observer(class Card extends React.Component<Props> {
const relations = getVisibleRelations();
const idPrefix = getIdPrefix();
const subId = S.Record.getGroupSubId(rootId, block.id, groupId);
const record = S.Detail.get(subId, id);
const record = S.Detail.get(subId, id, relations.map(it => it.relationKey));
const cn = [ 'card', U.Data.layoutClass(record.id, record.layout) ];
const { done } = record;
const cover = getCoverObject(id);
@ -42,6 +42,7 @@ const Card = observer(class Card extends React.Component<Props> {
<div className="inner">
{relations.map((relation: any, i: number) => {
const id = Relation.cellId(idPrefix, relation.relationKey, record.id);
return (
<Cell
elementId={id}

View file

@ -81,14 +81,13 @@ const ViewCalendar = observer(class ViewCalendar extends React.Component<I.ViewC
<div className="body">
{data.map((item, i) => {
const { d, m, y } = item;
const cn = [];
const current = [ d, m, y ].join('-');
const current = [ item.d, item.m, item.y ].join('-');
if (m != item.m) {
cn.push('other');
};
if ((today.d == d) && (today.m == m) && (today.y == y)) {
if ((today.d == item.d) && (today.m == item.m) && (today.y == item.y)) {
cn.push('active');
};
if (i < 7) {
@ -239,12 +238,11 @@ const ViewCalendar = observer(class ViewCalendar extends React.Component<I.ViewC
const objectId = getTarget().id;
const flags: I.ObjectFlag[] = [ I.ObjectFlag.SelectTemplate ];
const type = S.Record.getTypeById(getTypeId());
const typeKey = type.uniqueKey;
const templateId = getTemplateId();
details = Object.assign(Dataview.getDetails(rootId, J.Constant.blockId.dataview, objectId, view.id), details);
C.ObjectCreate(details, flags, templateId, typeKey, S.Common.space, (message: any) => {
C.ObjectCreate(details, flags, templateId, type?.uniqueKey, S.Common.space, (message: any) => {
if (message.error.code) {
return;
};

View file

@ -1,7 +1,7 @@
import * as React from 'react';
import { observer } from 'mobx-react';
import { IconObject, ObjectName } from 'Component';
import { I, S, U, translate, Preview } from 'Lib';
import { I, S, U, C, translate, Preview } from 'Lib';
interface Props extends I.ViewComponent {
d: number;
@ -21,6 +21,7 @@ const Item = observer(class Item extends React.Component<Props> {
super(props);
this.onOpen = this.onOpen.bind(this);
this.onOpenDate = this.onOpenDate.bind(this);
this.onMore = this.onMore.bind(this);
this.onContext = this.onContext.bind(this);
this.canCreate = this.canCreate.bind(this);
@ -73,7 +74,7 @@ const Item = observer(class Item extends React.Component<Props> {
onContextMenu={this.onContext}
onDoubleClick={this.onDoubleClick}
>
<div className="number">
<div className="number" onClick={this.onOpenDate}>
<div className="inner">{d}</div>
</div>
<div className="items">
@ -128,16 +129,14 @@ const Item = observer(class Item extends React.Component<Props> {
onContext () {
const node = $(this.node);
const options = [];
const options = [
{ id: 'open', icon: 'expand', name: translate('commonOpenObject') }
] as I.Option[];
if (this.canCreate()) {
options.push({ id: 'add', name: translate('commonNewObject') });
};
if (!options.length) {
return;
};
S.Menu.open('select', {
element: node,
vertical: I.MenuDirection.Bottom,
@ -150,9 +149,10 @@ const Item = observer(class Item extends React.Component<Props> {
options,
noVirtualisation: true,
onSelect: (e: any, item: any) => {
if (item.id == 'add') {
this.onCreate();
}
switch (item.id) {
case 'open': this.onOpenDate(); break;
case 'add': this.onCreate(); break;
};
},
}
});
@ -173,6 +173,12 @@ const Item = observer(class Item extends React.Component<Props> {
onCreate(details);
};
onOpenDate () {
const { d, m, y } = this.props;
U.Object.openDateByTimestamp(U.Date.timestamp(y, m, d, 12, 0, 0), 'config');
};
canCreate (): boolean {
const { getView, isAllowedObject } = this.props;
const view = getView();

View file

@ -1,7 +1,7 @@
import * as React from 'react';
import { observer } from 'mobx-react';
import { Select } from 'Component';
import { I, S, C, J, keyboard, Relation, Dataview, analytics, translate } from 'Lib';
import { Icon } from 'Component';
import { I, S, C, U, keyboard, Relation, Dataview, analytics, translate } from 'Lib';
interface Props extends I.ViewComponent, I.ViewRelation {
rootId?: string;
@ -17,7 +17,6 @@ const FootCell = observer(class FootCell extends React.Component<Props, State> {
node = null;
menuContext = null;
refSelect = null;
state = {
isEditing: false,
@ -34,6 +33,7 @@ const FootCell = observer(class FootCell extends React.Component<Props, State> {
this.onChange = this.onChange.bind(this);
this.onMouseEnter = this.onMouseEnter.bind(this);
this.onMouseLeave = this.onMouseLeave.bind(this);
this.onSelect = this.onSelect.bind(this);
};
render () {
@ -43,27 +43,24 @@ const FootCell = observer(class FootCell extends React.Component<Props, State> {
const view = getView();
if (!relation || !view) {
return null;
return <div />;
};
// Subscriptions
const viewRelation = view.getRelation(relationKey);
if (!viewRelation) {
return null;
return <div />;
};
const cn = [ 'cellFoot', `cell-key-${relationKey}` ];
const sections = this.getSections();
const option = Relation.formulaByType(relation.format).find(it => it.id == String(viewRelation.formulaType));
const name = option.short || option.name;
const subId = S.Record.getSubId(rootId, block.id);
const records = S.Record.getRecords(subId, [ relationKey ], true);
if (viewRelation.formulaType != I.FormulaType.None) {
const subId = S.Record.getSubId(rootId, block.id);
const records = S.Record.getRecords(subId, [ relationKey ], true);
records.forEach(record => {
const value = record[relationKey];
});
};
records.forEach(record => {
const value = record[relationKey];
});
return (
<div
@ -77,28 +74,14 @@ const FootCell = observer(class FootCell extends React.Component<Props, State> {
<div className="cellContent">
<div className="flex">
{isEditing || (result === null) ? (
<Select
ref={ref => this.refSelect = ref}
id={`grid-foot-select-${relationKey}-${block.id}`}
value=""
onChange={() => this.refSelect.setValue('')}
options={sections}
arrowClassName="light"
menuParam={{
onOpen: this.onOpen,
onClose: this.onClose,
subIds: [ 'select2' ],
data: {
noScroll: true,
noVirtualisation: true,
onOver: this.onOver,
},
}}
/>
<div className="select" onClick={this.onSelect}>
<div className="name">{viewRelation.formulaType ? name : translate('commonCalculate')}</div>
<Icon className="arrow light" />
</div>
) : ''}
{!isEditing && option && (result !== null) ? (
<div className="result">
<span className="name">{option.short || option.name}</span>
<span className="name">{name}</span>
{result}
</div>
) : ''}
@ -138,8 +121,32 @@ const FootCell = observer(class FootCell extends React.Component<Props, State> {
};
onClick (e: any) {
this.setState({ isEditing: true }, () => {
window.setTimeout(() => this.refSelect.show(e), 10);
this.setState({ isEditing: true });
};
onSelect (e: any) {
const { relationKey } = this.props;
const id = Relation.cellId('foot', relationKey, '');
const options = U.Menu.getFormulaSections(relationKey);
S.Menu.closeAll([], () => {
S.Menu.open('select', {
element: `#${id} .select`,
horizontal: I.MenuDirection.Center,
onOpen: this.onOpen,
onClose: this.onClose,
subIds: [ 'select2' ],
data: {
options,
noScroll: true,
noVirtualisation: true,
onOver: this.onOver,
onSelect: (e: any, item: any) => {
this.onChange(item.id);
this.setEditing(false);
},
}
});
});
};
@ -193,23 +200,6 @@ const FootCell = observer(class FootCell extends React.Component<Props, State> {
});
};
getSections () {
const { relationKey } = this.props;
const relation = S.Record.getRelationByKey(relationKey);
const options = Relation.formulaByType(relation.format);
return [
{ id: I.FormulaSection.None, name: translate('formulaNone') },
].concat([
{ id: I.FormulaSection.Count, name: translate('formulaCount'), arrow: true },
{ id: I.FormulaSection.Percent, name: translate('formulaPercentage'), arrow: true },
{ id: I.FormulaSection.Math, name: translate('formulaMath'), arrow: true },
{ id: I.FormulaSection.Date, name: translate('formulaDate'), arrow: true },
].filter(s => {
return options.filter(it => it.section == s.id).length;
})).map(it => ({ ...it, id: String(it.id), checkbox: false }));
};
onChange (id: string): void {
const { rootId, block, relationKey, getView } = this.props;
const view = getView();
@ -226,15 +216,13 @@ const FootCell = observer(class FootCell extends React.Component<Props, State> {
};
onMouseEnter (): void {
const { block, relationKey } = this.props;
if (!keyboard.isDragging) {
$(`#block-${block.id} .cell-key-${relationKey}`).addClass('cellKeyHover');
$(this.node).addClass('hover');
};
};
onMouseLeave () {
$('.cellKeyHover').removeClass('cellKeyHover');
$(this.node).removeClass('hover');
};
});

View file

@ -486,6 +486,7 @@ const BlockFeatured = observer(class BlockFeatured extends React.Component<Props
const object = S.Detail.get(rootId, rootId, [ 'setOf', 'internalFlags' ]);
const menuParam = {
menuId: item.id,
element: `#${this.menuContext.getId()} #item-${item.id}`,
offsetX: this.menuContext.getSize().width,
vertical: I.MenuDirection.Center,
@ -550,8 +551,8 @@ const BlockFeatured = observer(class BlockFeatured extends React.Component<Props
});
};
if (menuId && !S.Menu.isOpen(menuId)) {
if (S.Menu.isOpen(menuId)) {
if (menuId) {
if (S.Menu.isOpen(menuId, item.id)) {
S.Menu.open(menuId, menuParam);
} else {
S.Menu.closeAll(J.Menu.featuredType, () => {
@ -690,6 +691,11 @@ const BlockFeatured = observer(class BlockFeatured extends React.Component<Props
isEmpty = true;
};
if (!this.canEdit(relation)) {
U.Object.openDateByTimestamp(value, 'config');
break;
};
menuId = 'dataviewCalendar';
menuData = {
value,

View file

@ -188,6 +188,11 @@ const Cell = observer(class Cell extends React.Component<Props> {
return;
};
if (relation.format == I.RelationType.Date) {
U.Object.openDateByTimestamp(value, 'config');
return;
}
if (relation.format == I.RelationType.Checkbox) {
return;
};

View file

@ -170,8 +170,8 @@ const CellText = observer(class CellText extends React.Component<I.Cell, State>
value = Number(value) || 0;
const day = showRelativeDates ? U.Date.dayString(value) : null;
const date = day ? day : U.Date.dateWithFormat(viewRelation.dateFormat, value);
const time = U.Date.timeWithFormat(viewRelation.timeFormat, value);
const date = day ? day : U.Date.dateWithFormat(S.Common.dateFormat, value);
const time = U.Date.timeWithFormat(S.Common.timeFormat, value);
value = viewRelation.includeTime ? [ date, time ].join((day ? ', ' : ' ')) : date;
} else {

View file

@ -2,16 +2,18 @@ import * as React from 'react';
import { observer } from 'mobx-react';
import { PieChart } from 'react-minimal-pie-chart';
import { Icon } from 'Component';
import { I, S, Preview, translate } from 'Lib';
import { I, J, S, Preview, translate } from 'Lib';
const FooterMainEdit = observer(class FooterMainEdit extends React.Component<I.FooterComponent> {
render () {
const { onHelp } = this.props;
const { show } = S.Progress;
const theme = S.Common.getThemeClass();
const current = S.Progress.getCurrent();
const total = S.Progress.getTotal();
const percent = Math.round((current / total) * 100);
const color = J.Theme[theme].progress;
return (
<div className="buttons">
@ -27,8 +29,8 @@ const FooterMainEdit = observer(class FooterMainEdit extends React.Component<I.F
startAngle={270}
lengthAngle={-360}
data={[
{ title: '', value: 100 - percent, color: '#ebebeb' },
{ title: '', value: percent, color: '#ffd15b' },
{ title: '', value: 100 - percent, color: color.bg },
{ title: '', value: percent, color: color.fg },
]}
/>
</div>

View file

@ -10,6 +10,7 @@ interface ButtonProps {
icon?: string;
arrow?: boolean;
text?: string;
active?: boolean;
color?: string;
className?: string;
tooltip?: string;
@ -44,7 +45,8 @@ const Button = forwardRef<ButtonRef, ButtonProps>(({
onMouseEnter,
onMouseLeave,
onMouseDown,
dataset
dataset,
active,
}, ref) => {
const [ isLoading, setIsLoading ] = useState(false);
const nodeRef = useRef<HTMLDivElement | HTMLInputElement>(null);
@ -56,6 +58,10 @@ const Button = forwardRef<ButtonRef, ButtonProps>(({
cn.push('isLoading');
};
if (active) {
cn.push('active');
};
const handleMouseEnter = (e: MouseEvent) => {
if (tooltip) {
Preview.tooltipShow({ text: tooltip, element: $(nodeRef.current), typeX: tooltipX, typeY: tooltipY });

View file

@ -316,4 +316,4 @@ const Input = forwardRef<InputRef, Props>(({
);
});
export default Input;
export default Input;

View file

@ -35,7 +35,10 @@ const HeaderMainObject = observer(class HeaderMainObject extends React.Component
const object = S.Detail.get(rootId, rootId, J.Relation.template);
const isLocked = root ? root.isLocked() : false;
const showMenu = true;
const isTypeOrRelation = U.Object.isTypeOrRelationLayout(object.layout);
const isDate = U.Object.isDateLayout(object.layout);
const showRelations = !isTypeOrRelation && !isDate;
const showMenu = true; //!isTypeOrRelation;
const canSync = showMenu && !object.templateIsBundled && !U.Object.isParticipantLayout(object.layout);
const cmd = keyboard.cmdSymbol();
const allowedTemplateSelect = (object.internalFlags || []).includes(I.ObjectFlag.SelectTemplate);
@ -95,7 +98,7 @@ const HeaderMainObject = observer(class HeaderMainObject extends React.Component
</div>
<div className="side right">
{showMenu ? <Icon id="button-header-relation" tooltip="Relations" tooltipCaption={`${cmd} + Shift + R`} className="relation withBackground" onClick={this.onRelation} /> : ''}
{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} /> : ''}
</div>
</React.Fragment>

View file

@ -1,7 +1,7 @@
import * as React from 'react';
import { observer } from 'mobx-react';
import { IconObject, Pager, ObjectName, Cell, SelectionTarget, Icon } from 'Component';
import { I, C, S, U, J, Relation, translate, keyboard } from 'Lib';
import { I, C, S, U, J, Relation, translate, keyboard, analytics } from 'Lib';
interface Column {
relationKey: string;
@ -20,6 +20,12 @@ interface Props {
sources?: string[];
filters?: I.Filter[];
relationKeys?: string[];
route: string;
};
interface State {
sortId: string;
sortType: I.SortType;
};
interface State {
@ -39,19 +45,20 @@ const ListObject = observer(class ListObject extends React.Component<Props, Stat
columns: [],
sources: [],
filters: [],
route: '',
};
state = {
sortId: 'lastModifiedDate',
sortId: '',
sortType: I.SortType.Desc,
};
render () {
const { subId, rootId } = this.props;
const { sortId, sortType } = this.state;
const columns = this.getColumns();
const items = this.getItems();
const { offset, total } = S.Record.getMeta(subId, '');
const columns = this.getColumns();
let pager = null;
if (total && items.length) {
@ -89,7 +96,7 @@ const ListObject = observer(class ListObject extends React.Component<Props, Stat
onContextMenu={e => this.onContext(e, item.id)}
>
{columns.map(column => {
const cn = [ 'cell' ];
const cn = [ 'cell', `c-${column.relationKey}` ];
const cnc = [ 'cellContent' ];
const value = item[column.relationKey];
@ -192,17 +199,17 @@ const ListObject = observer(class ListObject extends React.Component<Props, Stat
};
componentDidMount () {
this.getData(1);
const columns = this.getColumns();
if (columns.length) {
this.setState({ sortId: columns[0].relationKey }, () => this.getData(1));
};
};
componentWillUnmount(): void {
C.ObjectSearchUnsubscribe([ this.props.subId ]);
};
getColumns (): Column[] {
return ([ { relationKey: 'name', name: translate('commonName'), isObject: true } ] as any[]).concat(this.props.columns || []);
};
getItems () {
return S.Record.getRecords(this.props.subId, this.getKeys());
};
@ -211,6 +218,10 @@ const ListObject = observer(class ListObject extends React.Component<Props, Stat
return J.Relation.default.concat(this.props.columns.map(it => it.relationKey));
};
getColumns (): Column[] {
return ([ { relationKey: 'name', name: translate('commonName'), isObject: true } ] as any[]).concat(this.props.columns || []);
};
getData (page: number, callBack?: (message: any) => void) {
const { sortId, sortType } = this.state;
const { spaceId, subId, sources } = this.props;
@ -263,6 +274,7 @@ const ListObject = observer(class ListObject extends React.Component<Props, Stat
};
onSort (relationKey: string): void {
const { route } = this.props;
const { sortId, sortType } = this.state;
let type = I.SortType.Asc;
@ -272,6 +284,7 @@ const ListObject = observer(class ListObject extends React.Component<Props, Stat
};
this.setState({ sortId: relationKey, sortType: type }, () => this.getData(1));
analytics.event('ObjectListSort', { relationKey, route, type });
};
});

View file

@ -182,6 +182,7 @@ const MenuBlockContext = observer(class MenuBlockContext extends React.Component
};
const { from, to } = range;
const object = S.Detail.get(rootId, rootId);
keyboard.disableContextClose(true);
focus.set(blockId, range);
@ -211,6 +212,8 @@ const MenuBlockContext = observer(class MenuBlockContext extends React.Component
marks = Mark.toggle(marks, { type, param: '', range: { from, to } });
S.Menu.updateData(this.props.id, { marks });
onChange(marks);
analytics.event('ChangeTextStyle', { type, count: 1, objectType: object?.type });
break;
};
@ -312,6 +315,7 @@ const MenuBlockContext = observer(class MenuBlockContext extends React.Component
S.Menu.updateData(this.props.id, { marks });
onChange(marks);
analytics.event('ChangeTextStyle', { type: newType, count: 1, objectType: object?.type });
window.setTimeout(() => focus.apply(), 15);
}
});
@ -348,7 +352,9 @@ const MenuBlockContext = observer(class MenuBlockContext extends React.Component
};
marks = Mark.toggle(marks, { type, param, range: { from, to } });
S.Menu.updateData(this.props.id, { marks });
S.Menu.updateData(this.props.id, { marks })
analytics.event('ChangeTextStyle', { type, count: 1, objectType: object?.type });
onChange(marks);
},
});

View file

@ -2,7 +2,7 @@ import * as React from 'react';
import { observer } from 'mobx-react';
import $ from 'jquery';
import { MenuItemVertical, Loader, ObjectName, EmptySearch } from 'Component';
import { I, S, U, J, keyboard, Mark, translate, analytics } from 'Lib';
import { I, S, U, J, C, keyboard, Mark, translate, analytics } from 'Lib';
import { AutoSizer, CellMeasurer, InfiniteLoader, List, CellMeasurerCache } from 'react-virtualized';
interface State {
@ -31,6 +31,7 @@ const MenuBlockMention = observer(class MenuBlockMention extends React.Component
constructor (props: I.Menu) {
super(props);
this.rebind = this.rebind.bind(this);
this.onClick = this.onClick.bind(this);
this.loadMoreRows = this.loadMoreRows.bind(this);
};
@ -67,10 +68,15 @@ const MenuBlockMention = observer(class MenuBlockMention extends React.Component
cn.push('isHidden');
};
let object = null;
if (![ 'add', 'selectDate' ].includes(item.id)) {
object = item;
};
content = (
<MenuItemVertical
id={item.id}
object={item.id == 'add' ? undefined : item}
object={object}
icon={item.icon}
name={<ObjectName object={item} />}
onMouseEnter={e => this.onOver(e, item)}
@ -190,10 +196,30 @@ const MenuBlockMention = observer(class MenuBlockMention extends React.Component
const { canAdd } = data;
const filter = this.getFilter();
const sections: any[] = [];
const length = this.items.length;
let items = U.Common.objectCopy(this.items);
const dates = items.filter(it => U.Object.isDateLayout(it.layout));
items = items.filter(it => !U.Object.isDateLayout(it.layout));
const length = items.length;
if (dates.length) {
sections.push({
id: 'date',
name: translate('commonDates'),
children: [
...dates,
{ id: 'selectDate', icon: 'relation c-date', name: translate(`placeholderCell${I.RelationType.Date}`) },
{ isDiv: true },
]
});
};
if (length) {
sections.push({ id: I.MarkType.Object, name: translate('commonObjects'), children: this.items });
sections.push({ id: I.MarkType.Object, name: translate('commonObjects'), children: items.filter(it => !U.Object.isDateLayout(it.layout)) });
};
if (filter && canAdd) {
@ -255,6 +281,7 @@ 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 });
@ -301,7 +328,8 @@ const MenuBlockMention = observer(class MenuBlockMention extends React.Component
return;
};
const { param, close } = this.props;
const { space } = S.Common;
const { param, getId } = this.props;
const { data } = param;
const { onChange } = data;
const { from } = S.Common.filter;
@ -319,19 +347,47 @@ const MenuBlockMention = observer(class MenuBlockMention extends React.Component
});
onChange(object, name + ' ', marks, from, to + 1);
analytics.event('ChangeTextStyle', { type: I.MarkType.Mention, count: 1, objectType: object.type });
};
let close = true;
if (item.id == 'add') {
const name = this.getFilter();
U.Object.create('', '', { name }, I.BlockPosition.Bottom, '', [ I.ObjectFlag.SelectType, I.ObjectFlag.SelectTemplate ], analytics.route.mention, (message: any) => {
cb(message.details);
});
} else
if (item.id == 'selectDate') {
close = false;
S.Menu.open('dataviewCalendar', {
element: `#${getId()} #item-${item.id}`,
horizontal: I.MenuDirection.Center,
data: {
rebind: this.rebind,
canEdit: true,
canClear: false,
value: U.Date.now(),
onChange: (value: number) => {
C.ObjectDateByTimestamp(space, value, (message: any) => {
if (!message.error.code) {
cb(message.details);
this.props.close();
};
});
},
},
});
} else {
cb(item);
};
close();
if (close) {
this.props.close();
};
};
getRowHeight (item: any) {

View file

@ -93,31 +93,6 @@ const MenuBlockRelationEdit = observer(class MenuBlockRelationEdit extends React
</div>
);
};
/*
const opts = (
<React.Fragment>
{isDate && relation ? (
<div className="section">
<div className="item" onMouseEnter={this.menuClose}>
<Icon className="clock" />
<div className="name">{translate('menuBlockRelationEditIncludeTime')}</div>
<Switch value={relation ? relation.includeTime : false} onChange={(e: any, v: boolean) => { this.onChangeTime(v); }} />
</div>
<MenuItemVertical
id="date-settings"
icon="settings"
name={translate('commonPreferences')}
arrow={!isReadonly}
readonly={isReadonly}
onMouseEnter={this.onDateSettings}
/>
</div>
) : ''}
</React.Fragment>
);
*/
return (
<form

View file

@ -12,7 +12,7 @@ const MenuCalendar = observer(class MenuCalendar extends React.Component<I.Menu>
render () {
const { param } = this.props;
const { data, classNameWrap } = param;
const { value, isEmpty, canEdit } = data;
const { value, isEmpty, canEdit, canClear = true } = data;
const items = this.getData();
const { m, y } = U.Date.getCalendarDateParam(value);
const todayParam = U.Date.getCalendarDateParam(this.originalValue);
@ -94,11 +94,10 @@ const MenuCalendar = observer(class MenuCalendar extends React.Component<I.Menu>
return (
<div
key={i}
id={[ 'day', item.d, item.m, item.y ].join('-')}
className={cn.join(' ')}
onClick={(e: any) => {
e.stopPropagation();
this.setValue(U.Date.timestamp(item.y, item.m, item.d), true, true);
}}
onClick={e => this.onClick(e, item)}
onContextMenu={e => this.onContextMenu(e, item)}
>
{item.d}
</div>
@ -115,7 +114,7 @@ const MenuCalendar = observer(class MenuCalendar extends React.Component<I.Menu>
<div className="btn" onClick={() => this.setValue(U.Date.mergeTimeWithDate(tomorrow, value), true, true)}>{translate('commonTomorrow')}</div>
</div>
<div className="side right">
<div className="btn clear" onClick={() => this.setValue(null, true, true)}>{translate('commonClear')}</div>
{canClear && <div className="btn clear" onClick={() => this.setValue(null, true, true)}>{translate('commonClear')}</div>}
</div>
</div>
</div>
@ -146,6 +145,43 @@ const MenuCalendar = observer(class MenuCalendar extends React.Component<I.Menu>
this.props.position();
};
onClick (e: any, item: any) {
e.stopPropagation();
const { param } = this.props;
const { data } = param;
const { canEdit } = 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));
};
};
onContextMenu (e: any, item: any) {
e.preventDefault();
const { getId, param } = this.props;
const { className, classNameWrap } = param;
S.Menu.open('select', {
element: `#${getId()} #${[ 'day', item.d, item.m, item.y ].join('-')}`,
offsetY: 4,
noFlipY: true,
className,
classNameWrap,
data: {
options: [
{ id: 'open', icon: 'expand', name: translate('commonOpenObject') },
],
onSelect: () => {
U.Object.openDateByTimestamp(U.Date.timestamp(item.y, item.m, item.d));
}
}
});
};
setValue (value: number, save: boolean, close: boolean) {
const { param, id } = this.props;
const { data } = param;

View file

@ -445,6 +445,10 @@ class MenuContext extends React.Component<I.Menu> {
};
case 'createWidget': {
if (!first) {
break;
};
const firstBlock = S.Block.getFirstBlock(S.Block.widgets, 1, it => it.isWidget());
Action.createWidgetFromObject(first.id, first.id, firstBlock?.id, I.BlockPosition.Top, analytics.route.addWidgetMenu);
@ -468,4 +472,4 @@ class MenuContext extends React.Component<I.Menu> {
};
export default MenuContext;
export default MenuContext;

View file

@ -17,7 +17,6 @@ const MenuRelationEdit = observer(class MenuRelationEdit extends React.Component
this.onRelationType = this.onRelationType.bind(this);
this.onObjectType = this.onObjectType.bind(this);
this.onDateSettings = this.onDateSettings.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.onChange = this.onChange.bind(this);
this.onClick = this.onClick.bind(this);
@ -84,15 +83,6 @@ const MenuRelationEdit = observer(class MenuRelationEdit extends React.Component
switchValue={viewRelation?.includeTime}
onSwitch={(e: any, v: boolean) => this.onChangeTime(v)}
/>
<MenuItemVertical
id="date-settings"
icon="settings"
name={translate('commonPreferences')}
arrow={true}
onMouseEnter={this.onDateSettings}
onClick={this.onDateSettings}
/>
</div>
);
};
@ -152,7 +142,7 @@ const MenuRelationEdit = observer(class MenuRelationEdit extends React.Component
key={c}
{...action}
onClick={e => this.onClick(e, action)}
onMouseEnter={this.menuClose}
onMouseEnter={e => this.onMouseEnter(e, action)}
/>
))}
</div>
@ -220,6 +210,7 @@ const MenuRelationEdit = observer(class MenuRelationEdit extends React.Component
const canFilter = !isFile;
const canSort = !isFile;
const canHide = relation && (relation.relationKey != 'name');
const canCalculate = relation;
let unlinkText = translate('commonUnlink');
if (U.Object.isCollectionLayout(object.layout)) {
@ -255,16 +246,25 @@ const MenuRelationEdit = observer(class MenuRelationEdit extends React.Component
];
if (extendedOptions && !readonly) {
sections.push({
children: [
canFilter ? { id: 'filter', icon: 'relation-filter', name: translate('menuDataviewRelationEditAddFilter') } : null,
canSort ? { id: 'sort0', icon: 'relation-sort0', name: translate('menuDataviewRelationEditSortAscending'), type: I.SortType.Asc } : null,
canSort ? { id: 'sort1', icon: 'relation-sort1', name: translate('menuDataviewRelationEditSortDescending'), type: I.SortType.Desc } : null,
{ id: 'insert-left', icon: 'relation-insert-left', name: translate('menuDataviewRelationEditInsertLeft'), dir: -1 },
{ id: 'insert-right', icon: 'relation-insert-right', name: translate('menuDataviewRelationEditInsertRight'), dir: 1 },
canHide ? { id: 'hide', icon: 'relation-hide', name: translate('menuDataviewRelationEditHideRelation') } : null,
]
});
sections = sections.concat([
{
children: [
canFilter ? { id: 'filter', icon: 'relation-filter', name: translate('menuDataviewRelationEditAddFilter') } : null,
canSort ? { id: 'sort0', icon: 'relation-sort0', name: translate('menuDataviewRelationEditSortAscending'), type: I.SortType.Asc } : null,
canSort ? { id: 'sort1', icon: 'relation-sort1', name: translate('menuDataviewRelationEditSortDescending'), type: I.SortType.Desc } : null,
{ id: 'insert-left', icon: 'relation-insert-left', name: translate('menuDataviewRelationEditInsertLeft'), dir: -1 },
{ id: 'insert-right', icon: 'relation-insert-right', name: translate('menuDataviewRelationEditInsertRight'), dir: 1 },
canHide ? { id: 'hide', icon: 'relation-hide', name: translate('menuDataviewRelationEditHideRelation') } : null,
]
},
{
children: [
canCalculate ? { id: 'calculate', icon: 'relation c-number', name: translate('commonCalculate'), arrow: true } : null,
]
},
]);
};
sections = sections.filter((s: any) => {
@ -286,6 +286,97 @@ const MenuRelationEdit = observer(class MenuRelationEdit extends React.Component
return items;
};
onMouseEnter (e: any, item: any) {
if (!keyboard.isMouseDisabled) {
this.onOver(e, item);
};
};
onOver (e: any, item: any) {
if (!item.arrow) {
this.menuClose();
return;
};
const { getId, getSize, param } = this.props;
const { classNameWrap, data } = param;
const { rootId } = data;
const relation = this.getRelation();
const object = S.Detail.get(rootId, rootId);
if (!relation) {
return;
};
let menuContext = null;
let menuId = '';
const menuParam: any = {
menuKey: item.id,
element: `#${getId()} #item-${item.id}`,
vertical: I.MenuDirection.Center,
isSub: true,
offsetX: getSize().width,
classNameWrap,
onOpen: context => menuContext = context,
data: {},
};
switch (item.id) {
case 'calculate': {
const save = (id: any) => {
id = Number(id) || 0;
this.saveViewRelation('formulaType', id);
analytics.event('ChangeGridFormula', { type: id, format: relation.format, objectType: object.type });
};
menuId = 'select';
menuParam.subIds = [ 'select2' ];
menuParam.data = Object.assign(menuParam.data, {
options: U.Menu.getFormulaSections(relation.relationKey),
onOver: (e: any, item: any) => {
if (!item.arrow) {
S.Menu.closeAll([ 'select2' ]);
return;
};
const options = Relation.formulaByType(relation.format).filter(it => it.section == item.id);
S.Menu.closeAll([ 'select2' ], () => {
S.Menu.open('select2', {
component: 'select',
element: `#${menuContext.getId()} #item-${item.id}`,
offsetX: menuContext.getSize().width,
vertical: I.MenuDirection.Center,
isSub: true,
data: {
rootId,
options,
rebind: menuContext.ref?.rebind,
onSelect: (e: any, item: any) => {
save(item.id);
menuContext.close();
},
}
});
});
},
onSelect: (e: any, item: any) => {
save(item.id);
},
});
break;
};
};
if (menuId && !S.Menu.isOpen(menuId, item.id)) {
S.Menu.closeAll(J.Menu.relationEdit, () => {
S.Menu.open(menuId, menuParam);
});
};
};
onClick (e: any, item: any) {
e.preventDefault();
e.stopPropagation();
@ -402,10 +493,7 @@ const MenuRelationEdit = observer(class MenuRelationEdit extends React.Component
};
case 'hide': {
C.BlockDataviewViewRelationReplace(rootId, blockId, view.id, relation.relationKey, {
...view.getRelation(relation.relationKey),
isVisible: false,
});
this.saveViewRelation('isVisible', false);
break;
};
};
@ -497,39 +585,12 @@ const MenuRelationEdit = observer(class MenuRelationEdit extends React.Component
});
};
onDateSettings (e: any) {
e.preventDefault();
e.stopPropagation();
const { param, getId } = this.props;
const { data } = param;
const { readonly } = data;
if (readonly) {
return;
};
const relation = this.getRelation();
this.menuOpen('dataviewDate', {
element: `#${getId()} #item-date-settings`,
onClose: () => {
S.Menu.close('select');
},
data: {
...data,
relationKey: relation.relationKey,
}
});
};
menuOpen (id: string, options: I.MenuParam) {
const { getSize, param } = this.props;
const { classNameWrap } = param;
options = Object.assign(options, {
isSub: true,
passThrough: true,
offsetX: getSize().width,
vertical: I.MenuDirection.Center,
classNameWrap,
@ -551,15 +612,7 @@ const MenuRelationEdit = observer(class MenuRelationEdit extends React.Component
};
onChangeTime (v: boolean) {
const { param } = this.props;
const { data } = param;
const { rootId, blockId, getView } = data;
const view = getView();
const relation = this.getViewRelation();
if (view && relation) {
C.BlockDataviewViewRelationReplace(rootId, blockId, view.id, relation.relationKey, { ...relation, includeTime: v });
};
this.saveViewRelation('includeTime', v);
};
onSubmit (e: any) {
@ -619,6 +672,18 @@ const MenuRelationEdit = observer(class MenuRelationEdit extends React.Component
return readonly || !isAllowed || (relation && relation.isReadonlyRelation);
};
saveViewRelation (k: string, v: any) {
const { param } = this.props;
const { data } = param;
const { rootId, blockId, getView } = data;
const relation = this.getViewRelation();
const view = getView();
if (view && relation) {
C.BlockDataviewViewRelationReplace(rootId, blockId, view.id, relation.relationKey, { ...relation, [k]: v });
};
};
save () {
const name = this.ref ? this.ref.getValue() : '';
if (!name) {

View file

@ -94,6 +94,14 @@ class MenuObject extends React.Component<I.Menu> {
const object = this.getObject();
const cmd = keyboard.cmdSymbol();
const isTemplate = U.Object.isTemplate(object.type);
const isDate = U.Object.isDateLayout(object.layout);
const isChat = U.Object.isChatLayout(object.layout);
const isBookmark = U.Object.isBookmarkLayout(object.layout);
const isParticipant = U.Object.isParticipantLayout(object.layout);
const isInSetLayouts = U.Object.isInSetLayouts(object.layout);
const isInFileLayouts = U.Object.isInFileLayouts(object.layout);
const isInFileOrSystemLayouts = U.Object.isInFileOrSystemLayouts(object.layout);
const isTypeOrRelationLayout = U.Object.isTypeOrRelationLayout(object.layout);
const canWrite = U.Space.canMyParticipantWrite();
const canDelete = S.Block.checkFlags(rootId, rootId, [ I.RestrictionObject.Delete ]);
@ -157,31 +165,31 @@ class MenuObject extends React.Component<I.Menu> {
// Restrictions
const hasShortMenu = (
U.Object.isTypeOrRelationLayout(object.layout) ||
U.Object.isInFileLayouts(object.layout) ||
U.Object.isInSetLayouts(object.layout) ||
U.Object.isParticipantLayout(object.layout) ||
U.Object.isChatLayout(object.layout)
isTypeOrRelationLayout ||
isInFileLayouts ||
isInSetLayouts ||
isParticipant ||
isChat
);
const allowedArchive = canWrite && canDelete;
const allowedSearch = !isFilePreview && !U.Object.isInSetLayouts(object.layout);
const allowedHistory = !object.isArchived && !U.Object.isInFileOrSystemLayouts(object.layout) && !U.Object.isParticipantLayout(object.layout) && !object.templateIsBundled;
const allowedSearch = !isFilePreview && !isInSetLayouts;
const allowedHistory = !object.isArchived && !isInFileOrSystemLayouts && !isParticipant && !isDate && !object.templateIsBundled;
const allowedFav = canWrite && !object.isArchived && !object.templateIsBundled;
const allowedLock = canWrite && !object.isArchived && S.Block.checkFlags(rootId, rootId, [ I.RestrictionObject.Details ]);
const allowedLinkTo = canWrite && !object.isArchived;
const allowedAddCollection = canWrite && !object.isArchived;
const allowedPageLink = !object.isArchived;
const allowedCopy = canWrite && !object.isArchived && S.Block.checkFlags(rootId, rootId, [ I.RestrictionObject.Duplicate ]);
const allowedReload = canWrite && object.source && U.Object.isBookmarkLayout(object.layout);
const allowedInstall = canWrite && !object.isInstalled && U.Object.isTypeOrRelationLayout(object.layout);
const allowedUninstall = canWrite && object.isInstalled && U.Object.isTypeOrRelationLayout(object.layout) && canDelete;
const allowedReload = canWrite && object.source && isBookmark;
const allowedInstall = canWrite && !object.isInstalled && isTypeOrRelationLayout;
const allowedUninstall = canWrite && object.isInstalled && isTypeOrRelationLayout && canDelete;
const allowedTemplate = canWrite && !U.Object.getLayoutsWithoutTemplates().includes(object.layout) && S.Block.checkFlags(rootId, rootId, [ I.RestrictionObject.Template ]);
const allowedWidget = canWrite && !object.isArchived && !S.Block.checkBlockTypeExists(rootId);
const allowedExport = !isFilePreview && !U.Object.isChatLayout(object.layout);
const allowedExport = !isFilePreview && !isChat && !isDate;
const allowedPrint = !isFilePreview;
const allowedDownloadFile = U.Object.isInFileLayouts(object.layout);
const allowedOpenFile = U.Object.isInFileLayouts(object.layout);
const allowedDownloadFile = isInFileLayouts;
const allowedOpenFile = isInFileLayouts;
const allowedOpenObject = isFilePreview;
if (!allowedArchive) archive = null;

View file

@ -453,10 +453,10 @@ const MenuSelect = observer(class MenuSelect extends React.Component<I.Menu> {
content.css({ height });
};
obj.toggleClass('withFilter', !!withFilter);
obj.toggleClass('withAdd', !!withAdd);
obj.toggleClass('noScroll', !!noScroll);
obj.toggleClass('noVirtualisation', !!noVirtualisation);
obj.toggleClass('withFilter', Boolean(withFilter));
obj.toggleClass('withAdd', Boolean(withAdd));
obj.toggleClass('noScroll', Boolean(noScroll));
obj.toggleClass('noVirtualisation', Boolean(noVirtualisation));
position();
};

View file

@ -1,7 +1,7 @@
import * as React from 'react';
import { observer } from 'mobx-react';
import { IconObject, Block, Button, Editable } from 'Component';
import { I, M, S, U, J, Action, focus, keyboard, Relation, translate } from 'Lib';
import { IconObject, Block, Button, Editable, Icon } from 'Component';
import { I, M, S, U, J, Action, focus, keyboard, Relation, translate, analytics } from 'Lib';
interface Props {
rootId: string;
@ -46,8 +46,11 @@ const HeadSimple = observer(class Controls extends React.Component<Props> {
const blockFeatured: any = new M.Block({ id: 'featuredRelations', type: I.BlockType.Featured, childrenIds: [], fields: {}, content: {} });
const isTypeOrRelation = U.Object.isTypeOrRelationLayout(object.layout);
const isType = U.Object.isTypeLayout(object.layout);
const isDate = U.Object.isDateLayout(object.layout);
const isRelation = U.Object.isRelationLayout(object.layout);
const canEditIcon = allowDetails && !U.Object.isRelationLayout(object.layout);
const cn = [ 'headSimple', check.className ];
const placeholder = {
title: this.props.placeholder,
description: translate('placeholderBlockDescription'),
@ -77,7 +80,7 @@ const HeadSimple = observer(class Controls extends React.Component<Props> {
let descr = null;
let featured = null;
if (!isTypeOrRelation) {
if (!isTypeOrRelation && !isDate) {
if (featuredRelations.includes('description')) {
descr = <Editor className="descr" id="description" />;
};
@ -117,6 +120,16 @@ const HeadSimple = observer(class Controls extends React.Component<Props> {
};
};
if (isDate) {
buttonCreate = (
<React.Fragment>
<Icon className="arrow left withBackground" onClick={() => this.changeDate(-1)} />
<Icon className="arrow right withBackground" onClick={() => this.changeDate(1)}/>
<Icon id="calendar-icon" className="calendar withBackground" onClick={this.onCalendar} />
</React.Fragment>
);
};
if (!canWrite) {
buttonCreate = null;
};
@ -233,6 +246,7 @@ const HeadSimple = observer(class Controls extends React.Component<Props> {
};
setValue () {
const { dateFormat } = S.Common;
const { rootId } = this.props;
const object = S.Detail.get(rootId, rootId);
@ -242,6 +256,11 @@ const HeadSimple = observer(class Controls extends React.Component<Props> {
};
let text = String(object[item.relationKey] || '');
if (U.Object.isDateLayout(object.layout) && object.timestamp) {
text = U.Date.dateWithFormat(dateFormat, object.timestamp);
};
if (text == translate('defaultNamePage')) {
text = '';
};
@ -285,6 +304,32 @@ const HeadSimple = observer(class Controls extends React.Component<Props> {
return sources.includes(rootId);
};
onCalendar = () => {
const { rootId } = this.props;
const object = S.Detail.get(rootId, rootId);
S.Menu.open('dataviewCalendar', {
element: '#calendar-icon',
horizontal: I.MenuDirection.Center,
data: {
value: object.timestamp,
canEdit: true,
canClear: false,
onChange: (value: number) => U.Object.openDateByTimestamp(value),
},
});
analytics.event('ClickDateCalendarView');
};
changeDate = (dir: number) => {
const { rootId } = this.props;
const object = S.Detail.get(rootId, rootId);
U.Object.openDateByTimestamp(object.timestamp + dir * 86400);
analytics.event(dir > 0 ? 'ClickDateForward' : 'ClickDateBack');
};
});
export default HeadSimple;

View file

@ -31,6 +31,7 @@ import PageMainMembership from './main/membership';
import PageMainObject from './main/object';
import PageMainOnboarding from './main/onboarding';
import PageMainChat from './main/chat';
import PageMainDate from './main/date';
const Components = {
'index/index': PageAuthSelect,
@ -61,6 +62,7 @@ const Components = {
'main/onboarding': PageMainOnboarding,
'main/chat': PageMainChat,
'main/void': PageMainVoid,
'main/date': PageMainDate,
};
const Page = observer(class Page extends React.Component<I.PageComponent> {

View file

@ -0,0 +1,244 @@
import * as React from 'react';
import { observer } from 'mobx-react';
import { Header, Footer, Deleted, ListObject, Button } from 'Component';
import { I, C, S, U, Action, translate, analytics } from 'Lib';
import HeadSimple from 'Component/page/elements/head/simple';
interface State {
isDeleted: boolean;
relations: any[];
selectedRelation: string;
};
const SUB_ID = 'dateListObject';
const RELATION_KEY_MENTION = 'mentions';
const PageMainDate = observer(class PageMainDate extends React.Component<I.PageComponent, State> {
_isMounted = false;
node: any = null;
id = '';
refHeader: any = null;
refHead: any = null;
refList: any = null;
refCalIcon: any = null;
loading = false;
timeout = 0;
state = {
isDeleted: false,
relations: [],
selectedRelation: RELATION_KEY_MENTION,
};
render () {
const { space } = S.Common;
const { isDeleted, relations, selectedRelation } = this.state;
const rootId = this.getRootId();
const object = S.Detail.get(rootId, rootId, []);
if (isDeleted) {
return <Deleted {...this.props} />;
};
const relation = S.Record.getRelationByKey(selectedRelation);
if (!relation) {
return null;
};
const columns: any[] = [
{ relationKey: 'type', name: translate('commonObjectType'), isObject: true },
{ relationKey: 'creator', name: translate('relationCreator'), isObject: true },
];
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: selectedRelation, condition: I.FilterCondition.Equal, value: object.timestamp, format: I.RelationType.Date });
};
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}
/>
<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={selectedRelation == item.relationKey}
color="blank"
className="c36"
onClick={() => this.onCategory(item.relationKey)}
icon={icon}
text={item.name}
/>
{relations.length > 1 ? separator : ''}
</React.Fragment>
);
})}
</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}
/>
</div>
</div>
<Footer component="mainObject" {...this.props} />
</div>
);
};
componentDidMount () {
this._isMounted = true;
this.open();
};
componentDidUpdate () {
this.open();
this.checkDeleted();
};
componentWillUnmount () {
this._isMounted = false;
this.close();
};
checkDeleted () {
const { isDeleted } = this.state;
if (isDeleted) {
return;
};
const rootId = this.getRootId();
const object = S.Detail.get(rootId, rootId, []);
if (object.isDeleted) {
this.setState({ isDeleted: true });
};
};
open () {
const rootId = this.getRootId();
if (this.id == rootId) {
return;
};
this.close();
this.id = rootId;
this.setState({ isDeleted: false });
C.ObjectOpen(rootId, '', U.Router.getRouteSpaceId(), (message: any) => {
if (!U.Common.checkErrorOnOpen(rootId, message.error.code, this)) {
return;
};
const object = S.Detail.get(rootId, rootId, []);
if (object.isDeleted) {
this.setState({ isDeleted: true });
return;
};
this.refHeader?.forceUpdate();
this.refHead?.forceUpdate();
this.loadCategory();
});
};
close () {
if (!this.id) {
return;
};
const { isPopup, match } = this.props;
let close = true;
if (isPopup && (match.params.id == this.id)) {
close = false;
};
if (close) {
Action.pageClose(this.id, true);
};
};
loadCategory () {
const { space, config } = S.Common;
const rootId = this.getRootId();
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)) {
return true;
};
if ([ 'links', 'backlinks' ].includes(it.relationKey)) {
return false;
};
return config.debug.hidden ? true : !it.isHidden;
});
relations.sort((c1, c2) => {
const isMention1 = c1.relationKey == RELATION_KEY_MENTION;
const isMention2 = c2.relationKey == RELATION_KEY_MENTION;
if (isMention1 && !isMention2) return -1;
if (!isMention1 && isMention2) return 1;
return 0;
});
if (relations.length) {
this.setState({ relations });
this.onCategory(relations[0].relationKey);
};
});
};
onCategory (relationKey: string) {
this.setState({ selectedRelation: relationKey }, () => {
this.refList?.getData(1);
});
analytics.event('SwitchRelationDate', { relationKey });
};
getRootId () {
const { rootId, match } = this.props;
return rootId ? rootId : match.params.id;
};
});
export default PageMainDate;

View file

@ -1,7 +1,7 @@
import * as React from 'react';
import { observer } from 'mobx-react';
import { Header, Footer, Loader, ListObject, Deleted, Icon } from 'Component';
import { I, C, S, U, Action, translate } from 'Lib';
import { I, C, S, U, Action, translate, analytics } from 'Lib';
import HeadSimple from 'Component/page/elements/head/simple';
interface State {
@ -44,7 +44,7 @@ const PageMainRelation = observer(class PageMainRelation extends React.Component
const columnsObject: any[] = [
{
relationKey: 'lastModifiedDate', name: translate('commonUpdated'),
mapper: v => U.Date.dateWithFormat(I.DateFormat.MonthAbbrBeforeDay, v),
mapper: v => U.Date.dateWithFormat(S.Common.dateFormat, v),
},
{ relationKey: object.relationKey, name: object.name, isCell: true }
];
@ -92,6 +92,7 @@ const PageMainRelation = observer(class PageMainRelation extends React.Component
rootId={rootId}
columns={[]}
filters={filtersType}
route={analytics.route.screenRelation}
/>
</div>
</div>
@ -122,6 +123,7 @@ const PageMainRelation = observer(class PageMainRelation extends React.Component
subId={subIdObject}
rootId={rootId}
columns={columnsObject}
route={analytics.route.screenRelation}
/>
</div>
</div>

View file

@ -70,7 +70,7 @@ const PageMainType = observer(class PageMainType extends React.Component<I.PageC
const columns: any[] = [
{
relationKey: 'lastModifiedDate', name: translate('commonUpdated'),
mapper: v => v ? U.Date.dateWithFormat(I.DateFormat.MonthAbbrBeforeDay, v) : '',
mapper: v => v ? U.Date.dateWithFormat(S.Common.dateFormat, v) : '',
},
];
@ -173,6 +173,7 @@ const PageMainType = observer(class PageMainType extends React.Component<I.PageC
rootId={rootId}
columns={columns}
relationKeys={recommended}
route={analytics.route.screenType}
/>
</div>
</div>

View file

@ -3,7 +3,7 @@ import $ from 'jquery';
import raf from 'raf';
import * as Docs from 'Docs';
import { Label, Icon, Cover, Button } from 'Component';
import { I, U, J, translate } from 'Lib';
import { I, U, J, translate, Action } from 'Lib';
import Block from 'Component/block/help';
interface State {
@ -55,7 +55,7 @@ class PopupHelp extends React.Component<I.Popup, State> {
</div>
<div className="side right">
<Label text={translate('popupHelpLabel')} />
<Icon onClick={() => U.Common.onUrl(J.Url.mail)} className="mail" />
<Icon onClick={() => Action.openUrl(J.Url.mail)} className="mail" />
</div>
</div>

View file

@ -168,7 +168,7 @@ const PopupMembershipPageCurrent = observer(class PopupMembershipPageCurrent ext
} else {
C.MembershipGetPortalLinkUrl((message: any) => {
if (message.url) {
U.Common.onUrl(message.url);
Action.openUrl(message.url);
};
});
};

View file

@ -196,7 +196,7 @@ const PopupMembershipPagePaid = observer(class PopupMembershipPagePaid extends R
};
if (message.url) {
U.Common.onUrl(message.url);
Action.openUrl(message.url);
};
analytics.event('ClickMembership', { params: { tier, method }});

View file

@ -1,7 +1,7 @@
import * as React from 'react';
import { observer } from 'mobx-react';
import { Title, Label, Button, Icon } from 'Component';
import { I, S, U, J, translate, analytics, keyboard } from 'Lib';
import { I, S, U, J, Action, translate, analytics, keyboard } from 'Lib';
import { Swiper, SwiperSlide } from 'swiper/react';
import { Pagination, Autoplay, Mousewheel } from 'swiper/modules';
@ -172,7 +172,7 @@ const PopupSettingsPageMembership = observer(class PopupSettingsPageMembership e
};
onLink (item: any) {
U.Common.onUrl(item.url);
Action.openUrl(item.url);
analytics.event(item.type, { route: analytics.route.settingsMembership });
};

View file

@ -7,7 +7,7 @@ const PopupSettingsPagePersonal = observer(class PopupSettingsPagePersonal exten
render () {
const { getId } = this.props;
const { config, interfaceLang, navigationMenu, linkStyle, fullscreenObject, hideSidebar, showRelativeDates, showVault } = S.Common;
const { config, interfaceLang, navigationMenu, linkStyle, fullscreenObject, hideSidebar, showRelativeDates, showVault, dateFormat, timeFormat, } = S.Common;
const { hideTray, hideMenuBar, languages } = config;
const canHideMenu = U.Common.isPlatformWindows() || U.Common.isPlatformLinux();
@ -105,17 +105,6 @@ const PopupSettingsPagePersonal = observer(class PopupSettingsPagePersonal exten
/>
</div>
<div className="item">
<Label text={translate('popupSettingsPersonalRelativeDates')} />
<Switch
className="big"
value={showRelativeDates}
onChange={(e: any, v: boolean) => {
S.Common.showRelativeDatesSet(v);
analytics.event('RelativeDates', { type: v });
}}
/>
</div>
</div>
<Label className="section" text={translate('popupSettingsPersonalSectionApp')} />
@ -157,6 +146,48 @@ const PopupSettingsPagePersonal = observer(class PopupSettingsPagePersonal exten
) : ''}
</div>
<Label className="section" text={translate('popupSettingsPersonalSectionDateTime')} />
<div className="actionItems">
<div className="item">
<Label text={translate('popupSettingsPersonalRelativeDates')} />
<Switch
className="big"
value={showRelativeDates}
onChange={(e: any, v: boolean) => {
S.Common.showRelativeDatesSet(v);
analytics.event('RelativeDates', { type: v });
}}
/>
</div>
<div className="item">
<Label text={translate('popupSettingsPersonalDateFormat')} />
<Select
id="dateFormat"
value={String(dateFormat)}
options={U.Menu.dateFormatOptions().map(it => ({ ...it, id: String(it.id) }))}
onChange={v => S.Common.dateFormatSet(v)}
arrowClassName="black"
menuParam={{ horizontal: I.MenuDirection.Right }}
/>
</div>
<div className="item">
<Label text={translate('popupSettingsPersonalTimeFormat')} />
<Select
id="timeFormat"
value={String(timeFormat)}
options={U.Menu.timeFormatOptions().map(it => ({ ...it, id: String(it.id) }))}
onChange={v => S.Common.timeFormatSet(v)}
arrowClassName="black"
menuParam={{ horizontal: I.MenuDirection.Right }}
/>
</div>
</div>
</React.Fragment>
);
};

View file

@ -408,7 +408,7 @@ const PopupSettingsSpaceIndex = observer(class PopupSettingsSpaceIndex extends R
<div className="sides">
<div className="side left">
<Title text={translate(`popupSettingsSpaceIndexCreationDateTitle`)} />
<Label text={U.Date.dateWithFormat(I.DateFormat.MonthAbbrBeforeDay, space.createdDate)} />
<Label text={U.Date.dateWithFormat(S.Common.dateFormat, space.createdDate)} />
</div>
</div>
</div>

View file

@ -97,7 +97,7 @@ class PopupUsecasePageItem extends React.Component<I.PopupUsecase, State> {
<Tag key={i} text={name} />
))}
</div>
<Label text={U.Common.sprintf(translate('popupUsecaseUpdated'), U.Date.dateWithFormat(I.DateFormat.MonthAbbrBeforeDay, U.Date.now()))} />
<Label text={U.Common.sprintf(translate('popupUsecaseUpdated'), U.Date.dateWithFormat(S.Common.dateFormat, U.Date.now()))} />
<Label text={U.File.size(object.size)} />
</div>
</div>

View file

@ -9,8 +9,6 @@ type State = {
previewId: string;
};
const SUB_ID = 'widgetArchive';
const SidebarPageWidget = observer(class SidebarPageWidget extends React.Component<{}, State> {
state: State = {
@ -46,7 +44,6 @@ const SidebarPageWidget = observer(class SidebarPageWidget extends React.Compone
const cn = [ 'body' ];
const space = U.Space.getSpaceview();
const canWrite = U.Space.canMyParticipantWrite();
const archived = S.Record.getRecordIds(SUB_ID, '');
if (isEditing) {
cn.push('isEditing');
@ -172,26 +169,24 @@ const SidebarPageWidget = observer(class SidebarPageWidget extends React.Compone
/>
))}
{archived.length ? (
<DropTarget
{...this.props}
isTargetBottom={true}
rootId={S.Block.widgets}
id={last?.id}
dropType={I.DropType.Widget}
canDropMiddle={false}
className="lastTarget"
cacheKey="lastTarget"
>
<Button
text={translate('commonBin')}
color=""
className="widget"
icon="bin"
onClick={this.onArchive}
/>
</DropTarget>
) : ''}
<DropTarget
{...this.props}
isTargetBottom={true}
rootId={S.Block.widgets}
id={last?.id}
dropType={I.DropType.Widget}
canDropMiddle={false}
className="lastTarget"
cacheKey="lastTarget"
>
<Button
text={translate('commonBin')}
color=""
className="widget"
icon="bin"
onClick={this.onArchive}
/>
</DropTarget>
<div className="buttons">
{buttons.map(button => (
@ -225,14 +220,6 @@ const SidebarPageWidget = observer(class SidebarPageWidget extends React.Compone
);
};
componentDidMount (): void {
this.subscribeArchive();
};
componentDidUpdate (): void {
this.subscribeArchive();
};
onEdit (e: any): void {
e.stopPropagation();
@ -528,26 +515,6 @@ const SidebarPageWidget = observer(class SidebarPageWidget extends React.Compone
}, S.Menu.getTimeout());
};
subscribeArchive () {
const { space } = S.Common
if (this.isSubcribed == space) {
return;
};
U.Data.searchSubscribe({
subId: SUB_ID,
spaceId: space,
ignoreArchived: false,
filters: [
{ relationKey: 'isArchived', condition: I.FilterCondition.Equal, value: true },
],
limit: 1,
}, () => {
this.isSubcribed = space;
});
};
});
export default SidebarPageWidget;

View file

@ -91,7 +91,7 @@ const FontSize = {
96: 64,
};
const DefaultIcons = [ 'page', 'task', 'set', 'chat', 'bookmark', 'type' ];
const DefaultIcons = [ 'page', 'task', 'set', 'chat', 'bookmark', 'type', 'date' ];
const Ghost = require('img/icon/ghost.svg');
const CheckboxTask = {
@ -182,6 +182,10 @@ const IconObject = observer(class IconObject extends React.Component<Props> {
break;
};
case I.ObjectLayout.Date:
defaultIcon('date');
break;
case I.ObjectLayout.Collection:
case I.ObjectLayout.Set: {
if (iconImage) {

View file

@ -2,7 +2,7 @@ import React, { forwardRef, useRef, useEffect } from 'react';
import $ from 'jquery';
import mermaid from 'mermaid';
import { useLocalObservable } from 'mobx-react';
import { J, S } from 'Lib';
import { J, S, U } from 'Lib';
interface Props {
chart: string;
@ -46,11 +46,13 @@ const MediaMermaid = forwardRef<HTMLDivElement, Props>(({
};
node.find('.mermaid').html(svg);
U.Common.renderLinks(node);
};
useEffect(() => {
init();
mermaid.contentLoaded();
U.Common.renderLinks($(nodeRef.current));
});
useEffect(() => {

View file

@ -1,7 +1,7 @@
import * as React from 'react';
import { observer } from 'mobx-react';
import { Select, Icon } from 'Component';
import { I, S, U, J } from 'Lib';
import { I, S, U, J, translate } from 'Lib';
interface State {
value: number;
@ -38,6 +38,7 @@ const WidgetViewCalendar = observer(class WidgetViewCalendar extends React.Compo
const months = U.Date.getMonths();
const years = U.Date.getYears(0, 3000);
const { groupRelationKey } = view;
const dotMap = this.getDotMap(groupRelationKey);
return (
<div ref={ref => this.node = ref} className="body">
@ -89,13 +90,14 @@ const WidgetViewCalendar = observer(class WidgetViewCalendar extends React.Compo
cn.push('first');
};
const check = this.checkItems(item.d, item.m, item.y, groupRelationKey);
const check = dotMap.get([ item.d, item.m, item.y ].join('-'));
return (
<div
id={`day-${item.d}-${item.m}-${item.y}`}
id={[ 'day', item.d, item.m, item.y ].join('-')}
key={i}
className={cn.join(' ')}
onClick={() => this.onClick(item.d, item.m, item.y)}
onContextMenu={(e: any) => this.onContextMenu(e, item)}
>
<div className="inner">
{item.d}
@ -110,6 +112,23 @@ const WidgetViewCalendar = observer(class WidgetViewCalendar extends React.Compo
);
};
onContextMenu = (e: any, item: any) => {
e.preventDefault();
e.stopPropagation();
S.Menu.open('select', {
element: '#' + [ 'day', item.d, item.m, item.y ].join('-'),
offsetY: 4,
noFlipY: true,
data: {
options: [ { id: 'open', icon: 'expand', name: translate('commonOpenObject') } ],
onSelect: () => {
U.Object.openDateByTimestamp(U.Date.timestamp(item.y, item.m, item.d), 'auto');
}
}
});
};
componentDidMount(): void {
this.setSelectsValue(this.state.value);
};
@ -220,15 +239,24 @@ const WidgetViewCalendar = observer(class WidgetViewCalendar extends React.Compo
];
};
checkItems (d: number, m: number, y: number, relationKey: string) {
getDotMap (relationKey: string): Map<string, boolean> {
const { value } = this.state;
const { rootId } = this.props;
const data = U.Date.getCalendarMonth(value);
const items = S.Record.getRecords(S.Record.getSubId(rootId, J.Constant.blockId.dataview), [ relationKey ]);
const current = [ d, m, y ].join('-');
const ret = new Map();
return !!items.find(it => U.Date.date('j-n-Y', it[relationKey]) == current);
data.forEach(it => {
const current = [ it.d, it.m, it.y ].join('-');
if (items.find(it => U.Date.date('j-n-Y', it[relationKey]) == current)) {
ret.set(current, true);
};
});
return ret;
};
});
export default WidgetViewCalendar;

View file

@ -12,6 +12,9 @@ export enum DateFormat {
Short = 2, // 30/07/2020
ShortUS = 3, // 07/30/2020
ISO = 4, // 2020-07-30
Long = 5, // July 15, 2020
Nordic = 6, // 15. Jul 2020
European = 7, // 15.07.2020
};
export enum TimeFormat {
@ -130,8 +133,6 @@ export interface ViewRelation {
isVisible?: boolean;
width?: number;
includeTime?: boolean;
dateFormat?: I.DateFormat;
timeFormat?: I.TimeFormat;
formulaType?: I.FormulaType;
};

View file

@ -315,6 +315,7 @@ export interface SearchSubscribeParam {
ignoreHidden: boolean;
ignoreDeleted: boolean;
ignoreArchived: boolean;
skipLayoutFormat: I.ObjectLayout[];
noDeps: boolean;
};

View file

@ -165,6 +165,10 @@ class Action {
};
openUrl (url: string) {
if (!url) {
return;
};
url = U.Common.urlFix(url);
const storageKey = 'openUrl';
@ -188,6 +192,14 @@ class Action {
};
};
openPath (path: string) {
if (!path) {
return;
};
Renderer.send('openPath', path);
};
openFile (id: string, route: string) {
if (!id) {
return;
@ -732,7 +744,7 @@ class Action {
let layout = I.WidgetLayout.Link;
if (object && !object._empty_) {
if (U.Object.isInFileOrSystemLayouts(object.layout)) {
if (U.Object.isInFileOrSystemLayouts(object.layout) || U.Object.isDateLayout(object.layout)) {
layout = I.WidgetLayout.Link;
} else
if (U.Object.isInSetLayouts(object.layout)) {

View file

@ -51,6 +51,10 @@ class Analytics {
archive: 'Bin',
toast: 'Toast',
screenDate: 'ScreenDate',
screenRelation: 'ScreenRelation',
screenType: 'ScreenType',
menuOnboarding: 'MenuOnboarding',
menuObject: 'MenuObject',
menuSystem: 'MenuSystem',
@ -510,6 +514,11 @@ class Analytics {
break;
};
case 'ObjectListSort': {
data.type = I.SortType[Number(data.type)];
break;
};
};
param.middleTime = Number(data.middleTime) || 0;
@ -575,6 +584,7 @@ class Analytics {
'main/relation': 'ScreenRelation',
'main/media': 'ScreenMedia',
'main/history': 'ScreenHistory',
'main/date': 'ScreenDate',
};
return map[key] || '';

View file

@ -1773,6 +1773,15 @@ export const ObjectChatAdd = (objectId: string, callBack?: (message: any) => voi
dispatcher.request(ObjectChatAdd.name, request, callBack);
};
export const ObjectDateByTimestamp = (spaceId: string, timestamp: number, callBack?: (message: any) => void) => {
const request = new Rpc.Object.DateByTimestamp.Request();
request.setSpaceid(spaceId);
request.setTimestamp(timestamp);
dispatcher.request(ObjectDateByTimestamp.name, request, callBack);
};
// ---------------------- OBJECT LIST ---------------------- //
export const ObjectListDuplicate = (ids: string[], callBack?: (message: any) => void) => {
@ -2234,3 +2243,12 @@ export const ChatGetMessagesByIds = (objectId: string, ids: string[], callBack?:
dispatcher.request(ChatGetMessagesByIds.name, request, callBack);
};
export const RelationListWithValue = (spaceId: string, value: any, callBack?: (message: any) => void) => {
const request = new Rpc.Relation.ListWithValue.Request();
request.setSpaceid(spaceId);
request.setValue(Encode.value(value));
dispatcher.request(RelationListWithValue.name, request, callBack);
};

View file

@ -353,8 +353,6 @@ export const Mapper = {
isVisible: obj.getIsvisible(),
width: obj.getWidth(),
includeTime: obj.getDateincludetime(),
timeFormat: obj.getTimeformat(),
dateFormat: obj.getDateformat(),
formulaType: obj.getFormula(),
};
},
@ -905,8 +903,6 @@ export const Mapper = {
item.setIsvisible(obj.isVisible);
item.setWidth(obj.width);
item.setDateincludetime(obj.includeTime);
item.setTimeformat(obj.timeFormat);
item.setDateformat(obj.dateFormat);
item.setFormula(obj.formulaType);
return item;

View file

@ -304,6 +304,12 @@ export const ObjectChatAdd = (response: Rpc.Object.ChatAdd.Response) => {
};
};
export const ObjectDateByTimestamp = (response: Rpc.Object.DateByTimestamp.Response) => {
return {
details: Decode.struct(response.getDetails()),
};
};
export const BlockCreate = (response: Rpc.Block.Create.Response) => {
return {
blockId: response.getBlockid(),
@ -586,4 +592,15 @@ export const ChatAddMessage = (response: Rpc.Chat.AddMessage.Response) => {
return {
messageId: response.getMessageid(),
};
};
};
export const RelationListWithValue = (response: Rpc.Relation.ListWithValue.Response) => {
return {
relations: (response.getListList() || []).map(it => {
return {
relationKey: it.getRelationkey(),
counter: it.getCounter(),
};
}),
};
};

View file

@ -551,7 +551,7 @@ class Dataview {
return null;
};
const { formulaType, includeTime, timeFormat, dateFormat, relationKey } = viewRelation;
const { formulaType, includeTime, relationKey } = viewRelation;
const relation = S.Record.getRelationByKey(relationKey);
if (!relation) {
@ -573,8 +573,8 @@ class Dataview {
};
const date = (t: number) => {
const date = U.Date.dateWithFormat(dateFormat, t);
const time = U.Date.timeWithFormat(timeFormat, t);
const date = U.Date.dateWithFormat(S.Common.dateFormat, t);
const time = U.Date.timeWithFormat(S.Common.timeFormat, t);
return includeTime ? [ date, time ].join(' ') : date;
};

View file

@ -139,8 +139,7 @@ class Mark {
if (add) {
map[type].push(mark);
};
analytics.event('ChangeTextStyle', { type, count: 1 });
return U.Common.unmap(map).sort(this.sort);
};

View file

@ -120,7 +120,7 @@ class Relation {
];
let ret: any[] = [
{ id: I.FormulaType.None, name: translate('formulaNone') },
{ id: I.FormulaType.None, name: translate('commonNone') },
];
switch (type) {

View file

@ -454,20 +454,13 @@ class UtilCommon {
links.on('auxclick', e => e.preventDefault());
links.click((e: any) => {
const el = $(e.currentTarget);
const href = el.attr('href') || el.attr('xlink:href');
e.preventDefault();
el.hasClass('path') ? this.onPath(el.attr('href')) : this.onUrl(el.attr('href'));
el.hasClass('path') ? Action.openPath(href) : Action.openUrl(href);
});
};
onUrl (url: string) {
Action.openUrl(url);
};
onPath (path: string) {
Renderer.send('openPath', path);
};
checkEmail (v: string) {
v = String(v || '');

View file

@ -947,9 +947,10 @@ class UtilData {
ignoreHidden: true,
ignoreDeleted: true,
ignoreArchived: true,
skipLayoutFormat: null,
}, param);
const { spaceId, idField, sorts, offset, limit } = param;
const { spaceId, idField, sorts, offset, limit, skipLayoutFormat } = param;
const keys: string[] = [ ...new Set(param.keys as string[]) ];
const filters = this.searchDefaultFilters(param);
@ -968,7 +969,7 @@ class UtilData {
C.ObjectSearch(spaceId, filters, sorts.map(this.sortMapper), keys, param.fullText, offset, limit, (message: any) => {
if (message.records) {
message.records = message.records.map(it => S.Detail.mapper(it));
message.records = message.records.map(it => S.Detail.mapper(it, skipLayoutFormat));
};
if (callBack) {
@ -1005,7 +1006,6 @@ class UtilData {
};
graphFilters () {
const { space } = S.Common;
const templateType = S.Record.getTemplateType();
const filters: any[] = [
{ relationKey: 'isHidden', condition: I.FilterCondition.NotEqual, value: true },

View file

@ -186,6 +186,9 @@ class UtilDate {
case I.DateFormat.Short: f = 'd.m.Y'; break;
case I.DateFormat.ShortUS: f = 'm.d.Y'; break;
case I.DateFormat.ISO: f = 'Y-m-d'; break;
case I.DateFormat.Long: f = 'F j, Y'; break;
case I.DateFormat.Nordic: f = 'j. M Y'; break;
case I.DateFormat.European: f = 'j.m.Y'; break;
};
return f;
};

View file

@ -16,6 +16,11 @@ class UtilGraph {
break;
};
case I.ObjectLayout.Date: {
src = `img/icon/relation/date.svg`;
break;
};
case I.ObjectLayout.Audio:
case I.ObjectLayout.Video:
case I.ObjectLayout.Pdf:

View file

@ -409,7 +409,7 @@ class UtilMenu {
if (!isSystem) {
const isSet = U.Object.isInSetLayouts(layout);
const setLayouts = U.Object.getSetLayouts();
const treeSkipLayouts = setLayouts.concat(U.Object.getFileAndSystemLayouts()).concat([ I.ObjectLayout.Participant ]);
const treeSkipLayouts = setLayouts.concat(U.Object.getFileAndSystemLayouts()).concat([ I.ObjectLayout.Participant, I.ObjectLayout.Date ]);
// Sets can only become Link and List layouts, non-sets can't become List
if (treeSkipLayouts.includes(layout)) {
@ -1043,6 +1043,9 @@ class UtilMenu {
{ id: I.DateFormat.Short },
{ id: I.DateFormat.ShortUS },
{ id: I.DateFormat.ISO },
{ id: I.DateFormat.Long },
{ id: I.DateFormat.Nordic },
{ id: I.DateFormat.European },
] as any[]).map(it => {
it.name = U.Date.dateWithFormat(it.id, U.Date.now());
return it;
@ -1078,6 +1081,22 @@ class UtilMenu {
});
};
getFormulaSections (relationKey: string) {
const relation = S.Record.getRelationByKey(relationKey);
const options = Relation.formulaByType(relation.format);
return [
{ id: I.FormulaSection.None, name: translate('commonNone') },
].concat([
{ id: I.FormulaSection.Count, name: translate('formulaCount'), arrow: true },
{ id: I.FormulaSection.Percent, name: translate('formulaPercentage'), arrow: true },
{ id: I.FormulaSection.Math, name: translate('formulaMath'), arrow: true },
{ id: I.FormulaSection.Date, name: translate('formulaDate'), arrow: true },
].filter(s => {
return options.filter(it => it.section == s.id).length;
})).map(it => ({ ...it, id: String(it.id), checkbox: false }));
};
};
export default new UtilMenu();

View file

@ -26,6 +26,7 @@ class UtilObject {
case I.ObjectLayout.Empty: r = 'empty'; break;
case I.ObjectLayout.Space:
case I.ObjectLayout.Chat: r = 'chat'; break;
case I.ObjectLayout.Date: r = 'date'; break;
};
return r;
};
@ -409,7 +410,6 @@ class UtilObject {
return [
I.ObjectLayout.Set,
I.ObjectLayout.Collection,
I.ObjectLayout.Date,
];
};
@ -483,6 +483,22 @@ class UtilObject {
return this.getPageLayouts().includes(layout);
};
openDateByTimestamp (t: number, method?: string) {
method = method || 'auto';
const fn = U.Common.toCamelCase(`open-${method}`);
C.ObjectDateByTimestamp(S.Common.space, t, (message: any) => {
if (!message.error.code) {
if (U.Object[fn]) {
U.Object[fn](message.details);
} else {
U.Object.openConfig(message.details);
};
};
});
};
};
export default new UtilObject();

View file

@ -7,8 +7,6 @@ class ViewRelation implements I.ViewRelation {
width = 0;
isVisible = false;
includeTime = false;
dateFormat: I.DateFormat = I.DateFormat.MonthAbbrBeforeDay;
timeFormat: I.TimeFormat = I.TimeFormat.H12;
formulaType: I.FormulaType = I.FormulaType.None;
constructor (props: I.ViewRelation) {
@ -16,16 +14,12 @@ class ViewRelation implements I.ViewRelation {
this.width = Number(props.width) || 0;
this.isVisible = Boolean(props.isVisible);
this.includeTime = Boolean(props.includeTime);
this.dateFormat = Number(props.dateFormat) || I.DateFormat.MonthAbbrBeforeDay;
this.timeFormat = Number(props.timeFormat) || I.TimeFormat.H12;
this.formulaType = Number(props.formulaType) || I.FormulaType.None;
makeObservable(this, {
width: observable,
isVisible: observable,
includeTime: observable,
dateFormat: observable,
timeFormat: observable,
formulaType: observable,
});

View file

@ -39,6 +39,8 @@ class CommonStore {
public fullscreenObjectValue = null;
public navigationMenuValue = null;
public linkStyleValue = null;
public dateFormatValue = null;
public timeFormatValue = null;
public isOnlineValue = false;
public shareTooltipValue = false;
public showVaultValue = null;
@ -133,6 +135,8 @@ class CommonStore {
spaceStorageSet: action,
navigationMenuSet: action,
linkStyleSet: action,
dateFormatSet: action,
timeFormatSet: action,
isOnlineSet: action,
shareTooltipSet: action,
membershipTiersListSet: action,
@ -249,6 +253,22 @@ class CommonStore {
return Number(ret) || I.LinkCardStyle.Text;
};
get dateFormat (): I.DateFormat {
let ret = this.dateFormatValue;
if (ret === null) {
ret = Storage.get('dateFormat');
};
return Number(ret) || I.DateFormat.Long;
};
get timeFormat (): I.TimeFormat {
let ret = this.timeFormatValue;
if (ret === null) {
ret = Storage.get('timeFormat');
};
return Number(ret) || I.TimeFormat.H12;
};
get dataPath (): string {
return String(this.dataPathValue || '');
};
@ -488,6 +508,18 @@ class CommonStore {
Storage.set('linkStyle', v);
};
dateFormatSet (v: I.DateFormat) {
v = Number(v);
this.dateFormatValue = v;
Storage.set('dateFormat', v);
};
timeFormatSet (v: I.TimeFormat) {
v = Number(v);
this.timeFormatValue = v;
Storage.set('timeFormat', v);
};
isOnlineSet (v: boolean) {
this.isOnlineValue = Boolean(v);
console.log('[Online status]:', v);
@ -514,7 +546,7 @@ class CommonStore {
set(this.configObj, newConfig);
this.configObj.debug = this.configObj.debug || {};
html.toggleClass('debug', this.configObj.debug.ui);
html.toggleClass('debug', Boolean(this.configObj.debug.ui));
};
spaceStorageSet (value: Partial<SpaceStorage>) {

View file

@ -173,16 +173,18 @@ class DetailStore {
/** Mutates object provided and also returns a new object. Sets defaults.
* This Function contains domain logic which should be encapsulated in a model */
public mapper (object: any): any {
public mapper (object: any, skipLayoutFormat?: I.ObjectLayout[]): any {
object = this.mapCommon(object || {});
const fn = `map${I.ObjectLayout[object.layout]}`;
if (this[fn]) {
object = this[fn](object);
};
if (!skipLayoutFormat || !skipLayoutFormat.includes(object.layout)) {
const fn = `map${I.ObjectLayout[object.layout]}`;
if (this[fn]) {
object = this[fn](object);
};
if (U.Object.isInFileLayouts(object.layout)) {
object = this.mapFile(object);
if (U.Object.isInFileLayouts(object.layout)) {
object = this.mapFile(object);
};
};
return object;
@ -282,6 +284,15 @@ class DetailStore {
};
private mapDate (object: any) {
object.timestamp = Number(object.timestamp) || 0;
if (object.timestamp) {
const { showRelativeDates, dateFormat } = S.Common;
const day = showRelativeDates ? U.Date.dayString(object.timestamp) : null;
object.name = day ? day : U.Date.dateWithFormat(dateFormat, object.timestamp);
};
return this.mapSet(object);
};