1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-05 00:43:53 +00:00

feat: sf metadata display (#6249)

* feat: sf metadata display

* feat: update code

* feat: update code

* feat: lock react version

* feat: bug

* feat: optimize code

* feat: update transalte

* feat: update transalte

* feat: rebase code

* Feat: update code

* Feat: update code

---------

Co-authored-by: 杨国璇 <ygx@192.168.1.5>
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
Co-authored-by: 杨国璇 <ygx@192.168.1.13>
This commit is contained in:
杨国璇
2024-06-29 17:58:27 +08:00
committed by GitHub
parent 4f888be82c
commit 19f15c944f
207 changed files with 18330 additions and 348 deletions

View File

@@ -17,6 +17,7 @@
"@seafile/sdoc-editor": "1.0.4",
"@seafile/seafile-calendar": "0.0.12",
"@seafile/seafile-editor": "1.0.99",
"@seafile/sf-metadata-ui-component": "0.0.4",
"@uiw/codemirror-extensions-langs": "^4.19.4",
"@uiw/react-codemirror": "^4.19.4",
"chart.js": "2.9.4",
@@ -2879,14 +2880,14 @@
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
},
"node_modules/@emotion/react": {
"version": "11.11.1",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz",
"integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==",
"version": "11.11.4",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz",
"integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==",
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.11.0",
"@emotion/cache": "^11.11.0",
"@emotion/serialize": "^1.1.2",
"@emotion/serialize": "^1.1.3",
"@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
"@emotion/utils": "^1.2.1",
"@emotion/weak-memoize": "^0.3.1",
@@ -2902,9 +2903,9 @@
}
},
"node_modules/@emotion/serialize": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz",
"integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==",
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz",
"integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==",
"dependencies": {
"@emotion/hash": "^0.9.1",
"@emotion/memoize": "^0.8.1",
@@ -3055,26 +3056,41 @@
}
},
"node_modules/@floating-ui/core": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.4.1.tgz",
"integrity": "sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==",
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.3.tgz",
"integrity": "sha512-1ZpCvYf788/ZXOhRQGFxnYQOVgeU+pi0i+d0Ow34La7qjIXETi6RNswGVKkA6KcDO8/+Ysu2E/CeUmmeEBDvTg==",
"dependencies": {
"@floating-ui/utils": "^0.1.1"
"@floating-ui/utils": "^0.2.3"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.1.tgz",
"integrity": "sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw==",
"version": "1.6.6",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.6.tgz",
"integrity": "sha512-qiTYajAnh3P+38kECeffMSQgbvXty2VB6rS+42iWR4FPIlZjLK84E9qtLnMTLIpPz2znD/TaFqaiavMUrS+Hcw==",
"dependencies": {
"@floating-ui/core": "^1.4.1",
"@floating-ui/utils": "^0.1.1"
"@floating-ui/core": "^1.0.0",
"@floating-ui/utils": "^0.2.3"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.1.tgz",
"integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw=="
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.3.tgz",
"integrity": "sha512-XGndio0l5/Gvd6CLIABvsav9HHezgDFFhDfHk1bvLfr9ni8dojqLSvBbotJEjmIwNHL7vK4QzBJTdBRoB+c1ww=="
},
"node_modules/@formatjs/intl-unified-numberformat": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/@formatjs/intl-unified-numberformat/-/intl-unified-numberformat-3.3.7.tgz",
"integrity": "sha512-KnWgLRHzCAgT9eyt3OS34RHoyD7dPDYhRcuKn+/6Kv2knDF8Im43J6vlSW6Hm1w63fNq3ZIT1cFk7RuVO3Psag==",
"deprecated": "We have renamed the package to @formatjs/intl-numberformat",
"dependencies": {
"@formatjs/intl-utils": "^2.3.0"
}
},
"node_modules/@formatjs/intl-utils": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@formatjs/intl-utils/-/intl-utils-2.3.0.tgz",
"integrity": "sha512-KWk80UPIzPmUg+P0rKh6TqspRw0G6eux1PuJr+zz47ftMaZ9QDwbGzHZbtzWkl5hgayM/qrKRutllRC7D/vVXQ==",
"deprecated": "the package is rather renamed to @formatjs/ecma-abstract with some changes in functionality (primarily selectUnit is removed and we don't plan to make any further changes to this package"
},
"node_modules/@gatsbyjs/reach-router": {
"version": "1.3.9",
@@ -4936,6 +4952,53 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/@seafile/sf-metadata-ui-component": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.4.tgz",
"integrity": "sha512-Zf8SPVXkG7iQFqaDOtgsrcIVn5l0rfX7c8h4E9yf+Ho6Y/S4Rq3vL/emLUR4syE18OlWlw656oNhsvapeWsQjQ==",
"dependencies": {
"@seafile/seafile-calendar": "0.0.24",
"classnames": "2.3.2",
"dayjs": "1.10.7",
"escape-html": "^1.0.3",
"glamor": "^2.20.40",
"intl-messageformat": "^7.8.4",
"invariant": "^2.2.2",
"is-hotkey": "0.2.0",
"prop-types": "^15.8.1",
"react-app-polyfill": "^3.0.0",
"react-responsive": "9.0.2",
"react-select": "5.7.0",
"react-transition-group": "^4.4.1",
"reactstrap": "8.9.0"
},
"peerDependencies": {
"@seafile/seafile-calendar": "0.0.24",
"lodash-es": "^4.17.21",
"prop-types": "15.8.1",
"react": "17.0.0",
"react-dom": "17.0.0"
}
},
"node_modules/@seafile/sf-metadata-ui-component/node_modules/@seafile/seafile-calendar": {
"version": "0.0.24",
"resolved": "https://registry.npmjs.org/@seafile/seafile-calendar/-/seafile-calendar-0.0.24.tgz",
"integrity": "sha512-q1efVDcHAxJ2foMgsR8mQPD6Fbd6ISu2WHRM82P7tO0KPiQNS5pz9V0YVCblgi7da085jaog2iAplJM+vH7xLQ==",
"dependencies": {
"babel-runtime": "6.x",
"classnames": "2.x",
"dayjs": "1.10.7",
"prop-types": "^15.5.8",
"rc-trigger": "^2.2.0",
"rc-util": "^4.1.1",
"react-lifecycles-compat": "^3.0.4"
}
},
"node_modules/@seafile/sf-metadata-ui-component/node_modules/dayjs": {
"version": "1.10.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz",
"integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig=="
},
"node_modules/@seafile/slate": {
"version": "0.91.8",
"resolved": "https://registry.npmjs.org/@seafile/slate/-/slate-0.91.8.tgz",
@@ -5943,9 +6006,9 @@
"dev": true
},
"node_modules/@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
"version": "15.7.12",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
},
"node_modules/@types/q": {
"version": "1.5.6",
@@ -5966,19 +6029,18 @@
"dev": true
},
"node_modules/@types/react": {
"version": "18.2.21",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz",
"integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==",
"version": "18.3.3",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz",
"integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==",
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-transition-group": {
"version": "4.4.6",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz",
"integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==",
"version": "4.4.10",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz",
"integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==",
"dependencies": {
"@types/react": "*"
}
@@ -5998,11 +6060,6 @@
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
"dev": true
},
"node_modules/@types/scheduler": {
"version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
},
"node_modules/@types/semver": {
"version": "7.5.1",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz",
@@ -8232,12 +8289,18 @@
}
},
"node_modules/call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"dependencies": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -9725,6 +9788,22 @@
"node": ">= 10"
}
},
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
@@ -9735,10 +9814,11 @@
}
},
"node_modules/define-properties": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
"integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
"integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
"dependencies": {
"define-data-property": "^1.0.1",
"has-property-descriptors": "^1.0.0",
"object-keys": "^1.1.1"
},
@@ -10471,6 +10551,25 @@
"integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
"dev": true
},
"node_modules/es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"dependencies": {
"get-intrinsic": "^1.2.4"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-get-iterator": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
@@ -10577,8 +10676,7 @@
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"dev": true
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
},
"node_modules/escape-string-regexp": {
"version": "1.0.5",
@@ -12370,9 +12468,12 @@
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/function.prototype.name": {
"version": "1.1.6",
@@ -12419,14 +12520,18 @@
}
},
"node_modules/get-intrinsic": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"dependencies": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3"
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -12629,7 +12734,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dev": true,
"dependencies": {
"get-intrinsic": "^1.1.3"
},
@@ -12747,11 +12851,11 @@
}
},
"node_modules/has-property-descriptors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
"integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dependencies": {
"get-intrinsic": "^1.1.1"
"es-define-property": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -12894,6 +12998,17 @@
"minimalistic-assert": "^1.0.1"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/hast-util-embedded": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz",
@@ -13954,6 +14069,29 @@
"node": ">= 0.4"
}
},
"node_modules/intl-format-cache": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/intl-format-cache/-/intl-format-cache-4.3.1.tgz",
"integrity": "sha512-OEUYNA7D06agqPOYhbTkl0T8HA3QKSuwWh1HiClEnpd9vw7N+3XsQt5iZ0GUEchp5CW1fQk/tary+NsbF3yQ1Q=="
},
"node_modules/intl-messageformat": {
"version": "7.8.4",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-7.8.4.tgz",
"integrity": "sha512-yS0cLESCKCYjseCOGXuV4pxJm/buTfyCJ1nzQjryHmSehlptbZbn9fnlk1I9peLopZGGbjj46yHHiTAEZ1qOTA==",
"dependencies": {
"intl-format-cache": "^4.2.21",
"intl-messageformat-parser": "^3.6.4"
}
},
"node_modules/intl-messageformat-parser": {
"version": "3.6.4",
"resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-3.6.4.tgz",
"integrity": "sha512-RgPGwue0mJtoX2Ax8EmMzJzttxjnva7gx0Q7mKJ4oALrTZvtmCeAw5Msz2PcjW4dtCh/h7vN/8GJCxZO1uv+OA==",
"deprecated": "We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser",
"dependencies": {
"@formatjs/intl-unified-numberformat": "^3.2.0"
}
},
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -22286,16 +22424,19 @@
}
},
"node_modules/react-popper/node_modules/deep-equal": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
"integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz",
"integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==",
"dependencies": {
"is-arguments": "^1.0.4",
"is-date-object": "^1.0.1",
"is-regex": "^1.0.4",
"object-is": "^1.0.1",
"is-arguments": "^1.1.1",
"is-date-object": "^1.0.5",
"is-regex": "^1.1.4",
"object-is": "^1.1.5",
"object-keys": "^1.1.1",
"regexp.prototype.flags": "^1.2.0"
"regexp.prototype.flags": "^1.5.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -22596,13 +22737,14 @@
"dev": true
},
"node_modules/regexp.prototype.flags": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz",
"integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==",
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
"integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.2.0",
"functions-have-names": "^1.2.3"
"call-bind": "^1.0.6",
"define-properties": "^1.2.1",
"es-errors": "^1.3.0",
"set-function-name": "^2.0.1"
},
"engines": {
"node": ">= 0.4"
@@ -24249,6 +24391,36 @@
"node": ">= 0.8.0"
}
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/set-function-name": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
"integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"functions-have-names": "^1.2.3",
"has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/set-value": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
@@ -30368,14 +30540,14 @@
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
},
"@emotion/react": {
"version": "11.11.1",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz",
"integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==",
"version": "11.11.4",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz",
"integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==",
"requires": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.11.0",
"@emotion/cache": "^11.11.0",
"@emotion/serialize": "^1.1.2",
"@emotion/serialize": "^1.1.3",
"@emotion/use-insertion-effect-with-fallbacks": "^1.0.1",
"@emotion/utils": "^1.2.1",
"@emotion/weak-memoize": "^0.3.1",
@@ -30383,9 +30555,9 @@
}
},
"@emotion/serialize": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz",
"integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==",
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz",
"integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==",
"requires": {
"@emotion/hash": "^0.9.1",
"@emotion/memoize": "^0.8.1",
@@ -30499,26 +30671,39 @@
"dev": true
},
"@floating-ui/core": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.4.1.tgz",
"integrity": "sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==",
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.3.tgz",
"integrity": "sha512-1ZpCvYf788/ZXOhRQGFxnYQOVgeU+pi0i+d0Ow34La7qjIXETi6RNswGVKkA6KcDO8/+Ysu2E/CeUmmeEBDvTg==",
"requires": {
"@floating-ui/utils": "^0.1.1"
"@floating-ui/utils": "^0.2.3"
}
},
"@floating-ui/dom": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.1.tgz",
"integrity": "sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw==",
"version": "1.6.6",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.6.tgz",
"integrity": "sha512-qiTYajAnh3P+38kECeffMSQgbvXty2VB6rS+42iWR4FPIlZjLK84E9qtLnMTLIpPz2znD/TaFqaiavMUrS+Hcw==",
"requires": {
"@floating-ui/core": "^1.4.1",
"@floating-ui/utils": "^0.1.1"
"@floating-ui/core": "^1.0.0",
"@floating-ui/utils": "^0.2.3"
}
},
"@floating-ui/utils": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.1.tgz",
"integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw=="
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.3.tgz",
"integrity": "sha512-XGndio0l5/Gvd6CLIABvsav9HHezgDFFhDfHk1bvLfr9ni8dojqLSvBbotJEjmIwNHL7vK4QzBJTdBRoB+c1ww=="
},
"@formatjs/intl-unified-numberformat": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/@formatjs/intl-unified-numberformat/-/intl-unified-numberformat-3.3.7.tgz",
"integrity": "sha512-KnWgLRHzCAgT9eyt3OS34RHoyD7dPDYhRcuKn+/6Kv2knDF8Im43J6vlSW6Hm1w63fNq3ZIT1cFk7RuVO3Psag==",
"requires": {
"@formatjs/intl-utils": "^2.3.0"
}
},
"@formatjs/intl-utils": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@formatjs/intl-utils/-/intl-utils-2.3.0.tgz",
"integrity": "sha512-KWk80UPIzPmUg+P0rKh6TqspRw0G6eux1PuJr+zz47ftMaZ9QDwbGzHZbtzWkl5hgayM/qrKRutllRC7D/vVXQ=="
},
"@gatsbyjs/reach-router": {
"version": "1.3.9",
@@ -31934,6 +32119,48 @@
}
}
},
"@seafile/sf-metadata-ui-component": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/@seafile/sf-metadata-ui-component/-/sf-metadata-ui-component-0.0.4.tgz",
"integrity": "sha512-Zf8SPVXkG7iQFqaDOtgsrcIVn5l0rfX7c8h4E9yf+Ho6Y/S4Rq3vL/emLUR4syE18OlWlw656oNhsvapeWsQjQ==",
"requires": {
"@seafile/seafile-calendar": "0.0.24",
"classnames": "2.3.2",
"dayjs": "1.10.7",
"escape-html": "^1.0.3",
"glamor": "^2.20.40",
"intl-messageformat": "^7.8.4",
"invariant": "^2.2.2",
"is-hotkey": "0.2.0",
"prop-types": "^15.8.1",
"react-app-polyfill": "^3.0.0",
"react-responsive": "9.0.2",
"react-select": "5.7.0",
"react-transition-group": "^4.4.1",
"reactstrap": "8.9.0"
},
"dependencies": {
"@seafile/seafile-calendar": {
"version": "0.0.24",
"resolved": "https://registry.npmjs.org/@seafile/seafile-calendar/-/seafile-calendar-0.0.24.tgz",
"integrity": "sha512-q1efVDcHAxJ2foMgsR8mQPD6Fbd6ISu2WHRM82P7tO0KPiQNS5pz9V0YVCblgi7da085jaog2iAplJM+vH7xLQ==",
"requires": {
"babel-runtime": "6.x",
"classnames": "2.x",
"dayjs": "1.10.7",
"prop-types": "^15.5.8",
"rc-trigger": "^2.2.0",
"rc-util": "^4.1.1",
"react-lifecycles-compat": "^3.0.4"
}
},
"dayjs": {
"version": "1.10.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz",
"integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig=="
}
}
},
"@seafile/slate": {
"version": "0.91.8",
"resolved": "https://registry.npmjs.org/@seafile/slate/-/slate-0.91.8.tgz",
@@ -32733,9 +32960,9 @@
"dev": true
},
"@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
"version": "15.7.12",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
},
"@types/q": {
"version": "1.5.6",
@@ -32756,19 +32983,18 @@
"dev": true
},
"@types/react": {
"version": "18.2.21",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz",
"integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==",
"version": "18.3.3",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz",
"integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==",
"requires": {
"@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2"
}
},
"@types/react-transition-group": {
"version": "4.4.6",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz",
"integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==",
"version": "4.4.10",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz",
"integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==",
"requires": {
"@types/react": "*"
}
@@ -32788,11 +33014,6 @@
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
"dev": true
},
"@types/scheduler": {
"version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
},
"@types/semver": {
"version": "7.5.1",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz",
@@ -34518,12 +34739,15 @@
}
},
"call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"requires": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.1"
}
},
"callsites": {
@@ -35666,6 +35890,16 @@
"execa": "^5.0.0"
}
},
"define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"requires": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
}
},
"define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
@@ -35673,10 +35907,11 @@
"dev": true
},
"define-properties": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
"integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
"integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
"requires": {
"define-data-property": "^1.0.1",
"has-property-descriptors": "^1.0.0",
"object-keys": "^1.1.1"
}
@@ -36281,6 +36516,19 @@
"integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
"dev": true
},
"es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"requires": {
"get-intrinsic": "^1.2.4"
}
},
"es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="
},
"es-get-iterator": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
@@ -36372,8 +36620,7 @@
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"dev": true
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
},
"escape-string-regexp": {
"version": "1.0.5",
@@ -37717,9 +37964,9 @@
"optional": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
},
"function.prototype.name": {
"version": "1.1.6",
@@ -37751,14 +37998,15 @@
"dev": true
},
"get-intrinsic": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"requires": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3"
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
}
},
"get-own-enumerable-property-symbols": {
@@ -37907,7 +38155,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dev": true,
"requires": {
"get-intrinsic": "^1.1.3"
}
@@ -38005,11 +38252,11 @@
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
},
"has-property-descriptors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
"integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"requires": {
"get-intrinsic": "^1.1.1"
"es-define-property": "^1.0.0"
}
},
"has-proto": {
@@ -38116,6 +38363,14 @@
"minimalistic-assert": "^1.0.1"
}
},
"hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"requires": {
"function-bind": "^1.1.2"
}
},
"hast-util-embedded": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz",
@@ -38907,6 +39162,28 @@
"side-channel": "^1.0.4"
}
},
"intl-format-cache": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/intl-format-cache/-/intl-format-cache-4.3.1.tgz",
"integrity": "sha512-OEUYNA7D06agqPOYhbTkl0T8HA3QKSuwWh1HiClEnpd9vw7N+3XsQt5iZ0GUEchp5CW1fQk/tary+NsbF3yQ1Q=="
},
"intl-messageformat": {
"version": "7.8.4",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-7.8.4.tgz",
"integrity": "sha512-yS0cLESCKCYjseCOGXuV4pxJm/buTfyCJ1nzQjryHmSehlptbZbn9fnlk1I9peLopZGGbjj46yHHiTAEZ1qOTA==",
"requires": {
"intl-format-cache": "^4.2.21",
"intl-messageformat-parser": "^3.6.4"
}
},
"intl-messageformat-parser": {
"version": "3.6.4",
"resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-3.6.4.tgz",
"integrity": "sha512-RgPGwue0mJtoX2Ax8EmMzJzttxjnva7gx0Q7mKJ4oALrTZvtmCeAw5Msz2PcjW4dtCh/h7vN/8GJCxZO1uv+OA==",
"requires": {
"@formatjs/intl-unified-numberformat": "^3.2.0"
}
},
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -44937,16 +45214,16 @@
},
"dependencies": {
"deep-equal": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
"integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz",
"integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==",
"requires": {
"is-arguments": "^1.0.4",
"is-date-object": "^1.0.1",
"is-regex": "^1.0.4",
"object-is": "^1.0.1",
"is-arguments": "^1.1.1",
"is-date-object": "^1.0.5",
"is-regex": "^1.1.4",
"object-is": "^1.1.5",
"object-keys": "^1.1.1",
"regexp.prototype.flags": "^1.2.0"
"regexp.prototype.flags": "^1.5.1"
}
}
}
@@ -45192,13 +45469,14 @@
"dev": true
},
"regexp.prototype.flags": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz",
"integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==",
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
"integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==",
"requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.2.0",
"functions-have-names": "^1.2.3"
"call-bind": "^1.0.6",
"define-properties": "^1.2.1",
"es-errors": "^1.3.0",
"set-function-name": "^2.0.1"
}
},
"regexpu-core": {
@@ -46451,6 +46729,30 @@
"send": "0.18.0"
}
},
"set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"requires": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2"
}
},
"set-function-name": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
"integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
"requires": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"functions-have-names": "^1.2.3",
"has-property-descriptors": "^1.0.2"
}
},
"set-value": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",

View File

@@ -12,6 +12,7 @@
"@seafile/sdoc-editor": "1.0.4",
"@seafile/seafile-calendar": "0.0.12",
"@seafile/seafile-editor": "1.0.99",
"@seafile/sf-metadata-ui-component": "0.0.4",
"@uiw/codemirror-extensions-langs": "^4.19.4",
"@uiw/react-codemirror": "^4.19.4",
"chart.js": "2.9.4",

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718879587705" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18226" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M448 448V64h128v384h384v128H576v384h-128V576H64v-128h384z" p-id="18227"></path></svg>

After

Width:  |  Height:  |  Size: 419 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718873383615" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17658" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M992 512c0 266.336-213.664 480-480 480S32 778.336 32 512 245.664 32 512 32s480 213.664 480 480z m-535.744 253.92l356.16-356.128a29.92 29.92 0 0 0 0-43.328l-43.392-43.36a29.92 29.92 0 0 0-43.36 0l-291.072 291.104-136.256-136.288a29.92 29.92 0 0 0-43.36 0L211.616 521.28a29.92 29.92 0 0 0 0 43.36l201.28 201.28a29.92 29.92 0 0 0 43.36 0z" p-id="17659"></path></svg>

After

Width:  |  Height:  |  Size: 697 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718937171668" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18368" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M489.6 444.8l294.4-294.4 67.2 67.2-294.4 294.4 294.4 294.4-67.2 67.2-294.4-294.4-272 272-67.2-67.2 272-272-272-272 67.2-67.2z" p-id="18369"></path></svg>

After

