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>
590
frontend/package-lock.json
generated
@@ -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",
|
||||
|
@@ -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",
|
||||
|
1
frontend/src/assets/icons/add-table.svg
Normal 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 |
1
frontend/src/assets/icons/check-circle.svg
Normal 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 |
1
frontend/src/assets/icons/close.svg
Normal 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 |
1
frontend/src/assets/icons/creation-time.svg
Normal 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 |
1
frontend/src/assets/icons/creator.svg
Normal 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 |
1
frontend/src/assets/icons/drop-down copy.svg
Normal 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 |
1
frontend/src/assets/icons/exclamation-circle.svg
Normal 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 |
1
frontend/src/assets/icons/exclamation-triangle.svg
Normal 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 |
1
frontend/src/assets/icons/filter.svg
Normal 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 |
1
frontend/src/assets/icons/fork-number.svg
Normal 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 |
1
frontend/src/assets/icons/group.svg
Normal 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 |
1
frontend/src/assets/icons/hide.svg
Normal 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 |
1
frontend/src/assets/icons/number.svg
Normal 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 |
1
frontend/src/assets/icons/open.svg
Normal 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 |
1
frontend/src/assets/icons/row-height-default.svg
Normal 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 |
1
frontend/src/assets/icons/row-height-double.svg
Normal 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 |
1
frontend/src/assets/icons/row-height-quadruple.svg
Normal 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 |
1
frontend/src/assets/icons/row-height-triple.svg
Normal 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 |
1
frontend/src/assets/icons/sort.svg
Normal 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 |
1
frontend/src/assets/icons/text.svg
Normal 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 |
@@ -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) &&
|
||||
|
@@ -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}
|
||||
|
@@ -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}/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
@@ -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'];
|
||||
|
||||
|
@@ -11,6 +11,7 @@
|
||||
width: 100%;
|
||||
height: calc(100% - 48px);
|
||||
border-top: 1px solid #e8e8e8;
|
||||
transform: translateZ(10px);
|
||||
}
|
||||
|
||||
.view-mode-container {
|
||||
|
@@ -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;
|
9
frontend/src/metadata/index.js
Normal 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,
|
||||
};
|
@@ -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 => {
|
74
frontend/src/metadata/metadata-tree-view/index.js
Normal 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;
|
@@ -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,
|
||||
};
|
@@ -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,
|
||||
};
|
@@ -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,
|
||||
};
|
@@ -0,0 +1,10 @@
|
||||
const CellType = {
|
||||
DEFAULT: 'default',
|
||||
TEXT: 'text',
|
||||
CREATOR: 'creator',
|
||||
CTIME: 'ctime',
|
||||
LAST_MODIFIER: 'last-modifier',
|
||||
MTIME: 'mtime',
|
||||
};
|
||||
|
||||
export default CellType;
|
@@ -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,
|
||||
};
|
@@ -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 };
|
@@ -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,
|
||||
};
|
@@ -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,
|
||||
};
|
@@ -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';
|
@@ -0,0 +1,8 @@
|
||||
const HEADER_HEIGHT_TYPE = {
|
||||
DEFAULT: 'default',
|
||||
DOUBLE: 'double',
|
||||
};
|
||||
|
||||
export {
|
||||
HEADER_HEIGHT_TYPE,
|
||||
};
|
@@ -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,
|
||||
};
|
@@ -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,
|
||||
};
|
@@ -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,
|
||||
};
|
@@ -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,
|
||||
};
|
@@ -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,
|
||||
};
|
22
frontend/src/metadata/metadata-view/_basic/constants/sort.js
Normal 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,
|
||||
};
|
135
frontend/src/metadata/metadata-view/_basic/constants/z-index.js
Normal 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 3,row 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;
|
114
frontend/src/metadata/metadata-view/_basic/index.js
Normal 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';
|
@@ -0,0 +1,5 @@
|
||||
import UserService from './user-service';
|
||||
|
||||
export {
|
||||
UserService,
|
||||
};
|
@@ -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;
|
@@ -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,
|
||||
};
|
@@ -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 };
|
@@ -0,0 +1,8 @@
|
||||
export {
|
||||
getColumnType,
|
||||
getColumnsByType,
|
||||
} from './core';
|
||||
export {
|
||||
isDateColumn,
|
||||
isSupportDateColumnFormat,
|
||||
} from './date';
|
113
frontend/src/metadata/metadata-view/_basic/utils/common.js
Normal 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);
|
||||
}
|
||||
};
|
||||
};
|
207
frontend/src/metadata/metadata-view/_basic/utils/date.js
Normal 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 };
|
273
frontend/src/metadata/metadata-view/_basic/utils/filter/core.js
Normal 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,
|
||||
};
|
@@ -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,
|
||||
};
|
@@ -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,
|
||||
};
|
@@ -0,0 +1,3 @@
|
||||
export { creatorFilter } from './creator';
|
||||
export { dateFilter } from './date';
|
||||
export { textFilter } from './text';
|
@@ -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,
|
||||
};
|
@@ -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,
|
||||
};
|
@@ -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';
|
@@ -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,
|
||||
};
|
@@ -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,
|
||||
};
|
@@ -0,0 +1,10 @@
|
||||
export {
|
||||
deleteInvalidGroupby,
|
||||
isValidGroupby,
|
||||
getValidGroupbys,
|
||||
} from './core';
|
||||
|
||||
export {
|
||||
groupTableRows,
|
||||
groupViewRows,
|
||||
} from './group-row';
|
27
frontend/src/metadata/metadata-view/_basic/utils/hotkey.js
Normal 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');
|
88
frontend/src/metadata/metadata-view/_basic/utils/index.js
Normal 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,
|
||||
};
|
@@ -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;
|
31
frontend/src/metadata/metadata-view/_basic/utils/row/core.js
Normal 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,
|
||||
};
|
@@ -0,0 +1,4 @@
|
||||
export {
|
||||
isTableRows,
|
||||
updateTableRowsWithRowsData,
|
||||
} from './core';
|
@@ -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,
|
||||
};
|
@@ -0,0 +1,16 @@
|
||||
export {
|
||||
isValidSort,
|
||||
getValidSorts,
|
||||
deleteInvalidSort,
|
||||
getMultipleIndexesOrderbyOptions,
|
||||
} from './core';
|
||||
|
||||
export {
|
||||
sortDate,
|
||||
sortText,
|
||||
} from './sort-column';
|
||||
|
||||
export {
|
||||
sortRowsWithMultiSorts,
|
||||
sortTableRows,
|
||||
} from './sort-row';
|
@@ -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,
|
||||
};
|
@@ -0,0 +1,6 @@
|
||||
|
||||
export { sortDate } from './date';
|
||||
export {
|
||||
compareString,
|
||||
sortText,
|
||||
} from './text';
|
@@ -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,
|
||||
};
|
@@ -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,
|
||||
};
|
@@ -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,
|
||||
};
|
@@ -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,
|
||||
};
|
@@ -0,0 +1,5 @@
|
||||
export {
|
||||
getTableById, getTableByName, getTableByIndex,
|
||||
} from './core';
|
||||
export { getTableColumnByKey, getTableColumnByName } from './column';
|
||||
export { getRowById, getRowsByIds } from './row';
|
@@ -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,
|
||||
};
|
@@ -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 };
|
@@ -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,
|
||||
};
|
@@ -0,0 +1,5 @@
|
||||
export { isValidEmail } from './email';
|
||||
export {
|
||||
ValidateFilter,
|
||||
DATE_MODIFIERS_REQUIRE_TERM,
|
||||
} from './filter';
|
@@ -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,
|
||||
};
|
@@ -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,
|
||||
};
|
@@ -0,0 +1,14 @@
|
||||
export {
|
||||
getViewById,
|
||||
getViewByName,
|
||||
isDefaultView,
|
||||
isFilterView,
|
||||
isGroupView,
|
||||
isSortView,
|
||||
isHiddenColumnsView,
|
||||
getViewShownColumns,
|
||||
} from './core';
|
||||
|
||||
export {
|
||||
getGroupByPath,
|
||||
} from './group';
|
@@ -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;
|
@@ -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;
|
@@ -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;
|
@@ -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;
|
@@ -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,
|
||||
};
|
@@ -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;
|
@@ -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;
|
@@ -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;
|
9
frontend/src/metadata/metadata-view/components/index.js
Normal 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,
|
||||
};
|