mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-06 17:57:42 +02:00
Merge branch 'main' into ano/attachments-initial
# Conflicts: # plugins/board-resources/src/components/editor/CardActions.svelte
This commit is contained in:
Generated
+122
-9
@@ -53,6 +53,7 @@ specifiers:
|
||||
'@rush-temp/login': file:./projects/login.tgz
|
||||
'@rush-temp/login-assets': file:./projects/login-assets.tgz
|
||||
'@rush-temp/login-resources': file:./projects/login-resources.tgz
|
||||
'@rush-temp/middleware': file:./projects/middleware.tgz
|
||||
'@rush-temp/model': file:./projects/model.tgz
|
||||
'@rush-temp/model-activity': file:./projects/model-activity.tgz
|
||||
'@rush-temp/model-all': file:./projects/model-all.tgz
|
||||
@@ -67,6 +68,7 @@ specifiers:
|
||||
'@rush-temp/model-inventory': file:./projects/model-inventory.tgz
|
||||
'@rush-temp/model-lead': file:./projects/model-lead.tgz
|
||||
'@rush-temp/model-notification': file:./projects/model-notification.tgz
|
||||
'@rush-temp/model-preference': file:./projects/model-preference.tgz
|
||||
'@rush-temp/model-presentation': file:./projects/model-presentation.tgz
|
||||
'@rush-temp/model-recruit': file:./projects/model-recruit.tgz
|
||||
'@rush-temp/model-rig': file:./projects/model-rig.tgz
|
||||
@@ -101,6 +103,8 @@ specifiers:
|
||||
'@rush-temp/platform': file:./projects/platform.tgz
|
||||
'@rush-temp/platform-rig': file:./projects/platform-rig.tgz
|
||||
'@rush-temp/pod-account': file:./projects/pod-account.tgz
|
||||
'@rush-temp/preference': file:./projects/preference.tgz
|
||||
'@rush-temp/preference-assets': file:./projects/preference-assets.tgz
|
||||
'@rush-temp/presentation': file:./projects/presentation.tgz
|
||||
'@rush-temp/prod': file:./projects/prod.tgz
|
||||
'@rush-temp/prod-tracker': file:./projects/prod-tracker.tgz
|
||||
@@ -127,6 +131,7 @@ specifiers:
|
||||
'@rush-temp/server-lead-resources': file:./projects/server-lead-resources.tgz
|
||||
'@rush-temp/server-notification': file:./projects/server-notification.tgz
|
||||
'@rush-temp/server-notification-resources': file:./projects/server-notification-resources.tgz
|
||||
'@rush-temp/server-preference': file:./projects/server-preference.tgz
|
||||
'@rush-temp/server-recruit': file:./projects/server-recruit.tgz
|
||||
'@rush-temp/server-recruit-resources': file:./projects/server-recruit-resources.tgz
|
||||
'@rush-temp/server-setting': file:./projects/server-setting.tgz
|
||||
@@ -320,6 +325,7 @@ dependencies:
|
||||
'@rush-temp/login': file:projects/login.tgz
|
||||
'@rush-temp/login-assets': file:projects/login-assets.tgz_typescript@4.5.4
|
||||
'@rush-temp/login-resources': file:projects/login-resources.tgz_096c09b0b673a57c275d9767a12070b1
|
||||
'@rush-temp/middleware': file:projects/middleware.tgz
|
||||
'@rush-temp/model': file:projects/model.tgz
|
||||
'@rush-temp/model-activity': file:projects/model-activity.tgz_typescript@4.5.4
|
||||
'@rush-temp/model-all': file:projects/model-all.tgz_typescript@4.5.4
|
||||
@@ -334,6 +340,7 @@ dependencies:
|
||||
'@rush-temp/model-inventory': file:projects/model-inventory.tgz_typescript@4.5.4
|
||||
'@rush-temp/model-lead': file:projects/model-lead.tgz_typescript@4.5.4
|
||||
'@rush-temp/model-notification': file:projects/model-notification.tgz_typescript@4.5.4
|
||||
'@rush-temp/model-preference': file:projects/model-preference.tgz_typescript@4.5.4
|
||||
'@rush-temp/model-presentation': file:projects/model-presentation.tgz_typescript@4.5.4
|
||||
'@rush-temp/model-recruit': file:projects/model-recruit.tgz_typescript@4.5.4
|
||||
'@rush-temp/model-rig': file:projects/model-rig.tgz_37f79b97d0d86442e45d380c86f520c5
|
||||
@@ -368,6 +375,8 @@ dependencies:
|
||||
'@rush-temp/platform': file:projects/platform.tgz
|
||||
'@rush-temp/platform-rig': file:projects/platform-rig.tgz_37f79b97d0d86442e45d380c86f520c5
|
||||
'@rush-temp/pod-account': file:projects/pod-account.tgz
|
||||
'@rush-temp/preference': file:projects/preference.tgz
|
||||
'@rush-temp/preference-assets': file:projects/preference-assets.tgz
|
||||
'@rush-temp/presentation': file:projects/presentation.tgz_096c09b0b673a57c275d9767a12070b1
|
||||
'@rush-temp/prod': file:projects/prod.tgz_a07ec81d4d975778878ca12202ea119e
|
||||
'@rush-temp/prod-tracker': file:projects/prod-tracker.tgz_a07ec81d4d975778878ca12202ea119e
|
||||
@@ -394,6 +403,7 @@ dependencies:
|
||||
'@rush-temp/server-lead-resources': file:projects/server-lead-resources.tgz
|
||||
'@rush-temp/server-notification': file:projects/server-notification.tgz
|
||||
'@rush-temp/server-notification-resources': file:projects/server-notification-resources.tgz
|
||||
'@rush-temp/server-preference': file:projects/server-preference.tgz
|
||||
'@rush-temp/server-recruit': file:projects/server-recruit.tgz
|
||||
'@rush-temp/server-recruit-resources': file:projects/server-recruit-resources.tgz
|
||||
'@rush-temp/server-setting': file:projects/server-setting.tgz
|
||||
@@ -11738,7 +11748,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/board-resources.tgz_096c09b0b673a57c275d9767a12070b1:
|
||||
resolution: {integrity: sha512-vNNtH4in7D54mv4ckcYvE+iLxqt10x0kpToPg+xMRl4LuXIehuKDM+x9uiSb61y7FBesLyrJ3+606D99cc+1fQ==, tarball: file:projects/board-resources.tgz}
|
||||
resolution: {integrity: sha512-nI0XMUXXwKiWWIwh1LHsD6P+VjNPy1M1NN3VBxLzPFsG6TGmEX/RHZhR9a6Ao6YwY783n8TfdrajFABs8IrGPg==, tarball: file:projects/board-resources.tgz}
|
||||
id: file:projects/board-resources.tgz
|
||||
name: '@rush-temp/board-resources'
|
||||
version: 0.0.0
|
||||
@@ -11773,7 +11783,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/board.tgz:
|
||||
resolution: {integrity: sha512-K6XdiXv9w+xvcegm1Ql7XmBXIR/nMElWROWkVabz/SGhrvmENtjOa6mKVZploQf5cTafItd1V2e/NxZu8DnrgA==, tarball: file:projects/board.tgz}
|
||||
resolution: {integrity: sha512-Jgyvcqd3njP8w/I6a1M7PNNCQWojSyNBzzVauBja3IRaFLMgFv7g3zqbilHkJw1IoYpsLab8wZoJ7X4+TM1s6w==, tarball: file:projects/board.tgz}
|
||||
name: '@rush-temp/board'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@@ -12770,6 +12780,26 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/middleware.tgz:
|
||||
resolution: {integrity: sha512-beRGnoVJZVP/5mEU9bxmXLikwWCRLXBWaeFyIGIZbgjHypFCY57237qoOpLEgXwmBzEUk2jhophOYiT+Xd4qLQ==, tarball: file:projects/middleware.tgz}
|
||||
name: '@rush-temp/middleware'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.41.8
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
|
||||
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
|
||||
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
prettier: 2.5.1
|
||||
typescript: 4.5.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/model-activity.tgz_typescript@4.5.4:
|
||||
resolution: {integrity: sha512-TslFvrgSosU016NXZCahvZcTkVGONQ37mfJCHJ/PRA9I33M++MaIlw/ZIhWl9Z/e8dCuYW8PVrlijoYwCmc+kg==, tarball: file:projects/model-activity.tgz}
|
||||
id: file:projects/model-activity.tgz
|
||||
@@ -12792,7 +12822,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/model-all.tgz_typescript@4.5.4:
|
||||
resolution: {integrity: sha512-RLEP3hoSiGNZeIxc4bRN7FQFcFFz4Sv6E2rF1tQHkBIIFJdUSVxDk4xwkopcU5UNNZyoW1UrwlG5jXOzWV9Pkg==, tarball: file:projects/model-all.tgz}
|
||||
resolution: {integrity: sha512-NxbYGWKtCvK3tUbHGFiHRNdc/AmAFb9WWma26yTTzkRBM0ZLgXFfNqNaloQad+dXa1NR0SqpCKswdhelZ1QYgg==, tarball: file:projects/model-all.tgz}
|
||||
id: file:projects/model-all.tgz
|
||||
name: '@rush-temp/model-all'
|
||||
version: 0.0.0
|
||||
@@ -13047,6 +13077,27 @@ packages:
|
||||
- typescript
|
||||
dev: false
|
||||
|
||||
file:projects/model-preference.tgz_typescript@4.5.4:
|
||||
resolution: {integrity: sha512-VA7EbuQe0imqaTsLhaXC83m/EB+vTdMRhCDU6AsPsU+Ay0eXTuebZucwwFh92lQ0fDbu1KDJTFcyPxpWwscSXw==, tarball: file:projects/model-preference.tgz}
|
||||
id: file:projects/model-preference.tgz
|
||||
name: '@rush-temp/model-preference'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.41.8
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
|
||||
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
|
||||
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
prettier: 2.5.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
dev: false
|
||||
|
||||
file:projects/model-presentation.tgz_typescript@4.5.4:
|
||||
resolution: {integrity: sha512-nJF6vTxtGM/Q3nvEyhNk7LBiTrnCQpDLMco/zvSyLsRhjrA8yrAZwo3hdIK9K22Sic8Rl1vw6et/DH7CjGXjAA==, tarball: file:projects/model-presentation.tgz}
|
||||
id: file:projects/model-presentation.tgz
|
||||
@@ -13832,6 +13883,47 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/preference-assets.tgz:
|
||||
resolution: {integrity: sha512-ppUED31aeD7XmNevsK2XDz527e+gbmV43y68t7bM/DfypniAvcPaNBZ20CzQETmHsBrHSdUNTGFMv3HKMtmzNA==, tarball: file:projects/preference-assets.tgz}
|
||||
name: '@rush-temp/preference-assets'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.41.8
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@types/node': 16.11.14
|
||||
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
|
||||
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
|
||||
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
prettier: 2.5.1
|
||||
typescript: 4.5.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/preference.tgz:
|
||||
resolution: {integrity: sha512-WGzTspMfF/YBgM5AlF26hbbPD9Qbj4Vp7AL0DJeescfvY2MF4rFQ+wJK5ewuFWbDYGVu0kMJiszRP/7OVXqHFQ==, tarball: file:projects/preference.tgz}
|
||||
name: '@rush-temp/preference'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.41.8
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
|
||||
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
|
||||
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
prettier: 2.5.1
|
||||
typescript: 4.5.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/presentation.tgz_096c09b0b673a57c275d9767a12070b1:
|
||||
resolution: {integrity: sha512-wc5U6pu+2JFrN7NknB2nCs3qbDEh5AgNhRfoTFBPHOxsBplCEtNtUvHtt91pY2lGlgQVZvNsLKEhaDKycVF19w==, tarball: file:projects/presentation.tgz}
|
||||
id: file:projects/presentation.tgz
|
||||
@@ -13869,7 +13961,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/prod-tracker.tgz_a07ec81d4d975778878ca12202ea119e:
|
||||
resolution: {integrity: sha512-dl5eoirwPVY6dcl4eNTnk0BUpGnXVxppdq2EQd1S81HGk0zT+2X76KJaZ3sUf7byks3kNFl4rFmWOn+JLrpXNA==, tarball: file:projects/prod-tracker.tgz}
|
||||
resolution: {integrity: sha512-DegdZVFP21HUwqbhSqazxR2nu9OqSlJ8QxOn1S3IOD9AGi7H5OOSvw6E/RItOlGAts0bi261Z81Dgi9EJvTtvA==, tarball: file:projects/prod-tracker.tgz}
|
||||
id: file:projects/prod-tracker.tgz
|
||||
name: '@rush-temp/prod-tracker'
|
||||
version: 0.0.0
|
||||
@@ -13915,7 +14007,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/prod.tgz_a07ec81d4d975778878ca12202ea119e:
|
||||
resolution: {integrity: sha512-VvhwhIktsLPXGK+aXdKgBVK9CbQkWIVyYy+MmgjEDIpMcoNWbZzL/fdu+Dw/F1OT80Vq01Y7aDeN8TaoHJkWZg==, tarball: file:projects/prod.tgz}
|
||||
resolution: {integrity: sha512-WaWihWZzBZ9WDVbJtB+fFusKyv8pR4hbBAZGkpcZ0yiOQL4tL5R0ZFsh0TxDy1TD68XKFqF2FnM3ngNQQr8bKA==, tarball: file:projects/prod.tgz}
|
||||
id: file:projects/prod.tgz
|
||||
name: '@rush-temp/prod'
|
||||
version: 0.0.0
|
||||
@@ -14444,6 +14536,27 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/server-preference.tgz:
|
||||
resolution: {integrity: sha512-YYnxaZnUVlY/TL829SCP22F6jd03uQROfLRH7oqTH9tnBPaDvr66Zpb4Qyyw/lK2zsNFuZRbJHSGPF45fsa+Zg==, tarball: file:projects/server-preference.tgz}
|
||||
name: '@rush-temp/server-preference'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@rushstack/heft': 0.41.8
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@types/node': 16.11.14
|
||||
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
|
||||
'@typescript-eslint/parser': 5.7.0_eslint@7.32.0+typescript@4.5.4
|
||||
eslint: 7.32.0
|
||||
eslint-config-standard-with-typescript: 21.0.1_ce2fa0c4dfa1c256100cababd749a13a
|
||||
eslint-plugin-import: 2.25.3_eslint@7.32.0
|
||||
eslint-plugin-node: 11.1.0_eslint@7.32.0
|
||||
eslint-plugin-promise: 5.2.0_eslint@7.32.0
|
||||
prettier: 2.5.1
|
||||
typescript: 4.5.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
file:projects/server-recruit-resources.tgz:
|
||||
resolution: {integrity: sha512-TM38Nr2D+for8rBa4J83JMrgVC6wAFoOhXjCaBZTLceLtec+43JObZjd27ZE+7N0fyB57ONxZOADN381res5wg==, tarball: file:projects/server-recruit-resources.tgz}
|
||||
name: '@rush-temp/server-recruit-resources'
|
||||
@@ -14728,7 +14841,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/server.tgz:
|
||||
resolution: {integrity: sha512-TEYNvDfpWbWWHcltHwEyC1B81v5c9UUyGmNx9C9sdItiWxLb8P/lQ+a+xca+5eJ+FQmRxm3VJywrvscmttTfPg==, tarball: file:projects/server.tgz}
|
||||
resolution: {integrity: sha512-6vh78sMqcZsw0xfPyn3wQfOBG0e2jWhVLU8zsSvN1m9bFOuuf7strkWQWun+c5dkgtd6vvd57yh0ihz4BDMkZw==, tarball: file:projects/server.tgz}
|
||||
name: '@rush-temp/server'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@@ -14934,7 +15047,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/task-resources.tgz_6cae74ea501386c76c405ad0408e0339:
|
||||
resolution: {integrity: sha512-YOuOktJH1qu9wET1Zf204iCCzaL8yT6HjNuQr/u3ib921oc0nz0kdrw9UVWsMGabA2xkzJxT1BjGQFPPGgFTvA==, tarball: file:projects/task-resources.tgz}
|
||||
resolution: {integrity: sha512-RGVNKBNCTeRoBjCTl05vX1s7rHV02HUFdxSd3QjZIUYSUo+09fAnaVSeohOq0aXy+R4vvd8shkZuppJupufu7A==, tarball: file:projects/task-resources.tgz}
|
||||
id: file:projects/task-resources.tgz
|
||||
name: '@rush-temp/task-resources'
|
||||
version: 0.0.0
|
||||
@@ -15312,7 +15425,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/tracker-resources.tgz_096c09b0b673a57c275d9767a12070b1:
|
||||
resolution: {integrity: sha512-am69PuPAqWdxUR/7fLKNvmEvfAxMPptRLgABjeQmvBFfS7/RTRFsjz2y+aiLUoMVUktcfHIIMWXxWx/otsmPVw==, tarball: file:projects/tracker-resources.tgz}
|
||||
resolution: {integrity: sha512-2Ap2E6dyQwwxZqOqSHVvQzjCVVuisPK/llz6Qe9I24qb7QvZk4sZrccxpVYNhj4n1FrvNkk9tjccb13eegCAow==, tarball: file:projects/tracker-resources.tgz}
|
||||
id: file:projects/tracker-resources.tgz
|
||||
name: '@rush-temp/tracker-resources'
|
||||
version: 0.0.0
|
||||
@@ -15502,7 +15615,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/workbench-resources.tgz_096c09b0b673a57c275d9767a12070b1:
|
||||
resolution: {integrity: sha512-PB/gIO0hbQwlQ3b3RlHWf0ZcsjET1b4nuzS8sTOs6xDdtVZbWH3xTbms89OiLW0znBpHTsKRV5BIacXKe9cc+g==, tarball: file:projects/workbench-resources.tgz}
|
||||
resolution: {integrity: sha512-VMERIjagr6R/HiIj4wyaMCkQBe3K5Lck4JLs9qBw6U025TSH+6aJeJCpo7+lF/yEfANR+Y5HMbCSlohp9GMgaA==, tarball: file:projects/workbench-resources.tgz}
|
||||
id: file:projects/workbench-resources.tgz
|
||||
name: '@rush-temp/workbench-resources'
|
||||
version: 0.0.0
|
||||
|
||||
@@ -105,6 +105,8 @@
|
||||
"@anticrm/notification": "~0.6.0",
|
||||
"@anticrm/notification-assets": "~0.6.0",
|
||||
"@anticrm/notification-resources": "~0.6.0",
|
||||
"@anticrm/preference": "~0.6.0",
|
||||
"@anticrm/preference-assets": "~0.6.0",
|
||||
"@anticrm/core": "~0.6.16",
|
||||
"@anticrm/rekoni": "~0.6.0",
|
||||
"@anticrm/tags-assets": "~0.6.0",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright © 2020 Anticrm Platform Contributors.
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
@@ -34,6 +34,7 @@ import { imageCropperId } from '@anticrm/image-cropper'
|
||||
import { inventoryId } from '@anticrm/inventory'
|
||||
import { templatesId } from '@anticrm/templates'
|
||||
import { notificationId } from '@anticrm/notification'
|
||||
import { preferenceId } from '@anticrm/preference'
|
||||
import { tagsId } from '@anticrm/tags'
|
||||
import { calendarId } from '@anticrm/calendar'
|
||||
import { trackerId } from '@anticrm/tracker'
|
||||
@@ -60,6 +61,7 @@ import '@anticrm/tags-assets'
|
||||
import '@anticrm/calendar-assets'
|
||||
import '@anticrm/tracker-assets'
|
||||
import '@anticrm/board-assets'
|
||||
import '@anticrm/preference-assets'
|
||||
import presentation, { presentationId } from '@anticrm/presentation'
|
||||
import { coreId } from '@anticrm/core'
|
||||
import { textEditorId } from '@anticrm/text-editor'
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
import type { Doc, Ref, TxResult } from '@anticrm/core'
|
||||
import { DOMAIN_TX, MeasureMetricsContext } from '@anticrm/core'
|
||||
import { createInMemoryAdapter, createInMemoryTxAdapter } from '@anticrm/dev-storage'
|
||||
import type { DbConfiguration } from '@anticrm/server-core'
|
||||
import { createServerStorage, FullTextAdapter, IndexedDoc } from '@anticrm/server-core'
|
||||
import { createPipeline, DbConfiguration, FullTextAdapter, IndexedDoc } from '@anticrm/server-core'
|
||||
import { start as startJsonRpc } from '@anticrm/server-ws'
|
||||
|
||||
class NullFullTextAdapter implements FullTextAdapter {
|
||||
@@ -71,6 +70,6 @@ export async function start (port: number, host?: string): Promise<void> {
|
||||
},
|
||||
workspace: ''
|
||||
}
|
||||
return createServerStorage(conf)
|
||||
return createPipeline(conf, [])
|
||||
}, port, host)
|
||||
}
|
||||
|
||||
@@ -68,6 +68,8 @@
|
||||
"@anticrm/model-server-gmail": "~0.6.0",
|
||||
"@anticrm/model-server-telegram": "~0.6.0",
|
||||
"@anticrm/model-tracker": "~0.6.0",
|
||||
"@anticrm/model-board": "~0.6.0"
|
||||
"@anticrm/model-board": "~0.6.0",
|
||||
"@anticrm/model-preference": "~0.6.0"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright © 2020 Anticrm Platform Contributors.
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
@@ -54,6 +54,7 @@ import { createModel as serverGmailModel } from '@anticrm/model-server-gmail'
|
||||
import { createModel as serverTelegramModel } from '@anticrm/model-server-telegram'
|
||||
import { createModel as trackerModel } from '@anticrm/model-tracker'
|
||||
import { createModel as boardModel } from '@anticrm/model-board'
|
||||
import { createModel as preferenceModel } from '@anticrm/model-preference'
|
||||
|
||||
export const version: Data<Version> = jsonVersion as Data<Version>
|
||||
|
||||
@@ -78,6 +79,7 @@ const builders: [(b: Builder) => void, string][] = [
|
||||
[templatesModel, 'templates'],
|
||||
[textEditorModel, 'text-editor'],
|
||||
[notificationModel, 'notification'],
|
||||
[preferenceModel, 'preference'],
|
||||
|
||||
[serverCoreModel, 'server-core'],
|
||||
[serverAttachmentModel, 'server-attachment'],
|
||||
|
||||
@@ -35,6 +35,9 @@ export class TChannel extends TSpace implements Channel {
|
||||
@Prop(TypeTimestamp(), chunter.string.LastMessage)
|
||||
lastMessage?: Timestamp
|
||||
|
||||
@Prop(ArrOf(TypeRef(chunter.class.ChunterMessage)), chunter.string.PinnedMessages)
|
||||
pinned?: Ref<ChunterMessage>[]
|
||||
|
||||
@Prop(TypeString(), chunter.string.Topic)
|
||||
@Index(IndexKind.FullText)
|
||||
topic?: string
|
||||
|
||||
@@ -48,7 +48,8 @@ export default mergeIds(chunterId, chunter, {
|
||||
CreateBy: '' as IntlString,
|
||||
Create: '' as IntlString,
|
||||
MarkUnread: '' as IntlString,
|
||||
LastMessage: '' as IntlString
|
||||
LastMessage: '' as IntlString,
|
||||
PinnedMessages: '' as IntlString
|
||||
},
|
||||
viewlet: {
|
||||
Chat: '' as Ref<ViewletDescriptor>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/model-rig/profiles/default/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
||||
@@ -0,0 +1,18 @@
|
||||
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
|
||||
/**
|
||||
* (Required) The name of the rig package to inherit from.
|
||||
* It should be an NPM package name with the "-rig" suffix.
|
||||
*/
|
||||
"rigPackageName": "@anticrm/model-rig"
|
||||
|
||||
/**
|
||||
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||
* If omitted, then the "default" profile will be used."
|
||||
*/
|
||||
// "rigProfile": "your-profile-name"
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "@anticrm/model-preference",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "heft build",
|
||||
"build:watch": "tsc",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"lint": "eslint src",
|
||||
"format": "prettier --write src && eslint --fix src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/model-rig": "~0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"@types/heft-jest": "^1.0.2",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"prettier": "^2.4.1",
|
||||
"@rushstack/heft": "^0.41.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/core": "~0.6.16",
|
||||
"@anticrm/model": "~0.6.0",
|
||||
"@anticrm/ui": "~0.6.0",
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"@anticrm/model-core": "~0.6.0",
|
||||
"@anticrm/preference": "~0.6.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Doc, Ref, Space } from '@anticrm/core'
|
||||
import { Builder, Model, Prop, TypeRef } from '@anticrm/model'
|
||||
import core, { TDoc } from '@anticrm/model-core'
|
||||
import preference, { DOMAIN_PREFERENCE, Preference, SpacePreference } from '@anticrm/preference'
|
||||
|
||||
@Model(preference.class.Preference, core.class.Doc, DOMAIN_PREFERENCE)
|
||||
export class TPreference extends TDoc implements Preference {
|
||||
@Prop(TypeRef(core.class.Doc), core.string.AttachedTo)
|
||||
attachedTo!: Ref<Doc>
|
||||
}
|
||||
|
||||
@Model(preference.class.SpacePreference, preference.class.Preference)
|
||||
export class TSpacePreference extends TPreference implements SpacePreference {
|
||||
@Prop(TypeRef(core.class.Space), core.string.Space)
|
||||
declare attachedTo: Ref<Space>
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TPreference, TSpacePreference)
|
||||
}
|
||||
|
||||
export { preference as default }
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./node_modules/@anticrm/model-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
//
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 Hardcore Engineering Inc.
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
@@ -16,6 +15,8 @@
|
||||
|
||||
import { MeasureContext } from '.'
|
||||
import type { Doc, Class, Ref } from './classes'
|
||||
import { Hierarchy } from './hierarchy'
|
||||
import { ModelDb } from './memdb'
|
||||
import type { DocumentQuery, FindOptions, FindResult, TxResult } from './storage'
|
||||
import type { Tx } from './tx'
|
||||
|
||||
@@ -23,6 +24,8 @@ import type { Tx } from './tx'
|
||||
* @public
|
||||
*/
|
||||
export interface ServerStorage {
|
||||
hierarchy: Hierarchy
|
||||
modelDb: ModelDb
|
||||
findAll: <T extends Doc>(
|
||||
ctx: MeasureContext,
|
||||
_class: Ref<Class<T>>,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Members": "Members",
|
||||
"Search": "Search...",
|
||||
"Unassigned": "Unassigned",
|
||||
"CreateMore": "Create more"
|
||||
"CreateMore": "Create more",
|
||||
"NumberMembers": "{lenght, plural, =0 {no members} =1 {1 member} other {# members}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"Members": "Участники",
|
||||
"Search": "Поиск...",
|
||||
"Unassigned": "Не назначен",
|
||||
"CreateMore": "Создать еще"
|
||||
"CreateMore": "Создать еще",
|
||||
"NumberMembers": "{lenght, plural, =0 {нет участников} =1 {1 участик} other {# участника}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
export let avatar: string | null | undefined = undefined
|
||||
export let direct: Blob | undefined = undefined
|
||||
export let size: 'x-small' | 'small' | 'medium' | 'large' | 'x-large'
|
||||
export let size: 'inline' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large'
|
||||
|
||||
let url: string | undefined
|
||||
$: if (direct !== undefined) {
|
||||
@@ -50,7 +50,7 @@
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-color: var(--popup-bg-hover);
|
||||
background-color: var(--avatar-bg-color);
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
|
||||
@@ -61,6 +61,10 @@
|
||||
&.no-img { border-color: transparent; }
|
||||
}
|
||||
|
||||
.ava-inline {
|
||||
width: .875rem; // 24
|
||||
height: .875rem;
|
||||
}
|
||||
.ava-x-small {
|
||||
width: 1.5rem; // 24
|
||||
height: 1.5rem;
|
||||
@@ -92,6 +96,7 @@
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.ava-inline .ava-mask, .ava-inline.no-img,
|
||||
.ava-x-small .ava-mask, .ava-x-small.no-img,
|
||||
.ava-small .ava-mask, .ava-small.no-img,
|
||||
.ava-medium .ava-mask, .ava-medium.no-img { border-style: none; }
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<!--
|
||||
// Copyright © 2020 Anticrm Platform Contributors.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { Class, Doc, Ref } from '@anticrm/core'
|
||||
import { createQuery } from '../utils'
|
||||
import { Person } from '@anticrm/contact'
|
||||
import Avatar from './Avatar.svelte'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let items: Ref<Person>[] = []
|
||||
export let size: 'inline' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large'
|
||||
export let limit: number = 3
|
||||
|
||||
let persons: Person[] = []
|
||||
const query = createQuery()
|
||||
$: query.query<Person>(_class, { _id: { $in: items } }, result => { persons = result }, { limit })
|
||||
</script>
|
||||
|
||||
<div class="avatars-container">
|
||||
{#each persons as person}
|
||||
<div class="combine-avatar {size}">
|
||||
<Avatar avatar={person.avatar} {size} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.avatars-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.combine-avatar.inline:not(:first-child) { margin-left: calc(1px - (.875rem / 2)); }
|
||||
.combine-avatar.x-small:not(:first-child) { margin-left: calc(1px - (1.5rem / 2)); }
|
||||
.combine-avatar.small:not(:first-child) { margin-left: calc(1px - 1rem); }
|
||||
.combine-avatar.medium:not(:first-child) { margin-left: calc(1px - (2.25rem / 2)); }
|
||||
.combine-avatar.large:not(:first-child) { margin-left: calc(1px - (4.5rem / 2)); }
|
||||
.combine-avatar.x-large:not(:first-child) { margin-left: calc(1px - (7.5rem / 2)); }
|
||||
.combine-avatar:not(:last-child) { mask: radial-gradient(circle at 100% 50%, rgba(0, 0, 0, 0) 48.5%, rgb(0, 0, 0) 50%); }
|
||||
}
|
||||
</style>
|
||||
@@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { getClient } from '../utils'
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
<div class="selectPopup">
|
||||
<div class="header">
|
||||
<input type='text' bind:value={search} placeholder={phTraslate} on:input={(ev) => { }} on:change/>
|
||||
<input type='text' bind:value={search} placeholder={phTraslate} on:input={() => { }} on:change/>
|
||||
</div>
|
||||
<div class="scroll">
|
||||
<div class="box">
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
import contact, { Contact, formatName } from '@anticrm/contact'
|
||||
import type { Class, Ref } from '@anticrm/core'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import type { TooltipAligment } from '@anticrm/ui'
|
||||
import type { TooltipAlignment, ButtonKind, ButtonSize } from '@anticrm/ui'
|
||||
import { Button, Label, showPopup, Tooltip } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import presentation from '..'
|
||||
@@ -35,17 +35,16 @@
|
||||
export let allowDeselect = false
|
||||
export let titleDeselect: IntlString | undefined = undefined
|
||||
export let readonly = false
|
||||
export let kind: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'link' | 'dangerous' = 'no-border'
|
||||
export let size: 'small' | 'medium' | 'large' | 'x-large' = 'small'
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = undefined
|
||||
export let labelDirection: TooltipAligment | undefined = undefined
|
||||
export let labelDirection: TooltipAlignment | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let selected: Contact | undefined
|
||||
let container: HTMLElement
|
||||
let opened: boolean = false
|
||||
|
||||
const client = getClient()
|
||||
|
||||
@@ -62,14 +61,13 @@
|
||||
</script>
|
||||
|
||||
<div bind:this={container} class="min-w-0">
|
||||
<Tooltip label={label} fill={width === '100%'} direction={labelDirection}>
|
||||
<Tooltip {label} fill={width === '100%'} direction={labelDirection}>
|
||||
<Button
|
||||
icon={(size === 'x-large' && selected) ? undefined : IconPerson}
|
||||
width={width ?? 'min-content'}
|
||||
{size} {kind} {justify}
|
||||
on:click={() => {
|
||||
if (!opened && !readonly) {
|
||||
opened = true
|
||||
if (!readonly) {
|
||||
showPopup(UsersPopup, { _class, allowDeselect, selected: value, titleDeselect, placeholder }, container, (result) => {
|
||||
if (result === null) {
|
||||
value = null
|
||||
@@ -79,7 +77,6 @@
|
||||
value = result._id
|
||||
dispatch('change', value)
|
||||
}
|
||||
opened = false
|
||||
})
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -15,18 +15,26 @@
|
||||
<script lang="ts">
|
||||
import { Person } from '@anticrm/contact'
|
||||
import type { Class, Doc, Ref } from '@anticrm/core'
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
import { ActionIcon, CircleButton, IconAdd, IconClose, Label, ShowMore, showPopup } from '@anticrm/ui'
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { translate } from '@anticrm/platform'
|
||||
import type { ButtonKind, ButtonSize, TooltipAlignment } from '@anticrm/ui'
|
||||
import { Tooltip, showPopup, Button } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { UserInfo } from '..'
|
||||
import presentation, { CombineAvatars, UsersPopup } from '..'
|
||||
import { createQuery } from '../utils'
|
||||
import UsersPopup from './UsersPopup.svelte'
|
||||
import Members from './icons/Members.svelte'
|
||||
|
||||
export let items: Ref<Person>[] = []
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let title: IntlString
|
||||
export let label: IntlString
|
||||
export let noItems: IntlString
|
||||
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = undefined
|
||||
export let labelDirection: TooltipAlignment | undefined = undefined
|
||||
|
||||
let persons: Person[] = []
|
||||
|
||||
const query = createQuery()
|
||||
@@ -37,71 +45,45 @@
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function addRef (person: Person): Promise<void> {
|
||||
dispatch('open', person)
|
||||
}
|
||||
async function addPerson (evt: Event): Promise<void> {
|
||||
showPopup(
|
||||
UsersPopup,
|
||||
{
|
||||
_class,
|
||||
title,
|
||||
label,
|
||||
multiSelect: true,
|
||||
allowDeselect: false,
|
||||
ignoreUsers: items
|
||||
selectedUsers: items
|
||||
},
|
||||
evt.target as HTMLElement,
|
||||
() => { },
|
||||
(result) => {
|
||||
// We have some value selected
|
||||
if (result !== undefined) {
|
||||
addRef(result)
|
||||
items = result
|
||||
dispatch('update', items)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function removePerson (person: Person): Promise<void> {
|
||||
dispatch('delete', person)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-row">
|
||||
<ShowMore>
|
||||
<div class="persons-container">
|
||||
<div class="flex flex-reverse">
|
||||
<div class="ml-4">
|
||||
<CircleButton icon={IconAdd} size={'small'} selected on:click={addPerson} />
|
||||
<Tooltip {label} fill={width === '100%'} direction={labelDirection}>
|
||||
<Button
|
||||
icon={persons.length === 0 ? Members : undefined}
|
||||
label={persons.length === 0 ? presentation.string.Members : undefined}
|
||||
width={width ?? 'min-content'}
|
||||
{kind} {size} {justify}
|
||||
on:click={addPerson}
|
||||
>
|
||||
<svelte:fragment slot="content">
|
||||
{#if persons.length > 0}
|
||||
<div class="flex-row-center flex-nowrap">
|
||||
<CombineAvatars {_class} bind:items size={'inline'} />
|
||||
{#await translate(presentation.string.NumberMembers, { lenght: persons.length }) then text}
|
||||
<span class="ml-1-5">{text}</span>
|
||||
{/await}
|
||||
</div>
|
||||
<div class="person-items">
|
||||
{#if items?.length === 0}
|
||||
<div class="flex flex-grow title-center">
|
||||
<Label label={noItems} />
|
||||
</div>
|
||||
{/if}
|
||||
{#each persons as person}
|
||||
<div class="antiComponentBox flex-center antiComponentBoxFocused">
|
||||
<UserInfo value={person} size={'x-small'} />
|
||||
<div class="ml-1">
|
||||
<ActionIcon icon={IconClose} size={'small'} action={() => removePerson(person)} />
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ShowMore>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.persons-container {
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
.person-items {
|
||||
flex-grow: 1;
|
||||
display: grid;
|
||||
gap: 0.25rem;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
.margin_025 {
|
||||
margin: 0.25rem;
|
||||
}
|
||||
</style>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import { translate } from '@anticrm/platform'
|
||||
import { afterUpdate, createEventDispatcher } from 'svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
import { Tooltip, CheckBox } from '@anticrm/ui'
|
||||
import UserInfo from './UserInfo.svelte'
|
||||
@@ -28,10 +28,11 @@
|
||||
export let _class: Ref<Class<Person>>
|
||||
export let selected: Ref<Person> | undefined
|
||||
|
||||
export let multiSelect: boolean = false
|
||||
export let allowDeselect: boolean = false
|
||||
export let titleDeselect: IntlString | undefined = undefined
|
||||
export let placeholder: IntlString = presentation.string.Search
|
||||
|
||||
export let selectedUsers: Ref<Person>[] = []
|
||||
export let ignoreUsers: Ref<Person>[] = []
|
||||
|
||||
let search: string = ''
|
||||
@@ -43,23 +44,47 @@
|
||||
|
||||
let phTraslate: string = ''
|
||||
$: if (placeholder) translate(placeholder, {}).then(res => { phTraslate = res })
|
||||
afterUpdate(() => { dispatch('update', Date.now()) })
|
||||
|
||||
const isSelected = (person: Person): boolean => {
|
||||
if (selectedUsers.filter(p => p === person._id).length > 0) return true
|
||||
return false
|
||||
}
|
||||
const checkSelected = (person: Person): void => {
|
||||
if (isSelected(person)) selectedUsers = selectedUsers.filter(p => p !== person._id)
|
||||
else selectedUsers.push(person._id)
|
||||
objects = objects
|
||||
dispatch('update', selectedUsers)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="selectPopup">
|
||||
<div class="header">
|
||||
<input type='text' bind:value={search} placeholder={phTraslate} on:input={(ev) => { }} on:change/>
|
||||
<input type='text' bind:value={search} placeholder={phTraslate} on:change/>
|
||||
</div>
|
||||
<div class="scroll">
|
||||
<div class="box">
|
||||
{#each objects as person}
|
||||
<button class="menu-item flex-between" on:click={() => { dispatch('close', person) }}>
|
||||
<button class="menu-item" on:click={() => {
|
||||
if (!multiSelect) {
|
||||
selected = person._id === selected ? undefined : person._id
|
||||
dispatch('close', selected != undefined ? person : undefined)
|
||||
} else checkSelected(person)
|
||||
}}>
|
||||
{#if multiSelect}
|
||||
<div class="check pointer-events-none">
|
||||
<CheckBox checked={isSelected(person)} primary />
|
||||
</div>
|
||||
{/if}
|
||||
<UserInfo size={'x-small'} value={person} />
|
||||
{#if allowDeselect && person._id === selected}
|
||||
<div class="check" on:click|stopPropagation={() => { dispatch('close', null) }}>
|
||||
<Tooltip label={titleDeselect ?? presentation.string.Deselect}>
|
||||
<CheckBox checked primary />
|
||||
</Tooltip>
|
||||
<div class="check-right pointer-events-none">
|
||||
{#if titleDeselect}
|
||||
<Tooltip label={titleDeselect ?? presentation.string.Deselect}>
|
||||
<CheckBox checked circle primary />
|
||||
</Tooltip>
|
||||
{:else}
|
||||
<CheckBox checked circle primary />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
export let size: 'x-small' | 'small' | 'medium' | 'large' | 'x-large'
|
||||
export let size: 'inline' | 'x-small' | 'small' | 'medium' | 'large' | 'x-large'
|
||||
|
||||
const fill: string = 'var(--theme-caption-color)'
|
||||
</script>
|
||||
@@ -30,6 +30,10 @@
|
||||
.svg-avatar {
|
||||
.op { opacity: .05; }
|
||||
}
|
||||
.inline {
|
||||
width: .75rem;
|
||||
height: .75rem;
|
||||
}
|
||||
.x-small {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.2857 5.44444C10.2857 6.79447 9.26237 7.88889 8 7.88889C6.73764 7.88889 5.71429 6.79447 5.71429 5.44444C5.71429 4.09441 6.73764 3 8 3C9.26237 3 10.2857 4.09441 10.2857 5.44444Z" />
|
||||
<path d="M3.42857 11.9603C3.42857 10.9748 3.98128 10.081 4.85831 9.7786C5.79546 9.45545 7.02325 9.11111 8 9.11111C8.97675 9.11111 10.2045 9.45545 11.1417 9.77859C12.0187 10.081 12.5714 10.9748 12.5714 11.9603V12.7778C12.5714 13.4528 12.0598 14 11.4286 14H4.57143C3.94025 14 3.42857 13.4528 3.42857 12.7778V11.9603Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.63272 4.75066C4.59249 4.97531 4.57143 5.20725 4.57143 5.44444C4.57143 6.00206 4.68782 6.5306 4.89604 7.00381C4.59565 7.53433 4.05083 7.88889 3.42857 7.88889C2.4818 7.88889 1.71429 7.06808 1.71429 6.05556C1.71429 5.04303 2.4818 4.22222 3.42857 4.22222C3.89788 4.22222 4.32315 4.42391 4.63272 4.75066Z" />
|
||||
<path d="M2.28571 12.7778V11.9603C2.28571 10.8522 2.76028 9.77952 3.59669 9.11537C3.5397 9.11257 3.48361 9.11111 3.42857 9.11111C2.69601 9.11111 1.77516 9.36937 1.0723 9.61172C0.414531 9.83853 0 10.5089 0 11.248V11.8611C0 12.3674 0.383756 12.7778 0.857143 12.7778H2.28571Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.3716 4.75066C11.4118 4.97531 11.4329 5.20725 11.4329 5.44444C11.4329 6.00206 11.3166 6.5306 11.1086 7.00381C11.4087 7.53433 11.953 7.88889 12.5747 7.88889C13.5205 7.88889 14.2873 7.06808 14.2873 6.05556C14.2873 5.04303 13.5205 4.22222 12.5747 4.22222C12.1058 4.22222 11.6809 4.42391 11.3716 4.75066Z" />
|
||||
<path d="M13.7164 12.7778V11.9603C13.7164 10.8522 13.2423 9.77952 12.4067 9.11537C12.4636 9.11257 12.5197 9.11111 12.5747 9.11111C13.3065 9.11111 14.2265 9.36937 14.9287 9.61172C15.5859 9.83853 16 10.5089 16 11.248V11.8611C16 12.3674 15.6166 12.7778 15.1437 12.7778H13.7164Z" />
|
||||
</svg>
|
||||
@@ -21,6 +21,7 @@ export { default as AttributeBarEditor } from './components/AttributeBarEditor.s
|
||||
export { default as AttributeEditor } from './components/AttributeEditor.svelte'
|
||||
export { default as AttributesBar } from './components/AttributesBar.svelte'
|
||||
export { default as Avatar } from './components/Avatar.svelte'
|
||||
export { default as CombineAvatars } from './components/CombineAvatars.svelte'
|
||||
export { default as Card } from './components/Card.svelte'
|
||||
export { default as EditableAvatar } from './components/EditableAvatar.svelte'
|
||||
export { default as Members } from './components/Members.svelte'
|
||||
|
||||
@@ -43,7 +43,8 @@ export default plugin(presentationId, {
|
||||
Members: '' as IntlString,
|
||||
Search: '' as IntlString,
|
||||
Unassigned: '' as IntlString,
|
||||
CreateMore: '' as IntlString
|
||||
CreateMore: '' as IntlString,
|
||||
NumberMembers: '' as IntlString
|
||||
},
|
||||
metadata: {
|
||||
RequiredVersion: '' as Metadata<string>
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
--popup-shadow: rgb(0 0 0 / 20%) 0px 4px 24px;
|
||||
--card-shadow: rgb(0 0 0 / 50%) 0px 16px 70px;
|
||||
--card-overlay-color: rgba(28, 29, 31, .5);
|
||||
--avatar-bg-color: #4f5358;
|
||||
|
||||
--button-bg-color: #303236;
|
||||
--button-bg-hover: #37383b;
|
||||
|
||||
@@ -185,7 +185,7 @@ p:last-child { margin-block-end: 0; }
|
||||
|
||||
&.circle {
|
||||
padding: .25rem;
|
||||
background-color: var(--popup-bg-hover);
|
||||
background-color: var(--avatar-bg-color);
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
@@ -286,6 +286,7 @@ p:last-child { margin-block-end: 0; }
|
||||
.step-tb75 + .step-tb75 { margin-top: .75rem; }
|
||||
|
||||
.ml-1 { margin-left: .25rem; }
|
||||
.ml-1-5 { margin-left: .375rem; }
|
||||
.ml-2 { margin-left: .5rem; }
|
||||
.ml-3 { margin-left: .75rem; }
|
||||
.ml-4 { margin-left: 1rem; }
|
||||
@@ -378,6 +379,7 @@ p:last-child { margin-block-end: 0; }
|
||||
.h-full { height: 100%; }
|
||||
.h-2 { height: .5rem; }
|
||||
.h-9 { height: 2.25rem; }
|
||||
.h-16 { height: 4rem; }
|
||||
.h-18 { height: 4.5rem; }
|
||||
.w-full { width: 100%; }
|
||||
.w-9 { width: 2.25rem; }
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
color: var(--caption-color);
|
||||
cursor: pointer;
|
||||
|
||||
&.high { height: 3rem; }
|
||||
.icon {
|
||||
margin-right: .75rem;
|
||||
width: 1rem;
|
||||
@@ -84,14 +85,13 @@
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
.check {
|
||||
.check, .check-right {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
margin-left: 1rem;
|
||||
width: 1rem;
|
||||
margin-right: .75rem;
|
||||
height: 2rem;
|
||||
}
|
||||
.check-right { margin: 0 0 0 2rem; }
|
||||
&:hover { background-color: var(--popup-bg-hover); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
|
||||
<script lang="ts">
|
||||
import type { IntlString, Asset } from '@anticrm/platform'
|
||||
import type { AnySvelteComponent, TooltipAligment } from '../types'
|
||||
import type { AnySvelteComponent, TooltipAlignment } from '../types'
|
||||
|
||||
import Icon from './Icon.svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
|
||||
export let label: IntlString = '' as IntlString
|
||||
export let labelProps: any = undefined
|
||||
export let direction: TooltipAligment | undefined = undefined
|
||||
export let direction: TooltipAlignment | undefined = undefined
|
||||
export let icon: Asset | AnySvelteComponent
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
export let action: (ev: Event) => Promise<void> | void = async () => { }
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { IntlString, Asset } from '@anticrm/platform'
|
||||
import type { AnySvelteComponent } from '../types'
|
||||
import type { AnySvelteComponent, ButtonKind, ButtonSize } from '../types'
|
||||
import Spinner from './Spinner.svelte'
|
||||
import Label from './Label.svelte'
|
||||
import Icon from './Icon.svelte'
|
||||
@@ -22,8 +22,8 @@
|
||||
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let labelParams: Record<string, any> = {}
|
||||
export let kind: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'link' | 'link-bordered' | 'dangerous' = 'secondary'
|
||||
export let size: 'small' | 'medium' | 'large' | 'x-large' = 'medium'
|
||||
export let kind: ButtonKind = 'secondary'
|
||||
export let size: ButtonSize = 'medium'
|
||||
export let shape: 'circle' | undefined = undefined
|
||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
@@ -83,7 +83,8 @@
|
||||
<style lang="scss">
|
||||
.small {
|
||||
height: 1.5rem;
|
||||
line-height: 1.5rem;
|
||||
font-size: .75rem;
|
||||
line-height: .75rem;
|
||||
&.only-icon { width: 1.5rem; }
|
||||
}
|
||||
.medium {
|
||||
|
||||
@@ -34,13 +34,6 @@
|
||||
<label class="checkbox" class:circle class:primary class:checked>
|
||||
<input class="chBox" type="checkbox" bind:checked on:change={handleValueChanged} />
|
||||
<svg class="checkSVG" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
{#if !circle}
|
||||
<path
|
||||
class="back"
|
||||
class:primary
|
||||
d="M4,0h8c2.2,0,4,1.8,4,4v8c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4V4C0,1.8,1.8,0,4,0z"
|
||||
/>
|
||||
{/if}
|
||||
{#if checked}
|
||||
{#if symbol === 'minus'}
|
||||
<rect class="check" class:primary x="4" y="7.4" width="8" height="1.2" />
|
||||
@@ -54,12 +47,27 @@
|
||||
<style lang="scss">
|
||||
.checkbox {
|
||||
flex-shrink: 0;
|
||||
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
width: .875rem;
|
||||
height: .875rem;
|
||||
border: 1px solid var(--dark-color);
|
||||
border-radius: .25rem;
|
||||
|
||||
&.circle {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border-radius: 50%;
|
||||
}
|
||||
&.checked {
|
||||
background-color: var(--accent-color);
|
||||
border-color: transparent;
|
||||
}
|
||||
&.primary.checked {
|
||||
background-color: var(--primary-bg-color);
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.chBox {
|
||||
position: absolute;
|
||||
@@ -94,35 +102,13 @@
|
||||
}
|
||||
}
|
||||
.checkSVG {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border-radius: 0.25rem;
|
||||
width: .875rem;
|
||||
height: .875rem;
|
||||
|
||||
.back {
|
||||
fill: var(--theme-button-bg-hovered);
|
||||
}
|
||||
.check {
|
||||
visibility: hidden;
|
||||
fill: var(--theme-button-bg-enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.circle {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
background-color: var(--theme-button-bg-hovered);
|
||||
border: 1px solid var(--theme-bg-focused-color);
|
||||
border-radius: 50%;
|
||||
|
||||
&.checked {
|
||||
background-color: var(--theme-bg-check);
|
||||
}
|
||||
&.primary {
|
||||
border-color: transparent;
|
||||
&.checked {
|
||||
background-color: var(--primary-button-enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
import Label from './Label.svelte'
|
||||
import Icon from './Icon.svelte'
|
||||
import { showPopup, Button, Tooltip, DropdownPopup } from '..'
|
||||
import type { AnySvelteComponent, ListItem, TooltipAligment } from '../types'
|
||||
import type { AnySvelteComponent, ListItem, TooltipAlignment, ButtonKind, ButtonSize } from '../types'
|
||||
|
||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||
export let label: IntlString
|
||||
@@ -28,11 +28,11 @@
|
||||
export let items: ListItem[] = []
|
||||
export let selected: ListItem | undefined = undefined
|
||||
|
||||
export let kind: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'link' | 'dangerous' = 'no-border'
|
||||
export let size: 'small' | 'medium' | 'large' | 'x-large' = 'small'
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = undefined
|
||||
export let labelDirection: TooltipAligment | undefined = undefined
|
||||
export let labelDirection: TooltipAlignment | undefined = undefined
|
||||
|
||||
let container: HTMLElement
|
||||
let opened: boolean = false
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<script lang="ts">
|
||||
import { IntlString, Asset } from '@anticrm/platform'
|
||||
import DropdownLabelsPopup from './DropdownLabelsPopup.svelte'
|
||||
import type { AnySvelteComponent, DropdownTextItem, TooltipAligment } from '../types'
|
||||
import type { AnySvelteComponent, DropdownTextItem, TooltipAlignment, ButtonKind, ButtonSize } from '../types'
|
||||
import { showPopup, Tooltip, Button, Label } from '..'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import ui from '../plugin'
|
||||
@@ -27,11 +27,11 @@
|
||||
export let items: DropdownTextItem[]
|
||||
export let selected: DropdownTextItem['id'] | undefined = undefined
|
||||
|
||||
export let kind: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'link' | 'dangerous' = 'no-border'
|
||||
export let size: 'small' | 'medium' | 'large' | 'x-large' = 'small'
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = undefined
|
||||
export let labelDirection: TooltipAligment | undefined = undefined
|
||||
export let labelDirection: TooltipAlignment | undefined = undefined
|
||||
|
||||
let container: HTMLElement
|
||||
let opened: boolean = false
|
||||
@@ -73,46 +73,3 @@
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<!-- <div class="flex-col cursor-pointer"
|
||||
bind:this={btn}
|
||||
on:click|preventDefault={() => {
|
||||
if (!opened) {
|
||||
opened = true
|
||||
showPopup(DropdownLabelsPopup, { placeholder, items, selected }, btn, (result) => {
|
||||
if (result) {
|
||||
selected = result
|
||||
dispatch('selected', result)
|
||||
}
|
||||
opened = false
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class="overflow-label label"><Label label={label} /></div>
|
||||
<div class="flex-row-center space">
|
||||
<span class="mr-1">
|
||||
{#if opened}
|
||||
<IconUp size={'small'} />
|
||||
{:else}
|
||||
<IconDown size={'small'} />
|
||||
{/if}
|
||||
</span>
|
||||
<span class="overflow-label" class:caption-color={selected} class:content-dark-color={!selected}>
|
||||
{#if selectedItem}
|
||||
{selectedItem.label}
|
||||
{:else}
|
||||
<Label label={none} />
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.label {
|
||||
margin-bottom: .125rem;
|
||||
font-weight: 500;
|
||||
font-size: .75rem;
|
||||
color: var(--theme-content-accent-color);
|
||||
}
|
||||
</style> -->
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
import type { Asset, IntlString } from '@anticrm/platform'
|
||||
import { translate } from '@anticrm/platform'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Icon, Label, getPlatformColor } from '..'
|
||||
import { Icon, Label } from '..'
|
||||
|
||||
export let placeholder: IntlString | undefined = undefined
|
||||
export let placeholderParam: any | undefined = undefined
|
||||
export let searchable: boolean = false
|
||||
export let value: Array<{id: number | string, color: number, label: IntlString}>
|
||||
export let value: Array<{id: number | string, icon: Asset, label: IntlString}>
|
||||
|
||||
let search: string = ''
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<div class="box">
|
||||
{#each value.filter(el => el.label.toLowerCase().includes(search.toLowerCase())) as item}
|
||||
<button class="menu-item" on:click={() => { dispatch('close', item.id) }}>
|
||||
<div class="color" style="background-color: {getPlatformColor(item.color)}" />
|
||||
<div class="icon"><Icon icon={item.icon} size={'small'} /></div>
|
||||
<span class="label"><Label label={item.label} /></span>
|
||||
</button>
|
||||
{/each}
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { IntlString } from '@anticrm/platform'
|
||||
import type { TooltipAligment, AnySvelteComponent, AnyComponent } from '..'
|
||||
import type { TooltipAlignment, AnySvelteComponent, AnyComponent } from '..'
|
||||
import { tooltipstore as tooltip, showTooltip } from '..'
|
||||
|
||||
export let label: IntlString | undefined = undefined
|
||||
export let direction: TooltipAligment | undefined = undefined
|
||||
export let direction: TooltipAlignment | undefined = undefined
|
||||
export let component: AnySvelteComponent | AnyComponent | undefined = undefined
|
||||
export let props: any | undefined = undefined
|
||||
export let anchor: HTMLElement | undefined = undefined
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
<script lang="ts">
|
||||
import { afterUpdate, onDestroy } from 'svelte'
|
||||
import { tooltipstore as tooltip, closeTooltip, Component } from '..'
|
||||
import type { TooltipAligment } from '..'
|
||||
import type { TooltipAlignment } from '..'
|
||||
import Label from './Label.svelte'
|
||||
|
||||
let tooltipHTML: HTMLElement
|
||||
let nubHTML: HTMLElement
|
||||
let dir: TooltipAligment
|
||||
let dir: TooltipAlignment
|
||||
let rect: DOMRect
|
||||
let rectAnchor: DOMRect
|
||||
let tooltipSW: boolean // tooltipSW = true - Label; false - Component
|
||||
|
||||
@@ -25,10 +25,11 @@
|
||||
export let withTime: boolean = false
|
||||
export let mondayStart: boolean = true
|
||||
export let editable: boolean = false
|
||||
export let icon: 'normal' | 'warning' | 'overdue' = 'normal'
|
||||
export let icon: 'normal' | 'warning' | 'critical' | 'overdue' = 'normal'
|
||||
export let labelOver: IntlString | undefined = undefined // label instead of date
|
||||
export let labelNull: IntlString = ui.string.NoDate
|
||||
export let showIcon = true
|
||||
export let shouldShowLabel: boolean = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -54,27 +55,35 @@
|
||||
<button
|
||||
class="datetime-button"
|
||||
class:editable
|
||||
class:dateTimeButtonNoLabel={!shouldShowLabel}
|
||||
on:click={() => {
|
||||
if (editable && !opened) {
|
||||
opened = true
|
||||
showPopup(DatePopup,
|
||||
{ currentDate, mondayStart, withTime },
|
||||
undefined,
|
||||
() => { opened = false },
|
||||
(result) => { if (result !== undefined) onChange(result) })
|
||||
showPopup(
|
||||
DatePopup,
|
||||
{ currentDate, mondayStart, withTime },
|
||||
undefined,
|
||||
() => {
|
||||
opened = false
|
||||
},
|
||||
(result) => {
|
||||
if (result !== undefined) onChange(result)
|
||||
}
|
||||
)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if showIcon}
|
||||
<div class="btn-icon {icon}">
|
||||
<Icon icon={icon === 'overdue' ? DPCalendarOver : DPCalendar} size={'full'}/>
|
||||
<div class="btn-icon {icon}" class:buttonIconNoLabel={!shouldShowLabel}>
|
||||
<Icon icon={icon === 'overdue' ? DPCalendarOver : DPCalendar} size={'full'} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if value !== null && value !== undefined}
|
||||
{#if labelOver !== undefined}
|
||||
{#if shouldShowLabel && labelOver !== undefined}
|
||||
<Label label={labelOver} />
|
||||
{:else}
|
||||
{new Date(value).getDate()} {getMonthName(new Date(value), 'short')}
|
||||
{:else if shouldShowLabel}
|
||||
{new Date(value).getDate()}
|
||||
{getMonthName(new Date(value), 'short')}
|
||||
{#if new Date(value).getFullYear() !== today.getFullYear()}
|
||||
{new Date(value).getFullYear()}
|
||||
{/if}
|
||||
@@ -85,7 +94,7 @@
|
||||
{new Date(value).getMinutes().toString().padStart(2, '0')}
|
||||
{/if}
|
||||
{/if}
|
||||
{:else}
|
||||
{:else if shouldShowLabel}
|
||||
<Label label={labelNull} />
|
||||
{/if}
|
||||
</button>
|
||||
@@ -96,7 +105,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding: 0 .5rem;
|
||||
padding: 0 0.5rem;
|
||||
font-weight: 400;
|
||||
min-width: 1.5rem;
|
||||
width: auto;
|
||||
@@ -106,22 +115,38 @@
|
||||
color: var(--accent-color);
|
||||
background-color: var(--button-bg-color);
|
||||
border: 1px solid transparent;
|
||||
border-radius: .25rem;
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: var(--button-shadow);
|
||||
transition-property: border, background-color, color, box-shadow;
|
||||
transition-duration: .15s;
|
||||
transition-duration: 0.15s;
|
||||
cursor: default;
|
||||
|
||||
&.dateTimeButtonNoLabel {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
margin-right: .375rem;
|
||||
width: .875rem;
|
||||
height: .875rem;
|
||||
transition: color .15s;
|
||||
margin-right: 0.375rem;
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
transition: color 0.15s;
|
||||
pointer-events: none;
|
||||
|
||||
&.normal { color: var(--content-color); }
|
||||
&.warning { color: var(--warning-color); }
|
||||
&.overdue { color: var(--error-color); }
|
||||
&.buttonIconNoLabel {
|
||||
margin-right: 0;
|
||||
}
|
||||
&.normal {
|
||||
color: var(--content-color);
|
||||
}
|
||||
&.warning {
|
||||
color: var(--warning-color);
|
||||
}
|
||||
&.critical {
|
||||
color: var(--error-color);
|
||||
}
|
||||
&.overdue {
|
||||
color: var(--error-color);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@@ -134,15 +159,28 @@
|
||||
&:hover {
|
||||
background-color: var(--button-bg-hover);
|
||||
.btn-icon {
|
||||
&.normal { color: var(--caption-color); }
|
||||
&.warning { color: var(--warning-color); }
|
||||
&.overdue { color: var(--error-color); }
|
||||
&.normal {
|
||||
color: var(--caption-color);
|
||||
}
|
||||
&.warning {
|
||||
color: var(--warning-color);
|
||||
}
|
||||
&.critical {
|
||||
color: var(--error-color);
|
||||
}
|
||||
&.overdue {
|
||||
color: var(--error-color);
|
||||
}
|
||||
}
|
||||
.time-divider {
|
||||
background-color: var(--button-border-hover);
|
||||
}
|
||||
.time-divider { background-color: var(--button-border-hover); }
|
||||
}
|
||||
&:focus-within {
|
||||
border-color: var(--primary-edit-border-color);
|
||||
&:hover { background-color: transparent; }
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:disabled {
|
||||
@@ -151,17 +189,21 @@
|
||||
|
||||
&:hover {
|
||||
color: var(--content-color);
|
||||
.btn-icon { color: var(--content-color); }
|
||||
.btn-icon {
|
||||
color: var(--content-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
.time-divider {
|
||||
flex-shrink: 0;
|
||||
margin: 0 .25rem;
|
||||
margin: 0 0.25rem;
|
||||
width: 1px;
|
||||
min-width: 1px;
|
||||
height: .75rem;
|
||||
height: 0.75rem;
|
||||
background-color: var(--button-border-color);
|
||||
}
|
||||
.separator { margin: 0 .1rem; }
|
||||
.separator {
|
||||
margin: 0 0.1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -78,6 +78,7 @@ export function getMonthName (date: Date, option: 'narrow' | 'short' | 'long' |
|
||||
return new Intl.DateTimeFormat(locale, { month: option }).format(date)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,3 +101,10 @@ export function addZero (value: number): string {
|
||||
}
|
||||
return `${value}`
|
||||
}
|
||||
|
||||
export const getDaysDifference = (from: Date, to: Date): number => {
|
||||
const firstDateMs = from.setHours(0, 0, 0, 0)
|
||||
const secondDateMs = to.setHours(0, 0, 0, 0)
|
||||
|
||||
return Math.round(Math.abs(secondDateMs - firstDateMs) / MILLISECONDS_IN_DAY)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import { readable } from 'svelte/store'
|
||||
|
||||
import Root from './components/internal/Root.svelte'
|
||||
|
||||
export type { AnyComponent, AnySvelteComponent, Action, LabelAndProps, TooltipAligment, AnySvelteComponentWithProps, Location, PopupAlignment } from './types'
|
||||
export type { AnyComponent, AnySvelteComponent, Action, LabelAndProps, TooltipAlignment, AnySvelteComponentWithProps, Location, PopupAlignment, ButtonKind, ButtonSize } from './types'
|
||||
// export { applicationShortcutKey } from './utils'
|
||||
export { getCurrentLocation, locationToUrl, navigate, location } from './location'
|
||||
|
||||
@@ -102,6 +102,8 @@ export { default as IconCheck } from './components/icons/Check.svelte'
|
||||
export { default as IconArrowLeft } from './components/icons/ArrowLeft.svelte'
|
||||
export { default as IconNavPrev } from './components/icons/NavPrev.svelte'
|
||||
export { default as IconNavNext } from './components/icons/NavNext.svelte'
|
||||
export { default as IconDPCalendar } from './components/calendar/icons/DPCalendar.svelte'
|
||||
export { default as IconDPCalendarOver } from './components/calendar/icons/DPCalendarOver.svelte'
|
||||
|
||||
export { default as PanelInstance } from './components/PanelInstance.svelte'
|
||||
export { default as Panel } from './components/Panel.svelte'
|
||||
@@ -110,6 +112,7 @@ export * from './utils'
|
||||
export * from './popups'
|
||||
export * from './tooltips'
|
||||
export * from './panelup'
|
||||
export * from './components/calendar/internal/DateUtils'
|
||||
|
||||
export function createApp (target: HTMLElement): SvelteComponent {
|
||||
return new Root({ target })
|
||||
|
||||
@@ -123,9 +123,9 @@ export function fitPopupElement (modalHTML: HTMLElement, element?: PopupAlignmen
|
||||
const rectPopup = modalHTML.getBoundingClientRect()
|
||||
// Vertical
|
||||
if (rect.bottom + rectPopup.height + 28 <= document.body.clientHeight) {
|
||||
modalHTML.style.top = `calc(${rect.bottom}px + .75rem)`
|
||||
modalHTML.style.top = `calc(${rect.bottom}px + .125rem)`
|
||||
} else if (rectPopup.height + 28 < rect.top) {
|
||||
modalHTML.style.bottom = `calc(${document.body.clientHeight - rect.y}px + .75rem)`
|
||||
modalHTML.style.bottom = `calc(${document.body.clientHeight - rect.y}px + .125rem)`
|
||||
} else {
|
||||
modalHTML.style.top = modalHTML.style.bottom = '1rem'
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AnySvelteComponent, AnyComponent, LabelAndProps, TooltipAligment } from './types'
|
||||
import { AnySvelteComponent, AnyComponent, LabelAndProps, TooltipAlignment } from './types'
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
@@ -15,7 +15,7 @@ export const tooltipstore = writable<LabelAndProps>({
|
||||
export function showTooltip (
|
||||
label: IntlString | undefined,
|
||||
element: HTMLElement,
|
||||
direction?: TooltipAligment,
|
||||
direction?: TooltipAlignment,
|
||||
component?: AnySvelteComponent | AnyComponent,
|
||||
props?: any,
|
||||
anchor?: HTMLElement,
|
||||
|
||||
@@ -63,14 +63,15 @@ export interface Tab {
|
||||
|
||||
export type TabModel = Tab[]
|
||||
|
||||
export type ButtonKind = 'primary' | 'secondary' | 'no-border' | 'transparent' | 'link' | 'link-bordered' | 'dangerous'
|
||||
export type ButtonSize = 'small' | 'medium' | 'large' | 'x-large'
|
||||
export type PopupAlignment = HTMLElement | EventTarget | null | 'right' | 'top' | 'account' | 'full' | 'content' | 'middle'
|
||||
|
||||
export type TooltipAligment = 'top' | 'bottom' | 'left' | 'right'
|
||||
export type TooltipAlignment = 'top' | 'bottom' | 'left' | 'right'
|
||||
|
||||
export interface LabelAndProps {
|
||||
label: IntlString | undefined
|
||||
element: HTMLElement | undefined
|
||||
direction?: TooltipAligment
|
||||
direction?: TooltipAlignment
|
||||
component?: AnySvelteComponent | AnyComponent
|
||||
props?: any
|
||||
anchor: HTMLElement | undefined
|
||||
|
||||
@@ -36,20 +36,18 @@
|
||||
export let _class: Ref<Class<Card>>
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const query = createQuery()
|
||||
const cardQuery = createQuery()
|
||||
const stateQuery = createQuery()
|
||||
|
||||
let object: Card | undefined
|
||||
let state: State | undefined
|
||||
let handleMove: () => void
|
||||
|
||||
$: _id &&
|
||||
_class &&
|
||||
query.query(_class, { _id }, async (result) => {
|
||||
$: cardQuery.query(_class, { _id }, async (result) => {
|
||||
object = result[0]
|
||||
})
|
||||
|
||||
$: object &&
|
||||
query.query(task.class.State, { _id: object.state }, async (result) => {
|
||||
$: object?.state && stateQuery.query(task.class.State, { _id: object.state }, async (result) => {
|
||||
state = result[0]
|
||||
})
|
||||
|
||||
@@ -104,7 +102,7 @@
|
||||
<div class="flex-grow mr-4">
|
||||
<div class="flex-row-streach">
|
||||
<div class="w-9" />
|
||||
<CardDetails value={object} />
|
||||
<CardDetails bind:value={object} />
|
||||
</div>
|
||||
<div class="flex-row-streach mt-4 mb-2">
|
||||
<div class="w-9">
|
||||
@@ -128,10 +126,10 @@
|
||||
</div>
|
||||
<CardAttachments value={object} />
|
||||
<!-- TODO checklists -->
|
||||
<CardActivity value={object} />
|
||||
<CardActivity bind:value={object} />
|
||||
</div>
|
||||
|
||||
<CardActions value={object} />
|
||||
<CardActions bind:value={object} />
|
||||
</div>
|
||||
</div>
|
||||
</Scroller>
|
||||
|
||||
@@ -36,12 +36,12 @@ export function hasDate (card: Card): boolean {
|
||||
|
||||
export function addCurrentUser (card: Card, client: Client): void {
|
||||
const employee = (getCurrentAccount() as EmployeeAccount).employee
|
||||
card.members = card.members ?? []
|
||||
const members = card.members ?? []
|
||||
|
||||
if (card.members.includes(employee)) {
|
||||
if (members.includes(employee)) {
|
||||
return
|
||||
}
|
||||
|
||||
card.members.push(employee)
|
||||
updateCard(client, card, 'members', card.members)
|
||||
members.push(employee)
|
||||
updateCard(client, card, 'members', members)
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
<UserBoxList
|
||||
_class={contact.class.Employee}
|
||||
items={participants}
|
||||
title={calendar.string.Participants}
|
||||
label={calendar.string.Participants}
|
||||
on:open={(evt) => {
|
||||
participants.push(evt.detail._id)
|
||||
participants = participants
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<UserBoxList
|
||||
_class={contact.class.Employee}
|
||||
items={object.participants}
|
||||
title={calendar.string.Participants}
|
||||
label={calendar.string.Participants}
|
||||
on:open={(evt) => {
|
||||
client.update(object, { $push: { participants: evt.detail._id } })
|
||||
}}
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
<UserBoxList
|
||||
_class={contact.class.Employee}
|
||||
items={participants}
|
||||
title={calendar.string.Participants}
|
||||
label={calendar.string.Participants}
|
||||
on:open={(evt) => {
|
||||
participants.push(evt.detail._id)
|
||||
participants = participants
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
"MarkUnread": "Mark unread",
|
||||
"GetNewReplies": "Get notified about new replies",
|
||||
"TurnOffReplies": "Turn off notifications for replies",
|
||||
"PinMessage": "Pin message",
|
||||
"UnpinMessage": "Unpin message",
|
||||
"Pinned": "Pinned:",
|
||||
"EditMessage": "Edit message",
|
||||
"DeleteMessage": "Delete message"
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,10 @@
|
||||
"MarkUnread": "Отметить как непрочитанное",
|
||||
"GetNewReplies": "Получать уведомления о новых ответах",
|
||||
"TurnOffReplies": "Выключить уведомления об ответах",
|
||||
"PinMessage": "Закрепить сообщение",
|
||||
"UnpinMessage": "Открепить сообщение",
|
||||
"Pinned": "Закреплено:",
|
||||
"EditMessage": "Редактировать сообщение",
|
||||
"DeleteMessage": "Удалить сообщение"
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import attachment from '@anticrm/attachment'
|
||||
import type { Message } from '@anticrm/chunter'
|
||||
import type { ChunterMessage, Message } from '@anticrm/chunter'
|
||||
import contact, { Employee } from '@anticrm/contact'
|
||||
import core, { Doc, Ref, Space, WithLookup } from '@anticrm/core'
|
||||
import { NotificationClientImpl } from '@anticrm/notification-resources'
|
||||
@@ -23,7 +23,9 @@
|
||||
import chunter from '../plugin'
|
||||
import ChannelSeparator from './ChannelSeparator.svelte'
|
||||
import MessageComponent from './Message.svelte'
|
||||
|
||||
export let space: Ref<Space> | undefined
|
||||
export let pinnedIds: Ref<ChunterMessage>[]
|
||||
|
||||
let div: HTMLDivElement | undefined
|
||||
let autoscroll: boolean = false
|
||||
@@ -111,7 +113,7 @@
|
||||
{#if newMessagesPos === i}
|
||||
<ChannelSeparator title={chunter.string.New} line reverse isNew />
|
||||
{/if}
|
||||
<MessageComponent {message} {employees} on:openThread />
|
||||
<MessageComponent {message} {employees} on:openThread isPinned={pinnedIds.includes(message._id)} />
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -14,14 +14,15 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { AttachmentRefInput } from '@anticrm/attachment-resources'
|
||||
import { Message } from '@anticrm/chunter'
|
||||
import { ChunterMessage, Message } from '@anticrm/chunter'
|
||||
import { generateId, getCurrentAccount, Ref, Space, TxFactory } from '@anticrm/core'
|
||||
import { NotificationClientImpl } from '@anticrm/notification-resources'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { getCurrentLocation, navigate } from '@anticrm/ui'
|
||||
import { createBacklinks } from '../backlinks'
|
||||
import chunter from '../plugin'
|
||||
import Channel from './Channel.svelte'
|
||||
import PinnedMessages from './PinnedMessages.svelte'
|
||||
|
||||
export let space: Ref<Space>
|
||||
|
||||
@@ -63,13 +64,26 @@
|
||||
loc.path[3] = _id
|
||||
navigate(loc)
|
||||
}
|
||||
|
||||
const pinnedQuery = createQuery()
|
||||
let pinnedIds: Ref<ChunterMessage>[] = []
|
||||
pinnedQuery.query(
|
||||
chunter.class.Channel,
|
||||
{ _id: space },
|
||||
(res) => {
|
||||
pinnedIds = res[0]?.pinned ?? []
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
</script>
|
||||
|
||||
<PinnedMessages {space} {pinnedIds} />
|
||||
<Channel
|
||||
{space}
|
||||
on:openThread={(e) => {
|
||||
openThread(e.detail)
|
||||
}}
|
||||
{pinnedIds}
|
||||
/>
|
||||
<div class="reference">
|
||||
<AttachmentRefInput {space} {_class} objectId={_id} on:message={onMessage} />
|
||||
|
||||
@@ -14,17 +14,18 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Attachment } from '@anticrm/attachment'
|
||||
import { AttachmentList } from '@anticrm/attachment-resources'
|
||||
import { AttachmentList, AttachmentRefInput } from '@anticrm/attachment-resources'
|
||||
import type { Message } from '@anticrm/chunter'
|
||||
import { Employee, EmployeeAccount, formatName } from '@anticrm/contact'
|
||||
import { Ref, WithLookup, getCurrentAccount } from '@anticrm/core'
|
||||
import { NotificationClientImpl } from '@anticrm/notification-resources'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { Avatar, getClient, MessageViewer } from '@anticrm/presentation'
|
||||
import { ActionIcon, IconMoreH, Menu, showPopup, getCurrentLocation, navigate } from '@anticrm/ui'
|
||||
import { ActionIcon, IconMoreH, Menu, showPopup } from '@anticrm/ui'
|
||||
import { Action } from '@anticrm/view'
|
||||
import { getActions } from '@anticrm/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { UnpinMessage } from '../index';
|
||||
import chunter from '../plugin'
|
||||
import { getTime } from '../utils'
|
||||
// import Share from './icons/Share.svelte'
|
||||
@@ -37,6 +38,7 @@
|
||||
export let message: WithLookup<Message>
|
||||
export let employees: Map<Ref<Employee>, Employee>
|
||||
export let thread: boolean = false
|
||||
export let isPinned: boolean = false
|
||||
|
||||
$: employee = getEmployee(message)
|
||||
$: attachments = (message.$lookup?.attachments ?? []) as Attachment[]
|
||||
@@ -59,24 +61,39 @@
|
||||
action: chunter.actionImpl.SubscribeMessage
|
||||
} as Action)
|
||||
|
||||
async function deleteMessage () {
|
||||
await client.remove(message)
|
||||
const loc = getCurrentLocation()
|
||||
$: pinActions = isPinned
|
||||
? ({
|
||||
label: chunter.string.UnpinMessage,
|
||||
action: chunter.actionImpl.UnpinMessage
|
||||
} as Action)
|
||||
: ({
|
||||
label: chunter.string.PinMessage,
|
||||
action: chunter.actionImpl.PinMessage
|
||||
} as Action)
|
||||
|
||||
if (loc.path[3] === message._id) {
|
||||
loc.path.length = 3
|
||||
navigate(loc)
|
||||
}
|
||||
}
|
||||
$: isEditing = false;
|
||||
|
||||
const editAction = {
|
||||
label: chunter.string.EditMessage,
|
||||
action: () => isEditing = true
|
||||
}
|
||||
|
||||
const deleteAction = {
|
||||
label: chunter.string.DeleteMessage,
|
||||
action: deleteMessage
|
||||
action: async () => {
|
||||
(await client.findAll(chunter.class.ThreadMessage, {attachedTo: message._id})).forEach(c => {
|
||||
UnpinMessage(c)
|
||||
})
|
||||
UnpinMessage(message)
|
||||
await client.remove(message)
|
||||
}
|
||||
}
|
||||
|
||||
const showMenu = async (ev: Event): Promise<void> => {
|
||||
const actions = await getActions(client, message, chunter.class.Message)
|
||||
actions.push(subscribeAction)
|
||||
actions.push(pinActions)
|
||||
|
||||
showPopup(
|
||||
Menu,
|
||||
{
|
||||
@@ -89,13 +106,28 @@
|
||||
await impl(message)
|
||||
}
|
||||
})),
|
||||
...(getCurrentAccount()._id === message.createBy ? [deleteAction] : [])
|
||||
...(getCurrentAccount()._id === message.createBy ? [editAction, deleteAction] : [])
|
||||
]
|
||||
},
|
||||
ev.target as HTMLElement
|
||||
)
|
||||
}
|
||||
|
||||
async function onMessageEdit (event: CustomEvent) {
|
||||
const { message: newContent, attachments: newAttachments } = event.detail
|
||||
|
||||
if (newContent !== message.content || newAttachments !== attachments) {
|
||||
await client.update(
|
||||
message,
|
||||
{
|
||||
content: newContent,
|
||||
attachments: newAttachments
|
||||
}
|
||||
)
|
||||
}
|
||||
isEditing = false
|
||||
}
|
||||
|
||||
function getEmployee (message: WithLookup<Message>): Employee | undefined {
|
||||
const employee = (message.$lookup?.createBy as EmployeeAccount).employee
|
||||
if (employee !== undefined) {
|
||||
@@ -115,8 +147,18 @@
|
||||
{#if employee}{formatName(employee.name)}{/if}
|
||||
<span>{getTime(message.createOn)}</span>
|
||||
</div>
|
||||
<div class="text"><MessageViewer message={message.content} /></div>
|
||||
{#if message.attachments}<div class="attachments"><AttachmentList {attachments} /></div>{/if}
|
||||
{#if isEditing}
|
||||
<AttachmentRefInput
|
||||
space={message.space}
|
||||
_class={chunter.class.Comment}
|
||||
objectId={message._id}
|
||||
content={message.content}
|
||||
on:message={onMessageEdit}
|
||||
/>
|
||||
{:else}
|
||||
<div class="text"><MessageViewer message={message.content} /></div>
|
||||
{#if message.attachments}<div class="attachments"><AttachmentList {attachments} /></div>{/if}
|
||||
{/if}
|
||||
{#if reactions || message.replies}
|
||||
<div class="footer flex-col">
|
||||
<div>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<script lang="ts">
|
||||
import { ChunterMessage } from '@anticrm/chunter'
|
||||
import { Ref, Space } from '@anticrm/core'
|
||||
import { Label, showPopup } from '@anticrm/ui'
|
||||
import PinnedMessagesPopup from './PinnedMessagesPopup.svelte'
|
||||
import chunter from '../plugin'
|
||||
|
||||
export let space: Ref<Space>
|
||||
export let pinnedIds: Ref<ChunterMessage>[]
|
||||
|
||||
function showMessages (ev: MouseEvent & { currentTarget: EventTarget & HTMLDivElement }) {
|
||||
showPopup(PinnedMessagesPopup, { space }, ev.target)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if pinnedIds.length > 0}
|
||||
<div class="bottom-divider over-underline pt-2 pb-2 container">
|
||||
<div on:click={(ev) => showMessages(ev)}>
|
||||
<Label label={chunter.string.Pinned} />
|
||||
{pinnedIds.length}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
padding-left: 2.5rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,116 @@
|
||||
<script lang="ts">
|
||||
import chunter, { ChunterMessage } from '@anticrm/chunter'
|
||||
import contact, { Employee, EmployeeAccount, formatName } from '@anticrm/contact'
|
||||
import { Ref, Space } from '@anticrm/core'
|
||||
import { Avatar, createQuery, getClient, MessageViewer } from '@anticrm/presentation'
|
||||
import { IconClose } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { UnpinMessage } from '../index'
|
||||
import { getTime } from '../utils'
|
||||
|
||||
export let space: Ref<Space>
|
||||
|
||||
const pinnedQuery = createQuery()
|
||||
let pinnedIds: Ref<ChunterMessage>[] = []
|
||||
pinnedQuery.query(
|
||||
chunter.class.Channel,
|
||||
{ _id: space },
|
||||
(res) => {
|
||||
pinnedIds = res[0]?.pinned ?? []
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
|
||||
const messagesQuery = createQuery()
|
||||
let pinnedMessages: ChunterMessage[] = []
|
||||
|
||||
$: pinnedIds &&
|
||||
messagesQuery.query(chunter.class.ChunterMessage, { _id: { $in: pinnedIds } }, (res) => {
|
||||
pinnedMessages = res
|
||||
})
|
||||
|
||||
const employeeAccoutsQuery = createQuery()
|
||||
let employeeAcounts: EmployeeAccount[]
|
||||
|
||||
employeeAccoutsQuery.query(contact.class.EmployeeAccount, {}, (res) => (employeeAcounts = res))
|
||||
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function getAvatar (_id?: Ref<Employee>): Promise<string | undefined | null> {
|
||||
if (_id === undefined) return (await client.findOne(contact.class.Employee, { _id }))?.avatar
|
||||
}
|
||||
|
||||
function getEmployee (message: ChunterMessage): EmployeeAccount | undefined {
|
||||
return employeeAcounts?.find((e) => e._id === message.createBy)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="antiPopup vScroll popup">
|
||||
{#each pinnedMessages as message}
|
||||
<div class="message">
|
||||
<div class="header">
|
||||
{#await getEmployee(message) then employeeAccount}
|
||||
{#await getAvatar(employeeAccount?.employee) then avatar}
|
||||
<div class="avatar">
|
||||
<Avatar size={'medium'} {avatar} />
|
||||
</div>
|
||||
{/await}
|
||||
<span class="name">
|
||||
{formatName(employeeAccount?.name ?? '')}
|
||||
</span>
|
||||
{/await}
|
||||
<div
|
||||
class="cross"
|
||||
on:click={async () => {
|
||||
if (pinnedIds.length === 1)
|
||||
dispatch('close')
|
||||
UnpinMessage(message)
|
||||
}}
|
||||
>
|
||||
<IconClose size="small" />
|
||||
</div>
|
||||
</div>
|
||||
<MessageViewer message={message.content} />
|
||||
<span class="time">{getTime(message.createOn)}</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.popup {
|
||||
padding: 1.25rem 1.25rem 1.25rem;
|
||||
max-height: 20rem;
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
|
||||
.message {
|
||||
padding: 0.75rem 1rem 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
box-shadow: inherit;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.name {
|
||||
font-weight: 500;
|
||||
margin-left: 1rem;
|
||||
flex-grow: 2;
|
||||
}
|
||||
|
||||
.cross {
|
||||
opacity: 0.4;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.time {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
</style>
|
||||
@@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Attachment } from '@anticrm/attachment'
|
||||
import { AttachmentList } from '@anticrm/attachment-resources'
|
||||
import { AttachmentList, AttachmentRefInput } from '@anticrm/attachment-resources'
|
||||
import type { ThreadMessage } from '@anticrm/chunter'
|
||||
import { Employee, EmployeeAccount, formatName } from '@anticrm/contact'
|
||||
import { Ref, WithLookup, getCurrentAccount } from '@anticrm/core'
|
||||
@@ -24,6 +24,7 @@
|
||||
import { ActionIcon, IconMoreH, Menu, showPopup } from '@anticrm/ui'
|
||||
import { Action } from '@anticrm/view'
|
||||
import { getActions } from '@anticrm/view-resources'
|
||||
import { UnpinMessage } from '../index';
|
||||
import chunter from '../plugin'
|
||||
import { getTime } from '../utils'
|
||||
// import Share from './icons/Share.svelte'
|
||||
@@ -33,6 +34,7 @@
|
||||
|
||||
export let message: WithLookup<ThreadMessage>
|
||||
export let employees: Map<Ref<Employee>, Employee>
|
||||
export let isPinned: boolean = false
|
||||
|
||||
$: attachments = (message.$lookup?.attachments ?? []) as Attachment[]
|
||||
|
||||
@@ -53,14 +55,35 @@
|
||||
action: chunter.actionImpl.SubscribeComment
|
||||
} as Action)
|
||||
|
||||
$: pinActions = isPinned
|
||||
? ({
|
||||
label: chunter.string.UnpinMessage,
|
||||
action: chunter.actionImpl.UnpinMessage
|
||||
} as Action)
|
||||
: ({
|
||||
label: chunter.string.PinMessage,
|
||||
action: chunter.actionImpl.PinMessage
|
||||
} as Action)
|
||||
|
||||
$: isEditing = false;
|
||||
|
||||
const editAction = {
|
||||
label: chunter.string.EditMessage,
|
||||
action: () => isEditing = true
|
||||
}
|
||||
|
||||
const deleteAction = {
|
||||
label: chunter.string.DeleteMessage,
|
||||
action: async () => await client.removeDoc(message._class, message.space, message._id)
|
||||
action: async () => {
|
||||
await client.removeDoc(message._class, message.space, message._id)
|
||||
UnpinMessage(message)
|
||||
}
|
||||
}
|
||||
|
||||
const showMenu = async (ev: Event): Promise<void> => {
|
||||
const actions = await getActions(client, message, chunter.class.ThreadMessage)
|
||||
actions.push(subscribeAction)
|
||||
actions.push(pinActions)
|
||||
showPopup(
|
||||
Menu,
|
||||
{
|
||||
@@ -73,13 +96,28 @@
|
||||
await impl(message)
|
||||
}
|
||||
})),
|
||||
...(getCurrentAccount()._id === message.createBy ? [deleteAction] : [])
|
||||
...(getCurrentAccount()._id === message.createBy ? [editAction, deleteAction] : [])
|
||||
]
|
||||
},
|
||||
ev.target as HTMLElement
|
||||
)
|
||||
}
|
||||
|
||||
async function onMessageEdit (event: CustomEvent) {
|
||||
const { message: newContent, attachments: newAttachments } = event.detail
|
||||
|
||||
if (newContent !== message.content || newAttachments !== attachments) {
|
||||
await client.update(
|
||||
message,
|
||||
{
|
||||
content: newContent,
|
||||
attachments: newAttachments
|
||||
}
|
||||
)
|
||||
}
|
||||
isEditing = false
|
||||
}
|
||||
|
||||
$: employee = getEmployee(message)
|
||||
|
||||
function getEmployee (comment: WithLookup<ThreadMessage>): Employee | undefined {
|
||||
@@ -97,8 +135,18 @@
|
||||
{#if employee}{formatName(employee.name)}{/if}
|
||||
<span>{getTime(message.createOn)}</span>
|
||||
</div>
|
||||
<div class="text"><MessageViewer message={message.content} /></div>
|
||||
{#if message.attachments}<div class="attachments"><AttachmentList {attachments} /></div>{/if}
|
||||
{#if isEditing}
|
||||
<AttachmentRefInput
|
||||
space={message.space}
|
||||
_class={chunter.class.Comment}
|
||||
objectId={message._id}
|
||||
content={message.content}
|
||||
on:message={onMessageEdit}
|
||||
/>
|
||||
{:else}
|
||||
<div class="text"><MessageViewer message={message.content} /></div>
|
||||
{#if message.attachments}<div class="attachments"><AttachmentList {attachments} /></div>{/if}
|
||||
{/if}
|
||||
{#if reactions}
|
||||
<div class="footer">
|
||||
<div><Reactions /></div>
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
<script lang="ts">
|
||||
import attachment from '@anticrm/attachment'
|
||||
import { AttachmentRefInput } from '@anticrm/attachment-resources'
|
||||
import type { ThreadMessage, Message } from '@anticrm/chunter'
|
||||
import type { ThreadMessage, Message, ChunterMessage, Channel } from '@anticrm/chunter'
|
||||
import contact, { Employee } from '@anticrm/contact'
|
||||
import core, { Doc, generateId, getCurrentAccount, Ref, Space, TxFactory } from '@anticrm/core'
|
||||
import { NotificationClientImpl } from '@anticrm/notification-resources'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { IconClose, Label } from '@anticrm/ui'
|
||||
import { IconClose, Label, getCurrentLocation, navigate } from '@anticrm/ui'
|
||||
import { afterUpdate, beforeUpdate, createEventDispatcher } from 'svelte'
|
||||
import { createBacklinks } from '../backlinks'
|
||||
import chunter from '../plugin'
|
||||
@@ -57,6 +57,9 @@
|
||||
createBy: core.class.Account
|
||||
}
|
||||
|
||||
const pinnedQuery = createQuery()
|
||||
let pinnedIds: Ref<ChunterMessage>[] = []
|
||||
|
||||
$: updateQueries(_id)
|
||||
|
||||
function updateQueries (id: Ref<Message>) {
|
||||
@@ -65,7 +68,15 @@
|
||||
{
|
||||
_id: id
|
||||
},
|
||||
(res) => (message = res[0]),
|
||||
(res) => {
|
||||
message = res[0]
|
||||
|
||||
if (!message) {
|
||||
const loc = getCurrentLocation()
|
||||
loc.path.length = 3
|
||||
navigate(loc)
|
||||
}
|
||||
},
|
||||
{
|
||||
lookup: {
|
||||
_id: { attachments: attachment.class.Attachment },
|
||||
@@ -88,6 +99,15 @@
|
||||
lookup
|
||||
}
|
||||
)
|
||||
|
||||
pinnedQuery.query(
|
||||
chunter.class.Channel,
|
||||
{ _id: currentSpace },
|
||||
(res) => {
|
||||
pinnedIds = res[0]?.pinned ?? []
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
}
|
||||
|
||||
let employees: Map<Ref<Employee>, Employee> = new Map<Ref<Employee>, Employee>()
|
||||
@@ -174,7 +194,11 @@
|
||||
{#if newMessagesPos === i}
|
||||
<ChannelSeparator title={chunter.string.New} line reverse isNew />
|
||||
{/if}
|
||||
<ThreadComment message={comment} {employees} />
|
||||
<ThreadComment
|
||||
message={comment}
|
||||
{employees}
|
||||
isPinned={pinnedIds.includes(comment._id)}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import chunter, { Message, ThreadMessage } from '@anticrm/chunter'
|
||||
import core from '@anticrm/core'
|
||||
import chunter, { Channel, ChunterMessage, Message, ThreadMessage } from '@anticrm/chunter'
|
||||
import { NotificationClientImpl } from '@anticrm/notification-resources'
|
||||
import { Resources } from '@anticrm/platform'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import TxBacklinkCreate from './components/activity/TxBacklinkCreate.svelte'
|
||||
import TxBacklinkReference from './components/activity/TxBacklinkReference.svelte'
|
||||
import TxCommentCreate from './components/activity/TxCommentCreate.svelte'
|
||||
@@ -62,6 +64,22 @@ async function UnsubscribeComment (object: ThreadMessage): Promise<void> {
|
||||
await client.unsubscribe(object.attachedTo)
|
||||
}
|
||||
|
||||
async function PinMessage (message: ChunterMessage): Promise<void> {
|
||||
const client = getClient()
|
||||
|
||||
await client.updateDoc<Channel>(chunter.class.Channel, core.space.Space, message.space, {
|
||||
$push: { pinned: message._id }
|
||||
})
|
||||
}
|
||||
|
||||
export async function UnpinMessage (message: ChunterMessage): Promise<void> {
|
||||
const client = getClient()
|
||||
|
||||
await client.updateDoc<Channel>(chunter.class.Channel, core.space.Space, message.space, {
|
||||
$pull: { pinned: message._id }
|
||||
})
|
||||
}
|
||||
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
CommentInput,
|
||||
@@ -85,6 +103,8 @@ export default async (): Promise<Resources> => ({
|
||||
SubscribeMessage,
|
||||
SubscribeComment,
|
||||
UnsubscribeMessage,
|
||||
UnsubscribeComment
|
||||
UnsubscribeComment,
|
||||
PinMessage,
|
||||
UnpinMessage
|
||||
}
|
||||
})
|
||||
|
||||
@@ -30,7 +30,9 @@ export default mergeIds(chunterId, chunter, {
|
||||
SubscribeMessage: '' as Resource<(object: Doc) => Promise<void>>,
|
||||
SubscribeComment: '' as Resource<(object: Doc) => Promise<void>>,
|
||||
UnsubscribeMessage: '' as Resource<(object: Doc) => Promise<void>>,
|
||||
UnsubscribeComment: '' as Resource<(object: Doc) => Promise<void>>
|
||||
UnsubscribeComment: '' as Resource<(object: Doc) => Promise<void>>,
|
||||
PinMessage: '' as Resource<(object: Doc) => Promise<void>>,
|
||||
UnpinMessage: '' as Resource<(object: Doc) => Promise<void>>
|
||||
},
|
||||
string: {
|
||||
Channel: '' as IntlString,
|
||||
@@ -51,6 +53,10 @@ export default mergeIds(chunterId, chunter, {
|
||||
New: '' as IntlString,
|
||||
GetNewReplies: '' as IntlString,
|
||||
TurnOffReplies: '' as IntlString,
|
||||
DeleteMessage: '' as IntlString
|
||||
PinMessage: '' as IntlString,
|
||||
UnpinMessage: '' as IntlString,
|
||||
Pinned: '' as IntlString,
|
||||
DeleteMessage: '' as IntlString,
|
||||
EditMessage: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
||||
@@ -24,6 +24,7 @@ import { AnyComponent } from '@anticrm/ui'
|
||||
*/
|
||||
export interface Channel extends Space {
|
||||
lastMessage?: Timestamp
|
||||
pinned?: Ref<ChunterMessage>[]
|
||||
topic?: string
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
import { IntlString } from '@anticrm/platform'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { Dropdown } from '@anticrm/ui'
|
||||
import type { TooltipAligment } from '@anticrm/ui'
|
||||
import type { TooltipAlignment, ButtonKind, ButtonSize } from '@anticrm/ui'
|
||||
import { ListItem } from '@anticrm/ui/src/types'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import contact from '../plugin'
|
||||
@@ -28,11 +28,11 @@
|
||||
export let value: Ref<Organization> | undefined
|
||||
export let label: IntlString = contact.string.Organization
|
||||
|
||||
export let kind: 'primary' | 'secondary' | 'no-border' | 'transparent' | 'link' | 'dangerous' = 'no-border'
|
||||
export let size: 'small' | 'medium' | 'large' | 'x-large' = 'small'
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = undefined
|
||||
export let labelDirection: TooltipAligment | undefined = undefined
|
||||
export let labelDirection: TooltipAlignment | undefined = undefined
|
||||
|
||||
const query = createQuery()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/platform-rig/profiles/default/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||
<symbol id="star" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M4.8,14.5c-0.3,0-0.5-0.1-0.7-0.2c-0.4-0.3-0.6-0.8-0.6-1.3L4,10.1c0-0.1,0-0.2-0.1-0.3l-2-2C1.5,7.4,1.4,6.8,1.6,6.3 c0.2-0.5,0.6-0.8,1.1-0.9L5.4,5c0.1,0,0.2-0.1,0.2-0.2l1.2-2.6C7,1.8,7.5,1.5,8,1.5s1,0.3,1.2,0.8l0,0l1.2,2.6 c0,0.1,0.1,0.2,0.2,0.2l2.7,0.4c0.5,0.1,0.9,0.4,1.1,0.9c0.2,0.5,0,1-0.3,1.4l-2,2C12.1,9.8,12,10,12,10.1l0.5,2.8 c0.1,0.5-0.1,1-0.6,1.3c-0.4,0.3-0.9,0.3-1.4,0.1L8.1,13c-0.1,0-0.2,0-0.3,0l-2.4,1.3C5.2,14.4,5,14.5,4.8,14.5z M8,2.5 c-0.1,0-0.2,0-0.3,0.2L6.5,5.3C6.3,5.7,5.9,6,5.5,6L2.8,6.4c-0.2,0-0.2,0.2-0.3,0.2c0,0.1-0.1,0.2,0.1,0.4l2,2 C4.9,9.4,5,9.8,4.9,10.2l-0.5,2.8c0,0.2,0.1,0.3,0.1,0.4c0.1,0,0.2,0.1,0.3,0l2.4-1.3c0.4-0.2,0.9-0.2,1.2,0l2.4,1.3 c0.1,0.1,0.3,0,0.3,0c0,0,0.2-0.1,0.1-0.4l-0.5-2.8c-0.1-0.4,0.1-0.9,0.4-1.2l2-2c0.1-0.2,0.1-0.3,0.1-0.4c0-0.1-0.1-0.2-0.3-0.2 L10.5,6c-0.4-0.1-0.8-0.3-1-0.7L8.3,2.7C8.2,2.5,8.1,2.5,8,2.5z"
|
||||
/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1006 B |
@@ -0,0 +1,18 @@
|
||||
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
|
||||
/**
|
||||
* (Required) The name of the rig package to inherit from.
|
||||
* It should be an NPM package name with the "-rig" suffix.
|
||||
*/
|
||||
"rigPackageName": "@anticrm/platform-rig"
|
||||
|
||||
/**
|
||||
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||
* If omitted, then the "default" profile will be used."
|
||||
*/
|
||||
// "rigProfile": "your-profile-name"
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"string": {
|
||||
"DeleteStarred": "Delete starred",
|
||||
"Starred": "Starred",
|
||||
"Star": "Star",
|
||||
"Unstar": "Unstar"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"string": {
|
||||
"DeleteStarred": "Удалить закладки",
|
||||
"Starred": "Закладки",
|
||||
"Star": "Добавить в закладки",
|
||||
"Unstar": "Удалить из закладок"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "@anticrm/preference-assets",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "heft build",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"format": "prettier --write src && eslint --fix src",
|
||||
"build:watch": "tsc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/platform-rig": "~0.6.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint": "^7.32.0",
|
||||
"prettier": "^2.4.1",
|
||||
"@types/heft-jest": "^1.0.2",
|
||||
"@rushstack/heft": "^0.41.1",
|
||||
"typescript": "^4.3.5",
|
||||
"@types/node": "~16.11.12"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"@anticrm/preference": "~0.6.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { addStringsLoader, loadMetadata } from '@anticrm/platform'
|
||||
import preference, { preferenceId } from '@anticrm/preference'
|
||||
|
||||
const icons = require('../assets/icons.svg') as string // eslint-disable-line
|
||||
loadMetadata(preference.icon, {
|
||||
Star: `${icons}#star`
|
||||
})
|
||||
|
||||
addStringsLoader(preferenceId, async (lang: string) => await import(`../lang/${lang}.json`))
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"declaration": true,
|
||||
"outDir": "./lib",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ['./node_modules/@anticrm/platform-rig/profiles/default/config/eslint.config.json'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: './tsconfig.json'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
*
|
||||
!/lib/**
|
||||
!CHANGELOG.md
|
||||
/lib/**/__tests__/
|
||||
@@ -0,0 +1,18 @@
|
||||
// The "rig.json" file directs tools to look for their config files in an external package.
|
||||
// Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
|
||||
|
||||
/**
|
||||
* (Required) The name of the rig package to inherit from.
|
||||
* It should be an NPM package name with the "-rig" suffix.
|
||||
*/
|
||||
"rigPackageName": "@anticrm/platform-rig"
|
||||
|
||||
/**
|
||||
* (Optional) Selects a config profile from the rig package. The name must consist of
|
||||
* lowercase alphanumeric words separated by hyphens, for example "sample-profile".
|
||||
* If omitted, then the "default" profile will be used."
|
||||
*/
|
||||
// "rigProfile": "your-profile-name"
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "@anticrm/preference",
|
||||
"version": "0.6.0",
|
||||
"main": "lib/index.js",
|
||||
"author": "Anticrm Platform Contributors",
|
||||
"license": "EPL-2.0",
|
||||
"scripts": {
|
||||
"build": "heft build",
|
||||
"build:watch": "tsc",
|
||||
"lint:fix": "eslint --fix src",
|
||||
"lint": "eslint src",
|
||||
"format": "prettier --write src && eslint --fix src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anticrm/platform-rig": "~0.6.0",
|
||||
"@types/heft-jest": "^1.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"eslint-config-standard-with-typescript": "^21.0.1",
|
||||
"prettier": "^2.4.1",
|
||||
"@rushstack/heft": "^0.41.1",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"@anticrm/core": "~0.6.16",
|
||||
"@anticrm/ui": "~0.6.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Class, Doc, Domain, Ref, Space } from '@anticrm/core'
|
||||
import type { Asset, IntlString, Plugin } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface Preference extends Doc {
|
||||
attachedTo: Ref<Doc>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface SpacePreference extends Preference {
|
||||
attachedTo: Ref<Space>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const preferenceId = 'preference' as Plugin
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const DOMAIN_PREFERENCE = 'preference' as Domain
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
const preference = plugin(preferenceId, {
|
||||
class: {
|
||||
Preference: '' as Ref<Class<Preference>>,
|
||||
SpacePreference: '' as Ref<Class<SpacePreference>>
|
||||
},
|
||||
space: {
|
||||
Preference: '' as Ref<Space>
|
||||
},
|
||||
icon: {
|
||||
Star: '' as Asset
|
||||
},
|
||||
string: {
|
||||
DeleteStarred: '' as IntlString,
|
||||
Starred: '' as IntlString,
|
||||
Star: '' as IntlString,
|
||||
Unstar: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
||||
export default preference
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./node_modules/@anticrm/platform-rig/profiles/default/tsconfig.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib",
|
||||
"lib": ["esnext", "dom"]
|
||||
}
|
||||
}
|
||||
@@ -165,18 +165,6 @@
|
||||
placeholder={recruit.string.Location} bind:value={location}
|
||||
maxWidth={'37.5rem'} kind={'small-style'}
|
||||
/>
|
||||
<UserBoxList
|
||||
_class={contact.class.Employee}
|
||||
items={doc.participants}
|
||||
title={calendar.string.Participants}
|
||||
on:open={(evt) => {
|
||||
doc.participants = [...(doc.participants ?? []), evt.detail._id]
|
||||
}}
|
||||
on:delete={(evt) => {
|
||||
doc.participants = doc.participants?.filter((it) => it !== evt.detail._id) ?? [currentUser.employee]
|
||||
}}
|
||||
noItems={calendar.string.NoParticipants}
|
||||
/>
|
||||
<StyledTextBox
|
||||
emphasized
|
||||
showButtons={false}
|
||||
@@ -199,5 +187,14 @@
|
||||
/>
|
||||
<DateRangePresenter bind:value={startDate} labelNull={recruit.string.StartDate} withTime editable on:change={updateStart} />
|
||||
<DateRangePresenter bind:value={dueDate} labelNull={recruit.string.DueDate} withTime editable />
|
||||
<UserBoxList
|
||||
_class={contact.class.Employee}
|
||||
items={doc.participants}
|
||||
label={calendar.string.Participants}
|
||||
on:update={(evt) => {
|
||||
doc.participants = evt.detail
|
||||
}}
|
||||
noItems={calendar.string.NoParticipants}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
<UserBoxList
|
||||
_class={contact.class.Employee}
|
||||
items={object.participants}
|
||||
title={calendar.string.Participants}
|
||||
label={calendar.string.Participants}
|
||||
on:open={(evt) => {
|
||||
client.update(object, { $push: { participants: evt.detail._id } })
|
||||
}}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Canceled": "Canceled",
|
||||
"SetPriority": "Set priority\u2026",
|
||||
"SetStatus": "Set status\u2026",
|
||||
"SelectIssue": "Select issue",
|
||||
"Priority": "Priority",
|
||||
"NoPriority": "No priority",
|
||||
"Urgent": "Urgent",
|
||||
@@ -51,7 +52,7 @@
|
||||
"Project": "Project",
|
||||
"Space": "",
|
||||
"DueDate": "Set due date\u2026",
|
||||
"ModificationDate": "Last Modified: {value}",
|
||||
"ModificationDate": "Updated {value}",
|
||||
"Team": "",
|
||||
"Issue": "",
|
||||
"Document": "",
|
||||
@@ -60,7 +61,11 @@
|
||||
"Rank": "",
|
||||
"IssueTitlePlaceholder": "Issue title",
|
||||
"IssueDescriptionPlaceholder": "Add description",
|
||||
"AddIssueTooltip": "Add issue..."
|
||||
"AddIssueTooltip": "Add issue...",
|
||||
"DueDatePopupTitle": "Due on {value}",
|
||||
"DueDatePopupOverdueTitle": "Was due on {value}",
|
||||
"DueDatePopupDescription": "{value, plural, =0 {Today} =1 {Tomorrow} other {# days remaining}}",
|
||||
"DueDatePopupOverdueDescription": "{value, plural, =1 {1 day overdue} other {# days overdue}}"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
@@ -91,8 +91,6 @@
|
||||
{ icon: tracker.icon.Parent, label: tracker.string.Parent }
|
||||
]
|
||||
|
||||
let issueDate: number | null = null
|
||||
|
||||
const handlePriorityChanged = (newPriority: IssuePriority | undefined) => {
|
||||
if (newPriority === undefined) {
|
||||
return
|
||||
@@ -166,7 +164,7 @@
|
||||
size="small"
|
||||
kind="no-border"
|
||||
/>
|
||||
<DatePresenter value={issueDate} editable />
|
||||
<DatePresenter bind:value={object.dueDate} editable />
|
||||
<Button
|
||||
icon={tracker.icon.MoreActions}
|
||||
width="min-content"
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
/>
|
||||
{:else if kind === 'icon'}
|
||||
<div class="flex-presenter" on:click={handlePriorityEditorOpened}>
|
||||
<div class="icon">
|
||||
<div class="priorityIcon">
|
||||
<Icon icon={issuePriorities[priority].icon} size={'small'} />
|
||||
</div>
|
||||
{#if shouldShowLabel}
|
||||
@@ -62,3 +62,15 @@
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.priorityIcon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
color: var(--theme-content-dark-color);
|
||||
|
||||
&:hover {
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
/>
|
||||
{:else if kind === 'icon'}
|
||||
<div class="flex-presenter" on:click={handleStatusEditorOpened}>
|
||||
<div class="icon">
|
||||
<div class="statusIcon">
|
||||
<Icon icon={issueStatuses[status].icon} size={'small'} />
|
||||
</div>
|
||||
{#if shouldShowLabel}
|
||||
@@ -62,3 +62,10 @@
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.statusIcon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -49,11 +49,14 @@
|
||||
<Scroller>
|
||||
<IssuesList
|
||||
_class={tracker.class.Issue}
|
||||
config={[
|
||||
leftItemsConfig={[
|
||||
{ key: '', presenter: tracker.component.PriorityPresenter, props: { currentSpace } },
|
||||
{ key: '', presenter: tracker.component.IssuePresenter, props: { currentTeam } },
|
||||
{ key: '', presenter: tracker.component.StatusPresenter, props: { currentSpace } },
|
||||
{ key: '', presenter: tracker.component.TitlePresenter },
|
||||
{ key: '', presenter: tracker.component.TitlePresenter }
|
||||
]}
|
||||
rightItemsConfig={[
|
||||
{ key: '', presenter: tracker.component.DueDatePresenter, props: { currentSpace } },
|
||||
{ key: 'modifiedOn', presenter: tracker.component.ModificationDatePresenter },
|
||||
{ key: '', presenter: tracker.component.AssigneePresenter, props: { currentSpace } }
|
||||
]}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<!--
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Icon, Label, IconDPCalendarOver, IconDPCalendar } from '@anticrm/ui'
|
||||
import tracker from '../../plugin'
|
||||
|
||||
export let formattedDate: string = ''
|
||||
export let daysDifference: number = 0
|
||||
export let isOverdue: boolean = false
|
||||
export let iconModifier: 'warning' | 'critical' | 'overdue' | undefined = undefined
|
||||
</script>
|
||||
|
||||
{#if formattedDate}
|
||||
<div class="root">
|
||||
<div
|
||||
class="iconContainer"
|
||||
class:mIconContainerWarning={iconModifier === 'warning'}
|
||||
class:mIconContainerCritical={iconModifier === 'critical' || iconModifier === 'overdue'}
|
||||
>
|
||||
<Icon icon={isOverdue ? IconDPCalendarOver : IconDPCalendar} size={'small'} />
|
||||
</div>
|
||||
<div class="messageContainer">
|
||||
<div class="title">
|
||||
<Label
|
||||
label={isOverdue ? tracker.string.DueDatePopupOverdueTitle : tracker.string.DueDatePopupTitle}
|
||||
params={{ value: formattedDate }}
|
||||
/>
|
||||
</div>
|
||||
<div class="description">
|
||||
<Label
|
||||
label={isOverdue ? tracker.string.DueDatePopupOverdueDescription : tracker.string.DueDatePopupDescription}
|
||||
params={{ value: daysDifference }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
display: flex;
|
||||
width: 15rem;
|
||||
}
|
||||
|
||||
.iconContainer {
|
||||
color: var(--content-color);
|
||||
margin-right: 1rem;
|
||||
|
||||
&.mIconContainerWarning {
|
||||
color: var(--warning-color);
|
||||
}
|
||||
|
||||
&.mIconContainerCritical {
|
||||
color: var(--error-color);
|
||||
}
|
||||
}
|
||||
|
||||
.messageContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: var(--theme-caption-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-top: 0.25rem;
|
||||
color: var(--theme-content-dark-color);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,89 @@
|
||||
<!--
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref, Timestamp } from '@anticrm/core'
|
||||
import { Issue, IssueStatus, Team } from '@anticrm/tracker'
|
||||
import { DatePresenter, Tooltip, getDaysDifference } from '@anticrm/ui'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import DueDatePopup from './DueDatePopup.svelte'
|
||||
import tracker from '../../plugin'
|
||||
|
||||
export let value: Issue
|
||||
export let currentSpace: Ref<Team> | undefined = undefined
|
||||
|
||||
const WARNING_DAYS = 7
|
||||
const client = getClient()
|
||||
|
||||
$: today = new Date(new Date(Date.now()).setHours(0, 0, 0, 0))
|
||||
$: dueDateMs = value.dueDate
|
||||
$: isOverdue = dueDateMs !== null && dueDateMs < today.getTime()
|
||||
$: dueDate = dueDateMs === null ? null : new Date(dueDateMs)
|
||||
$: daysDifference = dueDate === null ? null : getDaysDifference(today, dueDate)
|
||||
$: iconModifier = getIconModifier(isOverdue, daysDifference)
|
||||
$: formattedDate = !dueDateMs ? '' : new Date(dueDateMs).toLocaleString('default', { month: 'short', day: 'numeric' })
|
||||
|
||||
const handleDueDateChanged = async (event: CustomEvent<Timestamp>) => {
|
||||
const newDate = event.detail
|
||||
|
||||
if (newDate === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const currentIssue = await client.findOne(tracker.class.Issue, { space: currentSpace, _id: value._id })
|
||||
|
||||
if (currentIssue === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
await client.update(currentIssue, { dueDate: newDate })
|
||||
}
|
||||
|
||||
const getIconModifier = (isOverdue: boolean, daysDifference: number | null) => {
|
||||
if (isOverdue) {
|
||||
return 'overdue' as 'overdue' // Fixes `DatePresenter` icon type issue
|
||||
}
|
||||
|
||||
if (daysDifference === 0) {
|
||||
return 'critical' as 'critical'
|
||||
}
|
||||
|
||||
if (daysDifference !== null && daysDifference <= WARNING_DAYS) {
|
||||
return 'warning' as 'warning'
|
||||
}
|
||||
}
|
||||
|
||||
$: shouldRenderPresenter = dueDateMs && value.status !== IssueStatus.Done && value.status !== IssueStatus.Canceled
|
||||
</script>
|
||||
|
||||
{#if shouldRenderPresenter}
|
||||
<Tooltip
|
||||
direction={'top'}
|
||||
component={DueDatePopup}
|
||||
props={{
|
||||
formattedDate: formattedDate,
|
||||
daysDifference: daysDifference,
|
||||
isOverdue: isOverdue,
|
||||
iconModifier: iconModifier
|
||||
}}
|
||||
>
|
||||
<DatePresenter
|
||||
value={dueDateMs}
|
||||
editable={true}
|
||||
shouldShowLabel={false}
|
||||
icon={iconModifier}
|
||||
on:change={handleDueDateChanged}
|
||||
/>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
@@ -16,14 +16,16 @@
|
||||
import { Class, Doc, DocumentQuery, FindOptions, Ref, getObjectValue } from '@anticrm/core'
|
||||
import { SortingOrder } from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { CheckBox, Loading, showPopup, Spinner, IconMoreV } from '@anticrm/ui'
|
||||
import { CheckBox, Loading, showPopup, Spinner, IconMoreV, Tooltip } from '@anticrm/ui'
|
||||
import { BuildModelKey } from '@anticrm/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { buildModel, LoadingProps, Menu } from '@anticrm/view-resources'
|
||||
import tracker from '../../plugin'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
|
||||
export let config: (BuildModelKey | string)[]
|
||||
export let leftItemsConfig: (BuildModelKey | string)[]
|
||||
export let rightItemsConfig: (BuildModelKey | string)[] | undefined = undefined
|
||||
export let options: FindOptions<Doc> | undefined = undefined
|
||||
export let query: DocumentQuery<Doc>
|
||||
|
||||
@@ -92,13 +94,20 @@
|
||||
|
||||
return props.length
|
||||
}
|
||||
|
||||
const buildItemModels = async () => {
|
||||
const leftModels = await buildModel({ client, _class, keys: leftItemsConfig, options })
|
||||
const rightModels = rightItemsConfig && (await buildModel({ client, _class, keys: rightItemsConfig, options }))
|
||||
|
||||
return { leftModels, rightModels }
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await buildModel({ client, _class, keys: config, options })}
|
||||
{#await buildItemModels()}
|
||||
{#if !isLoading}
|
||||
<Loading />
|
||||
{/if}
|
||||
{:then attributeModels}
|
||||
{:then itemModels}
|
||||
<div class="listRoot">
|
||||
{#if docObjects}
|
||||
{#each docObjects as docObject, rowIndex (docObject._id)}
|
||||
@@ -107,65 +116,80 @@
|
||||
class:mListGridChecked={selectedIssueIds.has(docObject._id)}
|
||||
class:mListGridFixed={rowIndex === selectedRowIndex}
|
||||
>
|
||||
{#each attributeModels as attributeModel, attributeModelIndex}
|
||||
{#if attributeModelIndex === 0}
|
||||
<div class="gridElement">
|
||||
<div class="eListGridCheckBox ml-2">
|
||||
<CheckBox
|
||||
checked={selectedIssueIds.has(docObject._id)}
|
||||
on:value={(event) => {
|
||||
handleIssueSelected(docObject._id, event)
|
||||
}}
|
||||
/>
|
||||
<div class="modelsContainer">
|
||||
{#each itemModels.leftModels as attributeModel, attributeModelIndex}
|
||||
{#if attributeModelIndex === 0}
|
||||
<div class="gridElement">
|
||||
<Tooltip direction={'bottom'} label={tracker.string.SelectIssue}>
|
||||
<div class="eListGridCheckBox ml-2">
|
||||
<CheckBox
|
||||
checked={selectedIssueIds.has(docObject._id)}
|
||||
on:value={(event) => {
|
||||
handleIssueSelected(docObject._id, event)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div class="priorityPresenter">
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="priorityPresenter">
|
||||
{:else if attributeModelIndex === 1}
|
||||
<div class="issuePresenter">
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
<div
|
||||
id="context-menu"
|
||||
class="eIssuePresenterContextMenu"
|
||||
on:click={(event) => showMenu(event, docObject, rowIndex)}
|
||||
>
|
||||
<IconMoreV size={'small'} />
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="gridElement">
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{:else if attributeModelIndex === 1}
|
||||
<div class="issuePresenter">
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
<div
|
||||
id="context-menu"
|
||||
class="eIssuePresenterContextMenu"
|
||||
on:click={(event) => showMenu(event, docObject, rowIndex)}
|
||||
>
|
||||
<IconMoreV size={'small'} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{#if itemModels.rightModels}
|
||||
<div class="modelsContainer">
|
||||
{#each itemModels.rightModels as attributeModel}
|
||||
<div class="gridElement">
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="gridElement">
|
||||
<svelte:component
|
||||
this={attributeModel.presenter}
|
||||
value={getObjectValue(attributeModel.key, docObject) ?? ''}
|
||||
{...attributeModel.props}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{:else if loadingProps !== undefined}
|
||||
{#each Array(getLoadingElementsLength(loadingProps, options)) as _, rowIndex}
|
||||
<div class="listGrid mListGridIsLoading" class:fixed={rowIndex === selectedRowIndex}>
|
||||
{#each attributeModels as _, attributeModelIndex}
|
||||
{#if attributeModelIndex === 0}
|
||||
<div class="gridElement">
|
||||
<CheckBox checked={false} />
|
||||
<div class="ml-4">
|
||||
<Spinner size="small" />
|
||||
</div>
|
||||
<div class="modelsContainer">
|
||||
<div class="gridElement">
|
||||
<CheckBox checked={false} />
|
||||
<div class="ml-4">
|
||||
<Spinner size="small" />
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
@@ -182,8 +206,9 @@
|
||||
}
|
||||
|
||||
.listGrid {
|
||||
display: grid;
|
||||
grid-template-columns: 4rem 5rem 2rem auto 4rem 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 3.25rem;
|
||||
color: var(--theme-caption-color);
|
||||
border-bottom: 1px solid var(--theme-button-border-hovered);
|
||||
@@ -203,7 +228,7 @@
|
||||
}
|
||||
|
||||
&.mListGridIsLoading {
|
||||
grid-template-columns: auto;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@@ -225,24 +250,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
.checkBox {
|
||||
.modelsContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.03rem;
|
||||
border-radius: 0.25rem;
|
||||
background-color: rgba(247, 248, 248, 0.5);
|
||||
opacity: 0;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.gridElement {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
margin-left: 0.5rem;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.priorityPresenter {
|
||||
@@ -252,7 +273,7 @@
|
||||
.issuePresenter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 1rem;
|
||||
margin-left: 0.5rem;
|
||||
|
||||
.eIssuePresenterContextMenu {
|
||||
visibility: hidden;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import { Ref } from '@anticrm/core'
|
||||
import { Issue, IssuePriority, Team } from '@anticrm/tracker'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Tooltip } from '@anticrm/ui'
|
||||
import tracker from '../../plugin'
|
||||
import PrioritySelector from '../PrioritySelector.svelte'
|
||||
|
||||
@@ -40,10 +41,12 @@
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<PrioritySelector
|
||||
kind={'icon'}
|
||||
shouldShowLabel={false}
|
||||
priority={value.priority}
|
||||
onPriorityChange={handlePriorityChanged}
|
||||
/>
|
||||
<Tooltip direction={'bottom'} label={tracker.string.SetPriority}>
|
||||
<PrioritySelector
|
||||
kind={'icon'}
|
||||
shouldShowLabel={false}
|
||||
priority={value.priority}
|
||||
onPriorityChange={handlePriorityChanged}
|
||||
/>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import { Ref } from '@anticrm/core'
|
||||
import { Issue, IssueStatus, Team } from '@anticrm/tracker'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Tooltip } from '@anticrm/ui'
|
||||
import tracker from '../../plugin'
|
||||
import StatusSelector from '../StatusSelector.svelte'
|
||||
|
||||
@@ -40,5 +41,7 @@
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<StatusSelector kind={'icon'} shouldShowLabel={false} status={value.status} onStatusChange={handleStatusChanged} />
|
||||
<Tooltip direction={'bottom'} label={tracker.string.SetStatus}>
|
||||
<StatusSelector kind={'icon'} shouldShowLabel={false} status={value.status} onStatusChange={handleStatusChanged} />
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
@@ -29,6 +29,7 @@ import IssuePresenter from './components/issues/IssuePresenter.svelte'
|
||||
import TitlePresenter from './components/issues/TitlePresenter.svelte'
|
||||
import PriorityPresenter from './components/issues/PriorityPresenter.svelte'
|
||||
import StatusPresenter from './components/issues/StatusPresenter.svelte'
|
||||
import DueDatePresenter from './components/issues/DueDatePresenter.svelte'
|
||||
import AssigneePresenter from './components/issues/AssigneePresenter.svelte'
|
||||
|
||||
import ModificationDatePresenter from './components/issues/ModificationDatePresenter.svelte'
|
||||
@@ -52,6 +53,7 @@ export default async (): Promise<Resources> => ({
|
||||
PriorityPresenter,
|
||||
StatusPresenter,
|
||||
AssigneePresenter,
|
||||
DueDatePresenter,
|
||||
EditIssue,
|
||||
NewIssueHeader
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ export default mergeIds(trackerId, tracker, {
|
||||
AddIssue: '' as IntlString,
|
||||
NewIssue: '' as IntlString,
|
||||
Team: '' as IntlString,
|
||||
SelectIssue: '' as IntlString,
|
||||
SelectTeam: '' as IntlString,
|
||||
SaveIssue: '' as IntlString,
|
||||
Todo: '' as IntlString,
|
||||
@@ -76,6 +77,10 @@ export default mergeIds(trackerId, tracker, {
|
||||
DocumentIcon: '' as IntlString,
|
||||
DocumentColor: '' as IntlString,
|
||||
Rank: '' as IntlString,
|
||||
DueDatePopupTitle: '' as IntlString,
|
||||
DueDatePopupOverdueTitle: '' as IntlString,
|
||||
DueDatePopupDescription: '' as IntlString,
|
||||
DueDatePopupOverdueDescription: '' as IntlString,
|
||||
|
||||
IssueTitlePlaceholder: '' as IntlString,
|
||||
IssueDescriptionPlaceholder: '' as IntlString,
|
||||
@@ -98,6 +103,7 @@ export default mergeIds(trackerId, tracker, {
|
||||
PriorityPresenter: '' as AnyComponent,
|
||||
StatusPresenter: '' as AnyComponent,
|
||||
AssigneePresenter: '' as AnyComponent,
|
||||
DueDatePresenter: '' as AnyComponent,
|
||||
EditIssue: '' as AnyComponent,
|
||||
CreateTeam: '' as AnyComponent,
|
||||
NewIssueHeader: '' as AnyComponent
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"@anticrm/calendar": "~0.6.0",
|
||||
"@anticrm/notification": "~0.6.0",
|
||||
"@anticrm/notification-resources": "~0.6.0",
|
||||
"@anticrm/preference": "~0.6.0",
|
||||
"@anticrm/contact": "~0.6.5",
|
||||
"@anticrm/view-resources": "~0.6.0"
|
||||
}
|
||||
|
||||
@@ -69,11 +69,10 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="antiPopup">
|
||||
<div class="ap-space" />
|
||||
<div class="ap-scroll"><div class="ap-box">
|
||||
<div class="selectPopup">
|
||||
<div class="scroll"><div class="box">
|
||||
{#await getItems() then items}
|
||||
<div class="ap-menuItem flex-row-center" on:click={() => { editProfile(items) }}>
|
||||
<div class="menu-item high flex-row-center" on:click={() => { editProfile(items) }}>
|
||||
{#if employee}
|
||||
<Avatar avatar={employee.avatar} size={'medium'} />
|
||||
{/if}
|
||||
@@ -86,7 +85,7 @@
|
||||
</div>
|
||||
{#if items}
|
||||
{#each filterItems(items) as item }
|
||||
<button class="ap-menuItem" on:click={() => selectCategory(item)}>
|
||||
<button class="menu-item" on:click={() => selectCategory(item)}>
|
||||
<div class='mr-2'>
|
||||
<Icon icon={item.icon} size={'small'}/>
|
||||
</div>
|
||||
@@ -94,13 +93,13 @@
|
||||
</button>
|
||||
{/each}
|
||||
{/if}
|
||||
<button class="ap-menuItem" on:click={selectWorkspace}>
|
||||
<button class="menu-item" on:click={selectWorkspace}>
|
||||
<div class='mr-2'>
|
||||
<Icon icon={setting.icon.SelectWorkspace} size={'small'}/>
|
||||
</div>
|
||||
<Label label={setting.string.SelectWorkspace} />
|
||||
</button>
|
||||
<button class="ap-menuItem" on:click={signOut}>
|
||||
<button class="menu-item" on:click={signOut}>
|
||||
<div class='mr-2'>
|
||||
<Icon icon={setting.icon.Signout} size={'small'}/>
|
||||
</div>
|
||||
@@ -108,5 +107,4 @@
|
||||
</button>
|
||||
{/await}
|
||||
</div></div>
|
||||
<div class="ap-space" />
|
||||
</div>
|
||||
|
||||
@@ -1,40 +1,45 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
//
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import core, { Ref, SortingOrder, Space } from '@anticrm/core'
|
||||
import core, { Doc, Ref, SortingOrder, Space } from '@anticrm/core'
|
||||
import { getResource } from '@anticrm/platform'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { Scroller } from '@anticrm/ui'
|
||||
import type { NavigatorModel, SpecialNavModel } from '@anticrm/workbench'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import preferece, { SpacePreference } from '@anticrm/preference'
|
||||
import { getSpecialSpaceClass } from '../utils'
|
||||
import preference from '@anticrm/preference'
|
||||
import SpacesNav from './navigator/SpacesNav.svelte'
|
||||
import SpecialElement from './navigator/SpecialElement.svelte'
|
||||
import StarredNav from './navigator/StarredNav.svelte'
|
||||
import TreeSeparator from './navigator/TreeSeparator.svelte'
|
||||
|
||||
export let model: NavigatorModel | undefined
|
||||
export let currentSpace: Ref<Space> | undefined
|
||||
export let currentSpecial: string | undefined
|
||||
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const query = createQuery()
|
||||
|
||||
let spaces: Space[] = []
|
||||
let starred: Space[] = []
|
||||
let shownSpaces: Space[] = []
|
||||
|
||||
|
||||
$: if (model) {
|
||||
query.query(
|
||||
core.class.Space,
|
||||
@@ -45,31 +50,31 @@
|
||||
{ sort: { name: SortingOrder.Ascending } })
|
||||
}
|
||||
|
||||
let specTopCount: number
|
||||
let specBottomCount: number
|
||||
let spModelCount: number
|
||||
|
||||
let topSpecials: SpecialNavModel[] = []
|
||||
let bottomSpecials: SpecialNavModel[] = []
|
||||
|
||||
async function update (model: NavigatorModel, spaces: Space[]) {
|
||||
let preferences: Map<Ref<Doc>, SpacePreference> = new Map<Ref<Doc>, SpacePreference>()
|
||||
|
||||
const preferenceQuery = createQuery()
|
||||
|
||||
preferenceQuery.query(preferece.class.SpacePreference, {}, (res) => {
|
||||
preferences = new Map(res.map((r) => { return [r.attachedTo, r] }))
|
||||
})
|
||||
|
||||
async function update (model: NavigatorModel, spaces: Space[], preferences: Map<Ref<Doc>, SpacePreference>) {
|
||||
if (model.specials !== undefined) {
|
||||
topSpecials = await getSpecials(model.specials, 'top', spaces)
|
||||
specTopCount = topSpecials.length
|
||||
bottomSpecials = await getSpecials(model.specials, 'bottom', spaces)
|
||||
specBottomCount = bottomSpecials.length
|
||||
} else {
|
||||
topSpecials = []
|
||||
bottomSpecials = []
|
||||
specBottomCount = 0
|
||||
specTopCount = 0
|
||||
}
|
||||
if (model.spaces) spModelCount = model.spaces.length
|
||||
shownSpaces = spaces.filter(sp => !sp.archived)
|
||||
shownSpaces = spaces.filter((sp) => !sp.archived && !preferences.has(sp._id))
|
||||
starred = spaces.filter((sp) => preferences.has(sp._id))
|
||||
}
|
||||
|
||||
$: if (model) update(model, spaces)
|
||||
|
||||
$: if (model) update(model, spaces, preferences)
|
||||
|
||||
async function getSpecials (specials: SpecialNavModel[], state: 'top' | 'bottom', spaces: Space[]): Promise<SpecialNavModel[]> {
|
||||
const result: SpecialNavModel[] = []
|
||||
for (const sp of specials) {
|
||||
@@ -105,6 +110,10 @@
|
||||
|
||||
{#if topSpecials.length > 0 || bottomSpecials.length > 0}<TreeSeparator />{/if}
|
||||
|
||||
{#if starred.length}
|
||||
<StarredNav label={preference.string.Starred} spaces={starred} on:space {currentSpace} />
|
||||
{/if}
|
||||
|
||||
{#each model.spaces as m (m.label)}
|
||||
<SpacesNav spaces={shownSpaces.filter(it => hierarchy.isDerived(it._class, m.spaceClass))} {currentSpace} model={m} on:space {currentSpecial}/>
|
||||
{/each}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Action, AnyComponent, IconAdd, IconEdit, showPanel, showPopup } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import preference from '@anticrm/preference'
|
||||
import { getActions as getContributedActions } from '@anticrm/view-resources'
|
||||
import { SpacesNavModel } from '@anticrm/workbench'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -54,7 +55,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function getEditor(_class: Ref<Class<Doc>>): Promise<AnyComponent | undefined> {
|
||||
const starSpace: Action = {
|
||||
label: preference.string.Star,
|
||||
icon: preference.icon.Star,
|
||||
action: async (_id: Ref<Space>): Promise<void> => {
|
||||
await client.createDoc(preference.class.SpacePreference, preference.space.Preference, {
|
||||
attachedTo: _id
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function getEditor (_class: Ref<Class<Doc>>): Promise<AnyComponent | undefined> {
|
||||
const hierarchy = client.getHierarchy()
|
||||
const clazz = hierarchy.getClass(_class)
|
||||
const editorMixin = hierarchy.as(clazz, view.mixin.ObjectEditor)
|
||||
@@ -62,12 +73,12 @@
|
||||
return editorMixin.editor
|
||||
}
|
||||
|
||||
function selectSpace(id: Ref<Space>, spaceSpecial?: string) {
|
||||
function selectSpace (id: Ref<Space>, spaceSpecial?: string) {
|
||||
dispatch('space', { space: id, spaceSpecial })
|
||||
}
|
||||
|
||||
async function getActions(space: Space): Promise<Action[]> {
|
||||
const result = [editSpace]
|
||||
async function getActions (space: Space): Promise<Action[]> {
|
||||
const result = [editSpace, starSpace]
|
||||
|
||||
const extraActions = await getContributedActions(client, space, core.class.Space)
|
||||
for (const act of extraActions) {
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
<!--
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Doc, Ref, Space } from '@anticrm/core'
|
||||
import core from '@anticrm/core'
|
||||
import notification from '@anticrm/notification'
|
||||
import { NotificationClientImpl } from '@anticrm/notification-resources'
|
||||
import { getResource, IntlString } from '@anticrm/platform'
|
||||
import preference from '@anticrm/preference'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Action, IconEdit } from '@anticrm/ui'
|
||||
import view from '@anticrm/view'
|
||||
import { getActions as getContributedActions } from '@anticrm/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { classIcon } from '../../utils'
|
||||
import TreeItem from './TreeItem.svelte'
|
||||
import TreeNode from './TreeNode.svelte'
|
||||
|
||||
export let label: IntlString
|
||||
export let currentSpace: Ref<Space> | undefined
|
||||
export let spaces: Space[]
|
||||
const client = getClient()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const unStarSpace: Action = {
|
||||
label: preference.string.Unstar,
|
||||
icon: preference.icon.Star,
|
||||
action: async (_id: Ref<Doc>): Promise<void> => {
|
||||
const current = await client.findOne(preference.class.SpacePreference, { attachedTo: _id as Ref<Space> })
|
||||
if (current !== undefined) {
|
||||
await client.remove(current)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const unStarAll: Action = {
|
||||
label: preference.string.DeleteStarred,
|
||||
icon: view.icon.Delete,
|
||||
action: async (): Promise<void> => {
|
||||
const ids = spaces.map((space) => space._id)
|
||||
const current = await client.findAll(preference.class.SpacePreference, { attachedTo: { $in: ids } })
|
||||
|
||||
await Promise.all(current.map(async (item) => { await client.remove(item) }))
|
||||
}
|
||||
}
|
||||
|
||||
function selectSpace (id: Ref<Space>, spaceSpecial?: string) {
|
||||
dispatch('space', { space: id, spaceSpecial })
|
||||
}
|
||||
|
||||
async function getActions (space: Space): Promise<Action[]> {
|
||||
const result = [unStarSpace]
|
||||
|
||||
const extraActions = await getContributedActions(client, space, core.class.Space)
|
||||
for (const act of extraActions) {
|
||||
result.push({
|
||||
icon: act.icon ?? IconEdit,
|
||||
label: act.label,
|
||||
action: async () => {
|
||||
const impl = await getResource(act.action)
|
||||
await impl(space)
|
||||
}
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
const lastViews = notificationClient.getLastViews()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
function isChanged (space: Space, lastViews: Map<Ref<Doc>, number>): boolean {
|
||||
const clazz = hierarchy.getClass(space._class)
|
||||
const lastEditMixin = hierarchy.as(clazz, notification.mixin.SpaceLastEdit)
|
||||
const field = lastEditMixin?.lastEditField
|
||||
const lastView = lastViews.get(space._id)
|
||||
if (lastView === undefined || lastView === -1) return false
|
||||
if (field === undefined) return false
|
||||
const value = (space as any)[field]
|
||||
if (isNaN(value)) return false
|
||||
|
||||
return lastView < value
|
||||
}
|
||||
</script>
|
||||
|
||||
<TreeNode label={label} parent actions={async () => [unStarAll]} indent={'ml-2'}>
|
||||
{#each spaces as space (space._id)}
|
||||
<TreeItem
|
||||
indent={'ml-4'}
|
||||
_id={space._id}
|
||||
title={space.name}
|
||||
icon={classIcon(client, space._class)}
|
||||
selected={currentSpace === space._id}
|
||||
actions={() => getActions(space)}
|
||||
bold={isChanged(space, $lastViews)}
|
||||
on:click={() => {
|
||||
selectSpace(space._id)
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
</TreeNode>
|
||||
@@ -98,6 +98,8 @@
|
||||
"@anticrm/notification": "~0.6.0",
|
||||
"@anticrm/notification-assets": "~0.6.0",
|
||||
"@anticrm/notification-resources": "~0.6.0",
|
||||
"@anticrm/preference": "~0.6.0",
|
||||
"@anticrm/preference-assets": "~0.6.0",
|
||||
"@anticrm/calendar": "~0.6.0",
|
||||
"@anticrm/calendar-assets": "~0.6.0",
|
||||
"@anticrm/calendar-resources": "~0.6.0",
|
||||
|
||||
@@ -33,6 +33,7 @@ import { templatesId } from '@anticrm/templates'
|
||||
import { notificationId } from '@anticrm/notification'
|
||||
import { calendarId } from '@anticrm/calendar'
|
||||
import { trackerId } from '@anticrm/tracker'
|
||||
import { preferenceId } from '@anticrm/preference'
|
||||
|
||||
import '@anticrm/login-assets'
|
||||
import '@anticrm/task-assets'
|
||||
@@ -47,6 +48,7 @@ import '@anticrm/gmail-assets'
|
||||
import '@anticrm/workbench-assets'
|
||||
import '@anticrm/templates-assets'
|
||||
import '@anticrm/notification-assets'
|
||||
import '@anticrm/preference-assets'
|
||||
import '@anticrm/tracker-assets'
|
||||
import presentation, { presentationId } from '@anticrm/presentation'
|
||||
import { coreId } from '@anticrm/core'
|
||||
|
||||
@@ -1262,6 +1262,31 @@
|
||||
"packageName": "@anticrm/kanban",
|
||||
"projectFolder": "packages/kanban",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/preference",
|
||||
"projectFolder": "plugins/preference",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/preference-assets",
|
||||
"projectFolder": "plugins/preference-assets",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/model-preference",
|
||||
"projectFolder": "models/preference",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/server-preference",
|
||||
"projectFolder": "server-plugins/preference",
|
||||
"shouldPublish": true
|
||||
},
|
||||
{
|
||||
"packageName": "@anticrm/middleware",
|
||||
"projectFolder": "server/middleware",
|
||||
"shouldPublish": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -15,7 +15,21 @@
|
||||
|
||||
import chunter, { Channel, Comment, Message, ThreadMessage } from '@anticrm/chunter'
|
||||
import { EmployeeAccount } from '@anticrm/contact'
|
||||
import core, { Class, Doc, DocumentQuery, FindOptions, FindResult, Hierarchy, Ref, Tx, TxCreateDoc, TxProcessor, TxUpdateDoc, TxRemoveDoc } from '@anticrm/core'
|
||||
import core, {
|
||||
Class,
|
||||
Doc,
|
||||
DocumentQuery,
|
||||
FindOptions,
|
||||
FindResult,
|
||||
Hierarchy,
|
||||
Ref,
|
||||
Tx,
|
||||
TxCreateDoc,
|
||||
TxProcessor,
|
||||
TxUpdateDoc,
|
||||
TxRemoveDoc,
|
||||
TxCollectionCUD
|
||||
} from '@anticrm/core'
|
||||
import login from '@anticrm/login'
|
||||
import { getMetadata } from '@anticrm/platform'
|
||||
import { TriggerControl } from '@anticrm/server-core'
|
||||
@@ -144,12 +158,50 @@ export async function MessageCreate (tx: Tx, control: TriggerControl): Promise<T
|
||||
return []
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function MessageDelete (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const hierarchy = control.hierarchy
|
||||
if (tx._class !== core.class.TxCollectionCUD) return []
|
||||
|
||||
const rmTx = (tx as TxCollectionCUD<Channel, Message>).tx
|
||||
if (!hierarchy.isDerived(rmTx.objectClass, chunter.class.Message)) {
|
||||
return []
|
||||
}
|
||||
const createTx = (await control.findAll(core.class.TxCreateDoc, {
|
||||
objectId: rmTx.objectId
|
||||
}, { limit: 1 }))[0]
|
||||
|
||||
const message = TxProcessor.createDoc2Doc(createTx as TxCreateDoc<Message>)
|
||||
|
||||
const channel = (await control.findAll(chunter.class.Channel, {
|
||||
_id: message.space
|
||||
}, { limit: 1 }))[0]
|
||||
|
||||
if (channel.lastMessage === message.createOn) {
|
||||
const messages = await control.findAll(chunter.class.Message, {
|
||||
attachedTo: channel._id
|
||||
})
|
||||
const lastMessageDate = messages.reduce((maxDate, mess) => mess.createOn > maxDate ? mess.createOn : maxDate, 0)
|
||||
|
||||
const updateTx = control.txFactory.createTxUpdateDoc<Channel>(channel._class, channel.space, channel._id, {
|
||||
lastMessage: lastMessageDate > 0 ? lastMessageDate : undefined
|
||||
})
|
||||
|
||||
return [updateTx]
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function ChunterTrigger (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||
const promises = [
|
||||
MessageCreate(tx, control),
|
||||
MessageDelete(tx, control),
|
||||
CommentCreate(tx, control),
|
||||
CommentDelete(tx, control)
|
||||
]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user