Width:  |  Height:  |  Size: 487 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718865369801" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16662" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M681.6 480c-9.6 0-19.2 6.4-19.2 16-44.8 16-83.2 41.6-115.2 76.8v-70.4c0-12.8-9.6-22.4-22.4-22.4h-70.4c-12.8 0-22.4 9.6-22.4 22.4v70.4c0 12.8 9.6 22.4 22.4 22.4H531.2c-25.6 32-41.6 70.4-48 112h-28.8c-12.8 0-22.4 9.6-22.4 22.4V800c0 12.8 9.6 22.4 22.4 22.4h32c9.6 41.6 28.8 80 57.6 112L124.8 928c-54.4 0-83.2-38.4-83.2-83.2V368h896v182.4c-48-44.8-112-70.4-182.4-70.4h-73.6zM300.8 704H230.4c-12.8 0-22.4 9.6-22.4 22.4v70.4c0 12.8 9.6 22.4 22.4 22.4h70.4c12.8 0 22.4-9.6 22.4-22.4v-70.4c0-12.8-9.6-22.4-22.4-22.4z m0-224H230.4c-12.8 0-22.4 9.6-22.4 22.4v70.4c0 12.8 9.6 22.4 22.4 22.4h70.4c12.8 0 22.4-9.6 22.4-22.4v-70.4c0-12.8-9.6-22.4-22.4-22.4z m48-448c16 0 28.8 12.8 28.8 28.8v83.2h227.2V60.8c0-16 12.8-28.8 28.8-28.8h57.6c16 0 28.8 12.8 28.8 28.8v83.2h140.8c48 0 80 38.4 80 83.2v83.2h-896V227.2c0-44.8 38.4-83.2 83.2-83.2h140.8V60.8c-3.2-16 9.6-28.8 25.6-28.8h54.4z" p-id="16663"></path><path d="M758.4 979.2c-60.8 0-118.4-22.4-156.8-64-41.6-41.6-64-99.2-64-156.8-3.2-124.8 99.2-227.2 224-227.2s224 99.2 224 224-105.6 224-227.2 224z m86.4-195.2c12.8 0 25.6-9.6 25.6-25.6s-9.6-25.6-25.6-25.6h-73.6v-118.4c0-12.8-9.6-25.6-25.6-25.6s-25.6 9.6-25.6 25.6v144c0 12.8 9.6 25.6 25.6 25.6h99.2z" fill="#949494" p-id="16664"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718865297246" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16378" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M691.2 531.2c-54.4 35.2-118.4 51.2-179.2 51.2-64 0-128-19.2-179.2-51.2C192 598.4 96 704 96 860.8c0 169.6 835.2 166.4 835.2 0-3.2-156.8-102.4-262.4-240-329.6zM336 438.4c25.6 22.4 51.2 38.4 80 51.2 60.8 25.6 128 25.6 188.8 0 32-12.8 57.6-28.8 80-51.2 25.6-22.4 41.6-48 54.4-80 12.8-28.8 19.2-60.8 19.2-89.6 0-64-28.8-124.8-73.6-169.6-22.4-22.4-48-38.4-80-51.2-60.8-25.6-128-25.6-188.8 0-32 12.8-57.6 28.8-80 51.2-25.6 22.4-44.8 51.2-57.6 80-9.6 32-16 60.8-16 92.8s6.4 64 19.2 89.6c9.6 28.8 28.8 57.6 54.4 76.8z" p-id="16379"></path></svg>

After

Width:  |  Height:  |  Size: 870 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718865209281" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15526" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M566.4 806.4l374.4-470.4c44.8-54.4 9.6-144-54.4-144H137.6c-64 0-99.2 86.4-54.4 144l374.4 470.4c28.8 35.2 80 35.2 108.8 0z" p-id="15527"></path></svg>

After

Width:  |  Height:  |  Size: 483 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718873403625" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17942" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 32c87.584 0 168.16 21.024 241.76 63.04a467.136 467.136 0 0 1 175.168 175.2C970.976 343.84 992 424.416 992 512s-21.024 168.16-63.04 241.76a467.136 467.136 0 0 1-175.2 175.168C680.16 970.976 599.584 992 512 992s-168.16-21.024-241.76-63.04a467.136 467.136 0 0 1-175.168-175.2C53.024 680.16 32 599.584 32 512s21.024-168.16 63.04-241.76a467.136 467.136 0 0 1 175.2-175.168C343.84 53.024 424.416 32 512 32z m80.576 781.312v-119.136c0-7.008-3.52-10.496-7.008-14.016-3.52-3.488-7.008-7.008-14.016-7.008H452.48c-7.04 0-10.528 3.52-14.016 7.04-3.52 3.488-7.04 10.496-7.04 13.984v119.136c0 7.008 3.52 10.528 7.04 14.016 3.52 3.52 10.496 7.008 14.016 7.008h119.104c7.04 0 10.528-3.52 14.016-7.008 3.52-3.52 7.04-10.496 7.04-14.016z m-3.52-217.216l10.56-388.928c0-3.488-3.52-7.008-7.04-10.496-3.52-3.52-10.496-3.52-14.016-3.52h-136.64c-7.008 0-10.496 0-14.016 3.52-3.52 3.52-7.008 7.008-7.008 10.496l10.528 388.928c0 3.52 3.52 7.008 7.008 10.496 3.52 3.52 10.496 3.52 14.016 3.52h115.616c7.008 0 10.496 0 14.016-3.52 7.008-3.52 7.008-7.008 7.008-10.496z" p-id="17943"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718873396854" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17800" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M579.168 800v-102.016c0-5.984-3.008-8.96-6.016-12-3.008-2.976-5.984-5.984-12-5.984H459.2c-6.016 0-9.024 3.008-12 6.016-3.008 2.976-6.016 8.96-6.016 12V800c0 6.016 3.008 8.992 6.016 12 2.976 3.008 5.984 6.016 12 6.016h101.984c6.016 0 8.992-3.008 12-6.016 3.008-3.008 6.016-8.992 6.016-12z m-3.008-200.992l8.992-246.016c0-3.008-3.008-8.992-5.984-8.992-6.016-3.008-9.024-6.016-12-6.016h-117.024c-2.976 0-8.96 3.008-12 6.016-2.976 3.008-5.984 6.016-5.984 12l8.992 246.016c0 2.976 3.008 5.984 6.016 8.96 2.976 3.04 8.96 3.04 12 3.04h98.976c6.016 0 9.024 0 12-3.008 3.008-3.008 6.016-9.024 6.016-12z m-6.016-501.024l411.008 753.024c12 24 12 44.992 0 65.984a91.296 91.296 0 0 1-24 24c-12 6.016-20.992 9.024-32.992 9.024H99.168c-12 0-24-3.008-33.024-9.024a91.296 91.296 0 0 1-24-24c-12-24-14.976-44.992 0-65.984l408-753.024c6.016-11.968 15.04-20.992 24-26.976 12-6.016 24-8.992 36-8.992s24 2.976 36 8.96c12 6.016 18.016 15.04 24 27.04z" p-id="17801"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718866549156" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16805" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M985.6 704h-236.8c-22.4 0-38.4-32-38.4-70.4 0-38.4 19.2-70.4 38.4-70.4h236.8c22.4 0 38.4 32 38.4 70.4 0 38.4-19.2 70.4-38.4 70.4z m0-230.4h-236.8c-22.4 0-38.4-32-38.4-70.4s19.2-70.4 38.4-70.4h236.8c22.4 0 38.4 32 38.4 70.4s-19.2 70.4-38.4 70.4z m-211.2-243.2L556.8 480v348.8L384 944c-38.4 22.4-86.4 9.6-108.8-28.8-6.4-9.6-9.6-25.6-9.6-38.4V476.8L22.4 230.4c-28.8-32-28.8-83.2 3.2-115.2 12.8-12.8 35.2-19.2 51.2-19.2h636.8c41.6 0 76.8 35.2 76.8 80 6.4 19.2 0 38.4-16 54.4z m-409.6 192c9.6 9.6 12.8 22.4 12.8 32V800l57.6-38.4v-304c0-9.6 3.2-22.4 12.8-28.8l195.2-227.2H166.4l198.4 220.8z m320 374.4h288c25.6 0 48 32 48 70.4 0 38.4-22.4 70.4-48 70.4h-288c-25.6 0-48-32-48-70.4 0-38.4 22.4-70.4 48-70.4z" p-id="16806"></path></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718875410271" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18084" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M611.2 512l300.8-300.8c28.8-28.8 28.8-73.6 0-99.2s-73.6-28.8-99.2 0L512 412.8 211.2 112c-28.8-28.8-73.6-28.8-99.2 0s-28.8 73.6 0 99.2l300.8 300.8-300.8 300.8c-28.8 28.8-28.8 73.6 0 99.2 28.8 25.6 73.6 28.8 99.2 0l300.8-300.8 300.8 300.8c28.8 28.8 73.6 28.8 99.2 0 28.8-28.8 28.8-73.6 0-99.2L611.2 512z" p-id="18085"></path></svg>

After

Width:  |  Height:  |  Size: 663 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718866570524" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16947" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M98.272 34.272h832c35.2 0 64 28.8 64 64v832c0 35.2-28.8 64-64 64h-832c-35.2 0-64-28.8-64-64v-832c0-35.2 28.8-64 64-64z m128 128c-35.2 0-64 28.8-64 64v576c0 35.2 28.8 64 64 64h576c35.2 0 64-28.8 64-64v-576c0-35.2-28.8-64-64-64h-576z" p-id="16948"></path><path d="M312.672 293.472c44.8 0 80 35.2 80 80s-35.2 80-80 80-80-35.2-80-80 35.2-80 80-80zM312.672 562.272c44.8 0 80 35.2 80 80s-35.2 80-80 80-80-35.2-80-80 35.2-80 80-80zM549.472 293.472h160c44.8 0 80 35.2 80 80s-35.2 80-80 80h-160c-44.8 0-80-35.2-80-80s38.4-80 80-80zM549.472 562.272h160c44.8 0 80 35.2 80 80s-35.2 80-80 80h-160c-44.8 0-80-35.2-80-80s38.4-80 80-80z" fill="#949494" p-id="16949"></path></svg>

After

Width:  |  Height:  |  Size: 997 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718865285895" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16236" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M339.2 630.4c-12.8-28.8-22.4-60.8-22.4-92.8 0-118.4 92.8-211.2 211.2-211.2l83.2-137.6c-28.8-3.2-57.6-9.6-83.2-9.6C310.4 179.2 121.6 320 3.2 537.6 64 646.4 140.8 739.2 233.6 800l105.6-169.6zM774.4 256l-89.6 156.8c67.2 92.8 48 227.2-35.2 297.6-44.8 38.4-102.4 51.2-160 38.4l-76.8 131.2c35.2 9.6 70.4 12.8 112 12.8 204.8 0 384-140.8 496-358.4-64-118.4-147.2-217.6-246.4-278.4zM275.2 950.4c-22.4 0-41.6-19.2-41.6-41.6 0-9.6 0-12.8 3.2-22.4L704 140.8c12.8-19.2 41.6-28.8 60.8-12.8 19.2 12.8 28.8 41.6 12.8 60.8L320 928c-9.6 12.8-25.6 22.4-44.8 22.4z" p-id="16237"></path></svg>

After

Width:  |  Height:  |  Size: 907 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718865224104" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15668" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M595.2 764.8h-166.4v144c0 44.8-38.4 83.2-83.2 83.2s-83.2-38.4-83.2-83.2v-144H115.2c-44.8 0-83.2-38.4-83.2-83.2s38.4-83.2 83.2-83.2h144v-166.4H115.2C70.4 428.8 32 390.4 32 345.6s38.4-83.2 83.2-83.2h144V115.2C259.2 70.4 297.6 32 345.6 32s83.2 38.4 83.2 83.2v144h166.4V115.2c0-44.8 38.4-83.2 83.2-83.2s83.2 38.4 83.2 83.2v144h144c44.8 0 83.2 38.4 83.2 83.2s-38.4 83.2-83.2 83.2h-144v166.4h144c44.8 0 83.2 38.4 83.2 83.2s-38.4 83.2-83.2 83.2h-144v144c0 44.8-38.4 83.2-83.2 83.2s-83.2-38.4-83.2-83.2v-137.6z m0-169.6v-166.4h-166.4v166.4h166.4z" p-id="15669"></path></svg>

After

Width:  |  Height:  |  Size: 901 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718865250054" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15952" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M681.6 227.2h-64c-35.2 0-64-28.8-64-64V128c0-35.2 28.8-64 64-64H960v342.4c0 35.2-28.8 64-64 64h-35.2c-35.2 0-64-28.8-64-64v-64l-195.2 195.2-115.2-115.2 195.2-195.2zM342.4 796.8h64c35.2 0 64 28.8 64 64V896c0 35.2-28.8 64-64 64H64V617.6c0-35.2 28.8-64 64-64h35.2c35.2 0 64 28.8 64 64v64l195.2-195.2 115.2 115.2-195.2 195.2z" p-id="15953"></path></svg>

After

Width:  |  Height:  |  Size: 683 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718866629391" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17516" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M640 96H64c-19.2 0-32 12.8-32 32v160c0 19.2 12.8 32 32 32h576c19.2 0 32-12.8 32-32V128c0-19.2-12.8-32-32-32z m0 576H64c-19.2 0-32 12.8-32 32s12.8 32 32 32h576c19.2 0 32-12.8 32-32s-12.8-32-32-32z m0 192H64c-19.2 0-32 12.8-32 32s12.8 32 32 32h576c19.2 0 32-12.8 32-32s-12.8-32-32-32zM64 480c-19.2 0-32 12.8-32 32s12.8 32 32 32h576c19.2 0 32-12.8 32-32s-12.8-32-32-32H64zM864 57.6c-9.6 0-19.2 3.2-28.8 16L736 227.2v6.4c0 3.2 3.2 3.2 3.2 3.2h64c3.2 0 3.2 3.2 3.2 6.4v540.8c0 3.2-3.2 6.4-3.2 6.4h-64c-3.2 0-3.2 0-3.2 3.2v6.4l99.2 153.6 9.6 9.6c16 9.6 35.2 6.4 44.8-9.6l99.2-153.6v-6.4c0-3.2-3.2-3.2-3.2-3.2h-64c-3.2 0-3.2-3.2-3.2-6.4V243.2c0-3.2 3.2-6.4 3.2-6.4h64c3.2 0 3.2 0 3.2-3.2v-6.4l-99.2-153.6c-3.2-3.2-12.8-16-25.6-16z" p-id="17517"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718866625733" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17374" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M640 704H64c-19.2 0-32 12.8-32 32s12.8 32 32 32h576c19.2 0 32-12.8 32-32s-12.8-32-32-32zM64 864c-19.2 0-32 12.8-32 32s12.8 32 32 32h576c19.2 0 32-12.8 32-32s-12.8-32-32-32H64zM64 96c-19.2 0-32 12.8-32 32v416c0 19.2 12.8 32 32 32h576c19.2 0 32-12.8 32-32V128c0-19.2-12.8-32-32-32H64z m800-38.4c-9.6 0-19.2 3.2-28.8 16L736 227.2v6.4c0 3.2 3.2 3.2 3.2 3.2h64c3.2 0 3.2 3.2 3.2 6.4v540.8c0 3.2-3.2 6.4-3.2 6.4h-64c-3.2 0-3.2 0-3.2 3.2v6.4l99.2 153.6 9.6 9.6c16 9.6 35.2 6.4 44.8-9.6l99.2-153.6v-6.4c0-3.2-3.2-3.2-3.2-3.2h-64c-3.2 0-3.2-3.2-3.2-6.4V243.2c0-3.2 3.2-6.4 3.2-6.4h64c3.2 0 3.2 0 3.2-3.2v-6.4l-99.2-153.6c-3.2-3.2-12.8-16-25.6-16z" p-id="17375"></path></svg>

After

Width:  |  Height:  |  Size: 1000 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718866617030" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17090" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M880 64c-16-9.6-35.2-6.4-44.8 9.6L736 227.2v6.4c0 3.2 3.2 3.2 3.2 3.2h64c3.2 0 3.2 3.2 3.2 6.4v540.8c0 3.2-3.2 6.4-3.2 6.4h-64c-3.2 0-3.2 0-3.2 3.2v6.4l99.2 153.6 9.6 9.6c16 9.6 35.2 6.4 44.8-9.6l99.2-153.6v-6.4c0-3.2-3.2-3.2-3.2-3.2h-64c-3.2 0-3.2-3.2-3.2-6.4V243.2c0-3.2 3.2-6.4 3.2-6.4h64c3.2 0 3.2 0 3.2-3.2v-6.4l-99.2-153.6c0-3.2-3.2-6.4-9.6-9.6zM64 96c-19.2 0-32 12.8-32 32v768c0 19.2 12.8 32 32 32h576c19.2 0 32-12.8 32-32V128c0-19.2-12.8-32-32-32H64z" p-id="17091"></path></svg>

After

Width:  |  Height:  |  Size: 821 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718866621705" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17232" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M880 64c-16-9.6-35.2-6.4-44.8 9.6L736 227.2v6.4c0 3.2 3.2 3.2 3.2 3.2h64c3.2 0 3.2 3.2 3.2 6.4v540.8c0 3.2-3.2 6.4-3.2 6.4h-64c-3.2 0-3.2 0-3.2 3.2v6.4l99.2 153.6 9.6 9.6c16 9.6 35.2 6.4 44.8-9.6l99.2-153.6v-6.4c0-3.2-3.2-3.2-3.2-3.2h-64c-3.2 0-3.2-3.2-3.2-6.4V243.2c0-3.2 3.2-6.4 3.2-6.4h64c3.2 0 3.2 0 3.2-3.2v-6.4l-99.2-153.6c0-3.2-3.2-6.4-9.6-9.6zM64 864c-19.2 0-32 12.8-32 32s12.8 32 32 32h576c19.2 0 32-12.8 32-32s-12.8-32-32-32H64zM64 96c-19.2 0-32 12.8-32 32v576c0 19.2 12.8 32 32 32h576c19.2 0 32-12.8 32-32V128c0-19.2-12.8-32-32-32H64z" p-id="17233"></path></svg>

After

Width:  |  Height:  |  Size: 908 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718865263224" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16094" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M812.8 483.2v428.8c0 25.6-22.4 48-48 48h-83.2c-25.6 0-48-22.4-48-48V483.2h-118.4C608 243.2 678.4 124.8 723.2 124.8s115.2 118.4 208 358.4h-118.4z m-419.2 57.6H512c-89.6 240-160 358.4-208 358.4S185.6 780.8 96 540.8h118.4V112c0-25.6 22.4-48 48-48h83.2c25.6 0 48 22.4 48 48v428.8z" p-id="16095"></path></svg>

After

Width:  |  Height:  |  Size: 639 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718865237398" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15810" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M316.8 768l-80 172.8c-19.2 41.6-67.2 60.8-112 41.6-41.6-16-51.2-67.2-35.2-108.8v-3.2L438.4 105.6c19.2-41.6 60.8-60.8 105.6-44.8 25.6 9.6 41.6 35.2 54.4 60.8l320 755.2c16 41.6-3.2 86.4-41.6 102.4h-3.2c-41.6 16-89.6-3.2-108.8-44.8L691.2 768H316.8z m67.2-150.4h243.2L512 342.4l-128 275.2z" fill="#979797" p-id="15811"></path></svg>

After

Width:  |  Height:  |  Size: 662 B

View File

@@ -7,6 +7,7 @@ import { Utils } from '../../utils/utils';
import { InternalLinkOperation } from '../operations';
import DirOperationToolBar from '../../components/toolbar/dir-operation-toolbar';
import ViewFileToolbar from '../../components/toolbar/view-file-toolbar';
import { PRIVATE_FILE_TYPE } from '../../constants';
const propTypes = {
repoID: PropTypes.string.isRequired,
@@ -62,6 +63,15 @@ class DirPath extends React.Component {
return null;
}
if (index === (pathList.length - 1)) {
if (item === PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES) {
return (
<Fragment key={index}>
<span className="path-split">/</span>
<span className="path-item">{gettext('File extended properties')}</span>
</Fragment>
);
}
return (
<Fragment key={index}>
<span className="path-split">/</span>
@@ -115,6 +125,13 @@ class DirPath extends React.Component {
return pathElem;
};
isViewMetadata = () => {
const { currentPath } = this.props;
const path = currentPath[currentPath.length - 1] === '/' ? currentPath.slice(0, currentPath.length - 1) : currentPath;
const pathList = path.split('/');
return pathList[pathList.length - 1] === PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES;
};
render() {
let { currentPath, repoName, fileTags } = this.props;
let pathElem = this.turnPathToLink(currentPath);
@@ -172,7 +189,7 @@ class DirPath extends React.Component {
<span className="path-item" data-path="/" onClick={this.onPathClick} role="button">{repoName}</span>
}
{pathElem}
{this.props.isViewFile && (
{this.props.isViewFile && !this.isViewMetadata() && (
<InternalLinkOperation repoID={this.props.repoID} path={this.props.currentPath}/>
)}
{(this.props.isViewFile && fileTags.length !== 0) &&

View File

@@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { SeafileMetadata } from '../../metadata';
import { Utils } from '../../utils/utils';
import { gettext, siteRoot } from '../../utils/constants';
import { gettext, siteRoot, lang, mediaUrl } from '../../utils/constants';
import SeafileMarkdownViewer from '../seafile-markdown-viewer';
const propTypes = {
@@ -48,6 +49,22 @@ class DirColumnFile extends React.Component {
<div className="message err-tip">{gettext('File does not exist.')}</div>
);
}
if (this.props.content === '__sf-metadata') {
window.sfMetadata = {
siteRoot,
lang,
mediaUrl,
};
return (
<div className="w-100 h-100 o-hidden d-flex" style={{ paddingRight: 10, flexDirection: 'column', alignItems: 'center' }}>
<div className="" style={{ width: '100%', height: 10, zIndex: 7, transform: 'translateZ(1000px)', position: 'relative', background: '#fff' }}></div>
<SeafileMetadata repoID={this.props.repoID} />
</div>
);
}
return (
<SeafileMarkdownViewer
isTOCShow={false}

View File

@@ -63,7 +63,7 @@ class DirColumnNav extends React.Component {
onNodeClick = (node) => {
this.setState({opNode: node});
if (Utils.imageCheck(node.object.name)) {
if (Utils.imageCheck(node?.object?.name || '')) {
this.showNodeImagePopup(node);
return;
}
@@ -268,7 +268,7 @@ class DirColumnNav extends React.Component {
repoID={this.props.repoID}
/>
</TreeSection>
<DirViews repoID={this.props.repoID} userPerm={this.props.userPerm} />
<DirViews repoID={this.props.repoID} currentPath={this.props.currentPath} userPerm={this.props.userPerm} onNodeClick={this.onNodeClick}/>
</>
);
};

View File

@@ -3,14 +3,13 @@ import PropTypes from 'prop-types';
import { gettext } from '../../../utils/constants';
import { Utils } from '../../../utils/utils';
import TreeSection from '../../tree-section';
import MetadataStatusManagementDialog from '../../metadata-manage/metadata-status-manage-dialog';
import metadataManagerAPI from '../../metadata-manage/api';
import { MetadataStatusManagementDialog, MetadataTreeView } from '../../../metadata';
import metadataAPI from '../../../metadata/api';
import toaster from '../../toast';
import MetadataViews from '../../metadata-manage/metadata-views';
import './index.css';
const DirViews = ({ userPerm, repoID }) => {
const DirViews = ({ userPerm, repoID, currentPath, onNodeClick }) => {
const enableMetadataManagement = useMemo(() => {
return window.app.pageOptions.enableMetadataManagement;
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -33,7 +32,7 @@ const DirViews = ({ userPerm, repoID }) => {
return;
}
const repoMetadataManagementEnabledStatusRes = metadataManagerAPI.getRepoMetadataManagementEnabledStatus(repoID);
const repoMetadataManagementEnabledStatusRes = metadataAPI.getMetadataStatus(repoID);
Promise.all([repoMetadataManagementEnabledStatusRes]).then(results => {
const [repoMetadataManagementEnabledStatusRes] = results;
setMetadataStatus(repoMetadataManagementEnabledStatusRes.data.enabled);
@@ -65,7 +64,7 @@ const DirViews = ({ userPerm, repoID }) => {
return (
<>
<TreeSection title={gettext('Views')} moreKey={{ name: 'views' }} moreOperations={moreOperations} moreOperationClick={moreOperationClick}>
{!loading && metadataStatus && (<MetadataViews repoID={repoID} />)}
{!loading && metadataStatus && (<MetadataTreeView repoID={repoID} currentPath={currentPath} onNodeClick={onNodeClick} />)}
</TreeSection>
{showMetadataStatusManagementDialog && (
<MetadataStatusManagementDialog value={metadataStatus} repoID={repoID} toggle={closeMetadataManagementDialog} submit={toggleMetadataStatus} />
@@ -77,6 +76,8 @@ const DirViews = ({ userPerm, repoID }) => {
DirViews.propTypes = {
userPerm: PropTypes.string,
repoID: PropTypes.string,
currentPath: PropTypes.string,
onNodeClick: PropTypes.func,
};
export default DirViews;

View File

@@ -1,58 +0,0 @@
import React, { useCallback, useState } from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../../utils/constants';
import { siteRoot } from '../../../utils/constants';
import Icon from '../../icon';
import './index.css';
const MetadataViews = ({ repoID }) => {
const [highlight, setHighlight] = useState(false);
const onMouseEnter = useCallback(() => {
setHighlight(true);
}, []);
const onMouseOver = useCallback(() => {
setHighlight(true);
}, []);
const onMouseLeave = useCallback(() => {
setHighlight(false);
}, []);
const openView = useCallback(() => {
const server = siteRoot.substring(0, siteRoot.length-1);
window.open(server + '/repos/' + repoID + '/metadata/table-view/', '_blank');
}, [repoID]);
return (
<div className="tree-view tree metadata-tree-view">
<div className="tree-node">
<div className="children" style={{ paddingLeft: 20 }}>
<div
className={`tree-node-inner text-nowrap${highlight ? ' tree-node-inner-hover' : ''}`}
title={gettext('File extended properties')}
onMouseEnter={onMouseEnter}
onMouseOver={onMouseOver}
onMouseLeave={onMouseLeave}
onClick={openView}
>
<div className="tree-node-text">{gettext('File extended properties')}</div>
<div className="left-icon">
<div className="tree-node-icon">
<Icon symbol="table" className="metadata-views-icon" />
</div>
</div>
</div>
</div>
</div>
</div>
);
};
MetadataViews.propTypes = {
repoID: PropTypes.string.isRequired,
};
export default MetadataViews;

View File

@@ -106,6 +106,10 @@ export const DURATION_DECIMAL_DIGITS = {
[DURATION_FORMATS_MAP.H_MM_SS_SSS]: 3,
};
export const PRIVATE_FILE_TYPE = {
FILE_EXTENDED_PROPERTIES: '__file_extended_properties'
};
const TAG_COLORS = ['#FBD44A', '#EAA775', '#F4667C', '#DC82D2', '#9860E5', '#9F8CF1', '#59CB74', '#ADDF84',
'#89D2EA', '#4ECCCB', '#46A1FD', '#C2C2C2'];

View File

@@ -11,6 +11,7 @@
width: 100%;
height: calc(100% - 48px);
border-top: 1px solid #e8e8e8;
transform: translateZ(10px);
}
.view-mode-container {

View File

@@ -1,6 +1,6 @@
import axios from 'axios';
import cookie from 'react-cookies';
import { siteRoot } from '../../utils/constants';
import { siteRoot } from '../utils/constants';
class MetadataManagerAPI {
init({ server, username, password, token }) {
@@ -43,22 +43,22 @@ class MetadataManagerAPI {
}
}
getRepoMetadataManagementEnabledStatus(repoID) {
getMetadataStatus(repoID) {
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/';
return this.req.get(url);
}
openRepoMetadataManagement(repoID) {
createMetadata(repoID) {
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/';
return this.req.put(url);
}
closeRepoMetadataManagement(repoID) {
deleteMetadata(repoID) {
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/';
return this.req.delete(url);
}
getMetadataRecords(repoID, params) {
getMetadata(repoID, params) {
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/records/';
return this.req.get(url, {params: params});
}
@@ -72,7 +72,7 @@ class MetadataManagerAPI {
return this.req.post(url, data);
}
updateMetadataRecord(repoID, recordID, creator, createTime, modifier, modifyTime, parentDir, name) {
updateMetadataRecord = (repoID, recordID, creator, createTime, modifier, modifyTime, parentDir, name) => {
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/records/' + recordID + '/';
const data = {
'creator': creator,
@@ -83,16 +83,23 @@ class MetadataManagerAPI {
'name': name,
};
return this.req.put(url, data);
}
};
deleteMetadataRecord(repoID, recordID) {
deleteMetadataRecord = (repoID, recordID) => {
const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/records/' + recordID + '/';
return this.req.delete(url);
}
};
listUserInfo = (userIds) => {
const url = this.server + '/api/v2.1/user-list/';
const params = { user_id_list: userIds };
return this._sendPostRequest(url, params, { headers: { 'Content-type': 'application/json' } });
};
}
const metadataManagerAPI = new MetadataManagerAPI();
const metadataAPI = new MetadataManagerAPI();
const xcsrfHeaders = cookie.load('sfcsrftoken');
metadataManagerAPI.initForSeahubUsage({ siteRoot, xcsrfHeaders });
metadataAPI.initForSeahubUsage({ siteRoot, xcsrfHeaders });
export default metadataManagerAPI;
export default metadataAPI;

View File

@@ -0,0 +1,9 @@
import SeafileMetadata from './metadata-view';
import MetadataStatusManagementDialog from './metadata-status-manage-dialog';
import MetadataTreeView from './metadata-tree-view';
export {
SeafileMetadata,
MetadataStatusManagementDialog,
MetadataTreeView,
};

View File

@@ -1,11 +1,11 @@
import React, { useCallback, useState } from 'react';
import PropTypes from 'prop-types';
import { Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap';
import { gettext } from '../../../utils/constants';
import Switch from '../../common/switch';
import metadataManagerAPI from '../api';
import { Utils } from '../../../utils/utils';
import toaster from '../../toast';
import { gettext } from '../../utils/constants';
import Switch from '../../components/common/switch';
import metadataAPI from '../api';
import { Utils } from '../../utils/utils';
import toaster from '../../components/toast';
import './index.css';
@@ -20,8 +20,8 @@ const MetadataStatusManagementDialog = ({ value: oldValue, repoID, toggle, submi
const onSubmit = useCallback(() => {
setSubmitting(true);
const apiName = value ? 'openRepoMetadataManagement' : 'closeRepoMetadataManagement';
metadataManagerAPI[apiName](repoID).then(res => {
const apiName = value ? 'createMetadata' : 'deleteMetadata';
metadataAPI[apiName](repoID).then(res => {
submit(value);
toggle();
}).catch(error => {

View File

@@ -0,0 +1,74 @@
import React, { useCallback, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { gettext } from '../../utils/constants';
import Icon from '../../components/icon';
import { PRIVATE_FILE_TYPE } from '../../constants';
import './index.css';
const MetadataTreeView = ({ repoID, currentPath, onNodeClick }) => {
const node = useMemo(() => {
return {
children: [],
path: '/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES,
isExpanded: false,
isLoaded: true,
isPreload: true,
object: {
file_tags: [],
id: PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES,
name: gettext('File extended properties'),
type: PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES,
isDir: () => false,
},
parentNode: {},
key: repoID,
};
}, [repoID]);
const [highlight, setHighlight] = useState(false);
const onMouseEnter = useCallback(() => {
setHighlight(true);
}, []);
const onMouseOver = useCallback(() => {
setHighlight(true);
}, []);
const onMouseLeave = useCallback(() => {
setHighlight(false);
}, []);
return (
<div className="tree-view tree metadata-tree-view">
<div className="tree-node">
<div className="children" style={{ paddingLeft: 20 }}>
<div
className={classnames('tree-node-inner text-nowrap', { 'tree-node-inner-hover': highlight, 'tree-node-hight-light': currentPath === node.path })}
title={gettext('File extended properties')}
onMouseEnter={onMouseEnter}
onMouseOver={onMouseOver}
onMouseLeave={onMouseLeave}
onClick={() => onNodeClick(node)}
>
<div className="tree-node-text">{gettext('File extended properties')}</div>
<div className="left-icon">
<div className="tree-node-icon">
<Icon symbol="table" className="metadata-views-icon" />
</div>
</div>
</div>
</div>
</div>
</div>
);
};
MetadataTreeView.propTypes = {
repoID: PropTypes.string.isRequired,
currentPath: PropTypes.string,
onNodeClick: PropTypes.func,
};
export default MetadataTreeView;

View File

@@ -0,0 +1,82 @@
import CellType from './type';
const DATE_COLUMN_OPTIONS = [
CellType.CTIME, CellType.MTIME,
];
const NUMERIC_COLUMNS_TYPES = [
];
const COLLABORATOR_COLUMN_TYPES = [
CellType.CREATOR, CellType.LAST_MODIFIER,
];
// date
const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD';
const UTC_FORMAT_DEFAULT = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
const DATE_UNIT = {
YEAR: 'year',
MONTH: 'month',
WEEK: 'week',
DAY: 'day',
HOUR: 'hour',
HOURS: 'hours',
MINUTE: 'minute',
MINUTES: 'minutes',
SECOND: 'second',
};
const DATE_FORMAT_MAP = {
YYYY_MM_DD: 'YYYY-MM-DD',
YYYY_MM_DD_HH_MM: 'YYYY-MM-DD HH:mm',
YYYY_MM_DD_HH_MM_SS: 'YYYY-MM-DD HH:mm:ss',
};
// number
const DEFAULT_NUMBER_FORMAT = 'number';
const NOT_SUPPORT_EDIT_COLUMN_TYPE = [
CellType.CTIME,
CellType.MTIME,
CellType.CREATOR,
CellType.LAST_MODIFIER,
];
const NOT_SUPPORT_EDIT_COLUMN_TYPE_MAP = {
[CellType.CTIME]: true,
[CellType.MTIME]: true,
[CellType.CREATOR]: true,
[CellType.LAST_MODIFIER]: true,
};
const MULTIPLE_CELL_VALUE_COLUMN_TYPE_MAP = {
};
const SINGLE_CELL_VALUE_COLUMN_TYPE_MAP = {
[CellType.TEXT]: true,
[CellType.CTIME]: true,
[CellType.MTIME]: true,
[CellType.CREATOR]: true,
[CellType.LAST_MODIFIER]: true,
};
const DATE_DEFAULT_TYPES = {
SPECIFIC_DATE: 'specific_date',
CURRENT_DATE: 'current_date',
DAYS_BEFORE: 'days_before',
DAYS_AFTER: 'days_after',
};
export {
COLLABORATOR_COLUMN_TYPES,
DATE_COLUMN_OPTIONS,
NUMERIC_COLUMNS_TYPES,
DEFAULT_DATE_FORMAT,
UTC_FORMAT_DEFAULT,
DATE_UNIT,
DATE_FORMAT_MAP,
DEFAULT_NUMBER_FORMAT,
DATE_DEFAULT_TYPES,
NOT_SUPPORT_EDIT_COLUMN_TYPE,
NOT_SUPPORT_EDIT_COLUMN_TYPE_MAP,
MULTIPLE_CELL_VALUE_COLUMN_TYPE_MAP,
SINGLE_CELL_VALUE_COLUMN_TYPE_MAP,
};

View File

@@ -0,0 +1,24 @@
import CellType from './type';
const COLUMNS_ICON_CONFIG = {
[CellType.CREATOR]: 'creator',
[CellType.LAST_MODIFIER]: 'creator',
[CellType.CTIME]: 'creation-time',
[CellType.MTIME]: 'creation-time',
[CellType.DEFAULT]: 'text',
[CellType.TEXT]: 'text',
};
const COLUMNS_ICON_NAME = {
[CellType.CREATOR]: 'Creator',
[CellType.LAST_MODIFIER]: 'Last_modifier',
[CellType.CTIME]: 'CTime',
[CellType.MTIME]: 'Last_modified_time',
[CellType.DEFAULT]: 'Text',
[CellType.TEXT]: 'Text',
};
export {
COLUMNS_ICON_CONFIG,
COLUMNS_ICON_NAME,
};

View File

@@ -0,0 +1,27 @@
import CellType from './type';
import {
COLUMNS_ICON_CONFIG,
COLUMNS_ICON_NAME,
} from './icon';
export {
COLLABORATOR_COLUMN_TYPES,
DATE_COLUMN_OPTIONS,
NUMERIC_COLUMNS_TYPES,
DEFAULT_DATE_FORMAT,
UTC_FORMAT_DEFAULT,
DATE_UNIT,
DATE_FORMAT_MAP,
DEFAULT_NUMBER_FORMAT,
DATE_DEFAULT_TYPES,
NOT_SUPPORT_EDIT_COLUMN_TYPE,
NOT_SUPPORT_EDIT_COLUMN_TYPE_MAP,
MULTIPLE_CELL_VALUE_COLUMN_TYPE_MAP,
SINGLE_CELL_VALUE_COLUMN_TYPE_MAP,
} from './format';
export {
CellType,
COLUMNS_ICON_CONFIG,
COLUMNS_ICON_NAME,
};

View File

@@ -0,0 +1,10 @@
const CellType = {
DEFAULT: 'default',
TEXT: 'text',
CREATOR: 'creator',
CTIME: 'ctime',
LAST_MODIFIER: 'last-modifier',
MTIME: 'mtime',
};
export default CellType;

View File

@@ -0,0 +1,84 @@
import { CellType } from '../column';
import { FILTER_TERM_MODIFIER_TYPE } from './filter-modifier';
import { FILTER_PREDICATE_TYPE } from './filter-predicate';
const textPredicates = [
FILTER_PREDICATE_TYPE.CONTAINS,
FILTER_PREDICATE_TYPE.NOT_CONTAIN,
FILTER_PREDICATE_TYPE.IS,
FILTER_PREDICATE_TYPE.IS_NOT,
FILTER_PREDICATE_TYPE.EMPTY,
FILTER_PREDICATE_TYPE.NOT_EMPTY,
FILTER_PREDICATE_TYPE.IS_CURRENT_USER_ID,
];
const datePredicates = [
FILTER_PREDICATE_TYPE.IS,
FILTER_PREDICATE_TYPE.IS_WITHIN,
FILTER_PREDICATE_TYPE.IS_BEFORE,
FILTER_PREDICATE_TYPE.IS_AFTER,
FILTER_PREDICATE_TYPE.IS_ON_OR_BEFORE,
FILTER_PREDICATE_TYPE.IS_ON_OR_AFTER,
FILTER_PREDICATE_TYPE.IS_NOT,
FILTER_PREDICATE_TYPE.EMPTY,
FILTER_PREDICATE_TYPE.NOT_EMPTY,
];
const dateTermModifiers = [
FILTER_TERM_MODIFIER_TYPE.TODAY,
FILTER_TERM_MODIFIER_TYPE.TOMORROW,
FILTER_TERM_MODIFIER_TYPE.YESTERDAY,
FILTER_TERM_MODIFIER_TYPE.ONE_WEEK_AGO,
FILTER_TERM_MODIFIER_TYPE.ONE_WEEK_FROM_NOW,
FILTER_TERM_MODIFIER_TYPE.ONE_MONTH_AGO,
FILTER_TERM_MODIFIER_TYPE.ONE_MONTH_FROM_NOW,
FILTER_TERM_MODIFIER_TYPE.NUMBER_OF_DAYS_AGO,
FILTER_TERM_MODIFIER_TYPE.NUMBER_OF_DAYS_FROM_NOW,
FILTER_TERM_MODIFIER_TYPE.EXACT_DATE,
];
const FILTER_COLUMN_OPTIONS = {
[CellType.TEXT]: {
filterPredicateList: textPredicates,
},
[CellType.CTIME]: {
filterPredicateList: datePredicates,
filterTermModifierList: dateTermModifiers,
},
[CellType.MTIME]: {
filterPredicateList: datePredicates,
filterTermModifierList: dateTermModifiers,
},
[CellType.CREATOR]: {
filterPredicateList: [
FILTER_PREDICATE_TYPE.CONTAINS,
FILTER_PREDICATE_TYPE.NOT_CONTAIN,
FILTER_PREDICATE_TYPE.INCLUDE_ME,
FILTER_PREDICATE_TYPE.IS,
FILTER_PREDICATE_TYPE.IS_NOT,
],
},
[CellType.LAST_MODIFIER]: {
filterPredicateList: [
FILTER_PREDICATE_TYPE.CONTAINS,
FILTER_PREDICATE_TYPE.NOT_CONTAIN,
FILTER_PREDICATE_TYPE.INCLUDE_ME,
FILTER_PREDICATE_TYPE.IS,
FILTER_PREDICATE_TYPE.IS_NOT,
],
},
[CellType.URL]: {
filterPredicateList: [
FILTER_PREDICATE_TYPE.CONTAINS,
FILTER_PREDICATE_TYPE.NOT_CONTAIN,
FILTER_PREDICATE_TYPE.IS,
FILTER_PREDICATE_TYPE.IS_NOT,
FILTER_PREDICATE_TYPE.EMPTY,
FILTER_PREDICATE_TYPE.NOT_EMPTY,
],
},
};
export {
FILTER_COLUMN_OPTIONS,
};

View File

@@ -0,0 +1,30 @@
import { FILTER_TERM_MODIFIER_TYPE } from './filter-modifier';
const filterTermModifierNotWithin = [
FILTER_TERM_MODIFIER_TYPE.EXACT_DATE,
FILTER_TERM_MODIFIER_TYPE.TODAY,
FILTER_TERM_MODIFIER_TYPE.TOMORROW,
FILTER_TERM_MODIFIER_TYPE.YESTERDAY,
FILTER_TERM_MODIFIER_TYPE.ONE_WEEK_AGO,
FILTER_TERM_MODIFIER_TYPE.ONE_WEEK_FROM_NOW,
FILTER_TERM_MODIFIER_TYPE.ONE_MONTH_AGO,
FILTER_TERM_MODIFIER_TYPE.ONE_MONTH_FROM_NOW,
FILTER_TERM_MODIFIER_TYPE.NUMBER_OF_DAYS_AGO,
FILTER_TERM_MODIFIER_TYPE.NUMBER_OF_DAYS_FROM_NOW,
];
const filterTermModifierIsWithin = [
FILTER_TERM_MODIFIER_TYPE.THE_PAST_WEEK,
FILTER_TERM_MODIFIER_TYPE.THE_PAST_MONTH,
FILTER_TERM_MODIFIER_TYPE.THE_PAST_YEAR,
FILTER_TERM_MODIFIER_TYPE.THIS_WEEK,
FILTER_TERM_MODIFIER_TYPE.THIS_MONTH,
FILTER_TERM_MODIFIER_TYPE.THIS_YEAR,
FILTER_TERM_MODIFIER_TYPE.THE_NEXT_WEEK,
FILTER_TERM_MODIFIER_TYPE.THE_NEXT_MONTH,
FILTER_TERM_MODIFIER_TYPE.THE_NEXT_YEAR,
FILTER_TERM_MODIFIER_TYPE.THE_NEXT_NUMBERS_OF_DAYS,
FILTER_TERM_MODIFIER_TYPE.THE_PAST_NUMBERS_OF_DAYS,
];
export { filterTermModifierNotWithin, filterTermModifierIsWithin };

View File

@@ -0,0 +1,54 @@
import { gettext } from '../../../../../utils/constants';
const FILTER_TERM_MODIFIER_TYPE = {
TODAY: 'today',
TOMORROW: 'tomorrow',
YESTERDAY: 'yesterday',
ONE_WEEK_AGO: 'one_week_ago',
ONE_WEEK_FROM_NOW: 'one_week_from_now',
ONE_MONTH_AGO: 'one_month_ago',
ONE_MONTH_FROM_NOW: 'one_month_from_now',
NUMBER_OF_DAYS_AGO: 'number_of_days_ago',
NUMBER_OF_DAYS_FROM_NOW: 'number_of_days_from_now',
EXACT_DATE: 'exact_date',
THE_PAST_WEEK: 'the_past_week',
THE_PAST_MONTH: 'the_past_month',
THE_PAST_YEAR: 'the_past_year',
THE_NEXT_WEEK: 'the_next_week',
THE_NEXT_MONTH: 'the_next_month',
THE_NEXT_YEAR: 'the_next_year',
THE_NEXT_NUMBERS_OF_DAYS: 'the_next_numbers_of_days',
THE_PAST_NUMBERS_OF_DAYS: 'the_past_numbers_of_days',
THIS_WEEK: 'this_week',
THIS_MONTH: 'this_month',
THIS_YEAR: 'this_year',
};
const FILTER_TERM_MODIFIER_SHOW = {
[FILTER_TERM_MODIFIER_TYPE.TODAY]: gettext('Today'),
[FILTER_TERM_MODIFIER_TYPE.TOMORROW]: gettext('Tomorrow'),
[FILTER_TERM_MODIFIER_TYPE.YESTERDAY]: gettext('Yesterday'),
[FILTER_TERM_MODIFIER_TYPE.ONE_WEEK_AGO]: gettext('One_week_ago'),
[FILTER_TERM_MODIFIER_TYPE.ONE_WEEK_FROM_NOW]: gettext('One_week_from_now'),
[FILTER_TERM_MODIFIER_TYPE.ONE_MONTH_AGO]: gettext('One_month_ago'),
[FILTER_TERM_MODIFIER_TYPE.ONE_MONTH_FROM_NOW]: gettext('One_month_from_now'),
[FILTER_TERM_MODIFIER_TYPE.NUMBER_OF_DAYS_AGO]: gettext('Number_of_days_ago'),
[FILTER_TERM_MODIFIER_TYPE.NUMBER_OF_DAYS_FROM_NOW]: gettext('Number_of_days_from_now'),
[FILTER_TERM_MODIFIER_TYPE.EXACT_DATE]: gettext('Exact_date'),
[FILTER_TERM_MODIFIER_TYPE.THE_PAST_WEEK]: gettext('The_past_week'),
[FILTER_TERM_MODIFIER_TYPE.THE_PAST_MONTH]: gettext('The_past_month'),
[FILTER_TERM_MODIFIER_TYPE.THE_PAST_YEAR]: gettext('The_past_year'),
[FILTER_TERM_MODIFIER_TYPE.THE_NEXT_WEEK]: gettext('The_next_week'),
[FILTER_TERM_MODIFIER_TYPE.THE_NEXT_MONTH]: gettext('The_next_month'),
[FILTER_TERM_MODIFIER_TYPE.THE_NEXT_YEAR]: gettext('The_next_year'),
[FILTER_TERM_MODIFIER_TYPE.THE_NEXT_NUMBERS_OF_DAYS]: gettext('The_next_numbers_of_days'),
[FILTER_TERM_MODIFIER_TYPE.THE_PAST_NUMBERS_OF_DAYS]: gettext('The_past_numbers_of_days'),
[FILTER_TERM_MODIFIER_TYPE.THIS_WEEK]: gettext('This_week'),
[FILTER_TERM_MODIFIER_TYPE.THIS_MONTH]: gettext('This_month'),
[FILTER_TERM_MODIFIER_TYPE.THIS_YEAR]: gettext('This_year'),
};
export {
FILTER_TERM_MODIFIER_TYPE,
FILTER_TERM_MODIFIER_SHOW,
};

View File

@@ -0,0 +1,57 @@
const FILTER_PREDICATE_TYPE = {
CONTAINS: 'contains',
NOT_CONTAIN: 'does_not_contain',
IS: 'is',
IS_NOT: 'is_not',
EQUAL: 'equal',
NOT_EQUAL: 'not_equal',
LESS: 'less',
GREATER: 'greater',
LESS_OR_EQUAL: 'less_or_equal',
GREATER_OR_EQUAL: 'greater_or_equal',
EMPTY: 'is_empty',
NOT_EMPTY: 'is_not_empty',
IS_WITHIN: 'is_within',
IS_BEFORE: 'is_before',
IS_AFTER: 'is_after',
IS_ON_OR_BEFORE: 'is_on_or_before',
IS_ON_OR_AFTER: 'is_on_or_after',
HAS_ANY_OF: 'has_any_of',
HAS_ALL_OF: 'has_all_of',
HAS_NONE_OF: 'has_none_of',
IS_EXACTLY: 'is_exactly',
INCLUDE_ME: 'include_me',
IS_CURRENT_USER_ID: 'is_current_user_ID',
IS_ANY_OF: 'is_any_of',
IS_NONE_OF: 'is_none_of',
};
const FILTER_PREDICATE_SHOW = {
[FILTER_PREDICATE_TYPE.CONTAINS]: 'contains',
[FILTER_PREDICATE_TYPE.NOT_CONTAIN]: 'does not contain',
[FILTER_PREDICATE_TYPE.IS]: 'is',
[FILTER_PREDICATE_TYPE.IS_NOT]: 'is not',
[FILTER_PREDICATE_TYPE.EQUAL]: '\u003d',
[FILTER_PREDICATE_TYPE.NOT_EQUAL]: '\u2260',
[FILTER_PREDICATE_TYPE.LESS]: '\u003C',
[FILTER_PREDICATE_TYPE.GREATER]: '\u003E',
[FILTER_PREDICATE_TYPE.LESS_OR_EQUAL]: '\u2264',
[FILTER_PREDICATE_TYPE.GREATER_OR_EQUAL]: '\u2265',
[FILTER_PREDICATE_TYPE.EMPTY]: 'is empty',
[FILTER_PREDICATE_TYPE.NOT_EMPTY]: 'is not empty',
[FILTER_PREDICATE_TYPE.IS_WITHIN]: 'is within...',
[FILTER_PREDICATE_TYPE.IS_BEFORE]: 'is before...',
[FILTER_PREDICATE_TYPE.IS_AFTER]: 'is after...',
[FILTER_PREDICATE_TYPE.IS_ON_OR_BEFORE]: 'is on or before...',
[FILTER_PREDICATE_TYPE.IS_ON_OR_AFTER]: 'is on or after...',
[FILTER_PREDICATE_TYPE.HAS_ANY_OF]: 'has any of...',
[FILTER_PREDICATE_TYPE.HAS_ALL_OF]: 'has all of...',
[FILTER_PREDICATE_TYPE.HAS_NONE_OF]: 'has none of...',
[FILTER_PREDICATE_TYPE.IS_EXACTLY]: 'is exactly...',
[FILTER_PREDICATE_TYPE.IS_CURRENT_USER_ID]: 'is current user\'s ID',
};
export {
FILTER_PREDICATE_TYPE,
FILTER_PREDICATE_SHOW,
};

View File

@@ -0,0 +1,36 @@
const FILTER_CONJUNCTION_TYPE = {
AND: 'And',
OR: 'Or',
};
const FILTER_ERR_MSG = {
INVALID_FILTER: 'invalid filter',
INCOMPLETE_FILTER: 'incomplete filter',
COLUMN_MISSING: 'the column to filter does not exist',
COLUMN_NOT_SUPPORTED: 'the column to filter is not supported',
UNMATCHED_PREDICATE: 'unmatched filter predicate',
UNMATCHED_MODIFIER: 'unmatched filter modifier',
INVALID_TERM: 'invalid filter term',
};
export {
FILTER_CONJUNCTION_TYPE,
FILTER_ERR_MSG,
};
export { FILTER_COLUMN_OPTIONS } from './filter-column-options';
export {
FILTER_TERM_MODIFIER_TYPE,
FILTER_TERM_MODIFIER_SHOW,
} from './filter-modifier';
export {
FILTER_PREDICATE_TYPE,
FILTER_PREDICATE_SHOW,
} from './filter-predicate';
export {
filterTermModifierIsWithin,
filterTermModifierNotWithin,
} from './filter-is-within';

View File

@@ -0,0 +1,8 @@
const HEADER_HEIGHT_TYPE = {
DEFAULT: 'default',
DOUBLE: 'double',
};
export {
HEADER_HEIGHT_TYPE,
};

View File

@@ -0,0 +1,58 @@
import { CellType } from './column';
const MAX_GROUP_LEVEL = 3;
const GROUP_DATE_GRANULARITY = {
DAY: 'day',
WEEK: 'week',
MONTH: 'month',
QUARTAR: 'quartar',
YEAR: 'year',
};
const DISPLAY_GROUP_DATE_GRANULARITY = {
[GROUP_DATE_GRANULARITY.DAY]: 'By_day',
[GROUP_DATE_GRANULARITY.WEEK]: 'By_week',
[GROUP_DATE_GRANULARITY.MONTH]: 'By_month',
[GROUP_DATE_GRANULARITY.QUARTAR]: 'By_quarter',
[GROUP_DATE_GRANULARITY.YEAR]: 'By_year',
};
const GROUP_GEOLOCATION_GRANULARITY = {
PROVINCE: 'province',
CITY: 'city',
DISTRICT: 'district',
COUNTRY: 'country',
};
const DISPLAY_GROUP_GEOLOCATION_GRANULARITY = {
[GROUP_GEOLOCATION_GRANULARITY.PROVINCE]: 'By_province',
[GROUP_GEOLOCATION_GRANULARITY.CITY]: 'By_city',
[GROUP_GEOLOCATION_GRANULARITY.DISTRICT]: 'By_district',
};
const SUPPORT_GROUP_COLUMN_TYPES = [
CellType.TEXT,
CellType.CTIME,
CellType.MTIME,
CellType.CREATOR,
CellType.LAST_MODIFIER,
];
const GROUPBY_DATE_GRANULARITY_LIST = [
GROUP_DATE_GRANULARITY.DAY,
GROUP_DATE_GRANULARITY.WEEK,
GROUP_DATE_GRANULARITY.MONTH,
GROUP_DATE_GRANULARITY.QUARTAR,
GROUP_DATE_GRANULARITY.YEAR,
];
export {
MAX_GROUP_LEVEL,
GROUP_DATE_GRANULARITY,
DISPLAY_GROUP_DATE_GRANULARITY,
GROUP_GEOLOCATION_GRANULARITY,
DISPLAY_GROUP_GEOLOCATION_GRANULARITY,
SUPPORT_GROUP_COLUMN_TYPES,
GROUPBY_DATE_GRANULARITY_LIST,
};

View File

@@ -0,0 +1,63 @@
import KeyCodes from './key-codes';
import * as Z_INDEX from './z-index';
export {
CellType,
COLUMNS_ICON_CONFIG,
COLUMNS_ICON_NAME,
COLLABORATOR_COLUMN_TYPES,
DATE_COLUMN_OPTIONS,
NUMERIC_COLUMNS_TYPES,
DEFAULT_DATE_FORMAT,
UTC_FORMAT_DEFAULT,
DATE_UNIT,
DATE_FORMAT_MAP,
DEFAULT_NUMBER_FORMAT,
DATE_DEFAULT_TYPES,
NOT_SUPPORT_EDIT_COLUMN_TYPE,
NOT_SUPPORT_EDIT_COLUMN_TYPE_MAP,
MULTIPLE_CELL_VALUE_COLUMN_TYPE_MAP,
SINGLE_CELL_VALUE_COLUMN_TYPE_MAP,
} from './column';
export {
FILTER_CONJUNCTION_TYPE,
FILTER_ERR_MSG,
FILTER_COLUMN_OPTIONS,
FILTER_TERM_MODIFIER_TYPE,
FILTER_TERM_MODIFIER_SHOW,
FILTER_PREDICATE_TYPE,
FILTER_PREDICATE_SHOW,
filterTermModifierIsWithin,
filterTermModifierNotWithin,
} from './filter';
export {
MAX_GROUP_LEVEL,
GROUP_DATE_GRANULARITY,
DISPLAY_GROUP_DATE_GRANULARITY,
GROUP_GEOLOCATION_GRANULARITY,
DISPLAY_GROUP_GEOLOCATION_GRANULARITY,
SUPPORT_GROUP_COLUMN_TYPES,
GROUPBY_DATE_GRANULARITY_LIST,
} from './group';
export {
HEADER_HEIGHT_TYPE
} from './grid-header';
export {
REG_STRING_NUMBER_PARTS,
REG_NUMBER_DIGIT,
} from './reg';
export {
SELECT_OPTION_COLORS,
HIGHLIGHT_COLORS,
} from './select-option';
export {
SORT_TYPE,
SORT_COLUMN_OPTIONS,
TEXT_SORTER_COLUMN_TYPES,
NUMBER_SORTER_COLUMN_TYPES,
} from './sort';
export {
KeyCodes,
Z_INDEX,
};

View File

@@ -0,0 +1,102 @@
module.exports = {
Backspace: 8,
Tab: 9,
Enter: 13,
Shift: 16,
Ctrl: 17,
Alt: 18,
PauseBreak: 19,
CapsLock: 20,
Escape: 27,
Esc: 27,
Space: 32,
PageUp: 33,
PageDown: 34,
End: 35,
Home: 36,
LeftArrow: 37,
UpArrow: 38,
RightArrow: 39,
DownArrow: 40,
Insert: 45,
Delete: 46,
0: 48,
1: 49,
2: 50,
3: 51,
4: 52,
5: 53,
6: 54,
7: 55,
8: 56,
9: 57,
a: 65,
b: 66,
c: 67,
d: 68,
e: 69,
f: 70,
g: 71,
h: 72,
i: 73,
j: 74,
k: 75,
l: 76,
m: 77,
n: 78,
o: 79,
p: 80,
q: 81,
r: 82,
s: 83,
t: 84,
u: 85,
v: 86,
w: 87,
x: 88,
y: 89,
z: 90,
LeftWindowKey: 91,
RightWindowKey: 92,
SelectKey: 93,
NumPad0: 96,
NumPad1: 97,
NumPad2: 98,
NumPad3: 99,
NumPad4: 100,
NumPad5: 101,
NumPad6: 102,
NumPad7: 103,
NumPad8: 104,
NumPad9: 105,
Multiply: 106,
Add: 107,
Subtract: 109,
DecimalPoint: 110,
Divide: 111,
F1: 112,
F2: 113,
F3: 114,
F4: 115,
F5: 116,
F6: 117,
F7: 118,
F8: 119,
F9: 120,
F10: 121,
F12: 123,
NumLock: 144,
ScrollLock: 145,
SemiColon: 186,
EqualSign: 187,
Comma: 188,
Dash: 189,
Period: 190,
ForwardSlash: 191,
GraveAccent: 192,
OpenBracket: 219,
BackSlash: 220,
CloseBracket: 221,
SingleQuote: 222,
ChineseInputMethod: 229,
};

View File

@@ -0,0 +1,7 @@
const REG_STRING_NUMBER_PARTS = /\d+|\D+/g;
const REG_NUMBER_DIGIT = /\d/;
export {
REG_STRING_NUMBER_PARTS,
REG_NUMBER_DIGIT,
};

View File

@@ -0,0 +1,62 @@
const SELECT_OPTION_COLORS = [
{ COLOR: '#FFFCB5', BORDER_COLOR: '#E8E79D', TEXT_COLOR: '#212529' },
{ COLOR: '#FFEAB6', BORDER_COLOR: '#ECD084', TEXT_COLOR: '#212529' },
{ COLOR: '#FFD9C8', BORDER_COLOR: '#EFBAA3', TEXT_COLOR: '#212529' },
{ COLOR: '#FFDDE5', BORDER_COLOR: '#EDC4C1', TEXT_COLOR: '#212529' },
{ COLOR: '#FFD4FF', BORDER_COLOR: '#E6B6E6', TEXT_COLOR: '#212529' },
{ COLOR: '#DAD7FF', BORDER_COLOR: '#C3BEEF', TEXT_COLOR: '#212529' },
{ COLOR: '#DDFFE6', BORDER_COLOR: '#BBEBCD', TEXT_COLOR: '#212529' },
{ COLOR: '#DEF7C4', BORDER_COLOR: '#C5EB9E', TEXT_COLOR: '#212529' },
{ COLOR: '#D8FAFF', BORDER_COLOR: '#B4E4E9', TEXT_COLOR: '#212529' },
{ COLOR: '#D7E8FF', BORDER_COLOR: '#BAD1E9', TEXT_COLOR: '#212529' },
{ COLOR: '#B7CEF9', BORDER_COLOR: '#96B2E1', TEXT_COLOR: '#212529' },
{ COLOR: '#E9E9E9', BORDER_COLOR: '#DADADA', TEXT_COLOR: '#212529' },
{ COLOR: '#FBD44A', BORDER_COLOR: '#E5C142', TEXT_COLOR: '#FFFFFF' },
{ COLOR: '#EAA775', BORDER_COLOR: '#D59361', TEXT_COLOR: '#FFFFFF' },
{ COLOR: '#F4667C', BORDER_COLOR: '#DC556A', TEXT_COLOR: '#FFFFFF' },
{ COLOR: '#DC82D2', BORDER_COLOR: '#D166C5', TEXT_COLOR: '#FFFFFF' },
{ COLOR: '#9860E5', BORDER_COLOR: '#844BD2', TEXT_COLOR: '#FFFFFF' },
{ COLOR: '#9F8CF1', BORDER_COLOR: '#8F75E2', TEXT_COLOR: '#FFFFFF' },
{ COLOR: '#59CB74', BORDER_COLOR: '#4EB867', TEXT_COLOR: '#FFFFFF' },
{ COLOR: '#ADDF84', BORDER_COLOR: '#9CCF72', TEXT_COLOR: '#FFFFFF' },
{ COLOR: '#89D2EA', BORDER_COLOR: '#7BC0D6', TEXT_COLOR: '#FFFFFF' },
{ COLOR: '#4ECCCB', BORDER_COLOR: '#45BAB9', TEXT_COLOR: '#FFFFFF' },
{ COLOR: '#46A1FD', BORDER_COLOR: '#3C8FE4', TEXT_COLOR: '#FFFFFF' },
{ COLOR: '#C2C2C2', BORDER_COLOR: '#ADADAD', TEXT_COLOR: '#FFFFFF' },
];
const HIGHLIGHT_COLORS = {
'#FFE8E6': '#FF6052',
'#FFDED5': '#FF714A',
'#FFE7D1': '#FF851A',
'#EED5FF': '#B64DFD',
'#DAD7FF': '#5F4CFF',
'#D7E8FF': '#3C8FFF',
'#D8FAFF': '#41E7FF',
'#DDFFE6': '#16BA51',
'#E9E9E9': '#999999',
'#FBD44A': '#E5C142',
'#EAA775': '#D59361',
'#F4667C': '#DC556A',
'#DC82D2': '#D166C5',
'#9860E5': '#844BD2',
'#9F8CF1': '#8F75E2',
'#59CB74': '#4EB867',
'#ADDF84': '#9CCF72',
'#89D2EA': '#7BC0D6',
'#4ECCCB': '#45BAB9',
'#46A1FD': '#3C8FE4',
'#C2C2C2': '#ADADAD',
'#FFFCB5': '#E8E79D',
'#FFEAB6': '#ECD084',
'#FFD9C8': '#EFBAA3',
'#FFDDE5': '#EDC4C1',
'#FFD4FF': '#E6B6E6',
'#DEF7C4': '#C5EB9E',
'#B7CEF9': '#96B2E1',
};
export {
SELECT_OPTION_COLORS,
HIGHLIGHT_COLORS,
};

View File

@@ -0,0 +1,22 @@
import { CellType } from './column';
const SORT_TYPE = {
UP: 'up',
DOWN: 'down',
};
const SORT_COLUMN_OPTIONS = [
CellType.CTIME,
CellType.MTIME,
CellType.TEXT,
];
const TEXT_SORTER_COLUMN_TYPES = [CellType.TEXT];
const NUMBER_SORTER_COLUMN_TYPES = [];
export {
SORT_TYPE,
SORT_COLUMN_OPTIONS,
TEXT_SORTER_COLUMN_TYPES,
NUMBER_SORTER_COLUMN_TYPES,
};

View File

@@ -0,0 +1,135 @@
// copy from sf-metadata
// Drop Target of top row's z-index is -1.
export const DEFAULT_DROP_TARGET = -1;
// CellMasks should render in front of the cells
// Unfrozen cells do not have a zIndex specifed
export const CELL_MASK = 1;
export const TABLE_MAIN_INTERVAL = 1;
export const RESIZE_HANDLE = 1;
export const SEQUENCE_COLUMN = 1;
// higher than unfrozen header cell(0), RESIZE_HANDLE
export const FROZEN_HEADER_CELL = 2;
export const GROUP_FROZEN_HEADER = 2;
export const SCROLL_BAR = 2;
// In front of CELL_MASK/non-frozen cell(1)、back of the frozen cells (2)
export const GROUP_BACKDROP = 2;
export const MOBILE_RECORDS_COLUMN_NAMES = 2;
export const FROZEN_GROUP_CELL = 2;
// Frozen cells have a zIndex value of 2 so CELL_MASK should have a higher value
export const FROZEN_CELL_MASK = 3;
// GALLERY_MAIN_HEADER, TABLE_MAIN_INTERVAL is 3 to hide first freeze column
export const GALLERY_MAIN_HEADER = 3;
// APP_HEADER is 8 than TABLE_HEADER because Logout Popover need to be at the top
export const APP_HEADER = 8;
// higher than frozen cells(2)
export const GRID_HORIZONTAL_SCROLLBAR = 3;
// In mobile list mode, row name fixed, so upper components z-index is 3
export const SEARCH_ALL_TABLES = 3;
export const MOBILE_TABLES_TABS_CONTAINER = 3;
export const MOBILE_TABLE_TOOLBAR = 3;
export const MOBILE_HEADER = 3;
// need higher than the doms(etc. cell, cell_mask) which behind of the grid header
export const GRID_HEADER = 4;
export const GRID_FOOTER = 4;
export const UPLOAD_PROGRESS = 4;
// frozen column header z-index is 3row drop target horizontal line shoule appear so z-index is 4
export const ROW_DROP_TARGET = 4;
export const VIEW_SIDEBAR_RESIZE_HANDLER = 4;
// need higher or equal to GRID_HEADER(frozen): 4
export const TABLE_SETTING_PANEL = 4;
// higher than PANE_DIVIDER(4)
export const TABLE_TOOLBAR = 5;
export const TABLE_RIGHT_PANEL = 5;
// higher than TABLE_TOOLBAR(5)
export const TABLES_TABS_CONTAINER = 6;
// higher than TABLES_TABS_CONTAINER(6)
export const TABLE_HEADER = 7;
export const TABLE_COMMENT_CONTAINER = 7;
// higher than TABLE_HEADER(7)
export const PANE_DIVIDER = 8;
// EditorContainer is rendered outside the grid and it higher FROZEN_GROUP_CELL(2) and PANE_DIVIDER(8)
export const EDITOR_CONTAINER = 9;
// APP_LEFT_BAR_COLLAPSE z-index should taller than the APP_HEADER and the PANE_DIVIDER(8)
export const APP_LEFT_BAR_COLLAPSE = 9;
export const EXPAND_ROW_ICON = 99;
export const MOBILE_MASK = 100;
export const ROW_EXPAND_VIEW = 100;
export const APP_NAV_SLIDER = 100;
// LINK_RECORDS z-index should higher than EXPAND_ROW_ICON
export const LINK_RECORDS = 100;
// APP_LEFT_BAR is higher than APP_NAV_SLIDER
export const APP_LEFT_BAR = 101;
export const MOBILE_APP_NAV = 101;
export const STATISTIC_DIALOG_MODAL = 800;
export const STATISTIC_ENLARGE_DIALOG_MODAL = 900;
export const STATISTIC_RECORDS_DIALOG_MODAL = 1000;
export const SEARCH_TABLES_DIALOG_MODAL = 1000;
export const DATE_EDITOR = 1001;
export const NOTIFICATION_LIST_MODAL = 1046;
export const TRIGGER_ROWS_MODAL = 1047;
export const TRIGGER_ROWS_VIEW = 1047;
export const RECORD_DETAILS_DIALOG = 1048;
export const CALENDAR_DIALOG_MODAL = 1048;
export const PRINT_ROW_TYPE_MODAL = 1049;
export const IMAGE_PREVIEW_LIGHTBOX = 1051;
export const DROPDOWN_MENU = 1051;
export const RC_CALENDAR = 1053;
export const EDIT_COLUMN_POPOVER = 1060;
export const LARGE_MAP_EDITOR_DIALOG_MODAL = 1061;
export const TOAST_MANAGER = 999999;
export const LINK_PICKER = 10;

View File

@@ -0,0 +1,114 @@
export {
UserService
} from './services';
export {
CellType,
COLUMNS_ICON_CONFIG,
COLUMNS_ICON_NAME,
COLLABORATOR_COLUMN_TYPES,
DATE_COLUMN_OPTIONS,
NUMERIC_COLUMNS_TYPES,
DEFAULT_DATE_FORMAT,
UTC_FORMAT_DEFAULT,
DATE_UNIT,
DATE_FORMAT_MAP,
DEFAULT_NUMBER_FORMAT,
DATE_DEFAULT_TYPES,
NOT_SUPPORT_EDIT_COLUMN_TYPE,
NOT_SUPPORT_EDIT_COLUMN_TYPE_MAP,
MULTIPLE_CELL_VALUE_COLUMN_TYPE_MAP,
SINGLE_CELL_VALUE_COLUMN_TYPE_MAP,
FILTER_CONJUNCTION_TYPE,
FILTER_ERR_MSG,
FILTER_COLUMN_OPTIONS,
FILTER_TERM_MODIFIER_TYPE,
FILTER_TERM_MODIFIER_SHOW,
FILTER_PREDICATE_TYPE,
FILTER_PREDICATE_SHOW,
filterTermModifierIsWithin,
filterTermModifierNotWithin,
MAX_GROUP_LEVEL,
GROUP_DATE_GRANULARITY,
DISPLAY_GROUP_DATE_GRANULARITY,
GROUP_GEOLOCATION_GRANULARITY,
DISPLAY_GROUP_GEOLOCATION_GRANULARITY,
SUPPORT_GROUP_COLUMN_TYPES,
REG_STRING_NUMBER_PARTS,
REG_NUMBER_DIGIT,
SELECT_OPTION_COLORS,
HIGHLIGHT_COLORS,
SORT_TYPE,
SORT_COLUMN_OPTIONS,
TEXT_SORTER_COLUMN_TYPES,
NUMBER_SORTER_COLUMN_TYPES,
KeyCodes,
Z_INDEX,
GROUPBY_DATE_GRANULARITY_LIST,
HEADER_HEIGHT_TYPE,
} from './constants';
export {
getColumnType,
getColumnsByType,
isDateColumn,
isSupportDateColumnFormat,
getValidFilters,
getValidFiltersWithoutError,
deleteInvalidFilter,
otherDate,
getFormattedFilterOtherDate,
getFormattedFilter,
getFormattedFilters,
creatorFilter,
dateFilter,
textFilter,
filterRow,
filterRows,
deleteInvalidGroupby,
isValidGroupby,
getValidGroupbys,
groupTableRows,
groupViewRows,
isTableRows,
updateTableRowsWithRowsData,
isValidSort,
getValidSorts,
deleteInvalidSort,
getMultipleIndexesOrderbyOptions,
sortDate,
sortText,
sortRowsWithMultiSorts,
sortTableRows,
getTableById,
getTableByName,
getTableByIndex,
getTableColumnByKey,
getTableColumnByName,
getRowById,
getRowsByIds,
isValidEmail,
ValidateFilter,
DATE_MODIFIERS_REQUIRE_TERM,
getViewById,
getViewByName,
isDefaultView,
isFilterView,
isGroupView,
isSortView,
isHiddenColumnsView,
getViewShownColumns,
getGroupByPath,
getType,
isMac,
base64ToFile,
bytesToSize,
getErrorMsg,
DateUtils,
CommonlyUsedHotkey,
LocalStorage,
isFunction,
isEmpty,
isEmptyObject,
debounce,
throttle,
} from './utils';

View File

@@ -0,0 +1,5 @@
import UserService from './user-service';
export {
UserService,
};

View File

@@ -0,0 +1,68 @@
const PENDING_INTERVAL = 1000; // 1s
class UserService {
constructor({ api, mediaUrl = '' }) {
this.api = api;
this.defaultAvatarUrl = `${mediaUrl}/avatars/default.png`;
this.waitingQueryEmails = [];
this.waitingExecCallbacks = [];
this.emailUserMap = {};
}
queryUser = (email, callback) => {
if (!email) return;
this.waitingExecCallbacks.push(callback);
if (this.emailUserMap[email] || this.waitingQueryEmails.includes(email)) return;
this.waitingQueryEmails.push(email);
this.startQueryUsers();
};
queryUsers = (emails, callback) => {
if (!Array.isArray(emails) || emails.length === 0) return;
let validEmails = [];
emails.forEach(email => {
this.waitingExecCallbacks.push(callback);
if (this.emailUserMap[email] || this.waitingQueryEmails.includes(email)) return;
validEmails.push(email);
});
if (validEmails.length === 0) return;
this.waitingQueryEmails.push(...validEmails);
this.startQueryUsers();
};
startQueryUsers = () => {
if (this.pendingTimer || this.waitingQueryEmails.length === 0) return;
this.pendingTimer = setTimeout(() => {
this.api(this.waitingQueryEmails).then(res => {
const { user_list } = res.data;
user_list.forEach(user => {
this.emailUserMap[user.email] = user;
});
this.queryUserCallback();
}).catch(() => {
this.waitingQueryEmails.forEach(email => {
this.emailUserMap[email] = {
email: email,
name: email,
avatar_url: this.defaultAvatarUrl,
};
});
this.queryUserCallback();
});
clearTimeout(this.pendingTimer);
this.pendingTimer = null;
}, PENDING_INTERVAL);
};
queryUserCallback = () => {
this.waitingExecCallbacks.forEach(callback => {
callback(this.emailUserMap);
});
this.waitingQueryEmails = [];
this.waitingExecCallbacks = [];
};
}
export default UserService;

View File

@@ -0,0 +1,27 @@
/**
* Get column type.
* @param {object} column { type, ... }
* @returns column type
*/
const getColumnType = (column) => {
const { type } = column;
return type;
};
/**
* Get columns by type.
* @param {array} columns
* @param {string} columnType
* @returns the target type columns, array
*/
const getColumnsByType = (columns, columnType) => {
if (!Array.isArray(columns) || !columnType) {
return [];
}
return columns.filter((column) => column.type === columnType);
};
export {
getColumnType,
getColumnsByType,
};

View File

@@ -0,0 +1,30 @@
import { getColumnType } from './core';
import { DATE_COLUMN_OPTIONS, DATE_FORMAT_MAP } from '../../constants/column';
/**
* Check whether is date column:
* - column type is date, ctime or mtime etc.
* - column type is formula and result_type is date
* - column type is link/link_fromula and array_type is date, ctime or mtime etc.
* @param {object} column e.g. { type, data }
* @returns true/false, bool
*/
const isDateColumn = (column) => DATE_COLUMN_OPTIONS.includes(getColumnType(column));
/**
* Check whether the format is supported in date column
* @param {string} format
* @returns bool
*/
const isSupportDateColumnFormat = (format) => {
if (!format) {
return false;
}
return (
format === DATE_FORMAT_MAP.YYYY_MM_DD
|| format === DATE_FORMAT_MAP.YYYY_MM_DD_HH_MM
|| format === DATE_FORMAT_MAP.YYYY_MM_DD_HH_MM_SS
);
};
export { isDateColumn, isSupportDateColumnFormat };

View File

@@ -0,0 +1,8 @@
export {
getColumnType,
getColumnsByType,
} from './core';
export {
isDateColumn,
isSupportDateColumnFormat,
} from './date';

View File

@@ -0,0 +1,113 @@
export const getType = (value) => {
return Object.prototype.toString.call(value).slice(8, -1);
};
export const isMac = () => {
const platform = navigator.platform;
return (platform === 'Mac68K') || (platform === 'MacPPC') || (platform === 'Macintosh') || (platform === 'MacIntel');
};
export const base64ToFile = (data, fileName) => {
const parts = data.split(';base64,');
const contentType = parts[0].split(':')[1];
const raw = window.atob(parts[1]);
const rawLength = raw.length;
const uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
const blob = new Blob([uInt8Array], { type: contentType });
const file = new File([blob], fileName, { type: contentType });
return file;
};
export const bytesToSize = (bytes) => {
if (typeof(bytes) == 'undefined') return ' ';
if (bytes < 0) return '--';
const sizes = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
if (bytes === 0) return bytes + ' ' + sizes[0];
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1000)), 10);
if (i === 0) return bytes + ' ' + sizes[i];
return (bytes / (1000 ** i)).toFixed(1) + ' ' + sizes[i];
};
export const getErrorMsg = (error) => {
let errorMsg = '';
if (error.response) {
if (error.response.status === 403) {
errorMsg = 'Permission_denied';
} else if (error.response.data &&
error.response.data['error_msg']) {
errorMsg = error.response.data['error_msg'];
} else {
errorMsg = 'Error';
}
} else {
errorMsg = 'Please_check_the_network';
}
return errorMsg;
};
export const isFunction = (functionToCheck) => {
const getType = {};
return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
};
/**
* Check whether the given value is empty
* @param {any} val
* @returns bool
*/
export const isEmpty = (val) => {
if (val === null || val === undefined) return true;
if (val.length !== undefined) return val.length === 0;
if (val instanceof Date) return false;
if (typeof val === 'object') return Object.keys(val).length === 0;
return false;
};
/**
* Check whether the object is empty.
* The true will be returned if the "obj" is invalid.
* @param {object} obj
* @returns bool
*/
export const isEmptyObject = (obj) => {
let name;
// eslint-disable-next-line
for (name in obj) {
return false;
}
return true;
};
export const debounce = (fn, wait) => {
let timeout = null;
return function () {
if (timeout !== null) clearTimeout(timeout);
timeout = setTimeout(fn, wait);
};
};
export const throttle = (func, delay) => {
let timer = null;
let startTime = Date.now();
return function () {
let curTime = Date.now();
let remaining = delay - (curTime - startTime);
let context = this;
let args = arguments;
clearTimeout(timer);
if (remaining <= 0) {
func.apply(context, args);
startTime = Date.now();
} else {
timer = setTimeout(func, remaining);
}
};
};

View File

@@ -0,0 +1,207 @@
import {
DEFAULT_DATE_FORMAT,
DATE_UNIT,
} from '../constants';
const MONTH_QUARTERS = [1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4];
const FORMATTING_TOKENS = /(\[[^[]*\])|([-:/.()\s]+)|(A|a|YYYY|YY?|MM?M?M?|Do|DD?|hh?|HH?|mm?|ss?|S{1,3}|z|ZZ?)/g;
const MATCH_1_2 = /\d\d?/; // 0 - 99
const MATCH2 = /\d\d/; // 00 - 99
const MATCH4 = /\d{4}/; // 0000 - 9999
const MATCHER_EXPRESSIONS = {
mm: [MATCH_1_2, DATE_UNIT.MINUTES],
HH: [MATCH_1_2, DATE_UNIT.HOURS],
D: [MATCH_1_2, DATE_UNIT.DAY],
DD: [MATCH2, DATE_UNIT.DAY],
M: [MATCH_1_2, DATE_UNIT.MONTH],
MM: [MATCH2, DATE_UNIT.MONTH],
YYYY: [MATCH4, DATE_UNIT.YEAR],
};
const MATCHER_DATE_PARTS = ['YYYY', 'MM', 'M', 'DD', 'D'];
class DateUtils {
/**
* return the formatted date with target format.
* @param {string|date object} date
* @param {string} format
* @returns formatted date
*/
static format(date, format) {
const dateObject = this.getValidDate(date);
if (!dateObject) {
return '';
}
const upperCaseFormat = format && format.toUpperCase();
const year = dateObject.getFullYear();
const month = dateObject.getMonth() + 1;
const day = dateObject.getDate();
const displayMonth = month < 10 ? `0${month}` : month;
const displayDay = day < 10 ? `0${day}` : day;
switch (upperCaseFormat) {
case 'YYYY-MM-DD HH:MM:SS': {
const hours = dateObject.getHours();
const minutes = dateObject.getMinutes();
const seconds = dateObject.getSeconds();
const disPlayHours = hours < 10 ? `0${hours}` : hours;
const disPlayMinutes = minutes < 10 ? `0${minutes}` : minutes;
const disPlaySeconds = seconds < 10 ? `0${seconds}` : seconds;
return `${year}-${displayMonth}-${displayDay} ${disPlayHours}:${disPlayMinutes}:${disPlaySeconds}`;
}
case 'YYYY-MM-DD HH:MM': {
const hours = dateObject.getHours();
const minutes = dateObject.getMinutes();
const disPlayHours = hours < 10 ? `0${hours}` : hours;
const disPlayMinutes = minutes < 10 ? `0${minutes}` : minutes;
return `${year}-${displayMonth}-${displayDay} ${disPlayHours}:${disPlayMinutes}`;
}
default: {
return `${year}-${displayMonth}-${displayDay}`;
}
}
}
/**
* returns the formatted date with granularity.
* @param {string|date object} date
* @param {string} granularity
* @returns formatted date
*/
static getDateByGranularity(date, granularity) {
const dateObject = this.getValidDate(date);
if (!dateObject) {
return '';
}
const upperCaseGranularity = granularity && granularity.toUpperCase();
const year = dateObject.getFullYear();
switch (upperCaseGranularity) {
case 'YEAR': {
return `${year}`;
}
case 'QUARTAR': {
const month = dateObject.getMonth();
const quarter = MONTH_QUARTERS[month];
return `${year}-Q${quarter}`;
}
case 'MONTH': {
const month = dateObject.getMonth() + 1;
const displayMonth = month < 10 ? `0${month}` : month;
return `${year}-${displayMonth}`;
}
case 'WEEK': {
const weekNum = dateObject.getDay();
const startOfWeekDay = dateObject.getDate() + (weekNum === 0 ? -6 : 1 - weekNum);
const startOfWeekDate = new Date(year, dateObject.getMonth(), startOfWeekDay);
const month = startOfWeekDate.getMonth() + 1;
const day = startOfWeekDate.getDate();
const displayMonth = month < 10 ? `0${month}` : month;
const displayDay = day < 10 ? `0${day}` : day;
return `${startOfWeekDate.getFullYear()}-${displayMonth}-${displayDay}`;
}
case 'DAY': {
const month = dateObject.getMonth() + 1;
const day = dateObject.getDate();
const displayMonth = month < 10 ? `0${month}` : month;
const displayDay = day < 10 ? `0${day}` : day;
return `${year}-${displayMonth}-${displayDay}`;
}
default: {
return '';
}
}
}
static isValidDateObject(dateObject) {
return dateObject instanceof Date && !isNaN(dateObject.getTime());
}
static getValidDate(date) {
if (!date) {
return null;
}
const isDateTypeString = typeof date === 'string';
let dateString = date;
let dateObject = date;
if (isDateTypeString) {
if (dateString.split(' ').length > 1 || dateString.includes('T')) {
dateObject = new Date(date);
} else {
// given date is without time precision
dateString = `${date} 00:00:00`;
dateObject = new Date(dateString);
}
}
if (this.isValidDateObject(dateObject)) return dateObject;
if (!isDateTypeString) return null;
// ios phone and safari browser not support use '2021-09-10 12:30', support '2021/09/10 12:30'
dateObject = new Date(dateString.replace(/-/g, '/'));
if (this.isValidDateObject(dateObject)) return dateObject;
return null;
}
/**
* @param {string} dateString
* @param {string} format
* @returns Date Object
*/
static parseDateWithFormat(dateString, format) {
if (dateString.includes('T')) {
// ISO 8601 format with "T" separator directly using Date object
const dateObj = new Date(dateString);
return this.isValidDateObject(dateObj) ? dateObj : this.getValidDate(dateString);
}
try {
const parser = this.makeParser(format);
let {
year, month, day, hours, minutes,
} = parser(dateString);
if (!year) {
const nowDate = new Date();
year = nowDate.getFullYear();
}
let dateObj = new Date(`${year}-${month}-${day} ${hours || '00'}:${minutes || '00'}`);
if (!this.isValidDateObject(dateObj)) {
return this.getValidDate(dateString);
}
return dateObj;
} catch (err) {
return this.getValidDate(dateString);
}
}
static makeParser(format) {
// 'YYYY-MM-DD HH:mm'.match(formattingTokens):
// ['YYYY', '-', 'MM', '-', 'DD', ' ', 'HH', ':', 'mm']
const tokens = (format || DEFAULT_DATE_FORMAT).match(FORMATTING_TOKENS);
const { length: formatPartsLength } = tokens;
return (dateString) => {
const dateParts = dateString.split(' ');
let datePart = dateParts[0] || '';
let timePart = dateParts[1] || '';
let time = {};
for (let i = 0; i < formatPartsLength; i++) {
const token = tokens[i];
const parseTo = MATCHER_EXPRESSIONS[token];
if (!parseTo) continue;
const regex = parseTo[0];
const parserType = parseTo[1];
if (!parserType) continue;
const isDatePart = MATCHER_DATE_PARTS.includes(token);
let match = isDatePart ? regex.exec(datePart) : regex.exec(timePart);
if (!match) continue;
const value = match[0];
time[parserType] = value;
if (isDatePart) {
datePart = datePart.replace(value, '');
} else {
timePart = timePart.replace(value, '');
}
}
return time;
};
}
}
export { DateUtils };

View File

@@ -0,0 +1,273 @@
import { ValidateFilter } from '../validate/filter';
import { DateUtils } from '../date';
import {
FILTER_ERR_MSG,
FILTER_TERM_MODIFIER_TYPE,
} from '../../constants/filter';
import { CellType } from '../../constants/column';
const EXACT_DATE_TERM_MODIFIER_TYPES = [
FILTER_TERM_MODIFIER_TYPE.TODAY,
FILTER_TERM_MODIFIER_TYPE.TOMORROW,
FILTER_TERM_MODIFIER_TYPE.YESTERDAY,
FILTER_TERM_MODIFIER_TYPE.ONE_WEEK_AGO,
FILTER_TERM_MODIFIER_TYPE.ONE_WEEK_FROM_NOW,
FILTER_TERM_MODIFIER_TYPE.ONE_MONTH_AGO,
FILTER_TERM_MODIFIER_TYPE.ONE_MONTH_FROM_NOW,
FILTER_TERM_MODIFIER_TYPE.NUMBER_OF_DAYS_AGO,
FILTER_TERM_MODIFIER_TYPE.NUMBER_OF_DAYS_FROM_NOW,
FILTER_TERM_MODIFIER_TYPE.EXACT_DATE,
];
/**
* Get filters which excludes incomplete
* @param {array} filters e.g. [{ column_key, filter_predicate, ... }]
* @param {array} columns
* @returns valid filters, array
*/
const getValidFilters = (filters, columns) => {
if (!Array.isArray(filters) || !Array.isArray(columns)) {
return [];
}
return filters.filter((filter) => {
const { error_message } = ValidateFilter.validate(filter, columns);
return !error_message || error_message !== FILTER_ERR_MSG.INCOMPLETE_FILTER;
});
};
/**
* Get filters without error messages
* @param {array} filters e.g. [{ column_key, filter_predicate, ... }]
* @param {array} columns
* @returns valid filters, array
*/
const getValidFiltersWithoutError = (filters, columns) => {
if (!Array.isArray(filters) || !Array.isArray(columns)) {
return [];
}
return filters.filter((filter) => !ValidateFilter.validate(filter, columns).error_message);
};
/**
* Generate date for filter
* @param {string} filterTermModifier
* @param {any} filterTerm
* @returns date | date range, object
*/
const otherDate = (filterTermModifier, filterTerm) => {
const today = new Date();
const year = today.getFullYear();
const month = today.getMonth(); // use js month representation: 0 - 11
const day = today.getDate();
// 0 1 2 3 4 5 6 7 8 9 10 11 days in every month
let days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
days[1] = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) ? 29 : 28; // is leap year
switch (filterTermModifier) {
case FILTER_TERM_MODIFIER_TYPE.TODAY: {
// today, should start at 0:00 and end at 24:00
return new Date(year, month, day, 0, 0, 0);
}
case FILTER_TERM_MODIFIER_TYPE.TOMORROW: {
return new Date(year, month, day + 1);
}
case FILTER_TERM_MODIFIER_TYPE.YESTERDAY: {
return new Date(year, month, day - 1);
}
case FILTER_TERM_MODIFIER_TYPE.ONE_WEEK_AGO: {
return new Date(year, month, day - 7);
}
case FILTER_TERM_MODIFIER_TYPE.ONE_WEEK_FROM_NOW: {
return new Date(year, month, day + 7);
}
case FILTER_TERM_MODIFIER_TYPE.ONE_MONTH_AGO: {
const pastMonth = month - 1;
const monthDaysIndex = month === 0 ? 11 : pastMonth;
const currentDay = day > days[monthDaysIndex] ? days[monthDaysIndex] : day;
return new Date(year, pastMonth, currentDay);
}
case FILTER_TERM_MODIFIER_TYPE.ONE_MONTH_FROM_NOW: {
const nextMonth = month + 1;
const monthDaysIndex = month === 11 ? 0 : nextMonth;
const currentDay = day > days[monthDaysIndex] ? days[monthDaysIndex] : day;
return new Date(year, nextMonth, currentDay);
}
case FILTER_TERM_MODIFIER_TYPE.NUMBER_OF_DAYS_AGO: {
return new Date(year, month, day - Number(filterTerm));
}
case FILTER_TERM_MODIFIER_TYPE.NUMBER_OF_DAYS_FROM_NOW: {
return new Date(year, month, day + Number(filterTerm));
}
case FILTER_TERM_MODIFIER_TYPE.EXACT_DATE: {
return new Date(filterTerm);
}
case FILTER_TERM_MODIFIER_TYPE.THE_PAST_WEEK: {
const weekDay = today.getDay() !== 0 ? today.getDay() : 7;
return {
startDate: new Date(year, month, day - weekDay - 6),
endDate: new Date(year, month, day - weekDay),
};
}
case FILTER_TERM_MODIFIER_TYPE.THIS_WEEK: {
const weekDay = today.getDay() !== 0 ? today.getDay() : 7;
return {
startDate: new Date(year, month, day - weekDay + 1),
endDate: new Date(year, month, day - weekDay + 7),
};
}
case FILTER_TERM_MODIFIER_TYPE.THE_NEXT_WEEK: {
const weekDay = today.getDay() !== 0 ? today.getDay() : 7;
return {
startDate: new Date(year, month, day - weekDay + 8),
endDate: new Date(year, month, day - weekDay + 14),
};
}
case FILTER_TERM_MODIFIER_TYPE.THE_PAST_MONTH: {
const pastMonth = month - 1;
return {
startDate: new Date(year, pastMonth, 1),
endDate: new Date(year, pastMonth, days[month === 0 ? 11 : pastMonth]),
};
}
case FILTER_TERM_MODIFIER_TYPE.THIS_MONTH: {
return {
startDate: new Date(year, month, 1),
endDate: new Date(year, month, days[month]),
};
}
case FILTER_TERM_MODIFIER_TYPE.THE_NEXT_MONTH: {
const nextMonth = month + 1;
return {
startDate: new Date(year, nextMonth, 1),
endDate: new Date(year, nextMonth, month === 11 ? days[0] : days[nextMonth]),
};
}
case FILTER_TERM_MODIFIER_TYPE.THE_PAST_YEAR: {
const pastYear = year - 1;
return {
startDate: new Date(pastYear, 0, 1), // The computer's month starts at 0.
endDate: new Date(pastYear, 11, 31),
};
}
case FILTER_TERM_MODIFIER_TYPE.THIS_YEAR: {
return {
startDate: new Date(year, 0, 1),
endDate: new Date(year, 11, 31),
};
}
case FILTER_TERM_MODIFIER_TYPE.THE_NEXT_YEAR: {
const nextYear = year + 1;
return {
startDate: new Date(nextYear, 0, 1),
endDate: new Date(nextYear, 11, 31),
};
}
case FILTER_TERM_MODIFIER_TYPE.THE_NEXT_NUMBERS_OF_DAYS: {
return {
startDate: new Date(year, month, day + 1, 0, 0, 0),
endDate: new Date(year, month, day + Number(filterTerm)),
};
}
case FILTER_TERM_MODIFIER_TYPE.THE_PAST_NUMBERS_OF_DAYS: {
return {
startDate: new Date(year, month, day - Number(filterTerm)),
endDate: new Date(year, month, day, 0, 0, 0),
};
}
default: {
return {};
}
}
};
/**
* Generate formatted date for filter
* @param {string} filterTermModifier
* @param {any} filterTerm
* @returns formatted date | date range, object
*/
const getFormattedFilterOtherDate = (filterTermModifier, filterTerm) => {
const _otherDate = otherDate(filterTermModifier, filterTerm);
if (EXACT_DATE_TERM_MODIFIER_TYPES.includes(filterTermModifier)) {
return DateUtils.format(_otherDate);
}
const { startDate, endDate } = _otherDate;
return {
startDate: startDate ? DateUtils.format(startDate) : '',
endDate: endDate ? DateUtils.format(endDate) : '',
};
};
/**
* Format filter with other_date, linked_column etc.
* @param {object} filter e.g. { filter_term, filter_term_modifier, ... }
* @param {object} column
* @returns formatted filter
*/
const getFormattedFilter = (filter, column) => {
const { filter_term, filter_term_modifier } = filter;
let { type: columnType } = column;
let formattedFilter = filter;
switch (columnType) {
case CellType.CTIME:
case CellType.MTIME: {
formattedFilter.other_date = getFormattedFilterOtherDate(filter_term_modifier, filter_term);
break;
}
default: {
break;
}
}
return formattedFilter;
};
/**
* Get formatted filters with other_date, linked_column etc.
* @param {array} filters [{ filter_term, filter_term_modifier, column, ... }]
* @returns formatted filters, array
*/
const getFormattedFilters = (filters) => (
filters.map((filter) => (
getFormattedFilter(filter, filter.column)
))
);
/**
* Get filters without error messages and formatted with filter column
* @param {array} filters e.g. [{ column_key, filter_predicate, ... }]
* @param {array} columns
* @returns filters, array
*/
const deleteInvalidFilter = (filters, columns) => {
if (!Array.isArray(filters) || filters.length === 0) {
return [];
}
let cleanFilters = [];
filters.forEach((filter) => {
const { column_key } = filter;
const { error_message } = ValidateFilter.validate(filter, columns);
if (error_message) {
if (error_message !== FILTER_ERR_MSG.INCOMPLETE_FILTER) {
throw new Error(error_message);
}
} else {
const filterColumn = columns.find((column) => column.key === column_key);
const newFilter = { ...filter, column: filterColumn };
cleanFilters.push(newFilter);
}
});
return cleanFilters;
};
export {
getValidFilters,
getValidFiltersWithoutError,
deleteInvalidFilter,
otherDate,
getFormattedFilterOtherDate,
getFormattedFilter,
getFormattedFilters,
};

View File

@@ -0,0 +1,49 @@
import { FILTER_PREDICATE_TYPE } from '../../../constants/filter/filter-predicate';
/**
* Filter creator
* @param {string} email
* @param {string} filter_predicate
* @param {array} filter_term e.g. [ collaborator.email, ... ]
* @param {string} username
* @returns bool
*/
const creatorFilter = (email, { filter_predicate, filter_term }, username) => {
switch (filter_predicate) {
case FILTER_PREDICATE_TYPE.CONTAINS: {
if (!Array.isArray(filter_term)) {
return true;
}
if (!email) {
return false;
}
return filter_term.findIndex((filterEmail) => filterEmail === email) > -1;
}
case FILTER_PREDICATE_TYPE.NOT_CONTAIN: {
if (!Array.isArray(filter_term) || !email) {
return true;
}
return filter_term.findIndex((filterEmail) => filterEmail === email) < 0;
}
case FILTER_PREDICATE_TYPE.INCLUDE_ME: {
return email === username;
}
case FILTER_PREDICATE_TYPE.IS: {
if (!filter_term) return true;
if (!Array.isArray(filter_term)) return email === filter_term;
return email === filter_term[0];
}
case FILTER_PREDICATE_TYPE.IS_NOT: {
if (!filter_term) return true;
if (!Array.isArray(filter_term)) return email !== filter_term;
return email !== filter_term[0];
}
default: {
return false;
}
}
};
export {
creatorFilter,
};

View File

@@ -0,0 +1,95 @@
import { DateUtils } from '../../date';
import { FILTER_PREDICATE_TYPE } from '../../../constants/filter/filter-predicate';
import { FILTER_TERM_MODIFIER_TYPE } from '../../../constants/filter/filter-modifier';
/**
* Filter date
* @param {string} date
* @param {string} filter_predicate
* @param {string} filter_term_modifier
* @param {any} filter_term date string or number etc.
* @param {string|object} other_date date string or { startDate, endDate }
* @returns bool
*/
const dateFilter = (date, {
filter_predicate, filter_term_modifier, filter_term, other_date,
}) => {
switch (filter_predicate) {
case FILTER_PREDICATE_TYPE.IS: {
return (
(filter_term_modifier === FILTER_TERM_MODIFIER_TYPE.EXACT_DATE && !filter_term)
|| DateUtils.format(date) === other_date
);
}
case FILTER_PREDICATE_TYPE.IS_WITHIN: {
if (filter_term_modifier === FILTER_TERM_MODIFIER_TYPE.EXACT_DATE && !filter_term) {
return true;
}
if (!date) {
return false;
}
const { startDate, endDate } = other_date;
const currentDate = DateUtils.format(date);
return currentDate >= startDate && currentDate <= endDate;
}
case FILTER_PREDICATE_TYPE.IS_BEFORE: {
if (filter_term_modifier === FILTER_TERM_MODIFIER_TYPE.EXACT_DATE && !filter_term) {
return true;
}
if (!date || !DateUtils.getValidDate(date)) {
return false;
}
return DateUtils.format(date) < other_date;
}
case FILTER_PREDICATE_TYPE.IS_AFTER: {
if (filter_term_modifier === FILTER_TERM_MODIFIER_TYPE.EXACT_DATE && !filter_term) {
return true;
}
if (!date || !DateUtils.getValidDate(date)) {
return false;
}
return DateUtils.format(date) > other_date;
}
case FILTER_PREDICATE_TYPE.IS_ON_OR_BEFORE: {
if (filter_term_modifier === FILTER_TERM_MODIFIER_TYPE.EXACT_DATE && !filter_term) {
return true;
}
if (!date || !DateUtils.getValidDate(date)) {
return false;
}
return DateUtils.format(date) <= other_date;
}
case FILTER_PREDICATE_TYPE.IS_ON_OR_AFTER: {
if (filter_term_modifier === FILTER_TERM_MODIFIER_TYPE.EXACT_DATE && !filter_term) {
return true;
}
if (!date || !DateUtils.getValidDate(date)) {
return false;
}
return DateUtils.format(date) >= other_date;
}
case FILTER_PREDICATE_TYPE.IS_NOT: {
if (filter_term_modifier === FILTER_TERM_MODIFIER_TYPE.EXACT_DATE && !filter_term) {
return true;
}
if (!date || !DateUtils.getValidDate(date)) {
return false;
}
return DateUtils.format(date) !== other_date;
}
case FILTER_PREDICATE_TYPE.EMPTY: {
return !(date && DateUtils.getValidDate(date));
}
case FILTER_PREDICATE_TYPE.NOT_EMPTY: {
return !!(date && DateUtils.getValidDate(date));
}
default: {
return false;
}
}
};
export {
dateFilter,
};

View File

@@ -0,0 +1,3 @@
export { creatorFilter } from './creator';
export { dateFilter } from './date';
export { textFilter } from './text';

View File

@@ -0,0 +1,52 @@
import { FILTER_PREDICATE_TYPE } from '../../../constants/filter/filter-predicate';
/**
* Filter text
* @param {string} text
* @param {string} filter_predicate
* @param {string} filter_term
* @param {string} userId
* @returns bool
*/
const textFilter = (text, { filter_predicate, filter_term }, userId) => {
switch (filter_predicate) {
case FILTER_PREDICATE_TYPE.CONTAINS: {
if (!filter_term) {
return true;
}
if (!text) {
return false;
}
return text.toString().toLowerCase().indexOf(filter_term.toLowerCase()) > -1;
}
case FILTER_PREDICATE_TYPE.NOT_CONTAIN: {
if (!filter_term || !text) {
return true;
}
return text.toString().toLowerCase().indexOf(filter_term.toLowerCase()) < 0;
}
case FILTER_PREDICATE_TYPE.IS: {
return !filter_term || text === filter_term;
}
case FILTER_PREDICATE_TYPE.IS_NOT: {
return !filter_term || text !== filter_term;
}
case FILTER_PREDICATE_TYPE.EMPTY: {
return !text;
}
case FILTER_PREDICATE_TYPE.NOT_EMPTY: {
return !!text;
}
case FILTER_PREDICATE_TYPE.IS_CURRENT_USER_ID: {
if (!userId) return false;
return text === userId;
}
default: {
return false;
}
}
};
export {
textFilter,
};

View File

@@ -0,0 +1,86 @@
import {
getFormattedFilters,
} from './core';
import {
creatorFilter,
dateFilter,
textFilter,
} from './filter-column';
import {
FILTER_CONJUNCTION_TYPE,
} from '../../constants/filter';
import { DateUtils } from '../date';
import { CellType, DATE_FORMAT_MAP } from '../../constants/column';
const getFilterResult = (row, filter, { username, userId }) => {
const { column_key, column } = filter;
let cellValue = row[column_key];
switch (column.type) {
case CellType.CTIME:
case CellType.MTIME: {
cellValue = DateUtils.format(cellValue, DATE_FORMAT_MAP.YYYY_MM_DD_HH_MM_SS);
return dateFilter(cellValue, filter);
}
case CellType.TEXT: {
return textFilter(cellValue, filter, userId);
}
case CellType.LAST_MODIFIER:
case CellType.CREATOR: {
return creatorFilter(cellValue, filter, username);
}
default: {
return false;
}
}
};
/**
* Filter row
* @param {object} row e.g. { _id, .... }
* @param {string} filterConjunction e.g. 'And' | 'Or'
* @param {array} filters e.g. [{ column_key, filter_predicate, ... }, ...]
* @param {object} formulaRow
* @param {string} username
* @param {string} userId
* @param {object} userDepartmentIdsMap e.g. { current_user_department_ids: [8, 10], current_user_department_and_sub_ids: [8, 10, 12, 34] }
* @returns filter result, bool
*/
const filterRow = (row, filterConjunction, filters, { username = '', userId } = {}) => {
if (filterConjunction === FILTER_CONJUNCTION_TYPE.AND) {
return filters.every((filter) => (
getFilterResult(row, filter, { username, userId })
));
}
if (filterConjunction === FILTER_CONJUNCTION_TYPE.OR) {
return filters.some((filter) => (
getFilterResult(row, filter, { username, userId })
));
}
return false;
};
/**
* Filter rows
* @param {string} filterConjunction e.g. 'And' | 'Or'
* @param {array} filters e.g. [{ column_key, filter_predicate, ... }, ...]
* @param {array} rows e.g. [{ _id, .... }, ...]
* @param {string} username
* @param {string} userId
* @returns filtered rows ids, array
*/
const filterRows = (filterConjunction, filters, rows, { username, userId }) => {
let filteredRows = [];
const formattedFilters = getFormattedFilters(filters);
rows.forEach((row) => {
const rowId = row._id;
if (filterRow(row, filterConjunction, formattedFilters, { username, userId })) {
filteredRows.push(rowId);
}
});
return filteredRows;
};
export {
filterRow,
filterRows,
};

View File

@@ -0,0 +1,20 @@
export {
getValidFilters,
getValidFiltersWithoutError,
deleteInvalidFilter,
otherDate,
getFormattedFilterOtherDate,
getFormattedFilter,
getFormattedFilters,
} from './core';
export {
creatorFilter,
dateFilter,
textFilter,
} from './filter-column';
export {
filterRow,
filterRows,
} from './filter-row';

View File

@@ -0,0 +1,74 @@
import { CellType } from '../../constants/column';
import {
GROUP_DATE_GRANULARITY,
SUPPORT_GROUP_COLUMN_TYPES,
} from '../../constants/group';
/**
* Check is valid groupby
* @param {object} groupby e.g. { column_key, count_type, sort_type, ... }
* @param {array} columns
* @returns bool
*/
const isValidGroupby = (groupby, columns) => {
if (!groupby || !Array.isArray(columns)) return false;
const { column_key } = groupby;
const groupbyColumn = columns.find((column) => column.key === column_key);
if (!groupbyColumn) {
return false;
}
return SUPPORT_GROUP_COLUMN_TYPES.includes(groupbyColumn.type);
};
/**
* Get valid groupbys
* @param {array} groupbys e.g. [{ column_key, count_type, ... }, ...]
* @param {array} columns
* @returns valid groupbys, array
*/
const getValidGroupbys = (groupbys, columns) => {
if (!Array.isArray(groupbys) || !Array.isArray(columns)) {
return [];
}
return groupbys.filter((groupby) => isValidGroupby(groupby, columns));
};
/**
* Get valid and formatted groupbys
* @param {array} groupbys e.g. [{ column_key, count_type, ... }, ...]
* @param {array} columns
* @param {object} currentTable e.g. { _id, ... }
* @param {object} value e.g. { tables, collaborators }
* @returns valid and formatted groupbys
*/
const deleteInvalidGroupby = (groupbys, columns) => {
const validGroupbys = getValidGroupbys(groupbys, columns);
let cleanGroupbys = [];
validGroupbys.forEach((groupby) => {
const { column_key: groupbyColumnKey, count_type } = groupby;
const groupbyColumn = columns.find((column) => groupbyColumnKey === column.key);
const { type: columnType } = groupbyColumn;
let newGroupby = { ...groupby, column: groupbyColumn };
switch (columnType) {
case CellType.CTIME:
case CellType.MTIME: {
newGroupby.count_type = count_type || GROUP_DATE_GRANULARITY.MONTH;
break;
}
default: {
break;
}
}
cleanGroupbys.push(newGroupby);
});
return cleanGroupbys;
};
export {
deleteInvalidGroupby,
isValidGroupby,
getValidGroupbys,
};

View File

@@ -0,0 +1,250 @@
import { getRowsByIds } from '../table/row';
import { DateUtils } from '../date';
import {
sortDate,
sortText,
} from '../sort/sort-column';
import { MAX_GROUP_LEVEL } from '../../constants/group';
import {
CellType,
DATE_COLUMN_OPTIONS,
MULTIPLE_CELL_VALUE_COLUMN_TYPE_MAP,
SINGLE_CELL_VALUE_COLUMN_TYPE_MAP,
} from '../../constants/column';
import {
SORT_COLUMN_OPTIONS,
SORT_TYPE,
TEXT_SORTER_COLUMN_TYPES,
} from '../../constants/sort';
const _getCellValue = (row, groupby) => {
const { column_key } = groupby;
let cellValue = row[column_key];
return cellValue;
};
const _getFormattedCellValue = (cellValue, groupby) => {
const { column, count_type: countType } = groupby;
const { type: columnType } = column;
switch (columnType) {
case CellType.TEXT:
case CellType.LAST_MODIFIER:
case CellType.CREATOR: {
return cellValue || null;
}
case CellType.CTIME:
case CellType.MTIME: {
return DateUtils.getDateByGranularity(cellValue, countType) || null;
}
default: {
return null;
}
}
};
const _getStrCellValue = (cellValue, columnType) => {
let sCellValue = null;
if (SINGLE_CELL_VALUE_COLUMN_TYPE_MAP[columnType]) {
sCellValue = typeof cellValue === 'string' ? cellValue : String(cellValue);
} else if (MULTIPLE_CELL_VALUE_COLUMN_TYPE_MAP[columnType]) {
sCellValue = [...cellValue].sort().toString();
}
return sCellValue;
};
const _findGroupIndexWithMultipleGroupbys = (sCellValue, cellValue2GroupIndexMap, groupsLength) => {
const target = cellValue2GroupIndexMap[sCellValue];
if (target && target.index > -1) {
return target.index;
}
// eslint-disable-next-line
cellValue2GroupIndexMap[sCellValue] = {};
// eslint-disable-next-line
cellValue2GroupIndexMap[sCellValue].subgroups = {};
// eslint-disable-next-line
cellValue2GroupIndexMap[sCellValue].index = groupsLength;
return -1;
};
const _findGroupIndex = (sCellValue, cellValue2GroupIndexMap, groupsLength) => {
const index = cellValue2GroupIndexMap[sCellValue];
if (index > -1) {
return index;
}
// eslint-disable-next-line
cellValue2GroupIndexMap[sCellValue] = groupsLength;
return -1;
};
const getSortedGroups = (groups, groupbys, level) => {
const sortFlag = 0;
const { column, sort_type } = groupbys[level];
const { type: columnType } = column;
const normalizedSortType = sort_type || SORT_TYPE.UP;
groups.sort((currGroupRow, nextGroupRow) => {
let { cell_value: currCellVal } = currGroupRow;
let { cell_value: nextCellVal } = nextGroupRow;
if (SORT_COLUMN_OPTIONS.includes(columnType)) {
let sortResult;
if (TEXT_SORTER_COLUMN_TYPES.includes(columnType)) {
sortResult = sortText(currCellVal, nextCellVal, normalizedSortType);
} else if (DATE_COLUMN_OPTIONS.includes(columnType)) {
sortResult = sortDate(currCellVal, nextCellVal, normalizedSortType);
}
return sortFlag || sortResult;
}
if (currCellVal === '') return 1;
if (nextCellVal === '') return -1;
return 0;
});
// for nested group.
const isNestedGroup = Array.isArray(groups[0].subgroups) && groups[0].subgroups.length > 0;
if (isNestedGroup) {
const nextLevel = level + 1;
// eslint-disable-next-line
groups = groups.map((group) => {
const sortedSubgroups = getSortedGroups(group.subgroups, groupbys, nextLevel);
return {
...group,
subgroups: sortedSubgroups,
};
});
}
return groups;
};
const groupRowsWithMultipleGroupbys = (groupbys, rows, value) => {
const validGroupbys = groupbys.length > MAX_GROUP_LEVEL
? groupbys.slice(0, MAX_GROUP_LEVEL)
: [...groupbys];
let groups = [];
let cellValue2GroupIndexMap = {};
rows.forEach((row) => {
const rowId = row._id;
let updatedGroup;
let updateCellValue2GroupIndexMap;
for (let level = 0; level < validGroupbys.length; level++) {
const currentGroupby = validGroupbys[level];
const { column, column_key } = currentGroupby;
const { type: columnType } = column;
const cellValue = _getCellValue(row, currentGroupby);
const formattedValue = _getFormattedCellValue(cellValue, currentGroupby);
const sCellValue = _getStrCellValue(formattedValue, columnType);
const group = {
cell_value: formattedValue,
original_cell_value: cellValue,
row_ids: null,
column_key,
subgroups: [],
summaries: {},
};
if (level === 0) {
let groupedRowIndex = _findGroupIndexWithMultipleGroupbys(sCellValue, cellValue2GroupIndexMap, groups.length);
updateCellValue2GroupIndexMap = cellValue2GroupIndexMap[sCellValue].subgroups;
if (groupedRowIndex < 0) {
groups.push(group);
updatedGroup = groups[groups.length - 1];
} else {
updatedGroup = groups[groupedRowIndex];
}
} else {
let groupedRowIndex = _findGroupIndexWithMultipleGroupbys(sCellValue, updateCellValue2GroupIndexMap, updatedGroup.subgroups.length);
updateCellValue2GroupIndexMap = updateCellValue2GroupIndexMap[sCellValue].subgroups;
if (groupedRowIndex < 0) {
updatedGroup.subgroups.push(group);
updatedGroup = updatedGroup.subgroups[updatedGroup.subgroups.length - 1];
} else {
updatedGroup = updatedGroup.subgroups[groupedRowIndex];
}
// update row_ids in the deepest group.
if (level === validGroupbys.length - 1) {
if (!updatedGroup.row_ids) {
updatedGroup.row_ids = [rowId];
} else {
updatedGroup.row_ids.push(rowId);
}
}
}
}
});
groups = getSortedGroups(groups, validGroupbys, value, 0);
return groups;
};
/**
* Group table rows
* @param {array} groupbys e.g. [{ column_key, count_type, column, ... }, ...]
* @param {array} rows e.g. [{ _id, ... }, ...]
* @param {object} value e.g. { collaborators, ... }
* @returns groups: [{
* cell_value, original_cell_value, column_key,
row_ids, subgroups, summaries, ...}, ...], array
*/
const groupTableRows = (groupbys, rows) => {
if (groupbys.length === 0) {
return [];
}
if (groupbys.length > 1) {
return groupRowsWithMultipleGroupbys(groupbys, rows);
}
const groupby = groupbys[0];
const { column_key, column } = groupby;
const { type: columnType } = column;
let groups = [];
let cellValue2GroupIndexMap = {};
rows.forEach((r) => {
const cellValue = _getCellValue(r, groupby);
const formattedValue = _getFormattedCellValue(cellValue, groupby);
const sCellValue = _getStrCellValue(formattedValue, columnType);
let groupedRowIndex = _findGroupIndex(sCellValue, cellValue2GroupIndexMap, groups.length);
if (groupedRowIndex > -1) {
groups[groupedRowIndex].row_ids.push(r._id);
} else {
groups.push({
cell_value: formattedValue,
original_cell_value: cellValue,
column_key,
row_ids: [r._id],
subgroups: null,
summaries: {},
});
}
});
// sort groups
groups = getSortedGroups(groups, groupbys, 0);
return groups;
};
/**
* Group view rows
* @param {array} groupbys e.g. [{ column_key, count_type, column, ... }, ...]
* @param {object} table e.g. { id_row_map, ... }
* @param {array} rowsIds e.g. [ row._id, ...]
* @param {object} value e.g. { collaborators, ... }
* @returns groups: [{
* cell_value, original_cell_value, column_key,
row_ids, subgroups, summaries, ...}, ...], array
*/
const groupViewRows = (groupbys, table, rowsIds) => {
if (rowsIds.length === 0) {
return [];
}
let rowsData = getRowsByIds(table, rowsIds);
return groupTableRows(groupbys, rowsData);
};
export {
groupTableRows,
groupViewRows,
};

View File

@@ -0,0 +1,10 @@
export {
deleteInvalidGroupby,
isValidGroupby,
getValidGroupbys,
} from './core';
export {
groupTableRows,
groupViewRows,
} from './group-row';

View File

@@ -0,0 +1,27 @@
import isHotkey from 'is-hotkey';
export const isModS = isHotkey('mod+s');
export const isModZ = isHotkey('mod+z');
export const isModL = isHotkey('mod+l');
export const isModF = isHotkey('mod+f');
export const isModP = isHotkey('mod+p');
export const isModG = isHotkey('mod+g');
export const isModDot = isHotkey('mod+.');
export const isModComma = isHotkey('mod+,');
export const isModSlash = isHotkey('mod+/');
export const isModBackslash = isHotkey('mod+\'');
export const isModSemicolon = isHotkey('mod+;');
export const isModUp = isHotkey('mod+up');
export const isModDown = isHotkey('mod+down');
export const isModLeft = isHotkey('mod+left');
export const isModRight = isHotkey('mod+right');
export const isModShiftZ = isHotkey('mod+shift+z');
export const isModShiftG = isHotkey('mod+shift+g');
export const isModShiftDot = isHotkey('mod+shift+.');
export const isModShiftComma = isHotkey('mod+shift+,');
export const isShiftEnter = isHotkey('shift+enter');
export const isShiftModEnter = isHotkey('shift+mod+enter');
export const isOptPageUp = isHotkey('opt+pageup');
export const isOptPageDown = isHotkey('opt+pagedown');
export const isSpace = isHotkey('space');
export const isEnter = isHotkey('enter');

View File

@@ -0,0 +1,88 @@
import * as CommonlyUsedHotkey from './hotkey';
import LocalStorage from './local-storage';
export {
getColumnType,
getColumnsByType,
isDateColumn,
isSupportDateColumnFormat,
} from './column';
export {
getValidFilters,
getValidFiltersWithoutError,
deleteInvalidFilter,
otherDate,
getFormattedFilterOtherDate,
getFormattedFilter,
getFormattedFilters,
creatorFilter,
dateFilter,
textFilter,
filterRow,
filterRows,
} from './filter';
export {
deleteInvalidGroupby,
isValidGroupby,
getValidGroupbys,
groupTableRows,
groupViewRows,
} from './group';
export {
isTableRows,
updateTableRowsWithRowsData,
} from './row';
export {
isValidSort,
getValidSorts,
deleteInvalidSort,
getMultipleIndexesOrderbyOptions,
sortDate,
sortText,
sortRowsWithMultiSorts,
sortTableRows,
} from './sort';
export {
getTableById,
getTableByName,
getTableByIndex,
getTableColumnByKey,
getTableColumnByName,
getRowById,
getRowsByIds,
} from './table';
export {
isValidEmail,
ValidateFilter,
DATE_MODIFIERS_REQUIRE_TERM,
} from './validate';
export {
getViewById,
getViewByName,
isDefaultView,
isFilterView,
isGroupView,
isSortView,
isHiddenColumnsView,
getViewShownColumns,
getGroupByPath,
} from './view';
export {
getType,
isMac,
base64ToFile,
bytesToSize,
getErrorMsg,
isFunction,
isEmpty,
isEmptyObject,
debounce,
throttle,
} from './common';
export {
DateUtils
} from './date';
export {
CommonlyUsedHotkey,
LocalStorage,
};

View File

@@ -0,0 +1,28 @@
class LocalStorage {
constructor(baseName) {
this.baseName = baseName || 'sf-metadata';
}
getStorage() {
try {
return JSON.parse(window.localStorage.getItem(this.baseName) || '{}');
} catch (error) {
return '';
}
}
setItem(key, value) {
const storage = this.getStorage();
const newValue = { ...storage, [key]: value };
return window.localStorage.setItem(this.baseName, JSON.stringify(newValue));
}
getItem(key) {
const storage = this.getStorage();
return storage[key];
}
}
export default LocalStorage;

View File

@@ -0,0 +1,31 @@
import { getTableById } from '../table/core';
/**
* Check is table rows
* @param {array} rows e.g. table rows: [{ _id, xxx }, ...] | view rows: [ row._id, ... ]
* @returns bool
*/
const isTableRows = (rows) => (
Array.isArray(rows) && typeof rows[0] === 'object'
);
const updateTableRowsWithRowsData = (tables, tableId, rowsData = []) => {
let table = getTableById(tables, tableId);
let idRowDataMap = {};
rowsData.forEach((rowData) => idRowDataMap[rowData._id] = rowData);
table.rows.forEach((row, index) => {
const rowId = row._id;
const newRowData = idRowDataMap[rowId];
if (!newRowData) {
return;
}
const newRow = Object.assign({}, row, newRowData);
table.rows[index] = newRow;
table.id_row_map[rowId] = newRow;
});
};
export {
isTableRows,
updateTableRowsWithRowsData,
};

View File

@@ -0,0 +1,4 @@
export {
isTableRows,
updateTableRowsWithRowsData,
} from './core';

View File

@@ -0,0 +1,70 @@
import { SORT_COLUMN_OPTIONS } from '../../constants/sort';
/**
* Check is valid sort
* @param {object} sort e.g. { column_key, sort_type, ... }
* @param {array} columns
* @returns bool
*/
const isValidSort = (sort, columns) => {
const sortByColumn = sort && columns.find((column) => column.key === sort.column_key);
if (!sortByColumn) return false;
return SORT_COLUMN_OPTIONS.includes(sortByColumn.type);
};
/**
* Get valid sorts
* 1. sort column is exist or not
* 2. valid sort type
* @param {array} sorts e.g. [{ column_key, sort_type, ... }, ...]
* @param {array} columns
* @returns valid sorts, array
*/
const getValidSorts = (sorts, columns) => {
if (!Array.isArray(sorts) || !Array.isArray(columns)) return [];
return sorts.filter((sort) => isValidSort(sort, columns));
};
/**
* Get sorted option index of the "optionIds"
* @param {array} optionIds
* @param {object} option_id_index_map e.g. {[option.id]: 0, ...}
* @returns sorted options index, array
*/
const getMultipleIndexesOrderbyOptions = (optionIds, option_id_index_map) => {
let indexArr = [];
optionIds.forEach((optionId) => {
const index = option_id_index_map[optionId];
if (index > -1) {
indexArr.push(index);
}
});
return indexArr.sort();
};
/**
* Get valid and formatted sorts
* @param {array} sorts e.g. [{ column_key, sort_type, ... }, ...]
* @param {array} columns
* @returns valid and formatted sorts, array
*/
const deleteInvalidSort = (sorts, columns) => {
const validSorts = getValidSorts(sorts, columns);
let cleanSorts = [];
validSorts.forEach((sort) => {
const { column_key } = sort;
const sortColumn = columns.find((column) => column.key === column_key);
let newSort = { ...sort, column: sortColumn };
cleanSorts.push(newSort);
});
return cleanSorts;
};
export {
isValidSort,
getValidSorts,
deleteInvalidSort,
getMultipleIndexesOrderbyOptions,
};

View File

@@ -0,0 +1,16 @@
export {
isValidSort,
getValidSorts,
deleteInvalidSort,
getMultipleIndexesOrderbyOptions,
} from './core';
export {
sortDate,
sortText,
} from './sort-column';
export {
sortRowsWithMultiSorts,
sortTableRows,
} from './sort-row';

View File

@@ -0,0 +1,30 @@
import { SORT_TYPE } from '../../../constants/sort';
/**
* Sort date
* @param {string} leftDate e.g. '2023-07-31'
* @param {string} nextDate
* @param {string} sortType e.g. 'up' | 'down'
* @returns number
*/
const sortDate = (leftDate, rightDate, sortType) => {
const emptyLeftDate = !leftDate;
const emptyRightDate = !rightDate;
if (emptyLeftDate && emptyRightDate) return 0;
if (emptyLeftDate) return 1;
if (emptyRightDate) return -1;
if (leftDate > rightDate) {
return sortType === SORT_TYPE.UP ? 1 : -1;
}
if (leftDate < rightDate) {
return sortType === SORT_TYPE.UP ? -1 : 1;
}
return 0;
};
export {
sortDate,
};

View File

@@ -0,0 +1,6 @@
export { sortDate } from './date';
export {
compareString,
sortText,
} from './text';

View File

@@ -0,0 +1,77 @@
import {
REG_NUMBER_DIGIT,
REG_STRING_NUMBER_PARTS,
} from '../../../constants/reg';
import { SORT_TYPE } from '../../../constants/sort';
/**
* Compare strings
* @param {string} leftString
* @param {string} rightString
* @returns number
*/
const compareString = (leftString, rightString) => {
if (!leftString && !rightString) return 0;
if (!leftString) return -1;
if (!rightString) return 1;
if (typeof leftString !== 'string' || typeof rightString !== 'string') return 0;
let leftStringParts = leftString.match(REG_STRING_NUMBER_PARTS);
let rightStringParts = rightString.match(REG_STRING_NUMBER_PARTS);
let len = Math.min(leftStringParts.length, rightStringParts.length);
let isDigitPart;
let leftStringPart;
let rightStringPart;
// Loop through each substring part to canCompare the overall strings.
for (let i = 0; i < len; i++) {
leftStringPart = leftStringParts[i];
rightStringPart = rightStringParts[i];
isDigitPart = REG_NUMBER_DIGIT.test(leftStringPart) && REG_NUMBER_DIGIT.test(rightStringPart);
if (isDigitPart) {
leftStringPart = parseInt(leftStringPart);
rightStringPart = parseInt(rightStringPart);
if (leftStringPart > rightStringPart) {
return 1;
}
if (leftStringPart < rightStringPart) {
return -1;
}
}
if (leftStringPart !== rightStringPart) {
return leftString.localeCompare(rightString);
}
}
return leftString.localeCompare(rightString);
};
/**
* Sort text
* @param {string} leftText
* @param {string} rightText
* @param {string} sortType e.g. 'up' | 'down
* @returns number
*/
const sortText = (leftText, rightText, sortType) => {
const emptyLeftText = !leftText;
const emptyRightText = !rightText;
if (emptyLeftText && emptyRightText) {
return 0;
}
if (emptyLeftText) {
return 1;
}
if (emptyRightText) {
return -1;
}
if (rightText === leftText) {
return 0;
}
return sortType === SORT_TYPE.UP ? compareString(leftText, rightText) : -1 * compareString(leftText, rightText);
};
export {
compareString,
sortText,
};

View File

@@ -0,0 +1,51 @@
import { deleteInvalidSort } from './core';
import {
sortDate,
sortText,
} from './sort-column';
import { DATE_COLUMN_OPTIONS } from '../../constants/column';
/**
* Sort rows with multiple sorts
* @param {array} tableRows e.g. [{ _id, [column.key]: '', ...}, ...]
* @param {array} sorts e.g. [{ column_key, sort_type, column, ... }, ...]
* @param {object} value e.g. { collaborators, ... }
*/
const sortRowsWithMultiSorts = (tableRows, sorts) => {
tableRows.sort((currentRow, nextRow) => {
let initValue = 0;
sorts.forEach((sort) => {
const { column_key, sort_type, column } = sort;
const { type: columnType } = column;
let currCellVal = currentRow[column_key];
let nextCellVal = nextRow[column_key];
if (DATE_COLUMN_OPTIONS.includes(columnType)) {
initValue = initValue || sortDate(currCellVal, nextCellVal, sort_type);
} else {
initValue = initValue || sortText(currCellVal, nextCellVal, sort_type);
}
});
return initValue;
});
};
/**
* Get sorted rows ids from table rows with multiple sorts
* @param {array} sorts e.g. [{ column_key, sort_type, column, ... }, ...]
* @param {array} rows e.g. [{ _id, [column.key]: '', ...}, ...]
* @param {array} columns e.g. [{ key, type, ... }, ...]
* @param {object} value e.g. { collaborators, ... }
* @returns sorted rows ids, array
*/
const sortTableRows = (sorts, rows, columns) => {
if (!Array.isArray(rows) || rows.length === 0) return [];
const sortRows = rows.slice(0);
const validSorts = deleteInvalidSort(sorts, columns);
sortRowsWithMultiSorts(sortRows, validSorts);
return sortRows.map((row) => row._id);
};
export {
sortRowsWithMultiSorts,
sortTableRows,
};

View File

@@ -0,0 +1,26 @@
/**
* Get column by key from table
* @param {object} table
* @param {string} columnKey
* @returns column, object
*/
const getTableColumnByKey = (table, columnKey) => {
if (!table || !Array.isArray(table.columns) || !columnKey) return null;
return table.columns.find((column) => column.key === columnKey);
};
/**
* Get table column by name
* @param {object} table
* @param {string} columnName
* @returns column, object
*/
const getTableColumnByName = (table, columnName) => {
if (!table || !Array.isArray(table.columns) || !columnName) return null;
return table.columns.find((column) => column.name === columnName);
};
export {
getTableColumnByKey,
getTableColumnByName,
};

View File

@@ -0,0 +1,32 @@
/**
* Get table by id
* @param {array} tables
* @param {string} tableId
* @returns table, object
*/
const getTableById = (tables, tableId) => {
if (!Array.isArray(tables) || !tableId) return null;
return tables.find((table) => table._id === tableId);
};
/**
* Get table by name
* @param {array} tables
* @param {string} tableName
* @returns table, object
*/
const getTableByName = (tables, tableName) => {
if (!Array.isArray(tables) || !tableName) return null;
return tables.find((table) => table.name === tableName);
};
const getTableByIndex = (tables, tableIndex) => {
if (!Array.isArray(tables) || tableIndex < 0) return null;
return tables[tableIndex];
};
export {
getTableById,
getTableByName,
getTableByIndex,
};

View File

@@ -0,0 +1,5 @@
export {
getTableById, getTableByName, getTableByIndex,
} from './core';
export { getTableColumnByKey, getTableColumnByName } from './column';
export { getRowById, getRowsByIds } from './row';

View File

@@ -0,0 +1,26 @@
/**
* Get table row by id
* @param {object} table
* @param {string} rowId the id of row
* @returns row, object
*/
const getRowById = (table, rowId) => {
if (!table || !table.id_row_map || !rowId) return null;
return table.id_row_map[rowId];
};
/**
* Get table rows by ids
* @param {object} table { id_row_map, ... }
* @param {array} rowsIds [ row._id, ... ]
* @returns rows, array
*/
const getRowsByIds = (table, rowsIds) => {
if (!table || !table.id_row_map || !Array.isArray(rowsIds)) return [];
return rowsIds.map((rowId) => table.id_row_map[rowId]).filter(Boolean);
};
export {
getRowById,
getRowsByIds,
};

View File

@@ -0,0 +1,10 @@
/**
* Check email format is valid.
* @param {string} email
* @returns true/false, bool
*/
const isValidEmail = (email) => (
/^[A-Za-z0-9]+([-_.][A-Za-z0-9]+)*@([A-Za-z0-9]+[-.])+[A-Za-z0-9]{2,20}$/.test(email)
);
export { isValidEmail };

View File

@@ -0,0 +1,297 @@
import { CellType, COLLABORATOR_COLUMN_TYPES } from '../../constants/column';
import {
FILTER_COLUMN_OPTIONS,
FILTER_TERM_MODIFIER_TYPE,
FILTER_PREDICATE_TYPE,
filterTermModifierIsWithin,
filterTermModifierNotWithin,
FILTER_ERR_MSG,
} from '../../constants/filter';
import { isDateColumn } from '../column/date';
const TERM_TYPE_MAP = {
NUMBER: 'number',
STRING: 'string',
BOOLEAN: 'boolean',
ARRAY: 'array',
};
const TEXT_COLUMN_TYPES = [CellType.TEXT, CellType.STRING];
const CHECK_EMPTY_PREDICATES = [FILTER_PREDICATE_TYPE.EMPTY, FILTER_PREDICATE_TYPE.NOT_EMPTY];
const DATE_MODIFIERS_REQUIRE_TERM = [
FILTER_TERM_MODIFIER_TYPE.NUMBER_OF_DAYS_AGO,
FILTER_TERM_MODIFIER_TYPE.NUMBER_OF_DAYS_FROM_NOW,
FILTER_TERM_MODIFIER_TYPE.THE_NEXT_NUMBERS_OF_DAYS,
FILTER_TERM_MODIFIER_TYPE.THE_PAST_NUMBERS_OF_DAYS,
FILTER_TERM_MODIFIER_TYPE.EXACT_DATE,
];
const MODIFIERS_REQUIRE_NUMERIC_TERM = [
FILTER_TERM_MODIFIER_TYPE.NUMBER_OF_DAYS_AGO,
FILTER_TERM_MODIFIER_TYPE.NUMBER_OF_DAYS_FROM_NOW,
FILTER_TERM_MODIFIER_TYPE.THE_NEXT_NUMBERS_OF_DAYS,
FILTER_TERM_MODIFIER_TYPE.THE_PAST_NUMBERS_OF_DAYS,
];
class ValidateFilter {
/**
* Check filter is valid. The error_message from returns will be null if the filter is valid.
* 1.incomplete filter which should be ignored
* - column_key: required
* - filter_predicate: required
* - filter_term_modifier: determined by the column to filter with
* - filter_term: determined by filter_predicate / the column to filter with
* 2.illegal filter
* - column missing: cannot find the column to filter
* - column not support: the column to filter is not support
* - mismatch: filter_predicate, filter_term_modifier mismatch
* - wrong data type: filter_term with wrong data type
* @param {object} filter e.g. { column_key, filter_term, ... }
* @param {array} columns e.g. [{ key, name, ... }, ...]
* @param {bool} isValidTerm No longer to validate filter term if false. default as false
* @returns { error_message }, object
*/
static validate(filter, columns, isValidTerm = true) {
const {
column_key, filter_predicate, filter_term_modifier, filter_term,
} = filter;
const { error_message: column_error_message } = this.validateColumn(column_key, columns);
if (column_error_message) {
return { error_message: column_error_message };
}
const filterColumn = columns.find((column) => column.key === column_key);
const {
error_message: predicate_error_message,
} = this.validatePredicate(filter_predicate, filterColumn);
if (predicate_error_message) {
return { error_message: predicate_error_message };
}
if (this.isFilterOnlyWithPredicate(filter_predicate, filterColumn)) {
return { error_message: null };
}
const {
error_message: modifier_error_message,
} = this.validateModifier(filter_term_modifier, filter_predicate, filterColumn);
if (modifier_error_message) {
return { error_message: modifier_error_message };
}
if (this.isFilterOnlyWithModifier(filter_term_modifier, filterColumn)) {
return { error_message: null };
}
if (isValidTerm) {
const {
error_message: term_error_message,
} = this.validateTerm(filter_term, filter_predicate, filter_term_modifier, filterColumn);
if (term_error_message) {
return { error_message: term_error_message };
}
}
return { error_message: null };
}
static validateColumn(column_key, columns) {
if (!column_key) {
return { error_message: FILTER_ERR_MSG.INCOMPLETE_FILTER };
}
const filterColumn = columns.find((column) => column.key === column_key);
if (!filterColumn) {
return { error_message: FILTER_ERR_MSG.COLUMN_MISSING };
}
if (!this.isValidColumnType(filterColumn)) {
return { error_message: FILTER_ERR_MSG.COLUMN_NOT_SUPPORTED };
}
return { error_message: null };
}
/**
* the column to filter must be available
*/
static validatePredicate(predicate, filterColumn) {
if (!predicate) {
return { error_message: FILTER_ERR_MSG.INCOMPLETE_FILTER };
}
const { type: columnType } = filterColumn;
const filterConfigs = FILTER_COLUMN_OPTIONS[columnType];
const { filterPredicateList: predicateList } = filterConfigs;
if (!predicateList.includes(predicate)) {
return { error_message: FILTER_ERR_MSG.UNMATCHED_PREDICATE };
}
return { error_message: null };
}
static validatePredicateWithArrayType(predicate, filterColumn) {
const { data } = filterColumn;
const { array_type } = data;
// Only support: is
if (array_type === CellType.CHECKBOX || array_type === CellType.BOOL) {
return this.validatePredicate(predicate, { type: CellType.CHECKBOX });
}
// Filter predicate should support: is_empty/is_not_empty(excludes checkbox and bool)
if (CHECK_EMPTY_PREDICATES.includes(predicate)) {
return true;
}
if (array_type === CellType.SINGLE_SELECT || array_type === CellType.DEPARTMENT_SINGLE_SELECT) {
return this.validatePredicate(predicate, { type: CellType.MULTIPLE_SELECT });
}
if (COLLABORATOR_COLUMN_TYPES.includes(array_type)) {
return this.validatePredicate(predicate, { type: CellType.COLLABORATOR });
}
return this.validatePredicate(predicate, { type: array_type });
}
/**
* filter predicate must be available.
* filterColumn the column to filter must be available
*/
static isFilterOnlyWithPredicate(predicate, filterColumn) {
if (CHECK_EMPTY_PREDICATES.includes(predicate)) {
return true;
}
const { type: columnType } = filterColumn;
const { IS_CURRENT_USER_ID, INCLUDE_ME } = FILTER_PREDICATE_TYPE;
if (predicate === IS_CURRENT_USER_ID && TEXT_COLUMN_TYPES.includes(columnType)) {
return true;
}
if (predicate === INCLUDE_ME && COLLABORATOR_COLUMN_TYPES.includes(columnType)) {
return true;
}
return false;
}
/**
* filter predicate must be available.
* the column to filter must be available
*/
static validateModifier(modifier, predicate, filterColumn) {
if (!isDateColumn(filterColumn)) {
return { error_message: null };
}
if (!modifier) {
return { error_message: FILTER_ERR_MSG.INCOMPLETE_FILTER };
}
if (predicate === FILTER_PREDICATE_TYPE.IS_WITHIN) {
if (filterTermModifierIsWithin.includes(modifier)) {
return { error_message: null };
}
} else if (filterTermModifierNotWithin.includes(modifier)) {
return { error_message: null };
}
return { error_message: FILTER_ERR_MSG.UNMATCHED_MODIFIER };
}
/**
* filter predicate must be available.
* filter modifier must be available.
* the column to filter must be available
*/
static isFilterOnlyWithModifier(modifier, filterColumn) {
if (isDateColumn(filterColumn)) {
return !DATE_MODIFIERS_REQUIRE_TERM.includes(modifier);
}
return false;
}
static validateTerm(term, predicate, modifier, filterColumn) {
if (this.isTermMissing(term)) {
return { error_message: FILTER_ERR_MSG.INCOMPLETE_FILTER };
}
if (!this.isValidTerm(term, predicate, modifier, filterColumn)) {
return { error_message: FILTER_ERR_MSG.INVALID_TERM };
}
return { error_message: null };
}
static isTermMissing(term) {
return (!term && term !== 0 && term !== false)
|| (Array.isArray(term) && term.length === 0);
}
static isValidTerm(term, predicate, modifier, filterColumn) {
switch (filterColumn.type) {
case CellType.TEXT: {
return this.isValidTermType(term, TERM_TYPE_MAP.STRING);
}
case CellType.CHECKBOX:
case CellType.BOOL: {
return this.isValidTermType(term, TERM_TYPE_MAP.BOOLEAN);
}
case CellType.COLLABORATOR:
case CellType.CREATOR:
case CellType.LAST_MODIFIER: {
return this.isValidTermType(term, TERM_TYPE_MAP.ARRAY);
}
case CellType.DATE:
case CellType.CTIME:
case CellType.MTIME: {
if (MODIFIERS_REQUIRE_NUMERIC_TERM.includes(modifier)) {
return this.isValidTermType(term, TERM_TYPE_MAP.NUMBER);
}
return this.isValidTermType(term, TERM_TYPE_MAP.STRING);
}
default: {
return false;
}
}
}
static isValidTermType(term, type) {
if (type === TERM_TYPE_MAP.ARRAY) {
return Array.isArray(term) && term.length > 0;
}
if (type === CellType.NUMBER) {
// is a number or a number string
// eslint-disable-next-line
return typeof term === type || !isNaN(Number(term));
}
// eslint-disable-next-line
return typeof term === type;
}
static isValidTermWithArrayType(term, predicate, modifier, filterColumn) {
const { data } = filterColumn;
const { array_type, array_data } = data;
if (array_type === CellType.SINGLE_SELECT) {
return this.isValidTerm(term, predicate, modifier, {
type: CellType.MULTIPLE_SELECT, data: array_data,
});
}
if (array_type === CellType.DEPARTMENT_SINGLE_SELECT) {
return this.isValidTermType(term, TERM_TYPE_MAP.ARRAY);
}
if (COLLABORATOR_COLUMN_TYPES.includes(array_type)) {
return this.isValidTerm(term, predicate, modifier, { type: CellType.COLLABORATOR });
}
return this.isValidTerm(term, predicate, modifier, { type: array_type, data: array_data });
}
static isValidColumnType(filterColumn) {
const { type: columnType } = filterColumn;
// eslint-disable-next-line
return FILTER_COLUMN_OPTIONS.hasOwnProperty(columnType);
}
static isValidSelectedOptions(selectedOptionIds, options) {
const validSelectedOptions = options.filter((option) => selectedOptionIds.includes(option.id));
return selectedOptionIds.length === validSelectedOptions.length;
}
}
export {
ValidateFilter,
DATE_MODIFIERS_REQUIRE_TERM,
};

View File

@@ -0,0 +1,5 @@
export { isValidEmail } from './email';
export {
ValidateFilter,
DATE_MODIFIERS_REQUIRE_TERM,
} from './filter';

View File

@@ -0,0 +1,98 @@
import {
getValidFilters,
} from '../filter/core';
import { getValidGroupbys } from '../group/core';
import { getValidSorts } from '../sort/core';
/**
* Get view by id
* @param {array} views e.g. [{ _id, ... }, ...]
* @param {string} viewId
* @returns view, object
*/
const getViewById = (views, viewId) => {
if (!Array.isArray(views) || !viewId) return null;
return views.find((view) => view._id === viewId);
};
/**
* Get view by name
* @param {array} views
* @param {string} viewName
* @returns view, object
*/
const getViewByName = (views, viewName) => {
if (!Array.isArray(views) || !viewName) return null;
return views.find((view) => view.name === viewName);
};
/**
* Check whether the view contains filters
* @param {object} view e.g. { filters, ... }
* @param {array} columns
* @returns bool
*/
const isFilterView = (view, columns) => {
const validFilters = getValidFilters(view.filters, columns);
return validFilters.length > 0;
};
/**
* Check whether the view contains groupbys
* @param {object} view e.g. { groupbys, ... }
* @param {array} columns
* @returns bool
*/
const isGroupView = (view, columns) => {
const validGroupbys = getValidGroupbys(view.groupbys, columns);
return validGroupbys.length > 0;
};
/**
* Check whether the view contains sorts
* @param {object} view e.g. { sorts, ... }
* @param {array} columns
* @returns bool
*/
const isSortView = (view, columns) => {
const validSorts = getValidSorts(view.sorts, columns);
return validSorts.length > 0;
};
/**
* Check whether the view has hidden columns
* @param {object} view e.g. { hidden_columns, ... }
* @returns bool
*/
const isHiddenColumnsView = (view) => {
const { hidden_columns } = view || {};
return Array.isArray(hidden_columns) && hidden_columns.length > 0;
};
/**
* Check is default view which no contains filters, sorts, groupbys etc.
* @param {object} view e.g. { filters, groupbys, sorts, ... }
* @param {array} columns
* @returns bool
*/
const isDefaultView = (view, columns) => (
!isFilterView(view, columns) && !isSortView(view, columns) && !isGroupView(view, columns)
);
const getViewShownColumns = (view, columns) => {
if (!Array.isArray(columns)) return [];
if (!isHiddenColumnsView(view)) return columns;
const { hidden_columns } = view;
return columns.filter((column) => !hidden_columns.includes(column.key));
};
export {
getViewById,
getViewByName,
isDefaultView,
isFilterView,
isGroupView,
isSortView,
isHiddenColumnsView,
getViewShownColumns,
};

View File

@@ -0,0 +1,39 @@
/**
* Get group by paths
* @param {array} paths e.g. [ 0, 1, 2 ]
* @param {array} groups grouped rows
* @returns group, object
*/
const getGroupByPath = (paths, groups) => {
if (!Array.isArray(paths) || !Array.isArray(groups)) {
return null;
}
const level0GroupIndex = paths[0];
if (level0GroupIndex < 0 || level0GroupIndex >= groups.length) {
return null;
}
let level = 1;
let foundGroup = groups[level0GroupIndex];
while (level < paths.length) {
if (!foundGroup) {
break;
}
const subGroups = foundGroup.subgroups;
const currentLevelGroupIndex = paths[level];
if (
!Array.isArray(subGroups)
|| (currentLevelGroupIndex < 0 || currentLevelGroupIndex >= subGroups.length)
) {
break;
}
foundGroup = subGroups[currentLevelGroupIndex];
level += 1;
}
return foundGroup;
};
export {
getGroupByPath,
};

View File

@@ -0,0 +1,14 @@
export {
getViewById,
getViewByName,
isDefaultView,
isFilterView,
isGroupView,
isSortView,
isHiddenColumnsView,
getViewShownColumns,
} from './core';
export {
getGroupByPath,
} from './group';

View File

@@ -0,0 +1,27 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Formatter } from '@seafile/sf-metadata-ui-component';
import { useCollaborators } from '../../hooks';
const CellFormatter = ({ readonly, value, field, }) => {
const { collaborators, collaboratorsCache, updateCollaboratorsCache } = useCollaborators();
return (
<Formatter
readonly={readonly}
value={value}
field={field}
collaborators={collaborators}
collaboratorsCache={collaboratorsCache}
updateCollaboratorsCache={updateCollaboratorsCache}
queryUserAPI={window.sfMetadataContext.userService.queryUser}
/>
);
};
CellFormatter.propTypes = {
readonly: PropTypes.bool,
value: PropTypes.any,
field: PropTypes.object.isRequired,
};
export default CellFormatter;

View File

@@ -0,0 +1,94 @@
import React from 'react';
import PropTypes from 'prop-types';
import deepCopy from 'deep-copy';
import { Icon } from '@seafile/sf-metadata-ui-component';
import { getValidFilters, CommonlyUsedHotkey } from '../../_basic';
import { gettext } from '../../../../utils/constants';
const propTypes = {
wrapperClass: PropTypes.string,
filtersClassName: PropTypes.string,
target: PropTypes.string,
isNeedSubmit: PropTypes.bool,
filterConjunction: PropTypes.string,
filters: PropTypes.array,
columns: PropTypes.array,
onFiltersChange: PropTypes.func,
collaborators: PropTypes.array,
isPre: PropTypes.bool,
};
class FilterSetter extends React.Component {
static defaultProps = {
target: 'sf-metadata-filter-popover',
isNeedSubmit: false,
};
constructor(props) {
super(props);
this.state = {
isShowFilterSetter: false,
};
}
onKeyDown = (e) => {
e.stopPropagation();
if (CommonlyUsedHotkey.isEnter(e) || CommonlyUsedHotkey.isSpace(e)) this.onFilterSetterToggle();
};
onFilterSetterToggle = () => {
this.setState({ isShowFilterSetter: !this.state.isShowFilterSetter });
};
update = (update) => {
const { filters, filter_conjunction } = update || {};
const { columns } = this.props;
const valid_filters = getValidFilters(filters, columns);
this.props.onFiltersChange(valid_filters, filter_conjunction);
};
render() {
const {
wrapperClass, filters, columns, isNeedSubmit,
// collaborators, filtersClassName, filterConjunction,
} = this.props;
if (!columns) return null;
// const { isShowFilterSetter } = this.state;
const validFilters = deepCopy(getValidFilters(filters || [], columns));
const filtersLength = validFilters ? validFilters.length : 0;
let filterMessage = isNeedSubmit ? gettext('Preset filter') : gettext('Filter');
if (filtersLength === 1) {
filterMessage = isNeedSubmit ? gettext('1 preset filter') : gettext('1 filter');
} else if (filtersLength > 1) {
filterMessage = isNeedSubmit ? gettext('Preset filters') : gettext('Filters');
filterMessage = filtersLength + ' ' + filterMessage;
}
let labelClass = wrapperClass || '';
labelClass = (labelClass && filtersLength > 0) ? labelClass + ' active' : labelClass;
return (
<>
<div className={`setting-item ${labelClass ? 'mr-2' : 'mb-1'}`}>
<div
className={`setting-item-btn filters-setting-btn ${labelClass}`}
onClick={this.onFilterSetterToggle}
role="button"
onKeyDown={this.onKeyDown}
title={filterMessage}
aria-label={filterMessage}
tabIndex={0}
id={this.props.target}
>
<Icon iconName='filter' />
<span>{filterMessage}</span>
</div>
</div>
</>
);
}
}
FilterSetter.propTypes = propTypes;
export default FilterSetter;

View File

@@ -0,0 +1,76 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { Icon } from '@seafile/sf-metadata-ui-component';
import { CommonlyUsedHotkey } from '../../_basic';
import { gettext } from '../../utils';
class GroupbySetter extends Component {
static defaultProps = {
target: 'sf-metadata-groupby-popover',
isNeedSubmit: false,
};
constructor(props) {
super(props);
this.state = {
isShowGroupbySetter: false,
};
}
onKeyDown = (e) => {
if (CommonlyUsedHotkey.isEnter(e) || CommonlyUsedHotkey.isSpace(e)) this.onGroupbySetterToggle();
};
onGroupbySetterToggle = () => {
this.setState({ isShowGroupbySetter: !this.state.isShowGroupbySetter });
};
render() {
const { columns, groupbys, wrapperClass } = this.props;
if (!columns) return null;
const groupbysLength = groupbys ? groupbys.length : 0;
const activated = groupbysLength > 0;
let groupbyMessage = gettext('Group');
if (groupbysLength === 1) {
groupbyMessage = gettext('Grouped by 1 column');
} else if (groupbysLength > 1) {
groupbyMessage = gettext('Grouped by xxx columns').replace('xxx', groupbysLength);
}
let labelClass = wrapperClass || '';
labelClass = (labelClass && activated) ? labelClass + ' active' : labelClass;
return (
<>
<div className={classnames('setting-item mr-2', { 'mb-1': !labelClass })}>
<div
className={classnames('mr-2 setting-item-btn groupbys-setting-btn', labelClass)}
onClick={this.onGroupbySetterToggle}
role="button"
onKeyDown={this.onKeyDown}
title={groupbyMessage}
aria-label={groupbyMessage}
tabIndex={0}
id={this.props.target}
>
<Icon iconName="group" />
<span>{groupbyMessage}</span>
</div>
</div>
</>
);
}
}
GroupbySetter.propTypes = {
wrapperClass: PropTypes.string,
columns: PropTypes.array,
groupbys: PropTypes.array, // valid groupbys
modifyGroupbys: PropTypes.func,
target: PropTypes.string,
isNeedSubmit: PropTypes.bool,
};
export default GroupbySetter;

View File

@@ -0,0 +1,70 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Icon } from '@seafile/sf-metadata-ui-component';
import { CommonlyUsedHotkey } from '../../_basic';
import { gettext } from '../../utils';
class HideColumnSetter extends Component {
constructor(props) {
super(props);
this.state = {
isHideColumnSetterShow: false,
};
}
onKeyDown = (e) => {
if (CommonlyUsedHotkey.isEnter(e) || CommonlyUsedHotkey.isSpace(e)) this.onHideColumnToggle();
};
onHideColumnToggle = () => {
this.setState({ isHideColumnSetterShow: !this.state.isHideColumnSetterShow });
};
render() {
const { columns, wrapperClass, target, localShownColumnKeys } = this.props;
if (!columns) return null;
let message = gettext('Hide columns');
const hiddenColumns = columns.filter((column) => !localShownColumnKeys.includes(column.key));
const hiddenColumnsLength = hiddenColumns.length;
if (hiddenColumnsLength === 1) {
message = gettext('1 hidden column');
} else if (hiddenColumnsLength > 1) {
message = gettext('xxx hidden columns').replace('xxx', hiddenColumnsLength);
}
let labelClass = wrapperClass || '';
labelClass = (labelClass && hiddenColumnsLength > 0) ? labelClass + ' active' : labelClass;
return (
<>
<div className={`setting-item ${labelClass ? '' : 'mb-1'}`}>
<div
className={`mr-2 setting-item-btn filters-setting-btn ${labelClass}`}
onClick={this.onHideColumnToggle}
role="button"
onKeyDown={this.onKeyDown}
title={message}
aria-label={message}
tabIndex={0}
id={target}
>
<Icon iconName="hide" />
<span>{message}</span>
</div>
</div>
</>
);
}
}
HideColumnSetter.propTypes = {
wrapperClass: PropTypes.string,
target: PropTypes.string,
page: PropTypes.object,
shownColumnKeys: PropTypes.array,
localShownColumnKeys: PropTypes.array,
columns: PropTypes.array,
modifyHiddenColumns: PropTypes.func,
};
export default HideColumnSetter;

View File

@@ -0,0 +1,13 @@
import FilterSetter from './filter-setter';
import SortSetter from './sort-setter';
import GroupbySetter from './groupby-setter';
import PreHideColumnSetter from './pre-hide-column-setter';
import HideColumnSetter from './hide-column-setter';
export {
FilterSetter,
SortSetter,
GroupbySetter,
PreHideColumnSetter,
HideColumnSetter,
};

View File

@@ -0,0 +1,72 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Icon } from '@seafile/sf-metadata-ui-component';
import { gettext } from '../../utils';
class PreHideColumnSetter extends React.Component {
constructor(props) {
super(props);
this.state = {
isShowHideColumnSetter: false,
shownColumnKeys: props.shownColumnKeys || [],
};
}
UNSAFE_componentWillReceiveProps(nextProps) {
const { shownColumnKeys } = nextProps;
if (shownColumnKeys !== this.props.shownColumnKeys) {
this.setState({
isShowHideColumnSetter: false,
shownColumnKeys,
});
}
}
onHideColumnToggle = () => {
const { isShowHideColumnSetter } = this.state;
if (isShowHideColumnSetter) {
const { shownColumnKeys } = this.state;
this.props.onSettingUpdate(shownColumnKeys);
}
this.setState({ isShowHideColumnSetter: !isShowHideColumnSetter });
};
modifyHiddenColumns = (shownColumnKeys) => {
this.setState({ shownColumnKeys });
};
render() {
const { columns, wrapperClass } = this.props;
if (!columns) return null;
const { shownColumnKeys } = this.state;
const shown_column_keys = shownColumnKeys || [];
const hiddenColumns = columns.filter((column) => !shown_column_keys.includes(column.key));
const hiddenColumnsLength = hiddenColumns.length;
let message = gettext('Preset hide columns');
if (hiddenColumnsLength === 1) {
message = gettext('1 preset hidden column');
} else if (hiddenColumnsLength > 1) {
message = gettext('xxx preset hidden columns').replace('xxx', hiddenColumnsLength);
}
let settingClass = wrapperClass || '';
settingClass = (settingClass && hiddenColumnsLength > 0) ? settingClass + ' active' : settingClass;
return (
<div className={`setting-item ${settingClass ? '' : 'mb-1'}`}>
<div className="mr-2 filters-setting-btn" onClick={this.onHideColumnToggle} id="sf-metadata-hidden-column-popover">
<Icon iconName="hide" />
<span>{message}</span>
</div>
</div>
);
}
}
PreHideColumnSetter.propTypes = {
shownColumnKeys: PropTypes.array,
columns: PropTypes.array,
onSettingUpdate: PropTypes.func.isRequired,
wrapperClass: PropTypes.string,
};
export default PreHideColumnSetter;

View File

@@ -0,0 +1,84 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Icon } from '@seafile/sf-metadata-ui-component';
import { getValidSorts, CommonlyUsedHotkey } from '../../_basic';
import { gettext } from '../../utils';
const propTypes = {
wrapperClass: PropTypes.string,
target: PropTypes.string,
isNeedSubmit: PropTypes.bool,
sorts: PropTypes.array,
columns: PropTypes.array,
onSortsChange: PropTypes.func,
};
class SortSetter extends Component {
static defaultProps = {
target: 'sf-metadata-sort-popover',
isNeedSubmit: false,
};
constructor(props) {
super(props);
this.state = {
isSortPopoverShow: false,
};
}
onSortToggle = () => {
this.setState({ isSortPopoverShow: !this.state.isSortPopoverShow });
};
onKeyDown = (e) => {
e.stopPropagation();
if (CommonlyUsedHotkey.isEnter(e) || CommonlyUsedHotkey.isSpace(e)) this.onSortToggle();
};
update = (update) => {
const { sorts } = update || {};
this.props.onSortsChange(sorts);
};
render() {
const { sorts, columns, isNeedSubmit, wrapperClass } = this.props;
if (!columns) return null;
const validSorts = getValidSorts(sorts || [], columns);
const sortsLength = validSorts ? validSorts.length : 0;
let sortMessage = isNeedSubmit ? gettext('Preset sort') : gettext('Sort');
if (sortsLength === 1) {
sortMessage = isNeedSubmit ? gettext('1 preset sort') : gettext('1 sort');
} else if (sortsLength > 1) {
sortMessage = isNeedSubmit ? gettext('xxx preset sorts') : gettext('xxx sorts');
sortMessage = sortMessage.replace('xxx', sortsLength);
}
let labelClass = wrapperClass || '';
labelClass = (labelClass && sortsLength > 0) ? labelClass + ' active' : labelClass;
return (
<>
<div className={`setting-item ${labelClass ? '' : 'mb-1'}`}>
<div
className={`mr-2 setting-item-btn filters-setting-btn ${labelClass}`}
onClick={this.onSortToggle}
role="button"
onKeyDown={this.onKeyDown}
title={sortMessage}
aria-label={sortMessage}
tabIndex={0}
id={this.props.target}
>
<Icon iconName="sort" />
<span>{sortMessage}</span>
</div>
</div>
</>
);
}
}
SortSetter.propTypes = propTypes;
export default SortSetter;

View File

@@ -0,0 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import { gettext } from '../../utils';
const DeleteConfirmDialog = ({ title, content, onToggle, onSubmit }) => {
return (
<Modal isOpen={true} toggle={onToggle}>
<ModalHeader toggle={onToggle}>{title}</ModalHeader>
<ModalBody>
<p>{gettext('Are you sure to delete ') + content}</p>
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={onToggle}>{gettext('Cancel')}</Button>
<Button color="primary" onClick={onSubmit}>{gettext('Delete')}</Button>
</ModalFooter>
</Modal>
);
};
DeleteConfirmDialog.propTypes = {
title: PropTypes.string.isRequired,
content: PropTypes.string,
onToggle: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
};
export default DeleteConfirmDialog;

View File

@@ -0,0 +1,9 @@
import DeleteConfirmDialog from './delete-confirm-dialog';
import RecordDetailsDialog from './record-details-dialog';
import Table from './table';
export {
DeleteConfirmDialog,
RecordDetailsDialog,
Table,
};

Some files were not shown because too many files have changed in this diff Show More