diff --git a/frontend/auto-imports.d.ts b/frontend/auto-imports.d.ts new file mode 100644 index 0000000..918aad8 --- /dev/null +++ b/frontend/auto-imports.d.ts @@ -0,0 +1,8 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// Generated by unplugin-auto-import +export {} +declare global { + +} diff --git a/frontend/components.d.ts b/frontend/components.d.ts new file mode 100644 index 0000000..d8da8b6 --- /dev/null +++ b/frontend/components.d.ts @@ -0,0 +1,63 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// Generated by unplugin-vue-components +// Read more: https://github.com/vuejs/core/pull/3399 +import '@vue/runtime-core' + +export {} + +declare module '@vue/runtime-core' { + export interface GlobalComponents { + ElBreadcrumb: typeof import('element-plus-secondary/es')['ElBreadcrumb'] + ElBreadcrumbItem: typeof import('element-plus-secondary/es')['ElBreadcrumbItem'] + ElButton: typeof import('element-plus-secondary/es')['ElButton'] + ElCard: typeof import('element-plus-secondary/es')['ElCard'] + ElCheckbox: typeof import('element-plus-secondary/es')['ElCheckbox'] + ElCheckboxGroup: typeof import('element-plus-secondary/es')['ElCheckboxGroup'] + ElCol: typeof import('element-plus-secondary/es')['ElCol'] + ElCollapse: typeof import('element-plus-secondary/es')['ElCollapse'] + ElCollapseItem: typeof import('element-plus-secondary/es')['ElCollapseItem'] + ElColorPicker: typeof import('element-plus-secondary/es')['ElColorPicker'] + ElContainer: typeof import('element-plus-secondary/es')['ElContainer'] + ElDatePicker: typeof import('element-plus-secondary/es')['ElDatePicker'] + ElDialog: typeof import('element-plus-secondary/es')['ElDialog'] + ElDivider: typeof import('element-plus-secondary/es')['ElDivider'] + ElDropdown: typeof import('element-plus-secondary/es')['ElDropdown'] + ElDropdownItem: typeof import('element-plus-secondary/es')['ElDropdownItem'] + ElDropdownMenu: typeof import('element-plus-secondary/es')['ElDropdownMenu'] + ElEmpty: typeof import('element-plus-secondary/es')['ElEmpty'] + ElForm: typeof import('element-plus-secondary/es')['ElForm'] + ElFormItem: typeof import('element-plus-secondary/es')['ElFormItem'] + ElHeader: typeof import('element-plus-secondary/es')['ElHeader'] + ElIcon: typeof import('element-plus-secondary/es')['ElIcon'] + ElInput: typeof import('element-plus-secondary/es')['ElInput'] + ElInputNumber: typeof import('element-plus-secondary/es')['ElInputNumber'] + ElMain: typeof import('element-plus-secondary/es')['ElMain'] + ElOption: typeof import('element-plus-secondary/es')['ElOption'] + ElOptionGroup: typeof import('element-plus-secondary/es')['ElOptionGroup'] + ElPopover: typeof import('element-plus-secondary/es')['ElPopover'] + ElRadio: typeof import('element-plus-secondary/es')['ElRadio'] + ElRadioGroup: typeof import('element-plus-secondary/es')['ElRadioGroup'] + ElRow: typeof import('element-plus-secondary/es')['ElRow'] + ElScrollbar: typeof import('element-plus-secondary/es')['ElScrollbar'] + ElSelect: typeof import('element-plus-secondary/es')['ElSelect'] + ElSelectV2: typeof import('element-plus-secondary/es')['ElSelectV2'] + ElSpace: typeof import('element-plus-secondary/es')['ElSpace'] + ElSwitch: typeof import('element-plus-secondary/es')['ElSwitch'] + ElTabPane: typeof import('element-plus-secondary/es')['ElTabPane'] + ElTabs: typeof import('element-plus-secondary/es')['ElTabs'] + ElTimeline: typeof import('element-plus-secondary/es')['ElTimeline'] + ElTimelineItem: typeof import('element-plus-secondary/es')['ElTimelineItem'] + ElTimePicker: typeof import('element-plus-secondary/es')['ElTimePicker'] + ElTooltip: typeof import('element-plus-secondary/es')['ElTooltip'] + ElTree: typeof import('element-plus-secondary/es')['ElTree'] + ElTreeSelect: typeof import('element-plus-secondary/es')['ElTreeSelect'] + ElUpload: typeof import('element-plus-secondary/es')['ElUpload'] + RouterLink: typeof import('vue-router')['RouterLink'] + RouterView: typeof import('vue-router')['RouterView'] + } + export interface ComponentCustomProperties { + vLoading: typeof import('element-plus-secondary/es')['ElLoadingDirective'] + } +} diff --git a/frontend/frontend.code-workspace b/frontend/frontend.code-workspace new file mode 100644 index 0000000..362d7c2 --- /dev/null +++ b/frontend/frontend.code-workspace @@ -0,0 +1,7 @@ +{ + "folders": [ + { + "path": "." + } + ] +} \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 8f1d584..d982475 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,27 +11,73 @@ "format": "prettier --write src/" }, "dependencies": { + "video.js": "^7.21.6", + "@videojs-player/vue": "^1.0.0", + "@vueuse/core": "^9.13.0", + "@npkg/tinymce-plugins": "^0.0.7", + "dayjs": "^1.11.9", + "flv.js": "^1.6.2", + "html2canvas": "^1.4.1", + "jspdf": "^2.5.1", + "html-to-image": "^1.11.11", + "echarts": "^5.5.1", + "file-saver": "^2.0.5", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "mathjs": "^11.6.0", + "@antv/g2plot": "^2.4.29", + "@antv/l7": "^2.22.0", + "@antv/l7plot": "^0.5.5", + "@antv/s2": "^1.49.0", + "vue-i18n": "^9.2.2", "@element-plus/icons-vue": "^2.3.1", "@form-create/designer": "^1.1.9", "@form-create/element-ui": "^3.2.22", "@form-create/vant": "^3.2.24", + "@turf/centroid": "^7.0.0", + "@tinymce/tinymce-vue": "^5.1.0", + "tinymce": "^5.8.2", "axios": "^1.6.0", + "consola": "^3.4.2", "element-plus": "^2.4.4", + "element-plus-secondary": "^0.6.8", + "element-resize-detector": "^1.2.4", + "vuedraggable": "^4.1.0", + "exceljs": "^4.4.0", + "js-base64": "^3.7.5", + "mitt": "^3.0.0", + "path": "^0.12.7", "pinia": "^2.1.7", + "qs": "^6.11.0", + "screenfull": "^6.0.2", + "snowflake-id": "^1.1.0", "vant": "^4.9.19", - "vue": "^3.4.0", - "vue-router": "^4.2.5" + "vue": "^3.3.4", + "vue-router": "^4.2.5", + "web-storage-cache": "^1.1.1", + "vue-types": "^5.0.2" }, "devDependencies": { + + "@intlify/unplugin-vue-i18n": "^0.8.2", "@rushstack/eslint-patch": "^1.3.3", "@vitejs/plugin-vue": "^4.5.2", + "@vitejs/plugin-vue-jsx": "^4.2.0", "@vue/eslint-config-prettier": "^8.0.0", + "less": "^4.1.3", + "jquery": "^3.6.4", "eslint": "^8.49.0", "eslint-plugin-vue": "^9.17.0", "prettier": "^3.0.3", "sass": "^1.89.0", "sass-loader": "^16.0.5", - "vite": "^5.0.8" + "typescript": "^5.8.3", + "unplugin-auto-import": "^0.15.1", + "unplugin-vue-components-secondary": "^0.24.6", + "vite": "^5.0.8", + "vite-plugin-style-import-secondary": "^2.0.0", + "consola": "^2.15.3", + "vite-svg-loader": "^5.1.0" }, "engines": { "node": ">=16.0.0", diff --git a/frontend/public/dataease.svg b/frontend/public/dataease.svg new file mode 100644 index 0000000..741ca75 --- /dev/null +++ b/frontend/public/dataease.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/public/images/code.png b/frontend/public/images/code.png new file mode 100644 index 0000000..33e52dd Binary files /dev/null and b/frontend/public/images/code.png differ diff --git a/frontend/public/images/loginbg.png b/frontend/public/images/loginbg.png new file mode 100644 index 0000000..2ca363b Binary files /dev/null and b/frontend/public/images/loginbg.png differ diff --git a/frontend/public/images/loginbtn.png b/frontend/public/images/loginbtn.png new file mode 100644 index 0000000..8f3104a Binary files /dev/null and b/frontend/public/images/loginbtn.png differ diff --git a/frontend/public/images/loginform.png b/frontend/public/images/loginform.png new file mode 100644 index 0000000..143299e Binary files /dev/null and b/frontend/public/images/loginform.png differ diff --git a/frontend/public/images/navbg.png b/frontend/public/images/navbg.png new file mode 100644 index 0000000..5fd47ac Binary files /dev/null and b/frontend/public/images/navbg.png differ diff --git a/frontend/public/images/navmenu.png b/frontend/public/images/navmenu.png new file mode 100644 index 0000000..d1a6e6d Binary files /dev/null and b/frontend/public/images/navmenu.png differ diff --git a/frontend/public/images/navmenucheck.png b/frontend/public/images/navmenucheck.png new file mode 100644 index 0000000..43b0e9c Binary files /dev/null and b/frontend/public/images/navmenucheck.png differ diff --git a/frontend/public/images/password.png b/frontend/public/images/password.png new file mode 100644 index 0000000..9f03c83 Binary files /dev/null and b/frontend/public/images/password.png differ diff --git a/frontend/public/images/splitline1.png b/frontend/public/images/splitline1.png new file mode 100644 index 0000000..2279ef5 Binary files /dev/null and b/frontend/public/images/splitline1.png differ diff --git a/frontend/public/images/splitline2.png b/frontend/public/images/splitline2.png new file mode 100644 index 0000000..a757106 Binary files /dev/null and b/frontend/public/images/splitline2.png differ diff --git a/frontend/public/images/titleline.png b/frontend/public/images/titleline.png new file mode 100644 index 0000000..68928bc Binary files /dev/null and b/frontend/public/images/titleline.png differ diff --git a/frontend/public/images/username.png b/frontend/public/images/username.png new file mode 100644 index 0000000..00dc875 Binary files /dev/null and b/frontend/public/images/username.png differ diff --git a/frontend/public/svg/icon_dashboard.svg b/frontend/public/svg/icon_dashboard.svg new file mode 100644 index 0000000..b726772 --- /dev/null +++ b/frontend/public/svg/icon_dashboard.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/public/svg/icon_data-visualization.svg b/frontend/public/svg/icon_data-visualization.svg new file mode 100644 index 0000000..8303575 --- /dev/null +++ b/frontend/public/svg/icon_data-visualization.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/public/svg/icon_database.svg b/frontend/public/svg/icon_database.svg new file mode 100644 index 0000000..b07810e --- /dev/null +++ b/frontend/public/svg/icon_database.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/public/svg/icon_dataset.svg b/frontend/public/svg/icon_dataset.svg new file mode 100644 index 0000000..da26700 --- /dev/null +++ b/frontend/public/svg/icon_dataset.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/public/svg/relation-dataset.svg b/frontend/public/svg/relation-dataset.svg new file mode 100644 index 0000000..c31cfd3 --- /dev/null +++ b/frontend/public/svg/relation-dataset.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/public/svg/relation-ds.svg b/frontend/public/svg/relation-ds.svg new file mode 100644 index 0000000..d5073da --- /dev/null +++ b/frontend/public/svg/relation-ds.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/svg/relation-panel.svg b/frontend/public/svg/relation-panel.svg new file mode 100644 index 0000000..090c1f4 --- /dev/null +++ b/frontend/public/svg/relation-panel.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/public/svg/relation-screen.svg b/frontend/public/svg/relation-screen.svg new file mode 100644 index 0000000..5b302b4 --- /dev/null +++ b/frontend/public/svg/relation-screen.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/public/tinymce-dataease-private/langs/zh_CN.js b/frontend/public/tinymce-dataease-private/langs/zh_CN.js new file mode 100644 index 0000000..64f2997 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/langs/zh_CN.js @@ -0,0 +1,423 @@ +tinymce.addI18n('zh_CN', { + "Redo": "\u91cd\u505a", + "Undo": "\u64a4\u9500", + "Cut": "\u526a\u5207", + "Copy": "\u590d\u5236", + "Paste": "\u7c98\u8d34", + "Select all": "\u5168\u9009", + "New document": "\u65b0\u6587\u4ef6", + "Ok": "\u786e\u5b9a", + "Cancel": "\u53d6\u6d88", + "Visual aids": "\u7f51\u683c\u7ebf", + "Bold": "\u7c97\u4f53", + "Italic": "\u659c\u4f53", + "Underline": "\u4e0b\u5212\u7ebf", + "Strikethrough": "\u5220\u9664\u7ebf", + "Superscript": "\u4e0a\u6807", + "Subscript": "\u4e0b\u6807", + "Clear formatting": "\u6e05\u9664\u683c\u5f0f", + "Align left": "\u5de6\u8fb9\u5bf9\u9f50", + "Align center": "\u4e2d\u95f4\u5bf9\u9f50", + "Align right": "\u53f3\u8fb9\u5bf9\u9f50", + "Justify": "\u4e24\u7aef\u5bf9\u9f50", + "Bullet list": "\u9879\u76ee\u7b26\u53f7", + "Numbered list": "\u7f16\u53f7\u5217\u8868", + "Decrease indent": "\u51cf\u5c11\u7f29\u8fdb", + "Increase indent": "\u589e\u52a0\u7f29\u8fdb", + "Close": "\u5173\u95ed", + "Formats": "\u683c\u5f0f", + "Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u6253\u5f00\u526a\u8d34\u677f\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u7b49\u5feb\u6377\u952e\u3002", + "Headers": "\u6807\u9898", + "Header 1": "\u6807\u98981", + "Header 2": "\u6807\u98982", + "Header 3": "\u6807\u98983", + "Header 4": "\u6807\u98984", + "Header 5": "\u6807\u98985", + "Header 6": "\u6807\u98986", + "Headings": "\u6807\u9898", + "Heading 1": "\u6807\u98981", + "Heading 2": "\u6807\u98982", + "Heading 3": "\u6807\u98983", + "Heading 4": "\u6807\u98984", + "Heading 5": "\u6807\u98985", + "Heading 6": "\u6807\u98986", + "Preformatted": "\u9884\u5148\u683c\u5f0f\u5316\u7684", + "Div": "Div", + "Pre": "Pre", + "Code": "\u4ee3\u7801", + "Paragraph": "\u6bb5\u843d", + "Blockquote": "\u5f15\u6587\u533a\u5757", + "Inline": "\u6587\u672c", + "Blocks": "\u57fa\u5757", + "Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002", + "Fonts": "\u5b57\u4f53", + "Font Sizes": "\u5b57\u53f7", + "Class": "\u7c7b\u578b", + "Browse for an image": "\u6d4f\u89c8\u56fe\u50cf", + "OR": "\u6216", + "Drop an image here": "\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64", + "Upload": "\u4e0a\u4f20", + "Block": "\u5757", + "Align": "\u5bf9\u9f50", + "Default": "\u9ed8\u8ba4", + "Circle": "\u7a7a\u5fc3\u5706", + "Disc": "\u5b9e\u5fc3\u5706", + "Square": "\u65b9\u5757", + "Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd", + "Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd", + "Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd", + "Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd", + "Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd", + "Anchor...": "\u951a\u70b9...", + "Name": "\u540d\u79f0", + "Id": "\u6807\u8bc6\u7b26", + "Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002", + "You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f", + "Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f", + "Special character...": "\u7279\u6b8a\u5b57\u7b26...", + "Source code": "\u6e90\u4ee3\u7801", + "Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b", + "Language": "\u8bed\u8a00", + "Code sample...": "\u793a\u4f8b\u4ee3\u7801...", + "Color Picker": "\u9009\u8272\u5668", + "R": "R", + "G": "G", + "B": "B", + "Left to right": "\u4ece\u5de6\u5230\u53f3", + "Right to left": "\u4ece\u53f3\u5230\u5de6", + "Emoticons...": "\u8868\u60c5\u7b26\u53f7...", + "Metadata and Document Properties": "\u5143\u6570\u636e\u548c\u6587\u6863\u5c5e\u6027", + "Title": "\u6807\u9898", + "Keywords": "\u5173\u952e\u8bcd", + "Description": "\u63cf\u8ff0", + "Robots": "\u673a\u5668\u4eba", + "Author": "\u4f5c\u8005", + "Encoding": "\u7f16\u7801", + "Fullscreen": "\u5168\u5c4f", + "Action": "\u64cd\u4f5c", + "Shortcut": "\u5feb\u6377\u952e", + "Help": "\u5e2e\u52a9", + "Address": "\u5730\u5740", + "Focus to menubar": "\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f", + "Focus to toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f", + "Focus to element path": "\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84", + "Focus to contextual toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355", + "Insert link (if link plugin activated)": "\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)", + "Save (if save plugin activated)": "\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)", + "Find (if searchreplace plugin activated)": "\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)", + "Plugins installed ({0}):": "\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):", + "Premium plugins:": "\u4f18\u79c0\u63d2\u4ef6\uff1a", + "Learn more...": "\u4e86\u89e3\u66f4\u591a...", + "You are using {0}": "\u4f60\u6b63\u5728\u4f7f\u7528 {0}", + "Plugins": "\u63d2\u4ef6", + "Handy Shortcuts": "\u5feb\u6377\u952e", + "Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf", + "Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247", + "Image description": "\u56fe\u7247\u63cf\u8ff0", + "Source": "\u5730\u5740", + "Dimensions": "\u5927\u5c0f", + "Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4", + "General": "\u666e\u901a", + "Advanced": "\u9ad8\u7ea7", + "Style": "\u6837\u5f0f", + "Vertical space": "\u5782\u76f4\u8fb9\u8ddd", + "Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd", + "Border": "\u8fb9\u6846", + "Insert image": "\u63d2\u5165\u56fe\u7247", + "Image...": "\u56fe\u7247...", + "Image list": "\u56fe\u7247\u5217\u8868", + "Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c", + "Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c", + "Flip vertically": "\u5782\u76f4\u7ffb\u8f6c", + "Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c", + "Edit image": "\u7f16\u8f91\u56fe\u7247", + "Image options": "\u56fe\u7247\u9009\u9879", + "Zoom in": "\u653e\u5927", + "Zoom out": "\u7f29\u5c0f", + "Crop": "\u88c1\u526a", + "Resize": "\u8c03\u6574\u5927\u5c0f", + "Orientation": "\u65b9\u5411", + "Brightness": "\u4eae\u5ea6", + "Sharpen": "\u9510\u5316", + "Contrast": "\u5bf9\u6bd4\u5ea6", + "Color levels": "\u989c\u8272\u5c42\u6b21", + "Gamma": "\u4f3d\u9a6c\u503c", + "Invert": "\u53cd\u8f6c", + "Apply": "\u5e94\u7528", + "Back": "\u540e\u9000", + "Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4", + "Date\/time": "\u65e5\u671f\/\u65f6\u95f4", + "Insert\/Edit Link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5", + "Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5", + "Text to display": "\u663e\u793a\u6587\u5b57", + "Url": "\u5730\u5740", + "Open link in...": "\u94fe\u63a5\u6253\u5f00\u4f4d\u7f6e...", + "Current window": "\u5f53\u524d\u7a97\u53e3", + "None": "\u65e0", + "New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00", + "Remove link": "\u5220\u9664\u94fe\u63a5", + "Anchors": "\u951a\u70b9", + "Link...": "\u94fe\u63a5...", + "Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5", + "The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f", + "The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f", + "Link list": "\u94fe\u63a5\u5217\u8868", + "Insert video": "\u63d2\u5165\u89c6\u9891", + "Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891", + "Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53", + "Alternative source": "\u955c\u50cf", + "Alternative source URL": "\u66ff\u4ee3\u6765\u6e90\u7f51\u5740", + "Media poster (Image URL)": "\u5c01\u9762(\u56fe\u7247\u5730\u5740)", + "Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:", + "Embed": "\u5185\u5d4c", + "Media...": "\u591a\u5a92\u4f53...", + "Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c", + "Page break": "\u5206\u9875\u7b26", + "Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c", + "Preview": "\u9884\u89c8", + "Print...": "\u6253\u5370...", + "Save": "\u4fdd\u5b58", + "Find": "\u67e5\u627e", + "Replace with": "\u66ff\u6362\u4e3a", + "Replace": "\u66ff\u6362", + "Replace all": "\u5168\u90e8\u66ff\u6362", + "Previous": "\u4e0a\u4e00\u4e2a", + "Next": "\u4e0b\u4e00\u4e2a", + "Find and replace...": "\u67e5\u627e\u5e76\u66ff\u6362...", + "Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.", + "Match case": "\u533a\u5206\u5927\u5c0f\u5199", + "Find whole words only": "\u5168\u5b57\u5339\u914d", + "Spell check": "\u62fc\u5199\u68c0\u67e5", + "Ignore": "\u5ffd\u7565", + "Ignore all": "\u5168\u90e8\u5ffd\u7565", + "Finish": "\u5b8c\u6210", + "Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178", + "Insert table": "\u63d2\u5165\u8868\u683c", + "Table properties": "\u8868\u683c\u5c5e\u6027", + "Delete table": "\u5220\u9664\u8868\u683c", + "Cell": "\u5355\u5143\u683c", + "Row": "\u884c", + "Column": "\u5217", + "Cell properties": "\u5355\u5143\u683c\u5c5e\u6027", + "Merge cells": "\u5408\u5e76\u5355\u5143\u683c", + "Split cell": "\u62c6\u5206\u5355\u5143\u683c", + "Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165", + "Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165", + "Delete row": "\u5220\u9664\u884c", + "Row properties": "\u884c\u5c5e\u6027", + "Cut row": "\u526a\u5207\u884c", + "Copy row": "\u590d\u5236\u884c", + "Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9", + "Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9", + "Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165", + "Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165", + "Delete column": "\u5220\u9664\u5217", + "Cols": "\u5217", + "Rows": "\u884c", + "Width": "\u5bbd", + "Height": "\u9ad8", + "Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd", + "Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd", + "Show caption": "\u663e\u793a\u6807\u9898", + "Left": "\u5de6\u5bf9\u9f50", + "Center": "\u5c45\u4e2d", + "Right": "\u53f3\u5bf9\u9f50", + "Cell type": "\u5355\u5143\u683c\u7c7b\u578b", + "Scope": "\u8303\u56f4", + "Alignment": "\u5bf9\u9f50\u65b9\u5f0f", + "H Align": "\u6c34\u5e73\u5bf9\u9f50", + "V Align": "\u5782\u76f4\u5bf9\u9f50", + "Top": "\u9876\u90e8\u5bf9\u9f50", + "Middle": "\u5782\u76f4\u5c45\u4e2d", + "Bottom": "\u5e95\u90e8\u5bf9\u9f50", + "Header cell": "\u8868\u5934\u5355\u5143\u683c", + "Row group": "\u884c\u7ec4", + "Column group": "\u5217\u7ec4", + "Row type": "\u884c\u7c7b\u578b", + "Header": "\u8868\u5934", + "Body": "\u8868\u4f53", + "Footer": "\u8868\u5c3e", + "Border color": "\u8fb9\u6846\u989c\u8272", + "Insert template...": "\u63d2\u5165\u6a21\u677f...", + "Templates": "\u6a21\u677f", + "Template": "\u6a21\u677f", + "Text color": "\u6587\u5b57\u989c\u8272", + "Background color": "\u80cc\u666f\u8272", + "Custom...": "\u81ea\u5b9a\u4e49...", + "Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272", + "No color": "\u65e0", + "Remove color": "\u79fb\u9664\u989c\u8272", + "Table of Contents": "\u5185\u5bb9\u5217\u8868", + "Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846", + "Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26", + "Word count": "\u5b57\u6570", + "Count": "\u8ba1\u6570", + "Document": "\u6587\u6863", + "Selection": "\u9009\u62e9", + "Words": "\u5355\u8bcd", + "Words: {0}": "\u5b57\u6570\uff1a{0}", + "{0} words": "{0} \u5b57", + "File": "\u6587\u4ef6", + "Edit": "\u7f16\u8f91", + "Insert": "\u63d2\u5165", + "View": "\u89c6\u56fe", + "Format": "\u683c\u5f0f", + "Table": "\u8868\u683c", + "Tools": "\u5de5\u5177", + "Powered by {0}": "\u7531{0}\u9a71\u52a8", + "Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9", + "Image title": "\u56fe\u7247\u6807\u9898", + "Border width": "\u8fb9\u6846\u5bbd\u5ea6", + "Border style": "\u8fb9\u6846\u6837\u5f0f", + "Error": "\u9519\u8bef", + "Warn": "\u8b66\u544a", + "Valid": "\u6709\u6548", + "To open the popup, press Shift+Enter": "\u6309Shitf+Enter\u952e\u6253\u5f00\u5bf9\u8bdd\u6846", + "Rich Text Area. Press ALT-0 for help.": "\u7f16\u8f91\u533a\u3002\u6309Alt+0\u952e\u6253\u5f00\u5e2e\u52a9\u3002", + "System Font": "\u7cfb\u7edf\u5b57\u4f53", + "Failed to upload image: {0}": "\u56fe\u7247\u4e0a\u4f20\u5931\u8d25: {0}", + "Failed to load plugin: {0} from url {1}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25: {0} \u6765\u81ea\u94fe\u63a5 {1}", + "Failed to load plugin url: {0}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25 \u94fe\u63a5: {0}", + "Failed to initialize plugin: {0}": "\u63d2\u4ef6\u521d\u59cb\u5316\u5931\u8d25: {0}", + "example": "\u793a\u4f8b", + "Search": "\u641c\u7d22", + "All": "\u5168\u90e8", + "Currency": "\u8d27\u5e01", + "Text": "\u6587\u5b57", + "Quotations": "\u5f15\u7528", + "Mathematical": "\u6570\u5b66", + "Extended Latin": "\u62c9\u4e01\u8bed\u6269\u5145", + "Symbols": "\u7b26\u53f7", + "Arrows": "\u7bad\u5934", + "User Defined": "\u81ea\u5b9a\u4e49", + "dollar sign": "\u7f8e\u5143\u7b26\u53f7", + "currency sign": "\u8d27\u5e01\u7b26\u53f7", + "euro-currency sign": "\u6b27\u5143\u7b26\u53f7", + "colon sign": "\u5192\u53f7", + "cruzeiro sign": "\u514b\u9c81\u8d5b\u7f57\u5e01\u7b26\u53f7", + "french franc sign": "\u6cd5\u90ce\u7b26\u53f7", + "lira sign": "\u91cc\u62c9\u7b26\u53f7", + "mill sign": "\u5bc6\u5c14\u7b26\u53f7", + "naira sign": "\u5948\u62c9\u7b26\u53f7", + "peseta sign": "\u6bd4\u585e\u5854\u7b26\u53f7", + "rupee sign": "\u5362\u6bd4\u7b26\u53f7", + "won sign": "\u97e9\u5143\u7b26\u53f7", + "new sheqel sign": "\u65b0\u8c22\u514b\u5c14\u7b26\u53f7", + "dong sign": "\u8d8a\u5357\u76fe\u7b26\u53f7", + "kip sign": "\u8001\u631d\u57fa\u666e\u7b26\u53f7", + "tugrik sign": "\u56fe\u683c\u91cc\u514b\u7b26\u53f7", + "drachma sign": "\u5fb7\u62c9\u514b\u9a6c\u7b26\u53f7", + "german penny symbol": "\u5fb7\u56fd\u4fbf\u58eb\u7b26\u53f7", + "peso sign": "\u6bd4\u7d22\u7b26\u53f7", + "guarani sign": "\u74dc\u62c9\u5c3c\u7b26\u53f7", + "austral sign": "\u6fb3\u5143\u7b26\u53f7", + "hryvnia sign": "\u683c\u91cc\u592b\u5c3c\u4e9a\u7b26\u53f7", + "cedi sign": "\u585e\u5730\u7b26\u53f7", + "livre tournois sign": "\u91cc\u5f17\u5f17\u5c14\u7b26\u53f7", + "spesmilo sign": "spesmilo\u7b26\u53f7", + "tenge sign": "\u575a\u6208\u7b26\u53f7", + "indian rupee sign": "\u5370\u5ea6\u5362\u6bd4", + "turkish lira sign": "\u571f\u8033\u5176\u91cc\u62c9", + "nordic mark sign": "\u5317\u6b27\u9a6c\u514b", + "manat sign": "\u9a6c\u7eb3\u7279\u7b26\u53f7", + "ruble sign": "\u5362\u5e03\u7b26\u53f7", + "yen character": "\u65e5\u5143\u5b57\u6837", + "yuan character": "\u4eba\u6c11\u5e01\u5143\u5b57\u6837", + "yuan character, in hong kong and taiwan": "\u5143\u5b57\u6837\uff08\u6e2f\u53f0\u5730\u533a\uff09", + "yen\/yuan character variant one": "\u5143\u5b57\u6837\uff08\u5927\u5199\uff09", + "Loading emoticons...": "\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7...", + "Could not load emoticons": "\u4e0d\u80fd\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7", + "People": "\u4eba\u7c7b", + "Animals and Nature": "\u52a8\u7269\u548c\u81ea\u7136", + "Food and Drink": "\u98df\u7269\u548c\u996e\u54c1", + "Activity": "\u6d3b\u52a8", + "Travel and Places": "\u65c5\u6e38\u548c\u5730\u70b9", + "Objects": "\u7269\u4ef6", + "Flags": "\u65d7\u5e1c", + "Characters": "\u5b57\u7b26", + "Characters (no spaces)": "\u5b57\u7b26(\u65e0\u7a7a\u683c)", + "{0} characters": "{0} \u4e2a\u5b57\u7b26", + "Error: Form submit field collision.": "\u9519\u8bef: \u8868\u5355\u63d0\u4ea4\u5b57\u6bb5\u51b2\u7a81\u3002", + "Error: No form element found.": "\u9519\u8bef: \u6ca1\u6709\u8868\u5355\u63a7\u4ef6\u3002", + "Update": "\u66f4\u65b0", + "Color swatch": "\u989c\u8272\u6837\u672c", + "Turquoise": "\u9752\u7eff\u8272", + "Green": "\u7eff\u8272", + "Blue": "\u84dd\u8272", + "Purple": "\u7d2b\u8272", + "Navy Blue": "\u6d77\u519b\u84dd", + "Dark Turquoise": "\u6df1\u84dd\u7eff\u8272", + "Dark Green": "\u6df1\u7eff\u8272", + "Medium Blue": "\u4e2d\u84dd\u8272", + "Medium Purple": "\u4e2d\u7d2b\u8272", + "Midnight Blue": "\u6df1\u84dd\u8272", + "Yellow": "\u9ec4\u8272", + "Orange": "\u6a59\u8272", + "Red": "\u7ea2\u8272", + "Light Gray": "\u6d45\u7070\u8272", + "Gray": "\u7070\u8272", + "Dark Yellow": "\u6697\u9ec4\u8272", + "Dark Orange": "\u6df1\u6a59\u8272", + "Dark Red": "\u6df1\u7ea2\u8272", + "Medium Gray": "\u4e2d\u7070\u8272", + "Dark Gray": "\u6df1\u7070\u8272", + "Light Green": "\u6d45\u7eff\u8272", + "Light Yellow": "\u6d45\u9ec4\u8272", + "Light Red": "\u6d45\u7ea2\u8272", + "Light Purple": "\u6d45\u7d2b\u8272", + "Light Blue": "\u6d45\u84dd\u8272", + "Dark Purple": "\u6df1\u7d2b\u8272", + "Dark Blue": "\u6df1\u84dd\u8272", + "Black": "\u9ed1\u8272", + "White": "\u767d\u8272", + "Switch to or from fullscreen mode": "\u5207\u6362\u5168\u5c4f\u6a21\u5f0f", + "Open help dialog": "\u6253\u5f00\u5e2e\u52a9\u5bf9\u8bdd\u6846", + "history": "\u5386\u53f2", + "styles": "\u6837\u5f0f", + "formatting": "\u683c\u5f0f\u5316", + "alignment": "\u5bf9\u9f50", + "indentation": "\u7f29\u8fdb", + "permanent pen": "\u8bb0\u53f7\u7b14", + "comments": "\u5907\u6ce8", + "Format Painter": "\u683c\u5f0f\u5237", + "Insert\/edit iframe": "\u63d2\u5165\/\u7f16\u8f91\u6846\u67b6", + "Capitalization": "\u5927\u5199", + "lowercase": "\u5c0f\u5199", + "UPPERCASE": "\u5927\u5199", + "Title Case": "\u9996\u5b57\u6bcd\u5927\u5199", + "Permanent Pen Properties": "\u6c38\u4e45\u7b14\u5c5e\u6027", + "Permanent pen properties...": "\u6c38\u4e45\u7b14\u5c5e\u6027...", + "Font": "\u5b57\u4f53", + "Size": "\u5b57\u53f7", + "More...": "\u66f4\u591a...", + "Spellcheck Language": "\u62fc\u5199\u68c0\u67e5\u8bed\u8a00", + "Select...": "\u9009\u62e9...", + "Preferences": "\u9996\u9009\u9879", + "Yes": "\u662f", + "No": "\u5426", + "Keyboard Navigation": "\u952e\u76d8\u6307\u5f15", + "Version": "\u7248\u672c", + "Anchor": "\u951a\u70b9", + "Special character": "\u7279\u6b8a\u7b26\u53f7", + "Code sample": "\u4ee3\u7801\u793a\u4f8b", + "Color": "\u989c\u8272", + "Emoticons": "\u8868\u60c5", + "Document properties": "\u6587\u6863\u5c5e\u6027", + "Image": "\u56fe\u7247", + "Insert link": "\u63d2\u5165\u94fe\u63a5", + "Target": "\u6253\u5f00\u65b9\u5f0f", + "Link": "\u94fe\u63a5", + "Poster": "\u5c01\u9762", + "Media": "\u5a92\u4f53", + "Print": "\u6253\u5370", + "Prev": "\u4e0a\u4e00\u4e2a", + "Find and replace": "\u67e5\u627e\u548c\u66ff\u6362", + "Whole words": "\u5168\u5b57\u5339\u914d", + "Spellcheck": "\u62fc\u5199\u68c0\u67e5", + "Caption": "\u6807\u9898", + "Insert template": "\u63d2\u5165\u6a21\u677f", + "Cut column": "\u526a\u5207\u5217", + "Copy column": "\u590d\u5236\u5217", + "Paste column before": "\u7c98\u8d34\u5230\u524d\u65b9", + "Paste column after": "\u7c98\u8d34\u5230\u540e\u65b9" +}); diff --git a/frontend/public/tinymce-dataease-private/langs/zh_TW.js b/frontend/public/tinymce-dataease-private/langs/zh_TW.js new file mode 100644 index 0000000..b306e0a --- /dev/null +++ b/frontend/public/tinymce-dataease-private/langs/zh_TW.js @@ -0,0 +1,419 @@ +tinymce.addI18n('zh_TW', { + "Redo": "\u91cd\u505a", + "Undo": "\u64a4\u92b7", + "Cut": "\u526a\u4e0b", + "Copy": "\u8907\u88fd", + "Paste": "\u8cbc\u4e0a", + "Select all": "\u5168\u9078", + "New document": "\u65b0\u6587\u4ef6", + "Ok": "\u78ba\u5b9a", + "Cancel": "\u53d6\u6d88", + "Visual aids": "\u5c0f\u5e6b\u624b", + "Bold": "\u7c97\u9ad4", + "Italic": "\u659c\u9ad4", + "Underline": "\u4e0b\u5283\u7dda", + "Strikethrough": "\u522a\u9664\u7dda", + "Superscript": "\u4e0a\u6a19", + "Subscript": "\u4e0b\u6a19", + "Clear formatting": "\u6e05\u9664\u683c\u5f0f", + "Align left": "\u5de6\u908a\u5c0d\u9f4a", + "Align center": "\u4e2d\u9593\u5c0d\u9f4a", + "Align right": "\u53f3\u908a\u5c0d\u9f4a", + "Justify": "\u5de6\u53f3\u5c0d\u9f4a", + "Bullet list": "\u9805\u76ee\u6e05\u55ae", + "Numbered list": "\u6578\u5b57\u6e05\u55ae", + "Decrease indent": "\u6e1b\u5c11\u7e2e\u6392", + "Increase indent": "\u589e\u52a0\u7e2e\u6392", + "Close": "\u95dc\u9589", + "Formats": "\u683c\u5f0f", + "Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u60a8\u7684\u700f\u89bd\u5668\u4e0d\u652f\u63f4\u5b58\u53d6\u526a\u8cbc\u7c3f\uff0c\u53ef\u4ee5\u4f7f\u7528\u5feb\u901f\u9375 Ctrl + X\/C\/V \u4ee3\u66ff\u526a\u4e0b\u3001\u8907\u88fd\u8207\u8cbc\u4e0a\u3002", + "Headers": "\u6a19\u984c", + "Header 1": "\u6a19\u984c 1", + "Header 2": "\u6a19\u984c 2", + "Header 3": "\u6a19\u984c 3", + "Header 4": "\u6a19\u984c 4", + "Header 5": "\u6a19\u984c 5", + "Header 6": "\u6a19\u984c 6", + "Headings": "\u6a19\u984c", + "Heading 1": "\u6a19\u984c1", + "Heading 2": "\u6a19\u984c2", + "Heading 3": "\u6a19\u984c3", + "Heading 4": "\u6a19\u984c4", + "Heading 5": "\u6a19\u984c5", + "Heading 6": "\u6a19\u984c6", + "Preformatted": "\u9810\u5148\u683c\u5f0f\u5316\u7684", + "Div": "Div", + "Pre": "Pre", + "Code": "\u4ee3\u78bc", + "Paragraph": "\u6bb5\u843d", + "Blockquote": "\u5f15\u6587\u5340\u584a", + "Inline": "\u5167\u806f", + "Blocks": "\u57fa\u584a", + "Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u76ee\u524d\u5c07\u4ee5\u7d14\u6587\u5b57\u7684\u6a21\u5f0f\u8cbc\u4e0a\uff0c\u60a8\u53ef\u4ee5\u518d\u9ede\u9078\u4e00\u6b21\u53d6\u6d88\u3002", + "Fonts": "\u5b57\u578b", + "Font Sizes": "\u5b57\u578b\u5927\u5c0f", + "Class": "\u985e\u578b", + "Browse for an image": "\u5f9e\u5716\u7247\u4e2d\u700f\u89bd", + "OR": "\u6216", + "Drop an image here": "\u62d6\u66f3\u5716\u7247\u81f3\u6b64", + "Upload": "\u4e0a\u50b3", + "Block": "\u5340\u584a", + "Align": "\u5c0d\u9f4a", + "Default": "\u9810\u8a2d", + "Circle": "\u7a7a\u5fc3\u5713", + "Disc": "\u5be6\u5fc3\u5713", + "Square": "\u6b63\u65b9\u5f62", + "Lower Alpha": "\u5c0f\u5beb\u82f1\u6587\u5b57\u6bcd", + "Lower Greek": "\u5e0c\u81d8\u5b57\u6bcd", + "Lower Roman": "\u5c0f\u5beb\u7f85\u99ac\u6578\u5b57", + "Upper Alpha": "\u5927\u5beb\u82f1\u6587\u5b57\u6bcd", + "Upper Roman": "\u5927\u5beb\u7f85\u99ac\u6578\u5b57", + "Anchor...": "\u9328\u9ede...", + "Name": "\u540d\u7a31", + "Id": "Id", + "Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "Id\u61c9\u4ee5\u5b57\u6bcd\u958b\u982d\uff0c\u5f8c\u9762\u63a5\u8457\u5b57\u6bcd\uff0c\u6578\u5b57\uff0c\u7834\u6298\u865f\uff0c\u9ede\u6578\uff0c\u5192\u865f\u6216\u4e0b\u5283\u7dda\u3002", + "You have unsaved changes are you sure you want to navigate away?": "\u7de8\u8f2f\u5c1a\u672a\u88ab\u5132\u5b58\uff0c\u4f60\u78ba\u5b9a\u8981\u96e2\u958b\uff1f", + "Restore last draft": "\u8f09\u5165\u4e0a\u4e00\u6b21\u7de8\u8f2f\u7684\u8349\u7a3f", + "Special character...": "\u7279\u6b8a\u5b57\u5143......", + "Source code": "\u539f\u59cb\u78bc", + "Insert\/Edit code sample": "\u63d2\u5165\/\u7de8\u8f2f \u7a0b\u5f0f\u78bc\u7bc4\u4f8b", + "Language": "\u8a9e\u8a00", + "Code sample...": "\u7a0b\u5f0f\u78bc\u7bc4\u4f8b...", + "Color Picker": "\u9078\u8272\u5668", + "R": "\u7d05", + "G": "\u7da0", + "B": "\u85cd", + "Left to right": "\u5f9e\u5de6\u5230\u53f3", + "Right to left": "\u5f9e\u53f3\u5230\u5de6", + "Emoticons...": "\u8868\u60c5\u7b26\u865f\u2026", + "Metadata and Document Properties": "\u5f8c\u8a2d\u8cc7\u6599\u8207\u6587\u4ef6\u5c6c\u6027", + "Title": "\u6a19\u984c", + "Keywords": "\u95dc\u9375\u5b57", + "Description": "\u63cf\u8ff0", + "Robots": "\u6a5f\u5668\u4eba", + "Author": "\u4f5c\u8005", + "Encoding": "\u7de8\u78bc", + "Fullscreen": "\u5168\u87a2\u5e55", + "Action": "\u52d5\u4f5c", + "Shortcut": "\u5feb\u901f\u9375", + "Help": "\u5e6b\u52a9", + "Address": "\u5730\u5740", + "Focus to menubar": "\u8df3\u81f3\u9078\u55ae\u5217", + "Focus to toolbar": "\u8df3\u81f3\u5de5\u5177\u5217", + "Focus to element path": "\u8df3\u81f3HTML\u5143\u7d20\u5217", + "Focus to contextual toolbar": "\u8df3\u81f3\u5feb\u6377\u9078\u55ae", + "Insert link (if link plugin activated)": "\u65b0\u589e\u6377\u5f91 (\u6377\u5f91\u5916\u639b\u555f\u7528\u6642)", + "Save (if save plugin activated)": "\u5132\u5b58 (\u5132\u5b58\u5916\u639b\u555f\u7528\u6642)", + "Find (if searchreplace plugin activated)": "\u5c0b\u627e (\u5c0b\u627e\u53d6\u4ee3\u5916\u639b\u555f\u7528\u6642)", + "Plugins installed ({0}):": "({0}) \u500b\u5916\u639b\u5df2\u5b89\u88dd\uff1a", + "Premium plugins:": "\u52a0\u503c\u5916\u639b\uff1a", + "Learn more...": "\u4e86\u89e3\u66f4\u591a...", + "You are using {0}": "\u60a8\u6b63\u5728\u4f7f\u7528 {0}", + "Plugins": "\u5916\u639b", + "Handy Shortcuts": "\u5feb\u901f\u9375", + "Horizontal line": "\u6c34\u5e73\u7dda", + "Insert\/edit image": "\u63d2\u5165\/\u7de8\u8f2f \u5716\u7247", + "Image description": "\u5716\u7247\u63cf\u8ff0", + "Source": "\u5716\u7247\u7db2\u5740", + "Dimensions": "\u5c3a\u5bf8", + "Constrain proportions": "\u7b49\u6bd4\u4f8b\u7e2e\u653e", + "General": "\u4e00\u822c", + "Advanced": "\u9032\u968e", + "Style": "\u6a23\u5f0f", + "Vertical space": "\u9ad8\u5ea6", + "Horizontal space": "\u5bec\u5ea6", + "Border": "\u908a\u6846", + "Insert image": "\u63d2\u5165\u5716\u7247", + "Image...": "\u5716\u7247......", + "Image list": "\u5716\u7247\u6e05\u55ae", + "Rotate counterclockwise": "\u9006\u6642\u91dd\u65cb\u8f49", + "Rotate clockwise": "\u9806\u6642\u91dd\u65cb\u8f49", + "Flip vertically": "\u5782\u76f4\u7ffb\u8f49", + "Flip horizontally": "\u6c34\u5e73\u7ffb\u8f49", + "Edit image": "\u7de8\u8f2f\u5716\u7247", + "Image options": "\u5716\u7247\u9078\u9805", + "Zoom in": "\u653e\u5927", + "Zoom out": "\u7e2e\u5c0f", + "Crop": "\u88c1\u526a", + "Resize": "\u8abf\u6574\u5927\u5c0f", + "Orientation": "\u65b9\u5411", + "Brightness": "\u4eae\u5ea6", + "Sharpen": "\u92b3\u5316", + "Contrast": "\u5c0d\u6bd4", + "Color levels": "\u984f\u8272\u5c64\u6b21", + "Gamma": "\u4f3d\u99ac\u503c", + "Invert": "\u53cd\u8f49", + "Apply": "\u61c9\u7528", + "Back": "\u5f8c\u9000", + "Insert date\/time": "\u63d2\u5165 \u65e5\u671f\/\u6642\u9593", + "Date\/time": "\u65e5\u671f\/\u6642\u9593", + "Insert\/Edit Link": "\u63d2\u5165\/\u7de8\u8f2f\u9023\u7d50", + "Insert\/edit link": "\u63d2\u5165\/\u7de8\u8f2f\u9023\u7d50", + "Text to display": "\u986f\u793a\u6587\u5b57", + "Url": "\u7db2\u5740", + "Open link in...": "\u958b\u555f\u9023\u7d50\u65bc...", + "Current window": "\u76ee\u524d\u8996\u7a97", + "None": "\u7121", + "New window": "\u53e6\u958b\u8996\u7a97", + "Remove link": "\u79fb\u9664\u9023\u7d50", + "Anchors": "\u52a0\u5165\u9328\u9ede", + "Link...": "\u9023\u7d50...", + "Paste or type a link": "\u8cbc\u4e0a\u6216\u8f38\u5165\u9023\u7d50", + "The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5beb\u7684URL\u70ba\u96fb\u5b50\u90f5\u4ef6\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7db4\u55ce\uff1f", + "The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5beb\u7684URL\u5c6c\u65bc\u5916\u90e8\u93c8\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7db4\u55ce\uff1f", + "Link list": "\u9023\u7d50\u6e05\u55ae", + "Insert video": "\u63d2\u5165\u5f71\u97f3", + "Insert\/edit video": "\u63d2\u4ef6\/\u7de8\u8f2f \u5f71\u97f3", + "Insert\/edit media": "\u63d2\u5165\/\u7de8\u8f2f \u5a92\u9ad4", + "Alternative source": "\u66ff\u4ee3\u5f71\u97f3", + "Alternative source URL": "\u66ff\u4ee3\u4f86\u6e90URL", + "Media poster (Image URL)": "\u5a92\u9ad4\u6d77\u5831\uff08\u5f71\u50cfImage URL\uff09", + "Paste your embed code below:": "\u8acb\u5c07\u60a8\u7684\u5d4c\u5165\u5f0f\u7a0b\u5f0f\u78bc\u8cbc\u5728\u4e0b\u9762:", + "Embed": "\u5d4c\u5165\u78bc", + "Media...": "\u5a92\u9ad4...", + "Nonbreaking space": "\u4e0d\u5206\u884c\u7684\u7a7a\u683c", + "Page break": "\u5206\u9801", + "Paste as text": "\u4ee5\u7d14\u6587\u5b57\u8cbc\u4e0a", + "Preview": "\u9810\u89bd", + "Print...": "\u5217\u5370...", + "Save": "\u5132\u5b58", + "Find": "\u641c\u5c0b", + "Replace with": "\u66f4\u63db", + "Replace": "\u66ff\u63db", + "Replace all": "\u66ff\u63db\u5168\u90e8", + "Previous": "\u4e0a\u4e00\u500b", + "Next": "\u4e0b\u4e00\u500b", + "Find and replace...": "\u5c0b\u627e\u53ca\u53d6\u4ee3...", + "Could not find the specified string.": "\u7121\u6cd5\u67e5\u8a62\u5230\u6b64\u7279\u5b9a\u5b57\u4e32", + "Match case": "\u76f8\u5339\u914d\u6848\u4ef6", + "Find whole words only": "\u50c5\u627e\u51fa\u5b8c\u6574\u5b57\u532f", + "Spell check": "\u62fc\u5beb\u6aa2\u67e5", + "Ignore": "\u5ffd\u7565", + "Ignore all": "\u5ffd\u7565\u6240\u6709", + "Finish": "\u5b8c\u6210", + "Add to Dictionary": "\u52a0\u5165\u5b57\u5178\u4e2d", + "Insert table": "\u63d2\u5165\u8868\u683c", + "Table properties": "\u8868\u683c\u5c6c\u6027", + "Delete table": "\u522a\u9664\u8868\u683c", + "Cell": "\u5132\u5b58\u683c", + "Row": "\u5217", + "Column": "\u884c", + "Cell properties": "\u5132\u5b58\u683c\u5c6c\u6027", + "Merge cells": "\u5408\u4f75\u5132\u5b58\u683c", + "Split cell": "\u5206\u5272\u5132\u5b58\u683c", + "Insert row before": "\u63d2\u5165\u5217\u5728...\u4e4b\u524d", + "Insert row after": "\u63d2\u5165\u5217\u5728...\u4e4b\u5f8c", + "Delete row": "\u522a\u9664\u5217", + "Row properties": "\u5217\u5c6c\u6027", + "Cut row": "\u526a\u4e0b\u5217", + "Copy row": "\u8907\u88fd\u5217", + "Paste row before": "\u8cbc\u4e0a\u5217\u5728...\u4e4b\u524d", + "Paste row after": "\u8cbc\u4e0a\u5217\u5728...\u4e4b\u5f8c", + "Insert column before": "\u63d2\u5165\u6b04\u4f4d\u5728...\u4e4b\u524d", + "Insert column after": "\u63d2\u5165\u6b04\u4f4d\u5728...\u4e4b\u5f8c", + "Delete column": "\u522a\u9664\u884c", + "Cols": "\u6b04\u4f4d\u6bb5", + "Rows": "\u5217", + "Width": "\u5bec\u5ea6", + "Height": "\u9ad8\u5ea6", + "Cell spacing": "\u5132\u5b58\u683c\u5f97\u9593\u8ddd", + "Cell padding": "\u5132\u5b58\u683c\u7684\u908a\u8ddd", + "Show caption": "\u986f\u793a\u6a19\u984c", + "Left": "\u5de6\u908a", + "Center": "\u4e2d\u9593", + "Right": "\u53f3\u908a", + "Cell type": "\u5132\u5b58\u683c\u7684\u985e\u578b", + "Scope": "\u7bc4\u570d", + "Alignment": "\u5c0d\u9f4a", + "H Align": "\u6c34\u5e73\u4f4d\u7f6e", + "V Align": "\u5782\u76f4\u4f4d\u7f6e", + "Top": "\u7f6e\u9802", + "Middle": "\u7f6e\u4e2d", + "Bottom": "\u7f6e\u5e95", + "Header cell": "\u6a19\u982d\u5132\u5b58\u683c", + "Row group": "\u5217\u7fa4\u7d44", + "Column group": "\u6b04\u4f4d\u7fa4\u7d44", + "Row type": "\u884c\u7684\u985e\u578b", + "Header": "\u6a19\u982d", + "Body": "\u4e3b\u9ad4", + "Footer": "\u9801\u5c3e", + "Border color": "\u908a\u6846\u984f\u8272", + "Insert template...": "\u63d2\u5165\u6a23\u7248...", + "Templates": "\u6a23\u7248", + "Template": "\u6a23\u677f", + "Text color": "\u6587\u5b57\u984f\u8272", + "Background color": "\u80cc\u666f\u984f\u8272", + "Custom...": "\u81ea\u8a02", + "Custom color": "\u81ea\u8a02\u984f\u8272", + "No color": "No color", + "Remove color": "\u79fb\u9664\u984f\u8272", + "Table of Contents": "\u76ee\u9304", + "Show blocks": "\u986f\u793a\u5340\u584a\u8cc7\u8a0a", + "Show invisible characters": "\u986f\u793a\u96b1\u85cf\u5b57\u5143", + "Word count": "\u8a08\u7b97\u5b57\u6578", + "Count": "\u8a08\u7b97", + "Document": "\u6587\u4ef6", + "Selection": "\u9078\u9805", + "Words": "\u5b57\u6578", + "Words: {0}": "\u5b57\u6578\uff1a{0}", + "{0} words": "{0} \u5b57\u5143", + "File": "\u6a94\u6848", + "Edit": "\u7de8\u8f2f", + "Insert": "\u63d2\u5165", + "View": "\u6aa2\u8996", + "Format": "\u683c\u5f0f", + "Table": "\u8868\u683c", + "Tools": "\u5de5\u5177", + "Powered by {0}": "\u7531 {0} \u63d0\u4f9b", + "Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u8c50\u5bcc\u7684\u6587\u672c\u5340\u57df\u3002\u6309ALT-F9\u524d\u5f80\u4e3b\u9078\u55ae\u3002\u6309ALT-F10\u547c\u53eb\u5de5\u5177\u6b04\u3002\u6309ALT-0\u5c0b\u6c42\u5e6b\u52a9", + "Image title": "\u5716\u7247\u6a19\u984c", + "Border width": "\u6846\u7dda\u5bec\u5ea6", + "Border style": "\u6846\u7dda\u6a23\u5f0f", + "Error": "\u932f\u8aa4", + "Warn": "\u8b66\u544a", + "Valid": "\u6709\u6548", + "To open the popup, press Shift+Enter": "\u8981\u958b\u555f\u5f48\u51fa\u8996\u7a97\uff0c\u8acb\u6309Shift+Enter", + "Rich Text Area. Press ALT-0 for help.": "\u5bcc\u6587\u672c\u5340\u57df\u3002\u8acb\u6309ALT-0\u5c0b\u6c42\u5354\u52a9\u3002", + "System Font": "\u7cfb\u7d71\u5b57\u578b", + "Failed to upload image: {0}": "\u7121\u6cd5\u4e0a\u50b3\u5f71\u50cf\uff1a{0}", + "Failed to load plugin: {0} from url {1}": "\u7121\u6cd5\u4e0a\u50b3\u63d2\u4ef6\uff1a{0}\u81eaurl{1}", + "Failed to load plugin url: {0}": "\u7121\u6cd5\u4e0a\u50b3\u63d2\u4ef6\uff1a{0}", + "Failed to initialize plugin: {0}": "\u7121\u6cd5\u555f\u52d5\u63d2\u4ef6\uff1a{0}", + "example": "\u7bc4\u4f8b", + "Search": "\u641c\u7d22", + "All": "\u5168\u90e8", + "Currency": "\u8ca8\u5e63", + "Text": "\u6587\u672c", + "Quotations": "\u5f15\u7528", + "Mathematical": "\u6578\u5b78", + "Extended Latin": "\u62c9\u4e01\u5b57\u6bcd\u64f4\u5145", + "Symbols": "\u7b26\u865f", + "Arrows": "\u7bad\u982d", + "User Defined": "\u4f7f\u7528\u8005\u5df2\u5b9a\u7fa9", + "dollar sign": "\u7f8e\u5143\u7b26\u865f", + "currency sign": "\u8ca8\u5e63\u7b26\u865f", + "euro-currency sign": "\u6b50\u5143\u7b26\u865f", + "colon sign": "\u79d1\u6717\u7b26\u865f", + "cruzeiro sign": "\u514b\u9b6f\u8cfd\u7f85\u7b26\u865f", + "french franc sign": "\u6cd5\u6717\u7b26\u865f", + "lira sign": "\u91cc\u62c9\u7b26\u865f", + "mill sign": "\u6587\u7b26\u865f", + "naira sign": "\u5948\u62c9\u7b26\u865f", + "peseta sign": "\u6bd4\u585e\u5854\u7b26\u865f", + "rupee sign": "\u76e7\u6bd4\u7b26\u865f", + "won sign": "\u97d3\u571c\u7b26\u865f", + "new sheqel sign": "\u65b0\u8b1d\u514b\u723e\u7b26\u865f", + "dong sign": "\u8d8a\u5357\u76fe\u7b26\u865f", + "kip sign": "\u8001\u64be\u5e63\u7b26\u865f", + "tugrik sign": "\u8499\u53e4\u5e63\u7b26\u865f", + "drachma sign": "\u5fb7\u514b\u62c9\u99ac\u7b26\u865f", + "german penny symbol": "\u5fb7\u570b\u5206\u7b26\u865f", + "peso sign": "\u62ab\u7d22\u7b26\u865f", + "guarani sign": "\u5df4\u62c9\u572d\u5e63\u7b26\u865f", + "austral sign": "\u963f\u6839\u5ef7\u5e63\u7b26\u865f", + "hryvnia sign": "\u70cf\u514b\u862d\u5e63\u7b26\u865f", + "cedi sign": "\u8fe6\u7d0d\u5e63\u7b26\u865f", + "livre tournois sign": "\u91cc\u5f17\u723e\u7b26\u865f", + "spesmilo sign": "\u570b\u969b\u5e63\u7b26\u865f", + "tenge sign": "\u54c8\u85a9\u514b\u5e63\u7b26\u865f", + "indian rupee sign": "\u5370\u5ea6\u76e7\u6bd4\u7b26\u865f", + "turkish lira sign": "\u571f\u8033\u5176\u91cc\u62c9\u7b26\u865f", + "nordic mark sign": "\u5317\u6b50\u99ac\u514b\u7b26\u865f", + "manat sign": "\u4e9e\u585e\u62dc\u7136\u5e63\u7b26\u865f", + "ruble sign": "\u76e7\u5e03\u7b26\u865f", + "yen character": "\u65e5\u5713\u7b26\u865f", + "yuan character": "\u4eba\u6c11\u5e63\u7b26\u865f", + "yuan character, in hong kong and taiwan": "\u6e2f\u5143\u8207\u53f0\u5e63\u7b26\u865f", + "yen\/yuan character variant one": "\u65e5\u5713\/\u4eba\u6c11\u5e63\u7b26\u865f\u8b8a\u5316\u578b", + "Loading emoticons...": "\u8f09\u5165\u8868\u60c5\u7b26\u865f\u2026", + "Could not load emoticons": "\u7121\u6cd5\u8f09\u5165\u8868\u60c5\u7b26\u865f", + "People": "\u4eba", + "Animals and Nature": "\u52d5\u7269\u8207\u81ea\u7136", + "Food and Drink": "\u98f2\u98df", + "Activity": "\u6d3b\u52d5", + "Travel and Places": "\u65c5\u884c\u8207\u5730\u9ede", + "Objects": "\u7269\u4ef6", + "Flags": "\u65d7\u6a19", + "Characters": "\u5b57\u5143", + "Characters (no spaces)": "\u5b57\u5143\uff08\u7121\u7a7a\u683c\uff09", + "{0} characters": "{0}\u5b57\u5143", + "Error: Form submit field collision.": "\u932f\u8aa4\uff1a\u8868\u683c\u905e\u4ea4\u6b04\u4f4d\u885d\u7a81\u3002", + "Error: No form element found.": "\u932f\u8aa4\uff1a\u627e\u4e0d\u5230\u8868\u683c\u5143\u7d20\u3002", + "Update": "\u66f4\u65b0", + "Color swatch": "\u8272\u5f69\u6a23\u672c", + "Turquoise": "\u571f\u8033\u5176\u85cd", + "Green": "\u7da0\u8272", + "Blue": "\u85cd\u8272", + "Purple": "\u7d2b\u8272", + "Navy Blue": "\u6df1\u85cd\u8272", + "Dark Turquoise": "\u6df1\u571f\u8033\u5176\u85cd", + "Dark Green": "\u6df1\u7da0\u8272", + "Medium Blue": "\u4e2d\u85cd\u8272", + "Medium Purple": "\u4e2d\u7d2b\u8272", + "Midnight Blue": "\u9ed1\u85cd\u8272", + "Yellow": "\u9ec3\u8272", + "Orange": "\u6a59\u8272", + "Red": "\u7d05\u8272", + "Light Gray": "\u6dfa\u7070\u8272", + "Gray": "\u7070\u8272", + "Dark Yellow": "\u6df1\u9ec3\u8272", + "Dark Orange": "\u6df1\u6a59\u8272", + "Dark Red": "\u6697\u7d05\u8272", + "Medium Gray": "\u4e2d\u7070\u8272", + "Dark Gray": "\u6df1\u7070\u8272", + "Light Green": "\u6de1\u7da0\u8272", + "Light Yellow": "\u6dfa\u9ec3\u8272", + "Light Red": "\u6dfa\u7d05\u8272", + "Light Purple": "\u6dfa\u7d2b\u8272", + "Light Blue": "\u6dfa\u85cd\u8272", + "Dark Purple": "\u6df1\u7d2b\u8272", + "Dark Blue": "\u6df1\u85cd\u8272", + "Black": "\u9ed1\u8272", + "White": "\u767d\u8272", + "Switch to or from fullscreen mode": "\u8f49\u63db\u81ea\/\u81f3\u5168\u87a2\u5e55\u6a21\u5f0f", + "Open help dialog": "\u958b\u555f\u5354\u52a9\u5c0d\u8a71", + "history": "\u6b77\u53f2", + "styles": "\u6a23\u5f0f", + "formatting": "\u683c\u5f0f", + "alignment": "\u5c0d\u9f4a", + "indentation": "\u7e2e\u6392", + "permanent pen": "\u6c38\u4e45\u6027\u7b46", + "comments": "\u8a3b\u89e3", + "Format Painter": "\u8907\u88fd\u683c\u5f0f", + "Insert\/edit iframe": "\u63d2\u5165\/\u7de8\u8f2fiframe", + "Capitalization": "\u5927\u5beb", + "lowercase": "\u5c0f\u5beb", + "UPPERCASE": "\u5927\u5beb", + "Title Case": "\u5b57\u9996\u5927\u5beb", + "Permanent Pen Properties": "\u6c38\u4e45\u6a19\u8a18\u5c6c\u6027", + "Permanent pen properties...": "\u6c38\u4e45\u6a19\u8a18\u5c6c\u6027......", + "Font": "\u5b57\u578b", + "Size": "\u5b57\u5f62\u5927\u5c0f", + "More...": "\u66f4\u591a\u8cc7\u8a0a......", + "Spellcheck Language": "\u62fc\u5beb\u8a9e\u8a00", + "Select...": "\u9078\u64c7......", + "Preferences": "\u9996\u9078\u9805", + "Yes": "\u662f", + "No": "\u5426", + "Keyboard Navigation": "\u9375\u76e4\u5c0e\u822a", + "Version": "\u7248\u672c", + "Anchor": "\u52a0\u5165\u9328\u9ede", + "Special character": "\u7279\u6b8a\u5b57\u5143", + "Code sample": "\u7a0b\u5f0f\u78bc\u7bc4\u4f8b", + "Color": "\u984f\u8272", + "Emoticons": "\u8868\u60c5", + "Document properties": "\u6587\u4ef6\u7684\u5c6c\u6027", + "Image": "\u5716\u7247", + "Insert link": "\u63d2\u5165\u9023\u7d50", + "Target": "\u958b\u555f\u65b9\u5f0f", + "Link": "\u9023\u7d50", + "Poster": "\u9810\u89bd\u5716\u7247", + "Media": "\u5a92\u9ad4", + "Print": "\u5217\u5370", + "Prev": "\u4e0a\u4e00\u500b", + "Find and replace": "\u5c0b\u627e\u53ca\u53d6\u4ee3", + "Whole words": "\u6574\u500b\u55ae\u5b57", + "Spellcheck": "\u62fc\u5b57\u6aa2\u67e5", + "Caption": "\u8868\u683c\u6a19\u984c", + "Insert template": "\u63d2\u5165\u6a23\u7248" +}); diff --git a/frontend/public/tinymce-dataease-private/skins/content/dark/content.css b/frontend/public/tinymce-dataease-private/skins/content/dark/content.css new file mode 100644 index 0000000..3919f03 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/content/dark/content.css @@ -0,0 +1,84 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +body { + background-color: #2f3742; + color: #dfe0e4; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + line-height: 1.4; + margin: 1rem; +} + +a { + color: #4099ff; +} + +table { + border-collapse: collapse; +} + +/* Apply a default padding if legacy cellpadding attribute is missing */ +table:not([cellpadding]) th, +table:not([cellpadding]) td { + padding: 0.4rem; +} + +/* Set default table styles if a table has a positive border attribute + and no inline css */ +table[border]:not([border="0"]):not([style*="border-width"]) th, +table[border]:not([border="0"]):not([style*="border-width"]) td { + border-width: 1px; +} + +/* Set default table styles if a table has a positive border attribute + and no inline css */ +table[border]:not([border="0"]):not([style*="border-style"]) th, +table[border]:not([border="0"]):not([style*="border-style"]) td { + border-style: solid; +} + +/* Set default table styles if a table has a positive border attribute + and no inline css */ +table[border]:not([border="0"]):not([style*="border-color"]) th, +table[border]:not([border="0"]):not([style*="border-color"]) td { + border-color: #6d737b; +} + +figure { + display: table; + margin: 1rem auto; +} + +figure figcaption { + color: #8a8f97; + display: block; + margin-top: 0.25rem; + text-align: center; +} + +hr { + border-color: #6d737b; + border-style: solid; + border-width: 1px 0 0 0; +} + +code { + background-color: #6d737b; + border-radius: 3px; + padding: 0.1rem 0.2rem; +} + +.mce-content-body:not([dir=rtl]) blockquote { + border-left: 2px solid #6d737b; + margin-left: 1.5rem; + padding-left: 1rem; +} + +.mce-content-body[dir=rtl] blockquote { + border-right: 2px solid #6d737b; + margin-right: 1.5rem; + padding-right: 1rem; +} diff --git a/frontend/public/tinymce-dataease-private/skins/content/dark/content.min.css b/frontend/public/tinymce-dataease-private/skins/content/dark/content.min.css new file mode 100644 index 0000000..07d40c2 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/content/dark/content.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +body{background-color:#2f3742;color:#dfe0e4;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}a{color:#4099ff}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#6d737b}figure{display:table;margin:1rem auto}figure figcaption{color:#8a8f97;display:block;margin-top:.25rem;text-align:center}hr{border-color:#6d737b;border-style:solid;border-width:1px 0 0 0}code{background-color:#6d737b;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #6d737b;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #6d737b;margin-right:1.5rem;padding-right:1rem} diff --git a/frontend/public/tinymce-dataease-private/skins/content/default/content.css b/frontend/public/tinymce-dataease-private/skins/content/default/content.css new file mode 100644 index 0000000..2df0c23 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/content/default/content.css @@ -0,0 +1,73 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ + +table { + border-collapse: collapse; +} + +/* Apply a default padding if legacy cellpadding attribute is missing */ +table:not([cellpadding]) th, +table:not([cellpadding]) td { + padding: 0.4rem; +} + +/* Set default table styles if a table has a positive border attribute + and no inline css */ +table[border]:not([border="0"]):not([style*="border-width"]) th, +table[border]:not([border="0"]):not([style*="border-width"]) td { + border-width: 1px; +} + +/* Set default table styles if a table has a positive border attribute + and no inline css */ +table[border]:not([border="0"]):not([style*="border-style"]) th, +table[border]:not([border="0"]):not([style*="border-style"]) td { + border-style: solid; +} + +/* Set default table styles if a table has a positive border attribute + and no inline css */ +table[border]:not([border="0"]):not([style*="border-color"]) th, +table[border]:not([border="0"]):not([style*="border-color"]) td { + border-color: #ccc; +} + +figure { + display: table; + margin: 1rem auto; +} + +figure figcaption { + color: #999; + display: block; + margin-top: 0.25rem; + text-align: center; +} + +hr { + border-color: #ccc; + border-style: solid; + border-width: 1px 0 0 0; +} + +code { + background-color: #e8e8e8; + border-radius: 3px; + padding: 0.1rem 0.2rem; +} + +.mce-content-body:not([dir=rtl]) blockquote { + border-left: 2px solid #ccc; + margin-left: 1.5rem; + padding-left: 1rem; +} + +.mce-content-body[dir=rtl] blockquote { + border-right: 2px solid #ccc; + margin-right: 1.5rem; + padding-right: 1rem; +} diff --git a/frontend/public/tinymce-dataease-private/skins/content/default/content.min.css b/frontend/public/tinymce-dataease-private/skins/content/default/content.min.css new file mode 100644 index 0000000..29cd987 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/content/default/content.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#ccc}figure{display:table;margin:1rem auto}figure figcaption{color:#999;display:block;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}code{background-color:#e8e8e8;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem} diff --git a/frontend/public/tinymce-dataease-private/skins/content/document/content.css b/frontend/public/tinymce-dataease-private/skins/content/document/content.css new file mode 100644 index 0000000..abd6fc2 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/content/document/content.css @@ -0,0 +1,83 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +@media screen { + html { + background: #f4f4f4; + min-height: 100%; + } +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; +} + +@media screen { + body { + background-color: #fff; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.15); + box-sizing: border-box; + margin: 1rem auto 0; + max-width: 820px; + min-height: calc(100vh - 1rem); + padding: 4rem 6rem 6rem 6rem; + } +} + +table { + border-collapse: collapse; +} + +/* Apply a default padding if legacy cellpadding attribute is missing */ +table:not([cellpadding]) th, +table:not([cellpadding]) td { + padding: 0.4rem; +} + +/* Set default table styles if a table has a positive border attribute + and no inline css */ +table[border]:not([border="0"]):not([style*="border-width"]) th, +table[border]:not([border="0"]):not([style*="border-width"]) td { + border-width: 1px; +} + +/* Set default table styles if a table has a positive border attribute + and no inline css */ +table[border]:not([border="0"]):not([style*="border-style"]) th, +table[border]:not([border="0"]):not([style*="border-style"]) td { + border-style: solid; +} + +/* Set default table styles if a table has a positive border attribute + and no inline css */ +table[border]:not([border="0"]):not([style*="border-color"]) th, +table[border]:not([border="0"]):not([style*="border-color"]) td { + border-color: #ccc; +} + +figure figcaption { + color: #999; + margin-top: 0.25rem; + text-align: center; +} + +hr { + border-color: #ccc; + border-style: solid; + border-width: 1px 0 0 0; +} + +.mce-content-body:not([dir=rtl]) blockquote { + border-left: 2px solid #ccc; + margin-left: 1.5rem; + padding-left: 1rem; +} + +.mce-content-body[dir=rtl] blockquote { + border-right: 2px solid #ccc; + margin-right: 1.5rem; + padding-right: 1rem; +} diff --git a/frontend/public/tinymce-dataease-private/skins/content/document/content.min.css b/frontend/public/tinymce-dataease-private/skins/content/document/content.min.css new file mode 100644 index 0000000..a1feef4 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/content/document/content.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +@media screen{html{background:#f4f4f4;min-height:100%}}body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif}@media screen{body{background-color:#fff;box-shadow:0 0 4px rgba(0,0,0,.15);box-sizing:border-box;margin:1rem auto 0;max-width:820px;min-height:calc(100vh - 1rem);padding:4rem 6rem 6rem 6rem}}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#ccc}figure figcaption{color:#999;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem} diff --git a/frontend/public/tinymce-dataease-private/skins/content/writer/content.css b/frontend/public/tinymce-dataease-private/skins/content/writer/content.css new file mode 100644 index 0000000..df0b395 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/content/writer/content.css @@ -0,0 +1,79 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + line-height: 1.4; + margin: 1rem auto; + max-width: 900px; +} + +table { + border-collapse: collapse; +} + +/* Apply a default padding if legacy cellpadding attribute is missing */ +table:not([cellpadding]) th, +table:not([cellpadding]) td { + padding: 0.4rem; +} + +/* Set default table styles if a table has a positive border attribute + and no inline css */ +table[border]:not([border="0"]):not([style*="border-width"]) th, +table[border]:not([border="0"]):not([style*="border-width"]) td { + border-width: 1px; +} + +/* Set default table styles if a table has a positive border attribute + and no inline css */ +table[border]:not([border="0"]):not([style*="border-style"]) th, +table[border]:not([border="0"]):not([style*="border-style"]) td { + border-style: solid; +} + +/* Set default table styles if a table has a positive border attribute + and no inline css */ +table[border]:not([border="0"]):not([style*="border-color"]) th, +table[border]:not([border="0"]):not([style*="border-color"]) td { + border-color: #ccc; +} + +figure { + display: table; + margin: 1rem auto; +} + +figure figcaption { + color: #999; + display: block; + margin-top: 0.25rem; + text-align: center; +} + +hr { + border-color: #ccc; + border-style: solid; + border-width: 1px 0 0 0; +} + +code { + background-color: #e8e8e8; + border-radius: 3px; + padding: 0.1rem 0.2rem; +} + +.mce-content-body:not([dir=rtl]) blockquote { + border-left: 2px solid #ccc; + margin-left: 1.5rem; + padding-left: 1rem; +} + +.mce-content-body[dir=rtl] blockquote { + border-right: 2px solid #ccc; + margin-right: 1.5rem; + padding-right: 1rem; +} diff --git a/frontend/public/tinymce-dataease-private/skins/content/writer/content.min.css b/frontend/public/tinymce-dataease-private/skins/content/writer/content.min.css new file mode 100644 index 0000000..0d8f5d3 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/content/writer/content.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Open Sans','Helvetica Neue',sans-serif;line-height:1.4;margin:1rem auto;max-width:900px}table{border-collapse:collapse}table:not([cellpadding]) td,table:not([cellpadding]) th{padding:.4rem}table[border]:not([border="0"]):not([style*=border-width]) td,table[border]:not([border="0"]):not([style*=border-width]) th{border-width:1px}table[border]:not([border="0"]):not([style*=border-style]) td,table[border]:not([border="0"]):not([style*=border-style]) th{border-style:solid}table[border]:not([border="0"]):not([style*=border-color]) td,table[border]:not([border="0"]):not([style*=border-color]) th{border-color:#ccc}figure{display:table;margin:1rem auto}figure figcaption{color:#999;display:block;margin-top:.25rem;text-align:center}hr{border-color:#ccc;border-style:solid;border-width:1px 0 0 0}code{background-color:#e8e8e8;border-radius:3px;padding:.1rem .2rem}.mce-content-body:not([dir=rtl]) blockquote{border-left:2px solid #ccc;margin-left:1.5rem;padding-left:1rem}.mce-content-body[dir=rtl] blockquote{border-right:2px solid #ccc;margin-right:1.5rem;padding-right:1rem} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.css new file mode 100644 index 0000000..522751b --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.css @@ -0,0 +1,849 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.mce-content-body .mce-item-anchor { + background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%20fill%3D%22%23cccccc%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center; + cursor: default; + display: inline-block; + height: 12px !important; + padding: 0 2px; + -webkit-user-modify: read-only; + -moz-user-modify: read-only; + -webkit-user-select: all; + -moz-user-select: all; + -ms-user-select: all; + user-select: all; + width: 8px !important; +} + +.mce-content-body .mce-item-anchor[data-mce-selected] { + outline-offset: 1px; +} + +.tox-comments-visible .tox-comment { + background-color: #fff0b7; +} + +.tox-comments-visible .tox-comment--active { + background-color: #ffe168; +} + +.tox-checklist > li:not(.tox-checklist--hidden) { + list-style: none; + margin: 0.25em 0; +} + +.tox-checklist > li:not(.tox-checklist--hidden)::before { + content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%236d737b%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A"); + cursor: pointer; + height: 1em; + margin-left: -1.5em; + margin-top: 0.125em; + position: absolute; + width: 1em; +} + +.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before { + content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A"); +} + +[dir=rtl] .tox-checklist > li:not(.tox-checklist--hidden)::before { + margin-left: 0; + margin-right: -1.5em; +} + +/* stylelint-disable */ +/* http://prismjs.com/ */ +/** + * Dracula Theme originally by Zeno Rocha [@zenorocha] + * https://draculatheme.com/ + * + * Ported for PrismJS by Albert Vallverdu [@byverdu] + */ +code[class*="language-"], +pre[class*="language-"] { + color: #f8f8f2; + background: none; + text-shadow: 0 1px rgba(0, 0, 0, 0.3); + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + -moz-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: 0.5em 0; + overflow: auto; + border-radius: 0.3em; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #282a36; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #6272a4; +} + +.token.punctuation { + color: #f8f8f2; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.constant, +.token.symbol, +.token.deleted { + color: #ff79c6; +} + +.token.boolean, +.token.number { + color: #bd93f9; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #50fa7b; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string, +.token.variable { + color: #f8f8f2; +} + +.token.atrule, +.token.attr-value, +.token.function, +.token.class-name { + color: #f1fa8c; +} + +.token.keyword { + color: #8be9fd; +} + +.token.regex, +.token.important { + color: #ffb86c; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + +/* stylelint-enable */ +.mce-content-body { + overflow-wrap: break-word; + word-wrap: break-word; +} + +.mce-content-body .mce-visual-caret { + background-color: black; + background-color: currentColor; + position: absolute; +} + +.mce-content-body .mce-visual-caret-hidden { + display: none; +} + +.mce-content-body *[data-mce-caret] { + left: -1000px; + margin: 0; + padding: 0; + position: absolute; + right: auto; + top: 0; +} + +.mce-content-body .mce-offscreen-selection { + left: -2000000px; + max-width: 1000000px; + position: absolute; +} + +.mce-content-body *[contentEditable=false] { + cursor: default; +} + +.mce-content-body *[contentEditable=true] { + cursor: text; +} + +.tox-cursor-format-painter { + cursor: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"), default; +} + +.mce-content-body figure.align-left { + float: left; +} + +.mce-content-body figure.align-right { + float: right; +} + +.mce-content-body figure.image.align-center { + display: table; + margin-left: auto; + margin-right: auto; +} + +.mce-preview-object { + border: 1px solid gray; + display: inline-block; + line-height: 0; + margin: 0 2px 0 2px; + position: relative; +} + +.mce-preview-object .mce-shim { + background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} + +.mce-preview-object[data-mce-selected="2"] .mce-shim { + display: none; +} + +.mce-object { + background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%20fill%3D%22%23cccccc%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center; + border: 1px dashed #aaa; +} + +.mce-pagebreak { + border: 1px dashed #aaa; + cursor: default; + display: block; + height: 5px; + margin-top: 15px; + page-break-before: always; + width: 100%; +} + +@media print { + .mce-pagebreak { + border: 0; + } +} + +.tiny-pageembed .mce-shim { + background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} + +.tiny-pageembed[data-mce-selected="2"] .mce-shim { + display: none; +} + +.tiny-pageembed { + display: inline-block; + position: relative; +} + +.tiny-pageembed--21by9, +.tiny-pageembed--16by9, +.tiny-pageembed--4by3, +.tiny-pageembed--1by1 { + display: block; + overflow: hidden; + padding: 0; + position: relative; + width: 100%; +} + +.tiny-pageembed--21by9 { + padding-top: 42.857143%; +} + +.tiny-pageembed--16by9 { + padding-top: 56.25%; +} + +.tiny-pageembed--4by3 { + padding-top: 75%; +} + +.tiny-pageembed--1by1 { + padding-top: 100%; +} + +.tiny-pageembed--21by9 iframe, +.tiny-pageembed--16by9 iframe, +.tiny-pageembed--4by3 iframe, +.tiny-pageembed--1by1 iframe { + border: 0; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} + +.mce-content-body[data-mce-placeholder] { + position: relative; +} + +.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before { + color: rgba(34, 47, 62, 0.7); + content: attr(data-mce-placeholder); + position: absolute; +} + +.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before { + left: 1px; + color:#a1b7cb; +} + +.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before { + right: 1px; +} + +.mce-content-body div.mce-resizehandle { + background-color: #4099ff; + border-color: #4099ff; + border-style: solid; + border-width: 1px; + box-sizing: border-box; + height: 10px; + position: absolute; + width: 10px; + z-index: 10000; +} + +.mce-content-body div.mce-resizehandle:hover { + background-color: #4099ff; +} + +.mce-content-body div.mce-resizehandle:nth-of-type(1) { + cursor: nwse-resize; +} + +.mce-content-body div.mce-resizehandle:nth-of-type(2) { + cursor: nesw-resize; +} + +.mce-content-body div.mce-resizehandle:nth-of-type(3) { + cursor: nwse-resize; +} + +.mce-content-body div.mce-resizehandle:nth-of-type(4) { + cursor: nesw-resize; +} + +.mce-content-body .mce-resize-backdrop { + z-index: 10000; +} + +.mce-content-body .mce-clonedresizable { + cursor: default; + opacity: 0.5; + outline: 1px dashed black; + position: absolute; + z-index: 10001; +} + +.mce-content-body .mce-clonedresizable.mce-resizetable-columns th, +.mce-content-body .mce-clonedresizable.mce-resizetable-columns td { + border: 0; +} + +.mce-content-body .mce-resize-helper { + background: #555; + background: rgba(0, 0, 0, 0.75); + border: 1px; + border-radius: 3px; + color: white; + display: none; + font-family: sans-serif; + font-size: 12px; + line-height: 14px; + margin: 5px 10px; + padding: 5px; + position: absolute; + white-space: nowrap; + z-index: 10002; +} + +.tox-rtc-user-selection { + position: relative; +} + +.tox-rtc-user-cursor { + bottom: 0; + cursor: default; + position: absolute; + top: 0; + width: 2px; +} + +.tox-rtc-user-cursor::before { + background-color: inherit; + border-radius: 50%; + content: ''; + display: block; + height: 8px; + position: absolute; + right: -3px; + top: -3px; + width: 8px; +} + +.tox-rtc-user-cursor:hover::after { + background-color: inherit; + border-radius: 100px; + box-sizing: border-box; + color: #fff; + content: attr(data-user); + display: block; + font-size: 12px; + font-weight: bold; + left: -5px; + min-height: 8px; + min-width: 8px; + padding: 0 12px; + position: absolute; + top: -11px; + white-space: nowrap; + z-index: 1000; +} + +.tox-rtc-user-selection--1 .tox-rtc-user-cursor { + background-color: #2dc26b; +} + +.tox-rtc-user-selection--2 .tox-rtc-user-cursor { + background-color: #e03e2d; +} + +.tox-rtc-user-selection--3 .tox-rtc-user-cursor { + background-color: #f1c40f; +} + +.tox-rtc-user-selection--4 .tox-rtc-user-cursor { + background-color: #3598db; +} + +.tox-rtc-user-selection--5 .tox-rtc-user-cursor { + background-color: #b96ad9; +} + +.tox-rtc-user-selection--6 .tox-rtc-user-cursor { + background-color: #e67e23; +} + +.tox-rtc-user-selection--7 .tox-rtc-user-cursor { + background-color: #aaa69d; +} + +.tox-rtc-user-selection--8 .tox-rtc-user-cursor { + background-color: #f368e0; +} + +.tox-rtc-remote-image { + background: #eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center; + border: 1px solid #ccc; + min-height: 240px; + min-width: 320px; +} + +.mce-match-marker { + background: #aaa; + color: #fff; +} + +.mce-match-marker-selected { + background: #39f; + color: #fff; +} + +.mce-match-marker-selected::-moz-selection { + background: #39f; + color: #fff; +} + +.mce-match-marker-selected::selection { + background: #39f; + color: #fff; +} + +.mce-content-body img[data-mce-selected], +.mce-content-body video[data-mce-selected], +.mce-content-body audio[data-mce-selected], +.mce-content-body object[data-mce-selected], +.mce-content-body embed[data-mce-selected], +.mce-content-body table[data-mce-selected] { + outline: 3px solid #4099ff; +} + +.mce-content-body hr[data-mce-selected] { + outline: 3px solid #4099ff; + outline-offset: 1px; +} + +.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus { + outline: 3px solid #4099ff; +} + +.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover { + outline: 3px solid #4099ff; +} + +.mce-content-body *[contentEditable=false][data-mce-selected] { + cursor: not-allowed; + outline: 3px solid #4099ff; +} + +.mce-content-body.mce-content-readonly *[contentEditable=true]:focus, +.mce-content-body.mce-content-readonly *[contentEditable=true]:hover { + outline: none; +} + +.mce-content-body *[data-mce-selected="inline-boundary"] { + background-color: #4099ff; +} + +.mce-content-body .mce-edit-focus { + outline: 3px solid #4099ff; +} + +.mce-content-body td[data-mce-selected], +.mce-content-body th[data-mce-selected] { + position: relative; +} + +.mce-content-body td[data-mce-selected]::-moz-selection, +.mce-content-body th[data-mce-selected]::-moz-selection { + background: none; +} + +.mce-content-body td[data-mce-selected]::selection, +.mce-content-body th[data-mce-selected]::selection { + background: none; +} + +.mce-content-body td[data-mce-selected] *, +.mce-content-body th[data-mce-selected] * { + outline: none; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.mce-content-body td[data-mce-selected]::after, +.mce-content-body th[data-mce-selected]::after { + background-color: rgba(180, 215, 255, 0.7); + border: 1px solid transparent; + bottom: -1px; + content: ''; + left: -1px; + mix-blend-mode: lighten; + position: absolute; + right: -1px; + top: -1px; +} + +@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { + .mce-content-body td[data-mce-selected]::after, + .mce-content-body th[data-mce-selected]::after { + border-color: rgba(0, 84, 180, 0.7); + } +} + +.mce-content-body img::-moz-selection { + background: none; +} + +.mce-content-body img::selection { + background: none; +} + +.ephox-snooker-resizer-bar { + background-color: #4099ff; + opacity: 0; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.ephox-snooker-resizer-cols { + cursor: col-resize; +} + +.ephox-snooker-resizer-rows { + cursor: row-resize; +} + +.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging { + opacity: 1; +} + +.mce-spellchecker-word { + background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A"); + background-position: 0 calc(100% + 1px); + background-repeat: repeat-x; + background-size: auto 6px; + cursor: default; + height: 2rem; +} + +.mce-spellchecker-grammar { + background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A"); + background-position: 0 calc(100% + 1px); + background-repeat: repeat-x; + background-size: auto 6px; + cursor: default; +} + +.mce-toc { + border: 1px solid gray; +} + +.mce-toc h2 { + margin: 4px; +} + +.mce-toc li { + list-style-type: none; +} + +table[style*="border-width: 0px"], +.mce-item-table:not([border]), +.mce-item-table[border="0"], +table[style*="border-width: 0px"] td, +.mce-item-table:not([border]) td, +.mce-item-table[border="0"] td, +table[style*="border-width: 0px"] th, +.mce-item-table:not([border]) th, +.mce-item-table[border="0"] th, +table[style*="border-width: 0px"] caption, +.mce-item-table:not([border]) caption, +.mce-item-table[border="0"] caption { + border: 1px dashed #bbb; +} + +.mce-visualblocks p, +.mce-visualblocks h1, +.mce-visualblocks h2, +.mce-visualblocks h3, +.mce-visualblocks h4, +.mce-visualblocks h5, +.mce-visualblocks h6, +.mce-visualblocks div:not([data-mce-bogus]), +.mce-visualblocks section, +.mce-visualblocks article, +.mce-visualblocks blockquote, +.mce-visualblocks address, +.mce-visualblocks pre, +.mce-visualblocks figure, +.mce-visualblocks figcaption, +.mce-visualblocks hgroup, +.mce-visualblocks aside, +.mce-visualblocks ul, +.mce-visualblocks ol, +.mce-visualblocks dl { + background-repeat: no-repeat; + border: 1px dashed #bbb; + margin-left: 3px; + padding-top: 10px; +} + +.mce-visualblocks p { + background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7); +} + +.mce-visualblocks h1 { + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==); +} + +.mce-visualblocks h2 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==); +} + +.mce-visualblocks h3 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7); +} + +.mce-visualblocks h4 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==); +} + +.mce-visualblocks h5 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==); +} + +.mce-visualblocks h6 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==); +} + +.mce-visualblocks div:not([data-mce-bogus]) { + background-image: url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7); +} + +.mce-visualblocks section { + background-image: url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=); +} + +.mce-visualblocks article { + background-image: url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7); +} + +.mce-visualblocks blockquote { + background-image: url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7); +} + +.mce-visualblocks address { + background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=); +} + +.mce-visualblocks pre { + background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==); +} + +.mce-visualblocks figure { + background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7); +} + +.mce-visualblocks figcaption { + border: 1px dashed #bbb; +} + +.mce-visualblocks hgroup { + background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7); +} + +.mce-visualblocks aside { + background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=); +} + +.mce-visualblocks ul { + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==); +} + +.mce-visualblocks ol { + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==); +} + +.mce-visualblocks dl { + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==); +} + +.mce-visualblocks:not([dir=rtl]) p, +.mce-visualblocks:not([dir=rtl]) h1, +.mce-visualblocks:not([dir=rtl]) h2, +.mce-visualblocks:not([dir=rtl]) h3, +.mce-visualblocks:not([dir=rtl]) h4, +.mce-visualblocks:not([dir=rtl]) h5, +.mce-visualblocks:not([dir=rtl]) h6, +.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]), +.mce-visualblocks:not([dir=rtl]) section, +.mce-visualblocks:not([dir=rtl]) article, +.mce-visualblocks:not([dir=rtl]) blockquote, +.mce-visualblocks:not([dir=rtl]) address, +.mce-visualblocks:not([dir=rtl]) pre, +.mce-visualblocks:not([dir=rtl]) figure, +.mce-visualblocks:not([dir=rtl]) figcaption, +.mce-visualblocks:not([dir=rtl]) hgroup, +.mce-visualblocks:not([dir=rtl]) aside, +.mce-visualblocks:not([dir=rtl]) ul, +.mce-visualblocks:not([dir=rtl]) ol, +.mce-visualblocks:not([dir=rtl]) dl { + margin-left: 3px; +} + +.mce-visualblocks[dir=rtl] p, +.mce-visualblocks[dir=rtl] h1, +.mce-visualblocks[dir=rtl] h2, +.mce-visualblocks[dir=rtl] h3, +.mce-visualblocks[dir=rtl] h4, +.mce-visualblocks[dir=rtl] h5, +.mce-visualblocks[dir=rtl] h6, +.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]), +.mce-visualblocks[dir=rtl] section, +.mce-visualblocks[dir=rtl] article, +.mce-visualblocks[dir=rtl] blockquote, +.mce-visualblocks[dir=rtl] address, +.mce-visualblocks[dir=rtl] pre, +.mce-visualblocks[dir=rtl] figure, +.mce-visualblocks[dir=rtl] figcaption, +.mce-visualblocks[dir=rtl] hgroup, +.mce-visualblocks[dir=rtl] aside, +.mce-visualblocks[dir=rtl] ul, +.mce-visualblocks[dir=rtl] ol, +.mce-visualblocks[dir=rtl] dl { + background-position-x: right; + margin-right: 3px; +} + +.mce-nbsp, +.mce-shy { + background: #aaa; +} + +.mce-shy::after { + content: '-'; +} + +body { + font-family: sans-serif; +} + +table { + border-collapse: collapse; +} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.inline.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.inline.css new file mode 100644 index 0000000..fee4323 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.inline.css @@ -0,0 +1,861 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.mce-content-body .mce-item-anchor { + background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A") no-repeat center; + cursor: default; + display: inline-block; + height: 12px !important; + padding: 0 2px; + -webkit-user-modify: read-only; + -moz-user-modify: read-only; + -webkit-user-select: all; + -moz-user-select: all; + -ms-user-select: all; + user-select: all; + width: 8px !important; +} + +.mce-content-body .mce-item-anchor[data-mce-selected] { + outline-offset: 1px; +} + +.tox-comments-visible .tox-comment { + background-color: #fff0b7; +} + +.tox-comments-visible .tox-comment--active { + background-color: #ffe168; +} + +.tox-checklist > li:not(.tox-checklist--hidden) { + list-style: none; + margin: 0.25em 0; +} + +.tox-checklist > li:not(.tox-checklist--hidden)::before { + content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A"); + cursor: pointer; + height: 1em; + margin-left: -1.5em; + margin-top: 0.125em; + position: absolute; + width: 1em; +} + +.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before { + content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A"); +} + +[dir=rtl] .tox-checklist > li:not(.tox-checklist--hidden)::before { + margin-left: 0; + margin-right: -1.5em; +} + +/* stylelint-disable */ +/* http://prismjs.com/ */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ +code[class*="language-"], +pre[class*="language-"] { + color: black; + background: none; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 1em; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + -moz-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, +pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, +code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, +pre[class*="language-"] ::selection, +code[class*="language-"]::selection, +code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: 0.5em 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9a6e3a; + background: hsla(0, 0%, 100%, 0.5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function, +.token.class-name { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + +/* stylelint-enable */ +.mce-content-body { + overflow-wrap: break-word; + word-wrap: break-word; +} + +.mce-content-body .mce-visual-caret { + background-color: black; + background-color: currentColor; + position: absolute; +} + +.mce-content-body .mce-visual-caret-hidden { + display: none; +} + +.mce-content-body *[data-mce-caret] { + left: -1000px; + margin: 0; + padding: 0; + position: absolute; + right: auto; + top: 0; +} + +.mce-content-body .mce-offscreen-selection { + left: -2000000px; + max-width: 1000000px; + position: absolute; +} + +.mce-content-body *[contentEditable=false] { + cursor: default; +} + +.mce-content-body *[contentEditable=true] { + cursor: text; +} + +.tox-cursor-format-painter { + cursor: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"), default; +} + +.mce-content-body figure.align-left { + float: left; +} + +.mce-content-body figure.align-right { + float: right; +} + +.mce-content-body figure.image.align-center { + display: table; + margin-left: auto; + margin-right: auto; +} + +.mce-preview-object { + border: 1px solid gray; + display: inline-block; + line-height: 0; + margin: 0 2px 0 2px; + position: relative; +} + +.mce-preview-object .mce-shim { + background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} + +.mce-preview-object[data-mce-selected="2"] .mce-shim { + display: none; +} + +.mce-object { + background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center; + border: 1px dashed #aaa; +} + +.mce-pagebreak { + border: 1px dashed #aaa; + cursor: default; + display: block; + height: 5px; + margin-top: 15px; + page-break-before: always; + width: 100%; +} + +@media print { + .mce-pagebreak { + border: 0; + } +} + +.tiny-pageembed .mce-shim { + background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} + +.tiny-pageembed[data-mce-selected="2"] .mce-shim { + display: none; +} + +.tiny-pageembed { + display: inline-block; + position: relative; +} + +.tiny-pageembed--21by9, +.tiny-pageembed--16by9, +.tiny-pageembed--4by3, +.tiny-pageembed--1by1 { + display: block; + overflow: hidden; + padding: 0; + position: relative; + width: 100%; +} + +.tiny-pageembed--21by9 { + padding-top: 42.857143%; +} + +.tiny-pageembed--16by9 { + padding-top: 56.25%; +} + +.tiny-pageembed--4by3 { + padding-top: 75%; +} + +.tiny-pageembed--1by1 { + padding-top: 100%; +} + +.tiny-pageembed--21by9 iframe, +.tiny-pageembed--16by9 iframe, +.tiny-pageembed--4by3 iframe, +.tiny-pageembed--1by1 iframe { + border: 0; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} + +.mce-content-body[data-mce-placeholder] { + position: relative; +} + +.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before { + color: rgba(34, 47, 62, 0.7); + content: attr(data-mce-placeholder); + position: absolute; +} + +.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before { + left: 1px; + color:#a1b7cb; +} + +.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before { + right: 1px; +} + +.mce-content-body div.mce-resizehandle { + background-color: #4099ff; + border-color: #4099ff; + border-style: solid; + border-width: 1px; + box-sizing: border-box; + height: 10px; + position: absolute; + width: 10px; + z-index: 10000; +} + +.mce-content-body div.mce-resizehandle:hover { + background-color: #4099ff; +} + +.mce-content-body div.mce-resizehandle:nth-of-type(1) { + cursor: nwse-resize; +} + +.mce-content-body div.mce-resizehandle:nth-of-type(2) { + cursor: nesw-resize; +} + +.mce-content-body div.mce-resizehandle:nth-of-type(3) { + cursor: nwse-resize; +} + +.mce-content-body div.mce-resizehandle:nth-of-type(4) { + cursor: nesw-resize; +} + +.mce-content-body .mce-resize-backdrop { + z-index: 10000; +} + +.mce-content-body .mce-clonedresizable { + cursor: default; + opacity: 0.5; + outline: 1px dashed black; + position: absolute; + z-index: 10001; +} + +.mce-content-body .mce-clonedresizable.mce-resizetable-columns th, +.mce-content-body .mce-clonedresizable.mce-resizetable-columns td { + border: 0; +} + +.mce-content-body .mce-resize-helper { + background: #555; + background: rgba(0, 0, 0, 0.75); + border: 1px; + border-radius: 3px; + color: white; + display: none; + font-family: sans-serif; + font-size: 12px; + line-height: 14px; + margin: 5px 10px; + padding: 5px; + position: absolute; + white-space: nowrap; + z-index: 10002; +} + +.tox-rtc-user-selection { + position: relative; +} + +.tox-rtc-user-cursor { + bottom: 0; + cursor: default; + position: absolute; + top: 0; + width: 2px; +} + +.tox-rtc-user-cursor::before { + background-color: inherit; + border-radius: 50%; + content: ''; + display: block; + height: 8px; + position: absolute; + right: -3px; + top: -3px; + width: 8px; +} + +.tox-rtc-user-cursor:hover::after { + background-color: inherit; + border-radius: 100px; + box-sizing: border-box; + color: #fff; + content: attr(data-user); + display: block; + font-size: 12px; + font-weight: bold; + left: -5px; + min-height: 8px; + min-width: 8px; + padding: 0 12px; + position: absolute; + top: -11px; + white-space: nowrap; + z-index: 1000; +} + +.tox-rtc-user-selection--1 .tox-rtc-user-cursor { + background-color: #2dc26b; +} + +.tox-rtc-user-selection--2 .tox-rtc-user-cursor { + background-color: #e03e2d; +} + +.tox-rtc-user-selection--3 .tox-rtc-user-cursor { + background-color: #f1c40f; +} + +.tox-rtc-user-selection--4 .tox-rtc-user-cursor { + background-color: #3598db; +} + +.tox-rtc-user-selection--5 .tox-rtc-user-cursor { + background-color: #b96ad9; +} + +.tox-rtc-user-selection--6 .tox-rtc-user-cursor { + background-color: #e67e23; +} + +.tox-rtc-user-selection--7 .tox-rtc-user-cursor { + background-color: #aaa69d; +} + +.tox-rtc-user-selection--8 .tox-rtc-user-cursor { + background-color: #f368e0; +} + +.tox-rtc-remote-image { + background: #eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center; + border: 1px solid #ccc; + min-height: 240px; + min-width: 320px; +} + +.mce-match-marker { + background: #aaa; + color: #fff; +} + +.mce-match-marker-selected { + background: #39f; + color: #fff; +} + +.mce-match-marker-selected::-moz-selection { + background: #39f; + color: #fff; +} + +.mce-match-marker-selected::selection { + background: #39f; + color: #fff; +} + +.mce-content-body img[data-mce-selected], +.mce-content-body video[data-mce-selected], +.mce-content-body audio[data-mce-selected], +.mce-content-body object[data-mce-selected], +.mce-content-body embed[data-mce-selected], +.mce-content-body table[data-mce-selected] { + outline: 3px solid #b4d7ff; +} + +.mce-content-body hr[data-mce-selected] { + outline: 3px solid #b4d7ff; + outline-offset: 1px; +} + +.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus { + outline: 3px solid #b4d7ff; +} + +.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover { + outline: 3px solid #b4d7ff; +} + +.mce-content-body *[contentEditable=false][data-mce-selected] { + cursor: not-allowed; + outline: 3px solid #b4d7ff; +} + +.mce-content-body.mce-content-readonly *[contentEditable=true]:focus, +.mce-content-body.mce-content-readonly *[contentEditable=true]:hover { + outline: none; +} + +.mce-content-body *[data-mce-selected="inline-boundary"] { + background-color: #b4d7ff; +} + +.mce-content-body .mce-edit-focus { + outline: 3px solid #b4d7ff; +} + +.mce-content-body td[data-mce-selected], +.mce-content-body th[data-mce-selected] { + position: relative; +} + +.mce-content-body td[data-mce-selected]::-moz-selection, +.mce-content-body th[data-mce-selected]::-moz-selection { + background: none; +} + +.mce-content-body td[data-mce-selected]::selection, +.mce-content-body th[data-mce-selected]::selection { + background: none; +} + +.mce-content-body td[data-mce-selected] *, +.mce-content-body th[data-mce-selected] * { + outline: none; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.mce-content-body td[data-mce-selected]::after, +.mce-content-body th[data-mce-selected]::after { + background-color: rgba(180, 215, 255, 0.7); + border: 1px solid rgba(180, 215, 255, 0.7); + bottom: -1px; + content: ''; + left: -1px; + mix-blend-mode: multiply; + position: absolute; + right: -1px; + top: -1px; +} + +@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { + .mce-content-body td[data-mce-selected]::after, + .mce-content-body th[data-mce-selected]::after { + border-color: rgba(0, 84, 180, 0.7); + } +} + +.mce-content-body img::-moz-selection { + background: none; +} + +.mce-content-body img::selection { + background: none; +} + +.ephox-snooker-resizer-bar { + background-color: #b4d7ff; + opacity: 0; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.ephox-snooker-resizer-cols { + cursor: col-resize; +} + +.ephox-snooker-resizer-rows { + cursor: row-resize; +} + +.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging { + opacity: 1; +} + +.mce-spellchecker-word { + background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A"); + background-position: 0 calc(100% + 1px); + background-repeat: repeat-x; + background-size: auto 6px; + cursor: default; + height: 2rem; +} + +.mce-spellchecker-grammar { + background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A"); + background-position: 0 calc(100% + 1px); + background-repeat: repeat-x; + background-size: auto 6px; + cursor: default; +} + +.mce-toc { + border: 1px solid gray; +} + +.mce-toc h2 { + margin: 4px; +} + +.mce-toc li { + list-style-type: none; +} + +table[style*="border-width: 0px"], +.mce-item-table:not([border]), +.mce-item-table[border="0"], +table[style*="border-width: 0px"] td, +.mce-item-table:not([border]) td, +.mce-item-table[border="0"] td, +table[style*="border-width: 0px"] th, +.mce-item-table:not([border]) th, +.mce-item-table[border="0"] th, +table[style*="border-width: 0px"] caption, +.mce-item-table:not([border]) caption, +.mce-item-table[border="0"] caption { + border: 1px dashed #bbb; +} + +.mce-visualblocks p, +.mce-visualblocks h1, +.mce-visualblocks h2, +.mce-visualblocks h3, +.mce-visualblocks h4, +.mce-visualblocks h5, +.mce-visualblocks h6, +.mce-visualblocks div:not([data-mce-bogus]), +.mce-visualblocks section, +.mce-visualblocks article, +.mce-visualblocks blockquote, +.mce-visualblocks address, +.mce-visualblocks pre, +.mce-visualblocks figure, +.mce-visualblocks figcaption, +.mce-visualblocks hgroup, +.mce-visualblocks aside, +.mce-visualblocks ul, +.mce-visualblocks ol, +.mce-visualblocks dl { + background-repeat: no-repeat; + border: 1px dashed #bbb; + margin-left: 3px; + padding-top: 10px; +} + +.mce-visualblocks p { + background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7); +} + +.mce-visualblocks h1 { + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==); +} + +.mce-visualblocks h2 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==); +} + +.mce-visualblocks h3 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7); +} + +.mce-visualblocks h4 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==); +} + +.mce-visualblocks h5 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==); +} + +.mce-visualblocks h6 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==); +} + +.mce-visualblocks div:not([data-mce-bogus]) { + background-image: url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7); +} + +.mce-visualblocks section { + background-image: url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=); +} + +.mce-visualblocks article { + background-image: url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7); +} + +.mce-visualblocks blockquote { + background-image: url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7); +} + +.mce-visualblocks address { + background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=); +} + +.mce-visualblocks pre { + background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==); +} + +.mce-visualblocks figure { + background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7); +} + +.mce-visualblocks figcaption { + border: 1px dashed #bbb; +} + +.mce-visualblocks hgroup { + background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7); +} + +.mce-visualblocks aside { + background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=); +} + +.mce-visualblocks ul { + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==); +} + +.mce-visualblocks ol { + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==); +} + +.mce-visualblocks dl { + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==); +} + +.mce-visualblocks:not([dir=rtl]) p, +.mce-visualblocks:not([dir=rtl]) h1, +.mce-visualblocks:not([dir=rtl]) h2, +.mce-visualblocks:not([dir=rtl]) h3, +.mce-visualblocks:not([dir=rtl]) h4, +.mce-visualblocks:not([dir=rtl]) h5, +.mce-visualblocks:not([dir=rtl]) h6, +.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]), +.mce-visualblocks:not([dir=rtl]) section, +.mce-visualblocks:not([dir=rtl]) article, +.mce-visualblocks:not([dir=rtl]) blockquote, +.mce-visualblocks:not([dir=rtl]) address, +.mce-visualblocks:not([dir=rtl]) pre, +.mce-visualblocks:not([dir=rtl]) figure, +.mce-visualblocks:not([dir=rtl]) figcaption, +.mce-visualblocks:not([dir=rtl]) hgroup, +.mce-visualblocks:not([dir=rtl]) aside, +.mce-visualblocks:not([dir=rtl]) ul, +.mce-visualblocks:not([dir=rtl]) ol, +.mce-visualblocks:not([dir=rtl]) dl { + margin-left: 3px; +} + +.mce-visualblocks[dir=rtl] p, +.mce-visualblocks[dir=rtl] h1, +.mce-visualblocks[dir=rtl] h2, +.mce-visualblocks[dir=rtl] h3, +.mce-visualblocks[dir=rtl] h4, +.mce-visualblocks[dir=rtl] h5, +.mce-visualblocks[dir=rtl] h6, +.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]), +.mce-visualblocks[dir=rtl] section, +.mce-visualblocks[dir=rtl] article, +.mce-visualblocks[dir=rtl] blockquote, +.mce-visualblocks[dir=rtl] address, +.mce-visualblocks[dir=rtl] pre, +.mce-visualblocks[dir=rtl] figure, +.mce-visualblocks[dir=rtl] figcaption, +.mce-visualblocks[dir=rtl] hgroup, +.mce-visualblocks[dir=rtl] aside, +.mce-visualblocks[dir=rtl] ul, +.mce-visualblocks[dir=rtl] ol, +.mce-visualblocks[dir=rtl] dl { + background-position-x: right; + margin-right: 3px; +} + +.mce-nbsp, +.mce-shy { + background: #aaa; +} + +.mce-shy::after { + content: '-'; +} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.inline.min.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.inline.min.css new file mode 100644 index 0000000..5c66aca --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.inline.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.mce-content-body .mce-item-anchor{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;cursor:default;display:inline-block;height:12px!important;padding:0 2px;-webkit-user-modify:read-only;-moz-user-modify:read-only;-webkit-user-select:all;-moz-user-select:all;-ms-user-select:all;user-select:all;width:8px!important}.mce-content-body .mce-item-anchor[data-mce-selected]{outline-offset:1px}.tox-comments-visible .tox-comment{background-color:#fff0b7}.tox-comments-visible .tox-comment--active{background-color:#ffe168}.tox-checklist>li:not(.tox-checklist--hidden){list-style:none;margin:.25em 0}.tox-checklist>li:not(.tox-checklist--hidden)::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A");cursor:pointer;height:1em;margin-left:-1.5em;margin-top:.125em;position:absolute;width:1em}.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A")}[dir=rtl] .tox-checklist>li:not(.tox-checklist--hidden)::before{margin-left:0;margin-right:-1.5em}code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.mce-content-body{overflow-wrap:break-word;word-wrap:break-word}.mce-content-body .mce-visual-caret{background-color:#000;background-color:currentColor;position:absolute}.mce-content-body .mce-visual-caret-hidden{display:none}.mce-content-body [data-mce-caret]{left:-1000px;margin:0;padding:0;position:absolute;right:auto;top:0}.mce-content-body .mce-offscreen-selection{left:-2000000px;max-width:1000000px;position:absolute}.mce-content-body [contentEditable=false]{cursor:default}.mce-content-body [contentEditable=true]{cursor:text}.tox-cursor-format-painter{cursor:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"),default}.mce-content-body figure.align-left{float:left}.mce-content-body figure.align-right{float:right}.mce-content-body figure.image.align-center{display:table;margin-left:auto;margin-right:auto}.mce-preview-object{border:1px solid gray;display:inline-block;line-height:0;margin:0 2px 0 2px;position:relative}.mce-preview-object .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-object{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;border:1px dashed #aaa}.mce-pagebreak{border:1px dashed #aaa;cursor:default;display:block;height:5px;margin-top:15px;page-break-before:always;width:100%}@media print{.mce-pagebreak{border:0}}.tiny-pageembed .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.tiny-pageembed[data-mce-selected="2"] .mce-shim{display:none}.tiny-pageembed{display:inline-block;position:relative}.tiny-pageembed--16by9,.tiny-pageembed--1by1,.tiny-pageembed--21by9,.tiny-pageembed--4by3{display:block;overflow:hidden;padding:0;position:relative;width:100%}.tiny-pageembed--21by9{padding-top:42.857143%}.tiny-pageembed--16by9{padding-top:56.25%}.tiny-pageembed--4by3{padding-top:75%}.tiny-pageembed--1by1{padding-top:100%}.tiny-pageembed--16by9 iframe,.tiny-pageembed--1by1 iframe,.tiny-pageembed--21by9 iframe,.tiny-pageembed--4by3 iframe{border:0;height:100%;left:0;position:absolute;top:0;width:100%}.mce-content-body[data-mce-placeholder]{position:relative}.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before{color:rgba(34,47,62,.7);content:attr(data-mce-placeholder);position:absolute}.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before{left:1px;color:#a1b7cb;}.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before{right:1px}.mce-content-body div.mce-resizehandle{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;height:10px;position:absolute;width:10px;z-index:10000}.mce-content-body div.mce-resizehandle:hover{background-color:#4099ff}.mce-content-body div.mce-resizehandle:nth-of-type(1){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(2){cursor:nesw-resize}.mce-content-body div.mce-resizehandle:nth-of-type(3){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(4){cursor:nesw-resize}.mce-content-body .mce-resize-backdrop{z-index:10000}.mce-content-body .mce-clonedresizable{cursor:default;opacity:.5;outline:1px dashed #000;position:absolute;z-index:10001}.mce-content-body .mce-clonedresizable.mce-resizetable-columns td,.mce-content-body .mce-clonedresizable.mce-resizetable-columns th{border:0}.mce-content-body .mce-resize-helper{background:#555;background:rgba(0,0,0,.75);border:1px;border-radius:3px;color:#fff;display:none;font-family:sans-serif;font-size:12px;line-height:14px;margin:5px 10px;padding:5px;position:absolute;white-space:nowrap;z-index:10002}.tox-rtc-user-selection{position:relative}.tox-rtc-user-cursor{bottom:0;cursor:default;position:absolute;top:0;width:2px}.tox-rtc-user-cursor::before{background-color:inherit;border-radius:50%;content:'';display:block;height:8px;position:absolute;right:-3px;top:-3px;width:8px}.tox-rtc-user-cursor:hover::after{background-color:inherit;border-radius:100px;box-sizing:border-box;color:#fff;content:attr(data-user);display:block;font-size:12px;font-weight:700;left:-5px;min-height:8px;min-width:8px;padding:0 12px;position:absolute;top:-11px;white-space:nowrap;z-index:1000}.tox-rtc-user-selection--1 .tox-rtc-user-cursor{background-color:#2dc26b}.tox-rtc-user-selection--2 .tox-rtc-user-cursor{background-color:#e03e2d}.tox-rtc-user-selection--3 .tox-rtc-user-cursor{background-color:#f1c40f}.tox-rtc-user-selection--4 .tox-rtc-user-cursor{background-color:#3598db}.tox-rtc-user-selection--5 .tox-rtc-user-cursor{background-color:#b96ad9}.tox-rtc-user-selection--6 .tox-rtc-user-cursor{background-color:#e67e23}.tox-rtc-user-selection--7 .tox-rtc-user-cursor{background-color:#aaa69d}.tox-rtc-user-selection--8 .tox-rtc-user-cursor{background-color:#f368e0}.tox-rtc-remote-image{background:#eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center;border:1px solid #ccc;min-height:240px;min-width:320px}.mce-match-marker{background:#aaa;color:#fff}.mce-match-marker-selected{background:#39f;color:#fff}.mce-match-marker-selected::-moz-selection{background:#39f;color:#fff}.mce-match-marker-selected::selection{background:#39f;color:#fff}.mce-content-body audio[data-mce-selected],.mce-content-body embed[data-mce-selected],.mce-content-body img[data-mce-selected],.mce-content-body object[data-mce-selected],.mce-content-body table[data-mce-selected],.mce-content-body video[data-mce-selected]{outline:3px solid #b4d7ff}.mce-content-body hr[data-mce-selected]{outline:3px solid #b4d7ff;outline-offset:1px}.mce-content-body [contentEditable=false] [contentEditable=true]:focus{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false] [contentEditable=true]:hover{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false][data-mce-selected]{cursor:not-allowed;outline:3px solid #b4d7ff}.mce-content-body.mce-content-readonly [contentEditable=true]:focus,.mce-content-body.mce-content-readonly [contentEditable=true]:hover{outline:0}.mce-content-body [data-mce-selected=inline-boundary]{background-color:#b4d7ff}.mce-content-body .mce-edit-focus{outline:3px solid #b4d7ff}.mce-content-body td[data-mce-selected],.mce-content-body th[data-mce-selected]{position:relative}.mce-content-body td[data-mce-selected]::-moz-selection,.mce-content-body th[data-mce-selected]::-moz-selection{background:0 0}.mce-content-body td[data-mce-selected]::selection,.mce-content-body th[data-mce-selected]::selection{background:0 0}.mce-content-body td[data-mce-selected] *,.mce-content-body th[data-mce-selected] *{outline:0;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{background-color:rgba(180,215,255,.7);border:1px solid rgba(180,215,255,.7);bottom:-1px;content:'';left:-1px;mix-blend-mode:multiply;position:absolute;right:-1px;top:-1px}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{border-color:rgba(0,84,180,.7)}}.mce-content-body img::-moz-selection{background:0 0}.mce-content-body img::selection{background:0 0}.ephox-snooker-resizer-bar{background-color:#b4d7ff;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:1}.mce-spellchecker-word{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default;height:2rem}.mce-spellchecker-grammar{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-item-table:not([border]),.mce-item-table:not([border]) caption,.mce-item-table:not([border]) td,.mce-item-table:not([border]) th,.mce-item-table[border="0"],.mce-item-table[border="0"] caption,.mce-item-table[border="0"] td,.mce-item-table[border="0"] th,table[style*="border-width: 0px"],table[style*="border-width: 0px"] caption,table[style*="border-width: 0px"] td,table[style*="border-width: 0px"] th{border:1px dashed #bbb}.mce-visualblocks address,.mce-visualblocks article,.mce-visualblocks aside,.mce-visualblocks blockquote,.mce-visualblocks div:not([data-mce-bogus]),.mce-visualblocks dl,.mce-visualblocks figcaption,.mce-visualblocks figure,.mce-visualblocks h1,.mce-visualblocks h2,.mce-visualblocks h3,.mce-visualblocks h4,.mce-visualblocks h5,.mce-visualblocks h6,.mce-visualblocks hgroup,.mce-visualblocks ol,.mce-visualblocks p,.mce-visualblocks pre,.mce-visualblocks section,.mce-visualblocks ul{background-repeat:no-repeat;border:1px dashed #bbb;margin-left:3px;padding-top:10px}.mce-visualblocks p{background-image:url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7)}.mce-visualblocks h1{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==)}.mce-visualblocks h2{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==)}.mce-visualblocks h3{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7)}.mce-visualblocks h4{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==)}.mce-visualblocks h5{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==)}.mce-visualblocks h6{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==)}.mce-visualblocks div:not([data-mce-bogus]){background-image:url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7)}.mce-visualblocks section{background-image:url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=)}.mce-visualblocks article{background-image:url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7)}.mce-visualblocks blockquote{background-image:url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7)}.mce-visualblocks address{background-image:url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=)}.mce-visualblocks pre{background-image:url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==)}.mce-visualblocks figure{background-image:url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7)}.mce-visualblocks figcaption{border:1px dashed #bbb}.mce-visualblocks hgroup{background-image:url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7)}.mce-visualblocks aside{background-image:url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=)}.mce-visualblocks ul{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==)}.mce-visualblocks ol{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==)}.mce-visualblocks dl{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==)}.mce-visualblocks:not([dir=rtl]) address,.mce-visualblocks:not([dir=rtl]) article,.mce-visualblocks:not([dir=rtl]) aside,.mce-visualblocks:not([dir=rtl]) blockquote,.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),.mce-visualblocks:not([dir=rtl]) dl,.mce-visualblocks:not([dir=rtl]) figcaption,.mce-visualblocks:not([dir=rtl]) figure,.mce-visualblocks:not([dir=rtl]) h1,.mce-visualblocks:not([dir=rtl]) h2,.mce-visualblocks:not([dir=rtl]) h3,.mce-visualblocks:not([dir=rtl]) h4,.mce-visualblocks:not([dir=rtl]) h5,.mce-visualblocks:not([dir=rtl]) h6,.mce-visualblocks:not([dir=rtl]) hgroup,.mce-visualblocks:not([dir=rtl]) ol,.mce-visualblocks:not([dir=rtl]) p,.mce-visualblocks:not([dir=rtl]) pre,.mce-visualblocks:not([dir=rtl]) section,.mce-visualblocks:not([dir=rtl]) ul{margin-left:3px}.mce-visualblocks[dir=rtl] address,.mce-visualblocks[dir=rtl] article,.mce-visualblocks[dir=rtl] aside,.mce-visualblocks[dir=rtl] blockquote,.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),.mce-visualblocks[dir=rtl] dl,.mce-visualblocks[dir=rtl] figcaption,.mce-visualblocks[dir=rtl] figure,.mce-visualblocks[dir=rtl] h1,.mce-visualblocks[dir=rtl] h2,.mce-visualblocks[dir=rtl] h3,.mce-visualblocks[dir=rtl] h4,.mce-visualblocks[dir=rtl] h5,.mce-visualblocks[dir=rtl] h6,.mce-visualblocks[dir=rtl] hgroup,.mce-visualblocks[dir=rtl] ol,.mce-visualblocks[dir=rtl] p,.mce-visualblocks[dir=rtl] pre,.mce-visualblocks[dir=rtl] section,.mce-visualblocks[dir=rtl] ul{background-position-x:right;margin-right:3px}.mce-nbsp,.mce-shy{background:#aaa}.mce-shy::after{content:'-'} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.min.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.min.css new file mode 100644 index 0000000..e34ba50 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.mce-content-body .mce-item-anchor{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%20fill%3D%22%23cccccc%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;cursor:default;display:inline-block;height:12px!important;padding:0 2px;-webkit-user-modify:read-only;-moz-user-modify:read-only;-webkit-user-select:all;-moz-user-select:all;-ms-user-select:all;user-select:all;width:8px!important}.mce-content-body .mce-item-anchor[data-mce-selected]{outline-offset:1px}.tox-comments-visible .tox-comment{background-color:#fff0b7}.tox-comments-visible .tox-comment--active{background-color:#ffe168}.tox-checklist>li:not(.tox-checklist--hidden){list-style:none;margin:.25em 0}.tox-checklist>li:not(.tox-checklist--hidden)::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%236d737b%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A");cursor:pointer;height:1em;margin-left:-1.5em;margin-top:.125em;position:absolute;width:1em}.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A")}[dir=rtl] .tox-checklist>li:not(.tox-checklist--hidden)::before{margin-left:0;margin-right:-1.5em}code[class*=language-],pre[class*=language-]{color:#f8f8f2;background:0 0;text-shadow:0 1px rgba(0,0,0,.3);font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto;border-radius:.3em}:not(pre)>code[class*=language-],pre[class*=language-]{background:#282a36}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#6272a4}.token.punctuation{color:#f8f8f2}.namespace{opacity:.7}.token.constant,.token.deleted,.token.property,.token.symbol,.token.tag{color:#ff79c6}.token.boolean,.token.number{color:#bd93f9}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#50fa7b}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url,.token.variable{color:#f8f8f2}.token.atrule,.token.attr-value,.token.class-name,.token.function{color:#f1fa8c}.token.keyword{color:#8be9fd}.token.important,.token.regex{color:#ffb86c}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.mce-content-body{overflow-wrap:break-word;word-wrap:break-word}.mce-content-body .mce-visual-caret{background-color:#000;background-color:currentColor;position:absolute}.mce-content-body .mce-visual-caret-hidden{display:none}.mce-content-body [data-mce-caret]{left:-1000px;margin:0;padding:0;position:absolute;right:auto;top:0}.mce-content-body .mce-offscreen-selection{left:-2000000px;max-width:1000000px;position:absolute}.mce-content-body [contentEditable=false]{cursor:default}.mce-content-body [contentEditable=true]{cursor:text}.tox-cursor-format-painter{cursor:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"),default}.mce-content-body figure.align-left{float:left}.mce-content-body figure.align-right{float:right}.mce-content-body figure.image.align-center{display:table;margin-left:auto;margin-right:auto}.mce-preview-object{border:1px solid gray;display:inline-block;line-height:0;margin:0 2px 0 2px;position:relative}.mce-preview-object .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-object{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%20fill%3D%22%23cccccc%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;border:1px dashed #aaa}.mce-pagebreak{border:1px dashed #aaa;cursor:default;display:block;height:5px;margin-top:15px;page-break-before:always;width:100%}@media print{.mce-pagebreak{border:0}}.tiny-pageembed .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.tiny-pageembed[data-mce-selected="2"] .mce-shim{display:none}.tiny-pageembed{display:inline-block;position:relative}.tiny-pageembed--16by9,.tiny-pageembed--1by1,.tiny-pageembed--21by9,.tiny-pageembed--4by3{display:block;overflow:hidden;padding:0;position:relative;width:100%}.tiny-pageembed--21by9{padding-top:42.857143%}.tiny-pageembed--16by9{padding-top:56.25%}.tiny-pageembed--4by3{padding-top:75%}.tiny-pageembed--1by1{padding-top:100%}.tiny-pageembed--16by9 iframe,.tiny-pageembed--1by1 iframe,.tiny-pageembed--21by9 iframe,.tiny-pageembed--4by3 iframe{border:0;height:100%;left:0;position:absolute;top:0;width:100%}.mce-content-body[data-mce-placeholder]{position:relative}.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before{color:rgba(34,47,62,.7);content:attr(data-mce-placeholder);position:absolute}.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before{left:1px;color:#a1b7cb;}.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before{right:1px}.mce-content-body div.mce-resizehandle{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;height:10px;position:absolute;width:10px;z-index:10000}.mce-content-body div.mce-resizehandle:hover{background-color:#4099ff}.mce-content-body div.mce-resizehandle:nth-of-type(1){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(2){cursor:nesw-resize}.mce-content-body div.mce-resizehandle:nth-of-type(3){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(4){cursor:nesw-resize}.mce-content-body .mce-resize-backdrop{z-index:10000}.mce-content-body .mce-clonedresizable{cursor:default;opacity:.5;outline:1px dashed #000;position:absolute;z-index:10001}.mce-content-body .mce-clonedresizable.mce-resizetable-columns td,.mce-content-body .mce-clonedresizable.mce-resizetable-columns th{border:0}.mce-content-body .mce-resize-helper{background:#555;background:rgba(0,0,0,.75);border:1px;border-radius:3px;color:#fff;display:none;font-family:sans-serif;font-size:12px;line-height:14px;margin:5px 10px;padding:5px;position:absolute;white-space:nowrap;z-index:10002}.tox-rtc-user-selection{position:relative}.tox-rtc-user-cursor{bottom:0;cursor:default;position:absolute;top:0;width:2px}.tox-rtc-user-cursor::before{background-color:inherit;border-radius:50%;content:'';display:block;height:8px;position:absolute;right:-3px;top:-3px;width:8px}.tox-rtc-user-cursor:hover::after{background-color:inherit;border-radius:100px;box-sizing:border-box;color:#fff;content:attr(data-user);display:block;font-size:12px;font-weight:700;left:-5px;min-height:8px;min-width:8px;padding:0 12px;position:absolute;top:-11px;white-space:nowrap;z-index:1000}.tox-rtc-user-selection--1 .tox-rtc-user-cursor{background-color:#2dc26b}.tox-rtc-user-selection--2 .tox-rtc-user-cursor{background-color:#e03e2d}.tox-rtc-user-selection--3 .tox-rtc-user-cursor{background-color:#f1c40f}.tox-rtc-user-selection--4 .tox-rtc-user-cursor{background-color:#3598db}.tox-rtc-user-selection--5 .tox-rtc-user-cursor{background-color:#b96ad9}.tox-rtc-user-selection--6 .tox-rtc-user-cursor{background-color:#e67e23}.tox-rtc-user-selection--7 .tox-rtc-user-cursor{background-color:#aaa69d}.tox-rtc-user-selection--8 .tox-rtc-user-cursor{background-color:#f368e0}.tox-rtc-remote-image{background:#eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center;border:1px solid #ccc;min-height:240px;min-width:320px}.mce-match-marker{background:#aaa;color:#fff}.mce-match-marker-selected{background:#39f;color:#fff}.mce-match-marker-selected::-moz-selection{background:#39f;color:#fff}.mce-match-marker-selected::selection{background:#39f;color:#fff}.mce-content-body audio[data-mce-selected],.mce-content-body embed[data-mce-selected],.mce-content-body img[data-mce-selected],.mce-content-body object[data-mce-selected],.mce-content-body table[data-mce-selected],.mce-content-body video[data-mce-selected]{outline:3px solid #4099ff}.mce-content-body hr[data-mce-selected]{outline:3px solid #4099ff;outline-offset:1px}.mce-content-body [contentEditable=false] [contentEditable=true]:focus{outline:3px solid #4099ff}.mce-content-body [contentEditable=false] [contentEditable=true]:hover{outline:3px solid #4099ff}.mce-content-body [contentEditable=false][data-mce-selected]{cursor:not-allowed;outline:3px solid #4099ff}.mce-content-body.mce-content-readonly [contentEditable=true]:focus,.mce-content-body.mce-content-readonly [contentEditable=true]:hover{outline:0}.mce-content-body [data-mce-selected=inline-boundary]{background-color:#4099ff}.mce-content-body .mce-edit-focus{outline:3px solid #4099ff}.mce-content-body td[data-mce-selected],.mce-content-body th[data-mce-selected]{position:relative}.mce-content-body td[data-mce-selected]::-moz-selection,.mce-content-body th[data-mce-selected]::-moz-selection{background:0 0}.mce-content-body td[data-mce-selected]::selection,.mce-content-body th[data-mce-selected]::selection{background:0 0}.mce-content-body td[data-mce-selected] *,.mce-content-body th[data-mce-selected] *{outline:0;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{background-color:rgba(180,215,255,.7);border:1px solid transparent;bottom:-1px;content:'';left:-1px;mix-blend-mode:lighten;position:absolute;right:-1px;top:-1px}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{border-color:rgba(0,84,180,.7)}}.mce-content-body img::-moz-selection{background:0 0}.mce-content-body img::selection{background:0 0}.ephox-snooker-resizer-bar{background-color:#4099ff;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:1}.mce-spellchecker-word{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default;height:2rem}.mce-spellchecker-grammar{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-item-table:not([border]),.mce-item-table:not([border]) caption,.mce-item-table:not([border]) td,.mce-item-table:not([border]) th,.mce-item-table[border="0"],.mce-item-table[border="0"] caption,.mce-item-table[border="0"] td,.mce-item-table[border="0"] th,table[style*="border-width: 0px"],table[style*="border-width: 0px"] caption,table[style*="border-width: 0px"] td,table[style*="border-width: 0px"] th{border:1px dashed #bbb}.mce-visualblocks address,.mce-visualblocks article,.mce-visualblocks aside,.mce-visualblocks blockquote,.mce-visualblocks div:not([data-mce-bogus]),.mce-visualblocks dl,.mce-visualblocks figcaption,.mce-visualblocks figure,.mce-visualblocks h1,.mce-visualblocks h2,.mce-visualblocks h3,.mce-visualblocks h4,.mce-visualblocks h5,.mce-visualblocks h6,.mce-visualblocks hgroup,.mce-visualblocks ol,.mce-visualblocks p,.mce-visualblocks pre,.mce-visualblocks section,.mce-visualblocks ul{background-repeat:no-repeat;border:1px dashed #bbb;margin-left:3px;padding-top:10px}.mce-visualblocks p{background-image:url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7)}.mce-visualblocks h1{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==)}.mce-visualblocks h2{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==)}.mce-visualblocks h3{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7)}.mce-visualblocks h4{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==)}.mce-visualblocks h5{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==)}.mce-visualblocks h6{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==)}.mce-visualblocks div:not([data-mce-bogus]){background-image:url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7)}.mce-visualblocks section{background-image:url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=)}.mce-visualblocks article{background-image:url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7)}.mce-visualblocks blockquote{background-image:url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7)}.mce-visualblocks address{background-image:url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=)}.mce-visualblocks pre{background-image:url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==)}.mce-visualblocks figure{background-image:url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7)}.mce-visualblocks figcaption{border:1px dashed #bbb}.mce-visualblocks hgroup{background-image:url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7)}.mce-visualblocks aside{background-image:url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=)}.mce-visualblocks ul{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==)}.mce-visualblocks ol{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==)}.mce-visualblocks dl{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==)}.mce-visualblocks:not([dir=rtl]) address,.mce-visualblocks:not([dir=rtl]) article,.mce-visualblocks:not([dir=rtl]) aside,.mce-visualblocks:not([dir=rtl]) blockquote,.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),.mce-visualblocks:not([dir=rtl]) dl,.mce-visualblocks:not([dir=rtl]) figcaption,.mce-visualblocks:not([dir=rtl]) figure,.mce-visualblocks:not([dir=rtl]) h1,.mce-visualblocks:not([dir=rtl]) h2,.mce-visualblocks:not([dir=rtl]) h3,.mce-visualblocks:not([dir=rtl]) h4,.mce-visualblocks:not([dir=rtl]) h5,.mce-visualblocks:not([dir=rtl]) h6,.mce-visualblocks:not([dir=rtl]) hgroup,.mce-visualblocks:not([dir=rtl]) ol,.mce-visualblocks:not([dir=rtl]) p,.mce-visualblocks:not([dir=rtl]) pre,.mce-visualblocks:not([dir=rtl]) section,.mce-visualblocks:not([dir=rtl]) ul{margin-left:3px}.mce-visualblocks[dir=rtl] address,.mce-visualblocks[dir=rtl] article,.mce-visualblocks[dir=rtl] aside,.mce-visualblocks[dir=rtl] blockquote,.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),.mce-visualblocks[dir=rtl] dl,.mce-visualblocks[dir=rtl] figcaption,.mce-visualblocks[dir=rtl] figure,.mce-visualblocks[dir=rtl] h1,.mce-visualblocks[dir=rtl] h2,.mce-visualblocks[dir=rtl] h3,.mce-visualblocks[dir=rtl] h4,.mce-visualblocks[dir=rtl] h5,.mce-visualblocks[dir=rtl] h6,.mce-visualblocks[dir=rtl] hgroup,.mce-visualblocks[dir=rtl] ol,.mce-visualblocks[dir=rtl] p,.mce-visualblocks[dir=rtl] pre,.mce-visualblocks[dir=rtl] section,.mce-visualblocks[dir=rtl] ul{background-position-x:right;margin-right:3px}.mce-nbsp,.mce-shy{background:#aaa}.mce-shy::after{content:'-'}body{font-family:sans-serif}table{border-collapse:collapse} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.mobile.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.mobile.css new file mode 100644 index 0000000..68f5f0f --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.mobile.css @@ -0,0 +1,34 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection { + /* Note: this file is used inside the content, so isn't part of theming */ + background-color: green; + display: inline-block; + opacity: 0.5; + position: absolute; +} + +body { + -webkit-text-size-adjust: none; +} + +body img { + /* this is related to the content margin */ + max-width: 96vw; +} + +body table img { + max-width: 95%; +} + +body { + font-family: sans-serif; +} + +table { + border-collapse: collapse; +} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.mobile.min.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.mobile.min.css new file mode 100644 index 0000000..35f7dc0 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/content.mobile.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/fonts/tinymce-mobile.woff b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/fonts/tinymce-mobile.woff new file mode 100644 index 0000000..1e3be03 Binary files /dev/null and b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/fonts/tinymce-mobile.woff differ diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.css new file mode 100644 index 0000000..1881dc6 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.css @@ -0,0 +1,3616 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.tox { + box-shadow: none; + box-sizing: content-box; + color: #2A3746; + cursor: auto; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-size: 16px; + font-style: normal; + font-weight: normal; + line-height: normal; + -webkit-tap-highlight-color: transparent; + text-decoration: none; + text-shadow: none; + text-transform: none; + vertical-align: initial; + white-space: normal; +} + +.tox *:not(svg):not(rect) { + box-sizing: inherit; + color: inherit; + cursor: inherit; + direction: inherit; + font-family: inherit; + font-size: inherit; + font-style: inherit; + font-weight: inherit; + line-height: inherit; + -webkit-tap-highlight-color: inherit; + text-align: inherit; + text-decoration: inherit; + text-shadow: inherit; + text-transform: inherit; + vertical-align: inherit; + white-space: inherit; +} + +.tox *:not(svg):not(rect) { + /* stylelint-disable-line no-duplicate-selectors */ + background: transparent; + border: 0; + box-shadow: none; + float: none; + height: auto; + margin: 0; + max-width: none; + outline: 0; + padding: 0; + position: static; + width: auto; +} + +.tox:not([dir=rtl]) { + direction: ltr; + text-align: left; +} + +.tox[dir=rtl] { + direction: rtl; + text-align: right; +} + +.tox-tinymce { + border: 1px solid #000000; + border-radius: 0; + box-shadow: none; + box-sizing: border-box; + display: flex; + flex-direction: column; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + overflow: hidden; + position: relative; + visibility: inherit !important; +} + +.tox-tinymce-inline { + max-width: 850px; + border: none; + box-shadow: none; +} + +.tox-tinymce-inline .tox-editor-header { + background-color: transparent; + border: 1px solid #000000; + border-radius: 0; + box-shadow: none; +} + +.tox-tinymce-aux { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + z-index: 1300; +} + +.tox-tinymce *:focus, +.tox-tinymce-aux *:focus { + outline: none; +} + +button::-moz-focus-inner { + border: 0; +} + +.tox .accessibility-issue__header { + align-items: center; + display: flex; + margin-bottom: 4px; +} + +.tox .accessibility-issue__description { + align-items: stretch; + border: 1px solid #000000; + border-radius: 3px; + display: flex; + justify-content: space-between; +} + +.tox .accessibility-issue__description > div { + padding-bottom: 4px; +} + +.tox .accessibility-issue__description > div > div { + align-items: center; + display: flex; + margin-bottom: 4px; +} + +.tox .accessibility-issue__description > *:last-child:not(:only-child) { + border-color: #000000; + border-style: solid; +} + +.tox .accessibility-issue__repair { + margin-top: 16px; +} + +.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description { + background-color: rgba(32, 122, 183, 0.5); + border-color: #207ab7; + color: #fff; +} + +.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description > *:last-child { + border-color: #207ab7; +} + +.tox .tox-dialog__body-content .accessibility-issue--info .tox-form__group h2 { + color: #fff; +} + +.tox .tox-dialog__body-content .accessibility-issue--info .tox-icon svg { + fill: #fff; +} + +.tox .tox-dialog__body-content .accessibility-issue--info a .tox-icon { + color: #fff; +} + +.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description { + background-color: rgba(255, 165, 0, 0.5); + border-color: rgba(255, 165, 0, 0.8); + color: #fff; +} + +.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description > *:last-child { + border-color: rgba(255, 165, 0, 0.8); +} + +.tox .tox-dialog__body-content .accessibility-issue--warn .tox-form__group h2 { + color: #fff; +} + +.tox .tox-dialog__body-content .accessibility-issue--warn .tox-icon svg { + fill: #fff; +} + +.tox .tox-dialog__body-content .accessibility-issue--warn a .tox-icon { + color: #fff; +} + +.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description { + background-color: rgba(204, 0, 0, 0.5); + border-color: rgba(204, 0, 0, 0.8); + color: #fff; +} + +.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description > *:last-child { + border-color: rgba(204, 0, 0, 0.8); +} + +.tox .tox-dialog__body-content .accessibility-issue--error .tox-form__group h2 { + color: #fff; +} + +.tox .tox-dialog__body-content .accessibility-issue--error .tox-icon svg { + fill: #fff; +} + +.tox .tox-dialog__body-content .accessibility-issue--error a .tox-icon { + color: #fff; +} + +.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description { + background-color: rgba(120, 171, 70, 0.5); + border-color: rgba(120, 171, 70, 0.8); + color: #fff; +} + +.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description > *:last-child { + border-color: rgba(120, 171, 70, 0.8); +} + +.tox .tox-dialog__body-content .accessibility-issue--success .tox-form__group h2 { + color: #fff; +} + +.tox .tox-dialog__body-content .accessibility-issue--success .tox-icon svg { + fill: #fff; +} + +.tox .tox-dialog__body-content .accessibility-issue--success a .tox-icon { + color: #fff; +} + +.tox .tox-dialog__body-content .accessibility-issue__header h1, +.tox .tox-dialog__body-content .tox-form__group .accessibility-issue__description h2 { + margin-top: 0; +} + +.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header .tox-button { + margin-left: 4px; +} + +.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header > *:nth-last-child(2) { + margin-left: auto; +} + +.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description { + padding: 4px 4px 4px 8px; +} + +.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description > *:last-child { + border-left-width: 1px; + padding-left: 4px; +} + +.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header .tox-button { + margin-right: 4px; +} + +.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header > *:nth-last-child(2) { + margin-right: auto; +} + +.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description { + padding: 4px 8px 4px 4px; +} + +.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description > *:last-child { + border-right-width: 1px; + padding-right: 4px; +} + +.tox .tox-anchorbar { + display: flex; + flex: 0 0 auto; +} + +.tox .tox-bar { + display: flex; + flex: 0 0 auto; +} + +.tox .tox-button { + background-color: #207ab7; + background-image: none; + background-position: 0 0; + background-repeat: repeat; + border-color: #207ab7; + border-radius: 3px; + border-style: solid; + border-width: 1px; + box-shadow: none; + box-sizing: border-box; + color: #fff; + cursor: pointer; + display: inline-block; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-size: 14px; + font-style: normal; + font-weight: bold; + letter-spacing: normal; + line-height: 24px; + margin: 0; + outline: none; + padding: 4px 16px; + text-align: center; + text-decoration: none; + text-transform: none; + white-space: nowrap; +} + +.tox .tox-button[disabled] { + background-color: #207ab7; + background-image: none; + border-color: #207ab7; + box-shadow: none; + color: rgba(255, 255, 255, 0.5); + cursor: not-allowed; +} + +.tox .tox-button:focus:not(:disabled) { + background-color: #1c6ca1; + background-image: none; + border-color: #1c6ca1; + box-shadow: none; + color: #fff; +} + +.tox .tox-button:hover:not(:disabled) { + background-color: #1c6ca1; + background-image: none; + border-color: #1c6ca1; + box-shadow: none; + color: #fff; +} + +.tox .tox-button:active:not(:disabled) { + background-color: #185d8c; + background-image: none; + border-color: #185d8c; + box-shadow: none; + color: #fff; +} + +.tox .tox-button--secondary { + background-color: #3d546f; + background-image: none; + background-position: 0 0; + background-repeat: repeat; + border-color: #3d546f; + border-radius: 3px; + border-style: solid; + border-width: 1px; + box-shadow: none; + color: #fff; + font-size: 14px; + font-style: normal; + font-weight: bold; + letter-spacing: normal; + outline: none; + padding: 4px 16px; + text-decoration: none; + text-transform: none; +} + +.tox .tox-button--secondary[disabled] { + background-color: #3d546f; + background-image: none; + border-color: #3d546f; + box-shadow: none; + color: rgba(255, 255, 255, 0.5); +} + +.tox .tox-button--secondary:focus:not(:disabled) { + background-color: #34485f; + background-image: none; + border-color: #34485f; + box-shadow: none; + color: #fff; +} + +.tox .tox-button--secondary:hover:not(:disabled) { + background-color: #34485f; + background-image: none; + border-color: #34485f; + box-shadow: none; + color: #fff; +} + +.tox .tox-button--secondary:active:not(:disabled) { + background-color: #2b3b4e; + background-image: none; + border-color: #2b3b4e; + box-shadow: none; + color: #fff; +} + +.tox .tox-button--icon, +.tox .tox-button.tox-button--icon, +.tox .tox-button.tox-button--secondary.tox-button--icon { + padding: 4px; +} + +.tox .tox-button--icon .tox-icon svg, +.tox .tox-button.tox-button--icon .tox-icon svg, +.tox .tox-button.tox-button--secondary.tox-button--icon .tox-icon svg { + display: block; + fill: currentColor; +} + +.tox .tox-button-link { + background: 0; + border: none; + box-sizing: border-box; + cursor: pointer; + display: inline-block; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-size: 16px; + font-weight: normal; + line-height: 1.3; + margin: 0; + padding: 0; + white-space: nowrap; +} + +.tox .tox-button-link--sm { + font-size: 14px; +} + +.tox .tox-button--naked { + background-color: transparent; + border-color: transparent; + box-shadow: unset; + color: #fff; +} + +.tox .tox-button--naked[disabled] { + background-color: #3d546f; + border-color: #3d546f; + box-shadow: none; + color: rgba(255, 255, 255, 0.5); +} + +.tox .tox-button--naked:hover:not(:disabled) { + background-color: #34485f; + border-color: #34485f; + box-shadow: none; + color: #fff; +} + +.tox .tox-button--naked:focus:not(:disabled) { + background-color: #34485f; + border-color: #34485f; + box-shadow: none; + color: #fff; +} + +.tox .tox-button--naked:active:not(:disabled) { + background-color: #2b3b4e; + border-color: #2b3b4e; + box-shadow: none; + color: #fff; +} + +.tox .tox-button--naked .tox-icon svg { + fill: currentColor; +} + +.tox .tox-button--naked.tox-button--icon:hover:not(:disabled) { + color: #fff; +} + +.tox .tox-checkbox { + align-items: center; + border-radius: 3px; + cursor: pointer; + display: flex; + height: 36px; + min-width: 36px; +} + +.tox .tox-checkbox__input { + /* Hide from view but visible to screen readers */ + height: 1px; + overflow: hidden; + position: absolute; + top: auto; + width: 1px; +} + +.tox .tox-checkbox__icons { + align-items: center; + border-radius: 3px; + box-shadow: 0 0 0 2px transparent; + box-sizing: content-box; + display: flex; + height: 24px; + justify-content: center; + padding: calc(4px - 1px); + width: 24px; +} + +.tox .tox-checkbox__icons .tox-checkbox-icon__unchecked svg { + display: block; + fill: rgba(255, 255, 255, 0.2); +} + +.tox .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg { + display: none; + fill: #207ab7; +} + +.tox .tox-checkbox__icons .tox-checkbox-icon__checked svg { + display: none; + fill: #207ab7; +} + +.tox .tox-checkbox--disabled { + color: rgba(255, 255, 255, 0.5); + cursor: not-allowed; +} + +.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__checked svg { + fill: rgba(255, 255, 255, 0.5); +} + +.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__unchecked svg { + fill: rgba(255, 255, 255, 0.5); +} + +.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg { + fill: rgba(255, 255, 255, 0.5); +} + +.tox input.tox-checkbox__input:checked + .tox-checkbox__icons .tox-checkbox-icon__unchecked svg { + display: none; +} + +.tox input.tox-checkbox__input:checked + .tox-checkbox__icons .tox-checkbox-icon__checked svg { + display: block; +} + +.tox input.tox-checkbox__input:indeterminate + .tox-checkbox__icons .tox-checkbox-icon__unchecked svg { + display: none; +} + +.tox input.tox-checkbox__input:indeterminate + .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg { + display: block; +} + +.tox input.tox-checkbox__input:focus + .tox-checkbox__icons { + border-radius: 3px; + box-shadow: inset 0 0 0 1px #207ab7; + padding: calc(4px - 1px); +} + +.tox:not([dir=rtl]) .tox-checkbox__label { + margin-left: 4px; +} + +.tox:not([dir=rtl]) .tox-checkbox__input { + left: -10000px; +} + +.tox:not([dir=rtl]) .tox-bar .tox-checkbox { + margin-left: 4px; +} + +.tox[dir=rtl] .tox-checkbox__label { + margin-right: 4px; +} + +.tox[dir=rtl] .tox-checkbox__input { + right: -10000px; +} + +.tox[dir=rtl] .tox-bar .tox-checkbox { + margin-right: 4px; +} + +.tox { + /* stylelint-disable-next-line no-descending-specificity */ +} + +.tox .tox-collection--toolbar .tox-collection__group { + display: flex; + padding: 0; +} + +.tox .tox-collection--grid .tox-collection__group { + display: flex; + flex-wrap: wrap; + max-height: 208px; + overflow-x: hidden; + overflow-y: auto; + padding: 0; +} + +.tox .tox-collection--list .tox-collection__group { + border-bottom-width: 0; + border-color: #1a1a1a; + border-left-width: 0; + border-right-width: 0; + border-style: solid; + border-top-width: 1px; + padding: 4px 0; +} + +.tox .tox-collection--list .tox-collection__group:first-child { + border-top-width: 0; +} + +.tox .tox-collection__group-heading { + background-color: #333333; + color: #fff; + cursor: default; + font-size: 12px; + font-style: normal; + font-weight: normal; + margin-bottom: 4px; + margin-top: -4px; + padding: 4px 8px; + text-transform: none; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.tox .tox-collection__item { + align-items: center; + color: #fff; + cursor: pointer; + display: flex; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.tox .tox-collection--list .tox-collection__item { + padding: 4px 8px; +} + +.tox .tox-collection--toolbar .tox-collection__item { + border-radius: 3px; + padding: 4px; +} + +.tox .tox-collection--grid .tox-collection__item { + border-radius: 3px; + padding: 4px; +} + +.tox .tox-collection--list .tox-collection__item--enabled { + background-color: #2b3b4e; + color: #fff; +} + +.tox .tox-collection--list .tox-collection__item--active { + background-color: #4a5562; +} + +.tox .tox-collection--toolbar .tox-collection__item--enabled { + background-color: #757d87; + color: #fff; +} + +.tox .tox-collection--toolbar .tox-collection__item--active { + background-color: #4a5562; +} + +.tox .tox-collection--grid .tox-collection__item--enabled { + background-color: #757d87; + color: #fff; +} + +.tox .tox-collection--grid .tox-collection__item--active:not(.tox-collection__item--state-disabled) { + background-color: #4a5562; + color: #fff; +} + +.tox .tox-collection--list .tox-collection__item--active:not(.tox-collection__item--state-disabled) { + color: #fff; +} + +.tox .tox-collection--toolbar .tox-collection__item--active:not(.tox-collection__item--state-disabled) { + color: #fff; +} + +.tox .tox-collection__item--state-disabled { + background-color: transparent; + color: rgba(255, 255, 255, 0.5); + cursor: not-allowed; +} + +.tox .tox-collection__item-icon, +.tox .tox-collection__item-checkmark { + align-items: center; + display: flex; + height: 24px; + justify-content: center; + width: 24px; +} + +.tox .tox-collection__item-icon svg, +.tox .tox-collection__item-checkmark svg { + fill: currentColor; +} + +.tox .tox-collection--toolbar-lg .tox-collection__item-icon { + height: 48px; + width: 48px; +} + +.tox .tox-collection__item-label { + color: currentColor; + display: inline-block; + flex: 1; + -ms-flex-preferred-size: auto; + font-size: 14px; + font-style: normal; + font-weight: normal; + line-height: 24px; + text-transform: none; + word-break: break-all; +} + +.tox .tox-collection__item-accessory { + color: rgba(255, 255, 255, 0.5); + display: inline-block; + font-size: 14px; + height: 24px; + line-height: 24px; + text-transform: none; +} + +.tox .tox-collection__item-caret { + align-items: center; + display: flex; + min-height: 24px; +} + +.tox .tox-collection__item-caret::after { + content: ''; + font-size: 0; + min-height: inherit; +} + +.tox .tox-collection__item-caret svg { + fill: #fff; +} + +.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-checkmark svg { + display: none; +} + +.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-accessory + .tox-collection__item-checkmark { + display: none; +} + +.tox .tox-collection--horizontal { + background-color: #2b3b4e; + border: 1px solid #1a1a1a; + border-radius: 3px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15); + display: flex; + flex: 0 0 auto; + flex-shrink: 0; + flex-wrap: nowrap; + margin-bottom: 0; + overflow-x: auto; + padding: 0; +} + +.tox .tox-collection--horizontal .tox-collection__group { + align-items: center; + display: flex; + flex-wrap: nowrap; + margin: 0; + padding: 0 4px; +} + +.tox .tox-collection--horizontal .tox-collection__item { + height: 34px; + margin: 2px 0 3px 0; + padding: 0 4px; +} + +.tox .tox-collection--horizontal .tox-collection__item-label { + white-space: nowrap; +} + +.tox .tox-collection--horizontal .tox-collection__item-caret { + margin-left: 4px; +} + +.tox .tox-collection__item-container { + display: flex; +} + +.tox .tox-collection__item-container--row { + align-items: center; + flex: 1 1 auto; + flex-direction: row; +} + +.tox .tox-collection__item-container--row.tox-collection__item-container--align-left { + margin-right: auto; +} + +.tox .tox-collection__item-container--row.tox-collection__item-container--align-right { + justify-content: flex-end; + margin-left: auto; +} + +.tox .tox-collection__item-container--row.tox-collection__item-container--valign-top { + align-items: flex-start; + margin-bottom: auto; +} + +.tox .tox-collection__item-container--row.tox-collection__item-container--valign-middle { + align-items: center; +} + +.tox .tox-collection__item-container--row.tox-collection__item-container--valign-bottom { + align-items: flex-end; + margin-top: auto; +} + +.tox .tox-collection__item-container--column { + -ms-grid-row-align: center; + align-self: center; + flex: 1 1 auto; + flex-direction: column; +} + +.tox .tox-collection__item-container--column.tox-collection__item-container--align-left { + align-items: flex-start; +} + +.tox .tox-collection__item-container--column.tox-collection__item-container--align-right { + align-items: flex-end; +} + +.tox .tox-collection__item-container--column.tox-collection__item-container--valign-top { + align-self: flex-start; +} + +.tox .tox-collection__item-container--column.tox-collection__item-container--valign-middle { + -ms-grid-row-align: center; + align-self: center; +} + +.tox .tox-collection__item-container--column.tox-collection__item-container--valign-bottom { + align-self: flex-end; +} + +.tox:not([dir=rtl]) .tox-collection--horizontal .tox-collection__group:not(:last-of-type) { + border-right: 1px solid #000000; +} + +.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item > *:not(:first-child) { + margin-left: 8px; +} + +.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item > .tox-collection__item-label:first-child { + margin-left: 4px; +} + +.tox:not([dir=rtl]) .tox-collection__item-accessory { + margin-left: 16px; + text-align: right; +} + +.tox:not([dir=rtl]) .tox-collection .tox-collection__item-caret { + margin-left: 16px; +} + +.tox[dir=rtl] .tox-collection--horizontal .tox-collection__group:not(:last-of-type) { + border-left: 1px solid #000000; +} + +.tox[dir=rtl] .tox-collection--list .tox-collection__item > *:not(:first-child) { + margin-right: 8px; +} + +.tox[dir=rtl] .tox-collection--list .tox-collection__item > .tox-collection__item-label:first-child { + margin-right: 4px; +} + +.tox[dir=rtl] .tox-collection__item-icon-rtl { + /* stylelint-disable-next-line no-descending-specificity */ +} + +.tox[dir=rtl] .tox-collection__item-icon-rtl .tox-collection__item-icon svg { + transform: rotateY(180deg); +} + +.tox[dir=rtl] .tox-collection__item-accessory { + margin-right: 16px; + text-align: left; +} + +.tox[dir=rtl] .tox-collection .tox-collection__item-caret { + margin-right: 16px; + transform: rotateY(180deg); +} + +.tox[dir=rtl] .tox-collection--horizontal .tox-collection__item-caret { + margin-right: 4px; +} + +.tox .tox-color-picker-container { + display: flex; + flex-direction: row; + height: 225px; + margin: 0; +} + +.tox .tox-sv-palette { + box-sizing: border-box; + display: flex; + height: 100%; +} + +.tox .tox-sv-palette-spectrum { + height: 100%; +} + +.tox .tox-sv-palette, +.tox .tox-sv-palette-spectrum { + width: 225px; +} + +.tox .tox-sv-palette-thumb { + background: none; + border: 1px solid black; + border-radius: 50%; + box-sizing: content-box; + height: 12px; + position: absolute; + width: 12px; +} + +.tox .tox-sv-palette-inner-thumb { + border: 1px solid white; + border-radius: 50%; + height: 10px; + position: absolute; + width: 10px; +} + +.tox .tox-hue-slider { + box-sizing: border-box; + height: 100%; + width: 25px; +} + +.tox .tox-hue-slider-spectrum { + background: linear-gradient(to bottom, #f00, #ff0080, #f0f, #8000ff, #00f, #0080ff, #0ff, #00ff80, #0f0, #80ff00, #ff0, #ff8000, #f00); + height: 100%; + width: 100%; +} + +.tox .tox-hue-slider, +.tox .tox-hue-slider-spectrum { + width: 20px; +} + +.tox .tox-hue-slider-thumb { + background: white; + border: 1px solid black; + box-sizing: content-box; + height: 4px; + width: 100%; +} + +.tox .tox-rgb-form { + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.tox .tox-rgb-form div { + align-items: center; + display: flex; + justify-content: space-between; + margin-bottom: 5px; + width: inherit; +} + +.tox .tox-rgb-form input { + width: 6em; +} + +.tox .tox-rgb-form input.tox-invalid { + /* Need !important to override Chrome's focus styling unfortunately */ + border: 1px solid red !important; +} + +.tox .tox-rgb-form .tox-rgba-preview { + border: 1px solid black; + flex-grow: 2; + margin-bottom: 0; +} + +.tox:not([dir=rtl]) .tox-sv-palette { + margin-right: 15px; +} + +.tox:not([dir=rtl]) .tox-hue-slider { + margin-right: 15px; +} + +.tox:not([dir=rtl]) .tox-hue-slider-thumb { + margin-left: -1px; +} + +.tox:not([dir=rtl]) .tox-rgb-form label { + margin-right: 0.5em; +} + +.tox[dir=rtl] .tox-sv-palette { + margin-left: 15px; +} + +.tox[dir=rtl] .tox-hue-slider { + margin-left: 15px; +} + +.tox[dir=rtl] .tox-hue-slider-thumb { + margin-right: -1px; +} + +.tox[dir=rtl] .tox-rgb-form label { + margin-left: 0.5em; +} + +.tox .tox-toolbar .tox-swatches, +.tox .tox-toolbar__primary .tox-swatches, +.tox .tox-toolbar__overflow .tox-swatches { + margin: 2px 0 3px 4px; +} + +.tox .tox-collection--list .tox-collection__group .tox-swatches-menu { + border: 0; + margin: -4px 0; +} + +.tox .tox-swatches__row { + display: flex; +} + +.tox .tox-swatch { + height: 30px; + transition: transform 0.15s, box-shadow 0.15s; + width: 30px; +} + +.tox .tox-swatch:hover, +.tox .tox-swatch:focus { + box-shadow: 0 0 0 1px rgba(127, 127, 127, 0.3) inset; + transform: scale(0.8); +} + +.tox .tox-swatch--remove { + align-items: center; + display: flex; + justify-content: center; +} + +.tox .tox-swatch--remove svg path { + stroke: #e74c3c; +} + +.tox .tox-swatches__picker-btn { + align-items: center; + background-color: transparent; + border: 0; + cursor: pointer; + display: flex; + height: 30px; + justify-content: center; + outline: none; + padding: 0; + width: 30px; +} + +.tox .tox-swatches__picker-btn svg { + height: 24px; + width: 24px; +} + +.tox .tox-swatches__picker-btn:hover { + background: #4a5562; +} + +.tox:not([dir=rtl]) .tox-swatches__picker-btn { + margin-left: auto; +} + +.tox[dir=rtl] .tox-swatches__picker-btn { + margin-right: auto; +} + +.tox .tox-comment-thread { + background: #2b3b4e; + position: relative; +} + +.tox .tox-comment-thread > *:not(:first-child) { + margin-top: 8px; +} + +.tox .tox-comment { + background: #2b3b4e; + border: 1px solid #000000; + border-radius: 3px; + box-shadow: 0 4px 8px 0 rgba(42, 55, 70, 0.1); + padding: 8px 8px 16px 8px; + position: relative; +} + +.tox .tox-comment__header { + align-items: center; + color: #fff; + display: flex; + justify-content: space-between; +} + +.tox .tox-comment__date { + color: rgba(255, 255, 255, 0.5); + font-size: 12px; +} + +.tox .tox-comment__body { + color: #fff; + font-size: 14px; + font-style: normal; + font-weight: normal; + line-height: 1.3; + margin-top: 8px; + position: relative; + text-transform: initial; +} + +.tox .tox-comment__body textarea { + resize: none; + white-space: normal; + width: 100%; +} + +.tox .tox-comment__expander { + padding-top: 8px; +} + +.tox .tox-comment__expander p { + color: rgba(255, 255, 255, 0.5); + font-size: 14px; + font-style: normal; +} + +.tox .tox-comment__body p { + margin: 0; +} + +.tox .tox-comment__buttonspacing { + padding-top: 16px; + text-align: center; +} + +.tox .tox-comment-thread__overlay::after { + background: #2b3b4e; + bottom: 0; + content: ""; + display: flex; + left: 0; + opacity: 0.9; + position: absolute; + right: 0; + top: 0; + z-index: 5; +} + +.tox .tox-comment__reply { + display: flex; + flex-shrink: 0; + flex-wrap: wrap; + justify-content: flex-end; + margin-top: 8px; +} + +.tox .tox-comment__reply > *:first-child { + margin-bottom: 8px; + width: 100%; +} + +.tox .tox-comment__edit { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + margin-top: 16px; +} + +.tox .tox-comment__gradient::after { + background: linear-gradient(rgba(43, 59, 78, 0), #2b3b4e); + bottom: 0; + content: ""; + display: block; + height: 5em; + margin-top: -40px; + position: absolute; + width: 100%; +} + +.tox .tox-comment__overlay { + background: #2b3b4e; + bottom: 0; + display: flex; + flex-direction: column; + flex-grow: 1; + left: 0; + opacity: 0.9; + position: absolute; + right: 0; + text-align: center; + top: 0; + z-index: 5; +} + +.tox .tox-comment__loading-text { + align-items: center; + color: #fff; + display: flex; + flex-direction: column; + position: relative; +} + +.tox .tox-comment__loading-text > div { + padding-bottom: 16px; +} + +.tox .tox-comment__overlaytext { + bottom: 0; + flex-direction: column; + font-size: 14px; + left: 0; + padding: 1em; + position: absolute; + right: 0; + top: 0; + z-index: 10; +} + +.tox .tox-comment__overlaytext p { + background-color: #2b3b4e; + box-shadow: 0 0 8px 8px #2b3b4e; + color: #fff; + text-align: center; +} + +.tox .tox-comment__overlaytext div:nth-of-type(2) { + font-size: 0.8em; +} + +.tox .tox-comment__busy-spinner { + align-items: center; + background-color: #2b3b4e; + bottom: 0; + display: flex; + justify-content: center; + left: 0; + position: absolute; + right: 0; + top: 0; + z-index: 20; +} + +.tox .tox-comment__scroll { + display: flex; + flex-direction: column; + flex-shrink: 1; + overflow: auto; +} + +.tox .tox-conversations { + margin: 8px; +} + +.tox:not([dir=rtl]) .tox-comment__edit { + margin-left: 8px; +} + +.tox:not([dir=rtl]) .tox-comment__buttonspacing > *:last-child, +.tox:not([dir=rtl]) .tox-comment__edit > *:last-child, +.tox:not([dir=rtl]) .tox-comment__reply > *:last-child { + margin-left: 8px; +} + +.tox[dir=rtl] .tox-comment__edit { + margin-right: 8px; +} + +.tox[dir=rtl] .tox-comment__buttonspacing > *:last-child, +.tox[dir=rtl] .tox-comment__edit > *:last-child, +.tox[dir=rtl] .tox-comment__reply > *:last-child { + margin-right: 8px; +} + +.tox .tox-user { + align-items: center; + display: flex; +} + +.tox .tox-user__avatar svg { + fill: rgba(255, 255, 255, 0.5); +} + +.tox .tox-user__name { + color: rgba(255, 255, 255, 0.5); + font-size: 12px; + font-style: normal; + font-weight: bold; + text-transform: uppercase; +} + +.tox:not([dir=rtl]) .tox-user__avatar svg { + margin-right: 8px; +} + +.tox:not([dir=rtl]) .tox-user__avatar + .tox-user__name { + margin-left: 8px; +} + +.tox[dir=rtl] .tox-user__avatar svg { + margin-left: 8px; +} + +.tox[dir=rtl] .tox-user__avatar + .tox-user__name { + margin-right: 8px; +} + +.tox .tox-dialog-wrap { + align-items: center; + bottom: 0; + display: flex; + justify-content: center; + left: 0; + position: fixed; + right: 0; + top: 0; + z-index: 1100; +} + +.tox .tox-dialog-wrap__backdrop { + background-color: rgba(34, 47, 62, 0.75); + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; + z-index: 1; +} + +.tox .tox-dialog-wrap__backdrop--opaque { + background-color: #222f3e; +} + +.tox .tox-dialog { + background-color: #2b3b4e; + border-color: #000000; + border-radius: 3px; + border-style: solid; + border-width: 1px; + box-shadow: 0 16px 16px -10px rgba(42, 55, 70, 0.15), 0 0 40px 1px rgba(42, 55, 70, 0.15); + display: flex; + flex-direction: column; + max-height: 100%; + max-width: 480px; + overflow: hidden; + position: relative; + width: 95vw; + z-index: 2; +} + +@media only screen and (max-width: 767px) { + body:not(.tox-force-desktop) .tox .tox-dialog { + align-self: flex-start; + margin: 8px auto; + width: calc(100vw - 16px); + } +} + +.tox .tox-dialog-inline { + z-index: 1100; +} + +.tox .tox-dialog__header { + align-items: center; + background-color: #2b3b4e; + border-bottom: none; + color: #fff; + display: flex; + font-size: 16px; + justify-content: space-between; + padding: 8px 16px 0 16px; + position: relative; +} + +.tox .tox-dialog__header .tox-button { + z-index: 1; +} + +.tox .tox-dialog__draghandle { + cursor: grab; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} + +.tox .tox-dialog__draghandle:active { + cursor: grabbing; +} + +.tox .tox-dialog__dismiss { + margin-left: auto; +} + +.tox .tox-dialog__title { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-size: 20px; + font-style: normal; + font-weight: normal; + line-height: 1.3; + margin: 0; + text-transform: none; +} + +.tox .tox-dialog__body { + color: #fff; + display: flex; + flex: 1; + -ms-flex-preferred-size: auto; + font-size: 16px; + font-style: normal; + font-weight: normal; + line-height: 1.3; + min-width: 0; + text-align: left; + text-transform: none; +} + +@media only screen and (max-width: 767px) { + body:not(.tox-force-desktop) .tox .tox-dialog__body { + flex-direction: column; + } +} + +.tox .tox-dialog__body-nav { + align-items: flex-start; + display: flex; + flex-direction: column; + padding: 16px 16px; +} + +@media only screen and (max-width: 767px) { + body:not(.tox-force-desktop) .tox .tox-dialog__body-nav { + flex-direction: row; + -webkit-overflow-scrolling: touch; + overflow-x: auto; + padding-bottom: 0; + } +} + +.tox .tox-dialog__body-nav-item { + border-bottom: 2px solid transparent; + color: rgba(255, 255, 255, 0.5); + display: inline-block; + font-size: 14px; + line-height: 1.3; + margin-bottom: 8px; + text-decoration: none; + white-space: nowrap; +} + +.tox .tox-dialog__body-nav-item:focus { + background-color: rgba(32, 122, 183, 0.1); +} + +.tox .tox-dialog__body-nav-item--active { + border-bottom: 2px solid #207ab7; + color: #207ab7; +} + +.tox .tox-dialog__body-content { + box-sizing: border-box; + display: flex; + flex: 1; + flex-direction: column; + -ms-flex-preferred-size: auto; + max-height: 650px; + overflow: auto; + -webkit-overflow-scrolling: touch; + padding: 16px 16px; +} + +.tox .tox-dialog__body-content > * { + margin-bottom: 0; + margin-top: 16px; +} + +.tox .tox-dialog__body-content > *:first-child { + margin-top: 0; +} + +.tox .tox-dialog__body-content > *:last-child { + margin-bottom: 0; +} + +.tox .tox-dialog__body-content > *:only-child { + margin-bottom: 0; + margin-top: 0; +} + +.tox .tox-dialog__body-content a { + color: #207ab7; + cursor: pointer; + text-decoration: none; +} + +.tox .tox-dialog__body-content a:hover, +.tox .tox-dialog__body-content a:focus { + color: #185d8c; + text-decoration: none; +} + +.tox .tox-dialog__body-content a:active { + color: #185d8c; + text-decoration: none; +} + +.tox .tox-dialog__body-content svg { + fill: #fff; +} + +.tox .tox-dialog__body-content ul { + display: block; + list-style-type: disc; + margin-bottom: 16px; + -webkit-margin-end: 0; + margin-inline-end: 0; + -webkit-margin-start: 0; + margin-inline-start: 0; + -webkit-padding-start: 2.5rem; + padding-inline-start: 2.5rem; +} + +.tox .tox-dialog__body-content .tox-form__group h1 { + color: #fff; + font-size: 20px; + font-style: normal; + font-weight: bold; + letter-spacing: normal; + margin-bottom: 16px; + margin-top: 2rem; + text-transform: none; +} + +.tox .tox-dialog__body-content .tox-form__group h2 { + color: #fff; + font-size: 16px; + font-style: normal; + font-weight: bold; + letter-spacing: normal; + margin-bottom: 16px; + margin-top: 2rem; + text-transform: none; +} + +.tox .tox-dialog__body-content .tox-form__group p { + margin-bottom: 16px; +} + +.tox .tox-dialog__body-content .tox-form__group h1:first-child, +.tox .tox-dialog__body-content .tox-form__group h2:first-child, +.tox .tox-dialog__body-content .tox-form__group p:first-child { + margin-top: 0; +} + +.tox .tox-dialog__body-content .tox-form__group h1:last-child, +.tox .tox-dialog__body-content .tox-form__group h2:last-child, +.tox .tox-dialog__body-content .tox-form__group p:last-child { + margin-bottom: 0; +} + +.tox .tox-dialog__body-content .tox-form__group h1:only-child, +.tox .tox-dialog__body-content .tox-form__group h2:only-child, +.tox .tox-dialog__body-content .tox-form__group p:only-child { + margin-bottom: 0; + margin-top: 0; +} + +.tox .tox-dialog--width-lg { + height: 650px; + max-width: 1200px; +} + +.tox .tox-dialog--width-md { + max-width: 800px; +} + +.tox .tox-dialog--width-md .tox-dialog__body-content { + overflow: auto; +} + +.tox .tox-dialog__body-content--centered { + text-align: center; +} + +.tox .tox-dialog__footer { + align-items: center; + background-color: #2b3b4e; + border-top: 1px solid #000000; + display: flex; + justify-content: space-between; + padding: 8px 16px; +} + +.tox .tox-dialog__footer-start, +.tox .tox-dialog__footer-end { + display: flex; +} + +.tox .tox-dialog__busy-spinner { + align-items: center; + background-color: rgba(34, 47, 62, 0.75); + bottom: 0; + display: flex; + justify-content: center; + left: 0; + position: absolute; + right: 0; + top: 0; + z-index: 3; +} + +.tox .tox-dialog__table { + border-collapse: collapse; + width: 100%; +} + +.tox .tox-dialog__table thead th { + font-weight: bold; + padding-bottom: 8px; +} + +.tox .tox-dialog__table tbody tr { + border-bottom: 1px solid #000000; +} + +.tox .tox-dialog__table tbody tr:last-child { + border-bottom: none; +} + +.tox .tox-dialog__table td { + padding-bottom: 8px; + padding-top: 8px; +} + +.tox .tox-dialog__popups { + position: absolute; + width: 100%; + z-index: 1100; +} + +.tox .tox-dialog__body-iframe { + display: flex; + flex: 1; + flex-direction: column; + -ms-flex-preferred-size: auto; +} + +.tox .tox-dialog__body-iframe .tox-navobj { + display: flex; + flex: 1; + -ms-flex-preferred-size: auto; +} + +.tox .tox-dialog__body-iframe .tox-navobj :nth-child(2) { + flex: 1; + -ms-flex-preferred-size: auto; + height: 100%; +} + +.tox .tox-dialog-dock-fadeout { + opacity: 0; + visibility: hidden; +} + +.tox .tox-dialog-dock-fadein { + opacity: 1; + visibility: visible; +} + +.tox .tox-dialog-dock-transition { + transition: visibility 0s linear 0.3s, opacity 0.3s ease; +} + +.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein { + transition-delay: 0s; +} + +.tox.tox-platform-ie { + /* IE11 CSS styles go here */ +} + +.tox.tox-platform-ie .tox-dialog-wrap { + position: -ms-device-fixed; +} + +@media only screen and (max-width: 767px) { + body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav { + margin-right: 0; + } +} + +@media only screen and (max-width: 767px) { + body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child) { + margin-left: 8px; + } +} + +.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-start > *, +.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-end > * { + margin-left: 8px; +} + +.tox[dir=rtl] .tox-dialog__body { + text-align: right; +} + +@media only screen and (max-width: 767px) { + body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav { + margin-left: 0; + } +} + +@media only screen and (max-width: 767px) { + body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child) { + margin-right: 8px; + } +} + +.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-start > *, +.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-end > * { + margin-right: 8px; +} + +body.tox-dialog__disable-scroll { + overflow: hidden; +} + +.tox .tox-dropzone-container { + display: flex; + flex: 1; + -ms-flex-preferred-size: auto; +} + +.tox .tox-dropzone { + align-items: center; + background: #fff; + border: 2px dashed #000000; + box-sizing: border-box; + display: flex; + flex-direction: column; + flex-grow: 1; + justify-content: center; + min-height: 100px; + padding: 10px; +} + +.tox .tox-dropzone p { + color: rgba(255, 255, 255, 0.5); + margin: 0 0 16px 0; +} + +.tox .tox-edit-area { + display: flex; + flex: 1; + -ms-flex-preferred-size: auto; + overflow: hidden; + position: relative; +} + +.tox .tox-edit-area__iframe { + background-color: #fff; + border: 0; + box-sizing: border-box; + flex: 1; + -ms-flex-preferred-size: auto; + height: 100%; + position: absolute; + width: 100%; +} + +.tox.tox-inline-edit-area { + border: 1px dotted #000000; +} + +.tox .tox-editor-container { + display: flex; + flex: 1 1 auto; + flex-direction: column; + overflow: hidden; +} + +.tox .tox-editor-header { + z-index: 1000; +} + +.tox:not(.tox-tinymce-inline) .tox-editor-header { + box-shadow: none; + transition: box-shadow 0.5s; +} + +.tox.tox-tinymce--toolbar-bottom .tox-editor-header, +.tox.tox-tinymce-inline .tox-editor-header { + margin-bottom: -1px; +} + +.tox.tox-tinymce--toolbar-sticky-on .tox-editor-header { + background-color: transparent; + box-shadow: 0 4px 4px -3px rgba(0, 0, 0, 0.25); +} + +.tox-editor-dock-fadeout { + opacity: 0; + visibility: hidden; +} + +.tox-editor-dock-fadein { + opacity: 1; + visibility: visible; +} + +.tox-editor-dock-transition { + transition: visibility 0s linear 0.25s, opacity 0.25s ease; +} + +.tox-editor-dock-transition.tox-editor-dock-fadein { + transition-delay: 0s; +} + +.tox .tox-control-wrap { + flex: 1; + position: relative; +} + +.tox .tox-control-wrap:not(.tox-control-wrap--status-invalid) .tox-control-wrap__status-icon-invalid, +.tox .tox-control-wrap:not(.tox-control-wrap--status-unknown) .tox-control-wrap__status-icon-unknown, +.tox .tox-control-wrap:not(.tox-control-wrap--status-valid) .tox-control-wrap__status-icon-valid { + display: none; +} + +.tox .tox-control-wrap svg { + display: block; +} + +.tox .tox-control-wrap__status-icon-wrap { + position: absolute; + top: 50%; + transform: translateY(-50%); +} + +.tox .tox-control-wrap__status-icon-invalid svg { + fill: #c00; +} + +.tox .tox-control-wrap__status-icon-unknown svg { + fill: orange; +} + +.tox .tox-control-wrap__status-icon-valid svg { + fill: green; +} + +.tox:not([dir=rtl]) .tox-control-wrap--status-invalid .tox-textfield, +.tox:not([dir=rtl]) .tox-control-wrap--status-unknown .tox-textfield, +.tox:not([dir=rtl]) .tox-control-wrap--status-valid .tox-textfield { + padding-right: 32px; +} + +.tox:not([dir=rtl]) .tox-control-wrap__status-icon-wrap { + right: 4px; +} + +.tox[dir=rtl] .tox-control-wrap--status-invalid .tox-textfield, +.tox[dir=rtl] .tox-control-wrap--status-unknown .tox-textfield, +.tox[dir=rtl] .tox-control-wrap--status-valid .tox-textfield { + padding-left: 32px; +} + +.tox[dir=rtl] .tox-control-wrap__status-icon-wrap { + left: 4px; +} + +.tox .tox-autocompleter { + max-width: 25em; +} + +.tox .tox-autocompleter .tox-menu { + max-width: 25em; +} + +.tox .tox-autocompleter .tox-autocompleter-highlight { + font-weight: bold; +} + +.tox .tox-color-input { + display: flex; + position: relative; + z-index: 1; +} + +.tox .tox-color-input .tox-textfield { + z-index: -1; +} + +.tox .tox-color-input span { + border-color: rgba(42, 55, 70, 0.2); + border-radius: 3px; + border-style: solid; + border-width: 1px; + box-shadow: none; + box-sizing: border-box; + height: 24px; + position: absolute; + top: 6px; + width: 24px; +} + +.tox .tox-color-input span:hover:not([aria-disabled=true]), +.tox .tox-color-input span:focus:not([aria-disabled=true]) { + border-color: #207ab7; + cursor: pointer; +} + +.tox .tox-color-input span::before { + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.25) 25%, transparent 25%), linear-gradient(-45deg, rgba(255, 255, 255, 0.25) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, rgba(255, 255, 255, 0.25) 75%), linear-gradient(-45deg, transparent 75%, rgba(255, 255, 255, 0.25) 75%); + background-position: 0 0, 0 6px, 6px -6px, -6px 0; + background-size: 12px 12px; + border: 1px solid #2b3b4e; + border-radius: 3px; + box-sizing: border-box; + content: ''; + height: 24px; + left: -1px; + position: absolute; + top: -1px; + width: 24px; + z-index: -1; +} + +.tox .tox-color-input span[aria-disabled=true] { + cursor: not-allowed; +} + +.tox:not([dir=rtl]) .tox-color-input { + /* stylelint-disable-next-line no-descending-specificity */ +} + +.tox:not([dir=rtl]) .tox-color-input .tox-textfield { + padding-left: 36px; +} + +.tox:not([dir=rtl]) .tox-color-input span { + left: 6px; +} + +.tox[dir="rtl"] .tox-color-input { + /* stylelint-disable-next-line no-descending-specificity */ +} + +.tox[dir="rtl"] .tox-color-input .tox-textfield { + padding-right: 36px; +} + +.tox[dir="rtl"] .tox-color-input span { + right: 6px; +} + +.tox .tox-label, +.tox .tox-toolbar-label { + color: rgba(255, 255, 255, 0.5); + display: block; + font-size: 14px; + font-style: normal; + font-weight: normal; + line-height: 1.3; + padding: 0 8px 0 0; + text-transform: none; + white-space: nowrap; +} + +.tox .tox-toolbar-label { + padding: 0 8px; +} + +.tox[dir=rtl] .tox-label { + padding: 0 0 0 8px; +} + +.tox .tox-form { + display: flex; + flex: 1; + flex-direction: column; + -ms-flex-preferred-size: auto; +} + +.tox .tox-form__group { + box-sizing: border-box; + margin-bottom: 4px; +} + +.tox .tox-form-group--maximize { + flex: 1; +} + +.tox .tox-form__group--error { + color: #c00; +} + +.tox .tox-form__group--collection { + display: flex; +} + +.tox .tox-form__grid { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; +} + +.tox .tox-form__grid--2col > .tox-form__group { + width: calc(50% - (8px / 2)); +} + +.tox .tox-form__grid--3col > .tox-form__group { + width: calc(100% / 3 - (8px / 2)); +} + +.tox .tox-form__grid--4col > .tox-form__group { + width: calc(25% - (8px / 2)); +} + +.tox .tox-form__controls-h-stack { + align-items: center; + display: flex; +} + +.tox .tox-form__group--inline { + align-items: center; + display: flex; +} + +.tox .tox-form__group--stretched { + display: flex; + flex: 1; + flex-direction: column; + -ms-flex-preferred-size: auto; +} + +.tox .tox-form__group--stretched .tox-textarea { + flex: 1; + -ms-flex-preferred-size: auto; +} + +.tox .tox-form__group--stretched .tox-navobj { + display: flex; + flex: 1; + -ms-flex-preferred-size: auto; +} + +.tox .tox-form__group--stretched .tox-navobj :nth-child(2) { + flex: 1; + -ms-flex-preferred-size: auto; + height: 100%; +} + +.tox:not([dir=rtl]) .tox-form__controls-h-stack > *:not(:first-child) { + margin-left: 4px; +} + +.tox[dir=rtl] .tox-form__controls-h-stack > *:not(:first-child) { + margin-right: 4px; +} + +.tox .tox-lock.tox-locked .tox-lock-icon__unlock, +.tox .tox-lock:not(.tox-locked) .tox-lock-icon__lock { + display: none; +} + +.tox .tox-textfield, +.tox .tox-toolbar-textfield, +.tox .tox-listboxfield .tox-listbox--select, +.tox .tox-textarea { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #2b3b4e; + border-color: #000000; + border-radius: 3px; + border-style: solid; + border-width: 1px; + box-shadow: none; + box-sizing: border-box; + color: #fff; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-size: 16px; + line-height: 24px; + margin: 0; + min-height: 34px; + outline: none; + padding: 5px 4.75px; + resize: none; + width: 100%; +} + +.tox .tox-textfield[disabled], +.tox .tox-textarea[disabled] { + background-color: #222f3e; + color: rgba(255, 255, 255, 0.85); + cursor: not-allowed; +} + +.tox .tox-textfield:focus, +.tox .tox-listboxfield .tox-listbox--select:focus, +.tox .tox-textarea:focus { + background-color: #2b3b4e; + border-color: #207ab7; + box-shadow: none; + outline: none; +} + +.tox .tox-toolbar-textfield { + border-width: 0; + margin-bottom: 3px; + margin-top: 2px; + max-width: 250px; +} + +.tox .tox-naked-btn { + background-color: transparent; + border: 0; + border-color: transparent; + box-shadow: unset; + color: #207ab7; + cursor: pointer; + display: block; + margin: 0; + padding: 0; +} + +.tox .tox-naked-btn svg { + display: block; + fill: #fff; +} + +.tox:not([dir=rtl]) .tox-toolbar-textfield + * { + margin-left: 4px; +} + +.tox[dir=rtl] .tox-toolbar-textfield + * { + margin-right: 4px; +} + +.tox .tox-listboxfield { + cursor: pointer; + position: relative; +} + +.tox .tox-listboxfield .tox-listbox--select[disabled] { + background-color: #19232e; + color: rgba(255, 255, 255, 0.85); + cursor: not-allowed; +} + +.tox .tox-listbox__select-label { + cursor: default; + flex: 1; + margin: 0 4px; +} + +.tox .tox-listbox__select-chevron { + align-items: center; + display: flex; + justify-content: center; + width: 16px; +} + +.tox .tox-listbox__select-chevron svg { + fill: #fff; +} + +.tox .tox-listboxfield .tox-listbox--select { + align-items: center; + display: flex; +} + +.tox:not([dir=rtl]) .tox-listboxfield svg { + right: 8px; +} + +.tox[dir=rtl] .tox-listboxfield svg { + left: 8px; +} + +.tox .tox-selectfield { + cursor: pointer; + position: relative; +} + +.tox .tox-selectfield select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #2b3b4e; + border-color: #000000; + border-radius: 3px; + border-style: solid; + border-width: 1px; + box-shadow: none; + box-sizing: border-box; + color: #fff; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-size: 16px; + line-height: 24px; + margin: 0; + min-height: 34px; + outline: none; + padding: 5px 4.75px; + resize: none; + width: 100%; +} + +.tox .tox-selectfield select[disabled] { + background-color: #19232e; + color: rgba(255, 255, 255, 0.85); + cursor: not-allowed; +} + +.tox .tox-selectfield select::-ms-expand { + display: none; +} + +.tox .tox-selectfield select:focus { + background-color: #2b3b4e; + border-color: #207ab7; + box-shadow: none; + outline: none; +} + +.tox .tox-selectfield svg { + pointer-events: none; + position: absolute; + top: 50%; + transform: translateY(-50%); +} + +.tox:not([dir=rtl]) .tox-selectfield select[size="0"], +.tox:not([dir=rtl]) .tox-selectfield select[size="1"] { + padding-right: 24px; +} + +.tox:not([dir=rtl]) .tox-selectfield svg { + right: 8px; +} + +.tox[dir=rtl] .tox-selectfield select[size="0"], +.tox[dir=rtl] .tox-selectfield select[size="1"] { + padding-left: 24px; +} + +.tox[dir=rtl] .tox-selectfield svg { + left: 8px; +} + +.tox .tox-textarea { + -webkit-appearance: textarea; + -moz-appearance: textarea; + appearance: textarea; + white-space: pre-wrap; +} + +.tox-fullscreen { + border: 0; + height: 100%; + left: 0; + margin: 0; + overflow: hidden; + -ms-scroll-chaining: none; + overscroll-behavior: none; + padding: 0; + position: fixed; + top: 0; + touch-action: pinch-zoom; + width: 100%; +} + +.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle { + display: none; +} + +.tox.tox-tinymce.tox-fullscreen { + background-color: transparent; + z-index: 1200; +} + +.tox-shadowhost.tox-fullscreen { + z-index: 1200; +} + +.tox-fullscreen .tox.tox-tinymce-aux, +.tox-fullscreen ~ .tox.tox-tinymce-aux { + z-index: 1201; +} + +.tox .tox-help__more-link { + list-style: none; + margin-top: 1em; +} + +.tox .tox-image-tools { + width: 100%; +} + +.tox .tox-image-tools__toolbar { + align-items: center; + display: flex; + justify-content: center; +} + +.tox .tox-image-tools__image { + background-color: #666; + height: 380px; + overflow: auto; + position: relative; + width: 100%; +} + +.tox .tox-image-tools__image, +.tox .tox-image-tools__image + .tox-image-tools__toolbar { + margin-top: 8px; +} + +.tox .tox-image-tools__image-bg { + background: url(data:image/gif;base64,R0lGODdhDAAMAIABAMzMzP///ywAAAAADAAMAAACFoQfqYeabNyDMkBQb81Uat85nxguUAEAOw==); +} + +.tox .tox-image-tools__toolbar > .tox-spacer { + flex: 1; + -ms-flex-preferred-size: auto; +} + +.tox .tox-croprect-block { + background: black; + filter: alpha(opacity=50); + opacity: 0.5; + position: absolute; + zoom: 1; +} + +.tox .tox-croprect-handle { + border: 2px solid white; + height: 20px; + left: 0; + position: absolute; + top: 0; + width: 20px; +} + +.tox .tox-croprect-handle-move { + border: 0; + cursor: move; + position: absolute; +} + +.tox .tox-croprect-handle-nw { + border-width: 2px 0 0 2px; + cursor: nw-resize; + left: 100px; + margin: -2px 0 0 -2px; + top: 100px; +} + +.tox .tox-croprect-handle-ne { + border-width: 2px 2px 0 0; + cursor: ne-resize; + left: 200px; + margin: -2px 0 0 -20px; + top: 100px; +} + +.tox .tox-croprect-handle-sw { + border-width: 0 0 2px 2px; + cursor: sw-resize; + left: 100px; + margin: -20px 2px 0 -2px; + top: 200px; +} + +.tox .tox-croprect-handle-se { + border-width: 0 2px 2px 0; + cursor: se-resize; + left: 200px; + margin: -20px 0 0 -20px; + top: 200px; +} + +.tox:not([dir=rtl]) .tox-image-tools__toolbar > .tox-slider:not(:first-of-type) { + margin-left: 8px; +} + +.tox:not([dir=rtl]) .tox-image-tools__toolbar > .tox-button + .tox-slider { + margin-left: 32px; +} + +.tox:not([dir=rtl]) .tox-image-tools__toolbar > .tox-slider + .tox-button { + margin-left: 32px; +} + +.tox[dir=rtl] .tox-image-tools__toolbar > .tox-slider:not(:first-of-type) { + margin-right: 8px; +} + +.tox[dir=rtl] .tox-image-tools__toolbar > .tox-button + .tox-slider { + margin-right: 32px; +} + +.tox[dir=rtl] .tox-image-tools__toolbar > .tox-slider + .tox-button { + margin-right: 32px; +} + +.tox .tox-insert-table-picker { + display: flex; + flex-wrap: wrap; + width: 170px; +} + +.tox .tox-insert-table-picker > div { + border-color: #000000; + border-style: solid; + border-width: 0 1px 1px 0; + box-sizing: border-box; + height: 17px; + width: 17px; +} + +.tox .tox-collection--list .tox-collection__group .tox-insert-table-picker { + margin: -4px 0; +} + +.tox .tox-insert-table-picker .tox-insert-table-picker__selected { + background-color: rgba(32, 122, 183, 0.5); + border-color: rgba(32, 122, 183, 0.5); +} + +.tox .tox-insert-table-picker__label { + color: #fff; + display: block; + font-size: 14px; + padding: 4px; + text-align: center; + width: 100%; +} + +.tox:not([dir=rtl]) { + /* stylelint-disable-next-line no-descending-specificity */ +} + +.tox:not([dir=rtl]) .tox-insert-table-picker > div:nth-child(10n) { + border-right: 0; +} + +.tox[dir=rtl] { + /* stylelint-disable-next-line no-descending-specificity */ +} + +.tox[dir=rtl] .tox-insert-table-picker > div:nth-child(10n+1) { + border-right: 0; +} + +.tox { + /* stylelint-disable */ + /* stylelint-enable */ +} + +.tox .tox-menu { + background-color: #2b3b4e; + border: 1px solid #000000; + border-radius: 3px; + box-shadow: 0 4px 8px 0 rgba(42, 55, 70, 0.1); + display: inline-block; + overflow: hidden; + vertical-align: top; + z-index: 1150; +} + +.tox .tox-menu.tox-collection.tox-collection--list { + padding: 0; +} + +.tox .tox-menu.tox-collection.tox-collection--toolbar { + padding: 4px; +} + +.tox .tox-menu.tox-collection.tox-collection--grid { + padding: 4px; +} + +.tox .tox-menu__label h1, +.tox .tox-menu__label h2, +.tox .tox-menu__label h3, +.tox .tox-menu__label h4, +.tox .tox-menu__label h5, +.tox .tox-menu__label h6, +.tox .tox-menu__label p, +.tox .tox-menu__label blockquote, +.tox .tox-menu__label code { + margin: 0; +} + +.tox .tox-menubar { + background: url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23000000'/%3E%3C/svg%3E") left 0 top 0 #222f3e; + background-color: #222f3e; + display: flex; + flex: 0 0 auto; + flex-shrink: 0; + flex-wrap: wrap; + padding: 0 4px 0 4px; +} + +.tox.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar { + border-top: 1px solid #000000; +} + +/* Deprecated. Remove in next major release */ +.tox .tox-mbtn { + align-items: center; + background: transparent; + border: 0; + border-radius: 3px; + box-shadow: none; + color: #fff; + display: flex; + flex: 0 0 auto; + font-size: 14px; + font-style: normal; + font-weight: normal; + height: 34px; + justify-content: center; + margin: 2px 0 3px 0; + outline: none; + overflow: hidden; + padding: 0 4px; + text-transform: none; + width: auto; +} + +.tox .tox-mbtn[disabled] { + background-color: transparent; + border: 0; + box-shadow: none; + color: rgba(255, 255, 255, 0.5); + cursor: not-allowed; +} + +.tox .tox-mbtn:focus:not(:disabled) { + background: #4a5562; + border: 0; + box-shadow: none; + color: #fff; +} + +.tox .tox-mbtn--active { + background: #757d87; + border: 0; + box-shadow: none; + color: #fff; +} + +.tox .tox-mbtn:hover:not(:disabled):not(.tox-mbtn--active) { + background: #4a5562; + border: 0; + box-shadow: none; + color: #fff; +} + +.tox .tox-mbtn__select-label { + cursor: default; + font-weight: normal; + margin: 0 4px; +} + +.tox .tox-mbtn[disabled] .tox-mbtn__select-label { + cursor: not-allowed; +} + +.tox .tox-mbtn__select-chevron { + align-items: center; + display: flex; + justify-content: center; + width: 16px; + display: none; +} + +.tox .tox-notification { + border-radius: 3px; + border-style: solid; + border-width: 1px; + box-shadow: none; + box-sizing: border-box; + display: -ms-grid; + display: grid; + font-size: 14px; + font-weight: normal; + -ms-grid-columns: minmax(40px, 1fr) auto minmax(40px, 1fr); + grid-template-columns: minmax(40px, 1fr) auto minmax(40px, 1fr); + margin-top: 4px; + opacity: 0; + padding: 4px; + transition: transform 100ms ease-in, opacity 150ms ease-in; +} + +.tox .tox-notification p { + font-size: 14px; + font-weight: normal; +} + +.tox .tox-notification a { + text-decoration: underline; +} + +.tox .tox-notification--in { + opacity: 1; +} + +.tox .tox-notification--success { + background-color: #e4eeda; + border-color: #d7e6c8; + color: #fff; +} + +.tox .tox-notification--success p { + color: #fff; +} + +.tox .tox-notification--success a { + color: #547831; +} + +.tox .tox-notification--success svg { + fill: #fff; +} + +.tox .tox-notification--error { + background-color: #f8dede; + border-color: #f2bfbf; + color: #fff; +} + +.tox .tox-notification--error p { + color: #fff; +} + +.tox .tox-notification--error a { + color: #c00; +} + +.tox .tox-notification--error svg { + fill: #fff; +} + +.tox .tox-notification--warn, +.tox .tox-notification--warning { + background-color: #fffaea; + border-color: #ffe89d; + color: #fff; +} + +.tox .tox-notification--warn p, +.tox .tox-notification--warning p { + color: #fff; +} + +.tox .tox-notification--warn a, +.tox .tox-notification--warning a { + color: #fff; +} + +.tox .tox-notification--warn svg, +.tox .tox-notification--warning svg { + fill: #fff; +} + +.tox .tox-notification--info { + background-color: #d9edf7; + border-color: #779ecb; + color: #fff; +} + +.tox .tox-notification--info p { + color: #fff; +} + +.tox .tox-notification--info a { + color: #fff; +} + +.tox .tox-notification--info svg { + fill: #fff; +} + +.tox .tox-notification__body { + -ms-grid-row-align: center; + align-self: center; + color: #fff; + font-size: 14px; + -ms-grid-column-span: 1; + grid-column-end: 3; + -ms-grid-column: 2; + grid-column-start: 2; + -ms-grid-row-span: 1; + grid-row-end: 2; + -ms-grid-row: 1; + grid-row-start: 1; + text-align: center; + white-space: normal; + word-break: break-all; + word-break: break-word; +} + +.tox .tox-notification__body > * { + margin: 0; +} + +.tox .tox-notification__body > * + * { + margin-top: 1rem; +} + +.tox .tox-notification__icon { + -ms-grid-row-align: center; + align-self: center; + -ms-grid-column-span: 1; + grid-column-end: 2; + -ms-grid-column: 1; + grid-column-start: 1; + -ms-grid-row-span: 1; + grid-row-end: 2; + -ms-grid-row: 1; + grid-row-start: 1; + -ms-grid-column-align: end; + justify-self: end; +} + +.tox .tox-notification__icon svg { + display: block; +} + +.tox .tox-notification__dismiss { + -ms-grid-row-align: start; + align-self: start; + -ms-grid-column-span: 1; + grid-column-end: 4; + -ms-grid-column: 3; + grid-column-start: 3; + -ms-grid-row-span: 1; + grid-row-end: 2; + -ms-grid-row: 1; + grid-row-start: 1; + -ms-grid-column-align: end; + justify-self: end; +} + +.tox .tox-notification .tox-progress-bar { + -ms-grid-column-span: 3; + grid-column-end: 4; + -ms-grid-column: 1; + grid-column-start: 1; + -ms-grid-row-span: 1; + grid-row-end: 3; + -ms-grid-row: 2; + grid-row-start: 2; + -ms-grid-column-align: center; + justify-self: center; +} + +.tox .tox-pop { + display: inline-block; + position: relative; +} + +.tox .tox-pop--resizing { + transition: width 0.1s ease; +} + +.tox .tox-pop--resizing .tox-toolbar { + flex-wrap: nowrap; +} + +.tox .tox-pop__dialog { + background-color: #222f3e; + border: 1px solid #000000; + border-radius: 3px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15); + min-width: 0; + overflow: hidden; +} + +.tox .tox-pop__dialog > *:not(.tox-toolbar) { + margin: 4px 4px 4px 8px; +} + +.tox .tox-pop__dialog .tox-toolbar { + background-color: transparent; + margin-bottom: -1px; +} + +.tox .tox-pop::before, +.tox .tox-pop::after { + border-style: solid; + content: ''; + display: block; + height: 0; + position: absolute; + width: 0; +} + +.tox .tox-pop.tox-pop--bottom::before, +.tox .tox-pop.tox-pop--bottom::after { + left: 50%; + top: 100%; +} + +.tox .tox-pop.tox-pop--bottom::after { + border-color: #222f3e transparent transparent transparent; + border-width: 8px; + margin-left: -8px; + margin-top: -1px; +} + +.tox .tox-pop.tox-pop--bottom::before { + border-color: #000000 transparent transparent transparent; + border-width: 9px; + margin-left: -9px; +} + +.tox .tox-pop.tox-pop--top::before, +.tox .tox-pop.tox-pop--top::after { + left: 50%; + top: 0; + transform: translateY(-100%); +} + +.tox .tox-pop.tox-pop--top::after { + border-color: transparent transparent #222f3e transparent; + border-width: 8px; + margin-left: -8px; + margin-top: 1px; +} + +.tox .tox-pop.tox-pop--top::before { + border-color: transparent transparent #000000 transparent; + border-width: 9px; + margin-left: -9px; +} + +.tox .tox-pop.tox-pop--left::before, +.tox .tox-pop.tox-pop--left::after { + left: 0; + top: calc(50% - 1px); + transform: translateY(-50%); +} + +.tox .tox-pop.tox-pop--left::after { + border-color: transparent #222f3e transparent transparent; + border-width: 8px; + margin-left: -15px; +} + +.tox .tox-pop.tox-pop--left::before { + border-color: transparent #000000 transparent transparent; + border-width: 10px; + margin-left: -19px; +} + +.tox .tox-pop.tox-pop--right::before, +.tox .tox-pop.tox-pop--right::after { + left: 100%; + top: calc(50% + 1px); + transform: translateY(-50%); +} + +.tox .tox-pop.tox-pop--right::after { + border-color: transparent transparent transparent #222f3e; + border-width: 8px; + margin-left: -1px; +} + +.tox .tox-pop.tox-pop--right::before { + border-color: transparent transparent transparent #000000; + border-width: 10px; + margin-left: -1px; +} + +.tox .tox-pop.tox-pop--align-left::before, +.tox .tox-pop.tox-pop--align-left::after { + left: 20px; +} + +.tox .tox-pop.tox-pop--align-right::before, +.tox .tox-pop.tox-pop--align-right::after { + left: calc(100% - 20px); +} + +.tox .tox-sidebar-wrap { + display: flex; + flex-direction: row; + flex-grow: 1; + -ms-flex-preferred-size: 0; + min-height: 0; +} + +.tox .tox-sidebar { + background-color: #222f3e; + display: flex; + flex-direction: row; + justify-content: flex-end; +} + +.tox .tox-sidebar__slider { + display: flex; + overflow: hidden; +} + +.tox .tox-sidebar__pane-container { + display: flex; +} + +.tox .tox-sidebar__pane { + display: flex; +} + +.tox .tox-sidebar--sliding-closed { + opacity: 0; +} + +.tox .tox-sidebar--sliding-open { + opacity: 1; +} + +.tox .tox-sidebar--sliding-growing, +.tox .tox-sidebar--sliding-shrinking { + transition: width 0.5s ease, opacity 0.5s ease; +} + +.tox .tox-selector { + background-color: #4099ff; + border-color: #4099ff; + border-style: solid; + border-width: 1px; + box-sizing: border-box; + display: inline-block; + height: 10px; + position: absolute; + width: 10px; +} + +.tox.tox-platform-touch .tox-selector { + height: 12px; + width: 12px; +} + +.tox .tox-slider { + align-items: center; + display: flex; + flex: 1; + -ms-flex-preferred-size: auto; + height: 24px; + justify-content: center; + position: relative; +} + +.tox .tox-slider__rail { + background-color: transparent; + border: 1px solid #000000; + border-radius: 3px; + height: 10px; + min-width: 120px; + width: 100%; +} + +.tox .tox-slider__handle { + background-color: #207ab7; + border: 2px solid #185d8c; + border-radius: 3px; + box-shadow: none; + height: 24px; + left: 50%; + position: absolute; + top: 50%; + transform: translateX(-50%) translateY(-50%); + width: 14px; +} + +.tox .tox-source-code { + overflow: auto; +} + +.tox .tox-spinner { + display: flex; +} + +.tox .tox-spinner > div { + animation: tam-bouncing-dots 1.5s ease-in-out 0s infinite both; + background-color: rgba(255, 255, 255, 0.5); + border-radius: 100%; + height: 8px; + width: 8px; +} + +.tox .tox-spinner > div:nth-child(1) { + animation-delay: -0.32s; +} + +.tox .tox-spinner > div:nth-child(2) { + animation-delay: -0.16s; +} + +@keyframes tam-bouncing-dots { + 0%, + 80%, + 100% { + transform: scale(0); + } + 40% { + transform: scale(1); + } +} + +.tox:not([dir=rtl]) .tox-spinner > div:not(:first-child) { + margin-left: 4px; +} + +.tox[dir=rtl] .tox-spinner > div:not(:first-child) { + margin-right: 4px; +} + +.tox .tox-statusbar { + align-items: center; + background-color: #222f3e; + border-top: 1px solid #000000; + color: #fff; + display: flex; + flex: 0 0 auto; + font-size: 12px; + font-weight: normal; + height: 18px; + overflow: hidden; + padding: 0 8px; + position: relative; + text-transform: uppercase; +} + +.tox .tox-statusbar__text-container { + display: flex; + flex: 1 1 auto; + justify-content: flex-end; + overflow: hidden; +} + +.tox .tox-statusbar__path { + display: flex; + flex: 1 1 auto; + margin-right: auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.tox .tox-statusbar__path > * { + display: inline; + white-space: nowrap; +} + +.tox .tox-statusbar__wordcount { + flex: 0 0 auto; + margin-left: 1ch; +} + +.tox .tox-statusbar a, +.tox .tox-statusbar__path-item, +.tox .tox-statusbar__wordcount { + color: #fff; + text-decoration: none; +} + +.tox .tox-statusbar a:hover:not(:disabled):not([aria-disabled=true]), +.tox .tox-statusbar__path-item:hover:not(:disabled):not([aria-disabled=true]), +.tox .tox-statusbar__wordcount:hover:not(:disabled):not([aria-disabled=true]), +.tox .tox-statusbar a:focus:not(:disabled):not([aria-disabled=true]), +.tox .tox-statusbar__path-item:focus:not(:disabled):not([aria-disabled=true]), +.tox .tox-statusbar__wordcount:focus:not(:disabled):not([aria-disabled=true]) { + cursor: pointer; + text-decoration: underline; +} + +.tox .tox-statusbar__resize-handle { + align-items: flex-end; + align-self: stretch; + cursor: nwse-resize; + display: flex; + flex: 0 0 auto; + justify-content: flex-end; + margin-left: auto; + margin-right: -8px; + padding-left: 1ch; +} + +.tox .tox-statusbar__resize-handle svg { + display: block; + fill: #fff; +} + +.tox .tox-statusbar__resize-handle:focus svg { + background-color: #4a5562; + border-radius: 1px; + box-shadow: 0 0 0 2px #4a5562; +} + +.tox:not([dir=rtl]) .tox-statusbar__path > * { + margin-right: 4px; +} + +.tox:not([dir=rtl]) .tox-statusbar__branding { + margin-left: 1ch; +} + +.tox[dir=rtl] .tox-statusbar { + flex-direction: row-reverse; +} + +.tox[dir=rtl] .tox-statusbar__path > * { + margin-left: 4px; +} + +.tox .tox-throbber { + z-index: 1299; +} + +.tox .tox-throbber__busy-spinner { + align-items: center; + background-color: rgba(34, 47, 62, 0.6); + bottom: 0; + display: flex; + justify-content: center; + left: 0; + position: absolute; + right: 0; + top: 0; +} + +.tox .tox-tbtn { + align-items: center; + background: transparent; + border: 0; + border-radius: 3px; + box-shadow: none; + color: #fff; + display: flex; + flex: 0 0 auto; + font-size: 14px; + font-style: normal; + font-weight: normal; + height: 34px; + justify-content: center; + margin: 2px 0 3px 0; + outline: none; + overflow: hidden; + padding: 0; + text-transform: none; + width: 34px; +} + +.tox .tox-tbtn svg { + display: block; + fill: #fff; +} + +.tox .tox-tbtn.tox-tbtn-more { + padding-left: 5px; + padding-right: 5px; + width: inherit; +} + +.tox .tox-tbtn:focus { + background: #4a5562; + border: 0; + box-shadow: none; +} + +.tox .tox-tbtn:hover { + background: #4a5562; + border: 0; + box-shadow: none; + color: #fff; +} + +.tox .tox-tbtn:hover svg { + fill: #fff; +} + +.tox .tox-tbtn:active { + background: #757d87; + border: 0; + box-shadow: none; + color: #fff; +} + +.tox .tox-tbtn:active svg { + fill: #fff; +} + +.tox .tox-tbtn--disabled, +.tox .tox-tbtn--disabled:hover, +.tox .tox-tbtn:disabled, +.tox .tox-tbtn:disabled:hover { + background: transparent; + border: 0; + box-shadow: none; + color: rgba(255, 255, 255, 0.5); + cursor: not-allowed; +} + +.tox .tox-tbtn--disabled svg, +.tox .tox-tbtn--disabled:hover svg, +.tox .tox-tbtn:disabled svg, +.tox .tox-tbtn:disabled:hover svg { + /* stylelint-disable-line no-descending-specificity */ + fill: rgba(255, 255, 255, 0.5); +} + +.tox .tox-tbtn--enabled, +.tox .tox-tbtn--enabled:hover { + background: #757d87; + border: 0; + box-shadow: none; + color: #fff; +} + +.tox .tox-tbtn--enabled > *, +.tox .tox-tbtn--enabled:hover > * { + transform: none; +} + +.tox .tox-tbtn--enabled svg, +.tox .tox-tbtn--enabled:hover svg { + /* stylelint-disable-line no-descending-specificity */ + fill: #fff; +} + +.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) { + color: #fff; +} + +.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) svg { + fill: #fff; +} + +.tox .tox-tbtn:active > * { + transform: none; +} + +.tox .tox-tbtn--md { + height: 51px; + width: 51px; +} + +.tox .tox-tbtn--lg { + flex-direction: column; + height: 68px; + width: 68px; +} + +.tox .tox-tbtn--return { + -ms-grid-row-align: stretch; + align-self: stretch; + height: unset; + width: 16px; +} + +.tox .tox-tbtn--labeled { + padding: 0 4px; + width: unset; +} + +.tox .tox-tbtn__vlabel { + display: block; + font-size: 10px; + font-weight: normal; + letter-spacing: -0.025em; + margin-bottom: 4px; + white-space: nowrap; +} + +.tox .tox-tbtn--select { + margin: 2px 0 3px 0; + padding: 0 4px; + width: auto; +} + +.tox .tox-tbtn__select-label { + cursor: default; + font-weight: normal; + margin: 0 4px; +} + +.tox .tox-tbtn__select-chevron { + align-items: center; + display: flex; + justify-content: center; + width: 16px; +} + +.tox .tox-tbtn__select-chevron svg { + fill: rgba(255, 255, 255, 0.5); +} + +.tox .tox-tbtn--bespoke .tox-tbtn__select-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 7em; +} + +.tox .tox-split-button { + border: 0; + border-radius: 3px; + box-sizing: border-box; + display: flex; + margin: 2px 0 3px 0; + overflow: hidden; +} + +.tox .tox-split-button:hover { + box-shadow: 0 0 0 1px #4a5562 inset; +} + +.tox .tox-split-button:focus { + background: #4a5562; + box-shadow: none; + color: #fff; +} + +.tox .tox-split-button > * { + border-radius: 0; +} + +.tox .tox-split-button__chevron { + width: 16px; +} + +.tox .tox-split-button__chevron svg { + fill: rgba(255, 255, 255, 0.5); +} + +.tox .tox-split-button .tox-tbtn { + margin: 0; +} + +.tox.tox-platform-touch .tox-split-button .tox-tbtn:first-child { + width: 30px; +} + +.tox.tox-platform-touch .tox-split-button__chevron { + width: 20px; +} + +.tox .tox-split-button.tox-tbtn--disabled:hover, +.tox .tox-split-button.tox-tbtn--disabled:focus, +.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:hover, +.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:focus { + background: transparent; + box-shadow: none; + color: rgba(255, 255, 255, 0.5); +} + +.tox .tox-toolbar-overlord { + background-color: #222f3e; +} + +.tox .tox-toolbar, +.tox .tox-toolbar__primary, +.tox .tox-toolbar__overflow { + background: url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23000000'/%3E%3C/svg%3E") left 0 top 0 #222f3e; + background-color: #222f3e; + display: flex; + flex: 0 0 auto; + flex-shrink: 0; + flex-wrap: wrap; + padding: 0 0; +} + +.tox .tox-toolbar__overflow.tox-toolbar__overflow--closed { + height: 0; + opacity: 0; + padding-bottom: 0; + padding-top: 0; + visibility: hidden; +} + +.tox .tox-toolbar__overflow--growing { + transition: height 0.3s ease, opacity 0.2s linear 0.1s; +} + +.tox .tox-toolbar__overflow--shrinking { + transition: opacity 0.3s ease, height 0.2s linear 0.1s, visibility 0s linear 0.3s; +} + +.tox .tox-menubar + .tox-toolbar, +.tox .tox-menubar + .tox-toolbar-overlord .tox-toolbar__primary { + border-top: 1px solid #000000; + margin-top: -1px; +} + +.tox .tox-toolbar--scrolling { + flex-wrap: nowrap; + overflow-x: auto; +} + +.tox .tox-pop .tox-toolbar { + border-width: 0; +} + +.tox .tox-toolbar--no-divider { + background-image: none; +} + +.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar:first-child, +.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar-overlord:first-child .tox-toolbar__primary { + border-top: 1px solid #000000; +} + +.tox.tox-tinymce-aux .tox-toolbar__overflow { + background-color: #222f3e; + border: 1px solid #000000; + border-radius: 3px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15); +} + +.tox[dir=rtl] .tox-tbtn__icon-rtl svg { + transform: rotateY(180deg); +} + +.tox .tox-toolbar__group { + align-items: center; + display: flex; + flex-wrap: wrap; + margin: 0 0; + padding: 0 4px 0 4px; +} + +.tox .tox-toolbar__group--pull-right { + margin-left: auto; +} + +.tox .tox-toolbar--scrolling .tox-toolbar__group { + flex-shrink: 0; + flex-wrap: nowrap; +} + +.tox:not([dir=rtl]) .tox-toolbar__group:not(:last-of-type) { + border-right: 1px solid #000000; +} + +.tox[dir=rtl] .tox-toolbar__group:not(:last-of-type) { + border-left: 1px solid #000000; +} + +.tox .tox-tooltip { + display: inline-block; + padding: 8px; + position: relative; +} + +.tox .tox-tooltip__body { + background-color: #3d546f; + border-radius: 3px; + box-shadow: 0 2px 4px rgba(42, 55, 70, 0.3); + color: rgba(255, 255, 255, 0.75); + font-size: 14px; + font-style: normal; + font-weight: normal; + padding: 4px 8px; + text-transform: none; +} + +.tox .tox-tooltip__arrow { + position: absolute; +} + +.tox .tox-tooltip--down .tox-tooltip__arrow { + border-left: 8px solid transparent; + border-right: 8px solid transparent; + border-top: 8px solid #3d546f; + bottom: 0; + left: 50%; + position: absolute; + transform: translateX(-50%); +} + +.tox .tox-tooltip--up .tox-tooltip__arrow { + border-bottom: 8px solid #3d546f; + border-left: 8px solid transparent; + border-right: 8px solid transparent; + left: 50%; + position: absolute; + top: 0; + transform: translateX(-50%); +} + +.tox .tox-tooltip--right .tox-tooltip__arrow { + border-bottom: 8px solid transparent; + border-left: 8px solid #3d546f; + border-top: 8px solid transparent; + position: absolute; + right: 0; + top: 50%; + transform: translateY(-50%); +} + +.tox .tox-tooltip--left .tox-tooltip__arrow { + border-bottom: 8px solid transparent; + border-right: 8px solid #3d546f; + border-top: 8px solid transparent; + left: 0; + position: absolute; + top: 50%; + transform: translateY(-50%); +} + +.tox .tox-well { + border: 1px solid #000000; + border-radius: 3px; + padding: 8px; + width: 100%; +} + +.tox .tox-well > *:first-child { + margin-top: 0; +} + +.tox .tox-well > *:last-child { + margin-bottom: 0; +} + +.tox .tox-well > *:only-child { + margin: 0; +} + +.tox .tox-custom-editor { + border: 1px solid #000000; + border-radius: 3px; + display: flex; + flex: 1; + position: relative; +} + +/* stylelint-disable */ +.tox { + /* stylelint-enable */ +} + +.tox .tox-dialog-loading::before { + background-color: rgba(0, 0, 0, 0.5); + content: ""; + height: 100%; + position: absolute; + width: 100%; + z-index: 1000; +} + +.tox .tox-tab { + cursor: pointer; +} + +.tox .tox-dialog__content-js { + display: flex; + flex: 1; + -ms-flex-preferred-size: auto; +} + +.tox .tox-dialog__body-content .tox-collection { + display: flex; + flex: 1; + -ms-flex-preferred-size: auto; +} + +.tox .tox-image-tools-edit-panel { + height: 60px; +} + +.tox .tox-image-tools__sidebar { + height: 60px; +} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.min.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.min.css new file mode 100644 index 0000000..5683e99 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.tox{box-shadow:none;box-sizing:content-box;color:#2a3746;cursor:auto;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;font-style:normal;font-weight:400;line-height:normal;-webkit-tap-highlight-color:transparent;text-decoration:none;text-shadow:none;text-transform:none;vertical-align:initial;white-space:normal}.tox :not(svg):not(rect){box-sizing:inherit;color:inherit;cursor:inherit;direction:inherit;font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;line-height:inherit;-webkit-tap-highlight-color:inherit;text-align:inherit;text-decoration:inherit;text-shadow:inherit;text-transform:inherit;vertical-align:inherit;white-space:inherit}.tox :not(svg):not(rect){background:0 0;border:0;box-shadow:none;float:none;height:auto;margin:0;max-width:none;outline:0;padding:0;position:static;width:auto}.tox:not([dir=rtl]){direction:ltr;text-align:left}.tox[dir=rtl]{direction:rtl;text-align:right}.tox-tinymce{border:1px solid #000;border-radius:0;box-shadow:none;box-sizing:border-box;display:flex;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;overflow:hidden;position:relative;visibility:inherit!important}.tox-tinymce-inline{max-width:850px;border:none;box-shadow:none}.tox-tinymce-inline .tox-editor-header{background-color:transparent;border:1px solid #000;border-radius:0;box-shadow:none}.tox-tinymce-aux{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;z-index:1300}.tox-tinymce :focus,.tox-tinymce-aux :focus{outline:0}button::-moz-focus-inner{border:0}.tox .accessibility-issue__header{align-items:center;display:flex;margin-bottom:4px}.tox .accessibility-issue__description{align-items:stretch;border:1px solid #000;border-radius:3px;display:flex;justify-content:space-between}.tox .accessibility-issue__description>div{padding-bottom:4px}.tox .accessibility-issue__description>div>div{align-items:center;display:flex;margin-bottom:4px}.tox .accessibility-issue__description>:last-child:not(:only-child){border-color:#000;border-style:solid}.tox .accessibility-issue__repair{margin-top:16px}.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description{background-color:rgba(32,122,183,.5);border-color:#207ab7;color:#fff}.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description>:last-child{border-color:#207ab7}.tox .tox-dialog__body-content .accessibility-issue--info .tox-form__group h2{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--info .tox-icon svg{fill:#fff}.tox .tox-dialog__body-content .accessibility-issue--info a .tox-icon{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description{background-color:rgba(255,165,0,.5);border-color:rgba(255,165,0,.8);color:#fff}.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description>:last-child{border-color:rgba(255,165,0,.8)}.tox .tox-dialog__body-content .accessibility-issue--warn .tox-form__group h2{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--warn .tox-icon svg{fill:#fff}.tox .tox-dialog__body-content .accessibility-issue--warn a .tox-icon{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description{background-color:rgba(204,0,0,.5);border-color:rgba(204,0,0,.8);color:#fff}.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description>:last-child{border-color:rgba(204,0,0,.8)}.tox .tox-dialog__body-content .accessibility-issue--error .tox-form__group h2{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--error .tox-icon svg{fill:#fff}.tox .tox-dialog__body-content .accessibility-issue--error a .tox-icon{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description{background-color:rgba(120,171,70,.5);border-color:rgba(120,171,70,.8);color:#fff}.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description>:last-child{border-color:rgba(120,171,70,.8)}.tox .tox-dialog__body-content .accessibility-issue--success .tox-form__group h2{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--success .tox-icon svg{fill:#fff}.tox .tox-dialog__body-content .accessibility-issue--success a .tox-icon{color:#fff}.tox .tox-dialog__body-content .accessibility-issue__header h1,.tox .tox-dialog__body-content .tox-form__group .accessibility-issue__description h2{margin-top:0}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header .tox-button{margin-left:4px}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header>:nth-last-child(2){margin-left:auto}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description{padding:4px 4px 4px 8px}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description>:last-child{border-left-width:1px;padding-left:4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header .tox-button{margin-right:4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header>:nth-last-child(2){margin-right:auto}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description{padding:4px 8px 4px 4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description>:last-child{border-right-width:1px;padding-right:4px}.tox .tox-anchorbar{display:flex;flex:0 0 auto}.tox .tox-bar{display:flex;flex:0 0 auto}.tox .tox-button{background-color:#207ab7;background-image:none;background-position:0 0;background-repeat:repeat;border-color:#207ab7;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#fff;cursor:pointer;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:14px;font-style:normal;font-weight:700;letter-spacing:normal;line-height:24px;margin:0;outline:0;padding:4px 16px;text-align:center;text-decoration:none;text-transform:none;white-space:nowrap}.tox .tox-button[disabled]{background-color:#207ab7;background-image:none;border-color:#207ab7;box-shadow:none;color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-button:focus:not(:disabled){background-color:#1c6ca1;background-image:none;border-color:#1c6ca1;box-shadow:none;color:#fff}.tox .tox-button:hover:not(:disabled){background-color:#1c6ca1;background-image:none;border-color:#1c6ca1;box-shadow:none;color:#fff}.tox .tox-button:active:not(:disabled){background-color:#185d8c;background-image:none;border-color:#185d8c;box-shadow:none;color:#fff}.tox .tox-button--secondary{background-color:#3d546f;background-image:none;background-position:0 0;background-repeat:repeat;border-color:#3d546f;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;color:#fff;font-size:14px;font-style:normal;font-weight:700;letter-spacing:normal;outline:0;padding:4px 16px;text-decoration:none;text-transform:none}.tox .tox-button--secondary[disabled]{background-color:#3d546f;background-image:none;border-color:#3d546f;box-shadow:none;color:rgba(255,255,255,.5)}.tox .tox-button--secondary:focus:not(:disabled){background-color:#34485f;background-image:none;border-color:#34485f;box-shadow:none;color:#fff}.tox .tox-button--secondary:hover:not(:disabled){background-color:#34485f;background-image:none;border-color:#34485f;box-shadow:none;color:#fff}.tox .tox-button--secondary:active:not(:disabled){background-color:#2b3b4e;background-image:none;border-color:#2b3b4e;box-shadow:none;color:#fff}.tox .tox-button--icon,.tox .tox-button.tox-button--icon,.tox .tox-button.tox-button--secondary.tox-button--icon{padding:4px}.tox .tox-button--icon .tox-icon svg,.tox .tox-button.tox-button--icon .tox-icon svg,.tox .tox-button.tox-button--secondary.tox-button--icon .tox-icon svg{display:block;fill:currentColor}.tox .tox-button-link{background:0;border:none;box-sizing:border-box;cursor:pointer;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;white-space:nowrap}.tox .tox-button-link--sm{font-size:14px}.tox .tox-button--naked{background-color:transparent;border-color:transparent;box-shadow:unset;color:#fff}.tox .tox-button--naked[disabled]{background-color:#3d546f;border-color:#3d546f;box-shadow:none;color:rgba(255,255,255,.5)}.tox .tox-button--naked:hover:not(:disabled){background-color:#34485f;border-color:#34485f;box-shadow:none;color:#fff}.tox .tox-button--naked:focus:not(:disabled){background-color:#34485f;border-color:#34485f;box-shadow:none;color:#fff}.tox .tox-button--naked:active:not(:disabled){background-color:#2b3b4e;border-color:#2b3b4e;box-shadow:none;color:#fff}.tox .tox-button--naked .tox-icon svg{fill:currentColor}.tox .tox-button--naked.tox-button--icon:hover:not(:disabled){color:#fff}.tox .tox-checkbox{align-items:center;border-radius:3px;cursor:pointer;display:flex;height:36px;min-width:36px}.tox .tox-checkbox__input{height:1px;overflow:hidden;position:absolute;top:auto;width:1px}.tox .tox-checkbox__icons{align-items:center;border-radius:3px;box-shadow:0 0 0 2px transparent;box-sizing:content-box;display:flex;height:24px;justify-content:center;padding:calc(4px - 1px);width:24px}.tox .tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:block;fill:rgba(255,255,255,.2)}.tox .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{display:none;fill:#207ab7}.tox .tox-checkbox__icons .tox-checkbox-icon__checked svg{display:none;fill:#207ab7}.tox .tox-checkbox--disabled{color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__checked svg{fill:rgba(255,255,255,.5)}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__unchecked svg{fill:rgba(255,255,255,.5)}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{fill:rgba(255,255,255,.5)}.tox input.tox-checkbox__input:checked+.tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:none}.tox input.tox-checkbox__input:checked+.tox-checkbox__icons .tox-checkbox-icon__checked svg{display:block}.tox input.tox-checkbox__input:indeterminate+.tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:none}.tox input.tox-checkbox__input:indeterminate+.tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{display:block}.tox input.tox-checkbox__input:focus+.tox-checkbox__icons{border-radius:3px;box-shadow:inset 0 0 0 1px #207ab7;padding:calc(4px - 1px)}.tox:not([dir=rtl]) .tox-checkbox__label{margin-left:4px}.tox:not([dir=rtl]) .tox-checkbox__input{left:-10000px}.tox:not([dir=rtl]) .tox-bar .tox-checkbox{margin-left:4px}.tox[dir=rtl] .tox-checkbox__label{margin-right:4px}.tox[dir=rtl] .tox-checkbox__input{right:-10000px}.tox[dir=rtl] .tox-bar .tox-checkbox{margin-right:4px}.tox .tox-collection--toolbar .tox-collection__group{display:flex;padding:0}.tox .tox-collection--grid .tox-collection__group{display:flex;flex-wrap:wrap;max-height:208px;overflow-x:hidden;overflow-y:auto;padding:0}.tox .tox-collection--list .tox-collection__group{border-bottom-width:0;border-color:#1a1a1a;border-left-width:0;border-right-width:0;border-style:solid;border-top-width:1px;padding:4px 0}.tox .tox-collection--list .tox-collection__group:first-child{border-top-width:0}.tox .tox-collection__group-heading{background-color:#333;color:#fff;cursor:default;font-size:12px;font-style:normal;font-weight:400;margin-bottom:4px;margin-top:-4px;padding:4px 8px;text-transform:none;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tox .tox-collection__item{align-items:center;color:#fff;cursor:pointer;display:flex;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tox .tox-collection--list .tox-collection__item{padding:4px 8px}.tox .tox-collection--toolbar .tox-collection__item{border-radius:3px;padding:4px}.tox .tox-collection--grid .tox-collection__item{border-radius:3px;padding:4px}.tox .tox-collection--list .tox-collection__item--enabled{background-color:#2b3b4e;color:#fff}.tox .tox-collection--list .tox-collection__item--active{background-color:#4a5562}.tox .tox-collection--toolbar .tox-collection__item--enabled{background-color:#757d87;color:#fff}.tox .tox-collection--toolbar .tox-collection__item--active{background-color:#4a5562}.tox .tox-collection--grid .tox-collection__item--enabled{background-color:#757d87;color:#fff}.tox .tox-collection--grid .tox-collection__item--active:not(.tox-collection__item--state-disabled){background-color:#4a5562;color:#fff}.tox .tox-collection--list .tox-collection__item--active:not(.tox-collection__item--state-disabled){color:#fff}.tox .tox-collection--toolbar .tox-collection__item--active:not(.tox-collection__item--state-disabled){color:#fff}.tox .tox-collection__item--state-disabled{background-color:transparent;color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-collection__item-checkmark,.tox .tox-collection__item-icon{align-items:center;display:flex;height:24px;justify-content:center;width:24px}.tox .tox-collection__item-checkmark svg,.tox .tox-collection__item-icon svg{fill:currentColor}.tox .tox-collection--toolbar-lg .tox-collection__item-icon{height:48px;width:48px}.tox .tox-collection__item-label{color:currentColor;display:inline-block;flex:1;-ms-flex-preferred-size:auto;font-size:14px;font-style:normal;font-weight:400;line-height:24px;text-transform:none;word-break:break-all}.tox .tox-collection__item-accessory{color:rgba(255,255,255,.5);display:inline-block;font-size:14px;height:24px;line-height:24px;text-transform:none}.tox .tox-collection__item-caret{align-items:center;display:flex;min-height:24px}.tox .tox-collection__item-caret::after{content:'';font-size:0;min-height:inherit}.tox .tox-collection__item-caret svg{fill:#fff}.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-checkmark svg{display:none}.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-accessory+.tox-collection__item-checkmark{display:none}.tox .tox-collection--horizontal{background-color:#2b3b4e;border:1px solid #1a1a1a;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15);display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:nowrap;margin-bottom:0;overflow-x:auto;padding:0}.tox .tox-collection--horizontal .tox-collection__group{align-items:center;display:flex;flex-wrap:nowrap;margin:0;padding:0 4px}.tox .tox-collection--horizontal .tox-collection__item{height:34px;margin:2px 0 3px 0;padding:0 4px}.tox .tox-collection--horizontal .tox-collection__item-label{white-space:nowrap}.tox .tox-collection--horizontal .tox-collection__item-caret{margin-left:4px}.tox .tox-collection__item-container{display:flex}.tox .tox-collection__item-container--row{align-items:center;flex:1 1 auto;flex-direction:row}.tox .tox-collection__item-container--row.tox-collection__item-container--align-left{margin-right:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--align-right{justify-content:flex-end;margin-left:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-top{align-items:flex-start;margin-bottom:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-middle{align-items:center}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-bottom{align-items:flex-end;margin-top:auto}.tox .tox-collection__item-container--column{-ms-grid-row-align:center;align-self:center;flex:1 1 auto;flex-direction:column}.tox .tox-collection__item-container--column.tox-collection__item-container--align-left{align-items:flex-start}.tox .tox-collection__item-container--column.tox-collection__item-container--align-right{align-items:flex-end}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-top{align-self:flex-start}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-middle{-ms-grid-row-align:center;align-self:center}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-bottom{align-self:flex-end}.tox:not([dir=rtl]) .tox-collection--horizontal .tox-collection__group:not(:last-of-type){border-right:1px solid #000}.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item>:not(:first-child){margin-left:8px}.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item>.tox-collection__item-label:first-child{margin-left:4px}.tox:not([dir=rtl]) .tox-collection__item-accessory{margin-left:16px;text-align:right}.tox:not([dir=rtl]) .tox-collection .tox-collection__item-caret{margin-left:16px}.tox[dir=rtl] .tox-collection--horizontal .tox-collection__group:not(:last-of-type){border-left:1px solid #000}.tox[dir=rtl] .tox-collection--list .tox-collection__item>:not(:first-child){margin-right:8px}.tox[dir=rtl] .tox-collection--list .tox-collection__item>.tox-collection__item-label:first-child{margin-right:4px}.tox[dir=rtl] .tox-collection__item-icon-rtl .tox-collection__item-icon svg{transform:rotateY(180deg)}.tox[dir=rtl] .tox-collection__item-accessory{margin-right:16px;text-align:left}.tox[dir=rtl] .tox-collection .tox-collection__item-caret{margin-right:16px;transform:rotateY(180deg)}.tox[dir=rtl] .tox-collection--horizontal .tox-collection__item-caret{margin-right:4px}.tox .tox-color-picker-container{display:flex;flex-direction:row;height:225px;margin:0}.tox .tox-sv-palette{box-sizing:border-box;display:flex;height:100%}.tox .tox-sv-palette-spectrum{height:100%}.tox .tox-sv-palette,.tox .tox-sv-palette-spectrum{width:225px}.tox .tox-sv-palette-thumb{background:0 0;border:1px solid #000;border-radius:50%;box-sizing:content-box;height:12px;position:absolute;width:12px}.tox .tox-sv-palette-inner-thumb{border:1px solid #fff;border-radius:50%;height:10px;position:absolute;width:10px}.tox .tox-hue-slider{box-sizing:border-box;height:100%;width:25px}.tox .tox-hue-slider-spectrum{background:linear-gradient(to bottom,red,#ff0080,#f0f,#8000ff,#00f,#0080ff,#0ff,#00ff80,#0f0,#80ff00,#ff0,#ff8000,red);height:100%;width:100%}.tox .tox-hue-slider,.tox .tox-hue-slider-spectrum{width:20px}.tox .tox-hue-slider-thumb{background:#fff;border:1px solid #000;box-sizing:content-box;height:4px;width:100%}.tox .tox-rgb-form{display:flex;flex-direction:column;justify-content:space-between}.tox .tox-rgb-form div{align-items:center;display:flex;justify-content:space-between;margin-bottom:5px;width:inherit}.tox .tox-rgb-form input{width:6em}.tox .tox-rgb-form input.tox-invalid{border:1px solid red!important}.tox .tox-rgb-form .tox-rgba-preview{border:1px solid #000;flex-grow:2;margin-bottom:0}.tox:not([dir=rtl]) .tox-sv-palette{margin-right:15px}.tox:not([dir=rtl]) .tox-hue-slider{margin-right:15px}.tox:not([dir=rtl]) .tox-hue-slider-thumb{margin-left:-1px}.tox:not([dir=rtl]) .tox-rgb-form label{margin-right:.5em}.tox[dir=rtl] .tox-sv-palette{margin-left:15px}.tox[dir=rtl] .tox-hue-slider{margin-left:15px}.tox[dir=rtl] .tox-hue-slider-thumb{margin-right:-1px}.tox[dir=rtl] .tox-rgb-form label{margin-left:.5em}.tox .tox-toolbar .tox-swatches,.tox .tox-toolbar__overflow .tox-swatches,.tox .tox-toolbar__primary .tox-swatches{margin:2px 0 3px 4px}.tox .tox-collection--list .tox-collection__group .tox-swatches-menu{border:0;margin:-4px 0}.tox .tox-swatches__row{display:flex}.tox .tox-swatch{height:30px;transition:transform .15s,box-shadow .15s;width:30px}.tox .tox-swatch:focus,.tox .tox-swatch:hover{box-shadow:0 0 0 1px rgba(127,127,127,.3) inset;transform:scale(.8)}.tox .tox-swatch--remove{align-items:center;display:flex;justify-content:center}.tox .tox-swatch--remove svg path{stroke:#e74c3c}.tox .tox-swatches__picker-btn{align-items:center;background-color:transparent;border:0;cursor:pointer;display:flex;height:30px;justify-content:center;outline:0;padding:0;width:30px}.tox .tox-swatches__picker-btn svg{height:24px;width:24px}.tox .tox-swatches__picker-btn:hover{background:#4a5562}.tox:not([dir=rtl]) .tox-swatches__picker-btn{margin-left:auto}.tox[dir=rtl] .tox-swatches__picker-btn{margin-right:auto}.tox .tox-comment-thread{background:#2b3b4e;position:relative}.tox .tox-comment-thread>:not(:first-child){margin-top:8px}.tox .tox-comment{background:#2b3b4e;border:1px solid #000;border-radius:3px;box-shadow:0 4px 8px 0 rgba(42,55,70,.1);padding:8px 8px 16px 8px;position:relative}.tox .tox-comment__header{align-items:center;color:#fff;display:flex;justify-content:space-between}.tox .tox-comment__date{color:rgba(255,255,255,.5);font-size:12px}.tox .tox-comment__body{color:#fff;font-size:14px;font-style:normal;font-weight:400;line-height:1.3;margin-top:8px;position:relative;text-transform:initial}.tox .tox-comment__body textarea{resize:none;white-space:normal;width:100%}.tox .tox-comment__expander{padding-top:8px}.tox .tox-comment__expander p{color:rgba(255,255,255,.5);font-size:14px;font-style:normal}.tox .tox-comment__body p{margin:0}.tox .tox-comment__buttonspacing{padding-top:16px;text-align:center}.tox .tox-comment-thread__overlay::after{background:#2b3b4e;bottom:0;content:"";display:flex;left:0;opacity:.9;position:absolute;right:0;top:0;z-index:5}.tox .tox-comment__reply{display:flex;flex-shrink:0;flex-wrap:wrap;justify-content:flex-end;margin-top:8px}.tox .tox-comment__reply>:first-child{margin-bottom:8px;width:100%}.tox .tox-comment__edit{display:flex;flex-wrap:wrap;justify-content:flex-end;margin-top:16px}.tox .tox-comment__gradient::after{background:linear-gradient(rgba(43,59,78,0),#2b3b4e);bottom:0;content:"";display:block;height:5em;margin-top:-40px;position:absolute;width:100%}.tox .tox-comment__overlay{background:#2b3b4e;bottom:0;display:flex;flex-direction:column;flex-grow:1;left:0;opacity:.9;position:absolute;right:0;text-align:center;top:0;z-index:5}.tox .tox-comment__loading-text{align-items:center;color:#fff;display:flex;flex-direction:column;position:relative}.tox .tox-comment__loading-text>div{padding-bottom:16px}.tox .tox-comment__overlaytext{bottom:0;flex-direction:column;font-size:14px;left:0;padding:1em;position:absolute;right:0;top:0;z-index:10}.tox .tox-comment__overlaytext p{background-color:#2b3b4e;box-shadow:0 0 8px 8px #2b3b4e;color:#fff;text-align:center}.tox .tox-comment__overlaytext div:nth-of-type(2){font-size:.8em}.tox .tox-comment__busy-spinner{align-items:center;background-color:#2b3b4e;bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0;z-index:20}.tox .tox-comment__scroll{display:flex;flex-direction:column;flex-shrink:1;overflow:auto}.tox .tox-conversations{margin:8px}.tox:not([dir=rtl]) .tox-comment__edit{margin-left:8px}.tox:not([dir=rtl]) .tox-comment__buttonspacing>:last-child,.tox:not([dir=rtl]) .tox-comment__edit>:last-child,.tox:not([dir=rtl]) .tox-comment__reply>:last-child{margin-left:8px}.tox[dir=rtl] .tox-comment__edit{margin-right:8px}.tox[dir=rtl] .tox-comment__buttonspacing>:last-child,.tox[dir=rtl] .tox-comment__edit>:last-child,.tox[dir=rtl] .tox-comment__reply>:last-child{margin-right:8px}.tox .tox-user{align-items:center;display:flex}.tox .tox-user__avatar svg{fill:rgba(255,255,255,.5)}.tox .tox-user__name{color:rgba(255,255,255,.5);font-size:12px;font-style:normal;font-weight:700;text-transform:uppercase}.tox:not([dir=rtl]) .tox-user__avatar svg{margin-right:8px}.tox:not([dir=rtl]) .tox-user__avatar+.tox-user__name{margin-left:8px}.tox[dir=rtl] .tox-user__avatar svg{margin-left:8px}.tox[dir=rtl] .tox-user__avatar+.tox-user__name{margin-right:8px}.tox .tox-dialog-wrap{align-items:center;bottom:0;display:flex;justify-content:center;left:0;position:fixed;right:0;top:0;z-index:1100}.tox .tox-dialog-wrap__backdrop{background-color:rgba(34,47,62,.75);bottom:0;left:0;position:absolute;right:0;top:0;z-index:1}.tox .tox-dialog-wrap__backdrop--opaque{background-color:#222f3e}.tox .tox-dialog{background-color:#2b3b4e;border-color:#000;border-radius:3px;border-style:solid;border-width:1px;box-shadow:0 16px 16px -10px rgba(42,55,70,.15),0 0 40px 1px rgba(42,55,70,.15);display:flex;flex-direction:column;max-height:100%;max-width:480px;overflow:hidden;position:relative;width:95vw;z-index:2}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog{align-self:flex-start;margin:8px auto;width:calc(100vw - 16px)}}.tox .tox-dialog-inline{z-index:1100}.tox .tox-dialog__header{align-items:center;background-color:#2b3b4e;border-bottom:none;color:#fff;display:flex;font-size:16px;justify-content:space-between;padding:8px 16px 0 16px;position:relative}.tox .tox-dialog__header .tox-button{z-index:1}.tox .tox-dialog__draghandle{cursor:grab;height:100%;left:0;position:absolute;top:0;width:100%}.tox .tox-dialog__draghandle:active{cursor:grabbing}.tox .tox-dialog__dismiss{margin-left:auto}.tox .tox-dialog__title{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:20px;font-style:normal;font-weight:400;line-height:1.3;margin:0;text-transform:none}.tox .tox-dialog__body{color:#fff;display:flex;flex:1;-ms-flex-preferred-size:auto;font-size:16px;font-style:normal;font-weight:400;line-height:1.3;min-width:0;text-align:left;text-transform:none}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog__body{flex-direction:column}}.tox .tox-dialog__body-nav{align-items:flex-start;display:flex;flex-direction:column;padding:16px 16px}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog__body-nav{flex-direction:row;-webkit-overflow-scrolling:touch;overflow-x:auto;padding-bottom:0}}.tox .tox-dialog__body-nav-item{border-bottom:2px solid transparent;color:rgba(255,255,255,.5);display:inline-block;font-size:14px;line-height:1.3;margin-bottom:8px;text-decoration:none;white-space:nowrap}.tox .tox-dialog__body-nav-item:focus{background-color:rgba(32,122,183,.1)}.tox .tox-dialog__body-nav-item--active{border-bottom:2px solid #207ab7;color:#207ab7}.tox .tox-dialog__body-content{box-sizing:border-box;display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto;max-height:650px;overflow:auto;-webkit-overflow-scrolling:touch;padding:16px 16px}.tox .tox-dialog__body-content>*{margin-bottom:0;margin-top:16px}.tox .tox-dialog__body-content>:first-child{margin-top:0}.tox .tox-dialog__body-content>:last-child{margin-bottom:0}.tox .tox-dialog__body-content>:only-child{margin-bottom:0;margin-top:0}.tox .tox-dialog__body-content a{color:#207ab7;cursor:pointer;text-decoration:none}.tox .tox-dialog__body-content a:focus,.tox .tox-dialog__body-content a:hover{color:#185d8c;text-decoration:none}.tox .tox-dialog__body-content a:active{color:#185d8c;text-decoration:none}.tox .tox-dialog__body-content svg{fill:#fff}.tox .tox-dialog__body-content ul{display:block;list-style-type:disc;margin-bottom:16px;-webkit-margin-end:0;margin-inline-end:0;-webkit-margin-start:0;margin-inline-start:0;-webkit-padding-start:2.5rem;padding-inline-start:2.5rem}.tox .tox-dialog__body-content .tox-form__group h1{color:#fff;font-size:20px;font-style:normal;font-weight:700;letter-spacing:normal;margin-bottom:16px;margin-top:2rem;text-transform:none}.tox .tox-dialog__body-content .tox-form__group h2{color:#fff;font-size:16px;font-style:normal;font-weight:700;letter-spacing:normal;margin-bottom:16px;margin-top:2rem;text-transform:none}.tox .tox-dialog__body-content .tox-form__group p{margin-bottom:16px}.tox .tox-dialog__body-content .tox-form__group h1:first-child,.tox .tox-dialog__body-content .tox-form__group h2:first-child,.tox .tox-dialog__body-content .tox-form__group p:first-child{margin-top:0}.tox .tox-dialog__body-content .tox-form__group h1:last-child,.tox .tox-dialog__body-content .tox-form__group h2:last-child,.tox .tox-dialog__body-content .tox-form__group p:last-child{margin-bottom:0}.tox .tox-dialog__body-content .tox-form__group h1:only-child,.tox .tox-dialog__body-content .tox-form__group h2:only-child,.tox .tox-dialog__body-content .tox-form__group p:only-child{margin-bottom:0;margin-top:0}.tox .tox-dialog--width-lg{height:650px;max-width:1200px}.tox .tox-dialog--width-md{max-width:800px}.tox .tox-dialog--width-md .tox-dialog__body-content{overflow:auto}.tox .tox-dialog__body-content--centered{text-align:center}.tox .tox-dialog__footer{align-items:center;background-color:#2b3b4e;border-top:1px solid #000;display:flex;justify-content:space-between;padding:8px 16px}.tox .tox-dialog__footer-end,.tox .tox-dialog__footer-start{display:flex}.tox .tox-dialog__busy-spinner{align-items:center;background-color:rgba(34,47,62,.75);bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0;z-index:3}.tox .tox-dialog__table{border-collapse:collapse;width:100%}.tox .tox-dialog__table thead th{font-weight:700;padding-bottom:8px}.tox .tox-dialog__table tbody tr{border-bottom:1px solid #000}.tox .tox-dialog__table tbody tr:last-child{border-bottom:none}.tox .tox-dialog__table td{padding-bottom:8px;padding-top:8px}.tox .tox-dialog__popups{position:absolute;width:100%;z-index:1100}.tox .tox-dialog__body-iframe{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-iframe .tox-navobj{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-iframe .tox-navobj :nth-child(2){flex:1;-ms-flex-preferred-size:auto;height:100%}.tox .tox-dialog-dock-fadeout{opacity:0;visibility:hidden}.tox .tox-dialog-dock-fadein{opacity:1;visibility:visible}.tox .tox-dialog-dock-transition{transition:visibility 0s linear .3s,opacity .3s ease}.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein{transition-delay:0s}.tox.tox-platform-ie .tox-dialog-wrap{position:-ms-device-fixed}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav{margin-right:0}}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child){margin-left:8px}}.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-end>*,.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-start>*{margin-left:8px}.tox[dir=rtl] .tox-dialog__body{text-align:right}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav{margin-left:0}}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child){margin-right:8px}}.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-end>*,.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-start>*{margin-right:8px}body.tox-dialog__disable-scroll{overflow:hidden}.tox .tox-dropzone-container{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dropzone{align-items:center;background:#fff;border:2px dashed #000;box-sizing:border-box;display:flex;flex-direction:column;flex-grow:1;justify-content:center;min-height:100px;padding:10px}.tox .tox-dropzone p{color:rgba(255,255,255,.5);margin:0 0 16px 0}.tox .tox-edit-area{display:flex;flex:1;-ms-flex-preferred-size:auto;overflow:hidden;position:relative}.tox .tox-edit-area__iframe{background-color:#fff;border:0;box-sizing:border-box;flex:1;-ms-flex-preferred-size:auto;height:100%;position:absolute;width:100%}.tox.tox-inline-edit-area{border:1px dotted #000}.tox .tox-editor-container{display:flex;flex:1 1 auto;flex-direction:column;overflow:hidden}.tox .tox-editor-header{z-index:1000}.tox:not(.tox-tinymce-inline) .tox-editor-header{box-shadow:none;transition:box-shadow .5s}.tox.tox-tinymce--toolbar-bottom .tox-editor-header,.tox.tox-tinymce-inline .tox-editor-header{margin-bottom:-1px}.tox.tox-tinymce--toolbar-sticky-on .tox-editor-header{background-color:transparent;box-shadow:0 4px 4px -3px rgba(0,0,0,.25)}.tox-editor-dock-fadeout{opacity:0;visibility:hidden}.tox-editor-dock-fadein{opacity:1;visibility:visible}.tox-editor-dock-transition{transition:visibility 0s linear .25s,opacity .25s ease}.tox-editor-dock-transition.tox-editor-dock-fadein{transition-delay:0s}.tox .tox-control-wrap{flex:1;position:relative}.tox .tox-control-wrap:not(.tox-control-wrap--status-invalid) .tox-control-wrap__status-icon-invalid,.tox .tox-control-wrap:not(.tox-control-wrap--status-unknown) .tox-control-wrap__status-icon-unknown,.tox .tox-control-wrap:not(.tox-control-wrap--status-valid) .tox-control-wrap__status-icon-valid{display:none}.tox .tox-control-wrap svg{display:block}.tox .tox-control-wrap__status-icon-wrap{position:absolute;top:50%;transform:translateY(-50%)}.tox .tox-control-wrap__status-icon-invalid svg{fill:#c00}.tox .tox-control-wrap__status-icon-unknown svg{fill:orange}.tox .tox-control-wrap__status-icon-valid svg{fill:green}.tox:not([dir=rtl]) .tox-control-wrap--status-invalid .tox-textfield,.tox:not([dir=rtl]) .tox-control-wrap--status-unknown .tox-textfield,.tox:not([dir=rtl]) .tox-control-wrap--status-valid .tox-textfield{padding-right:32px}.tox:not([dir=rtl]) .tox-control-wrap__status-icon-wrap{right:4px}.tox[dir=rtl] .tox-control-wrap--status-invalid .tox-textfield,.tox[dir=rtl] .tox-control-wrap--status-unknown .tox-textfield,.tox[dir=rtl] .tox-control-wrap--status-valid .tox-textfield{padding-left:32px}.tox[dir=rtl] .tox-control-wrap__status-icon-wrap{left:4px}.tox .tox-autocompleter{max-width:25em}.tox .tox-autocompleter .tox-menu{max-width:25em}.tox .tox-autocompleter .tox-autocompleter-highlight{font-weight:700}.tox .tox-color-input{display:flex;position:relative;z-index:1}.tox .tox-color-input .tox-textfield{z-index:-1}.tox .tox-color-input span{border-color:rgba(42,55,70,.2);border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;height:24px;position:absolute;top:6px;width:24px}.tox .tox-color-input span:focus:not([aria-disabled=true]),.tox .tox-color-input span:hover:not([aria-disabled=true]){border-color:#207ab7;cursor:pointer}.tox .tox-color-input span::before{background-image:linear-gradient(45deg,rgba(255,255,255,.25) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.25) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.25) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.25) 75%);background-position:0 0,0 6px,6px -6px,-6px 0;background-size:12px 12px;border:1px solid #2b3b4e;border-radius:3px;box-sizing:border-box;content:'';height:24px;left:-1px;position:absolute;top:-1px;width:24px;z-index:-1}.tox .tox-color-input span[aria-disabled=true]{cursor:not-allowed}.tox:not([dir=rtl]) .tox-color-input .tox-textfield{padding-left:36px}.tox:not([dir=rtl]) .tox-color-input span{left:6px}.tox[dir=rtl] .tox-color-input .tox-textfield{padding-right:36px}.tox[dir=rtl] .tox-color-input span{right:6px}.tox .tox-label,.tox .tox-toolbar-label{color:rgba(255,255,255,.5);display:block;font-size:14px;font-style:normal;font-weight:400;line-height:1.3;padding:0 8px 0 0;text-transform:none;white-space:nowrap}.tox .tox-toolbar-label{padding:0 8px}.tox[dir=rtl] .tox-label{padding:0 0 0 8px}.tox .tox-form{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-form__group{box-sizing:border-box;margin-bottom:4px}.tox .tox-form-group--maximize{flex:1}.tox .tox-form__group--error{color:#c00}.tox .tox-form__group--collection{display:flex}.tox .tox-form__grid{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between}.tox .tox-form__grid--2col>.tox-form__group{width:calc(50% - (8px / 2))}.tox .tox-form__grid--3col>.tox-form__group{width:calc(100% / 3 - (8px / 2))}.tox .tox-form__grid--4col>.tox-form__group{width:calc(25% - (8px / 2))}.tox .tox-form__controls-h-stack{align-items:center;display:flex}.tox .tox-form__group--inline{align-items:center;display:flex}.tox .tox-form__group--stretched{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-textarea{flex:1;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-navobj{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-navobj :nth-child(2){flex:1;-ms-flex-preferred-size:auto;height:100%}.tox:not([dir=rtl]) .tox-form__controls-h-stack>:not(:first-child){margin-left:4px}.tox[dir=rtl] .tox-form__controls-h-stack>:not(:first-child){margin-right:4px}.tox .tox-lock.tox-locked .tox-lock-icon__unlock,.tox .tox-lock:not(.tox-locked) .tox-lock-icon__lock{display:none}.tox .tox-listboxfield .tox-listbox--select,.tox .tox-textarea,.tox .tox-textfield,.tox .tox-toolbar-textfield{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#2b3b4e;border-color:#000;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#fff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;line-height:24px;margin:0;min-height:34px;outline:0;padding:5px 4.75px;resize:none;width:100%}.tox .tox-textarea[disabled],.tox .tox-textfield[disabled]{background-color:#222f3e;color:rgba(255,255,255,.85);cursor:not-allowed}.tox .tox-listboxfield .tox-listbox--select:focus,.tox .tox-textarea:focus,.tox .tox-textfield:focus{background-color:#2b3b4e;border-color:#207ab7;box-shadow:none;outline:0}.tox .tox-toolbar-textfield{border-width:0;margin-bottom:3px;margin-top:2px;max-width:250px}.tox .tox-naked-btn{background-color:transparent;border:0;border-color:transparent;box-shadow:unset;color:#207ab7;cursor:pointer;display:block;margin:0;padding:0}.tox .tox-naked-btn svg{display:block;fill:#fff}.tox:not([dir=rtl]) .tox-toolbar-textfield+*{margin-left:4px}.tox[dir=rtl] .tox-toolbar-textfield+*{margin-right:4px}.tox .tox-listboxfield{cursor:pointer;position:relative}.tox .tox-listboxfield .tox-listbox--select[disabled]{background-color:#19232e;color:rgba(255,255,255,.85);cursor:not-allowed}.tox .tox-listbox__select-label{cursor:default;flex:1;margin:0 4px}.tox .tox-listbox__select-chevron{align-items:center;display:flex;justify-content:center;width:16px}.tox .tox-listbox__select-chevron svg{fill:#fff}.tox .tox-listboxfield .tox-listbox--select{align-items:center;display:flex}.tox:not([dir=rtl]) .tox-listboxfield svg{right:8px}.tox[dir=rtl] .tox-listboxfield svg{left:8px}.tox .tox-selectfield{cursor:pointer;position:relative}.tox .tox-selectfield select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#2b3b4e;border-color:#000;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#fff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;line-height:24px;margin:0;min-height:34px;outline:0;padding:5px 4.75px;resize:none;width:100%}.tox .tox-selectfield select[disabled]{background-color:#19232e;color:rgba(255,255,255,.85);cursor:not-allowed}.tox .tox-selectfield select::-ms-expand{display:none}.tox .tox-selectfield select:focus{background-color:#2b3b4e;border-color:#207ab7;box-shadow:none;outline:0}.tox .tox-selectfield svg{pointer-events:none;position:absolute;top:50%;transform:translateY(-50%)}.tox:not([dir=rtl]) .tox-selectfield select[size="0"],.tox:not([dir=rtl]) .tox-selectfield select[size="1"]{padding-right:24px}.tox:not([dir=rtl]) .tox-selectfield svg{right:8px}.tox[dir=rtl] .tox-selectfield select[size="0"],.tox[dir=rtl] .tox-selectfield select[size="1"]{padding-left:24px}.tox[dir=rtl] .tox-selectfield svg{left:8px}.tox .tox-textarea{-webkit-appearance:textarea;-moz-appearance:textarea;appearance:textarea;white-space:pre-wrap}.tox-fullscreen{border:0;height:100%;left:0;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;position:fixed;top:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox.tox-tinymce.tox-fullscreen{background-color:transparent;z-index:1200}.tox-shadowhost.tox-fullscreen{z-index:1200}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}.tox .tox-help__more-link{list-style:none;margin-top:1em}.tox .tox-image-tools{width:100%}.tox .tox-image-tools__toolbar{align-items:center;display:flex;justify-content:center}.tox .tox-image-tools__image{background-color:#666;height:380px;overflow:auto;position:relative;width:100%}.tox .tox-image-tools__image,.tox .tox-image-tools__image+.tox-image-tools__toolbar{margin-top:8px}.tox .tox-image-tools__image-bg{background:url(data:image/gif;base64,R0lGODdhDAAMAIABAMzMzP///ywAAAAADAAMAAACFoQfqYeabNyDMkBQb81Uat85nxguUAEAOw==)}.tox .tox-image-tools__toolbar>.tox-spacer{flex:1;-ms-flex-preferred-size:auto}.tox .tox-croprect-block{background:#000;opacity:.5;position:absolute;zoom:1}.tox .tox-croprect-handle{border:2px solid #fff;height:20px;left:0;position:absolute;top:0;width:20px}.tox .tox-croprect-handle-move{border:0;cursor:move;position:absolute}.tox .tox-croprect-handle-nw{border-width:2px 0 0 2px;cursor:nw-resize;left:100px;margin:-2px 0 0 -2px;top:100px}.tox .tox-croprect-handle-ne{border-width:2px 2px 0 0;cursor:ne-resize;left:200px;margin:-2px 0 0 -20px;top:100px}.tox .tox-croprect-handle-sw{border-width:0 0 2px 2px;cursor:sw-resize;left:100px;margin:-20px 2px 0 -2px;top:200px}.tox .tox-croprect-handle-se{border-width:0 2px 2px 0;cursor:se-resize;left:200px;margin:-20px 0 0 -20px;top:200px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-slider:not(:first-of-type){margin-left:8px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-button+.tox-slider{margin-left:32px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-slider+.tox-button{margin-left:32px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-slider:not(:first-of-type){margin-right:8px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-button+.tox-slider{margin-right:32px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-slider+.tox-button{margin-right:32px}.tox .tox-insert-table-picker{display:flex;flex-wrap:wrap;width:170px}.tox .tox-insert-table-picker>div{border-color:#000;border-style:solid;border-width:0 1px 1px 0;box-sizing:border-box;height:17px;width:17px}.tox .tox-collection--list .tox-collection__group .tox-insert-table-picker{margin:-4px 0}.tox .tox-insert-table-picker .tox-insert-table-picker__selected{background-color:rgba(32,122,183,.5);border-color:rgba(32,122,183,.5)}.tox .tox-insert-table-picker__label{color:#fff;display:block;font-size:14px;padding:4px;text-align:center;width:100%}.tox:not([dir=rtl]) .tox-insert-table-picker>div:nth-child(10n){border-right:0}.tox[dir=rtl] .tox-insert-table-picker>div:nth-child(10n+1){border-right:0}.tox .tox-menu{background-color:#2b3b4e;border:1px solid #000;border-radius:3px;box-shadow:0 4px 8px 0 rgba(42,55,70,.1);display:inline-block;overflow:hidden;vertical-align:top;z-index:1150}.tox .tox-menu.tox-collection.tox-collection--list{padding:0}.tox .tox-menu.tox-collection.tox-collection--toolbar{padding:4px}.tox .tox-menu.tox-collection.tox-collection--grid{padding:4px}.tox .tox-menu__label blockquote,.tox .tox-menu__label code,.tox .tox-menu__label h1,.tox .tox-menu__label h2,.tox .tox-menu__label h3,.tox .tox-menu__label h4,.tox .tox-menu__label h5,.tox .tox-menu__label h6,.tox .tox-menu__label p{margin:0}.tox .tox-menubar{background:url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23000000'/%3E%3C/svg%3E") left 0 top 0 #222f3e;background-color:#222f3e;display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:wrap;padding:0 4px 0 4px}.tox.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar{border-top:1px solid #000}.tox .tox-mbtn{align-items:center;background:0 0;border:0;border-radius:3px;box-shadow:none;color:#fff;display:flex;flex:0 0 auto;font-size:14px;font-style:normal;font-weight:400;height:34px;justify-content:center;margin:2px 0 3px 0;outline:0;overflow:hidden;padding:0 4px;text-transform:none;width:auto}.tox .tox-mbtn[disabled]{background-color:transparent;border:0;box-shadow:none;color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-mbtn:focus:not(:disabled){background:#4a5562;border:0;box-shadow:none;color:#fff}.tox .tox-mbtn--active{background:#757d87;border:0;box-shadow:none;color:#fff}.tox .tox-mbtn:hover:not(:disabled):not(.tox-mbtn--active){background:#4a5562;border:0;box-shadow:none;color:#fff}.tox .tox-mbtn__select-label{cursor:default;font-weight:400;margin:0 4px}.tox .tox-mbtn[disabled] .tox-mbtn__select-label{cursor:not-allowed}.tox .tox-mbtn__select-chevron{align-items:center;display:flex;justify-content:center;width:16px;display:none}.tox .tox-notification{border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;display:-ms-grid;display:grid;font-size:14px;font-weight:400;-ms-grid-columns:minmax(40px,1fr) auto minmax(40px,1fr);grid-template-columns:minmax(40px,1fr) auto minmax(40px,1fr);margin-top:4px;opacity:0;padding:4px;transition:transform .1s ease-in,opacity 150ms ease-in}.tox .tox-notification p{font-size:14px;font-weight:400}.tox .tox-notification a{text-decoration:underline}.tox .tox-notification--in{opacity:1}.tox .tox-notification--success{background-color:#e4eeda;border-color:#d7e6c8;color:#fff}.tox .tox-notification--success p{color:#fff}.tox .tox-notification--success a{color:#547831}.tox .tox-notification--success svg{fill:#fff}.tox .tox-notification--error{background-color:#f8dede;border-color:#f2bfbf;color:#fff}.tox .tox-notification--error p{color:#fff}.tox .tox-notification--error a{color:#c00}.tox .tox-notification--error svg{fill:#fff}.tox .tox-notification--warn,.tox .tox-notification--warning{background-color:#fffaea;border-color:#ffe89d;color:#fff}.tox .tox-notification--warn p,.tox .tox-notification--warning p{color:#fff}.tox .tox-notification--warn a,.tox .tox-notification--warning a{color:#fff}.tox .tox-notification--warn svg,.tox .tox-notification--warning svg{fill:#fff}.tox .tox-notification--info{background-color:#d9edf7;border-color:#779ecb;color:#fff}.tox .tox-notification--info p{color:#fff}.tox .tox-notification--info a{color:#fff}.tox .tox-notification--info svg{fill:#fff}.tox .tox-notification__body{-ms-grid-row-align:center;align-self:center;color:#fff;font-size:14px;-ms-grid-column-span:1;grid-column-end:3;-ms-grid-column:2;grid-column-start:2;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;text-align:center;white-space:normal;word-break:break-all;word-break:break-word}.tox .tox-notification__body>*{margin:0}.tox .tox-notification__body>*+*{margin-top:1rem}.tox .tox-notification__icon{-ms-grid-row-align:center;align-self:center;-ms-grid-column-span:1;grid-column-end:2;-ms-grid-column:1;grid-column-start:1;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;-ms-grid-column-align:end;justify-self:end}.tox .tox-notification__icon svg{display:block}.tox .tox-notification__dismiss{-ms-grid-row-align:start;align-self:start;-ms-grid-column-span:1;grid-column-end:4;-ms-grid-column:3;grid-column-start:3;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;-ms-grid-column-align:end;justify-self:end}.tox .tox-notification .tox-progress-bar{-ms-grid-column-span:3;grid-column-end:4;-ms-grid-column:1;grid-column-start:1;-ms-grid-row-span:1;grid-row-end:3;-ms-grid-row:2;grid-row-start:2;-ms-grid-column-align:center;justify-self:center}.tox .tox-pop{display:inline-block;position:relative}.tox .tox-pop--resizing{transition:width .1s ease}.tox .tox-pop--resizing .tox-toolbar{flex-wrap:nowrap}.tox .tox-pop__dialog{background-color:#222f3e;border:1px solid #000;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15);min-width:0;overflow:hidden}.tox .tox-pop__dialog>:not(.tox-toolbar){margin:4px 4px 4px 8px}.tox .tox-pop__dialog .tox-toolbar{background-color:transparent;margin-bottom:-1px}.tox .tox-pop::after,.tox .tox-pop::before{border-style:solid;content:'';display:block;height:0;position:absolute;width:0}.tox .tox-pop.tox-pop--bottom::after,.tox .tox-pop.tox-pop--bottom::before{left:50%;top:100%}.tox .tox-pop.tox-pop--bottom::after{border-color:#222f3e transparent transparent transparent;border-width:8px;margin-left:-8px;margin-top:-1px}.tox .tox-pop.tox-pop--bottom::before{border-color:#000 transparent transparent transparent;border-width:9px;margin-left:-9px}.tox .tox-pop.tox-pop--top::after,.tox .tox-pop.tox-pop--top::before{left:50%;top:0;transform:translateY(-100%)}.tox .tox-pop.tox-pop--top::after{border-color:transparent transparent #222f3e transparent;border-width:8px;margin-left:-8px;margin-top:1px}.tox .tox-pop.tox-pop--top::before{border-color:transparent transparent #000 transparent;border-width:9px;margin-left:-9px}.tox .tox-pop.tox-pop--left::after,.tox .tox-pop.tox-pop--left::before{left:0;top:calc(50% - 1px);transform:translateY(-50%)}.tox .tox-pop.tox-pop--left::after{border-color:transparent #222f3e transparent transparent;border-width:8px;margin-left:-15px}.tox .tox-pop.tox-pop--left::before{border-color:transparent #000 transparent transparent;border-width:10px;margin-left:-19px}.tox .tox-pop.tox-pop--right::after,.tox .tox-pop.tox-pop--right::before{left:100%;top:calc(50% + 1px);transform:translateY(-50%)}.tox .tox-pop.tox-pop--right::after{border-color:transparent transparent transparent #222f3e;border-width:8px;margin-left:-1px}.tox .tox-pop.tox-pop--right::before{border-color:transparent transparent transparent #000;border-width:10px;margin-left:-1px}.tox .tox-pop.tox-pop--align-left::after,.tox .tox-pop.tox-pop--align-left::before{left:20px}.tox .tox-pop.tox-pop--align-right::after,.tox .tox-pop.tox-pop--align-right::before{left:calc(100% - 20px)}.tox .tox-sidebar-wrap{display:flex;flex-direction:row;flex-grow:1;-ms-flex-preferred-size:0;min-height:0}.tox .tox-sidebar{background-color:#222f3e;display:flex;flex-direction:row;justify-content:flex-end}.tox .tox-sidebar__slider{display:flex;overflow:hidden}.tox .tox-sidebar__pane-container{display:flex}.tox .tox-sidebar__pane{display:flex}.tox .tox-sidebar--sliding-closed{opacity:0}.tox .tox-sidebar--sliding-open{opacity:1}.tox .tox-sidebar--sliding-growing,.tox .tox-sidebar--sliding-shrinking{transition:width .5s ease,opacity .5s ease}.tox .tox-selector{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;display:inline-block;height:10px;position:absolute;width:10px}.tox.tox-platform-touch .tox-selector{height:12px;width:12px}.tox .tox-slider{align-items:center;display:flex;flex:1;-ms-flex-preferred-size:auto;height:24px;justify-content:center;position:relative}.tox .tox-slider__rail{background-color:transparent;border:1px solid #000;border-radius:3px;height:10px;min-width:120px;width:100%}.tox .tox-slider__handle{background-color:#207ab7;border:2px solid #185d8c;border-radius:3px;box-shadow:none;height:24px;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%);width:14px}.tox .tox-source-code{overflow:auto}.tox .tox-spinner{display:flex}.tox .tox-spinner>div{animation:tam-bouncing-dots 1.5s ease-in-out 0s infinite both;background-color:rgba(255,255,255,.5);border-radius:100%;height:8px;width:8px}.tox .tox-spinner>div:nth-child(1){animation-delay:-.32s}.tox .tox-spinner>div:nth-child(2){animation-delay:-.16s}@keyframes tam-bouncing-dots{0%,100%,80%{transform:scale(0)}40%{transform:scale(1)}}.tox:not([dir=rtl]) .tox-spinner>div:not(:first-child){margin-left:4px}.tox[dir=rtl] .tox-spinner>div:not(:first-child){margin-right:4px}.tox .tox-statusbar{align-items:center;background-color:#222f3e;border-top:1px solid #000;color:#fff;display:flex;flex:0 0 auto;font-size:12px;font-weight:400;height:18px;overflow:hidden;padding:0 8px;position:relative;text-transform:uppercase}.tox .tox-statusbar__text-container{display:flex;flex:1 1 auto;justify-content:flex-end;overflow:hidden}.tox .tox-statusbar__path{display:flex;flex:1 1 auto;margin-right:auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tox .tox-statusbar__path>*{display:inline;white-space:nowrap}.tox .tox-statusbar__wordcount{flex:0 0 auto;margin-left:1ch}.tox .tox-statusbar a,.tox .tox-statusbar__path-item,.tox .tox-statusbar__wordcount{color:#fff;text-decoration:none}.tox .tox-statusbar a:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar a:hover:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__path-item:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__path-item:hover:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__wordcount:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__wordcount:hover:not(:disabled):not([aria-disabled=true]){cursor:pointer;text-decoration:underline}.tox .tox-statusbar__resize-handle{align-items:flex-end;align-self:stretch;cursor:nwse-resize;display:flex;flex:0 0 auto;justify-content:flex-end;margin-left:auto;margin-right:-8px;padding-left:1ch}.tox .tox-statusbar__resize-handle svg{display:block;fill:#fff}.tox .tox-statusbar__resize-handle:focus svg{background-color:#4a5562;border-radius:1px;box-shadow:0 0 0 2px #4a5562}.tox:not([dir=rtl]) .tox-statusbar__path>*{margin-right:4px}.tox:not([dir=rtl]) .tox-statusbar__branding{margin-left:1ch}.tox[dir=rtl] .tox-statusbar{flex-direction:row-reverse}.tox[dir=rtl] .tox-statusbar__path>*{margin-left:4px}.tox .tox-throbber{z-index:1299}.tox .tox-throbber__busy-spinner{align-items:center;background-color:rgba(34,47,62,.6);bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0}.tox .tox-tbtn{align-items:center;background:0 0;border:0;border-radius:3px;box-shadow:none;color:#fff;display:flex;flex:0 0 auto;font-size:14px;font-style:normal;font-weight:400;height:34px;justify-content:center;margin:2px 0 3px 0;outline:0;overflow:hidden;padding:0;text-transform:none;width:34px}.tox .tox-tbtn svg{display:block;fill:#fff}.tox .tox-tbtn.tox-tbtn-more{padding-left:5px;padding-right:5px;width:inherit}.tox .tox-tbtn:focus{background:#4a5562;border:0;box-shadow:none}.tox .tox-tbtn:hover{background:#4a5562;border:0;box-shadow:none;color:#fff}.tox .tox-tbtn:hover svg{fill:#fff}.tox .tox-tbtn:active{background:#757d87;border:0;box-shadow:none;color:#fff}.tox .tox-tbtn:active svg{fill:#fff}.tox .tox-tbtn--disabled,.tox .tox-tbtn--disabled:hover,.tox .tox-tbtn:disabled,.tox .tox-tbtn:disabled:hover{background:0 0;border:0;box-shadow:none;color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-tbtn--disabled svg,.tox .tox-tbtn--disabled:hover svg,.tox .tox-tbtn:disabled svg,.tox .tox-tbtn:disabled:hover svg{fill:rgba(255,255,255,.5)}.tox .tox-tbtn--enabled,.tox .tox-tbtn--enabled:hover{background:#757d87;border:0;box-shadow:none;color:#fff}.tox .tox-tbtn--enabled:hover>*,.tox .tox-tbtn--enabled>*{transform:none}.tox .tox-tbtn--enabled svg,.tox .tox-tbtn--enabled:hover svg{fill:#fff}.tox .tox-tbtn:focus:not(.tox-tbtn--disabled){color:#fff}.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) svg{fill:#fff}.tox .tox-tbtn:active>*{transform:none}.tox .tox-tbtn--md{height:51px;width:51px}.tox .tox-tbtn--lg{flex-direction:column;height:68px;width:68px}.tox .tox-tbtn--return{-ms-grid-row-align:stretch;align-self:stretch;height:unset;width:16px}.tox .tox-tbtn--labeled{padding:0 4px;width:unset}.tox .tox-tbtn__vlabel{display:block;font-size:10px;font-weight:400;letter-spacing:-.025em;margin-bottom:4px;white-space:nowrap}.tox .tox-tbtn--select{margin:2px 0 3px 0;padding:0 4px;width:auto}.tox .tox-tbtn__select-label{cursor:default;font-weight:400;margin:0 4px}.tox .tox-tbtn__select-chevron{align-items:center;display:flex;justify-content:center;width:16px}.tox .tox-tbtn__select-chevron svg{fill:rgba(255,255,255,.5)}.tox .tox-tbtn--bespoke .tox-tbtn__select-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:7em}.tox .tox-split-button{border:0;border-radius:3px;box-sizing:border-box;display:flex;margin:2px 0 3px 0;overflow:hidden}.tox .tox-split-button:hover{box-shadow:0 0 0 1px #4a5562 inset}.tox .tox-split-button:focus{background:#4a5562;box-shadow:none;color:#fff}.tox .tox-split-button>*{border-radius:0}.tox .tox-split-button__chevron{width:16px}.tox .tox-split-button__chevron svg{fill:rgba(255,255,255,.5)}.tox .tox-split-button .tox-tbtn{margin:0}.tox.tox-platform-touch .tox-split-button .tox-tbtn:first-child{width:30px}.tox.tox-platform-touch .tox-split-button__chevron{width:20px}.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:focus,.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:hover,.tox .tox-split-button.tox-tbtn--disabled:focus,.tox .tox-split-button.tox-tbtn--disabled:hover{background:0 0;box-shadow:none;color:rgba(255,255,255,.5)}.tox .tox-toolbar-overlord{background-color:#222f3e}.tox .tox-toolbar,.tox .tox-toolbar__overflow,.tox .tox-toolbar__primary{background:url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23000000'/%3E%3C/svg%3E") left 0 top 0 #222f3e;background-color:#222f3e;display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:wrap;padding:0 0}.tox .tox-toolbar__overflow.tox-toolbar__overflow--closed{height:0;opacity:0;padding-bottom:0;padding-top:0;visibility:hidden}.tox .tox-toolbar__overflow--growing{transition:height .3s ease,opacity .2s linear .1s}.tox .tox-toolbar__overflow--shrinking{transition:opacity .3s ease,height .2s linear .1s,visibility 0s linear .3s}.tox .tox-menubar+.tox-toolbar,.tox .tox-menubar+.tox-toolbar-overlord .tox-toolbar__primary{border-top:1px solid #000;margin-top:-1px}.tox .tox-toolbar--scrolling{flex-wrap:nowrap;overflow-x:auto}.tox .tox-pop .tox-toolbar{border-width:0}.tox .tox-toolbar--no-divider{background-image:none}.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar-overlord:first-child .tox-toolbar__primary,.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar:first-child{border-top:1px solid #000}.tox.tox-tinymce-aux .tox-toolbar__overflow{background-color:#222f3e;border:1px solid #000;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15)}.tox[dir=rtl] .tox-tbtn__icon-rtl svg{transform:rotateY(180deg)}.tox .tox-toolbar__group{align-items:center;display:flex;flex-wrap:wrap;margin:0 0;padding:0 4px 0 4px}.tox .tox-toolbar__group--pull-right{margin-left:auto}.tox .tox-toolbar--scrolling .tox-toolbar__group{flex-shrink:0;flex-wrap:nowrap}.tox:not([dir=rtl]) .tox-toolbar__group:not(:last-of-type){border-right:1px solid #000}.tox[dir=rtl] .tox-toolbar__group:not(:last-of-type){border-left:1px solid #000}.tox .tox-tooltip{display:inline-block;padding:8px;position:relative}.tox .tox-tooltip__body{background-color:#3d546f;border-radius:3px;box-shadow:0 2px 4px rgba(42,55,70,.3);color:rgba(255,255,255,.75);font-size:14px;font-style:normal;font-weight:400;padding:4px 8px;text-transform:none}.tox .tox-tooltip__arrow{position:absolute}.tox .tox-tooltip--down .tox-tooltip__arrow{border-left:8px solid transparent;border-right:8px solid transparent;border-top:8px solid #3d546f;bottom:0;left:50%;position:absolute;transform:translateX(-50%)}.tox .tox-tooltip--up .tox-tooltip__arrow{border-bottom:8px solid #3d546f;border-left:8px solid transparent;border-right:8px solid transparent;left:50%;position:absolute;top:0;transform:translateX(-50%)}.tox .tox-tooltip--right .tox-tooltip__arrow{border-bottom:8px solid transparent;border-left:8px solid #3d546f;border-top:8px solid transparent;position:absolute;right:0;top:50%;transform:translateY(-50%)}.tox .tox-tooltip--left .tox-tooltip__arrow{border-bottom:8px solid transparent;border-right:8px solid #3d546f;border-top:8px solid transparent;left:0;position:absolute;top:50%;transform:translateY(-50%)}.tox .tox-well{border:1px solid #000;border-radius:3px;padding:8px;width:100%}.tox .tox-well>:first-child{margin-top:0}.tox .tox-well>:last-child{margin-bottom:0}.tox .tox-well>:only-child{margin:0}.tox .tox-custom-editor{border:1px solid #000;border-radius:3px;display:flex;flex:1;position:relative}.tox .tox-dialog-loading::before{background-color:rgba(0,0,0,.5);content:"";height:100%;position:absolute;width:100%;z-index:1000}.tox .tox-tab{cursor:pointer}.tox .tox-dialog__content-js{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-content .tox-collection{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-image-tools-edit-panel{height:60px}.tox .tox-image-tools__sidebar{height:60px} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.mobile.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.mobile.css new file mode 100644 index 0000000..efcd1bb --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.mobile.css @@ -0,0 +1,798 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +/* RESET all the things! */ +.tinymce-mobile-outer-container { + all: initial; + display: block; +} + +.tinymce-mobile-outer-container * { + border: 0; + box-sizing: initial; + cursor: inherit; + float: none; + line-height: 1; + margin: 0; + outline: 0; + padding: 0; + -webkit-tap-highlight-color: transparent; + /* TBIO-3691, stop the gray flicker on touch. */ + text-shadow: none; + white-space: nowrap; +} + +.tinymce-mobile-icon-arrow-back::before { + content: "\e5cd"; +} + +.tinymce-mobile-icon-image::before { + content: "\e412"; +} + +.tinymce-mobile-icon-cancel-circle::before { + content: "\e5c9"; +} + +.tinymce-mobile-icon-full-dot::before { + content: "\e061"; +} + +.tinymce-mobile-icon-align-center::before { + content: "\e234"; +} + +.tinymce-mobile-icon-align-left::before { + content: "\e236"; +} + +.tinymce-mobile-icon-align-right::before { + content: "\e237"; +} + +.tinymce-mobile-icon-bold::before { + content: "\e238"; +} + +.tinymce-mobile-icon-italic::before { + content: "\e23f"; +} + +.tinymce-mobile-icon-unordered-list::before { + content: "\e241"; +} + +.tinymce-mobile-icon-ordered-list::before { + content: "\e242"; +} + +.tinymce-mobile-icon-font-size::before { + content: "\e245"; +} + +.tinymce-mobile-icon-underline::before { + content: "\e249"; +} + +.tinymce-mobile-icon-link::before { + content: "\e157"; +} + +.tinymce-mobile-icon-unlink::before { + content: "\eca2"; +} + +.tinymce-mobile-icon-color::before { + content: "\e891"; +} + +.tinymce-mobile-icon-previous::before { + content: "\e314"; +} + +.tinymce-mobile-icon-next::before { + content: "\e315"; +} + +.tinymce-mobile-icon-large-font::before, +.tinymce-mobile-icon-style-formats::before { + content: "\e264"; +} + +.tinymce-mobile-icon-undo::before { + content: "\e166"; +} + +.tinymce-mobile-icon-redo::before { + content: "\e15a"; +} + +.tinymce-mobile-icon-removeformat::before { + content: "\e239"; +} + +.tinymce-mobile-icon-small-font::before { + content: "\e906"; +} + +.tinymce-mobile-icon-readonly-back::before, +.tinymce-mobile-format-matches::after { + content: "\e5ca"; +} + +.tinymce-mobile-icon-small-heading::before { + content: "small"; +} + +.tinymce-mobile-icon-large-heading::before { + content: "large"; +} + +.tinymce-mobile-icon-small-heading::before, +.tinymce-mobile-icon-large-heading::before { + font-family: sans-serif; + font-size: 80%; +} + +.tinymce-mobile-mask-edit-icon::before { + content: "\e254"; +} + +.tinymce-mobile-icon-back::before { + content: "\e5c4"; +} + +.tinymce-mobile-icon-heading::before { + /* TODO: Translate */ + content: "Headings"; + font-family: sans-serif; + font-size: 80%; + font-weight: bold; +} + +.tinymce-mobile-icon-h1::before { + content: "H1"; + font-weight: bold; +} + +.tinymce-mobile-icon-h2::before { + content: "H2"; + font-weight: bold; +} + +.tinymce-mobile-icon-h3::before { + content: "H3"; + font-weight: bold; +} + +.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask { + align-items: center; + display: flex; + justify-content: center; + background: rgba(51, 51, 51, 0.5); + height: 100%; + position: absolute; + top: 0; + width: 100%; +} + +.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container { + align-items: center; + border-radius: 50%; + display: flex; + flex-direction: column; + font-family: sans-serif; + font-size: 1em; + justify-content: space-between; +} + +.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .mixin-menu-item { + align-items: center; + display: flex; + justify-content: center; + border-radius: 50%; + height: 2.1em; + width: 2.1em; +} + +.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section { + align-items: center; + display: flex; + justify-content: center; + flex-direction: column; + font-size: 1em; +} + +@media only screen and (min-device-width: 700px) { + .tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section { + font-size: 1.2em; + } +} + +.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon { + align-items: center; + display: flex; + justify-content: center; + border-radius: 50%; + height: 2.1em; + width: 2.1em; + background-color: white; + color: #207ab7; +} + +.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon::before { + content: "\e900"; + font-family: 'tinymce-mobile', sans-serif; +} + +.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section:not(.tinymce-mobile-mask-tap-icon-selected) .tinymce-mobile-mask-tap-icon { + z-index: 2; +} + +.tinymce-mobile-android-container.tinymce-mobile-android-maximized { + background: #ffffff; + border: none; + bottom: 0; + display: flex; + flex-direction: column; + left: 0; + position: fixed; + right: 0; + top: 0; +} + +.tinymce-mobile-android-container:not(.tinymce-mobile-android-maximized) { + position: relative; +} + +.tinymce-mobile-android-container .tinymce-mobile-editor-socket { + display: flex; + flex-grow: 1; +} + +.tinymce-mobile-android-container .tinymce-mobile-editor-socket iframe { + display: flex !important; + flex-grow: 1; + height: auto !important; +} + +.tinymce-mobile-android-scroll-reload { + overflow: hidden; +} + +:not(.tinymce-mobile-readonly-mode) > .tinymce-mobile-android-selection-context-toolbar { + margin-top: 23px; +} + +.tinymce-mobile-toolstrip { + background: #fff; + display: flex; + flex: 0 0 auto; + z-index: 1; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar { + align-items: center; + background-color: #fff; + border-bottom: 1px solid #cccccc; + display: flex; + flex: 1; + height: 2.5em; + width: 100%; + /* Make it no larger than the toolstrip, so that it needs to scroll */ +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group { + align-items: center; + display: flex; + height: 100%; + flex-shrink: 1; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group > div { + align-items: center; + display: flex; + height: 100%; + flex: 1; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-exit-container { + background: #f44336; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-toolbar-scrollable-group { + flex-grow: 1; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item { + padding-left: 0.5em; + padding-right: 0.5em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button { + align-items: center; + display: flex; + height: 80%; + margin-left: 2px; + margin-right: 2px; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button.tinymce-mobile-toolbar-button-selected { + background: #c8cbcf; + color: #cccccc; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:first-of-type, +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:last-of-type { + background: #207ab7; + color: #eceff1; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar { + /* Note, this file is imported inside .tinymce-mobile-context-toolbar, so that prefix is on everything here. */ +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group { + align-items: center; + display: flex; + height: 100%; + flex: 1; + padding-bottom: 0.4em; + padding-top: 0.4em; + /* Make any buttons appearing on the left and right display in the centre (e.g. color edges) */ + /* For widgets like the colour picker, use the whole height */ +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog { + display: flex; + min-height: 1.5em; + overflow: hidden; + padding-left: 0; + padding-right: 0; + position: relative; + width: 100%; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain { + display: flex; + height: 100%; + transition: left cubic-bezier(0.4, 0, 1, 1) 0.15s; + width: 100%; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen { + display: flex; + flex: 0 0 auto; + justify-content: space-between; + width: 100%; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen input { + font-family: Sans-serif; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container { + display: flex; + flex-grow: 1; + position: relative; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container .tinymce-mobile-input-container-x { + -ms-grid-row-align: center; + align-self: center; + background: inherit; + border: none; + border-radius: 50%; + color: #888; + font-size: 0.6em; + font-weight: bold; + height: 100%; + padding-right: 2px; + position: absolute; + right: 0; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container.tinymce-mobile-input-container-empty .tinymce-mobile-input-container-x { + display: none; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous, +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next { + align-items: center; + display: flex; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous::before, +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next::before { + align-items: center; + display: flex; + font-weight: bold; + height: 100%; + padding-left: 0.5em; + padding-right: 0.5em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous.tinymce-mobile-toolbar-navigation-disabled::before, +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next.tinymce-mobile-toolbar-navigation-disabled::before { + visibility: hidden; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item { + color: #cccccc; + font-size: 10px; + line-height: 10px; + margin: 0 2px; + padding-top: 3px; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item.tinymce-mobile-dot-active { + color: #c8cbcf; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-font::before, +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-heading::before { + margin-left: 0.5em; + margin-right: 0.9em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-font::before, +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-heading::before { + margin-left: 0.9em; + margin-right: 0.5em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider { + display: flex; + flex: 1; + margin-left: 0; + margin-right: 0; + padding: 0.28em 0; + position: relative; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container { + align-items: center; + display: flex; + flex-grow: 1; + height: 100%; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container .tinymce-mobile-slider-size-line { + background: #cccccc; + display: flex; + flex: 1; + height: 0.2em; + margin-bottom: 0.3em; + margin-top: 0.3em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container { + padding-left: 2em; + padding-right: 2em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container { + align-items: center; + display: flex; + flex-grow: 1; + height: 100%; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container .tinymce-mobile-slider-gradient { + background: linear-gradient(to right, hsl(0, 100%, 50%) 0%, hsl(60, 100%, 50%) 17%, hsl(120, 100%, 50%) 33%, hsl(180, 100%, 50%) 50%, hsl(240, 100%, 50%) 67%, hsl(300, 100%, 50%) 83%, hsl(0, 100%, 50%) 100%); + display: flex; + flex: 1; + height: 0.2em; + margin-bottom: 0.3em; + margin-top: 0.3em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-black { + /* Not part of theming */ + background: black; + height: 0.2em; + margin-bottom: 0.3em; + margin-top: 0.3em; + width: 1.2em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-white { + /* Not part of theming */ + background: white; + height: 0.2em; + margin-bottom: 0.3em; + margin-top: 0.3em; + width: 1.2em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb { + /* vertically centering trick (margin: auto, top: 0, bottom: 0). On iOS and Safari, if you leave + * out these values, then it shows the thumb at the top of the spectrum. This is probably because it is + * absolutely positioned with only a left value, and not a top. Note, on Chrome it seems to be fine without + * this approach. + */ + align-items: center; + background-clip: padding-box; + background-color: #455a64; + border: 0.5em solid rgba(136, 136, 136, 0); + border-radius: 3em; + bottom: 0; + color: #fff; + display: flex; + height: 0.5em; + justify-content: center; + left: -10px; + margin: auto; + position: absolute; + top: 0; + transition: border 120ms cubic-bezier(0.39, 0.58, 0.57, 1); + width: 0.5em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb.tinymce-mobile-thumb-active { + border: 0.5em solid rgba(136, 136, 136, 0.39); +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper, +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group > div { + align-items: center; + display: flex; + height: 100%; + flex: 1; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper { + flex-direction: column; + justify-content: center; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item { + align-items: center; + display: flex; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item:not(.tinymce-mobile-serialised-dialog) { + height: 100%; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-container { + display: flex; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input { + background: #ffffff; + border: none; + border-radius: 0; + color: #455a64; + flex-grow: 1; + font-size: 0.85em; + padding-bottom: 0.1em; + padding-left: 5px; + padding-top: 0.1em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::-webkit-input-placeholder { + /* WebKit, Blink, Edge */ + color: #888; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::placeholder { + /* WebKit, Blink, Edge */ + color: #888; +} + +/* dropup */ +.tinymce-mobile-dropup { + background: white; + display: flex; + overflow: hidden; + width: 100%; +} + +.tinymce-mobile-dropup.tinymce-mobile-dropup-shrinking { + transition: height 0.3s ease-out; +} + +.tinymce-mobile-dropup.tinymce-mobile-dropup-growing { + transition: height 0.3s ease-in; +} + +.tinymce-mobile-dropup.tinymce-mobile-dropup-closed { + flex-grow: 0; +} + +.tinymce-mobile-dropup.tinymce-mobile-dropup-open:not(.tinymce-mobile-dropup-growing) { + flex-grow: 1; +} + +/* TODO min-height for device size and orientation */ +.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed) { + min-height: 200px; +} + +@media only screen and (orientation: landscape) { + .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed) { + min-height: 200px; + } +} + +@media only screen and (min-device-width: 320px) and (max-device-width: 568px) and (orientation: landscape) { + .tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed) { + min-height: 150px; + } +} + +/* styles menu */ +.tinymce-mobile-styles-menu { + font-family: sans-serif; + outline: 4px solid black; + overflow: hidden; + position: relative; + width: 100%; +} + +.tinymce-mobile-styles-menu [role="menu"] { + display: flex; + flex-direction: column; + height: 100%; + position: absolute; + width: 100%; +} + +.tinymce-mobile-styles-menu [role="menu"].transitioning { + transition: transform 0.5s ease-in-out; +} + +.tinymce-mobile-styles-menu .tinymce-mobile-styles-item { + border-bottom: 1px solid #ddd; + color: #455a64; + cursor: pointer; + display: flex; + padding: 1em 1em; + position: relative; +} + +.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser .tinymce-mobile-styles-collapse-icon::before { + color: #455a64; + content: "\e314"; + font-family: 'tinymce-mobile', sans-serif; +} + +.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-styles-item-is-menu::after { + color: #455a64; + content: "\e315"; + font-family: 'tinymce-mobile', sans-serif; + padding-left: 1em; + padding-right: 1em; + position: absolute; + right: 0; +} + +.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-format-matches::after { + font-family: 'tinymce-mobile', sans-serif; + padding-left: 1em; + padding-right: 1em; + position: absolute; + right: 0; +} + +.tinymce-mobile-styles-menu .tinymce-mobile-styles-separator, +.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser { + align-items: center; + background: #fff; + border-top: #455a64; + color: #455a64; + display: flex; + min-height: 2.5em; + padding-left: 1em; + padding-right: 1em; +} + +.tinymce-mobile-styles-menu [data-transitioning-destination="before"][data-transitioning-state], +.tinymce-mobile-styles-menu [data-transitioning-state="before"] { + transform: translate(-100%); +} + +.tinymce-mobile-styles-menu [data-transitioning-destination="current"][data-transitioning-state], +.tinymce-mobile-styles-menu [data-transitioning-state="current"] { + transform: translate(0%); +} + +.tinymce-mobile-styles-menu [data-transitioning-destination="after"][data-transitioning-state], +.tinymce-mobile-styles-menu [data-transitioning-state="after"] { + transform: translate(100%); +} + +@font-face { + font-family: 'tinymce-mobile'; + font-style: normal; + font-weight: normal; + src: url('fonts/tinymce-mobile.woff?8x92w3') format('woff'); +} + +@media (min-device-width: 700px) { + .tinymce-mobile-outer-container, + .tinymce-mobile-outer-container input { + font-size: 25px; + } +} + +@media (max-device-width: 700px) { + .tinymce-mobile-outer-container, + .tinymce-mobile-outer-container input { + font-size: 18px; + } +} + +.tinymce-mobile-icon { + font-family: 'tinymce-mobile', sans-serif; +} + +.mixin-flex-and-centre { + align-items: center; + display: flex; + justify-content: center; +} + +.mixin-flex-bar { + align-items: center; + display: flex; + height: 100%; +} + +.tinymce-mobile-outer-container .tinymce-mobile-editor-socket iframe { + background-color: #fff; + width: 100%; +} + +.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon { + /* Note, on the iPod touch in landscape, this isn't visible when the navbar appears */ + background-color: #207ab7; + border-radius: 50%; + bottom: 1em; + color: white; + font-size: 1em; + height: 2.1em; + position: fixed; + right: 2em; + width: 2.1em; + align-items: center; + display: flex; + justify-content: center; +} + +@media only screen and (min-device-width: 700px) { + .tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon { + font-size: 1.2em; + } +} + +.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket { + height: 300px; + overflow: hidden; +} + +.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket iframe { + height: 100%; +} + +.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-toolstrip { + display: none; +} + +/* + Note, that if you don't include this (::-webkit-file-upload-button), the toolbar width gets + increased and the whole body becomes scrollable. It's important! + */ +input[type="file"]::-webkit-file-upload-button { + display: none; +} + +@media only screen and (min-device-width: 320px) and (max-device-width: 568px) and (orientation: landscape) { + .tinymce-mobile-ios-container .tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon { + bottom: 50%; + } +} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.mobile.min.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.mobile.min.css new file mode 100644 index 0000000..3a45cac --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.mobile.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.tinymce-mobile-outer-container{all:initial;display:block}.tinymce-mobile-outer-container *{border:0;box-sizing:initial;cursor:inherit;float:none;line-height:1;margin:0;outline:0;padding:0;-webkit-tap-highlight-color:transparent;text-shadow:none;white-space:nowrap}.tinymce-mobile-icon-arrow-back::before{content:"\e5cd"}.tinymce-mobile-icon-image::before{content:"\e412"}.tinymce-mobile-icon-cancel-circle::before{content:"\e5c9"}.tinymce-mobile-icon-full-dot::before{content:"\e061"}.tinymce-mobile-icon-align-center::before{content:"\e234"}.tinymce-mobile-icon-align-left::before{content:"\e236"}.tinymce-mobile-icon-align-right::before{content:"\e237"}.tinymce-mobile-icon-bold::before{content:"\e238"}.tinymce-mobile-icon-italic::before{content:"\e23f"}.tinymce-mobile-icon-unordered-list::before{content:"\e241"}.tinymce-mobile-icon-ordered-list::before{content:"\e242"}.tinymce-mobile-icon-font-size::before{content:"\e245"}.tinymce-mobile-icon-underline::before{content:"\e249"}.tinymce-mobile-icon-link::before{content:"\e157"}.tinymce-mobile-icon-unlink::before{content:"\eca2"}.tinymce-mobile-icon-color::before{content:"\e891"}.tinymce-mobile-icon-previous::before{content:"\e314"}.tinymce-mobile-icon-next::before{content:"\e315"}.tinymce-mobile-icon-large-font::before,.tinymce-mobile-icon-style-formats::before{content:"\e264"}.tinymce-mobile-icon-undo::before{content:"\e166"}.tinymce-mobile-icon-redo::before{content:"\e15a"}.tinymce-mobile-icon-removeformat::before{content:"\e239"}.tinymce-mobile-icon-small-font::before{content:"\e906"}.tinymce-mobile-format-matches::after,.tinymce-mobile-icon-readonly-back::before{content:"\e5ca"}.tinymce-mobile-icon-small-heading::before{content:"small"}.tinymce-mobile-icon-large-heading::before{content:"large"}.tinymce-mobile-icon-large-heading::before,.tinymce-mobile-icon-small-heading::before{font-family:sans-serif;font-size:80%}.tinymce-mobile-mask-edit-icon::before{content:"\e254"}.tinymce-mobile-icon-back::before{content:"\e5c4"}.tinymce-mobile-icon-heading::before{content:"Headings";font-family:sans-serif;font-size:80%;font-weight:700}.tinymce-mobile-icon-h1::before{content:"H1";font-weight:700}.tinymce-mobile-icon-h2::before{content:"H2";font-weight:700}.tinymce-mobile-icon-h3::before{content:"H3";font-weight:700}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask{align-items:center;display:flex;justify-content:center;background:rgba(51,51,51,.5);height:100%;position:absolute;top:0;width:100%}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container{align-items:center;border-radius:50%;display:flex;flex-direction:column;font-family:sans-serif;font-size:1em;justify-content:space-between}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .mixin-menu-item{align-items:center;display:flex;justify-content:center;border-radius:50%;height:2.1em;width:2.1em}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section{align-items:center;display:flex;justify-content:center;flex-direction:column;font-size:1em}@media only screen and (min-device-width:700px){.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section{font-size:1.2em}}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon{align-items:center;display:flex;justify-content:center;border-radius:50%;height:2.1em;width:2.1em;background-color:#fff;color:#207ab7}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon::before{content:"\e900";font-family:tinymce-mobile,sans-serif}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section:not(.tinymce-mobile-mask-tap-icon-selected) .tinymce-mobile-mask-tap-icon{z-index:2}.tinymce-mobile-android-container.tinymce-mobile-android-maximized{background:#fff;border:none;bottom:0;display:flex;flex-direction:column;left:0;position:fixed;right:0;top:0}.tinymce-mobile-android-container:not(.tinymce-mobile-android-maximized){position:relative}.tinymce-mobile-android-container .tinymce-mobile-editor-socket{display:flex;flex-grow:1}.tinymce-mobile-android-container .tinymce-mobile-editor-socket iframe{display:flex!important;flex-grow:1;height:auto!important}.tinymce-mobile-android-scroll-reload{overflow:hidden}:not(.tinymce-mobile-readonly-mode)>.tinymce-mobile-android-selection-context-toolbar{margin-top:23px}.tinymce-mobile-toolstrip{background:#fff;display:flex;flex:0 0 auto;z-index:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar{align-items:center;background-color:#fff;border-bottom:1px solid #ccc;display:flex;flex:1;height:2.5em;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group{align-items:center;display:flex;height:100%;flex-shrink:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group>div{align-items:center;display:flex;height:100%;flex:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-exit-container{background:#f44336}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-toolbar-scrollable-group{flex-grow:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item{padding-left:.5em;padding-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button{align-items:center;display:flex;height:80%;margin-left:2px;margin-right:2px}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button.tinymce-mobile-toolbar-button-selected{background:#c8cbcf;color:#ccc}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:first-of-type,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:last-of-type{background:#207ab7;color:#eceff1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group{align-items:center;display:flex;height:100%;flex:1;padding-bottom:.4em;padding-top:.4em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog{display:flex;min-height:1.5em;overflow:hidden;padding-left:0;padding-right:0;position:relative;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain{display:flex;height:100%;transition:left cubic-bezier(.4,0,1,1) .15s;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen{display:flex;flex:0 0 auto;justify-content:space-between;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen input{font-family:Sans-serif}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container{display:flex;flex-grow:1;position:relative}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container .tinymce-mobile-input-container-x{-ms-grid-row-align:center;align-self:center;background:inherit;border:none;border-radius:50%;color:#888;font-size:.6em;font-weight:700;height:100%;padding-right:2px;position:absolute;right:0}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container.tinymce-mobile-input-container-empty .tinymce-mobile-input-container-x{display:none}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous{align-items:center;display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous::before{align-items:center;display:flex;font-weight:700;height:100%;padding-left:.5em;padding-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next.tinymce-mobile-toolbar-navigation-disabled::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous.tinymce-mobile-toolbar-navigation-disabled::before{visibility:hidden}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item{color:#ccc;font-size:10px;line-height:10px;margin:0 2px;padding-top:3px}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item.tinymce-mobile-dot-active{color:#c8cbcf}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-font::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-heading::before{margin-left:.5em;margin-right:.9em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-font::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-heading::before{margin-left:.9em;margin-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider{display:flex;flex:1;margin-left:0;margin-right:0;padding:.28em 0;position:relative}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container{align-items:center;display:flex;flex-grow:1;height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container .tinymce-mobile-slider-size-line{background:#ccc;display:flex;flex:1;height:.2em;margin-bottom:.3em;margin-top:.3em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container{padding-left:2em;padding-right:2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container{align-items:center;display:flex;flex-grow:1;height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container .tinymce-mobile-slider-gradient{background:linear-gradient(to right,red 0,#feff00 17%,#0f0 33%,#00feff 50%,#00f 67%,#ff00fe 83%,red 100%);display:flex;flex:1;height:.2em;margin-bottom:.3em;margin-top:.3em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-black{background:#000;height:.2em;margin-bottom:.3em;margin-top:.3em;width:1.2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-white{background:#fff;height:.2em;margin-bottom:.3em;margin-top:.3em;width:1.2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb{align-items:center;background-clip:padding-box;background-color:#455a64;border:.5em solid rgba(136,136,136,0);border-radius:3em;bottom:0;color:#fff;display:flex;height:.5em;justify-content:center;left:-10px;margin:auto;position:absolute;top:0;transition:border 120ms cubic-bezier(.39,.58,.57,1);width:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb.tinymce-mobile-thumb-active{border:.5em solid rgba(136,136,136,.39)}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group>div{align-items:center;display:flex;height:100%;flex:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper{flex-direction:column;justify-content:center}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item{align-items:center;display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item:not(.tinymce-mobile-serialised-dialog){height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-container{display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input{background:#fff;border:none;border-radius:0;color:#455a64;flex-grow:1;font-size:.85em;padding-bottom:.1em;padding-left:5px;padding-top:.1em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::-webkit-input-placeholder{color:#888}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::placeholder{color:#888}.tinymce-mobile-dropup{background:#fff;display:flex;overflow:hidden;width:100%}.tinymce-mobile-dropup.tinymce-mobile-dropup-shrinking{transition:height .3s ease-out}.tinymce-mobile-dropup.tinymce-mobile-dropup-growing{transition:height .3s ease-in}.tinymce-mobile-dropup.tinymce-mobile-dropup-closed{flex-grow:0}.tinymce-mobile-dropup.tinymce-mobile-dropup-open:not(.tinymce-mobile-dropup-growing){flex-grow:1}.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:200px}@media only screen and (orientation:landscape){.tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:200px}}@media only screen and (min-device-width :320px) and (max-device-width :568px) and (orientation :landscape){.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:150px}}.tinymce-mobile-styles-menu{font-family:sans-serif;outline:4px solid #000;overflow:hidden;position:relative;width:100%}.tinymce-mobile-styles-menu [role=menu]{display:flex;flex-direction:column;height:100%;position:absolute;width:100%}.tinymce-mobile-styles-menu [role=menu].transitioning{transition:transform .5s ease-in-out}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item{border-bottom:1px solid #ddd;color:#455a64;cursor:pointer;display:flex;padding:1em 1em;position:relative}.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser .tinymce-mobile-styles-collapse-icon::before{color:#455a64;content:"\e314";font-family:tinymce-mobile,sans-serif}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-styles-item-is-menu::after{color:#455a64;content:"\e315";font-family:tinymce-mobile,sans-serif;padding-left:1em;padding-right:1em;position:absolute;right:0}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-format-matches::after{font-family:tinymce-mobile,sans-serif;padding-left:1em;padding-right:1em;position:absolute;right:0}.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser,.tinymce-mobile-styles-menu .tinymce-mobile-styles-separator{align-items:center;background:#fff;border-top:#455a64;color:#455a64;display:flex;min-height:2.5em;padding-left:1em;padding-right:1em}.tinymce-mobile-styles-menu [data-transitioning-destination=before][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=before]{transform:translate(-100%)}.tinymce-mobile-styles-menu [data-transitioning-destination=current][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=current]{transform:translate(0)}.tinymce-mobile-styles-menu [data-transitioning-destination=after][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=after]{transform:translate(100%)}@font-face{font-family:tinymce-mobile;font-style:normal;font-weight:400;src:url(fonts/tinymce-mobile.woff?8x92w3) format('woff')}@media (min-device-width:700px){.tinymce-mobile-outer-container,.tinymce-mobile-outer-container input{font-size:25px}}@media (max-device-width:700px){.tinymce-mobile-outer-container,.tinymce-mobile-outer-container input{font-size:18px}}.tinymce-mobile-icon{font-family:tinymce-mobile,sans-serif}.mixin-flex-and-centre{align-items:center;display:flex;justify-content:center}.mixin-flex-bar{align-items:center;display:flex;height:100%}.tinymce-mobile-outer-container .tinymce-mobile-editor-socket iframe{background-color:#fff;width:100%}.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{background-color:#207ab7;border-radius:50%;bottom:1em;color:#fff;font-size:1em;height:2.1em;position:fixed;right:2em;width:2.1em;align-items:center;display:flex;justify-content:center}@media only screen and (min-device-width:700px){.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{font-size:1.2em}}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket{height:300px;overflow:hidden}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket iframe{height:100%}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-toolstrip{display:none}input[type=file]::-webkit-file-upload-button{display:none}@media only screen and (min-device-width :320px) and (max-device-width :568px) and (orientation :landscape){.tinymce-mobile-ios-container .tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{bottom:50%}} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.shadowdom.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.shadowdom.css new file mode 100644 index 0000000..16f4d30 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.shadowdom.css @@ -0,0 +1,42 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +body.tox-dialog__disable-scroll { + overflow: hidden; +} + +.tox-fullscreen { + border: 0; + height: 100%; + left: 0; + margin: 0; + overflow: hidden; + -ms-scroll-chaining: none; + overscroll-behavior: none; + padding: 0; + position: fixed; + top: 0; + touch-action: pinch-zoom; + width: 100%; +} + +.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle { + display: none; +} + +.tox.tox-tinymce.tox-fullscreen { + background-color: transparent; + z-index: 1200; +} + +.tox-shadowhost.tox-fullscreen { + z-index: 1200; +} + +.tox-fullscreen .tox.tox-tinymce-aux, +.tox-fullscreen ~ .tox.tox-tinymce-aux { + z-index: 1201; +} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.shadowdom.min.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.shadowdom.min.css new file mode 100644 index 0000000..9ba6e02 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide-dark/skin.shadowdom.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;left:0;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;position:fixed;top:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox.tox-tinymce.tox-fullscreen{background-color:transparent;z-index:1200}.tox-shadowhost.tox-fullscreen{z-index:1200}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.css new file mode 100644 index 0000000..0f1e529 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.css @@ -0,0 +1,869 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.mce-content-body .mce-item-anchor { + background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A") no-repeat center; + cursor: default; + display: inline-block; + height: 12px !important; + padding: 0 2px; + -webkit-user-modify: read-only; + -moz-user-modify: read-only; + -webkit-user-select: all; + -moz-user-select: all; + -ms-user-select: all; + user-select: all; + width: 8px !important; +} + +.mce-content-body .mce-item-anchor[data-mce-selected] { + outline-offset: 1px; +} + +.tox-comments-visible .tox-comment { + background-color: #fff0b7; +} + +.tox-comments-visible .tox-comment--active { + background-color: #ffe168; +} + +.tox-checklist > li:not(.tox-checklist--hidden) { + list-style: none; + margin: 0.25em 0; +} + +.tox-checklist > li:not(.tox-checklist--hidden)::before { + content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A"); + cursor: pointer; + height: 1em; + margin-left: -1.5em; + margin-top: 0.125em; + position: absolute; + width: 1em; +} + +.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before { + content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A"); +} + +[dir=rtl] .tox-checklist > li:not(.tox-checklist--hidden)::before { + margin-left: 0; + margin-right: -1.5em; +} + +/* stylelint-disable */ +/* http://prismjs.com/ */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ +code[class*="language-"], +pre[class*="language-"] { + color: black; + background: none; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 1em; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + -moz-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, +pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, +code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, +pre[class*="language-"] ::selection, +code[class*="language-"]::selection, +code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: 0.5em 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9a6e3a; + background: hsla(0, 0%, 100%, 0.5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function, +.token.class-name { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + +/* stylelint-enable */ +.mce-content-body { + overflow-wrap: break-word; + word-wrap: break-word; +} + +.mce-content-body .mce-visual-caret { + background-color: black; + background-color: currentColor; + position: absolute; +} + +.mce-content-body .mce-visual-caret-hidden { + display: none; +} + +.mce-content-body *[data-mce-caret] { + left: -1000px; + margin: 0; + padding: 0; + position: absolute; + right: auto; + top: 0; +} + +.mce-content-body .mce-offscreen-selection { + left: -2000000px; + max-width: 1000000px; + position: absolute; +} + +.mce-content-body *[contentEditable=false] { + cursor: default; +} + +.mce-content-body *[contentEditable=true] { + cursor: text; +} + +.tox-cursor-format-painter { + cursor: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"), default; +} + +.mce-content-body figure.align-left { + float: left; +} + +.mce-content-body figure.align-right { + float: right; +} + +.mce-content-body figure.image.align-center { + display: table; + margin-left: auto; + margin-right: auto; +} + +.mce-preview-object { + border: 1px solid gray; + display: inline-block; + line-height: 0; + margin: 0 2px 0 2px; + position: relative; +} + +.mce-preview-object .mce-shim { + background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} + +.mce-preview-object[data-mce-selected="2"] .mce-shim { + display: none; +} + +.mce-object { + background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center; + border: 1px dashed #aaa; +} + +.mce-pagebreak { + border: 1px dashed #aaa; + cursor: default; + display: block; + height: 5px; + margin-top: 15px; + page-break-before: always; + width: 100%; +} + +@media print { + .mce-pagebreak { + border: 0; + } +} + +.tiny-pageembed .mce-shim { + background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} + +.tiny-pageembed[data-mce-selected="2"] .mce-shim { + display: none; +} + +.tiny-pageembed { + display: inline-block; + position: relative; +} + +.tiny-pageembed--21by9, +.tiny-pageembed--16by9, +.tiny-pageembed--4by3, +.tiny-pageembed--1by1 { + display: block; + overflow: hidden; + padding: 0; + position: relative; + width: 100%; +} + +.tiny-pageembed--21by9 { + padding-top: 42.857143%; +} + +.tiny-pageembed--16by9 { + padding-top: 56.25%; +} + +.tiny-pageembed--4by3 { + padding-top: 75%; +} + +.tiny-pageembed--1by1 { + padding-top: 100%; +} + +.tiny-pageembed--21by9 iframe, +.tiny-pageembed--16by9 iframe, +.tiny-pageembed--4by3 iframe, +.tiny-pageembed--1by1 iframe { + border: 0; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} + +.mce-content-body[data-mce-placeholder] { + position: relative; +} + +.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before { + color: rgba(34, 47, 62, 0.7); + content: attr(data-mce-placeholder); + position: absolute; +} + +.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before { + left: 1px; + color: #a1b7cb; +} + +.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before { + right: 1px; +} + +.mce-content-body div.mce-resizehandle { + background-color: #4099ff; + border-color: #4099ff; + border-style: solid; + border-width: 1px; + box-sizing: border-box; + height: 10px; + position: absolute; + width: 10px; + z-index: 10000; +} + +.mce-content-body div.mce-resizehandle:hover { + background-color: #4099ff; +} + +.mce-content-body div.mce-resizehandle:nth-of-type(1) { + cursor: nwse-resize; +} + +.mce-content-body div.mce-resizehandle:nth-of-type(2) { + cursor: nesw-resize; +} + +.mce-content-body div.mce-resizehandle:nth-of-type(3) { + cursor: nwse-resize; +} + +.mce-content-body div.mce-resizehandle:nth-of-type(4) { + cursor: nesw-resize; +} + +.mce-content-body .mce-resize-backdrop { + z-index: 10000; +} + +.mce-content-body .mce-clonedresizable { + cursor: default; + opacity: 0.5; + outline: 1px dashed black; + position: absolute; + z-index: 10001; +} + +.mce-content-body .mce-clonedresizable.mce-resizetable-columns th, +.mce-content-body .mce-clonedresizable.mce-resizetable-columns td { + border: 0; +} + +.mce-content-body .mce-resize-helper { + background: #555; + background: rgba(0, 0, 0, 0.75); + border: 1px; + border-radius: 3px; + color: white; + display: none; + font-family: sans-serif; + font-size: 12px; + line-height: 14px; + margin: 5px 10px; + padding: 5px; + position: absolute; + white-space: nowrap; + z-index: 10002; +} + +.tox-rtc-user-selection { + position: relative; +} + +.tox-rtc-user-cursor { + bottom: 0; + cursor: default; + position: absolute; + top: 0; + width: 2px; +} + +.tox-rtc-user-cursor::before { + background-color: inherit; + border-radius: 50%; + content: ''; + display: block; + height: 8px; + position: absolute; + right: -3px; + top: -3px; + width: 8px; +} + +.tox-rtc-user-cursor:hover::after { + background-color: inherit; + border-radius: 100px; + box-sizing: border-box; + color: #fff; + content: attr(data-user); + display: block; + font-size: 12px; + font-weight: bold; + left: -5px; + min-height: 8px; + min-width: 8px; + padding: 0 12px; + position: absolute; + top: -11px; + white-space: nowrap; + z-index: 1000; +} + +.tox-rtc-user-selection--1 .tox-rtc-user-cursor { + background-color: #2dc26b; +} + +.tox-rtc-user-selection--2 .tox-rtc-user-cursor { + background-color: #e03e2d; +} + +.tox-rtc-user-selection--3 .tox-rtc-user-cursor { + background-color: #f1c40f; +} + +.tox-rtc-user-selection--4 .tox-rtc-user-cursor { + background-color: #3598db; +} + +.tox-rtc-user-selection--5 .tox-rtc-user-cursor { + background-color: #b96ad9; +} + +.tox-rtc-user-selection--6 .tox-rtc-user-cursor { + background-color: #e67e23; +} + +.tox-rtc-user-selection--7 .tox-rtc-user-cursor { + background-color: #aaa69d; +} + +.tox-rtc-user-selection--8 .tox-rtc-user-cursor { + background-color: #f368e0; +} + +.tox-rtc-remote-image { + background: #eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center; + border: 1px solid #ccc; + min-height: 240px; + min-width: 320px; +} + +.mce-match-marker { + background: #aaa; + color: #fff; +} + +.mce-match-marker-selected { + background: #39f; + color: #fff; +} + +.mce-match-marker-selected::-moz-selection { + background: #39f; + color: #fff; +} + +.mce-match-marker-selected::selection { + background: #39f; + color: #fff; +} + +.mce-content-body img[data-mce-selected], +.mce-content-body video[data-mce-selected], +.mce-content-body audio[data-mce-selected], +.mce-content-body object[data-mce-selected], +.mce-content-body embed[data-mce-selected], +.mce-content-body table[data-mce-selected] { + outline: 3px solid #b4d7ff; +} + +.mce-content-body hr[data-mce-selected] { + outline: 3px solid #b4d7ff; + outline-offset: 1px; +} + +.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus { + outline: 3px solid #b4d7ff; +} + +.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover { + outline: 3px solid #b4d7ff; +} + +.mce-content-body *[contentEditable=false][data-mce-selected] { + cursor: not-allowed; + outline: 3px solid #b4d7ff; +} + +.mce-content-body.mce-content-readonly *[contentEditable=true]:focus, +.mce-content-body.mce-content-readonly *[contentEditable=true]:hover { + outline: none; +} + +.mce-content-body *[data-mce-selected="inline-boundary"] { + background-color: #b4d7ff; +} + +.mce-content-body .mce-edit-focus { + outline: 3px solid #b4d7ff; +} + +.mce-content-body td[data-mce-selected], +.mce-content-body th[data-mce-selected] { + position: relative; +} + +.mce-content-body td[data-mce-selected]::-moz-selection, +.mce-content-body th[data-mce-selected]::-moz-selection { + background: none; +} + +.mce-content-body td[data-mce-selected]::selection, +.mce-content-body th[data-mce-selected]::selection { + background: none; +} + +.mce-content-body td[data-mce-selected] *, +.mce-content-body th[data-mce-selected] * { + outline: none; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.mce-content-body td[data-mce-selected]::after, +.mce-content-body th[data-mce-selected]::after { + background-color: rgba(180, 215, 255, 0.7); + border: 1px solid rgba(180, 215, 255, 0.7); + bottom: -1px; + content: ''; + left: -1px; + mix-blend-mode: multiply; + position: absolute; + right: -1px; + top: -1px; +} + +@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { + .mce-content-body td[data-mce-selected]::after, + .mce-content-body th[data-mce-selected]::after { + border-color: rgba(0, 84, 180, 0.7); + } +} + +.mce-content-body img::-moz-selection { + background: none; +} + +.mce-content-body img::selection { + background: none; +} + +.ephox-snooker-resizer-bar { + background-color: #b4d7ff; + opacity: 0; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.ephox-snooker-resizer-cols { + cursor: col-resize; +} + +.ephox-snooker-resizer-rows { + cursor: row-resize; +} + +.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging { + opacity: 1; +} + +.mce-spellchecker-word { + background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A"); + background-position: 0 calc(100% + 1px); + background-repeat: repeat-x; + background-size: auto 6px; + cursor: default; + height: 2rem; +} + +.mce-spellchecker-grammar { + background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A"); + background-position: 0 calc(100% + 1px); + background-repeat: repeat-x; + background-size: auto 6px; + cursor: default; +} + +.mce-toc { + border: 1px solid gray; +} + +.mce-toc h2 { + margin: 4px; +} + +.mce-toc li { + list-style-type: none; +} + +table[style*="border-width: 0px"], +.mce-item-table:not([border]), +.mce-item-table[border="0"], +table[style*="border-width: 0px"] td, +.mce-item-table:not([border]) td, +.mce-item-table[border="0"] td, +table[style*="border-width: 0px"] th, +.mce-item-table:not([border]) th, +.mce-item-table[border="0"] th, +table[style*="border-width: 0px"] caption, +.mce-item-table:not([border]) caption, +.mce-item-table[border="0"] caption { + border: 1px dashed #bbb; +} + +.mce-visualblocks p, +.mce-visualblocks h1, +.mce-visualblocks h2, +.mce-visualblocks h3, +.mce-visualblocks h4, +.mce-visualblocks h5, +.mce-visualblocks h6, +.mce-visualblocks div:not([data-mce-bogus]), +.mce-visualblocks section, +.mce-visualblocks article, +.mce-visualblocks blockquote, +.mce-visualblocks address, +.mce-visualblocks pre, +.mce-visualblocks figure, +.mce-visualblocks figcaption, +.mce-visualblocks hgroup, +.mce-visualblocks aside, +.mce-visualblocks ul, +.mce-visualblocks ol, +.mce-visualblocks dl { + background-repeat: no-repeat; + border: 1px dashed #bbb; + margin-left: 3px; + padding-top: 10px; +} + +.mce-visualblocks p { + background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7); +} + +.mce-visualblocks h1 { + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==); +} + +.mce-visualblocks h2 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==); +} + +.mce-visualblocks h3 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7); +} + +.mce-visualblocks h4 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==); +} + +.mce-visualblocks h5 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==); +} + +.mce-visualblocks h6 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==); +} + +.mce-visualblocks div:not([data-mce-bogus]) { + background-image: url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7); +} + +.mce-visualblocks section { + background-image: url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=); +} + +.mce-visualblocks article { + background-image: url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7); +} + +.mce-visualblocks blockquote { + background-image: url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7); +} + +.mce-visualblocks address { + background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=); +} + +.mce-visualblocks pre { + background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==); +} + +.mce-visualblocks figure { + background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7); +} + +.mce-visualblocks figcaption { + border: 1px dashed #bbb; +} + +.mce-visualblocks hgroup { + background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7); +} + +.mce-visualblocks aside { + background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=); +} + +.mce-visualblocks ul { + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==); +} + +.mce-visualblocks ol { + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==); +} + +.mce-visualblocks dl { + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==); +} + +.mce-visualblocks:not([dir=rtl]) p, +.mce-visualblocks:not([dir=rtl]) h1, +.mce-visualblocks:not([dir=rtl]) h2, +.mce-visualblocks:not([dir=rtl]) h3, +.mce-visualblocks:not([dir=rtl]) h4, +.mce-visualblocks:not([dir=rtl]) h5, +.mce-visualblocks:not([dir=rtl]) h6, +.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]), +.mce-visualblocks:not([dir=rtl]) section, +.mce-visualblocks:not([dir=rtl]) article, +.mce-visualblocks:not([dir=rtl]) blockquote, +.mce-visualblocks:not([dir=rtl]) address, +.mce-visualblocks:not([dir=rtl]) pre, +.mce-visualblocks:not([dir=rtl]) figure, +.mce-visualblocks:not([dir=rtl]) figcaption, +.mce-visualblocks:not([dir=rtl]) hgroup, +.mce-visualblocks:not([dir=rtl]) aside, +.mce-visualblocks:not([dir=rtl]) ul, +.mce-visualblocks:not([dir=rtl]) ol, +.mce-visualblocks:not([dir=rtl]) dl { + margin-left: 3px; +} + +.mce-visualblocks[dir=rtl] p, +.mce-visualblocks[dir=rtl] h1, +.mce-visualblocks[dir=rtl] h2, +.mce-visualblocks[dir=rtl] h3, +.mce-visualblocks[dir=rtl] h4, +.mce-visualblocks[dir=rtl] h5, +.mce-visualblocks[dir=rtl] h6, +.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]), +.mce-visualblocks[dir=rtl] section, +.mce-visualblocks[dir=rtl] article, +.mce-visualblocks[dir=rtl] blockquote, +.mce-visualblocks[dir=rtl] address, +.mce-visualblocks[dir=rtl] pre, +.mce-visualblocks[dir=rtl] figure, +.mce-visualblocks[dir=rtl] figcaption, +.mce-visualblocks[dir=rtl] hgroup, +.mce-visualblocks[dir=rtl] aside, +.mce-visualblocks[dir=rtl] ul, +.mce-visualblocks[dir=rtl] ol, +.mce-visualblocks[dir=rtl] dl { + background-position-x: right; + margin-right: 3px; +} + +.mce-nbsp, +.mce-shy { + background: #aaa; +} + +.mce-shy::after { + content: '-'; +} + +body { + font-family: sans-serif; +} + +table { + border-collapse: collapse; +} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.inline.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.inline.css new file mode 100644 index 0000000..fc15063 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.inline.css @@ -0,0 +1,861 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.mce-content-body .mce-item-anchor { + background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A") no-repeat center; + cursor: default; + display: inline-block; + height: 12px !important; + padding: 0 2px; + -webkit-user-modify: read-only; + -moz-user-modify: read-only; + -webkit-user-select: all; + -moz-user-select: all; + -ms-user-select: all; + user-select: all; + width: 8px !important; +} + +.mce-content-body .mce-item-anchor[data-mce-selected] { + outline-offset: 1px; +} + +.tox-comments-visible .tox-comment { + background-color: #fff0b7; +} + +.tox-comments-visible .tox-comment--active { + background-color: #ffe168; +} + +.tox-checklist > li:not(.tox-checklist--hidden) { + list-style: none; + margin: 0.25em 0; +} + +.tox-checklist > li:not(.tox-checklist--hidden)::before { + content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A"); + cursor: pointer; + height: 1em; + margin-left: -1.5em; + margin-top: 0.125em; + position: absolute; + width: 1em; +} + +.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before { + content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A"); +} + +[dir=rtl] .tox-checklist > li:not(.tox-checklist--hidden)::before { + margin-left: 0; + margin-right: -1.5em; +} + +/* stylelint-disable */ +/* http://prismjs.com/ */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ +code[class*="language-"], +pre[class*="language-"] { + color: black; + background: none; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 1em; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + -moz-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, +pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, +code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, +pre[class*="language-"] ::selection, +code[class*="language-"]::selection, +code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: 0.5em 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9a6e3a; + background: hsla(0, 0%, 100%, 0.5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function, +.token.class-name { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} + +/* stylelint-enable */ +.mce-content-body { + overflow-wrap: break-word; + word-wrap: break-word; +} + +.mce-content-body .mce-visual-caret { + background-color: black; + background-color: currentColor; + position: absolute; +} + +.mce-content-body .mce-visual-caret-hidden { + display: none; +} + +.mce-content-body *[data-mce-caret] { + left: -1000px; + margin: 0; + padding: 0; + position: absolute; + right: auto; + top: 0; +} + +.mce-content-body .mce-offscreen-selection { + left: -2000000px; + max-width: 1000000px; + position: absolute; +} + +.mce-content-body *[contentEditable=false] { + cursor: default; +} + +.mce-content-body *[contentEditable=true] { + cursor: text; +} + +.tox-cursor-format-painter { + cursor: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"), default; +} + +.mce-content-body figure.align-left { + float: left; +} + +.mce-content-body figure.align-right { + float: right; +} + +.mce-content-body figure.image.align-center { + display: table; + margin-left: auto; + margin-right: auto; +} + +.mce-preview-object { + border: 1px solid gray; + display: inline-block; + line-height: 0; + margin: 0 2px 0 2px; + position: relative; +} + +.mce-preview-object .mce-shim { + background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} + +.mce-preview-object[data-mce-selected="2"] .mce-shim { + display: none; +} + +.mce-object { + background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center; + border: 1px dashed #aaa; +} + +.mce-pagebreak { + border: 1px dashed #aaa; + cursor: default; + display: block; + height: 5px; + margin-top: 15px; + page-break-before: always; + width: 100%; +} + +@media print { + .mce-pagebreak { + border: 0; + } +} + +.tiny-pageembed .mce-shim { + background: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} + +.tiny-pageembed[data-mce-selected="2"] .mce-shim { + display: none; +} + +.tiny-pageembed { + display: inline-block; + position: relative; +} + +.tiny-pageembed--21by9, +.tiny-pageembed--16by9, +.tiny-pageembed--4by3, +.tiny-pageembed--1by1 { + display: block; + overflow: hidden; + padding: 0; + position: relative; + width: 100%; +} + +.tiny-pageembed--21by9 { + padding-top: 42.857143%; +} + +.tiny-pageembed--16by9 { + padding-top: 56.25%; +} + +.tiny-pageembed--4by3 { + padding-top: 75%; +} + +.tiny-pageembed--1by1 { + padding-top: 100%; +} + +.tiny-pageembed--21by9 iframe, +.tiny-pageembed--16by9 iframe, +.tiny-pageembed--4by3 iframe, +.tiny-pageembed--1by1 iframe { + border: 0; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} + +.mce-content-body[data-mce-placeholder] { + position: relative; +} + +.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before { + color: rgba(34, 47, 62, 0.7); + content: attr(data-mce-placeholder); + position: absolute; +} + +.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before { + left: 1px; + color: #a1b7cb; +} + +.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before { + right: 1px; +} + +.mce-content-body div.mce-resizehandle { + background-color: #4099ff; + border-color: #4099ff; + border-style: solid; + border-width: 1px; + box-sizing: border-box; + height: 10px; + position: absolute; + width: 10px; + z-index: 10000; +} + +.mce-content-body div.mce-resizehandle:hover { + background-color: #4099ff; +} + +.mce-content-body div.mce-resizehandle:nth-of-type(1) { + cursor: nwse-resize; +} + +.mce-content-body div.mce-resizehandle:nth-of-type(2) { + cursor: nesw-resize; +} + +.mce-content-body div.mce-resizehandle:nth-of-type(3) { + cursor: nwse-resize; +} + +.mce-content-body div.mce-resizehandle:nth-of-type(4) { + cursor: nesw-resize; +} + +.mce-content-body .mce-resize-backdrop { + z-index: 10000; +} + +.mce-content-body .mce-clonedresizable { + cursor: default; + opacity: 0.5; + outline: 1px dashed black; + position: absolute; + z-index: 10001; +} + +.mce-content-body .mce-clonedresizable.mce-resizetable-columns th, +.mce-content-body .mce-clonedresizable.mce-resizetable-columns td { + border: 0; +} + +.mce-content-body .mce-resize-helper { + background: #555; + background: rgba(0, 0, 0, 0.75); + border: 1px; + border-radius: 3px; + color: white; + display: none; + font-family: sans-serif; + font-size: 12px; + line-height: 14px; + margin: 5px 10px; + padding: 5px; + position: absolute; + white-space: nowrap; + z-index: 10002; +} + +.tox-rtc-user-selection { + position: relative; +} + +.tox-rtc-user-cursor { + bottom: 0; + cursor: default; + position: absolute; + top: 0; + width: 2px; +} + +.tox-rtc-user-cursor::before { + background-color: inherit; + border-radius: 50%; + content: ''; + display: block; + height: 8px; + position: absolute; + right: -3px; + top: -3px; + width: 8px; +} + +.tox-rtc-user-cursor:hover::after { + background-color: inherit; + border-radius: 100px; + box-sizing: border-box; + color: #fff; + content: attr(data-user); + display: block; + font-size: 12px; + font-weight: bold; + left: -5px; + min-height: 8px; + min-width: 8px; + padding: 0 12px; + position: absolute; + top: -11px; + white-space: nowrap; + z-index: 1000; +} + +.tox-rtc-user-selection--1 .tox-rtc-user-cursor { + background-color: #2dc26b; +} + +.tox-rtc-user-selection--2 .tox-rtc-user-cursor { + background-color: #e03e2d; +} + +.tox-rtc-user-selection--3 .tox-rtc-user-cursor { + background-color: #f1c40f; +} + +.tox-rtc-user-selection--4 .tox-rtc-user-cursor { + background-color: #3598db; +} + +.tox-rtc-user-selection--5 .tox-rtc-user-cursor { + background-color: #b96ad9; +} + +.tox-rtc-user-selection--6 .tox-rtc-user-cursor { + background-color: #e67e23; +} + +.tox-rtc-user-selection--7 .tox-rtc-user-cursor { + background-color: #aaa69d; +} + +.tox-rtc-user-selection--8 .tox-rtc-user-cursor { + background-color: #f368e0; +} + +.tox-rtc-remote-image { + background: #eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center; + border: 1px solid #ccc; + min-height: 240px; + min-width: 320px; +} + +.mce-match-marker { + background: #aaa; + color: #fff; +} + +.mce-match-marker-selected { + background: #39f; + color: #fff; +} + +.mce-match-marker-selected::-moz-selection { + background: #39f; + color: #fff; +} + +.mce-match-marker-selected::selection { + background: #39f; + color: #fff; +} + +.mce-content-body img[data-mce-selected], +.mce-content-body video[data-mce-selected], +.mce-content-body audio[data-mce-selected], +.mce-content-body object[data-mce-selected], +.mce-content-body embed[data-mce-selected], +.mce-content-body table[data-mce-selected] { + outline: 3px solid #b4d7ff; +} + +.mce-content-body hr[data-mce-selected] { + outline: 3px solid #b4d7ff; + outline-offset: 1px; +} + +.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus { + outline: 3px solid #b4d7ff; +} + +.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover { + outline: 3px solid #b4d7ff; +} + +.mce-content-body *[contentEditable=false][data-mce-selected] { + cursor: not-allowed; + outline: 3px solid #b4d7ff; +} + +.mce-content-body.mce-content-readonly *[contentEditable=true]:focus, +.mce-content-body.mce-content-readonly *[contentEditable=true]:hover { + outline: none; +} + +.mce-content-body *[data-mce-selected="inline-boundary"] { + background-color: #b4d7ff; +} + +.mce-content-body .mce-edit-focus { + outline: 3px solid #b4d7ff; +} + +.mce-content-body td[data-mce-selected], +.mce-content-body th[data-mce-selected] { + position: relative; +} + +.mce-content-body td[data-mce-selected]::-moz-selection, +.mce-content-body th[data-mce-selected]::-moz-selection { + background: none; +} + +.mce-content-body td[data-mce-selected]::selection, +.mce-content-body th[data-mce-selected]::selection { + background: none; +} + +.mce-content-body td[data-mce-selected] *, +.mce-content-body th[data-mce-selected] * { + outline: none; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.mce-content-body td[data-mce-selected]::after, +.mce-content-body th[data-mce-selected]::after { + background-color: rgba(180, 215, 255, 0.7); + border: 1px solid rgba(180, 215, 255, 0.7); + bottom: -1px; + content: ''; + left: -1px; + mix-blend-mode: multiply; + position: absolute; + right: -1px; + top: -1px; +} + +@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { + .mce-content-body td[data-mce-selected]::after, + .mce-content-body th[data-mce-selected]::after { + border-color: rgba(0, 84, 180, 0.7); + } +} + +.mce-content-body img::-moz-selection { + background: none; +} + +.mce-content-body img::selection { + background: none; +} + +.ephox-snooker-resizer-bar { + background-color: #b4d7ff; + opacity: 0; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.ephox-snooker-resizer-cols { + cursor: col-resize; +} + +.ephox-snooker-resizer-rows { + cursor: row-resize; +} + +.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging { + opacity: 1; +} + +.mce-spellchecker-word { + background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A"); + background-position: 0 calc(100% + 1px); + background-repeat: repeat-x; + background-size: auto 6px; + cursor: default; + height: 2rem; +} + +.mce-spellchecker-grammar { + background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A"); + background-position: 0 calc(100% + 1px); + background-repeat: repeat-x; + background-size: auto 6px; + cursor: default; +} + +.mce-toc { + border: 1px solid gray; +} + +.mce-toc h2 { + margin: 4px; +} + +.mce-toc li { + list-style-type: none; +} + +table[style*="border-width: 0px"], +.mce-item-table:not([border]), +.mce-item-table[border="0"], +table[style*="border-width: 0px"] td, +.mce-item-table:not([border]) td, +.mce-item-table[border="0"] td, +table[style*="border-width: 0px"] th, +.mce-item-table:not([border]) th, +.mce-item-table[border="0"] th, +table[style*="border-width: 0px"] caption, +.mce-item-table:not([border]) caption, +.mce-item-table[border="0"] caption { + border: 1px dashed #bbb; +} + +.mce-visualblocks p, +.mce-visualblocks h1, +.mce-visualblocks h2, +.mce-visualblocks h3, +.mce-visualblocks h4, +.mce-visualblocks h5, +.mce-visualblocks h6, +.mce-visualblocks div:not([data-mce-bogus]), +.mce-visualblocks section, +.mce-visualblocks article, +.mce-visualblocks blockquote, +.mce-visualblocks address, +.mce-visualblocks pre, +.mce-visualblocks figure, +.mce-visualblocks figcaption, +.mce-visualblocks hgroup, +.mce-visualblocks aside, +.mce-visualblocks ul, +.mce-visualblocks ol, +.mce-visualblocks dl { + background-repeat: no-repeat; + border: 1px dashed #bbb; + margin-left: 3px; + padding-top: 10px; +} + +.mce-visualblocks p { + background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7); +} + +.mce-visualblocks h1 { + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==); +} + +.mce-visualblocks h2 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==); +} + +.mce-visualblocks h3 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7); +} + +.mce-visualblocks h4 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==); +} + +.mce-visualblocks h5 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==); +} + +.mce-visualblocks h6 { + background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==); +} + +.mce-visualblocks div:not([data-mce-bogus]) { + background-image: url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7); +} + +.mce-visualblocks section { + background-image: url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=); +} + +.mce-visualblocks article { + background-image: url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7); +} + +.mce-visualblocks blockquote { + background-image: url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7); +} + +.mce-visualblocks address { + background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=); +} + +.mce-visualblocks pre { + background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==); +} + +.mce-visualblocks figure { + background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7); +} + +.mce-visualblocks figcaption { + border: 1px dashed #bbb; +} + +.mce-visualblocks hgroup { + background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7); +} + +.mce-visualblocks aside { + background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=); +} + +.mce-visualblocks ul { + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==); +} + +.mce-visualblocks ol { + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==); +} + +.mce-visualblocks dl { + background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==); +} + +.mce-visualblocks:not([dir=rtl]) p, +.mce-visualblocks:not([dir=rtl]) h1, +.mce-visualblocks:not([dir=rtl]) h2, +.mce-visualblocks:not([dir=rtl]) h3, +.mce-visualblocks:not([dir=rtl]) h4, +.mce-visualblocks:not([dir=rtl]) h5, +.mce-visualblocks:not([dir=rtl]) h6, +.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]), +.mce-visualblocks:not([dir=rtl]) section, +.mce-visualblocks:not([dir=rtl]) article, +.mce-visualblocks:not([dir=rtl]) blockquote, +.mce-visualblocks:not([dir=rtl]) address, +.mce-visualblocks:not([dir=rtl]) pre, +.mce-visualblocks:not([dir=rtl]) figure, +.mce-visualblocks:not([dir=rtl]) figcaption, +.mce-visualblocks:not([dir=rtl]) hgroup, +.mce-visualblocks:not([dir=rtl]) aside, +.mce-visualblocks:not([dir=rtl]) ul, +.mce-visualblocks:not([dir=rtl]) ol, +.mce-visualblocks:not([dir=rtl]) dl { + margin-left: 3px; +} + +.mce-visualblocks[dir=rtl] p, +.mce-visualblocks[dir=rtl] h1, +.mce-visualblocks[dir=rtl] h2, +.mce-visualblocks[dir=rtl] h3, +.mce-visualblocks[dir=rtl] h4, +.mce-visualblocks[dir=rtl] h5, +.mce-visualblocks[dir=rtl] h6, +.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]), +.mce-visualblocks[dir=rtl] section, +.mce-visualblocks[dir=rtl] article, +.mce-visualblocks[dir=rtl] blockquote, +.mce-visualblocks[dir=rtl] address, +.mce-visualblocks[dir=rtl] pre, +.mce-visualblocks[dir=rtl] figure, +.mce-visualblocks[dir=rtl] figcaption, +.mce-visualblocks[dir=rtl] hgroup, +.mce-visualblocks[dir=rtl] aside, +.mce-visualblocks[dir=rtl] ul, +.mce-visualblocks[dir=rtl] ol, +.mce-visualblocks[dir=rtl] dl { + background-position-x: right; + margin-right: 3px; +} + +.mce-nbsp, +.mce-shy { + background: #aaa; +} + +.mce-shy::after { + content: '-'; +} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.inline.min.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.inline.min.css new file mode 100644 index 0000000..5c66aca --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.inline.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.mce-content-body .mce-item-anchor{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;cursor:default;display:inline-block;height:12px!important;padding:0 2px;-webkit-user-modify:read-only;-moz-user-modify:read-only;-webkit-user-select:all;-moz-user-select:all;-ms-user-select:all;user-select:all;width:8px!important}.mce-content-body .mce-item-anchor[data-mce-selected]{outline-offset:1px}.tox-comments-visible .tox-comment{background-color:#fff0b7}.tox-comments-visible .tox-comment--active{background-color:#ffe168}.tox-checklist>li:not(.tox-checklist--hidden){list-style:none;margin:.25em 0}.tox-checklist>li:not(.tox-checklist--hidden)::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A");cursor:pointer;height:1em;margin-left:-1.5em;margin-top:.125em;position:absolute;width:1em}.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A")}[dir=rtl] .tox-checklist>li:not(.tox-checklist--hidden)::before{margin-left:0;margin-right:-1.5em}code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.mce-content-body{overflow-wrap:break-word;word-wrap:break-word}.mce-content-body .mce-visual-caret{background-color:#000;background-color:currentColor;position:absolute}.mce-content-body .mce-visual-caret-hidden{display:none}.mce-content-body [data-mce-caret]{left:-1000px;margin:0;padding:0;position:absolute;right:auto;top:0}.mce-content-body .mce-offscreen-selection{left:-2000000px;max-width:1000000px;position:absolute}.mce-content-body [contentEditable=false]{cursor:default}.mce-content-body [contentEditable=true]{cursor:text}.tox-cursor-format-painter{cursor:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"),default}.mce-content-body figure.align-left{float:left}.mce-content-body figure.align-right{float:right}.mce-content-body figure.image.align-center{display:table;margin-left:auto;margin-right:auto}.mce-preview-object{border:1px solid gray;display:inline-block;line-height:0;margin:0 2px 0 2px;position:relative}.mce-preview-object .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-object{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;border:1px dashed #aaa}.mce-pagebreak{border:1px dashed #aaa;cursor:default;display:block;height:5px;margin-top:15px;page-break-before:always;width:100%}@media print{.mce-pagebreak{border:0}}.tiny-pageembed .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.tiny-pageembed[data-mce-selected="2"] .mce-shim{display:none}.tiny-pageembed{display:inline-block;position:relative}.tiny-pageembed--16by9,.tiny-pageembed--1by1,.tiny-pageembed--21by9,.tiny-pageembed--4by3{display:block;overflow:hidden;padding:0;position:relative;width:100%}.tiny-pageembed--21by9{padding-top:42.857143%}.tiny-pageembed--16by9{padding-top:56.25%}.tiny-pageembed--4by3{padding-top:75%}.tiny-pageembed--1by1{padding-top:100%}.tiny-pageembed--16by9 iframe,.tiny-pageembed--1by1 iframe,.tiny-pageembed--21by9 iframe,.tiny-pageembed--4by3 iframe{border:0;height:100%;left:0;position:absolute;top:0;width:100%}.mce-content-body[data-mce-placeholder]{position:relative}.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before{color:rgba(34,47,62,.7);content:attr(data-mce-placeholder);position:absolute}.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before{left:1px;color:#a1b7cb;}.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before{right:1px}.mce-content-body div.mce-resizehandle{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;height:10px;position:absolute;width:10px;z-index:10000}.mce-content-body div.mce-resizehandle:hover{background-color:#4099ff}.mce-content-body div.mce-resizehandle:nth-of-type(1){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(2){cursor:nesw-resize}.mce-content-body div.mce-resizehandle:nth-of-type(3){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(4){cursor:nesw-resize}.mce-content-body .mce-resize-backdrop{z-index:10000}.mce-content-body .mce-clonedresizable{cursor:default;opacity:.5;outline:1px dashed #000;position:absolute;z-index:10001}.mce-content-body .mce-clonedresizable.mce-resizetable-columns td,.mce-content-body .mce-clonedresizable.mce-resizetable-columns th{border:0}.mce-content-body .mce-resize-helper{background:#555;background:rgba(0,0,0,.75);border:1px;border-radius:3px;color:#fff;display:none;font-family:sans-serif;font-size:12px;line-height:14px;margin:5px 10px;padding:5px;position:absolute;white-space:nowrap;z-index:10002}.tox-rtc-user-selection{position:relative}.tox-rtc-user-cursor{bottom:0;cursor:default;position:absolute;top:0;width:2px}.tox-rtc-user-cursor::before{background-color:inherit;border-radius:50%;content:'';display:block;height:8px;position:absolute;right:-3px;top:-3px;width:8px}.tox-rtc-user-cursor:hover::after{background-color:inherit;border-radius:100px;box-sizing:border-box;color:#fff;content:attr(data-user);display:block;font-size:12px;font-weight:700;left:-5px;min-height:8px;min-width:8px;padding:0 12px;position:absolute;top:-11px;white-space:nowrap;z-index:1000}.tox-rtc-user-selection--1 .tox-rtc-user-cursor{background-color:#2dc26b}.tox-rtc-user-selection--2 .tox-rtc-user-cursor{background-color:#e03e2d}.tox-rtc-user-selection--3 .tox-rtc-user-cursor{background-color:#f1c40f}.tox-rtc-user-selection--4 .tox-rtc-user-cursor{background-color:#3598db}.tox-rtc-user-selection--5 .tox-rtc-user-cursor{background-color:#b96ad9}.tox-rtc-user-selection--6 .tox-rtc-user-cursor{background-color:#e67e23}.tox-rtc-user-selection--7 .tox-rtc-user-cursor{background-color:#aaa69d}.tox-rtc-user-selection--8 .tox-rtc-user-cursor{background-color:#f368e0}.tox-rtc-remote-image{background:#eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center;border:1px solid #ccc;min-height:240px;min-width:320px}.mce-match-marker{background:#aaa;color:#fff}.mce-match-marker-selected{background:#39f;color:#fff}.mce-match-marker-selected::-moz-selection{background:#39f;color:#fff}.mce-match-marker-selected::selection{background:#39f;color:#fff}.mce-content-body audio[data-mce-selected],.mce-content-body embed[data-mce-selected],.mce-content-body img[data-mce-selected],.mce-content-body object[data-mce-selected],.mce-content-body table[data-mce-selected],.mce-content-body video[data-mce-selected]{outline:3px solid #b4d7ff}.mce-content-body hr[data-mce-selected]{outline:3px solid #b4d7ff;outline-offset:1px}.mce-content-body [contentEditable=false] [contentEditable=true]:focus{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false] [contentEditable=true]:hover{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false][data-mce-selected]{cursor:not-allowed;outline:3px solid #b4d7ff}.mce-content-body.mce-content-readonly [contentEditable=true]:focus,.mce-content-body.mce-content-readonly [contentEditable=true]:hover{outline:0}.mce-content-body [data-mce-selected=inline-boundary]{background-color:#b4d7ff}.mce-content-body .mce-edit-focus{outline:3px solid #b4d7ff}.mce-content-body td[data-mce-selected],.mce-content-body th[data-mce-selected]{position:relative}.mce-content-body td[data-mce-selected]::-moz-selection,.mce-content-body th[data-mce-selected]::-moz-selection{background:0 0}.mce-content-body td[data-mce-selected]::selection,.mce-content-body th[data-mce-selected]::selection{background:0 0}.mce-content-body td[data-mce-selected] *,.mce-content-body th[data-mce-selected] *{outline:0;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{background-color:rgba(180,215,255,.7);border:1px solid rgba(180,215,255,.7);bottom:-1px;content:'';left:-1px;mix-blend-mode:multiply;position:absolute;right:-1px;top:-1px}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{border-color:rgba(0,84,180,.7)}}.mce-content-body img::-moz-selection{background:0 0}.mce-content-body img::selection{background:0 0}.ephox-snooker-resizer-bar{background-color:#b4d7ff;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:1}.mce-spellchecker-word{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default;height:2rem}.mce-spellchecker-grammar{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-item-table:not([border]),.mce-item-table:not([border]) caption,.mce-item-table:not([border]) td,.mce-item-table:not([border]) th,.mce-item-table[border="0"],.mce-item-table[border="0"] caption,.mce-item-table[border="0"] td,.mce-item-table[border="0"] th,table[style*="border-width: 0px"],table[style*="border-width: 0px"] caption,table[style*="border-width: 0px"] td,table[style*="border-width: 0px"] th{border:1px dashed #bbb}.mce-visualblocks address,.mce-visualblocks article,.mce-visualblocks aside,.mce-visualblocks blockquote,.mce-visualblocks div:not([data-mce-bogus]),.mce-visualblocks dl,.mce-visualblocks figcaption,.mce-visualblocks figure,.mce-visualblocks h1,.mce-visualblocks h2,.mce-visualblocks h3,.mce-visualblocks h4,.mce-visualblocks h5,.mce-visualblocks h6,.mce-visualblocks hgroup,.mce-visualblocks ol,.mce-visualblocks p,.mce-visualblocks pre,.mce-visualblocks section,.mce-visualblocks ul{background-repeat:no-repeat;border:1px dashed #bbb;margin-left:3px;padding-top:10px}.mce-visualblocks p{background-image:url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7)}.mce-visualblocks h1{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==)}.mce-visualblocks h2{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==)}.mce-visualblocks h3{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7)}.mce-visualblocks h4{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==)}.mce-visualblocks h5{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==)}.mce-visualblocks h6{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==)}.mce-visualblocks div:not([data-mce-bogus]){background-image:url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7)}.mce-visualblocks section{background-image:url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=)}.mce-visualblocks article{background-image:url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7)}.mce-visualblocks blockquote{background-image:url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7)}.mce-visualblocks address{background-image:url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=)}.mce-visualblocks pre{background-image:url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==)}.mce-visualblocks figure{background-image:url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7)}.mce-visualblocks figcaption{border:1px dashed #bbb}.mce-visualblocks hgroup{background-image:url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7)}.mce-visualblocks aside{background-image:url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=)}.mce-visualblocks ul{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==)}.mce-visualblocks ol{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==)}.mce-visualblocks dl{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==)}.mce-visualblocks:not([dir=rtl]) address,.mce-visualblocks:not([dir=rtl]) article,.mce-visualblocks:not([dir=rtl]) aside,.mce-visualblocks:not([dir=rtl]) blockquote,.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),.mce-visualblocks:not([dir=rtl]) dl,.mce-visualblocks:not([dir=rtl]) figcaption,.mce-visualblocks:not([dir=rtl]) figure,.mce-visualblocks:not([dir=rtl]) h1,.mce-visualblocks:not([dir=rtl]) h2,.mce-visualblocks:not([dir=rtl]) h3,.mce-visualblocks:not([dir=rtl]) h4,.mce-visualblocks:not([dir=rtl]) h5,.mce-visualblocks:not([dir=rtl]) h6,.mce-visualblocks:not([dir=rtl]) hgroup,.mce-visualblocks:not([dir=rtl]) ol,.mce-visualblocks:not([dir=rtl]) p,.mce-visualblocks:not([dir=rtl]) pre,.mce-visualblocks:not([dir=rtl]) section,.mce-visualblocks:not([dir=rtl]) ul{margin-left:3px}.mce-visualblocks[dir=rtl] address,.mce-visualblocks[dir=rtl] article,.mce-visualblocks[dir=rtl] aside,.mce-visualblocks[dir=rtl] blockquote,.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),.mce-visualblocks[dir=rtl] dl,.mce-visualblocks[dir=rtl] figcaption,.mce-visualblocks[dir=rtl] figure,.mce-visualblocks[dir=rtl] h1,.mce-visualblocks[dir=rtl] h2,.mce-visualblocks[dir=rtl] h3,.mce-visualblocks[dir=rtl] h4,.mce-visualblocks[dir=rtl] h5,.mce-visualblocks[dir=rtl] h6,.mce-visualblocks[dir=rtl] hgroup,.mce-visualblocks[dir=rtl] ol,.mce-visualblocks[dir=rtl] p,.mce-visualblocks[dir=rtl] pre,.mce-visualblocks[dir=rtl] section,.mce-visualblocks[dir=rtl] ul{background-position-x:right;margin-right:3px}.mce-nbsp,.mce-shy{background:#aaa}.mce-shy::after{content:'-'} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.min.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.min.css new file mode 100644 index 0000000..b3dcb52 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.mce-content-body .mce-item-anchor{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;cursor:default;display:inline-block;height:12px!important;padding:0 2px;-webkit-user-modify:read-only;-moz-user-modify:read-only;-webkit-user-select:all;-moz-user-select:all;-ms-user-select:all;user-select:all;width:8px!important}.mce-content-body .mce-item-anchor[data-mce-selected]{outline-offset:1px}.tox-comments-visible .tox-comment{background-color:#fff0b7}.tox-comments-visible .tox-comment--active{background-color:#ffe168}.tox-checklist>li:not(.tox-checklist--hidden){list-style:none;margin:.25em 0}.tox-checklist>li:not(.tox-checklist--hidden)::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A");cursor:pointer;height:1em;margin-left:-1.5em;margin-top:.125em;position:absolute;width:1em}.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A")}[dir=rtl] .tox-checklist>li:not(.tox-checklist--hidden)::before{margin-left:0;margin-right:-1.5em}code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.mce-content-body{overflow-wrap:break-word;word-wrap:break-word}.mce-content-body .mce-visual-caret{background-color:#000;background-color:currentColor;position:absolute}.mce-content-body .mce-visual-caret-hidden{display:none}.mce-content-body [data-mce-caret]{left:-1000px;margin:0;padding:0;position:absolute;right:auto;top:0}.mce-content-body .mce-offscreen-selection{left:-2000000px;max-width:1000000px;position:absolute}.mce-content-body [contentEditable=false]{cursor:default}.mce-content-body [contentEditable=true]{cursor:text}.tox-cursor-format-painter{cursor:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"),default}.mce-content-body figure.align-left{float:left}.mce-content-body figure.align-right{float:right}.mce-content-body figure.image.align-center{display:table;margin-left:auto;margin-right:auto}.mce-preview-object{border:1px solid gray;display:inline-block;line-height:0;margin:0 2px 0 2px;position:relative}.mce-preview-object .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-object{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;border:1px dashed #aaa}.mce-pagebreak{border:1px dashed #aaa;cursor:default;display:block;height:5px;margin-top:15px;page-break-before:always;width:100%}@media print{.mce-pagebreak{border:0}}.tiny-pageembed .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.tiny-pageembed[data-mce-selected="2"] .mce-shim{display:none}.tiny-pageembed{display:inline-block;position:relative}.tiny-pageembed--16by9,.tiny-pageembed--1by1,.tiny-pageembed--21by9,.tiny-pageembed--4by3{display:block;overflow:hidden;padding:0;position:relative;width:100%}.tiny-pageembed--21by9{padding-top:42.857143%}.tiny-pageembed--16by9{padding-top:56.25%}.tiny-pageembed--4by3{padding-top:75%}.tiny-pageembed--1by1{padding-top:100%}.tiny-pageembed--16by9 iframe,.tiny-pageembed--1by1 iframe,.tiny-pageembed--21by9 iframe,.tiny-pageembed--4by3 iframe{border:0;height:100%;left:0;position:absolute;top:0;width:100%}.mce-content-body[data-mce-placeholder]{position:relative}.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before{color:rgba(34,47,62,.7);content:attr(data-mce-placeholder);position:absolute}.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before{left:1px;color: #a1b7cb;}.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before{right:1px}.mce-content-body div.mce-resizehandle{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;height:10px;position:absolute;width:10px;z-index:10000}.mce-content-body div.mce-resizehandle:hover{background-color:#4099ff}.mce-content-body div.mce-resizehandle:nth-of-type(1){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(2){cursor:nesw-resize}.mce-content-body div.mce-resizehandle:nth-of-type(3){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(4){cursor:nesw-resize}.mce-content-body .mce-resize-backdrop{z-index:10000}.mce-content-body .mce-clonedresizable{cursor:default;opacity:.5;outline:1px dashed #000;position:absolute;z-index:10001}.mce-content-body .mce-clonedresizable.mce-resizetable-columns td,.mce-content-body .mce-clonedresizable.mce-resizetable-columns th{border:0}.mce-content-body .mce-resize-helper{background:#555;background:rgba(0,0,0,.75);border:1px;border-radius:3px;color:#fff;display:none;font-family:sans-serif;font-size:12px;line-height:14px;margin:5px 10px;padding:5px;position:absolute;white-space:nowrap;z-index:10002}.tox-rtc-user-selection{position:relative}.tox-rtc-user-cursor{bottom:0;cursor:default;position:absolute;top:0;width:2px}.tox-rtc-user-cursor::before{background-color:inherit;border-radius:50%;content:'';display:block;height:8px;position:absolute;right:-3px;top:-3px;width:8px}.tox-rtc-user-cursor:hover::after{background-color:inherit;border-radius:100px;box-sizing:border-box;color:#fff;content:attr(data-user);display:block;font-size:12px;font-weight:700;left:-5px;min-height:8px;min-width:8px;padding:0 12px;position:absolute;top:-11px;white-space:nowrap;z-index:1000}.tox-rtc-user-selection--1 .tox-rtc-user-cursor{background-color:#2dc26b}.tox-rtc-user-selection--2 .tox-rtc-user-cursor{background-color:#e03e2d}.tox-rtc-user-selection--3 .tox-rtc-user-cursor{background-color:#f1c40f}.tox-rtc-user-selection--4 .tox-rtc-user-cursor{background-color:#3598db}.tox-rtc-user-selection--5 .tox-rtc-user-cursor{background-color:#b96ad9}.tox-rtc-user-selection--6 .tox-rtc-user-cursor{background-color:#e67e23}.tox-rtc-user-selection--7 .tox-rtc-user-cursor{background-color:#aaa69d}.tox-rtc-user-selection--8 .tox-rtc-user-cursor{background-color:#f368e0}.tox-rtc-remote-image{background:#eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center;border:1px solid #ccc;min-height:240px;min-width:320px}.mce-match-marker{background:#aaa;color:#fff}.mce-match-marker-selected{background:#39f;color:#fff}.mce-match-marker-selected::-moz-selection{background:#39f;color:#fff}.mce-match-marker-selected::selection{background:#39f;color:#fff}.mce-content-body audio[data-mce-selected],.mce-content-body embed[data-mce-selected],.mce-content-body img[data-mce-selected],.mce-content-body object[data-mce-selected],.mce-content-body table[data-mce-selected],.mce-content-body video[data-mce-selected]{outline:3px solid #b4d7ff}.mce-content-body hr[data-mce-selected]{outline:3px solid #b4d7ff;outline-offset:1px}.mce-content-body [contentEditable=false] [contentEditable=true]:focus{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false] [contentEditable=true]:hover{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false][data-mce-selected]{cursor:not-allowed;outline:3px solid #b4d7ff}.mce-content-body.mce-content-readonly [contentEditable=true]:focus,.mce-content-body.mce-content-readonly [contentEditable=true]:hover{outline:0}.mce-content-body [data-mce-selected=inline-boundary]{background-color:#b4d7ff}.mce-content-body .mce-edit-focus{outline:3px solid #b4d7ff}.mce-content-body td[data-mce-selected],.mce-content-body th[data-mce-selected]{position:relative}.mce-content-body td[data-mce-selected]::-moz-selection,.mce-content-body th[data-mce-selected]::-moz-selection{background:0 0}.mce-content-body td[data-mce-selected]::selection,.mce-content-body th[data-mce-selected]::selection{background:0 0}.mce-content-body td[data-mce-selected] *,.mce-content-body th[data-mce-selected] *{outline:0;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{background-color:rgba(180,215,255,.7);border:1px solid rgba(180,215,255,.7);bottom:-1px;content:'';left:-1px;mix-blend-mode:multiply;position:absolute;right:-1px;top:-1px}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{border-color:rgba(0,84,180,.7)}}.mce-content-body img::-moz-selection{background:0 0}.mce-content-body img::selection{background:0 0}.ephox-snooker-resizer-bar{background-color:#b4d7ff;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:1}.mce-spellchecker-word{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default;height:2rem}.mce-spellchecker-grammar{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-item-table:not([border]),.mce-item-table:not([border]) caption,.mce-item-table:not([border]) td,.mce-item-table:not([border]) th,.mce-item-table[border="0"],.mce-item-table[border="0"] caption,.mce-item-table[border="0"] td,.mce-item-table[border="0"] th,table[style*="border-width: 0px"],table[style*="border-width: 0px"] caption,table[style*="border-width: 0px"] td,table[style*="border-width: 0px"] th{border:1px dashed #bbb}.mce-visualblocks address,.mce-visualblocks article,.mce-visualblocks aside,.mce-visualblocks blockquote,.mce-visualblocks div:not([data-mce-bogus]),.mce-visualblocks dl,.mce-visualblocks figcaption,.mce-visualblocks figure,.mce-visualblocks h1,.mce-visualblocks h2,.mce-visualblocks h3,.mce-visualblocks h4,.mce-visualblocks h5,.mce-visualblocks h6,.mce-visualblocks hgroup,.mce-visualblocks ol,.mce-visualblocks p,.mce-visualblocks pre,.mce-visualblocks section,.mce-visualblocks ul{background-repeat:no-repeat;border:1px dashed #bbb;margin-left:3px;padding-top:10px}.mce-visualblocks p{background-image:url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7)}.mce-visualblocks h1{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==)}.mce-visualblocks h2{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==)}.mce-visualblocks h3{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7)}.mce-visualblocks h4{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==)}.mce-visualblocks h5{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==)}.mce-visualblocks h6{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==)}.mce-visualblocks div:not([data-mce-bogus]){background-image:url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7)}.mce-visualblocks section{background-image:url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=)}.mce-visualblocks article{background-image:url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7)}.mce-visualblocks blockquote{background-image:url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7)}.mce-visualblocks address{background-image:url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=)}.mce-visualblocks pre{background-image:url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==)}.mce-visualblocks figure{background-image:url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7)}.mce-visualblocks figcaption{border:1px dashed #bbb}.mce-visualblocks hgroup{background-image:url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7)}.mce-visualblocks aside{background-image:url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=)}.mce-visualblocks ul{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==)}.mce-visualblocks ol{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==)}.mce-visualblocks dl{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==)}.mce-visualblocks:not([dir=rtl]) address,.mce-visualblocks:not([dir=rtl]) article,.mce-visualblocks:not([dir=rtl]) aside,.mce-visualblocks:not([dir=rtl]) blockquote,.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),.mce-visualblocks:not([dir=rtl]) dl,.mce-visualblocks:not([dir=rtl]) figcaption,.mce-visualblocks:not([dir=rtl]) figure,.mce-visualblocks:not([dir=rtl]) h1,.mce-visualblocks:not([dir=rtl]) h2,.mce-visualblocks:not([dir=rtl]) h3,.mce-visualblocks:not([dir=rtl]) h4,.mce-visualblocks:not([dir=rtl]) h5,.mce-visualblocks:not([dir=rtl]) h6,.mce-visualblocks:not([dir=rtl]) hgroup,.mce-visualblocks:not([dir=rtl]) ol,.mce-visualblocks:not([dir=rtl]) p,.mce-visualblocks:not([dir=rtl]) pre,.mce-visualblocks:not([dir=rtl]) section,.mce-visualblocks:not([dir=rtl]) ul{margin-left:3px}.mce-visualblocks[dir=rtl] address,.mce-visualblocks[dir=rtl] article,.mce-visualblocks[dir=rtl] aside,.mce-visualblocks[dir=rtl] blockquote,.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),.mce-visualblocks[dir=rtl] dl,.mce-visualblocks[dir=rtl] figcaption,.mce-visualblocks[dir=rtl] figure,.mce-visualblocks[dir=rtl] h1,.mce-visualblocks[dir=rtl] h2,.mce-visualblocks[dir=rtl] h3,.mce-visualblocks[dir=rtl] h4,.mce-visualblocks[dir=rtl] h5,.mce-visualblocks[dir=rtl] h6,.mce-visualblocks[dir=rtl] hgroup,.mce-visualblocks[dir=rtl] ol,.mce-visualblocks[dir=rtl] p,.mce-visualblocks[dir=rtl] pre,.mce-visualblocks[dir=rtl] section,.mce-visualblocks[dir=rtl] ul{background-position-x:right;margin-right:3px}.mce-nbsp,.mce-shy{background:#aaa}.mce-shy::after{content:'-'}body{font-family:sans-serif}table{border-collapse:collapse} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.mobile.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.mobile.css new file mode 100644 index 0000000..68f5f0f --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.mobile.css @@ -0,0 +1,34 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection { + /* Note: this file is used inside the content, so isn't part of theming */ + background-color: green; + display: inline-block; + opacity: 0.5; + position: absolute; +} + +body { + -webkit-text-size-adjust: none; +} + +body img { + /* this is related to the content margin */ + max-width: 96vw; +} + +body table img { + max-width: 95%; +} + +body { + font-family: sans-serif; +} + +table { + border-collapse: collapse; +} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.mobile.min.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.mobile.min.css new file mode 100644 index 0000000..35f7dc0 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide/content.mobile.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide/fonts/tinymce-mobile.woff b/frontend/public/tinymce-dataease-private/skins/ui/oxide/fonts/tinymce-mobile.woff new file mode 100644 index 0000000..1e3be03 Binary files /dev/null and b/frontend/public/tinymce-dataease-private/skins/ui/oxide/fonts/tinymce-mobile.woff differ diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.css new file mode 100644 index 0000000..8395782 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.css @@ -0,0 +1,3616 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.tox { + box-shadow: none; + box-sizing: content-box; + color: #222f3e; + cursor: auto; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-size: 16px; + font-style: normal; + font-weight: normal; + line-height: normal; + -webkit-tap-highlight-color: transparent; + text-decoration: none; + text-shadow: none; + text-transform: none; + vertical-align: initial; + white-space: normal; +} + +.tox *:not(svg):not(rect) { + box-sizing: inherit; + color: inherit; + cursor: inherit; + direction: inherit; + font-family: inherit; + font-size: inherit; + font-style: inherit; + font-weight: inherit; + line-height: inherit; + -webkit-tap-highlight-color: inherit; + text-align: inherit; + text-decoration: inherit; + text-shadow: inherit; + text-transform: inherit; + vertical-align: inherit; + white-space: inherit; +} + +.tox *:not(svg):not(rect) { + /* stylelint-disable-line no-duplicate-selectors */ + background: transparent; + border: 0; + box-shadow: none; + float: none; + height: auto; + margin: 0; + max-width: none; + outline: 0; + padding: 0; + position: static; + width: auto; +} + +.tox:not([dir=rtl]) { + direction: ltr; + text-align: left; +} + +.tox[dir=rtl] { + direction: rtl; + text-align: right; +} + +.tox-tinymce { + border: 1px solid #cccccc; + border-radius: 0; + box-shadow: none; + box-sizing: border-box; + display: flex; + flex-direction: column; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + overflow: hidden; + position: relative; + visibility: inherit !important; +} + +.tox-tinymce-inline { + max-width: 800px; + border: none; + box-shadow: none; +} + +.tox-tinymce-inline .tox-editor-header { + background-color: transparent; + border: 1px solid #cccccc; + border-radius: 0; + box-shadow: none; +} + +.tox-tinymce-aux { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + z-index: 1300; +} + +.tox-tinymce *:focus, +.tox-tinymce-aux *:focus { + outline: none; +} + +button::-moz-focus-inner { + border: 0; +} + +.tox .accessibility-issue__header { + align-items: center; + display: flex; + margin-bottom: 4px; +} + +.tox .accessibility-issue__description { + align-items: stretch; + border: 1px solid #cccccc; + border-radius: 3px; + display: flex; + justify-content: space-between; +} + +.tox .accessibility-issue__description > div { + padding-bottom: 4px; +} + +.tox .accessibility-issue__description > div > div { + align-items: center; + display: flex; + margin-bottom: 4px; +} + +.tox .accessibility-issue__description > *:last-child:not(:only-child) { + border-color: #cccccc; + border-style: solid; +} + +.tox .accessibility-issue__repair { + margin-top: 16px; +} + +.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description { + background-color: rgba(32, 122, 183, 0.1); + border-color: rgba(32, 122, 183, 0.4); + color: #222f3e; +} + +.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description > *:last-child { + border-color: rgba(32, 122, 183, 0.4); +} + +.tox .tox-dialog__body-content .accessibility-issue--info .tox-form__group h2 { + color: #207ab7; +} + +.tox .tox-dialog__body-content .accessibility-issue--info .tox-icon svg { + fill: #207ab7; +} + +.tox .tox-dialog__body-content .accessibility-issue--info a .tox-icon { + color: #207ab7; +} + +.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description { + background-color: rgba(255, 165, 0, 0.1); + border-color: rgba(255, 165, 0, 0.5); + color: #222f3e; +} + +.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description > *:last-child { + border-color: rgba(255, 165, 0, 0.5); +} + +.tox .tox-dialog__body-content .accessibility-issue--warn .tox-form__group h2 { + color: #cc8500; +} + +.tox .tox-dialog__body-content .accessibility-issue--warn .tox-icon svg { + fill: #cc8500; +} + +.tox .tox-dialog__body-content .accessibility-issue--warn a .tox-icon { + color: #cc8500; +} + +.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description { + background-color: rgba(204, 0, 0, 0.1); + border-color: rgba(204, 0, 0, 0.4); + color: #222f3e; +} + +.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description > *:last-child { + border-color: rgba(204, 0, 0, 0.4); +} + +.tox .tox-dialog__body-content .accessibility-issue--error .tox-form__group h2 { + color: #c00; +} + +.tox .tox-dialog__body-content .accessibility-issue--error .tox-icon svg { + fill: #c00; +} + +.tox .tox-dialog__body-content .accessibility-issue--error a .tox-icon { + color: #c00; +} + +.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description { + background-color: rgba(120, 171, 70, 0.1); + border-color: rgba(120, 171, 70, 0.4); + color: #222f3e; +} + +.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description > *:last-child { + border-color: rgba(120, 171, 70, 0.4); +} + +.tox .tox-dialog__body-content .accessibility-issue--success .tox-form__group h2 { + color: #78AB46; +} + +.tox .tox-dialog__body-content .accessibility-issue--success .tox-icon svg { + fill: #78AB46; +} + +.tox .tox-dialog__body-content .accessibility-issue--success a .tox-icon { + color: #78AB46; +} + +.tox .tox-dialog__body-content .accessibility-issue__header h1, +.tox .tox-dialog__body-content .tox-form__group .accessibility-issue__description h2 { + margin-top: 0; +} + +.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header .tox-button { + margin-left: 4px; +} + +.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header > *:nth-last-child(2) { + margin-left: auto; +} + +.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description { + padding: 4px 4px 4px 8px; +} + +.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description > *:last-child { + border-left-width: 1px; + padding-left: 4px; +} + +.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header .tox-button { + margin-right: 4px; +} + +.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header > *:nth-last-child(2) { + margin-right: auto; +} + +.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description { + padding: 4px 8px 4px 4px; +} + +.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description > *:last-child { + border-right-width: 1px; + padding-right: 4px; +} + +.tox .tox-anchorbar { + display: flex; + flex: 0 0 auto; +} + +.tox .tox-bar { + display: flex; + flex: 0 0 auto; +} + +.tox .tox-button { + background-color: #207ab7; + background-image: none; + background-position: 0 0; + background-repeat: repeat; + border-color: #207ab7; + border-radius: 3px; + border-style: solid; + border-width: 1px; + box-shadow: none; + box-sizing: border-box; + color: #fff; + cursor: pointer; + display: inline-block; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-size: 14px; + font-style: normal; + font-weight: bold; + letter-spacing: normal; + line-height: 24px; + margin: 0; + outline: none; + padding: 4px 16px; + text-align: center; + text-decoration: none; + text-transform: none; + white-space: nowrap; +} + +.tox .tox-button[disabled] { + background-color: #207ab7; + background-image: none; + border-color: #207ab7; + box-shadow: none; + color: rgba(255, 255, 255, 0.5); + cursor: not-allowed; +} + +.tox .tox-button:focus:not(:disabled) { + background-color: #1c6ca1; + background-image: none; + border-color: #1c6ca1; + box-shadow: none; + color: #fff; +} + +.tox .tox-button:hover:not(:disabled) { + background-color: #1c6ca1; + background-image: none; + border-color: #1c6ca1; + box-shadow: none; + color: #fff; +} + +.tox .tox-button:active:not(:disabled) { + background-color: #185d8c; + background-image: none; + border-color: #185d8c; + box-shadow: none; + color: #fff; +} + +.tox .tox-button--secondary { + background-color: #f0f0f0; + background-image: none; + background-position: 0 0; + background-repeat: repeat; + border-color: #f0f0f0; + border-radius: 3px; + border-style: solid; + border-width: 1px; + box-shadow: none; + color: #222f3e; + font-size: 14px; + font-style: normal; + font-weight: bold; + letter-spacing: normal; + outline: none; + padding: 4px 16px; + text-decoration: none; + text-transform: none; +} + +.tox .tox-button--secondary[disabled] { + background-color: #f0f0f0; + background-image: none; + border-color: #f0f0f0; + box-shadow: none; + color: rgba(34, 47, 62, 0.5); +} + +.tox .tox-button--secondary:focus:not(:disabled) { + background-color: #e3e3e3; + background-image: none; + border-color: #e3e3e3; + box-shadow: none; + color: #222f3e; +} + +.tox .tox-button--secondary:hover:not(:disabled) { + background-color: #e3e3e3; + background-image: none; + border-color: #e3e3e3; + box-shadow: none; + color: #222f3e; +} + +.tox .tox-button--secondary:active:not(:disabled) { + background-color: #d6d6d6; + background-image: none; + border-color: #d6d6d6; + box-shadow: none; + color: #222f3e; +} + +.tox .tox-button--icon, +.tox .tox-button.tox-button--icon, +.tox .tox-button.tox-button--secondary.tox-button--icon { + padding: 4px; +} + +.tox .tox-button--icon .tox-icon svg, +.tox .tox-button.tox-button--icon .tox-icon svg, +.tox .tox-button.tox-button--secondary.tox-button--icon .tox-icon svg { + display: block; + fill: currentColor; +} + +.tox .tox-button-link { + background: 0; + border: none; + box-sizing: border-box; + cursor: pointer; + display: inline-block; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-size: 16px; + font-weight: normal; + line-height: 1.3; + margin: 0; + padding: 0; + white-space: nowrap; +} + +.tox .tox-button-link--sm { + font-size: 14px; +} + +.tox .tox-button--naked { + background-color: transparent; + border-color: transparent; + box-shadow: unset; + color: #222f3e; +} + +.tox .tox-button--naked[disabled] { + background-color: #f0f0f0; + border-color: #f0f0f0; + box-shadow: none; + color: rgba(34, 47, 62, 0.5); +} + +.tox .tox-button--naked:hover:not(:disabled) { + background-color: #e3e3e3; + border-color: #e3e3e3; + box-shadow: none; + color: #222f3e; +} + +.tox .tox-button--naked:focus:not(:disabled) { + background-color: #e3e3e3; + border-color: #e3e3e3; + box-shadow: none; + color: #222f3e; +} + +.tox .tox-button--naked:active:not(:disabled) { + background-color: #d6d6d6; + border-color: #d6d6d6; + box-shadow: none; + color: #222f3e; +} + +.tox .tox-button--naked .tox-icon svg { + fill: currentColor; +} + +.tox .tox-button--naked.tox-button--icon:hover:not(:disabled) { + color: #222f3e; +} + +.tox .tox-checkbox { + align-items: center; + border-radius: 3px; + cursor: pointer; + display: flex; + height: 36px; + min-width: 36px; +} + +.tox .tox-checkbox__input { + /* Hide from view but visible to screen readers */ + height: 1px; + overflow: hidden; + position: absolute; + top: auto; + width: 1px; +} + +.tox .tox-checkbox__icons { + align-items: center; + border-radius: 3px; + box-shadow: 0 0 0 2px transparent; + box-sizing: content-box; + display: flex; + height: 24px; + justify-content: center; + padding: calc(4px - 1px); + width: 24px; +} + +.tox .tox-checkbox__icons .tox-checkbox-icon__unchecked svg { + display: block; + fill: rgba(34, 47, 62, 0.3); +} + +.tox .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg { + display: none; + fill: #207ab7; +} + +.tox .tox-checkbox__icons .tox-checkbox-icon__checked svg { + display: none; + fill: #207ab7; +} + +.tox .tox-checkbox--disabled { + color: rgba(34, 47, 62, 0.5); + cursor: not-allowed; +} + +.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__checked svg { + fill: rgba(34, 47, 62, 0.5); +} + +.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__unchecked svg { + fill: rgba(34, 47, 62, 0.5); +} + +.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg { + fill: rgba(34, 47, 62, 0.5); +} + +.tox input.tox-checkbox__input:checked + .tox-checkbox__icons .tox-checkbox-icon__unchecked svg { + display: none; +} + +.tox input.tox-checkbox__input:checked + .tox-checkbox__icons .tox-checkbox-icon__checked svg { + display: block; +} + +.tox input.tox-checkbox__input:indeterminate + .tox-checkbox__icons .tox-checkbox-icon__unchecked svg { + display: none; +} + +.tox input.tox-checkbox__input:indeterminate + .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg { + display: block; +} + +.tox input.tox-checkbox__input:focus + .tox-checkbox__icons { + border-radius: 3px; + box-shadow: inset 0 0 0 1px #207ab7; + padding: calc(4px - 1px); +} + +.tox:not([dir=rtl]) .tox-checkbox__label { + margin-left: 4px; +} + +.tox:not([dir=rtl]) .tox-checkbox__input { + left: -10000px; +} + +.tox:not([dir=rtl]) .tox-bar .tox-checkbox { + margin-left: 4px; +} + +.tox[dir=rtl] .tox-checkbox__label { + margin-right: 4px; +} + +.tox[dir=rtl] .tox-checkbox__input { + right: -10000px; +} + +.tox[dir=rtl] .tox-bar .tox-checkbox { + margin-right: 4px; +} + +.tox { + /* stylelint-disable-next-line no-descending-specificity */ +} + +.tox .tox-collection--toolbar .tox-collection__group { + display: flex; + padding: 0; +} + +.tox .tox-collection--grid .tox-collection__group { + display: flex; + flex-wrap: wrap; + max-height: 208px; + overflow-x: hidden; + overflow-y: auto; + padding: 0; +} + +.tox .tox-collection--list .tox-collection__group { + border-bottom-width: 0; + border-color: #cccccc; + border-left-width: 0; + border-right-width: 0; + border-style: solid; + border-top-width: 1px; + padding: 4px 0; +} + +.tox .tox-collection--list .tox-collection__group:first-child { + border-top-width: 0; +} + +.tox .tox-collection__group-heading { + background-color: #e6e6e6; + color: rgba(34, 47, 62, 0.7); + cursor: default; + font-size: 12px; + font-style: normal; + font-weight: normal; + margin-bottom: 4px; + margin-top: -4px; + padding: 4px 8px; + text-transform: none; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.tox .tox-collection__item { + align-items: center; + color: #222f3e; + cursor: pointer; + display: flex; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.tox .tox-collection--list .tox-collection__item { + padding: 4px 8px; +} + +.tox .tox-collection--toolbar .tox-collection__item { + border-radius: 3px; + padding: 4px; +} + +.tox .tox-collection--grid .tox-collection__item { + border-radius: 3px; + padding: 4px; +} + +.tox .tox-collection--list .tox-collection__item--enabled { + background-color: #fff; + color: #222f3e; +} + +.tox .tox-collection--list .tox-collection__item--active { + background-color: #dee0e2; +} + +.tox .tox-collection--toolbar .tox-collection__item--enabled { + background-color: #c8cbcf; + color: #222f3e; +} + +.tox .tox-collection--toolbar .tox-collection__item--active { + background-color: #dee0e2; +} + +.tox .tox-collection--grid .tox-collection__item--enabled { + background-color: #c8cbcf; + color: #222f3e; +} + +.tox .tox-collection--grid .tox-collection__item--active:not(.tox-collection__item--state-disabled) { + background-color: #dee0e2; + color: #222f3e; +} + +.tox .tox-collection--list .tox-collection__item--active:not(.tox-collection__item--state-disabled) { + color: #222f3e; +} + +.tox .tox-collection--toolbar .tox-collection__item--active:not(.tox-collection__item--state-disabled) { + color: #222f3e; +} + +.tox .tox-collection__item--state-disabled { + background-color: transparent; + color: rgba(34, 47, 62, 0.5); + cursor: not-allowed; +} + +.tox .tox-collection__item-icon, +.tox .tox-collection__item-checkmark { + align-items: center; + display: flex; + height: 24px; + justify-content: center; + width: 24px; +} + +.tox .tox-collection__item-icon svg, +.tox .tox-collection__item-checkmark svg { + fill: currentColor; +} + +.tox .tox-collection--toolbar-lg .tox-collection__item-icon { + height: 48px; + width: 48px; +} + +.tox .tox-collection__item-label { + color: currentColor; + display: inline-block; + flex: 1; + -ms-flex-preferred-size: auto; + font-size: 14px; + font-style: normal; + font-weight: normal; + line-height: 24px; + text-transform: none; + word-break: break-all; +} + +.tox .tox-collection__item-accessory { + color: rgba(34, 47, 62, 0.7); + display: inline-block; + font-size: 14px; + height: 24px; + line-height: 24px; + text-transform: none; +} + +.tox .tox-collection__item-caret { + align-items: center; + display: flex; + min-height: 24px; +} + +.tox .tox-collection__item-caret::after { + content: ''; + font-size: 0; + min-height: inherit; +} + +.tox .tox-collection__item-caret svg { + fill: #222f3e; +} + +.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-checkmark svg { + display: none; +} + +.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-accessory + .tox-collection__item-checkmark { + display: none; +} + +.tox .tox-collection--horizontal { + background-color: #fff; + border: 1px solid #cccccc; + border-radius: 3px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15); + display: flex; + flex: 0 0 auto; + flex-shrink: 0; + flex-wrap: nowrap; + margin-bottom: 0; + overflow-x: auto; + padding: 0; +} + +.tox .tox-collection--horizontal .tox-collection__group { + align-items: center; + display: flex; + flex-wrap: nowrap; + margin: 0; + padding: 0 4px; +} + +.tox .tox-collection--horizontal .tox-collection__item { + height: 34px; + margin: 2px 0 3px 0; + padding: 0 4px; +} + +.tox .tox-collection--horizontal .tox-collection__item-label { + white-space: nowrap; +} + +.tox .tox-collection--horizontal .tox-collection__item-caret { + margin-left: 4px; +} + +.tox .tox-collection__item-container { + display: flex; +} + +.tox .tox-collection__item-container--row { + align-items: center; + flex: 1 1 auto; + flex-direction: row; +} + +.tox .tox-collection__item-container--row.tox-collection__item-container--align-left { + margin-right: auto; +} + +.tox .tox-collection__item-container--row.tox-collection__item-container--align-right { + justify-content: flex-end; + margin-left: auto; +} + +.tox .tox-collection__item-container--row.tox-collection__item-container--valign-top { + align-items: flex-start; + margin-bottom: auto; +} + +.tox .tox-collection__item-container--row.tox-collection__item-container--valign-middle { + align-items: center; +} + +.tox .tox-collection__item-container--row.tox-collection__item-container--valign-bottom { + align-items: flex-end; + margin-top: auto; +} + +.tox .tox-collection__item-container--column { + -ms-grid-row-align: center; + align-self: center; + flex: 1 1 auto; + flex-direction: column; +} + +.tox .tox-collection__item-container--column.tox-collection__item-container--align-left { + align-items: flex-start; +} + +.tox .tox-collection__item-container--column.tox-collection__item-container--align-right { + align-items: flex-end; +} + +.tox .tox-collection__item-container--column.tox-collection__item-container--valign-top { + align-self: flex-start; +} + +.tox .tox-collection__item-container--column.tox-collection__item-container--valign-middle { + -ms-grid-row-align: center; + align-self: center; +} + +.tox .tox-collection__item-container--column.tox-collection__item-container--valign-bottom { + align-self: flex-end; +} + +.tox:not([dir=rtl]) .tox-collection--horizontal .tox-collection__group:not(:last-of-type) { + border-right: 1px solid #cccccc; +} + +.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item > *:not(:first-child) { + margin-left: 8px; +} + +.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item > .tox-collection__item-label:first-child { + margin-left: 4px; +} + +.tox:not([dir=rtl]) .tox-collection__item-accessory { + margin-left: 16px; + text-align: right; +} + +.tox:not([dir=rtl]) .tox-collection .tox-collection__item-caret { + margin-left: 16px; +} + +.tox[dir=rtl] .tox-collection--horizontal .tox-collection__group:not(:last-of-type) { + border-left: 1px solid #cccccc; +} + +.tox[dir=rtl] .tox-collection--list .tox-collection__item > *:not(:first-child) { + margin-right: 8px; +} + +.tox[dir=rtl] .tox-collection--list .tox-collection__item > .tox-collection__item-label:first-child { + margin-right: 4px; +} + +.tox[dir=rtl] .tox-collection__item-icon-rtl { + /* stylelint-disable-next-line no-descending-specificity */ +} + +.tox[dir=rtl] .tox-collection__item-icon-rtl .tox-collection__item-icon svg { + transform: rotateY(180deg); +} + +.tox[dir=rtl] .tox-collection__item-accessory { + margin-right: 16px; + text-align: left; +} + +.tox[dir=rtl] .tox-collection .tox-collection__item-caret { + margin-right: 16px; + transform: rotateY(180deg); +} + +.tox[dir=rtl] .tox-collection--horizontal .tox-collection__item-caret { + margin-right: 4px; +} + +.tox .tox-color-picker-container { + display: flex; + flex-direction: row; + height: 225px; + margin: 0; +} + +.tox .tox-sv-palette { + box-sizing: border-box; + display: flex; + height: 100%; +} + +.tox .tox-sv-palette-spectrum { + height: 100%; +} + +.tox .tox-sv-palette, +.tox .tox-sv-palette-spectrum { + width: 225px; +} + +.tox .tox-sv-palette-thumb { + background: none; + border: 1px solid black; + border-radius: 50%; + box-sizing: content-box; + height: 12px; + position: absolute; + width: 12px; +} + +.tox .tox-sv-palette-inner-thumb { + border: 1px solid white; + border-radius: 50%; + height: 10px; + position: absolute; + width: 10px; +} + +.tox .tox-hue-slider { + box-sizing: border-box; + height: 100%; + width: 25px; +} + +.tox .tox-hue-slider-spectrum { + background: linear-gradient(to bottom, #f00, #ff0080, #f0f, #8000ff, #00f, #0080ff, #0ff, #00ff80, #0f0, #80ff00, #ff0, #ff8000, #f00); + height: 100%; + width: 100%; +} + +.tox .tox-hue-slider, +.tox .tox-hue-slider-spectrum { + width: 20px; +} + +.tox .tox-hue-slider-thumb { + background: white; + border: 1px solid black; + box-sizing: content-box; + height: 4px; + width: 100%; +} + +.tox .tox-rgb-form { + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.tox .tox-rgb-form div { + align-items: center; + display: flex; + justify-content: space-between; + margin-bottom: 5px; + width: inherit; +} + +.tox .tox-rgb-form input { + width: 6em; +} + +.tox .tox-rgb-form input.tox-invalid { + /* Need !important to override Chrome's focus styling unfortunately */ + border: 1px solid red !important; +} + +.tox .tox-rgb-form .tox-rgba-preview { + border: 1px solid black; + flex-grow: 2; + margin-bottom: 0; +} + +.tox:not([dir=rtl]) .tox-sv-palette { + margin-right: 15px; +} + +.tox:not([dir=rtl]) .tox-hue-slider { + margin-right: 15px; +} + +.tox:not([dir=rtl]) .tox-hue-slider-thumb { + margin-left: -1px; +} + +.tox:not([dir=rtl]) .tox-rgb-form label { + margin-right: 0.5em; +} + +.tox[dir=rtl] .tox-sv-palette { + margin-left: 15px; +} + +.tox[dir=rtl] .tox-hue-slider { + margin-left: 15px; +} + +.tox[dir=rtl] .tox-hue-slider-thumb { + margin-right: -1px; +} + +.tox[dir=rtl] .tox-rgb-form label { + margin-left: 0.5em; +} + +.tox .tox-toolbar .tox-swatches, +.tox .tox-toolbar__primary .tox-swatches, +.tox .tox-toolbar__overflow .tox-swatches { + margin: 2px 0 3px 4px; +} + +.tox .tox-collection--list .tox-collection__group .tox-swatches-menu { + border: 0; + margin: -4px 0; +} + +.tox .tox-swatches__row { + display: flex; +} + +.tox .tox-swatch { + height: 30px; + transition: transform 0.15s, box-shadow 0.15s; + width: 30px; +} + +.tox .tox-swatch:hover, +.tox .tox-swatch:focus { + box-shadow: 0 0 0 1px rgba(127, 127, 127, 0.3) inset; + transform: scale(0.8); +} + +.tox .tox-swatch--remove { + align-items: center; + display: flex; + justify-content: center; +} + +.tox .tox-swatch--remove svg path { + stroke: #e74c3c; +} + +.tox .tox-swatches__picker-btn { + align-items: center; + background-color: transparent; + border: 0; + cursor: pointer; + display: flex; + height: 30px; + justify-content: center; + outline: none; + padding: 0; + width: 30px; +} + +.tox .tox-swatches__picker-btn svg { + height: 24px; + width: 24px; +} + +.tox .tox-swatches__picker-btn:hover { + background: #dee0e2; +} + +.tox:not([dir=rtl]) .tox-swatches__picker-btn { + margin-left: auto; +} + +.tox[dir=rtl] .tox-swatches__picker-btn { + margin-right: auto; +} + +.tox .tox-comment-thread { + background: #fff; + position: relative; +} + +.tox .tox-comment-thread > *:not(:first-child) { + margin-top: 8px; +} + +.tox .tox-comment { + background: #fff; + border: 1px solid #cccccc; + border-radius: 3px; + box-shadow: 0 4px 8px 0 rgba(34, 47, 62, 0.1); + padding: 8px 8px 16px 8px; + position: relative; +} + +.tox .tox-comment__header { + align-items: center; + color: #222f3e; + display: flex; + justify-content: space-between; +} + +.tox .tox-comment__date { + color: rgba(34, 47, 62, 0.7); + font-size: 12px; +} + +.tox .tox-comment__body { + color: #222f3e; + font-size: 14px; + font-style: normal; + font-weight: normal; + line-height: 1.3; + margin-top: 8px; + position: relative; + text-transform: initial; +} + +.tox .tox-comment__body textarea { + resize: none; + white-space: normal; + width: 100%; +} + +.tox .tox-comment__expander { + padding-top: 8px; +} + +.tox .tox-comment__expander p { + color: rgba(34, 47, 62, 0.7); + font-size: 14px; + font-style: normal; +} + +.tox .tox-comment__body p { + margin: 0; +} + +.tox .tox-comment__buttonspacing { + padding-top: 16px; + text-align: center; +} + +.tox .tox-comment-thread__overlay::after { + background: #fff; + bottom: 0; + content: ""; + display: flex; + left: 0; + opacity: 0.9; + position: absolute; + right: 0; + top: 0; + z-index: 5; +} + +.tox .tox-comment__reply { + display: flex; + flex-shrink: 0; + flex-wrap: wrap; + justify-content: flex-end; + margin-top: 8px; +} + +.tox .tox-comment__reply > *:first-child { + margin-bottom: 8px; + width: 100%; +} + +.tox .tox-comment__edit { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + margin-top: 16px; +} + +.tox .tox-comment__gradient::after { + background: linear-gradient(rgba(255, 255, 255, 0), #fff); + bottom: 0; + content: ""; + display: block; + height: 5em; + margin-top: -40px; + position: absolute; + width: 100%; +} + +.tox .tox-comment__overlay { + background: #fff; + bottom: 0; + display: flex; + flex-direction: column; + flex-grow: 1; + left: 0; + opacity: 0.9; + position: absolute; + right: 0; + text-align: center; + top: 0; + z-index: 5; +} + +.tox .tox-comment__loading-text { + align-items: center; + color: #222f3e; + display: flex; + flex-direction: column; + position: relative; +} + +.tox .tox-comment__loading-text > div { + padding-bottom: 16px; +} + +.tox .tox-comment__overlaytext { + bottom: 0; + flex-direction: column; + font-size: 14px; + left: 0; + padding: 1em; + position: absolute; + right: 0; + top: 0; + z-index: 10; +} + +.tox .tox-comment__overlaytext p { + background-color: #fff; + box-shadow: 0 0 8px 8px #fff; + color: #222f3e; + text-align: center; +} + +.tox .tox-comment__overlaytext div:nth-of-type(2) { + font-size: 0.8em; +} + +.tox .tox-comment__busy-spinner { + align-items: center; + background-color: #fff; + bottom: 0; + display: flex; + justify-content: center; + left: 0; + position: absolute; + right: 0; + top: 0; + z-index: 20; +} + +.tox .tox-comment__scroll { + display: flex; + flex-direction: column; + flex-shrink: 1; + overflow: auto; +} + +.tox .tox-conversations { + margin: 8px; +} + +.tox:not([dir=rtl]) .tox-comment__edit { + margin-left: 8px; +} + +.tox:not([dir=rtl]) .tox-comment__buttonspacing > *:last-child, +.tox:not([dir=rtl]) .tox-comment__edit > *:last-child, +.tox:not([dir=rtl]) .tox-comment__reply > *:last-child { + margin-left: 8px; +} + +.tox[dir=rtl] .tox-comment__edit { + margin-right: 8px; +} + +.tox[dir=rtl] .tox-comment__buttonspacing > *:last-child, +.tox[dir=rtl] .tox-comment__edit > *:last-child, +.tox[dir=rtl] .tox-comment__reply > *:last-child { + margin-right: 8px; +} + +.tox .tox-user { + align-items: center; + display: flex; +} + +.tox .tox-user__avatar svg { + fill: rgba(34, 47, 62, 0.7); +} + +.tox .tox-user__name { + color: rgba(34, 47, 62, 0.7); + font-size: 12px; + font-style: normal; + font-weight: bold; + text-transform: uppercase; +} + +.tox:not([dir=rtl]) .tox-user__avatar svg { + margin-right: 8px; +} + +.tox:not([dir=rtl]) .tox-user__avatar + .tox-user__name { + margin-left: 8px; +} + +.tox[dir=rtl] .tox-user__avatar svg { + margin-left: 8px; +} + +.tox[dir=rtl] .tox-user__avatar + .tox-user__name { + margin-right: 8px; +} + +.tox .tox-dialog-wrap { + align-items: center; + bottom: 0; + display: flex; + justify-content: center; + left: 0; + position: fixed; + right: 0; + top: 0; + z-index: 1100; +} + +.tox .tox-dialog-wrap__backdrop { + background-color: rgba(255, 255, 255, 0.75); + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; + z-index: 1; +} + +.tox .tox-dialog-wrap__backdrop--opaque { + background-color: #fff; +} + +.tox .tox-dialog { + background-color: #fff; + border-color: #cccccc; + border-radius: 3px; + border-style: solid; + border-width: 1px; + box-shadow: 0 16px 16px -10px rgba(34, 47, 62, 0.15), 0 0 40px 1px rgba(34, 47, 62, 0.15); + display: flex; + flex-direction: column; + max-height: 100%; + max-width: 480px; + overflow: hidden; + position: relative; + width: 95vw; + z-index: 2; +} + +@media only screen and (max-width: 767px) { + body:not(.tox-force-desktop) .tox .tox-dialog { + align-self: flex-start; + margin: 8px auto; + width: calc(100vw - 16px); + } +} + +.tox .tox-dialog-inline { + z-index: 1100; +} + +.tox .tox-dialog__header { + align-items: center; + background-color: #fff; + border-bottom: none; + color: #222f3e; + display: flex; + font-size: 16px; + justify-content: space-between; + padding: 8px 16px 0 16px; + position: relative; +} + +.tox .tox-dialog__header .tox-button { + z-index: 1; +} + +.tox .tox-dialog__draghandle { + cursor: grab; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} + +.tox .tox-dialog__draghandle:active { + cursor: grabbing; +} + +.tox .tox-dialog__dismiss { + margin-left: auto; +} + +.tox .tox-dialog__title { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-size: 20px; + font-style: normal; + font-weight: normal; + line-height: 1.3; + margin: 0; + text-transform: none; +} + +.tox .tox-dialog__body { + color: #222f3e; + display: flex; + flex: 1; + -ms-flex-preferred-size: auto; + font-size: 16px; + font-style: normal; + font-weight: normal; + line-height: 1.3; + min-width: 0; + text-align: left; + text-transform: none; +} + +@media only screen and (max-width: 767px) { + body:not(.tox-force-desktop) .tox .tox-dialog__body { + flex-direction: column; + } +} + +.tox .tox-dialog__body-nav { + align-items: flex-start; + display: flex; + flex-direction: column; + padding: 16px 16px; +} + +@media only screen and (max-width: 767px) { + body:not(.tox-force-desktop) .tox .tox-dialog__body-nav { + flex-direction: row; + -webkit-overflow-scrolling: touch; + overflow-x: auto; + padding-bottom: 0; + } +} + +.tox .tox-dialog__body-nav-item { + border-bottom: 2px solid transparent; + color: rgba(34, 47, 62, 0.7); + display: inline-block; + font-size: 14px; + line-height: 1.3; + margin-bottom: 8px; + text-decoration: none; + white-space: nowrap; +} + +.tox .tox-dialog__body-nav-item:focus { + background-color: rgba(32, 122, 183, 0.1); +} + +.tox .tox-dialog__body-nav-item--active { + border-bottom: 2px solid #207ab7; + color: #207ab7; +} + +.tox .tox-dialog__body-content { + box-sizing: border-box; + display: flex; + flex: 1; + flex-direction: column; + -ms-flex-preferred-size: auto; + max-height: 650px; + overflow: auto; + -webkit-overflow-scrolling: touch; + padding: 16px 16px; +} + +.tox .tox-dialog__body-content > * { + margin-bottom: 0; + margin-top: 16px; +} + +.tox .tox-dialog__body-content > *:first-child { + margin-top: 0; +} + +.tox .tox-dialog__body-content > *:last-child { + margin-bottom: 0; +} + +.tox .tox-dialog__body-content > *:only-child { + margin-bottom: 0; + margin-top: 0; +} + +.tox .tox-dialog__body-content a { + color: #207ab7; + cursor: pointer; + text-decoration: none; +} + +.tox .tox-dialog__body-content a:hover, +.tox .tox-dialog__body-content a:focus { + color: #185d8c; + text-decoration: none; +} + +.tox .tox-dialog__body-content a:active { + color: #185d8c; + text-decoration: none; +} + +.tox .tox-dialog__body-content svg { + fill: #222f3e; +} + +.tox .tox-dialog__body-content ul { + display: block; + list-style-type: disc; + margin-bottom: 16px; + -webkit-margin-end: 0; + margin-inline-end: 0; + -webkit-margin-start: 0; + margin-inline-start: 0; + -webkit-padding-start: 2.5rem; + padding-inline-start: 2.5rem; +} + +.tox .tox-dialog__body-content .tox-form__group h1 { + color: #222f3e; + font-size: 20px; + font-style: normal; + font-weight: bold; + letter-spacing: normal; + margin-bottom: 16px; + margin-top: 2rem; + text-transform: none; +} + +.tox .tox-dialog__body-content .tox-form__group h2 { + color: #222f3e; + font-size: 16px; + font-style: normal; + font-weight: bold; + letter-spacing: normal; + margin-bottom: 16px; + margin-top: 2rem; + text-transform: none; +} + +.tox .tox-dialog__body-content .tox-form__group p { + margin-bottom: 16px; +} + +.tox .tox-dialog__body-content .tox-form__group h1:first-child, +.tox .tox-dialog__body-content .tox-form__group h2:first-child, +.tox .tox-dialog__body-content .tox-form__group p:first-child { + margin-top: 0; +} + +.tox .tox-dialog__body-content .tox-form__group h1:last-child, +.tox .tox-dialog__body-content .tox-form__group h2:last-child, +.tox .tox-dialog__body-content .tox-form__group p:last-child { + margin-bottom: 0; +} + +.tox .tox-dialog__body-content .tox-form__group h1:only-child, +.tox .tox-dialog__body-content .tox-form__group h2:only-child, +.tox .tox-dialog__body-content .tox-form__group p:only-child { + margin-bottom: 0; + margin-top: 0; +} + +.tox .tox-dialog--width-lg { + height: 650px; + max-width: 1200px; +} + +.tox .tox-dialog--width-md { + max-width: 800px; +} + +.tox .tox-dialog--width-md .tox-dialog__body-content { + overflow: auto; +} + +.tox .tox-dialog__body-content--centered { + text-align: center; +} + +.tox .tox-dialog__footer { + align-items: center; + background-color: #fff; + border-top: 1px solid #cccccc; + display: flex; + justify-content: space-between; + padding: 8px 16px; +} + +.tox .tox-dialog__footer-start, +.tox .tox-dialog__footer-end { + display: flex; +} + +.tox .tox-dialog__busy-spinner { + align-items: center; + background-color: rgba(255, 255, 255, 0.75); + bottom: 0; + display: flex; + justify-content: center; + left: 0; + position: absolute; + right: 0; + top: 0; + z-index: 3; +} + +.tox .tox-dialog__table { + border-collapse: collapse; + width: 100%; +} + +.tox .tox-dialog__table thead th { + font-weight: bold; + padding-bottom: 8px; +} + +.tox .tox-dialog__table tbody tr { + border-bottom: 1px solid #cccccc; +} + +.tox .tox-dialog__table tbody tr:last-child { + border-bottom: none; +} + +.tox .tox-dialog__table td { + padding-bottom: 8px; + padding-top: 8px; +} + +.tox .tox-dialog__popups { + position: absolute; + width: 100%; + z-index: 1100; +} + +.tox .tox-dialog__body-iframe { + display: flex; + flex: 1; + flex-direction: column; + -ms-flex-preferred-size: auto; +} + +.tox .tox-dialog__body-iframe .tox-navobj { + display: flex; + flex: 1; + -ms-flex-preferred-size: auto; +} + +.tox .tox-dialog__body-iframe .tox-navobj :nth-child(2) { + flex: 1; + -ms-flex-preferred-size: auto; + height: 100%; +} + +.tox .tox-dialog-dock-fadeout { + opacity: 0; + visibility: hidden; +} + +.tox .tox-dialog-dock-fadein { + opacity: 1; + visibility: visible; +} + +.tox .tox-dialog-dock-transition { + transition: visibility 0s linear 0.3s, opacity 0.3s ease; +} + +.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein { + transition-delay: 0s; +} + +.tox.tox-platform-ie { + /* IE11 CSS styles go here */ +} + +.tox.tox-platform-ie .tox-dialog-wrap { + position: -ms-device-fixed; +} + +@media only screen and (max-width: 767px) { + body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav { + margin-right: 0; + } +} + +@media only screen and (max-width: 767px) { + body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child) { + margin-left: 8px; + } +} + +.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-start > *, +.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-end > * { + margin-left: 8px; +} + +.tox[dir=rtl] .tox-dialog__body { + text-align: right; +} + +@media only screen and (max-width: 767px) { + body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav { + margin-left: 0; + } +} + +@media only screen and (max-width: 767px) { + body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child) { + margin-right: 8px; + } +} + +.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-start > *, +.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-end > * { + margin-right: 8px; +} + +body.tox-dialog__disable-scroll { + overflow: hidden; +} + +.tox .tox-dropzone-container { + display: flex; + flex: 1; + -ms-flex-preferred-size: auto; +} + +.tox .tox-dropzone { + align-items: center; + background: #fff; + border: 2px dashed #cccccc; + box-sizing: border-box; + display: flex; + flex-direction: column; + flex-grow: 1; + justify-content: center; + min-height: 100px; + padding: 10px; +} + +.tox .tox-dropzone p { + color: rgba(34, 47, 62, 0.7); + margin: 0 0 16px 0; +} + +.tox .tox-edit-area { + display: flex; + flex: 1; + -ms-flex-preferred-size: auto; + overflow: hidden; + position: relative; +} + +.tox .tox-edit-area__iframe { + background-color: #fff; + border: 0; + box-sizing: border-box; + flex: 1; + -ms-flex-preferred-size: auto; + height: 100%; + position: absolute; + width: 100%; +} + +.tox.tox-inline-edit-area { + border: 1px dotted #cccccc; +} + +.tox .tox-editor-container { + display: flex; + flex: 1 1 auto; + flex-direction: column; + overflow: hidden; +} + +.tox .tox-editor-header { + z-index: 1000; +} + +.tox:not(.tox-tinymce-inline) .tox-editor-header { + box-shadow: none; + transition: box-shadow 0.5s; +} + +.tox.tox-tinymce--toolbar-bottom .tox-editor-header, +.tox.tox-tinymce-inline .tox-editor-header { + margin-bottom: -1px; +} + +.tox.tox-tinymce--toolbar-sticky-on .tox-editor-header { + background-color: transparent; + box-shadow: 0 4px 4px -3px rgba(0, 0, 0, 0.25); +} + +.tox-editor-dock-fadeout { + opacity: 0; + visibility: hidden; +} + +.tox-editor-dock-fadein { + opacity: 1; + visibility: visible; +} + +.tox-editor-dock-transition { + transition: visibility 0s linear 0.25s, opacity 0.25s ease; +} + +.tox-editor-dock-transition.tox-editor-dock-fadein { + transition-delay: 0s; +} + +.tox .tox-control-wrap { + flex: 1; + position: relative; +} + +.tox .tox-control-wrap:not(.tox-control-wrap--status-invalid) .tox-control-wrap__status-icon-invalid, +.tox .tox-control-wrap:not(.tox-control-wrap--status-unknown) .tox-control-wrap__status-icon-unknown, +.tox .tox-control-wrap:not(.tox-control-wrap--status-valid) .tox-control-wrap__status-icon-valid { + display: none; +} + +.tox .tox-control-wrap svg { + display: block; +} + +.tox .tox-control-wrap__status-icon-wrap { + position: absolute; + top: 50%; + transform: translateY(-50%); +} + +.tox .tox-control-wrap__status-icon-invalid svg { + fill: #c00; +} + +.tox .tox-control-wrap__status-icon-unknown svg { + fill: orange; +} + +.tox .tox-control-wrap__status-icon-valid svg { + fill: green; +} + +.tox:not([dir=rtl]) .tox-control-wrap--status-invalid .tox-textfield, +.tox:not([dir=rtl]) .tox-control-wrap--status-unknown .tox-textfield, +.tox:not([dir=rtl]) .tox-control-wrap--status-valid .tox-textfield { + padding-right: 32px; +} + +.tox:not([dir=rtl]) .tox-control-wrap__status-icon-wrap { + right: 4px; +} + +.tox[dir=rtl] .tox-control-wrap--status-invalid .tox-textfield, +.tox[dir=rtl] .tox-control-wrap--status-unknown .tox-textfield, +.tox[dir=rtl] .tox-control-wrap--status-valid .tox-textfield { + padding-left: 32px; +} + +.tox[dir=rtl] .tox-control-wrap__status-icon-wrap { + left: 4px; +} + +.tox .tox-autocompleter { + max-width: 25em; +} + +.tox .tox-autocompleter .tox-menu { + max-width: 25em; +} + +.tox .tox-autocompleter .tox-autocompleter-highlight { + font-weight: bold; +} + +.tox .tox-color-input { + display: flex; + position: relative; + z-index: 1; +} + +.tox .tox-color-input .tox-textfield { + z-index: -1; +} + +.tox .tox-color-input span { + border-color: rgba(34, 47, 62, 0.2); + border-radius: 3px; + border-style: solid; + border-width: 1px; + box-shadow: none; + box-sizing: border-box; + height: 24px; + position: absolute; + top: 6px; + width: 24px; +} + +.tox .tox-color-input span:hover:not([aria-disabled=true]), +.tox .tox-color-input span:focus:not([aria-disabled=true]) { + border-color: #207ab7; + cursor: pointer; +} + +.tox .tox-color-input span::before { + background-image: linear-gradient(45deg, rgba(0, 0, 0, 0.25) 25%, transparent 25%), linear-gradient(-45deg, rgba(0, 0, 0, 0.25) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, rgba(0, 0, 0, 0.25) 75%), linear-gradient(-45deg, transparent 75%, rgba(0, 0, 0, 0.25) 75%); + background-position: 0 0, 0 6px, 6px -6px, -6px 0; + background-size: 12px 12px; + border: 1px solid #fff; + border-radius: 3px; + box-sizing: border-box; + content: ''; + height: 24px; + left: -1px; + position: absolute; + top: -1px; + width: 24px; + z-index: -1; +} + +.tox .tox-color-input span[aria-disabled=true] { + cursor: not-allowed; +} + +.tox:not([dir=rtl]) .tox-color-input { + /* stylelint-disable-next-line no-descending-specificity */ +} + +.tox:not([dir=rtl]) .tox-color-input .tox-textfield { + padding-left: 36px; +} + +.tox:not([dir=rtl]) .tox-color-input span { + left: 6px; +} + +.tox[dir="rtl"] .tox-color-input { + /* stylelint-disable-next-line no-descending-specificity */ +} + +.tox[dir="rtl"] .tox-color-input .tox-textfield { + padding-right: 36px; +} + +.tox[dir="rtl"] .tox-color-input span { + right: 6px; +} + +.tox .tox-label, +.tox .tox-toolbar-label { + color: rgba(34, 47, 62, 0.7); + display: block; + font-size: 14px; + font-style: normal; + font-weight: normal; + line-height: 1.3; + padding: 0 8px 0 0; + text-transform: none; + white-space: nowrap; +} + +.tox .tox-toolbar-label { + padding: 0 8px; +} + +.tox[dir=rtl] .tox-label { + padding: 0 0 0 8px; +} + +.tox .tox-form { + display: flex; + flex: 1; + flex-direction: column; + -ms-flex-preferred-size: auto; +} + +.tox .tox-form__group { + box-sizing: border-box; + margin-bottom: 4px; +} + +.tox .tox-form-group--maximize { + flex: 1; +} + +.tox .tox-form__group--error { + color: #c00; +} + +.tox .tox-form__group--collection { + display: flex; +} + +.tox .tox-form__grid { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; +} + +.tox .tox-form__grid--2col > .tox-form__group { + width: calc(50% - (8px / 2)); +} + +.tox .tox-form__grid--3col > .tox-form__group { + width: calc(100% / 3 - (8px / 2)); +} + +.tox .tox-form__grid--4col > .tox-form__group { + width: calc(25% - (8px / 2)); +} + +.tox .tox-form__controls-h-stack { + align-items: center; + display: flex; +} + +.tox .tox-form__group--inline { + align-items: center; + display: flex; +} + +.tox .tox-form__group--stretched { + display: flex; + flex: 1; + flex-direction: column; + -ms-flex-preferred-size: auto; +} + +.tox .tox-form__group--stretched .tox-textarea { + flex: 1; + -ms-flex-preferred-size: auto; +} + +.tox .tox-form__group--stretched .tox-navobj { + display: flex; + flex: 1; + -ms-flex-preferred-size: auto; +} + +.tox .tox-form__group--stretched .tox-navobj :nth-child(2) { + flex: 1; + -ms-flex-preferred-size: auto; + height: 100%; +} + +.tox:not([dir=rtl]) .tox-form__controls-h-stack > *:not(:first-child) { + margin-left: 4px; +} + +.tox[dir=rtl] .tox-form__controls-h-stack > *:not(:first-child) { + margin-right: 4px; +} + +.tox .tox-lock.tox-locked .tox-lock-icon__unlock, +.tox .tox-lock:not(.tox-locked) .tox-lock-icon__lock { + display: none; +} + +.tox .tox-textfield, +.tox .tox-toolbar-textfield, +.tox .tox-listboxfield .tox-listbox--select, +.tox .tox-textarea { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + border-color: #cccccc; + border-radius: 3px; + border-style: solid; + border-width: 1px; + box-shadow: none; + box-sizing: border-box; + color: #222f3e; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-size: 16px; + line-height: 24px; + margin: 0; + min-height: 34px; + outline: none; + padding: 5px 4.75px; + resize: none; + width: 100%; +} + +.tox .tox-textfield[disabled], +.tox .tox-textarea[disabled] { + background-color: #f2f2f2; + color: rgba(34, 47, 62, 0.85); + cursor: not-allowed; +} + +.tox .tox-textfield:focus, +.tox .tox-listboxfield .tox-listbox--select:focus, +.tox .tox-textarea:focus { + background-color: #fff; + border-color: #207ab7; + box-shadow: none; + outline: none; +} + +.tox .tox-toolbar-textfield { + border-width: 0; + margin-bottom: 3px; + margin-top: 2px; + max-width: 250px; +} + +.tox .tox-naked-btn { + background-color: transparent; + border: 0; + border-color: transparent; + box-shadow: unset; + color: #207ab7; + cursor: pointer; + display: block; + margin: 0; + padding: 0; +} + +.tox .tox-naked-btn svg { + display: block; + fill: #222f3e; +} + +.tox:not([dir=rtl]) .tox-toolbar-textfield + * { + margin-left: 4px; +} + +.tox[dir=rtl] .tox-toolbar-textfield + * { + margin-right: 4px; +} + +.tox .tox-listboxfield { + cursor: pointer; + position: relative; +} + +.tox .tox-listboxfield .tox-listbox--select[disabled] { + background-color: #f2f2f2; + color: rgba(34, 47, 62, 0.85); + cursor: not-allowed; +} + +.tox .tox-listbox__select-label { + cursor: default; + flex: 1; + margin: 0 4px; +} + +.tox .tox-listbox__select-chevron { + align-items: center; + display: flex; + justify-content: center; + width: 16px; +} + +.tox .tox-listbox__select-chevron svg { + fill: #222f3e; +} + +.tox .tox-listboxfield .tox-listbox--select { + align-items: center; + display: flex; +} + +.tox:not([dir=rtl]) .tox-listboxfield svg { + right: 8px; +} + +.tox[dir=rtl] .tox-listboxfield svg { + left: 8px; +} + +.tox .tox-selectfield { + cursor: pointer; + position: relative; +} + +.tox .tox-selectfield select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + border-color: #cccccc; + border-radius: 3px; + border-style: solid; + border-width: 1px; + box-shadow: none; + box-sizing: border-box; + color: #222f3e; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-size: 16px; + line-height: 24px; + margin: 0; + min-height: 34px; + outline: none; + padding: 5px 4.75px; + resize: none; + width: 100%; +} + +.tox .tox-selectfield select[disabled] { + background-color: #f2f2f2; + color: rgba(34, 47, 62, 0.85); + cursor: not-allowed; +} + +.tox .tox-selectfield select::-ms-expand { + display: none; +} + +.tox .tox-selectfield select:focus { + background-color: #fff; + border-color: #207ab7; + box-shadow: none; + outline: none; +} + +.tox .tox-selectfield svg { + pointer-events: none; + position: absolute; + top: 50%; + transform: translateY(-50%); +} + +.tox:not([dir=rtl]) .tox-selectfield select[size="0"], +.tox:not([dir=rtl]) .tox-selectfield select[size="1"] { + padding-right: 24px; +} + +.tox:not([dir=rtl]) .tox-selectfield svg { + right: 8px; +} + +.tox[dir=rtl] .tox-selectfield select[size="0"], +.tox[dir=rtl] .tox-selectfield select[size="1"] { + padding-left: 24px; +} + +.tox[dir=rtl] .tox-selectfield svg { + left: 8px; +} + +.tox .tox-textarea { + -webkit-appearance: textarea; + -moz-appearance: textarea; + appearance: textarea; + white-space: pre-wrap; +} + +.tox-fullscreen { + border: 0; + height: 100%; + left: 0; + margin: 0; + overflow: hidden; + -ms-scroll-chaining: none; + overscroll-behavior: none; + padding: 0; + position: fixed; + top: 0; + touch-action: pinch-zoom; + width: 100%; +} + +.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle { + display: none; +} + +.tox.tox-tinymce.tox-fullscreen { + background-color: transparent; + z-index: 1200; +} + +.tox-shadowhost.tox-fullscreen { + z-index: 1200; +} + +.tox-fullscreen .tox.tox-tinymce-aux, +.tox-fullscreen ~ .tox.tox-tinymce-aux { + z-index: 1201; +} + +.tox .tox-help__more-link { + list-style: none; + margin-top: 1em; +} + +.tox .tox-image-tools { + width: 100%; +} + +.tox .tox-image-tools__toolbar { + align-items: center; + display: flex; + justify-content: center; +} + +.tox .tox-image-tools__image { + background-color: #666; + height: 380px; + overflow: auto; + position: relative; + width: 100%; +} + +.tox .tox-image-tools__image, +.tox .tox-image-tools__image + .tox-image-tools__toolbar { + margin-top: 8px; +} + +.tox .tox-image-tools__image-bg { + background: url(data:image/gif;base64,R0lGODdhDAAMAIABAMzMzP///ywAAAAADAAMAAACFoQfqYeabNyDMkBQb81Uat85nxguUAEAOw==); +} + +.tox .tox-image-tools__toolbar > .tox-spacer { + flex: 1; + -ms-flex-preferred-size: auto; +} + +.tox .tox-croprect-block { + background: black; + filter: alpha(opacity=50); + opacity: 0.5; + position: absolute; + zoom: 1; +} + +.tox .tox-croprect-handle { + border: 2px solid white; + height: 20px; + left: 0; + position: absolute; + top: 0; + width: 20px; +} + +.tox .tox-croprect-handle-move { + border: 0; + cursor: move; + position: absolute; +} + +.tox .tox-croprect-handle-nw { + border-width: 2px 0 0 2px; + cursor: nw-resize; + left: 100px; + margin: -2px 0 0 -2px; + top: 100px; +} + +.tox .tox-croprect-handle-ne { + border-width: 2px 2px 0 0; + cursor: ne-resize; + left: 200px; + margin: -2px 0 0 -20px; + top: 100px; +} + +.tox .tox-croprect-handle-sw { + border-width: 0 0 2px 2px; + cursor: sw-resize; + left: 100px; + margin: -20px 2px 0 -2px; + top: 200px; +} + +.tox .tox-croprect-handle-se { + border-width: 0 2px 2px 0; + cursor: se-resize; + left: 200px; + margin: -20px 0 0 -20px; + top: 200px; +} + +.tox:not([dir=rtl]) .tox-image-tools__toolbar > .tox-slider:not(:first-of-type) { + margin-left: 8px; +} + +.tox:not([dir=rtl]) .tox-image-tools__toolbar > .tox-button + .tox-slider { + margin-left: 32px; +} + +.tox:not([dir=rtl]) .tox-image-tools__toolbar > .tox-slider + .tox-button { + margin-left: 32px; +} + +.tox[dir=rtl] .tox-image-tools__toolbar > .tox-slider:not(:first-of-type) { + margin-right: 8px; +} + +.tox[dir=rtl] .tox-image-tools__toolbar > .tox-button + .tox-slider { + margin-right: 32px; +} + +.tox[dir=rtl] .tox-image-tools__toolbar > .tox-slider + .tox-button { + margin-right: 32px; +} + +.tox .tox-insert-table-picker { + display: flex; + flex-wrap: wrap; + width: 170px; +} + +.tox .tox-insert-table-picker > div { + border-color: #cccccc; + border-style: solid; + border-width: 0 1px 1px 0; + box-sizing: border-box; + height: 17px; + width: 17px; +} + +.tox .tox-collection--list .tox-collection__group .tox-insert-table-picker { + margin: -4px 0; +} + +.tox .tox-insert-table-picker .tox-insert-table-picker__selected { + background-color: rgba(32, 122, 183, 0.5); + border-color: rgba(32, 122, 183, 0.5); +} + +.tox .tox-insert-table-picker__label { + color: rgba(34, 47, 62, 0.7); + display: block; + font-size: 14px; + padding: 4px; + text-align: center; + width: 100%; +} + +.tox:not([dir=rtl]) { + /* stylelint-disable-next-line no-descending-specificity */ +} + +.tox:not([dir=rtl]) .tox-insert-table-picker > div:nth-child(10n) { + border-right: 0; +} + +.tox[dir=rtl] { + /* stylelint-disable-next-line no-descending-specificity */ +} + +.tox[dir=rtl] .tox-insert-table-picker > div:nth-child(10n+1) { + border-right: 0; +} + +.tox { + /* stylelint-disable */ + /* stylelint-enable */ +} + +.tox .tox-menu { + background-color: #fff; + border: 1px solid #cccccc; + border-radius: 3px; + box-shadow: 0 4px 8px 0 rgba(34, 47, 62, 0.1); + display: inline-block; + overflow: hidden; + vertical-align: top; + z-index: 1150; +} + +.tox .tox-menu.tox-collection.tox-collection--list { + padding: 0; +} + +.tox .tox-menu.tox-collection.tox-collection--toolbar { + padding: 4px; +} + +.tox .tox-menu.tox-collection.tox-collection--grid { + padding: 4px; +} + +.tox .tox-menu__label h1, +.tox .tox-menu__label h2, +.tox .tox-menu__label h3, +.tox .tox-menu__label h4, +.tox .tox-menu__label h5, +.tox .tox-menu__label h6, +.tox .tox-menu__label p, +.tox .tox-menu__label blockquote, +.tox .tox-menu__label code { + margin: 0; +} + +.tox .tox-menubar { + background: url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23cccccc'/%3E%3C/svg%3E") left 0 top 0 #fff; + background-color: #fff; + display: flex; + flex: 0 0 auto; + flex-shrink: 0; + flex-wrap: wrap; + padding: 0 4px 0 4px; +} + +.tox.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar { + border-top: 1px solid #cccccc; +} + +/* Deprecated. Remove in next major release */ +.tox .tox-mbtn { + align-items: center; + background: transparent; + border: 0; + border-radius: 3px; + box-shadow: none; + color: #222f3e; + display: flex; + flex: 0 0 auto; + font-size: 14px; + font-style: normal; + font-weight: normal; + height: 34px; + justify-content: center; + margin: 2px 0 3px 0; + outline: none; + overflow: hidden; + padding: 0 4px; + text-transform: none; + width: auto; +} + +.tox .tox-mbtn[disabled] { + background-color: transparent; + border: 0; + box-shadow: none; + color: rgba(34, 47, 62, 0.5); + cursor: not-allowed; +} + +.tox .tox-mbtn:focus:not(:disabled) { + background: #dee0e2; + border: 0; + box-shadow: none; + color: #222f3e; +} + +.tox .tox-mbtn--active { + background: #c8cbcf; + border: 0; + box-shadow: none; + color: #222f3e; +} + +.tox .tox-mbtn:hover:not(:disabled):not(.tox-mbtn--active) { + background: #dee0e2; + border: 0; + box-shadow: none; + color: #222f3e; +} + +.tox .tox-mbtn__select-label { + cursor: default; + font-weight: normal; + margin: 0 4px; +} + +.tox .tox-mbtn[disabled] .tox-mbtn__select-label { + cursor: not-allowed; +} + +.tox .tox-mbtn__select-chevron { + align-items: center; + display: flex; + justify-content: center; + width: 16px; + display: none; +} + +.tox .tox-notification { + border-radius: 3px; + border-style: solid; + border-width: 1px; + box-shadow: none; + box-sizing: border-box; + display: -ms-grid; + display: grid; + font-size: 14px; + font-weight: normal; + -ms-grid-columns: minmax(40px, 1fr) auto minmax(40px, 1fr); + grid-template-columns: minmax(40px, 1fr) auto minmax(40px, 1fr); + margin-top: 4px; + opacity: 0; + padding: 4px; + transition: transform 100ms ease-in, opacity 150ms ease-in; +} + +.tox .tox-notification p { + font-size: 14px; + font-weight: normal; +} + +.tox .tox-notification a { + text-decoration: underline; +} + +.tox .tox-notification--in { + opacity: 1; +} + +.tox .tox-notification--success { + background-color: #e4eeda; + border-color: #d7e6c8; + color: #222f3e; +} + +.tox .tox-notification--success p { + color: #222f3e; +} + +.tox .tox-notification--success a { + color: #547831; +} + +.tox .tox-notification--success svg { + fill: #222f3e; +} + +.tox .tox-notification--error { + background-color: #f8dede; + border-color: #f2bfbf; + color: #222f3e; +} + +.tox .tox-notification--error p { + color: #222f3e; +} + +.tox .tox-notification--error a { + color: #c00; +} + +.tox .tox-notification--error svg { + fill: #222f3e; +} + +.tox .tox-notification--warn, +.tox .tox-notification--warning { + background-color: #fffaea; + border-color: #ffe89d; + color: #222f3e; +} + +.tox .tox-notification--warn p, +.tox .tox-notification--warning p { + color: #222f3e; +} + +.tox .tox-notification--warn a, +.tox .tox-notification--warning a { + color: #222f3e; +} + +.tox .tox-notification--warn svg, +.tox .tox-notification--warning svg { + fill: #222f3e; +} + +.tox .tox-notification--info { + background-color: #d9edf7; + border-color: #779ecb; + color: #222f3e; +} + +.tox .tox-notification--info p { + color: #222f3e; +} + +.tox .tox-notification--info a { + color: #222f3e; +} + +.tox .tox-notification--info svg { + fill: #222f3e; +} + +.tox .tox-notification__body { + -ms-grid-row-align: center; + align-self: center; + color: #222f3e; + font-size: 14px; + -ms-grid-column-span: 1; + grid-column-end: 3; + -ms-grid-column: 2; + grid-column-start: 2; + -ms-grid-row-span: 1; + grid-row-end: 2; + -ms-grid-row: 1; + grid-row-start: 1; + text-align: center; + white-space: normal; + word-break: break-all; + word-break: break-word; +} + +.tox .tox-notification__body > * { + margin: 0; +} + +.tox .tox-notification__body > * + * { + margin-top: 1rem; +} + +.tox .tox-notification__icon { + -ms-grid-row-align: center; + align-self: center; + -ms-grid-column-span: 1; + grid-column-end: 2; + -ms-grid-column: 1; + grid-column-start: 1; + -ms-grid-row-span: 1; + grid-row-end: 2; + -ms-grid-row: 1; + grid-row-start: 1; + -ms-grid-column-align: end; + justify-self: end; +} + +.tox .tox-notification__icon svg { + display: block; +} + +.tox .tox-notification__dismiss { + -ms-grid-row-align: start; + align-self: start; + -ms-grid-column-span: 1; + grid-column-end: 4; + -ms-grid-column: 3; + grid-column-start: 3; + -ms-grid-row-span: 1; + grid-row-end: 2; + -ms-grid-row: 1; + grid-row-start: 1; + -ms-grid-column-align: end; + justify-self: end; +} + +.tox .tox-notification .tox-progress-bar { + -ms-grid-column-span: 3; + grid-column-end: 4; + -ms-grid-column: 1; + grid-column-start: 1; + -ms-grid-row-span: 1; + grid-row-end: 3; + -ms-grid-row: 2; + grid-row-start: 2; + -ms-grid-column-align: center; + justify-self: center; +} + +.tox .tox-pop { + display: inline-block; + position: relative; +} + +.tox .tox-pop--resizing { + transition: width 0.1s ease; +} + +.tox .tox-pop--resizing .tox-toolbar { + flex-wrap: nowrap; +} + +.tox .tox-pop__dialog { + background-color: #fff; + border: 1px solid #cccccc; + border-radius: 3px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15); + min-width: 0; + overflow: hidden; +} + +.tox .tox-pop__dialog > *:not(.tox-toolbar) { + margin: 4px 4px 4px 8px; +} + +.tox .tox-pop__dialog .tox-toolbar { + background-color: transparent; + margin-bottom: -1px; +} + +.tox .tox-pop::before, +.tox .tox-pop::after { + border-style: solid; + content: ''; + display: block; + height: 0; + position: absolute; + width: 0; +} + +.tox .tox-pop.tox-pop--bottom::before, +.tox .tox-pop.tox-pop--bottom::after { + left: 50%; + top: 100%; +} + +.tox .tox-pop.tox-pop--bottom::after { + border-color: #fff transparent transparent transparent; + border-width: 8px; + margin-left: -8px; + margin-top: -1px; +} + +.tox .tox-pop.tox-pop--bottom::before { + border-color: #cccccc transparent transparent transparent; + border-width: 9px; + margin-left: -9px; +} + +.tox .tox-pop.tox-pop--top::before, +.tox .tox-pop.tox-pop--top::after { + left: 50%; + top: 0; + transform: translateY(-100%); +} + +.tox .tox-pop.tox-pop--top::after { + border-color: transparent transparent #fff transparent; + border-width: 8px; + margin-left: -8px; + margin-top: 1px; +} + +.tox .tox-pop.tox-pop--top::before { + border-color: transparent transparent #cccccc transparent; + border-width: 9px; + margin-left: -9px; +} + +.tox .tox-pop.tox-pop--left::before, +.tox .tox-pop.tox-pop--left::after { + left: 0; + top: calc(50% - 1px); + transform: translateY(-50%); +} + +.tox .tox-pop.tox-pop--left::after { + border-color: transparent #fff transparent transparent; + border-width: 8px; + margin-left: -15px; +} + +.tox .tox-pop.tox-pop--left::before { + border-color: transparent #cccccc transparent transparent; + border-width: 10px; + margin-left: -19px; +} + +.tox .tox-pop.tox-pop--right::before, +.tox .tox-pop.tox-pop--right::after { + left: 100%; + top: calc(50% + 1px); + transform: translateY(-50%); +} + +.tox .tox-pop.tox-pop--right::after { + border-color: transparent transparent transparent #fff; + border-width: 8px; + margin-left: -1px; +} + +.tox .tox-pop.tox-pop--right::before { + border-color: transparent transparent transparent #cccccc; + border-width: 10px; + margin-left: -1px; +} + +.tox .tox-pop.tox-pop--align-left::before, +.tox .tox-pop.tox-pop--align-left::after { + left: 20px; +} + +.tox .tox-pop.tox-pop--align-right::before, +.tox .tox-pop.tox-pop--align-right::after { + left: calc(100% - 20px); +} + +.tox .tox-sidebar-wrap { + display: flex; + flex-direction: row; + flex-grow: 1; + -ms-flex-preferred-size: 0; + min-height: 0; +} + +.tox .tox-sidebar { + background-color: #fff; + display: flex; + flex-direction: row; + justify-content: flex-end; +} + +.tox .tox-sidebar__slider { + display: flex; + overflow: hidden; +} + +.tox .tox-sidebar__pane-container { + display: flex; +} + +.tox .tox-sidebar__pane { + display: flex; +} + +.tox .tox-sidebar--sliding-closed { + opacity: 0; +} + +.tox .tox-sidebar--sliding-open { + opacity: 1; +} + +.tox .tox-sidebar--sliding-growing, +.tox .tox-sidebar--sliding-shrinking { + transition: width 0.5s ease, opacity 0.5s ease; +} + +.tox .tox-selector { + background-color: #4099ff; + border-color: #4099ff; + border-style: solid; + border-width: 1px; + box-sizing: border-box; + display: inline-block; + height: 10px; + position: absolute; + width: 10px; +} + +.tox.tox-platform-touch .tox-selector { + height: 12px; + width: 12px; +} + +.tox .tox-slider { + align-items: center; + display: flex; + flex: 1; + -ms-flex-preferred-size: auto; + height: 24px; + justify-content: center; + position: relative; +} + +.tox .tox-slider__rail { + background-color: transparent; + border: 1px solid #cccccc; + border-radius: 3px; + height: 10px; + min-width: 120px; + width: 100%; +} + +.tox .tox-slider__handle { + background-color: #207ab7; + border: 2px solid #185d8c; + border-radius: 3px; + box-shadow: none; + height: 24px; + left: 50%; + position: absolute; + top: 50%; + transform: translateX(-50%) translateY(-50%); + width: 14px; +} + +.tox .tox-source-code { + overflow: auto; +} + +.tox .tox-spinner { + display: flex; +} + +.tox .tox-spinner > div { + animation: tam-bouncing-dots 1.5s ease-in-out 0s infinite both; + background-color: rgba(34, 47, 62, 0.7); + border-radius: 100%; + height: 8px; + width: 8px; +} + +.tox .tox-spinner > div:nth-child(1) { + animation-delay: -0.32s; +} + +.tox .tox-spinner > div:nth-child(2) { + animation-delay: -0.16s; +} + +@keyframes tam-bouncing-dots { + 0%, + 80%, + 100% { + transform: scale(0); + } + 40% { + transform: scale(1); + } +} + +.tox:not([dir=rtl]) .tox-spinner > div:not(:first-child) { + margin-left: 4px; +} + +.tox[dir=rtl] .tox-spinner > div:not(:first-child) { + margin-right: 4px; +} + +.tox .tox-statusbar { + align-items: center; + background-color: #fff; + border-top: 1px solid #cccccc; + color: rgba(34, 47, 62, 0.7); + display: flex; + flex: 0 0 auto; + font-size: 12px; + font-weight: normal; + height: 18px; + overflow: hidden; + padding: 0 8px; + position: relative; + text-transform: uppercase; +} + +.tox .tox-statusbar__text-container { + display: flex; + flex: 1 1 auto; + justify-content: flex-end; + overflow: hidden; +} + +.tox .tox-statusbar__path { + display: flex; + flex: 1 1 auto; + margin-right: auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.tox .tox-statusbar__path > * { + display: inline; + white-space: nowrap; +} + +.tox .tox-statusbar__wordcount { + flex: 0 0 auto; + margin-left: 1ch; +} + +.tox .tox-statusbar a, +.tox .tox-statusbar__path-item, +.tox .tox-statusbar__wordcount { + color: rgba(34, 47, 62, 0.7); + text-decoration: none; +} + +.tox .tox-statusbar a:hover:not(:disabled):not([aria-disabled=true]), +.tox .tox-statusbar__path-item:hover:not(:disabled):not([aria-disabled=true]), +.tox .tox-statusbar__wordcount:hover:not(:disabled):not([aria-disabled=true]), +.tox .tox-statusbar a:focus:not(:disabled):not([aria-disabled=true]), +.tox .tox-statusbar__path-item:focus:not(:disabled):not([aria-disabled=true]), +.tox .tox-statusbar__wordcount:focus:not(:disabled):not([aria-disabled=true]) { + cursor: pointer; + text-decoration: underline; +} + +.tox .tox-statusbar__resize-handle { + align-items: flex-end; + align-self: stretch; + cursor: nwse-resize; + display: flex; + flex: 0 0 auto; + justify-content: flex-end; + margin-left: auto; + margin-right: -8px; + padding-left: 1ch; +} + +.tox .tox-statusbar__resize-handle svg { + display: block; + fill: rgba(34, 47, 62, 0.7); +} + +.tox .tox-statusbar__resize-handle:focus svg { + background-color: #dee0e2; + border-radius: 1px; + box-shadow: 0 0 0 2px #dee0e2; +} + +.tox:not([dir=rtl]) .tox-statusbar__path > * { + margin-right: 4px; +} + +.tox:not([dir=rtl]) .tox-statusbar__branding { + margin-left: 1ch; +} + +.tox[dir=rtl] .tox-statusbar { + flex-direction: row-reverse; +} + +.tox[dir=rtl] .tox-statusbar__path > * { + margin-left: 4px; +} + +.tox .tox-throbber { + z-index: 1299; +} + +.tox .tox-throbber__busy-spinner { + align-items: center; + background-color: rgba(255, 255, 255, 0.6); + bottom: 0; + display: flex; + justify-content: center; + left: 0; + position: absolute; + right: 0; + top: 0; +} + +.tox .tox-tbtn { + align-items: center; + background: transparent; + border: 0; + border-radius: 3px; + box-shadow: none; + color: #222f3e; + display: flex; + flex: 0 0 auto; + font-size: 14px; + font-style: normal; + font-weight: normal; + height: 34px; + justify-content: center; + margin: 2px 0 3px 0; + outline: none; + overflow: hidden; + padding: 0; + text-transform: none; + width: 34px; +} + +.tox .tox-tbtn svg { + display: block; + fill: #222f3e; +} + +.tox .tox-tbtn.tox-tbtn-more { + padding-left: 5px; + padding-right: 5px; + width: inherit; +} + +.tox .tox-tbtn:focus { + background: #dee0e2; + border: 0; + box-shadow: none; +} + +.tox .tox-tbtn:hover { + background: #dee0e2; + border: 0; + box-shadow: none; + color: #222f3e; +} + +.tox .tox-tbtn:hover svg { + fill: #222f3e; +} + +.tox .tox-tbtn:active { + background: #c8cbcf; + border: 0; + box-shadow: none; + color: #222f3e; +} + +.tox .tox-tbtn:active svg { + fill: #222f3e; +} + +.tox .tox-tbtn--disabled, +.tox .tox-tbtn--disabled:hover, +.tox .tox-tbtn:disabled, +.tox .tox-tbtn:disabled:hover { + background: transparent; + border: 0; + box-shadow: none; + color: rgba(34, 47, 62, 0.5); + cursor: not-allowed; +} + +.tox .tox-tbtn--disabled svg, +.tox .tox-tbtn--disabled:hover svg, +.tox .tox-tbtn:disabled svg, +.tox .tox-tbtn:disabled:hover svg { + /* stylelint-disable-line no-descending-specificity */ + fill: rgba(34, 47, 62, 0.5); +} + +.tox .tox-tbtn--enabled, +.tox .tox-tbtn--enabled:hover { + background: #c8cbcf; + border: 0; + box-shadow: none; + color: #222f3e; +} + +.tox .tox-tbtn--enabled > *, +.tox .tox-tbtn--enabled:hover > * { + transform: none; +} + +.tox .tox-tbtn--enabled svg, +.tox .tox-tbtn--enabled:hover svg { + /* stylelint-disable-line no-descending-specificity */ + fill: #222f3e; +} + +.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) { + color: #222f3e; +} + +.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) svg { + fill: #222f3e; +} + +.tox .tox-tbtn:active > * { + transform: none; +} + +.tox .tox-tbtn--md { + height: 51px; + width: 51px; +} + +.tox .tox-tbtn--lg { + flex-direction: column; + height: 68px; + width: 68px; +} + +.tox .tox-tbtn--return { + -ms-grid-row-align: stretch; + align-self: stretch; + height: unset; + width: 16px; +} + +.tox .tox-tbtn--labeled { + padding: 0 4px; + width: unset; +} + +.tox .tox-tbtn__vlabel { + display: block; + font-size: 10px; + font-weight: normal; + letter-spacing: -0.025em; + margin-bottom: 4px; + white-space: nowrap; +} + +.tox .tox-tbtn--select { + margin: 2px 0 3px 0; + padding: 0 4px; + width: auto; +} + +.tox .tox-tbtn__select-label { + cursor: default; + font-weight: normal; + margin: 0 4px; +} + +.tox .tox-tbtn__select-chevron { + align-items: center; + display: flex; + justify-content: center; + width: 16px; +} + +.tox .tox-tbtn__select-chevron svg { + fill: rgba(34, 47, 62, 0.5); +} + +.tox .tox-tbtn--bespoke .tox-tbtn__select-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 7em; +} + +.tox .tox-split-button { + border: 0; + border-radius: 3px; + box-sizing: border-box; + display: flex; + margin: 2px 0 3px 0; + overflow: hidden; +} + +.tox .tox-split-button:hover { + box-shadow: 0 0 0 1px #dee0e2 inset; +} + +.tox .tox-split-button:focus { + background: #dee0e2; + box-shadow: none; + color: #222f3e; +} + +.tox .tox-split-button > * { + border-radius: 0; +} + +.tox .tox-split-button__chevron { + width: 16px; +} + +.tox .tox-split-button__chevron svg { + fill: rgba(34, 47, 62, 0.5); +} + +.tox .tox-split-button .tox-tbtn { + margin: 0; +} + +.tox.tox-platform-touch .tox-split-button .tox-tbtn:first-child { + width: 30px; +} + +.tox.tox-platform-touch .tox-split-button__chevron { + width: 20px; +} + +.tox .tox-split-button.tox-tbtn--disabled:hover, +.tox .tox-split-button.tox-tbtn--disabled:focus, +.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:hover, +.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:focus { + background: transparent; + box-shadow: none; + color: rgba(34, 47, 62, 0.5); +} + +.tox .tox-toolbar-overlord { + background-color: #fff; +} + +.tox .tox-toolbar, +.tox .tox-toolbar__primary, +.tox .tox-toolbar__overflow { + background: url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23cccccc'/%3E%3C/svg%3E") left 0 top 0 #fff; + background-color: #fff; + display: flex; + flex: 0 0 auto; + flex-shrink: 0; + flex-wrap: wrap; + padding: 0 0; +} + +.tox .tox-toolbar__overflow.tox-toolbar__overflow--closed { + height: 0; + opacity: 0; + padding-bottom: 0; + padding-top: 0; + visibility: hidden; +} + +.tox .tox-toolbar__overflow--growing { + transition: height 0.3s ease, opacity 0.2s linear 0.1s; +} + +.tox .tox-toolbar__overflow--shrinking { + transition: opacity 0.3s ease, height 0.2s linear 0.1s, visibility 0s linear 0.3s; +} + +.tox .tox-menubar + .tox-toolbar, +.tox .tox-menubar + .tox-toolbar-overlord .tox-toolbar__primary { + border-top: 1px solid #cccccc; + margin-top: -1px; +} + +.tox .tox-toolbar--scrolling { + flex-wrap: nowrap; + overflow-x: auto; +} + +.tox .tox-pop .tox-toolbar { + border-width: 0; +} + +.tox .tox-toolbar--no-divider { + background-image: none; +} + +.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar:first-child, +.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar-overlord:first-child .tox-toolbar__primary { + border-top: 1px solid #cccccc; +} + +.tox.tox-tinymce-aux .tox-toolbar__overflow { + background-color: #fff; + border: 1px solid #cccccc; + border-radius: 3px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15); +} + +.tox[dir=rtl] .tox-tbtn__icon-rtl svg { + transform: rotateY(180deg); +} + +.tox .tox-toolbar__group { + align-items: center; + display: flex; + flex-wrap: wrap; + margin: 0 0; + padding: 0 4px 0 4px; +} + +.tox .tox-toolbar__group--pull-right { + margin-left: auto; +} + +.tox .tox-toolbar--scrolling .tox-toolbar__group { + flex-shrink: 0; + flex-wrap: nowrap; +} + +.tox:not([dir=rtl]) .tox-toolbar__group:not(:last-of-type) { + border-right: 1px solid #cccccc; +} + +.tox[dir=rtl] .tox-toolbar__group:not(:last-of-type) { + border-left: 1px solid #cccccc; +} + +.tox .tox-tooltip { + display: inline-block; + padding: 8px; + position: relative; +} + +.tox .tox-tooltip__body { + background-color: #222f3e; + border-radius: 3px; + box-shadow: 0 2px 4px rgba(34, 47, 62, 0.3); + color: rgba(255, 255, 255, 0.75); + font-size: 14px; + font-style: normal; + font-weight: normal; + padding: 4px 8px; + text-transform: none; +} + +.tox .tox-tooltip__arrow { + position: absolute; +} + +.tox .tox-tooltip--down .tox-tooltip__arrow { + border-left: 8px solid transparent; + border-right: 8px solid transparent; + border-top: 8px solid #222f3e; + bottom: 0; + left: 50%; + position: absolute; + transform: translateX(-50%); +} + +.tox .tox-tooltip--up .tox-tooltip__arrow { + border-bottom: 8px solid #222f3e; + border-left: 8px solid transparent; + border-right: 8px solid transparent; + left: 50%; + position: absolute; + top: 0; + transform: translateX(-50%); +} + +.tox .tox-tooltip--right .tox-tooltip__arrow { + border-bottom: 8px solid transparent; + border-left: 8px solid #222f3e; + border-top: 8px solid transparent; + position: absolute; + right: 0; + top: 50%; + transform: translateY(-50%); +} + +.tox .tox-tooltip--left .tox-tooltip__arrow { + border-bottom: 8px solid transparent; + border-right: 8px solid #222f3e; + border-top: 8px solid transparent; + left: 0; + position: absolute; + top: 50%; + transform: translateY(-50%); +} + +.tox .tox-well { + border: 1px solid #cccccc; + border-radius: 3px; + padding: 8px; + width: 100%; +} + +.tox .tox-well > *:first-child { + margin-top: 0; +} + +.tox .tox-well > *:last-child { + margin-bottom: 0; +} + +.tox .tox-well > *:only-child { + margin: 0; +} + +.tox .tox-custom-editor { + border: 1px solid #cccccc; + border-radius: 3px; + display: flex; + flex: 1; + position: relative; +} + +/* stylelint-disable */ +.tox { + /* stylelint-enable */ +} + +.tox .tox-dialog-loading::before { + background-color: rgba(0, 0, 0, 0.5); + content: ""; + height: 100%; + position: absolute; + width: 100%; + z-index: 1000; +} + +.tox .tox-tab { + cursor: pointer; +} + +.tox .tox-dialog__content-js { + display: flex; + flex: 1; + -ms-flex-preferred-size: auto; +} + +.tox .tox-dialog__body-content .tox-collection { + display: flex; + flex: 1; + -ms-flex-preferred-size: auto; +} + +.tox .tox-image-tools-edit-panel { + height: 60px; +} + +.tox .tox-image-tools__sidebar { + height: 60px; +} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.min.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.min.css new file mode 100644 index 0000000..143255f --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.tox{box-shadow:none;box-sizing:content-box;color:#222f3e;cursor:auto;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;font-style:normal;font-weight:400;line-height:normal;-webkit-tap-highlight-color:transparent;text-decoration:none;text-shadow:none;text-transform:none;vertical-align:initial;white-space:normal}.tox :not(svg):not(rect){box-sizing:inherit;color:inherit;cursor:inherit;direction:inherit;font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;line-height:inherit;-webkit-tap-highlight-color:inherit;text-align:inherit;text-decoration:inherit;text-shadow:inherit;text-transform:inherit;vertical-align:inherit;white-space:inherit}.tox :not(svg):not(rect){background:0 0;border:0;box-shadow:none;float:none;height:auto;margin:0;max-width:none;outline:0;padding:0;position:static;width:auto}.tox:not([dir=rtl]){direction:ltr;text-align:left}.tox[dir=rtl]{direction:rtl;text-align:right}.tox-tinymce{border:1px solid #ccc;border-radius:0;box-shadow:none;box-sizing:border-box;display:flex;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;overflow:hidden;position:relative;visibility:inherit!important}.tox-tinymce-inline{max-width:850px;border:none;box-shadow:none}.tox-tinymce-inline .tox-editor-header{background-color:transparent;border:1px solid #ccc;border-radius:0;box-shadow:none}.tox-tinymce-aux{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;z-index:1300}.tox-tinymce :focus,.tox-tinymce-aux :focus{outline:0}button::-moz-focus-inner{border:0}.tox .accessibility-issue__header{align-items:center;display:flex;margin-bottom:4px}.tox .accessibility-issue__description{align-items:stretch;border:1px solid #ccc;border-radius:3px;display:flex;justify-content:space-between}.tox .accessibility-issue__description>div{padding-bottom:4px}.tox .accessibility-issue__description>div>div{align-items:center;display:flex;margin-bottom:4px}.tox .accessibility-issue__description>:last-child:not(:only-child){border-color:#ccc;border-style:solid}.tox .accessibility-issue__repair{margin-top:16px}.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description{background-color:rgba(32,122,183,.1);border-color:rgba(32,122,183,.4);color:#222f3e}.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description>:last-child{border-color:rgba(32,122,183,.4)}.tox .tox-dialog__body-content .accessibility-issue--info .tox-form__group h2{color:#207ab7}.tox .tox-dialog__body-content .accessibility-issue--info .tox-icon svg{fill:#207ab7}.tox .tox-dialog__body-content .accessibility-issue--info a .tox-icon{color:#207ab7}.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description{background-color:rgba(255,165,0,.1);border-color:rgba(255,165,0,.5);color:#222f3e}.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description>:last-child{border-color:rgba(255,165,0,.5)}.tox .tox-dialog__body-content .accessibility-issue--warn .tox-form__group h2{color:#cc8500}.tox .tox-dialog__body-content .accessibility-issue--warn .tox-icon svg{fill:#cc8500}.tox .tox-dialog__body-content .accessibility-issue--warn a .tox-icon{color:#cc8500}.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description{background-color:rgba(204,0,0,.1);border-color:rgba(204,0,0,.4);color:#222f3e}.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description>:last-child{border-color:rgba(204,0,0,.4)}.tox .tox-dialog__body-content .accessibility-issue--error .tox-form__group h2{color:#c00}.tox .tox-dialog__body-content .accessibility-issue--error .tox-icon svg{fill:#c00}.tox .tox-dialog__body-content .accessibility-issue--error a .tox-icon{color:#c00}.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description{background-color:rgba(120,171,70,.1);border-color:rgba(120,171,70,.4);color:#222f3e}.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description>:last-child{border-color:rgba(120,171,70,.4)}.tox .tox-dialog__body-content .accessibility-issue--success .tox-form__group h2{color:#78ab46}.tox .tox-dialog__body-content .accessibility-issue--success .tox-icon svg{fill:#78ab46}.tox .tox-dialog__body-content .accessibility-issue--success a .tox-icon{color:#78ab46}.tox .tox-dialog__body-content .accessibility-issue__header h1,.tox .tox-dialog__body-content .tox-form__group .accessibility-issue__description h2{margin-top:0}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header .tox-button{margin-left:4px}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header>:nth-last-child(2){margin-left:auto}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description{padding:4px 4px 4px 8px}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description>:last-child{border-left-width:1px;padding-left:4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header .tox-button{margin-right:4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header>:nth-last-child(2){margin-right:auto}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description{padding:4px 8px 4px 4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description>:last-child{border-right-width:1px;padding-right:4px}.tox .tox-anchorbar{display:flex;flex:0 0 auto}.tox .tox-bar{display:flex;flex:0 0 auto}.tox .tox-button{background-color:#207ab7;background-image:none;background-position:0 0;background-repeat:repeat;border-color:#207ab7;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#fff;cursor:pointer;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:14px;font-style:normal;font-weight:700;letter-spacing:normal;line-height:24px;margin:0;outline:0;padding:4px 16px;text-align:center;text-decoration:none;text-transform:none;white-space:nowrap}.tox .tox-button[disabled]{background-color:#207ab7;background-image:none;border-color:#207ab7;box-shadow:none;color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-button:focus:not(:disabled){background-color:#1c6ca1;background-image:none;border-color:#1c6ca1;box-shadow:none;color:#fff}.tox .tox-button:hover:not(:disabled){background-color:#1c6ca1;background-image:none;border-color:#1c6ca1;box-shadow:none;color:#fff}.tox .tox-button:active:not(:disabled){background-color:#185d8c;background-image:none;border-color:#185d8c;box-shadow:none;color:#fff}.tox .tox-button--secondary{background-color:#f0f0f0;background-image:none;background-position:0 0;background-repeat:repeat;border-color:#f0f0f0;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;color:#222f3e;font-size:14px;font-style:normal;font-weight:700;letter-spacing:normal;outline:0;padding:4px 16px;text-decoration:none;text-transform:none}.tox .tox-button--secondary[disabled]{background-color:#f0f0f0;background-image:none;border-color:#f0f0f0;box-shadow:none;color:rgba(34,47,62,.5)}.tox .tox-button--secondary:focus:not(:disabled){background-color:#e3e3e3;background-image:none;border-color:#e3e3e3;box-shadow:none;color:#222f3e}.tox .tox-button--secondary:hover:not(:disabled){background-color:#e3e3e3;background-image:none;border-color:#e3e3e3;box-shadow:none;color:#222f3e}.tox .tox-button--secondary:active:not(:disabled){background-color:#d6d6d6;background-image:none;border-color:#d6d6d6;box-shadow:none;color:#222f3e}.tox .tox-button--icon,.tox .tox-button.tox-button--icon,.tox .tox-button.tox-button--secondary.tox-button--icon{padding:4px}.tox .tox-button--icon .tox-icon svg,.tox .tox-button.tox-button--icon .tox-icon svg,.tox .tox-button.tox-button--secondary.tox-button--icon .tox-icon svg{display:block;fill:currentColor}.tox .tox-button-link{background:0;border:none;box-sizing:border-box;cursor:pointer;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;white-space:nowrap}.tox .tox-button-link--sm{font-size:14px}.tox .tox-button--naked{background-color:transparent;border-color:transparent;box-shadow:unset;color:#222f3e}.tox .tox-button--naked[disabled]{background-color:#f0f0f0;border-color:#f0f0f0;box-shadow:none;color:rgba(34,47,62,.5)}.tox .tox-button--naked:hover:not(:disabled){background-color:#e3e3e3;border-color:#e3e3e3;box-shadow:none;color:#222f3e}.tox .tox-button--naked:focus:not(:disabled){background-color:#e3e3e3;border-color:#e3e3e3;box-shadow:none;color:#222f3e}.tox .tox-button--naked:active:not(:disabled){background-color:#d6d6d6;border-color:#d6d6d6;box-shadow:none;color:#222f3e}.tox .tox-button--naked .tox-icon svg{fill:currentColor}.tox .tox-button--naked.tox-button--icon:hover:not(:disabled){color:#222f3e}.tox .tox-checkbox{align-items:center;border-radius:3px;cursor:pointer;display:flex;height:36px;min-width:36px}.tox .tox-checkbox__input{height:1px;overflow:hidden;position:absolute;top:auto;width:1px}.tox .tox-checkbox__icons{align-items:center;border-radius:3px;box-shadow:0 0 0 2px transparent;box-sizing:content-box;display:flex;height:24px;justify-content:center;padding:calc(4px - 1px);width:24px}.tox .tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:block;fill:rgba(34,47,62,.3)}.tox .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{display:none;fill:#207ab7}.tox .tox-checkbox__icons .tox-checkbox-icon__checked svg{display:none;fill:#207ab7}.tox .tox-checkbox--disabled{color:rgba(34,47,62,.5);cursor:not-allowed}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__checked svg{fill:rgba(34,47,62,.5)}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__unchecked svg{fill:rgba(34,47,62,.5)}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{fill:rgba(34,47,62,.5)}.tox input.tox-checkbox__input:checked+.tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:none}.tox input.tox-checkbox__input:checked+.tox-checkbox__icons .tox-checkbox-icon__checked svg{display:block}.tox input.tox-checkbox__input:indeterminate+.tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:none}.tox input.tox-checkbox__input:indeterminate+.tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{display:block}.tox input.tox-checkbox__input:focus+.tox-checkbox__icons{border-radius:3px;box-shadow:inset 0 0 0 1px #207ab7;padding:calc(4px - 1px)}.tox:not([dir=rtl]) .tox-checkbox__label{margin-left:4px}.tox:not([dir=rtl]) .tox-checkbox__input{left:-10000px}.tox:not([dir=rtl]) .tox-bar .tox-checkbox{margin-left:4px}.tox[dir=rtl] .tox-checkbox__label{margin-right:4px}.tox[dir=rtl] .tox-checkbox__input{right:-10000px}.tox[dir=rtl] .tox-bar .tox-checkbox{margin-right:4px}.tox .tox-collection--toolbar .tox-collection__group{display:flex;padding:0}.tox .tox-collection--grid .tox-collection__group{display:flex;flex-wrap:wrap;max-height:208px;overflow-x:hidden;overflow-y:auto;padding:0}.tox .tox-collection--list .tox-collection__group{border-bottom-width:0;border-color:#ccc;border-left-width:0;border-right-width:0;border-style:solid;border-top-width:1px;padding:4px 0}.tox .tox-collection--list .tox-collection__group:first-child{border-top-width:0}.tox .tox-collection__group-heading{background-color:#e6e6e6;color:rgba(34,47,62,.7);cursor:default;font-size:12px;font-style:normal;font-weight:400;margin-bottom:4px;margin-top:-4px;padding:4px 8px;text-transform:none;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tox .tox-collection__item{align-items:center;color:#222f3e;cursor:pointer;display:flex;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tox .tox-collection--list .tox-collection__item{padding:4px 8px}.tox .tox-collection--toolbar .tox-collection__item{border-radius:3px;padding:4px}.tox .tox-collection--grid .tox-collection__item{border-radius:3px;padding:4px}.tox .tox-collection--list .tox-collection__item--enabled{background-color:#fff;color:#222f3e}.tox .tox-collection--list .tox-collection__item--active{background-color:#dee0e2}.tox .tox-collection--toolbar .tox-collection__item--enabled{background-color:#c8cbcf;color:#222f3e}.tox .tox-collection--toolbar .tox-collection__item--active{background-color:#dee0e2}.tox .tox-collection--grid .tox-collection__item--enabled{background-color:#c8cbcf;color:#222f3e}.tox .tox-collection--grid .tox-collection__item--active:not(.tox-collection__item--state-disabled){background-color:#dee0e2;color:#222f3e}.tox .tox-collection--list .tox-collection__item--active:not(.tox-collection__item--state-disabled){color:#222f3e}.tox .tox-collection--toolbar .tox-collection__item--active:not(.tox-collection__item--state-disabled){color:#222f3e}.tox .tox-collection__item--state-disabled{background-color:transparent;color:rgba(34,47,62,.5);cursor:not-allowed}.tox .tox-collection__item-checkmark,.tox .tox-collection__item-icon{align-items:center;display:flex;height:24px;justify-content:center;width:24px}.tox .tox-collection__item-checkmark svg,.tox .tox-collection__item-icon svg{fill:currentColor}.tox .tox-collection--toolbar-lg .tox-collection__item-icon{height:48px;width:48px}.tox .tox-collection__item-label{color:currentColor;display:inline-block;flex:1;-ms-flex-preferred-size:auto;font-size:14px;font-style:normal;font-weight:400;line-height:24px;text-transform:none;word-break:break-all}.tox .tox-collection__item-accessory{color:rgba(34,47,62,.7);display:inline-block;font-size:14px;height:24px;line-height:24px;text-transform:none}.tox .tox-collection__item-caret{align-items:center;display:flex;min-height:24px}.tox .tox-collection__item-caret::after{content:'';font-size:0;min-height:inherit}.tox .tox-collection__item-caret svg{fill:#222f3e}.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-checkmark svg{display:none}.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-accessory+.tox-collection__item-checkmark{display:none}.tox .tox-collection--horizontal{background-color:#fff;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15);display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:nowrap;margin-bottom:0;overflow-x:auto;padding:0}.tox .tox-collection--horizontal .tox-collection__group{align-items:center;display:flex;flex-wrap:nowrap;margin:0;padding:0 4px}.tox .tox-collection--horizontal .tox-collection__item{height:34px;margin:2px 0 3px 0;padding:0 4px}.tox .tox-collection--horizontal .tox-collection__item-label{white-space:nowrap}.tox .tox-collection--horizontal .tox-collection__item-caret{margin-left:4px}.tox .tox-collection__item-container{display:flex}.tox .tox-collection__item-container--row{align-items:center;flex:1 1 auto;flex-direction:row}.tox .tox-collection__item-container--row.tox-collection__item-container--align-left{margin-right:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--align-right{justify-content:flex-end;margin-left:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-top{align-items:flex-start;margin-bottom:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-middle{align-items:center}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-bottom{align-items:flex-end;margin-top:auto}.tox .tox-collection__item-container--column{-ms-grid-row-align:center;align-self:center;flex:1 1 auto;flex-direction:column}.tox .tox-collection__item-container--column.tox-collection__item-container--align-left{align-items:flex-start}.tox .tox-collection__item-container--column.tox-collection__item-container--align-right{align-items:flex-end}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-top{align-self:flex-start}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-middle{-ms-grid-row-align:center;align-self:center}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-bottom{align-self:flex-end}.tox:not([dir=rtl]) .tox-collection--horizontal .tox-collection__group:not(:last-of-type){border-right:1px solid #ccc}.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item>:not(:first-child){margin-left:8px}.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item>.tox-collection__item-label:first-child{margin-left:4px}.tox:not([dir=rtl]) .tox-collection__item-accessory{margin-left:16px;text-align:right}.tox:not([dir=rtl]) .tox-collection .tox-collection__item-caret{margin-left:16px}.tox[dir=rtl] .tox-collection--horizontal .tox-collection__group:not(:last-of-type){border-left:1px solid #ccc}.tox[dir=rtl] .tox-collection--list .tox-collection__item>:not(:first-child){margin-right:8px}.tox[dir=rtl] .tox-collection--list .tox-collection__item>.tox-collection__item-label:first-child{margin-right:4px}.tox[dir=rtl] .tox-collection__item-icon-rtl .tox-collection__item-icon svg{transform:rotateY(180deg)}.tox[dir=rtl] .tox-collection__item-accessory{margin-right:16px;text-align:left}.tox[dir=rtl] .tox-collection .tox-collection__item-caret{margin-right:16px;transform:rotateY(180deg)}.tox[dir=rtl] .tox-collection--horizontal .tox-collection__item-caret{margin-right:4px}.tox .tox-color-picker-container{display:flex;flex-direction:row;height:225px;margin:0}.tox .tox-sv-palette{box-sizing:border-box;display:flex;height:100%}.tox .tox-sv-palette-spectrum{height:100%}.tox .tox-sv-palette,.tox .tox-sv-palette-spectrum{width:225px}.tox .tox-sv-palette-thumb{background:0 0;border:1px solid #000;border-radius:50%;box-sizing:content-box;height:12px;position:absolute;width:12px}.tox .tox-sv-palette-inner-thumb{border:1px solid #fff;border-radius:50%;height:10px;position:absolute;width:10px}.tox .tox-hue-slider{box-sizing:border-box;height:100%;width:25px}.tox .tox-hue-slider-spectrum{background:linear-gradient(to bottom,red,#ff0080,#f0f,#8000ff,#00f,#0080ff,#0ff,#00ff80,#0f0,#80ff00,#ff0,#ff8000,red);height:100%;width:100%}.tox .tox-hue-slider,.tox .tox-hue-slider-spectrum{width:20px}.tox .tox-hue-slider-thumb{background:#fff;border:1px solid #000;box-sizing:content-box;height:4px;width:100%}.tox .tox-rgb-form{display:flex;flex-direction:column;justify-content:space-between}.tox .tox-rgb-form div{align-items:center;display:flex;justify-content:space-between;margin-bottom:5px;width:inherit}.tox .tox-rgb-form input{width:6em}.tox .tox-rgb-form input.tox-invalid{border:1px solid red!important}.tox .tox-rgb-form .tox-rgba-preview{border:1px solid #000;flex-grow:2;margin-bottom:0}.tox:not([dir=rtl]) .tox-sv-palette{margin-right:15px}.tox:not([dir=rtl]) .tox-hue-slider{margin-right:15px}.tox:not([dir=rtl]) .tox-hue-slider-thumb{margin-left:-1px}.tox:not([dir=rtl]) .tox-rgb-form label{margin-right:.5em}.tox[dir=rtl] .tox-sv-palette{margin-left:15px}.tox[dir=rtl] .tox-hue-slider{margin-left:15px}.tox[dir=rtl] .tox-hue-slider-thumb{margin-right:-1px}.tox[dir=rtl] .tox-rgb-form label{margin-left:.5em}.tox .tox-toolbar .tox-swatches,.tox .tox-toolbar__overflow .tox-swatches,.tox .tox-toolbar__primary .tox-swatches{margin:2px 0 3px 4px}.tox .tox-collection--list .tox-collection__group .tox-swatches-menu{border:0;margin:-4px 0}.tox .tox-swatches__row{display:flex}.tox .tox-swatch{height:30px;transition:transform .15s,box-shadow .15s;width:30px}.tox .tox-swatch:focus,.tox .tox-swatch:hover{box-shadow:0 0 0 1px rgba(127,127,127,.3) inset;transform:scale(.8)}.tox .tox-swatch--remove{align-items:center;display:flex;justify-content:center}.tox .tox-swatch--remove svg path{stroke:#e74c3c}.tox .tox-swatches__picker-btn{align-items:center;background-color:transparent;border:0;cursor:pointer;display:flex;height:30px;justify-content:center;outline:0;padding:0;width:30px}.tox .tox-swatches__picker-btn svg{height:24px;width:24px}.tox .tox-swatches__picker-btn:hover{background:#dee0e2}.tox:not([dir=rtl]) .tox-swatches__picker-btn{margin-left:auto}.tox[dir=rtl] .tox-swatches__picker-btn{margin-right:auto}.tox .tox-comment-thread{background:#fff;position:relative}.tox .tox-comment-thread>:not(:first-child){margin-top:8px}.tox .tox-comment{background:#fff;border:1px solid #ccc;border-radius:3px;box-shadow:0 4px 8px 0 rgba(34,47,62,.1);padding:8px 8px 16px 8px;position:relative}.tox .tox-comment__header{align-items:center;color:#222f3e;display:flex;justify-content:space-between}.tox .tox-comment__date{color:rgba(34,47,62,.7);font-size:12px}.tox .tox-comment__body{color:#222f3e;font-size:14px;font-style:normal;font-weight:400;line-height:1.3;margin-top:8px;position:relative;text-transform:initial}.tox .tox-comment__body textarea{resize:none;white-space:normal;width:100%}.tox .tox-comment__expander{padding-top:8px}.tox .tox-comment__expander p{color:rgba(34,47,62,.7);font-size:14px;font-style:normal}.tox .tox-comment__body p{margin:0}.tox .tox-comment__buttonspacing{padding-top:16px;text-align:center}.tox .tox-comment-thread__overlay::after{background:#fff;bottom:0;content:"";display:flex;left:0;opacity:.9;position:absolute;right:0;top:0;z-index:5}.tox .tox-comment__reply{display:flex;flex-shrink:0;flex-wrap:wrap;justify-content:flex-end;margin-top:8px}.tox .tox-comment__reply>:first-child{margin-bottom:8px;width:100%}.tox .tox-comment__edit{display:flex;flex-wrap:wrap;justify-content:flex-end;margin-top:16px}.tox .tox-comment__gradient::after{background:linear-gradient(rgba(255,255,255,0),#fff);bottom:0;content:"";display:block;height:5em;margin-top:-40px;position:absolute;width:100%}.tox .tox-comment__overlay{background:#fff;bottom:0;display:flex;flex-direction:column;flex-grow:1;left:0;opacity:.9;position:absolute;right:0;text-align:center;top:0;z-index:5}.tox .tox-comment__loading-text{align-items:center;color:#222f3e;display:flex;flex-direction:column;position:relative}.tox .tox-comment__loading-text>div{padding-bottom:16px}.tox .tox-comment__overlaytext{bottom:0;flex-direction:column;font-size:14px;left:0;padding:1em;position:absolute;right:0;top:0;z-index:10}.tox .tox-comment__overlaytext p{background-color:#fff;box-shadow:0 0 8px 8px #fff;color:#222f3e;text-align:center}.tox .tox-comment__overlaytext div:nth-of-type(2){font-size:.8em}.tox .tox-comment__busy-spinner{align-items:center;background-color:#fff;bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0;z-index:20}.tox .tox-comment__scroll{display:flex;flex-direction:column;flex-shrink:1;overflow:auto}.tox .tox-conversations{margin:8px}.tox:not([dir=rtl]) .tox-comment__edit{margin-left:8px}.tox:not([dir=rtl]) .tox-comment__buttonspacing>:last-child,.tox:not([dir=rtl]) .tox-comment__edit>:last-child,.tox:not([dir=rtl]) .tox-comment__reply>:last-child{margin-left:8px}.tox[dir=rtl] .tox-comment__edit{margin-right:8px}.tox[dir=rtl] .tox-comment__buttonspacing>:last-child,.tox[dir=rtl] .tox-comment__edit>:last-child,.tox[dir=rtl] .tox-comment__reply>:last-child{margin-right:8px}.tox .tox-user{align-items:center;display:flex}.tox .tox-user__avatar svg{fill:rgba(34,47,62,.7)}.tox .tox-user__name{color:rgba(34,47,62,.7);font-size:12px;font-style:normal;font-weight:700;text-transform:uppercase}.tox:not([dir=rtl]) .tox-user__avatar svg{margin-right:8px}.tox:not([dir=rtl]) .tox-user__avatar+.tox-user__name{margin-left:8px}.tox[dir=rtl] .tox-user__avatar svg{margin-left:8px}.tox[dir=rtl] .tox-user__avatar+.tox-user__name{margin-right:8px}.tox .tox-dialog-wrap{align-items:center;bottom:0;display:flex;justify-content:center;left:0;position:fixed;right:0;top:0;z-index:1100}.tox .tox-dialog-wrap__backdrop{background-color:rgba(255,255,255,.75);bottom:0;left:0;position:absolute;right:0;top:0;z-index:1}.tox .tox-dialog-wrap__backdrop--opaque{background-color:#fff}.tox .tox-dialog{background-color:#fff;border-color:#ccc;border-radius:3px;border-style:solid;border-width:1px;box-shadow:0 16px 16px -10px rgba(34,47,62,.15),0 0 40px 1px rgba(34,47,62,.15);display:flex;flex-direction:column;max-height:100%;max-width:480px;overflow:hidden;position:relative;width:95vw;z-index:2}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog{align-self:flex-start;margin:8px auto;width:calc(100vw - 16px)}}.tox .tox-dialog-inline{z-index:1100}.tox .tox-dialog__header{align-items:center;background-color:#fff;border-bottom:none;color:#222f3e;display:flex;font-size:16px;justify-content:space-between;padding:8px 16px 0 16px;position:relative}.tox .tox-dialog__header .tox-button{z-index:1}.tox .tox-dialog__draghandle{cursor:grab;height:100%;left:0;position:absolute;top:0;width:100%}.tox .tox-dialog__draghandle:active{cursor:grabbing}.tox .tox-dialog__dismiss{margin-left:auto}.tox .tox-dialog__title{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:20px;font-style:normal;font-weight:400;line-height:1.3;margin:0;text-transform:none}.tox .tox-dialog__body{color:#222f3e;display:flex;flex:1;-ms-flex-preferred-size:auto;font-size:16px;font-style:normal;font-weight:400;line-height:1.3;min-width:0;text-align:left;text-transform:none}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog__body{flex-direction:column}}.tox .tox-dialog__body-nav{align-items:flex-start;display:flex;flex-direction:column;padding:16px 16px}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog__body-nav{flex-direction:row;-webkit-overflow-scrolling:touch;overflow-x:auto;padding-bottom:0}}.tox .tox-dialog__body-nav-item{border-bottom:2px solid transparent;color:rgba(34,47,62,.7);display:inline-block;font-size:14px;line-height:1.3;margin-bottom:8px;text-decoration:none;white-space:nowrap}.tox .tox-dialog__body-nav-item:focus{background-color:rgba(32,122,183,.1)}.tox .tox-dialog__body-nav-item--active{border-bottom:2px solid #207ab7;color:#207ab7}.tox .tox-dialog__body-content{box-sizing:border-box;display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto;max-height:650px;overflow:auto;-webkit-overflow-scrolling:touch;padding:16px 16px}.tox .tox-dialog__body-content>*{margin-bottom:0;margin-top:16px}.tox .tox-dialog__body-content>:first-child{margin-top:0}.tox .tox-dialog__body-content>:last-child{margin-bottom:0}.tox .tox-dialog__body-content>:only-child{margin-bottom:0;margin-top:0}.tox .tox-dialog__body-content a{color:#207ab7;cursor:pointer;text-decoration:none}.tox .tox-dialog__body-content a:focus,.tox .tox-dialog__body-content a:hover{color:#185d8c;text-decoration:none}.tox .tox-dialog__body-content a:active{color:#185d8c;text-decoration:none}.tox .tox-dialog__body-content svg{fill:#222f3e}.tox .tox-dialog__body-content ul{display:block;list-style-type:disc;margin-bottom:16px;-webkit-margin-end:0;margin-inline-end:0;-webkit-margin-start:0;margin-inline-start:0;-webkit-padding-start:2.5rem;padding-inline-start:2.5rem}.tox .tox-dialog__body-content .tox-form__group h1{color:#222f3e;font-size:20px;font-style:normal;font-weight:700;letter-spacing:normal;margin-bottom:16px;margin-top:2rem;text-transform:none}.tox .tox-dialog__body-content .tox-form__group h2{color:#222f3e;font-size:16px;font-style:normal;font-weight:700;letter-spacing:normal;margin-bottom:16px;margin-top:2rem;text-transform:none}.tox .tox-dialog__body-content .tox-form__group p{margin-bottom:16px}.tox .tox-dialog__body-content .tox-form__group h1:first-child,.tox .tox-dialog__body-content .tox-form__group h2:first-child,.tox .tox-dialog__body-content .tox-form__group p:first-child{margin-top:0}.tox .tox-dialog__body-content .tox-form__group h1:last-child,.tox .tox-dialog__body-content .tox-form__group h2:last-child,.tox .tox-dialog__body-content .tox-form__group p:last-child{margin-bottom:0}.tox .tox-dialog__body-content .tox-form__group h1:only-child,.tox .tox-dialog__body-content .tox-form__group h2:only-child,.tox .tox-dialog__body-content .tox-form__group p:only-child{margin-bottom:0;margin-top:0}.tox .tox-dialog--width-lg{height:650px;max-width:1200px}.tox .tox-dialog--width-md{max-width:800px}.tox .tox-dialog--width-md .tox-dialog__body-content{overflow:auto}.tox .tox-dialog__body-content--centered{text-align:center}.tox .tox-dialog__footer{align-items:center;background-color:#fff;border-top:1px solid #ccc;display:flex;justify-content:space-between;padding:8px 16px}.tox .tox-dialog__footer-end,.tox .tox-dialog__footer-start{display:flex}.tox .tox-dialog__busy-spinner{align-items:center;background-color:rgba(255,255,255,.75);bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0;z-index:3}.tox .tox-dialog__table{border-collapse:collapse;width:100%}.tox .tox-dialog__table thead th{font-weight:700;padding-bottom:8px}.tox .tox-dialog__table tbody tr{border-bottom:1px solid #ccc}.tox .tox-dialog__table tbody tr:last-child{border-bottom:none}.tox .tox-dialog__table td{padding-bottom:8px;padding-top:8px}.tox .tox-dialog__popups{position:absolute;width:100%;z-index:1100}.tox .tox-dialog__body-iframe{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-iframe .tox-navobj{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-iframe .tox-navobj :nth-child(2){flex:1;-ms-flex-preferred-size:auto;height:100%}.tox .tox-dialog-dock-fadeout{opacity:0;visibility:hidden}.tox .tox-dialog-dock-fadein{opacity:1;visibility:visible}.tox .tox-dialog-dock-transition{transition:visibility 0s linear .3s,opacity .3s ease}.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein{transition-delay:0s}.tox.tox-platform-ie .tox-dialog-wrap{position:-ms-device-fixed}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav{margin-right:0}}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child){margin-left:8px}}.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-end>*,.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-start>*{margin-left:8px}.tox[dir=rtl] .tox-dialog__body{text-align:right}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav{margin-left:0}}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child){margin-right:8px}}.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-end>*,.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-start>*{margin-right:8px}body.tox-dialog__disable-scroll{overflow:hidden}.tox .tox-dropzone-container{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dropzone{align-items:center;background:#fff;border:2px dashed #ccc;box-sizing:border-box;display:flex;flex-direction:column;flex-grow:1;justify-content:center;min-height:100px;padding:10px}.tox .tox-dropzone p{color:rgba(34,47,62,.7);margin:0 0 16px 0}.tox .tox-edit-area{display:flex;flex:1;-ms-flex-preferred-size:auto;overflow:hidden;position:relative}.tox .tox-edit-area__iframe{background-color:#fff;border:0;box-sizing:border-box;flex:1;-ms-flex-preferred-size:auto;height:100%;position:absolute;width:100%}.tox.tox-inline-edit-area{border:1px dotted #ccc}.tox .tox-editor-container{display:flex;flex:1 1 auto;flex-direction:column;overflow:hidden}.tox .tox-editor-header{z-index:1000}.tox:not(.tox-tinymce-inline) .tox-editor-header{box-shadow:none;transition:box-shadow .5s}.tox.tox-tinymce--toolbar-bottom .tox-editor-header,.tox.tox-tinymce-inline .tox-editor-header{margin-bottom:-1px}.tox.tox-tinymce--toolbar-sticky-on .tox-editor-header{background-color:transparent;box-shadow:0 4px 4px -3px rgba(0,0,0,.25)}.tox-editor-dock-fadeout{opacity:0;visibility:hidden}.tox-editor-dock-fadein{opacity:1;visibility:visible}.tox-editor-dock-transition{transition:visibility 0s linear .25s,opacity .25s ease}.tox-editor-dock-transition.tox-editor-dock-fadein{transition-delay:0s}.tox .tox-control-wrap{flex:1;position:relative}.tox .tox-control-wrap:not(.tox-control-wrap--status-invalid) .tox-control-wrap__status-icon-invalid,.tox .tox-control-wrap:not(.tox-control-wrap--status-unknown) .tox-control-wrap__status-icon-unknown,.tox .tox-control-wrap:not(.tox-control-wrap--status-valid) .tox-control-wrap__status-icon-valid{display:none}.tox .tox-control-wrap svg{display:block}.tox .tox-control-wrap__status-icon-wrap{position:absolute;top:50%;transform:translateY(-50%)}.tox .tox-control-wrap__status-icon-invalid svg{fill:#c00}.tox .tox-control-wrap__status-icon-unknown svg{fill:orange}.tox .tox-control-wrap__status-icon-valid svg{fill:green}.tox:not([dir=rtl]) .tox-control-wrap--status-invalid .tox-textfield,.tox:not([dir=rtl]) .tox-control-wrap--status-unknown .tox-textfield,.tox:not([dir=rtl]) .tox-control-wrap--status-valid .tox-textfield{padding-right:32px}.tox:not([dir=rtl]) .tox-control-wrap__status-icon-wrap{right:4px}.tox[dir=rtl] .tox-control-wrap--status-invalid .tox-textfield,.tox[dir=rtl] .tox-control-wrap--status-unknown .tox-textfield,.tox[dir=rtl] .tox-control-wrap--status-valid .tox-textfield{padding-left:32px}.tox[dir=rtl] .tox-control-wrap__status-icon-wrap{left:4px}.tox .tox-autocompleter{max-width:25em}.tox .tox-autocompleter .tox-menu{max-width:25em}.tox .tox-autocompleter .tox-autocompleter-highlight{font-weight:700}.tox .tox-color-input{display:flex;position:relative;z-index:1}.tox .tox-color-input .tox-textfield{z-index:-1}.tox .tox-color-input span{border-color:rgba(34,47,62,.2);border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;height:24px;position:absolute;top:6px;width:24px}.tox .tox-color-input span:focus:not([aria-disabled=true]),.tox .tox-color-input span:hover:not([aria-disabled=true]){border-color:#207ab7;cursor:pointer}.tox .tox-color-input span::before{background-image:linear-gradient(45deg,rgba(0,0,0,.25) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.25) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.25) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.25) 75%);background-position:0 0,0 6px,6px -6px,-6px 0;background-size:12px 12px;border:1px solid #fff;border-radius:3px;box-sizing:border-box;content:'';height:24px;left:-1px;position:absolute;top:-1px;width:24px;z-index:-1}.tox .tox-color-input span[aria-disabled=true]{cursor:not-allowed}.tox:not([dir=rtl]) .tox-color-input .tox-textfield{padding-left:36px}.tox:not([dir=rtl]) .tox-color-input span{left:6px}.tox[dir=rtl] .tox-color-input .tox-textfield{padding-right:36px}.tox[dir=rtl] .tox-color-input span{right:6px}.tox .tox-label,.tox .tox-toolbar-label{color:rgba(34,47,62,.7);display:block;font-size:14px;font-style:normal;font-weight:400;line-height:1.3;padding:0 8px 0 0;text-transform:none;white-space:nowrap}.tox .tox-toolbar-label{padding:0 8px}.tox[dir=rtl] .tox-label{padding:0 0 0 8px}.tox .tox-form{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-form__group{box-sizing:border-box;margin-bottom:4px}.tox .tox-form-group--maximize{flex:1}.tox .tox-form__group--error{color:#c00}.tox .tox-form__group--collection{display:flex}.tox .tox-form__grid{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between}.tox .tox-form__grid--2col>.tox-form__group{width:calc(50% - (8px / 2))}.tox .tox-form__grid--3col>.tox-form__group{width:calc(100% / 3 - (8px / 2))}.tox .tox-form__grid--4col>.tox-form__group{width:calc(25% - (8px / 2))}.tox .tox-form__controls-h-stack{align-items:center;display:flex}.tox .tox-form__group--inline{align-items:center;display:flex}.tox .tox-form__group--stretched{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-textarea{flex:1;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-navobj{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-navobj :nth-child(2){flex:1;-ms-flex-preferred-size:auto;height:100%}.tox:not([dir=rtl]) .tox-form__controls-h-stack>:not(:first-child){margin-left:4px}.tox[dir=rtl] .tox-form__controls-h-stack>:not(:first-child){margin-right:4px}.tox .tox-lock.tox-locked .tox-lock-icon__unlock,.tox .tox-lock:not(.tox-locked) .tox-lock-icon__lock{display:none}.tox .tox-listboxfield .tox-listbox--select,.tox .tox-textarea,.tox .tox-textfield,.tox .tox-toolbar-textfield{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#ccc;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#222f3e;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;line-height:24px;margin:0;min-height:34px;outline:0;padding:5px 4.75px;resize:none;width:100%}.tox .tox-textarea[disabled],.tox .tox-textfield[disabled]{background-color:#f2f2f2;color:rgba(34,47,62,.85);cursor:not-allowed}.tox .tox-listboxfield .tox-listbox--select:focus,.tox .tox-textarea:focus,.tox .tox-textfield:focus{background-color:#fff;border-color:#207ab7;box-shadow:none;outline:0}.tox .tox-toolbar-textfield{border-width:0;margin-bottom:3px;margin-top:2px;max-width:250px}.tox .tox-naked-btn{background-color:transparent;border:0;border-color:transparent;box-shadow:unset;color:#207ab7;cursor:pointer;display:block;margin:0;padding:0}.tox .tox-naked-btn svg{display:block;fill:#222f3e}.tox:not([dir=rtl]) .tox-toolbar-textfield+*{margin-left:4px}.tox[dir=rtl] .tox-toolbar-textfield+*{margin-right:4px}.tox .tox-listboxfield{cursor:pointer;position:relative}.tox .tox-listboxfield .tox-listbox--select[disabled]{background-color:#f2f2f2;color:rgba(34,47,62,.85);cursor:not-allowed}.tox .tox-listbox__select-label{cursor:default;flex:1;margin:0 4px}.tox .tox-listbox__select-chevron{align-items:center;display:flex;justify-content:center;width:16px}.tox .tox-listbox__select-chevron svg{fill:#222f3e}.tox .tox-listboxfield .tox-listbox--select{align-items:center;display:flex}.tox:not([dir=rtl]) .tox-listboxfield svg{right:8px}.tox[dir=rtl] .tox-listboxfield svg{left:8px}.tox .tox-selectfield{cursor:pointer;position:relative}.tox .tox-selectfield select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#ccc;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#222f3e;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;line-height:24px;margin:0;min-height:34px;outline:0;padding:5px 4.75px;resize:none;width:100%}.tox .tox-selectfield select[disabled]{background-color:#f2f2f2;color:rgba(34,47,62,.85);cursor:not-allowed}.tox .tox-selectfield select::-ms-expand{display:none}.tox .tox-selectfield select:focus{background-color:#fff;border-color:#207ab7;box-shadow:none;outline:0}.tox .tox-selectfield svg{pointer-events:none;position:absolute;top:50%;transform:translateY(-50%)}.tox:not([dir=rtl]) .tox-selectfield select[size="0"],.tox:not([dir=rtl]) .tox-selectfield select[size="1"]{padding-right:24px}.tox:not([dir=rtl]) .tox-selectfield svg{right:8px}.tox[dir=rtl] .tox-selectfield select[size="0"],.tox[dir=rtl] .tox-selectfield select[size="1"]{padding-left:24px}.tox[dir=rtl] .tox-selectfield svg{left:8px}.tox .tox-textarea{-webkit-appearance:textarea;-moz-appearance:textarea;appearance:textarea;white-space:pre-wrap}.tox-fullscreen{border:0;height:100%;left:0;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;position:fixed;top:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox.tox-tinymce.tox-fullscreen{background-color:transparent;z-index:1200}.tox-shadowhost.tox-fullscreen{z-index:1200}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}.tox .tox-help__more-link{list-style:none;margin-top:1em}.tox .tox-image-tools{width:100%}.tox .tox-image-tools__toolbar{align-items:center;display:flex;justify-content:center}.tox .tox-image-tools__image{background-color:#666;height:380px;overflow:auto;position:relative;width:100%}.tox .tox-image-tools__image,.tox .tox-image-tools__image+.tox-image-tools__toolbar{margin-top:8px}.tox .tox-image-tools__image-bg{background:url(data:image/gif;base64,R0lGODdhDAAMAIABAMzMzP///ywAAAAADAAMAAACFoQfqYeabNyDMkBQb81Uat85nxguUAEAOw==)}.tox .tox-image-tools__toolbar>.tox-spacer{flex:1;-ms-flex-preferred-size:auto}.tox .tox-croprect-block{background:#000;opacity:.5;position:absolute;zoom:1}.tox .tox-croprect-handle{border:2px solid #fff;height:20px;left:0;position:absolute;top:0;width:20px}.tox .tox-croprect-handle-move{border:0;cursor:move;position:absolute}.tox .tox-croprect-handle-nw{border-width:2px 0 0 2px;cursor:nw-resize;left:100px;margin:-2px 0 0 -2px;top:100px}.tox .tox-croprect-handle-ne{border-width:2px 2px 0 0;cursor:ne-resize;left:200px;margin:-2px 0 0 -20px;top:100px}.tox .tox-croprect-handle-sw{border-width:0 0 2px 2px;cursor:sw-resize;left:100px;margin:-20px 2px 0 -2px;top:200px}.tox .tox-croprect-handle-se{border-width:0 2px 2px 0;cursor:se-resize;left:200px;margin:-20px 0 0 -20px;top:200px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-slider:not(:first-of-type){margin-left:8px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-button+.tox-slider{margin-left:32px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-slider+.tox-button{margin-left:32px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-slider:not(:first-of-type){margin-right:8px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-button+.tox-slider{margin-right:32px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-slider+.tox-button{margin-right:32px}.tox .tox-insert-table-picker{display:flex;flex-wrap:wrap;width:170px}.tox .tox-insert-table-picker>div{border-color:#ccc;border-style:solid;border-width:0 1px 1px 0;box-sizing:border-box;height:17px;width:17px}.tox .tox-collection--list .tox-collection__group .tox-insert-table-picker{margin:-4px 0}.tox .tox-insert-table-picker .tox-insert-table-picker__selected{background-color:rgba(32,122,183,.5);border-color:rgba(32,122,183,.5)}.tox .tox-insert-table-picker__label{color:rgba(34,47,62,.7);display:block;font-size:14px;padding:4px;text-align:center;width:100%}.tox:not([dir=rtl]) .tox-insert-table-picker>div:nth-child(10n){border-right:0}.tox[dir=rtl] .tox-insert-table-picker>div:nth-child(10n+1){border-right:0}.tox .tox-menu{background-color:#fff;border:1px solid #ccc;border-radius:3px;box-shadow:0 4px 8px 0 rgba(34,47,62,.1);display:inline-block;overflow:hidden;vertical-align:top;z-index:1150}.tox .tox-menu.tox-collection.tox-collection--list{padding:0}.tox .tox-menu.tox-collection.tox-collection--toolbar{padding:4px}.tox .tox-menu.tox-collection.tox-collection--grid{padding:4px}.tox .tox-menu__label blockquote,.tox .tox-menu__label code,.tox .tox-menu__label h1,.tox .tox-menu__label h2,.tox .tox-menu__label h3,.tox .tox-menu__label h4,.tox .tox-menu__label h5,.tox .tox-menu__label h6,.tox .tox-menu__label p{margin:0}.tox .tox-menubar{background:url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23cccccc'/%3E%3C/svg%3E") left 0 top 0 #fff;background-color:#fff;display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:wrap;padding:0 4px 0 4px}.tox.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar{border-top:1px solid #ccc}.tox .tox-mbtn{align-items:center;background:0 0;border:0;border-radius:3px;box-shadow:none;color:#222f3e;display:flex;flex:0 0 auto;font-size:14px;font-style:normal;font-weight:400;height:34px;justify-content:center;margin:2px 0 3px 0;outline:0;overflow:hidden;padding:0 4px;text-transform:none;width:auto}.tox .tox-mbtn[disabled]{background-color:transparent;border:0;box-shadow:none;color:rgba(34,47,62,.5);cursor:not-allowed}.tox .tox-mbtn:focus:not(:disabled){background:#dee0e2;border:0;box-shadow:none;color:#222f3e}.tox .tox-mbtn--active{background:#c8cbcf;border:0;box-shadow:none;color:#222f3e}.tox .tox-mbtn:hover:not(:disabled):not(.tox-mbtn--active){background:#dee0e2;border:0;box-shadow:none;color:#222f3e}.tox .tox-mbtn__select-label{cursor:default;font-weight:400;margin:0 4px}.tox .tox-mbtn[disabled] .tox-mbtn__select-label{cursor:not-allowed}.tox .tox-mbtn__select-chevron{align-items:center;display:flex;justify-content:center;width:16px;display:none}.tox .tox-notification{border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;display:-ms-grid;display:grid;font-size:14px;font-weight:400;-ms-grid-columns:minmax(40px,1fr) auto minmax(40px,1fr);grid-template-columns:minmax(40px,1fr) auto minmax(40px,1fr);margin-top:4px;opacity:0;padding:4px;transition:transform .1s ease-in,opacity 150ms ease-in}.tox .tox-notification p{font-size:14px;font-weight:400}.tox .tox-notification a{text-decoration:underline}.tox .tox-notification--in{opacity:1}.tox .tox-notification--success{background-color:#e4eeda;border-color:#d7e6c8;color:#222f3e}.tox .tox-notification--success p{color:#222f3e}.tox .tox-notification--success a{color:#547831}.tox .tox-notification--success svg{fill:#222f3e}.tox .tox-notification--error{background-color:#f8dede;border-color:#f2bfbf;color:#222f3e}.tox .tox-notification--error p{color:#222f3e}.tox .tox-notification--error a{color:#c00}.tox .tox-notification--error svg{fill:#222f3e}.tox .tox-notification--warn,.tox .tox-notification--warning{background-color:#fffaea;border-color:#ffe89d;color:#222f3e}.tox .tox-notification--warn p,.tox .tox-notification--warning p{color:#222f3e}.tox .tox-notification--warn a,.tox .tox-notification--warning a{color:#222f3e}.tox .tox-notification--warn svg,.tox .tox-notification--warning svg{fill:#222f3e}.tox .tox-notification--info{background-color:#d9edf7;border-color:#779ecb;color:#222f3e}.tox .tox-notification--info p{color:#222f3e}.tox .tox-notification--info a{color:#222f3e}.tox .tox-notification--info svg{fill:#222f3e}.tox .tox-notification__body{-ms-grid-row-align:center;align-self:center;color:#222f3e;font-size:14px;-ms-grid-column-span:1;grid-column-end:3;-ms-grid-column:2;grid-column-start:2;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;text-align:center;white-space:normal;word-break:break-all;word-break:break-word}.tox .tox-notification__body>*{margin:0}.tox .tox-notification__body>*+*{margin-top:1rem}.tox .tox-notification__icon{-ms-grid-row-align:center;align-self:center;-ms-grid-column-span:1;grid-column-end:2;-ms-grid-column:1;grid-column-start:1;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;-ms-grid-column-align:end;justify-self:end}.tox .tox-notification__icon svg{display:block}.tox .tox-notification__dismiss{-ms-grid-row-align:start;align-self:start;-ms-grid-column-span:1;grid-column-end:4;-ms-grid-column:3;grid-column-start:3;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;-ms-grid-column-align:end;justify-self:end}.tox .tox-notification .tox-progress-bar{-ms-grid-column-span:3;grid-column-end:4;-ms-grid-column:1;grid-column-start:1;-ms-grid-row-span:1;grid-row-end:3;-ms-grid-row:2;grid-row-start:2;-ms-grid-column-align:center;justify-self:center}.tox .tox-pop{display:inline-block;position:relative}.tox .tox-pop--resizing{transition:width .1s ease}.tox .tox-pop--resizing .tox-toolbar{flex-wrap:nowrap}.tox .tox-pop__dialog{background-color:#fff;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15);min-width:0;overflow:hidden}.tox .tox-pop__dialog>:not(.tox-toolbar){margin:4px 4px 4px 8px}.tox .tox-pop__dialog .tox-toolbar{background-color:transparent;margin-bottom:-1px}.tox .tox-pop::after,.tox .tox-pop::before{border-style:solid;content:'';display:block;height:0;position:absolute;width:0}.tox .tox-pop.tox-pop--bottom::after,.tox .tox-pop.tox-pop--bottom::before{left:50%;top:100%}.tox .tox-pop.tox-pop--bottom::after{border-color:#fff transparent transparent transparent;border-width:8px;margin-left:-8px;margin-top:-1px}.tox .tox-pop.tox-pop--bottom::before{border-color:#ccc transparent transparent transparent;border-width:9px;margin-left:-9px}.tox .tox-pop.tox-pop--top::after,.tox .tox-pop.tox-pop--top::before{left:50%;top:0;transform:translateY(-100%)}.tox .tox-pop.tox-pop--top::after{border-color:transparent transparent #fff transparent;border-width:8px;margin-left:-8px;margin-top:1px}.tox .tox-pop.tox-pop--top::before{border-color:transparent transparent #ccc transparent;border-width:9px;margin-left:-9px}.tox .tox-pop.tox-pop--left::after,.tox .tox-pop.tox-pop--left::before{left:0;top:calc(50% - 1px);transform:translateY(-50%)}.tox .tox-pop.tox-pop--left::after{border-color:transparent #fff transparent transparent;border-width:8px;margin-left:-15px}.tox .tox-pop.tox-pop--left::before{border-color:transparent #ccc transparent transparent;border-width:10px;margin-left:-19px}.tox .tox-pop.tox-pop--right::after,.tox .tox-pop.tox-pop--right::before{left:100%;top:calc(50% + 1px);transform:translateY(-50%)}.tox .tox-pop.tox-pop--right::after{border-color:transparent transparent transparent #fff;border-width:8px;margin-left:-1px}.tox .tox-pop.tox-pop--right::before{border-color:transparent transparent transparent #ccc;border-width:10px;margin-left:-1px}.tox .tox-pop.tox-pop--align-left::after,.tox .tox-pop.tox-pop--align-left::before{left:20px}.tox .tox-pop.tox-pop--align-right::after,.tox .tox-pop.tox-pop--align-right::before{left:calc(100% - 20px)}.tox .tox-sidebar-wrap{display:flex;flex-direction:row;flex-grow:1;-ms-flex-preferred-size:0;min-height:0}.tox .tox-sidebar{background-color:#fff;display:flex;flex-direction:row;justify-content:flex-end}.tox .tox-sidebar__slider{display:flex;overflow:hidden}.tox .tox-sidebar__pane-container{display:flex}.tox .tox-sidebar__pane{display:flex}.tox .tox-sidebar--sliding-closed{opacity:0}.tox .tox-sidebar--sliding-open{opacity:1}.tox .tox-sidebar--sliding-growing,.tox .tox-sidebar--sliding-shrinking{transition:width .5s ease,opacity .5s ease}.tox .tox-selector{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;display:inline-block;height:10px;position:absolute;width:10px}.tox.tox-platform-touch .tox-selector{height:12px;width:12px}.tox .tox-slider{align-items:center;display:flex;flex:1;-ms-flex-preferred-size:auto;height:24px;justify-content:center;position:relative}.tox .tox-slider__rail{background-color:transparent;border:1px solid #ccc;border-radius:3px;height:10px;min-width:120px;width:100%}.tox .tox-slider__handle{background-color:#207ab7;border:2px solid #185d8c;border-radius:3px;box-shadow:none;height:24px;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%);width:14px}.tox .tox-source-code{overflow:auto}.tox .tox-spinner{display:flex}.tox .tox-spinner>div{animation:tam-bouncing-dots 1.5s ease-in-out 0s infinite both;background-color:rgba(34,47,62,.7);border-radius:100%;height:8px;width:8px}.tox .tox-spinner>div:nth-child(1){animation-delay:-.32s}.tox .tox-spinner>div:nth-child(2){animation-delay:-.16s}@keyframes tam-bouncing-dots{0%,100%,80%{transform:scale(0)}40%{transform:scale(1)}}.tox:not([dir=rtl]) .tox-spinner>div:not(:first-child){margin-left:4px}.tox[dir=rtl] .tox-spinner>div:not(:first-child){margin-right:4px}.tox .tox-statusbar{align-items:center;background-color:#fff;border-top:1px solid #ccc;color:rgba(34,47,62,.7);display:flex;flex:0 0 auto;font-size:12px;font-weight:400;height:18px;overflow:hidden;padding:0 8px;position:relative;text-transform:uppercase}.tox .tox-statusbar__text-container{display:flex;flex:1 1 auto;justify-content:flex-end;overflow:hidden}.tox .tox-statusbar__path{display:flex;flex:1 1 auto;margin-right:auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tox .tox-statusbar__path>*{display:inline;white-space:nowrap}.tox .tox-statusbar__wordcount{flex:0 0 auto;margin-left:1ch}.tox .tox-statusbar a,.tox .tox-statusbar__path-item,.tox .tox-statusbar__wordcount{color:rgba(34,47,62,.7);text-decoration:none}.tox .tox-statusbar a:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar a:hover:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__path-item:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__path-item:hover:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__wordcount:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__wordcount:hover:not(:disabled):not([aria-disabled=true]){cursor:pointer;text-decoration:underline}.tox .tox-statusbar__resize-handle{align-items:flex-end;align-self:stretch;cursor:nwse-resize;display:flex;flex:0 0 auto;justify-content:flex-end;margin-left:auto;margin-right:-8px;padding-left:1ch}.tox .tox-statusbar__resize-handle svg{display:block;fill:rgba(34,47,62,.7)}.tox .tox-statusbar__resize-handle:focus svg{background-color:#dee0e2;border-radius:1px;box-shadow:0 0 0 2px #dee0e2}.tox:not([dir=rtl]) .tox-statusbar__path>*{margin-right:4px}.tox:not([dir=rtl]) .tox-statusbar__branding{margin-left:1ch}.tox[dir=rtl] .tox-statusbar{flex-direction:row-reverse}.tox[dir=rtl] .tox-statusbar__path>*{margin-left:4px}.tox .tox-throbber{z-index:1299}.tox .tox-throbber__busy-spinner{align-items:center;background-color:rgba(255,255,255,.6);bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0}.tox .tox-tbtn{align-items:center;background:0 0;border:0;border-radius:3px;box-shadow:none;color:#222f3e;display:flex;flex:0 0 auto;font-size:14px;font-style:normal;font-weight:400;height:34px;justify-content:center;margin:2px 0 3px 0;outline:0;overflow:hidden;padding:0;text-transform:none;width:34px}.tox .tox-tbtn svg{display:block;fill:#222f3e}.tox .tox-tbtn.tox-tbtn-more{padding-left:5px;padding-right:5px;width:inherit}.tox .tox-tbtn:focus{background:#dee0e2;border:0;box-shadow:none}.tox .tox-tbtn:hover{background:#dee0e2;border:0;box-shadow:none;color:#222f3e}.tox .tox-tbtn:hover svg{fill:#222f3e}.tox .tox-tbtn:active{background:#c8cbcf;border:0;box-shadow:none;color:#222f3e}.tox .tox-tbtn:active svg{fill:#222f3e}.tox .tox-tbtn--disabled,.tox .tox-tbtn--disabled:hover,.tox .tox-tbtn:disabled,.tox .tox-tbtn:disabled:hover{background:0 0;border:0;box-shadow:none;color:rgba(34,47,62,.5);cursor:not-allowed}.tox .tox-tbtn--disabled svg,.tox .tox-tbtn--disabled:hover svg,.tox .tox-tbtn:disabled svg,.tox .tox-tbtn:disabled:hover svg{fill:rgba(34,47,62,.5)}.tox .tox-tbtn--enabled,.tox .tox-tbtn--enabled:hover{background:#c8cbcf;border:0;box-shadow:none;color:#222f3e}.tox .tox-tbtn--enabled:hover>*,.tox .tox-tbtn--enabled>*{transform:none}.tox .tox-tbtn--enabled svg,.tox .tox-tbtn--enabled:hover svg{fill:#222f3e}.tox .tox-tbtn:focus:not(.tox-tbtn--disabled){color:#222f3e}.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) svg{fill:#222f3e}.tox .tox-tbtn:active>*{transform:none}.tox .tox-tbtn--md{height:51px;width:51px}.tox .tox-tbtn--lg{flex-direction:column;height:68px;width:68px}.tox .tox-tbtn--return{-ms-grid-row-align:stretch;align-self:stretch;height:unset;width:16px}.tox .tox-tbtn--labeled{padding:0 4px;width:unset}.tox .tox-tbtn__vlabel{display:block;font-size:10px;font-weight:400;letter-spacing:-.025em;margin-bottom:4px;white-space:nowrap}.tox .tox-tbtn--select{margin:2px 0 3px 0;padding:0 4px;width:auto}.tox .tox-tbtn__select-label{cursor:default;font-weight:400;margin:0 4px}.tox .tox-tbtn__select-chevron{align-items:center;display:flex;justify-content:center;width:16px}.tox .tox-tbtn__select-chevron svg{fill:rgba(34,47,62,.5)}.tox .tox-tbtn--bespoke .tox-tbtn__select-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:7em}.tox .tox-split-button{border:0;border-radius:3px;box-sizing:border-box;display:flex;margin:2px 0 3px 0;overflow:hidden}.tox .tox-split-button:hover{box-shadow:0 0 0 1px #dee0e2 inset}.tox .tox-split-button:focus{background:#dee0e2;box-shadow:none;color:#222f3e}.tox .tox-split-button>*{border-radius:0}.tox .tox-split-button__chevron{width:16px}.tox .tox-split-button__chevron svg{fill:rgba(34,47,62,.5)}.tox .tox-split-button .tox-tbtn{margin:0}.tox.tox-platform-touch .tox-split-button .tox-tbtn:first-child{width:30px}.tox.tox-platform-touch .tox-split-button__chevron{width:20px}.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:focus,.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:hover,.tox .tox-split-button.tox-tbtn--disabled:focus,.tox .tox-split-button.tox-tbtn--disabled:hover{background:0 0;box-shadow:none;color:rgba(34,47,62,.5)}.tox .tox-toolbar-overlord{background-color:#fff}.tox .tox-toolbar,.tox .tox-toolbar__overflow,.tox .tox-toolbar__primary{background:url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23cccccc'/%3E%3C/svg%3E") left 0 top 0 #fff;background-color:#fff;display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:wrap;padding:0 0}.tox .tox-toolbar__overflow.tox-toolbar__overflow--closed{height:0;opacity:0;padding-bottom:0;padding-top:0;visibility:hidden}.tox .tox-toolbar__overflow--growing{transition:height .3s ease,opacity .2s linear .1s}.tox .tox-toolbar__overflow--shrinking{transition:opacity .3s ease,height .2s linear .1s,visibility 0s linear .3s}.tox .tox-menubar+.tox-toolbar,.tox .tox-menubar+.tox-toolbar-overlord .tox-toolbar__primary{border-top:1px solid #ccc;margin-top:-1px}.tox .tox-toolbar--scrolling{flex-wrap:nowrap;overflow-x:auto}.tox .tox-pop .tox-toolbar{border-width:0}.tox .tox-toolbar--no-divider{background-image:none}.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar-overlord:first-child .tox-toolbar__primary,.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar:first-child{border-top:1px solid #ccc}.tox.tox-tinymce-aux .tox-toolbar__overflow{background-color:#fff;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15)}.tox[dir=rtl] .tox-tbtn__icon-rtl svg{transform:rotateY(180deg)}.tox .tox-toolbar__group{align-items:center;display:flex;flex-wrap:wrap;margin:0 0;padding:0 4px 0 4px}.tox .tox-toolbar__group--pull-right{margin-left:auto}.tox .tox-toolbar--scrolling .tox-toolbar__group{flex-shrink:0;flex-wrap:nowrap}.tox:not([dir=rtl]) .tox-toolbar__group:not(:last-of-type){border-right:1px solid #ccc}.tox[dir=rtl] .tox-toolbar__group:not(:last-of-type){border-left:1px solid #ccc}.tox .tox-tooltip{display:inline-block;padding:8px;position:relative}.tox .tox-tooltip__body{background-color:#222f3e;border-radius:3px;box-shadow:0 2px 4px rgba(34,47,62,.3);color:rgba(255,255,255,.75);font-size:14px;font-style:normal;font-weight:400;padding:4px 8px;text-transform:none}.tox .tox-tooltip__arrow{position:absolute}.tox .tox-tooltip--down .tox-tooltip__arrow{border-left:8px solid transparent;border-right:8px solid transparent;border-top:8px solid #222f3e;bottom:0;left:50%;position:absolute;transform:translateX(-50%)}.tox .tox-tooltip--up .tox-tooltip__arrow{border-bottom:8px solid #222f3e;border-left:8px solid transparent;border-right:8px solid transparent;left:50%;position:absolute;top:0;transform:translateX(-50%)}.tox .tox-tooltip--right .tox-tooltip__arrow{border-bottom:8px solid transparent;border-left:8px solid #222f3e;border-top:8px solid transparent;position:absolute;right:0;top:50%;transform:translateY(-50%)}.tox .tox-tooltip--left .tox-tooltip__arrow{border-bottom:8px solid transparent;border-right:8px solid #222f3e;border-top:8px solid transparent;left:0;position:absolute;top:50%;transform:translateY(-50%)}.tox .tox-well{border:1px solid #ccc;border-radius:3px;padding:8px;width:100%}.tox .tox-well>:first-child{margin-top:0}.tox .tox-well>:last-child{margin-bottom:0}.tox .tox-well>:only-child{margin:0}.tox .tox-custom-editor{border:1px solid #ccc;border-radius:3px;display:flex;flex:1;position:relative}.tox .tox-dialog-loading::before{background-color:rgba(0,0,0,.5);content:"";height:100%;position:absolute;width:100%;z-index:1000}.tox .tox-tab{cursor:pointer}.tox .tox-dialog__content-js{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-content .tox-collection{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-image-tools-edit-panel{height:60px}.tox .tox-image-tools__sidebar{height:60px} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.mobile.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.mobile.css new file mode 100644 index 0000000..efcd1bb --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.mobile.css @@ -0,0 +1,798 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +/* RESET all the things! */ +.tinymce-mobile-outer-container { + all: initial; + display: block; +} + +.tinymce-mobile-outer-container * { + border: 0; + box-sizing: initial; + cursor: inherit; + float: none; + line-height: 1; + margin: 0; + outline: 0; + padding: 0; + -webkit-tap-highlight-color: transparent; + /* TBIO-3691, stop the gray flicker on touch. */ + text-shadow: none; + white-space: nowrap; +} + +.tinymce-mobile-icon-arrow-back::before { + content: "\e5cd"; +} + +.tinymce-mobile-icon-image::before { + content: "\e412"; +} + +.tinymce-mobile-icon-cancel-circle::before { + content: "\e5c9"; +} + +.tinymce-mobile-icon-full-dot::before { + content: "\e061"; +} + +.tinymce-mobile-icon-align-center::before { + content: "\e234"; +} + +.tinymce-mobile-icon-align-left::before { + content: "\e236"; +} + +.tinymce-mobile-icon-align-right::before { + content: "\e237"; +} + +.tinymce-mobile-icon-bold::before { + content: "\e238"; +} + +.tinymce-mobile-icon-italic::before { + content: "\e23f"; +} + +.tinymce-mobile-icon-unordered-list::before { + content: "\e241"; +} + +.tinymce-mobile-icon-ordered-list::before { + content: "\e242"; +} + +.tinymce-mobile-icon-font-size::before { + content: "\e245"; +} + +.tinymce-mobile-icon-underline::before { + content: "\e249"; +} + +.tinymce-mobile-icon-link::before { + content: "\e157"; +} + +.tinymce-mobile-icon-unlink::before { + content: "\eca2"; +} + +.tinymce-mobile-icon-color::before { + content: "\e891"; +} + +.tinymce-mobile-icon-previous::before { + content: "\e314"; +} + +.tinymce-mobile-icon-next::before { + content: "\e315"; +} + +.tinymce-mobile-icon-large-font::before, +.tinymce-mobile-icon-style-formats::before { + content: "\e264"; +} + +.tinymce-mobile-icon-undo::before { + content: "\e166"; +} + +.tinymce-mobile-icon-redo::before { + content: "\e15a"; +} + +.tinymce-mobile-icon-removeformat::before { + content: "\e239"; +} + +.tinymce-mobile-icon-small-font::before { + content: "\e906"; +} + +.tinymce-mobile-icon-readonly-back::before, +.tinymce-mobile-format-matches::after { + content: "\e5ca"; +} + +.tinymce-mobile-icon-small-heading::before { + content: "small"; +} + +.tinymce-mobile-icon-large-heading::before { + content: "large"; +} + +.tinymce-mobile-icon-small-heading::before, +.tinymce-mobile-icon-large-heading::before { + font-family: sans-serif; + font-size: 80%; +} + +.tinymce-mobile-mask-edit-icon::before { + content: "\e254"; +} + +.tinymce-mobile-icon-back::before { + content: "\e5c4"; +} + +.tinymce-mobile-icon-heading::before { + /* TODO: Translate */ + content: "Headings"; + font-family: sans-serif; + font-size: 80%; + font-weight: bold; +} + +.tinymce-mobile-icon-h1::before { + content: "H1"; + font-weight: bold; +} + +.tinymce-mobile-icon-h2::before { + content: "H2"; + font-weight: bold; +} + +.tinymce-mobile-icon-h3::before { + content: "H3"; + font-weight: bold; +} + +.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask { + align-items: center; + display: flex; + justify-content: center; + background: rgba(51, 51, 51, 0.5); + height: 100%; + position: absolute; + top: 0; + width: 100%; +} + +.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container { + align-items: center; + border-radius: 50%; + display: flex; + flex-direction: column; + font-family: sans-serif; + font-size: 1em; + justify-content: space-between; +} + +.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .mixin-menu-item { + align-items: center; + display: flex; + justify-content: center; + border-radius: 50%; + height: 2.1em; + width: 2.1em; +} + +.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section { + align-items: center; + display: flex; + justify-content: center; + flex-direction: column; + font-size: 1em; +} + +@media only screen and (min-device-width: 700px) { + .tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section { + font-size: 1.2em; + } +} + +.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon { + align-items: center; + display: flex; + justify-content: center; + border-radius: 50%; + height: 2.1em; + width: 2.1em; + background-color: white; + color: #207ab7; +} + +.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon::before { + content: "\e900"; + font-family: 'tinymce-mobile', sans-serif; +} + +.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section:not(.tinymce-mobile-mask-tap-icon-selected) .tinymce-mobile-mask-tap-icon { + z-index: 2; +} + +.tinymce-mobile-android-container.tinymce-mobile-android-maximized { + background: #ffffff; + border: none; + bottom: 0; + display: flex; + flex-direction: column; + left: 0; + position: fixed; + right: 0; + top: 0; +} + +.tinymce-mobile-android-container:not(.tinymce-mobile-android-maximized) { + position: relative; +} + +.tinymce-mobile-android-container .tinymce-mobile-editor-socket { + display: flex; + flex-grow: 1; +} + +.tinymce-mobile-android-container .tinymce-mobile-editor-socket iframe { + display: flex !important; + flex-grow: 1; + height: auto !important; +} + +.tinymce-mobile-android-scroll-reload { + overflow: hidden; +} + +:not(.tinymce-mobile-readonly-mode) > .tinymce-mobile-android-selection-context-toolbar { + margin-top: 23px; +} + +.tinymce-mobile-toolstrip { + background: #fff; + display: flex; + flex: 0 0 auto; + z-index: 1; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar { + align-items: center; + background-color: #fff; + border-bottom: 1px solid #cccccc; + display: flex; + flex: 1; + height: 2.5em; + width: 100%; + /* Make it no larger than the toolstrip, so that it needs to scroll */ +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group { + align-items: center; + display: flex; + height: 100%; + flex-shrink: 1; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group > div { + align-items: center; + display: flex; + height: 100%; + flex: 1; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-exit-container { + background: #f44336; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-toolbar-scrollable-group { + flex-grow: 1; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item { + padding-left: 0.5em; + padding-right: 0.5em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button { + align-items: center; + display: flex; + height: 80%; + margin-left: 2px; + margin-right: 2px; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button.tinymce-mobile-toolbar-button-selected { + background: #c8cbcf; + color: #cccccc; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:first-of-type, +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:last-of-type { + background: #207ab7; + color: #eceff1; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar { + /* Note, this file is imported inside .tinymce-mobile-context-toolbar, so that prefix is on everything here. */ +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group { + align-items: center; + display: flex; + height: 100%; + flex: 1; + padding-bottom: 0.4em; + padding-top: 0.4em; + /* Make any buttons appearing on the left and right display in the centre (e.g. color edges) */ + /* For widgets like the colour picker, use the whole height */ +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog { + display: flex; + min-height: 1.5em; + overflow: hidden; + padding-left: 0; + padding-right: 0; + position: relative; + width: 100%; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain { + display: flex; + height: 100%; + transition: left cubic-bezier(0.4, 0, 1, 1) 0.15s; + width: 100%; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen { + display: flex; + flex: 0 0 auto; + justify-content: space-between; + width: 100%; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen input { + font-family: Sans-serif; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container { + display: flex; + flex-grow: 1; + position: relative; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container .tinymce-mobile-input-container-x { + -ms-grid-row-align: center; + align-self: center; + background: inherit; + border: none; + border-radius: 50%; + color: #888; + font-size: 0.6em; + font-weight: bold; + height: 100%; + padding-right: 2px; + position: absolute; + right: 0; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container.tinymce-mobile-input-container-empty .tinymce-mobile-input-container-x { + display: none; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous, +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next { + align-items: center; + display: flex; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous::before, +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next::before { + align-items: center; + display: flex; + font-weight: bold; + height: 100%; + padding-left: 0.5em; + padding-right: 0.5em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous.tinymce-mobile-toolbar-navigation-disabled::before, +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next.tinymce-mobile-toolbar-navigation-disabled::before { + visibility: hidden; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item { + color: #cccccc; + font-size: 10px; + line-height: 10px; + margin: 0 2px; + padding-top: 3px; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item.tinymce-mobile-dot-active { + color: #c8cbcf; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-font::before, +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-heading::before { + margin-left: 0.5em; + margin-right: 0.9em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-font::before, +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-heading::before { + margin-left: 0.9em; + margin-right: 0.5em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider { + display: flex; + flex: 1; + margin-left: 0; + margin-right: 0; + padding: 0.28em 0; + position: relative; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container { + align-items: center; + display: flex; + flex-grow: 1; + height: 100%; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container .tinymce-mobile-slider-size-line { + background: #cccccc; + display: flex; + flex: 1; + height: 0.2em; + margin-bottom: 0.3em; + margin-top: 0.3em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container { + padding-left: 2em; + padding-right: 2em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container { + align-items: center; + display: flex; + flex-grow: 1; + height: 100%; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container .tinymce-mobile-slider-gradient { + background: linear-gradient(to right, hsl(0, 100%, 50%) 0%, hsl(60, 100%, 50%) 17%, hsl(120, 100%, 50%) 33%, hsl(180, 100%, 50%) 50%, hsl(240, 100%, 50%) 67%, hsl(300, 100%, 50%) 83%, hsl(0, 100%, 50%) 100%); + display: flex; + flex: 1; + height: 0.2em; + margin-bottom: 0.3em; + margin-top: 0.3em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-black { + /* Not part of theming */ + background: black; + height: 0.2em; + margin-bottom: 0.3em; + margin-top: 0.3em; + width: 1.2em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-white { + /* Not part of theming */ + background: white; + height: 0.2em; + margin-bottom: 0.3em; + margin-top: 0.3em; + width: 1.2em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb { + /* vertically centering trick (margin: auto, top: 0, bottom: 0). On iOS and Safari, if you leave + * out these values, then it shows the thumb at the top of the spectrum. This is probably because it is + * absolutely positioned with only a left value, and not a top. Note, on Chrome it seems to be fine without + * this approach. + */ + align-items: center; + background-clip: padding-box; + background-color: #455a64; + border: 0.5em solid rgba(136, 136, 136, 0); + border-radius: 3em; + bottom: 0; + color: #fff; + display: flex; + height: 0.5em; + justify-content: center; + left: -10px; + margin: auto; + position: absolute; + top: 0; + transition: border 120ms cubic-bezier(0.39, 0.58, 0.57, 1); + width: 0.5em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb.tinymce-mobile-thumb-active { + border: 0.5em solid rgba(136, 136, 136, 0.39); +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper, +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group > div { + align-items: center; + display: flex; + height: 100%; + flex: 1; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper { + flex-direction: column; + justify-content: center; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item { + align-items: center; + display: flex; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item:not(.tinymce-mobile-serialised-dialog) { + height: 100%; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-container { + display: flex; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input { + background: #ffffff; + border: none; + border-radius: 0; + color: #455a64; + flex-grow: 1; + font-size: 0.85em; + padding-bottom: 0.1em; + padding-left: 5px; + padding-top: 0.1em; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::-webkit-input-placeholder { + /* WebKit, Blink, Edge */ + color: #888; +} + +.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::placeholder { + /* WebKit, Blink, Edge */ + color: #888; +} + +/* dropup */ +.tinymce-mobile-dropup { + background: white; + display: flex; + overflow: hidden; + width: 100%; +} + +.tinymce-mobile-dropup.tinymce-mobile-dropup-shrinking { + transition: height 0.3s ease-out; +} + +.tinymce-mobile-dropup.tinymce-mobile-dropup-growing { + transition: height 0.3s ease-in; +} + +.tinymce-mobile-dropup.tinymce-mobile-dropup-closed { + flex-grow: 0; +} + +.tinymce-mobile-dropup.tinymce-mobile-dropup-open:not(.tinymce-mobile-dropup-growing) { + flex-grow: 1; +} + +/* TODO min-height for device size and orientation */ +.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed) { + min-height: 200px; +} + +@media only screen and (orientation: landscape) { + .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed) { + min-height: 200px; + } +} + +@media only screen and (min-device-width: 320px) and (max-device-width: 568px) and (orientation: landscape) { + .tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed) { + min-height: 150px; + } +} + +/* styles menu */ +.tinymce-mobile-styles-menu { + font-family: sans-serif; + outline: 4px solid black; + overflow: hidden; + position: relative; + width: 100%; +} + +.tinymce-mobile-styles-menu [role="menu"] { + display: flex; + flex-direction: column; + height: 100%; + position: absolute; + width: 100%; +} + +.tinymce-mobile-styles-menu [role="menu"].transitioning { + transition: transform 0.5s ease-in-out; +} + +.tinymce-mobile-styles-menu .tinymce-mobile-styles-item { + border-bottom: 1px solid #ddd; + color: #455a64; + cursor: pointer; + display: flex; + padding: 1em 1em; + position: relative; +} + +.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser .tinymce-mobile-styles-collapse-icon::before { + color: #455a64; + content: "\e314"; + font-family: 'tinymce-mobile', sans-serif; +} + +.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-styles-item-is-menu::after { + color: #455a64; + content: "\e315"; + font-family: 'tinymce-mobile', sans-serif; + padding-left: 1em; + padding-right: 1em; + position: absolute; + right: 0; +} + +.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-format-matches::after { + font-family: 'tinymce-mobile', sans-serif; + padding-left: 1em; + padding-right: 1em; + position: absolute; + right: 0; +} + +.tinymce-mobile-styles-menu .tinymce-mobile-styles-separator, +.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser { + align-items: center; + background: #fff; + border-top: #455a64; + color: #455a64; + display: flex; + min-height: 2.5em; + padding-left: 1em; + padding-right: 1em; +} + +.tinymce-mobile-styles-menu [data-transitioning-destination="before"][data-transitioning-state], +.tinymce-mobile-styles-menu [data-transitioning-state="before"] { + transform: translate(-100%); +} + +.tinymce-mobile-styles-menu [data-transitioning-destination="current"][data-transitioning-state], +.tinymce-mobile-styles-menu [data-transitioning-state="current"] { + transform: translate(0%); +} + +.tinymce-mobile-styles-menu [data-transitioning-destination="after"][data-transitioning-state], +.tinymce-mobile-styles-menu [data-transitioning-state="after"] { + transform: translate(100%); +} + +@font-face { + font-family: 'tinymce-mobile'; + font-style: normal; + font-weight: normal; + src: url('fonts/tinymce-mobile.woff?8x92w3') format('woff'); +} + +@media (min-device-width: 700px) { + .tinymce-mobile-outer-container, + .tinymce-mobile-outer-container input { + font-size: 25px; + } +} + +@media (max-device-width: 700px) { + .tinymce-mobile-outer-container, + .tinymce-mobile-outer-container input { + font-size: 18px; + } +} + +.tinymce-mobile-icon { + font-family: 'tinymce-mobile', sans-serif; +} + +.mixin-flex-and-centre { + align-items: center; + display: flex; + justify-content: center; +} + +.mixin-flex-bar { + align-items: center; + display: flex; + height: 100%; +} + +.tinymce-mobile-outer-container .tinymce-mobile-editor-socket iframe { + background-color: #fff; + width: 100%; +} + +.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon { + /* Note, on the iPod touch in landscape, this isn't visible when the navbar appears */ + background-color: #207ab7; + border-radius: 50%; + bottom: 1em; + color: white; + font-size: 1em; + height: 2.1em; + position: fixed; + right: 2em; + width: 2.1em; + align-items: center; + display: flex; + justify-content: center; +} + +@media only screen and (min-device-width: 700px) { + .tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon { + font-size: 1.2em; + } +} + +.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket { + height: 300px; + overflow: hidden; +} + +.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket iframe { + height: 100%; +} + +.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-toolstrip { + display: none; +} + +/* + Note, that if you don't include this (::-webkit-file-upload-button), the toolbar width gets + increased and the whole body becomes scrollable. It's important! + */ +input[type="file"]::-webkit-file-upload-button { + display: none; +} + +@media only screen and (min-device-width: 320px) and (max-device-width: 568px) and (orientation: landscape) { + .tinymce-mobile-ios-container .tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon { + bottom: 50%; + } +} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.mobile.min.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.mobile.min.css new file mode 100644 index 0000000..3a45cac --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.mobile.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +.tinymce-mobile-outer-container{all:initial;display:block}.tinymce-mobile-outer-container *{border:0;box-sizing:initial;cursor:inherit;float:none;line-height:1;margin:0;outline:0;padding:0;-webkit-tap-highlight-color:transparent;text-shadow:none;white-space:nowrap}.tinymce-mobile-icon-arrow-back::before{content:"\e5cd"}.tinymce-mobile-icon-image::before{content:"\e412"}.tinymce-mobile-icon-cancel-circle::before{content:"\e5c9"}.tinymce-mobile-icon-full-dot::before{content:"\e061"}.tinymce-mobile-icon-align-center::before{content:"\e234"}.tinymce-mobile-icon-align-left::before{content:"\e236"}.tinymce-mobile-icon-align-right::before{content:"\e237"}.tinymce-mobile-icon-bold::before{content:"\e238"}.tinymce-mobile-icon-italic::before{content:"\e23f"}.tinymce-mobile-icon-unordered-list::before{content:"\e241"}.tinymce-mobile-icon-ordered-list::before{content:"\e242"}.tinymce-mobile-icon-font-size::before{content:"\e245"}.tinymce-mobile-icon-underline::before{content:"\e249"}.tinymce-mobile-icon-link::before{content:"\e157"}.tinymce-mobile-icon-unlink::before{content:"\eca2"}.tinymce-mobile-icon-color::before{content:"\e891"}.tinymce-mobile-icon-previous::before{content:"\e314"}.tinymce-mobile-icon-next::before{content:"\e315"}.tinymce-mobile-icon-large-font::before,.tinymce-mobile-icon-style-formats::before{content:"\e264"}.tinymce-mobile-icon-undo::before{content:"\e166"}.tinymce-mobile-icon-redo::before{content:"\e15a"}.tinymce-mobile-icon-removeformat::before{content:"\e239"}.tinymce-mobile-icon-small-font::before{content:"\e906"}.tinymce-mobile-format-matches::after,.tinymce-mobile-icon-readonly-back::before{content:"\e5ca"}.tinymce-mobile-icon-small-heading::before{content:"small"}.tinymce-mobile-icon-large-heading::before{content:"large"}.tinymce-mobile-icon-large-heading::before,.tinymce-mobile-icon-small-heading::before{font-family:sans-serif;font-size:80%}.tinymce-mobile-mask-edit-icon::before{content:"\e254"}.tinymce-mobile-icon-back::before{content:"\e5c4"}.tinymce-mobile-icon-heading::before{content:"Headings";font-family:sans-serif;font-size:80%;font-weight:700}.tinymce-mobile-icon-h1::before{content:"H1";font-weight:700}.tinymce-mobile-icon-h2::before{content:"H2";font-weight:700}.tinymce-mobile-icon-h3::before{content:"H3";font-weight:700}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask{align-items:center;display:flex;justify-content:center;background:rgba(51,51,51,.5);height:100%;position:absolute;top:0;width:100%}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container{align-items:center;border-radius:50%;display:flex;flex-direction:column;font-family:sans-serif;font-size:1em;justify-content:space-between}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .mixin-menu-item{align-items:center;display:flex;justify-content:center;border-radius:50%;height:2.1em;width:2.1em}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section{align-items:center;display:flex;justify-content:center;flex-direction:column;font-size:1em}@media only screen and (min-device-width:700px){.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section{font-size:1.2em}}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon{align-items:center;display:flex;justify-content:center;border-radius:50%;height:2.1em;width:2.1em;background-color:#fff;color:#207ab7}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon::before{content:"\e900";font-family:tinymce-mobile,sans-serif}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section:not(.tinymce-mobile-mask-tap-icon-selected) .tinymce-mobile-mask-tap-icon{z-index:2}.tinymce-mobile-android-container.tinymce-mobile-android-maximized{background:#fff;border:none;bottom:0;display:flex;flex-direction:column;left:0;position:fixed;right:0;top:0}.tinymce-mobile-android-container:not(.tinymce-mobile-android-maximized){position:relative}.tinymce-mobile-android-container .tinymce-mobile-editor-socket{display:flex;flex-grow:1}.tinymce-mobile-android-container .tinymce-mobile-editor-socket iframe{display:flex!important;flex-grow:1;height:auto!important}.tinymce-mobile-android-scroll-reload{overflow:hidden}:not(.tinymce-mobile-readonly-mode)>.tinymce-mobile-android-selection-context-toolbar{margin-top:23px}.tinymce-mobile-toolstrip{background:#fff;display:flex;flex:0 0 auto;z-index:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar{align-items:center;background-color:#fff;border-bottom:1px solid #ccc;display:flex;flex:1;height:2.5em;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group{align-items:center;display:flex;height:100%;flex-shrink:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group>div{align-items:center;display:flex;height:100%;flex:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-exit-container{background:#f44336}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-toolbar-scrollable-group{flex-grow:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item{padding-left:.5em;padding-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button{align-items:center;display:flex;height:80%;margin-left:2px;margin-right:2px}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button.tinymce-mobile-toolbar-button-selected{background:#c8cbcf;color:#ccc}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:first-of-type,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:last-of-type{background:#207ab7;color:#eceff1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group{align-items:center;display:flex;height:100%;flex:1;padding-bottom:.4em;padding-top:.4em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog{display:flex;min-height:1.5em;overflow:hidden;padding-left:0;padding-right:0;position:relative;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain{display:flex;height:100%;transition:left cubic-bezier(.4,0,1,1) .15s;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen{display:flex;flex:0 0 auto;justify-content:space-between;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen input{font-family:Sans-serif}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container{display:flex;flex-grow:1;position:relative}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container .tinymce-mobile-input-container-x{-ms-grid-row-align:center;align-self:center;background:inherit;border:none;border-radius:50%;color:#888;font-size:.6em;font-weight:700;height:100%;padding-right:2px;position:absolute;right:0}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container.tinymce-mobile-input-container-empty .tinymce-mobile-input-container-x{display:none}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous{align-items:center;display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous::before{align-items:center;display:flex;font-weight:700;height:100%;padding-left:.5em;padding-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next.tinymce-mobile-toolbar-navigation-disabled::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous.tinymce-mobile-toolbar-navigation-disabled::before{visibility:hidden}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item{color:#ccc;font-size:10px;line-height:10px;margin:0 2px;padding-top:3px}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item.tinymce-mobile-dot-active{color:#c8cbcf}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-font::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-heading::before{margin-left:.5em;margin-right:.9em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-font::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-heading::before{margin-left:.9em;margin-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider{display:flex;flex:1;margin-left:0;margin-right:0;padding:.28em 0;position:relative}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container{align-items:center;display:flex;flex-grow:1;height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container .tinymce-mobile-slider-size-line{background:#ccc;display:flex;flex:1;height:.2em;margin-bottom:.3em;margin-top:.3em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container{padding-left:2em;padding-right:2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container{align-items:center;display:flex;flex-grow:1;height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container .tinymce-mobile-slider-gradient{background:linear-gradient(to right,red 0,#feff00 17%,#0f0 33%,#00feff 50%,#00f 67%,#ff00fe 83%,red 100%);display:flex;flex:1;height:.2em;margin-bottom:.3em;margin-top:.3em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-black{background:#000;height:.2em;margin-bottom:.3em;margin-top:.3em;width:1.2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-white{background:#fff;height:.2em;margin-bottom:.3em;margin-top:.3em;width:1.2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb{align-items:center;background-clip:padding-box;background-color:#455a64;border:.5em solid rgba(136,136,136,0);border-radius:3em;bottom:0;color:#fff;display:flex;height:.5em;justify-content:center;left:-10px;margin:auto;position:absolute;top:0;transition:border 120ms cubic-bezier(.39,.58,.57,1);width:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb.tinymce-mobile-thumb-active{border:.5em solid rgba(136,136,136,.39)}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group>div{align-items:center;display:flex;height:100%;flex:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper{flex-direction:column;justify-content:center}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item{align-items:center;display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item:not(.tinymce-mobile-serialised-dialog){height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-container{display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input{background:#fff;border:none;border-radius:0;color:#455a64;flex-grow:1;font-size:.85em;padding-bottom:.1em;padding-left:5px;padding-top:.1em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::-webkit-input-placeholder{color:#888}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::placeholder{color:#888}.tinymce-mobile-dropup{background:#fff;display:flex;overflow:hidden;width:100%}.tinymce-mobile-dropup.tinymce-mobile-dropup-shrinking{transition:height .3s ease-out}.tinymce-mobile-dropup.tinymce-mobile-dropup-growing{transition:height .3s ease-in}.tinymce-mobile-dropup.tinymce-mobile-dropup-closed{flex-grow:0}.tinymce-mobile-dropup.tinymce-mobile-dropup-open:not(.tinymce-mobile-dropup-growing){flex-grow:1}.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:200px}@media only screen and (orientation:landscape){.tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:200px}}@media only screen and (min-device-width :320px) and (max-device-width :568px) and (orientation :landscape){.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:150px}}.tinymce-mobile-styles-menu{font-family:sans-serif;outline:4px solid #000;overflow:hidden;position:relative;width:100%}.tinymce-mobile-styles-menu [role=menu]{display:flex;flex-direction:column;height:100%;position:absolute;width:100%}.tinymce-mobile-styles-menu [role=menu].transitioning{transition:transform .5s ease-in-out}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item{border-bottom:1px solid #ddd;color:#455a64;cursor:pointer;display:flex;padding:1em 1em;position:relative}.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser .tinymce-mobile-styles-collapse-icon::before{color:#455a64;content:"\e314";font-family:tinymce-mobile,sans-serif}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-styles-item-is-menu::after{color:#455a64;content:"\e315";font-family:tinymce-mobile,sans-serif;padding-left:1em;padding-right:1em;position:absolute;right:0}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-format-matches::after{font-family:tinymce-mobile,sans-serif;padding-left:1em;padding-right:1em;position:absolute;right:0}.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser,.tinymce-mobile-styles-menu .tinymce-mobile-styles-separator{align-items:center;background:#fff;border-top:#455a64;color:#455a64;display:flex;min-height:2.5em;padding-left:1em;padding-right:1em}.tinymce-mobile-styles-menu [data-transitioning-destination=before][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=before]{transform:translate(-100%)}.tinymce-mobile-styles-menu [data-transitioning-destination=current][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=current]{transform:translate(0)}.tinymce-mobile-styles-menu [data-transitioning-destination=after][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=after]{transform:translate(100%)}@font-face{font-family:tinymce-mobile;font-style:normal;font-weight:400;src:url(fonts/tinymce-mobile.woff?8x92w3) format('woff')}@media (min-device-width:700px){.tinymce-mobile-outer-container,.tinymce-mobile-outer-container input{font-size:25px}}@media (max-device-width:700px){.tinymce-mobile-outer-container,.tinymce-mobile-outer-container input{font-size:18px}}.tinymce-mobile-icon{font-family:tinymce-mobile,sans-serif}.mixin-flex-and-centre{align-items:center;display:flex;justify-content:center}.mixin-flex-bar{align-items:center;display:flex;height:100%}.tinymce-mobile-outer-container .tinymce-mobile-editor-socket iframe{background-color:#fff;width:100%}.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{background-color:#207ab7;border-radius:50%;bottom:1em;color:#fff;font-size:1em;height:2.1em;position:fixed;right:2em;width:2.1em;align-items:center;display:flex;justify-content:center}@media only screen and (min-device-width:700px){.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{font-size:1.2em}}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket{height:300px;overflow:hidden}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket iframe{height:100%}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-toolstrip{display:none}input[type=file]::-webkit-file-upload-button{display:none}@media only screen and (min-device-width :320px) and (max-device-width :568px) and (orientation :landscape){.tinymce-mobile-ios-container .tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{bottom:50%}} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.shadowdom.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.shadowdom.css new file mode 100644 index 0000000..16f4d30 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.shadowdom.css @@ -0,0 +1,42 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +body.tox-dialog__disable-scroll { + overflow: hidden; +} + +.tox-fullscreen { + border: 0; + height: 100%; + left: 0; + margin: 0; + overflow: hidden; + -ms-scroll-chaining: none; + overscroll-behavior: none; + padding: 0; + position: fixed; + top: 0; + touch-action: pinch-zoom; + width: 100%; +} + +.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle { + display: none; +} + +.tox.tox-tinymce.tox-fullscreen { + background-color: transparent; + z-index: 1200; +} + +.tox-shadowhost.tox-fullscreen { + z-index: 1200; +} + +.tox-fullscreen .tox.tox-tinymce-aux, +.tox-fullscreen ~ .tox.tox-tinymce-aux { + z-index: 1201; +} diff --git a/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.shadowdom.min.css b/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.shadowdom.min.css new file mode 100644 index 0000000..9ba6e02 --- /dev/null +++ b/frontend/public/tinymce-dataease-private/skins/ui/oxide/skin.shadowdom.min.css @@ -0,0 +1,7 @@ +/** + * Copyright (c) Tiny Technologies, Inc. All rights reserved. + * Licensed under the LGPL or a commercial license. + * For LGPL see License.txt in the project root for license information. + * For commercial licenses see https://www.tiny.cloud/ + */ +body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;left:0;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;position:fixed;top:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox.tox-tinymce.tox-fullscreen{background-color:transparent;z-index:1200}.tox-shadowhost.tox-fullscreen{z-index:1200}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201} diff --git a/frontend/public/vite.svg b/frontend/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/data-visualization/api/chart.ts b/frontend/src/api/chart.ts similarity index 100% rename from frontend/src/data-visualization/api/chart.ts rename to frontend/src/api/chart.ts diff --git a/frontend/src/api/data-visualization/chart.ts b/frontend/src/api/data-visualization/chart.ts new file mode 100644 index 0000000..b1bad2d --- /dev/null +++ b/frontend/src/api/data-visualization/chart.ts @@ -0,0 +1,124 @@ +import request from '@/data-visualization/config/axios' +export interface Field { + id: number | string + datasourceId: number | string + datasetTableId: number | string + datasetGroupId: number | string + originName: string + name: string + dataeaseName: string + groupType: string + type: string + deType: number + deExtractType: number + extField: number + checked: boolean + fieldShortName: string + desensitized: boolean +} + +export interface ComponentInfo { + id: string + name: string + deType: number + type: string + datasetId: string +} + +export const getFieldByDQ = async (id, chartId, data): Promise => { + return request.post({ url: `/chart/listByDQ/${id}/${chartId}`, data: data }).then(res => { + return res?.data + }) +} + +export const copyChartField = async (id, chartId): Promise => { + return request.post({ url: `/chart/copyField/${id}/${chartId}`, data: {} }).then(res => { + return res?.data + }) +} + +export const deleteChartField = async (id): Promise => { + return request.post({ url: `/chart/deleteField/${id}`, data: {} }).then(res => { + return res?.data + }) +} + +export const deleteChartFieldByChartId = async (chartId): Promise => { + return request.post({ url: `/chart/deleteFieldByChart/${chartId}`, data: {} }).then(res => { + return res?.data + }) +} + +// 通过图表对象获取数据 +export const getData = async (data): Promise => { + delete data.data + return request.post({ url: '/chartData/getData', data }).then(res => { + if (res.code === 0) { + return res?.data + } else { + return res + } + }) +} + +export const innerExportDetails = async (data): Promise => { + return request.post({ + url: '/chartData/innerExportDetails', + method: 'post', + data: data, + loading: true, + responseType: 'blob' + }) +} + +export const innerExportDataSetDetails = async (data): Promise => { + return request.post({ + url: '/chartData/innerExportDataSetDetails', + method: 'post', + data: data, + loading: true, + responseType: 'blob' + }) +} + +// 通过图表id获取数据 +export const getChart = async (id): Promise => { + return request.post({ url: `/chart/getChart/${id}`, data: {} }).then(res => { + return res?.data + }) +} + +// 单个图表保存测试 +export const saveChart = async (data): Promise => { + delete data.data + return request.post({ url: '/chart/save', data }).then(res => { + return res?.data + }) +} + +// 获取单个字段枚举值 +export const getFieldData = async ({ fieldId, fieldType, data }): Promise => { + delete data.data + return request + .post({ url: `/chartData/getFieldData/${fieldId}/${fieldType}`, data }) + .then(res => { + return res + }) +} + +// 获取下钻字段枚举值 +export const getDrillFieldData = async ({ fieldId, data }): Promise => { + delete data.data + return request.post({ url: `/chartData/getDrillFieldData/${fieldId}`, data }).then(res => { + return res + }) +} + +export const getChartDetail = async (id: string): Promise => { + return request.post({ url: `chart/getDetail/${id}`, data: {} }).then(res => { + return res + }) +} + +export const checkSameDataSet = async (viewIdSource, viewIdTarget) => + request.get({ url: '/chart/checkSameDataSet/' + viewIdSource + '/' + viewIdTarget }) diff --git a/frontend/src/api/data-visualization/dataset.ts b/frontend/src/api/data-visualization/dataset.ts new file mode 100644 index 0000000..e5babfb --- /dev/null +++ b/frontend/src/api/data-visualization/dataset.ts @@ -0,0 +1,368 @@ +import request from '@/utils/request' +import { type Field } from '@/api/data-visualization/chart' +import { nameTrim } from '@/data-visualization/utils/utils' +export interface DatasetOrFolder { + name: string + action?: string + id?: number | string + pid?: number | string + appId?: number | string + nodeType: 'folder' | 'dataset' + union?: Array<{}> + allFields?: Array<{}> +} + +export interface EnumValue { + queryId: string + displayId?: string + sortId?: string + sort?: string + resultMode?: number + searchText: string + filter?: Array<{}> +} + +interface Fields { + fields: Array<{}> + data: Array<{}> +} +export interface ParamsDetail { + datasetGroupId: string + type: Array + variableName: string +} + +export interface DatasetDetail { + id: string + name: string + componentId: string + fields: { + dimensionList: Array + quotaList: Array + parameterList?: Array + } + activelist?: string + hasParameter?: boolean + checkList: string[] + list: Array +} + +export interface FieldData { + allFields: Array<{}> + data: Fields + total?: number +} + +export interface Dataset { + id: string + pid: string + name: string + union?: Array<{}> + allFields?: Array<{}> +} + +export interface Table { + datasourceId: string + name: string + tableName: string + type: string + unableCheck?: boolean +} +// 获取权限路 +// edit +export const saveDatasetTree = async (data: DatasetOrFolder): Promise => { + nameTrim(data) + return request.post({ url: '/datasetTree/save', data }).then(res => { + return res?.data + }) +} + +// create +export const createDatasetTree = async (data: DatasetOrFolder): Promise => { + nameTrim(data) + return request.post({ url: '/datasetTree/create', data }).then(res => { + return res?.data + }) +} + +// rename +export const renameDatasetTree = async (data: DatasetOrFolder): Promise => { + nameTrim(data) + return request.post({ url: '/datasetTree/rename', data }).then(res => { + return res?.data + }) +} + +export const enumValueObj = async (data: EnumValue): Promise[]> => { + return request.post({ url: '/datasetData/enumValueObj', data }).then(res => { + return res?.data + }) +} + +export const enumValueDs = async (data: any): Promise[]> => { + return request.post({ url: '/datasetData/enumValueDs', data }).then(res => { + return res?.data + }) +} + +export const moveDatasetTree = async (data: DatasetOrFolder): Promise => { + return request.post({ url: '/datasetTree/move', data }).then(res => { + return res?.data + }) +} + +export const getDatasetTree = async (data: any): Promise => { + data.busiFlag = 'dataset' + return request.post({ url: '/datasetTree/tree', data }).then(res => { + return res?.data + }) +} + +export const barInfoApi = async (id): Promise => { + return request.get({ url: `/datasetTree/barInfo/${id}`, data: {} }).then(res => { + return res?.data + }) +} + +export const delDatasetTree = async (id): Promise => { + return request.post({ url: `/datasetTree/delete/${id}`, data: {} }).then(res => { + return res?.data + }) +} + +export const exportDatasetData = (data = {}) => { + return request.post({ + url: '/datasetTree/exportDataset', + method: 'post', + data: data, + loading: true + }) +} + +export const exportLimit = async (): Promise => { + return request.post({ url: `/exportCenter/exportLimit`, data: {} }).then(res => { + return res?.data + }) +} + +export const perDelete = async (id): Promise => { + return request.post({ url: `/datasetTree/perDelete/${id}`, data: {} }).then(res => { + return res?.data + }) +} + +export const getDatasourceList = async (weight: number,appId:any): Promise => { + const data = { + busiFlag: 'datasource', + appId:appId + } + if (weight) { + data['weight'] = weight + } + return request.post({ url: '/datasource/tree', data }).then(res => { + return res?.data + }) +} + +export const getTables = async (data): Promise => { + return request.post({ url: `/datasource/getTables`, data }).then(res => { + return res?.data + }) +} + +export const getTableField = async (data): Promise => { + return request.post({ url: '/datasetData/tableField', data }).then(res => { + return res?.data + }) +} + +export const getPreviewData = async (data): Promise => { + return request.post({ url: '/datasetData/previewData', data }).then(res => { + return res?.data + }) +} + +export const getDatasetPreview = async (id): Promise => { + return request.post({ url: `/datasetTree/get/${id}`, data: {} }).then(res => { + return res?.data + }) +} + +export const getDatasetTotal = async (id): Promise => { + return request.post({ url: `/datasetData/getDatasetTotal`, data: { id: id } }).then(res => { + return res?.data + }) +} + +export const getDatasetDetails = async (id): Promise => { + return request.post({ url: `/datasetTree/details/${id}`, data: {} }).then(res => { + return res?.data + }) +} + +export const tableUpdate = async (data): Promise => { + return request.post({ url: '/dataset/table/update', data }).then(res => { + return res?.data + }) +} + +export const getPreviewSql = async (data): Promise => { + return request.post({ url: '/datasetData/previewSql', data }).then(res => { + return res?.data + }) +} + +export const getDsDetails = async (data): Promise => { + return request.post({ url: '/datasetTree/dsDetails', data }).then(res => { + return res?.data + }) +} +export const getDsDetailsWithPerm = async (data): Promise => { + return request.post({ url: '/datasetTree/detailWithPerm', data }).then(res => { + return res?.data + }) +} +export const getSqlParams = async (data): Promise => { + return request.post({ url: '/datasetTree/getSqlParams', data }).then(res => { + return res?.data + }) +} +export const rowPermissionList = (page: number, limit: number, datasetId: number) => + request.get({ url: '/dataset/rowPermissions/pager/' + datasetId + '/' + page + '/' + limit }) + +export const columnPermissionList = (page: number, limit: number, datasetId: number) => + request.get({ url: '/dataset/columnPermissions/pager/' + datasetId + '/' + page + '/' + limit }) + +export const rowPermissionTargetObjList = (datasetId: number, type: string) => + request.get({ url: '/dataset/rowPermissions/authObjs/' + datasetId + '/' + type }) + +export const listFieldByDatasetGroup = (datasetId: number) => + request.post({ url: '/datasetField/listByDatasetGroup/' + datasetId }) + +export const multFieldValuesForPermissions = (data = {}) => { + return request.post({ url: '/datasetField/multFieldValuesForPermissions', data }) +} + +export const listFieldsWithPermissions = (datasetId: number) => { + return request.get({ url: '/datasetField/listWithPermissions/' + datasetId }) +} + +export const copilotFields = (datasetId: number) => { + return request.post({ url: '/datasetField/copilotFields/' + datasetId }) +} + +export const saveRowPermission = (data = {}) => { + return request.post({ url: '/dataset/rowPermissions/save', data }) +} + +export const saveColumnPermission = (data = {}) => { + return request.post({ url: '/dataset/columnPermissions/save', data }) +} + +export const deleteRowPermission = (data = {}) => { + return request.post({ url: '/dataset/rowPermissions/delete', data }) +} + +export const deleteColumnPermission = (data = {}) => { + return request.post({ url: '/dataset/columnPermissions/delete', data }) +} + +export const whiteListUsersForPermissions = (data = {}) => { + return request.post({ url: '/dataset/rowPermissions/whiteListUsers', data }) +} + +export const saveField = async (data): Promise => { + return request.post({ url: '/datasetField/save', data }).then(res => { + return res?.data + }) +} + +export const deleteField = async (id): Promise => { + return request.post({ url: `/datasetField/delete/${id}`, data: {} }).then(res => { + return res?.data + }) +} + +export const deleteFieldByChartId = async (id): Promise => { + return request.post({ url: `/datasetField/deleteByChartId/${id}`, data: {} }).then(res => { + return res?.data + }) +} + +export const getEnumValue = async (data): Promise => { + return request.post({ url: '/datasetData/enumValue', data }).then(res => { + return res?.data + }) +} + +export const getFunction = async (): Promise => { + return request.post({ url: '/datasetField/getFunction', data: {} }).then(res => { + return res?.data + }) +} + +export const exportTasks = async (type): Promise => { + return request.post({ url: '/exportCenter/exportTasks/' + type, data: {} }).then(res => { + return res + }) +} + +export const exportRetry = async (id): Promise => { + return request.post({ url: '/exportCenter/retry/' + id, data: {} }).then(res => { + return res?.data + }) +} + +export const downloadFile = async (id): Promise => { + return request.get({ url: 'exportCenter/download/' + id, responseType: 'blob' }).then(res => { + return res?.data + }) +} + +export const exportDelete = async (id): Promise => { + return request.get({ url: '/exportCenter/delete/' + id }).then(res => { + return res?.data + }) +} + +export const exportDeleteAll = async (type, data): Promise => { + return request.post({ url: '/exportCenter/deleteAll/' + type, data }).then(res => { + return res?.data + }) +} + +export const exportDeletePost = async (data): Promise => { + return request.post({ url: '/exportCenter/delete', data }).then(res => { + return res?.data + }) +} + +export const listByDsIds = async (data): Promise => { + return request.post({ url: 'datasetField/listByDsIds', data }).then(res => { + return res?.data + }) +} + +export const getFieldTree = async (data): Promise => { + return request.post({ url: 'datasetData/getFieldTree', data }).then(res => { + return res?.data + }) +} + +export const copilotChat = async (data): Promise => { + return request.post({ url: '/copilot/chat', data }).then(res => { + return res?.data + }) +} + +export const getListCopilot = async (): Promise => { + return request.post({ url: '/copilot/getList' }).then(res => { + return res?.data + }) +} + +export const clearAllCopilot = async (): Promise => { + return request.post({ url: '/copilot/clearAll' }).then(res => { + return res?.data + }) +} diff --git a/frontend/src/api/data-visualization/map.ts b/frontend/src/api/data-visualization/map.ts new file mode 100644 index 0000000..265ddb4 --- /dev/null +++ b/frontend/src/api/data-visualization/map.ts @@ -0,0 +1,53 @@ +import request from '@/data-visualization/config/axios' +import { FeatureCollection } from '@antv/l7plot/dist/esm/plots/choropleth/types' + +export const getWorldTree = (): Promise> => { + return request.get({ url: '/map/worldTree' }) +} + +export const getGeoJson = (areaId: string): Promise> => { + let prefix = '/map' + let areaCode = areaId + if (isCustomGeo(areaId)) { + prefix = '/geo' + areaCode = getBusiGeoCode(areaId) + } + const realCountry = areaCode.substring(0, 3) + const url = `${prefix}/${realCountry}/${areaCode}.json` + return request.get({ url }) +} + +const isCustomGeo = (id: string) => { + return id.startsWith('geo_') +} +const getBusiGeoCode = (id: string) => { + return id.substring(4) +} + +export const listCustomGeoArea = (): Promise> => { + return request.get({ url: '/customGeo/geoArea/list' }) +} + +export const getCustomGeoArea = (id: string): Promise> => { + return request.get({ url: `/customGeo/geoArea/${id}` }) +} + +export const deleteCustomGeoArea = (id: string) => { + return request.delete({ url: `/customGeo/geoArea/${id}` }) +} + +export const saveCustomGeoArea = (area: CustomGeoArea) => { + return request.post({ url: '/customGeo/geoArea/save', data: area }) +} + +export const deleteCustomGeoSubArea = (id: string) => { + return request.delete({ url: `/customGeo/geoSubArea/${id}` }) +} + +export const saveCustomGeoSubArea = (area: CustomGeoSubArea) => { + return request.post({ url: '/customGeo/geoSubArea/save', data: area }) +} + +export const listSubAreaOptions = (): Promise> => { + return request.get({ url: '/customGeo/geoSubArea/options' }) +} diff --git a/frontend/src/api/data-visualization/staticResource.ts b/frontend/src/api/data-visualization/staticResource.ts new file mode 100644 index 0000000..88e71f5 --- /dev/null +++ b/frontend/src/api/data-visualization/staticResource.ts @@ -0,0 +1,33 @@ +import request from '@/data-visualization/config/axios' +import { ElMessage } from 'element-plus-secondary' + +const staticResourcePath = '/static-resource/' +export const uploadFile = (fileId: number | string, param) => + request.post({ + url: '/staticResource/upload/' + fileId, + headersType: 'multipart/form-data', + loading: true, + data: param + }) + +export function beforeUploadCheck(file) { + const isImage = file.type.startsWith('image/') + const isSizeValid = file.size / 1024 / 1024 < 15 // 15MB + + if (!isImage) { + ElMessage.error('请上传图片') + return false + } + if (!isSizeValid) { + ElMessage.error('图片大小不能超过15M') + return false + } + return true +} + +export function findResourceAsBase64(params) { + return request.post({ + url: '/staticResource/findResourceAsBase64', + data: params + }) +} diff --git a/frontend/src/api/data-visualization/user.ts b/frontend/src/api/data-visualization/user.ts new file mode 100644 index 0000000..1e733eb --- /dev/null +++ b/frontend/src/api/data-visualization/user.ts @@ -0,0 +1,3 @@ +import request from '@/data-visualization/config/axios' + +export const ipInfoApi = () => request.get({ url: `/user/ipInfo` }) \ No newline at end of file diff --git a/frontend/src/api/data-visualization/visualization/dataVisualization.ts b/frontend/src/api/data-visualization/visualization/dataVisualization.ts new file mode 100644 index 0000000..efb4753 --- /dev/null +++ b/frontend/src/api/data-visualization/visualization/dataVisualization.ts @@ -0,0 +1,135 @@ +import request from '@/data-visualization/config/axios' +export interface ResourceOrFolder { + name: string + id?: number | string + pid?: number | string + nodeType: 'folder' | 'leaf' + type: string +} + +export interface Panel { + name: string + type: string + updateTime: number + createBy: string + updateBy: string +} + +export const findCopyResource = async (dvId, busiFlag): Promise => { + return request.get({ url: '/dataVisualization/findCopyResource/' + dvId + '/' + busiFlag }) +} + +export const findById = async ( + dvId, + busiFlag, + attachInfo = { source: 'main', taskId: null } +): Promise => { + let busiFlagResult = busiFlag + if (!busiFlagResult) { + await findDvType(dvId).then(res => { + busiFlagResult = res.data + }) + } + const data = { id: dvId, busiFlag: busiFlagResult, ...attachInfo } + return request.post({ url: '/dataVisualization/findById', data }) +} + +export const updateCheckVersion = dvId => + request.get({ url: `/dataVisualization/updateCheckVersion/${dvId}` }) + +export const queryTreeApi = async (data: any): Promise => { + return request.post({ url: '/dataVisualization/tree', data }).then(res => { + return res?.data + }) +} + +export const queryBusiTreeApi = async (data): Promise => { + return request.post({ url: '/dataVisualization/interactiveTree', data }).then(res => { + return res?.data + }) +} + +export const findDvType = async dvId => + request.get({ url: `/dataVisualization/findDvType/${dvId}` }) + +export const save = data => request.post({ url: '/dataVisualization/save', data }) + +export const checkCanvasChange = data => + request.post({ url: '/dataVisualization/checkCanvasChange', data, loading: true }) + +export const saveCanvas = data => + request.post({ url: '/dataVisualization/saveCanvas', data, loading: true }) + +export const appCanvasNameCheck = async data => + request.post({ url: '/dataVisualization/appCanvasNameCheck', data, loading: false }) + +export const updateBase = data => request.post({ url: '/dataVisualization/updateBase', data }) + +export const updateCanvas = data => + request.post({ url: '/dataVisualization/updateCanvas', data, loading: true }) + +export const moveResource = data => request.post({ url: '/dataVisualization/move', data }) + +export const copyResource = data => request.post({ url: '/dataVisualization/copy', data }) + +export const deleteLogic = (dvId, busiFlag) => + request.post({ url: '/dataVisualization/deleteLogic/' + dvId + '/' + busiFlag }) + +export const querySubjectWithGroupApi = data => + request.post({ url: '/visualizationSubject/querySubjectWithGroup', data }) + +export const saveOrUpdateSubject = data => + request.post({ url: '/visualizationSubject/update', data }) + +export const deleteSubject = id => request.post({ url: '/visualizationSubject/delete/' + id }) + +export const dvNameCheck = async data => request.post({ url: '/dataVisualization/nameCheck', data }) + +export const storeApi = (data): Promise => { + return request.post({ url: '/store/execute', data }) +} + +export const storeStatusApi = (id: string): Promise => { + return request.get({ url: `/store/favorited/${id}` }) +} + +export const decompression = async data => + request.post({ url: '/dataVisualization/decompression', data, loading: true }) + +export const viewDetailList = dvId => { + return request.get({ + url: '/dataVisualization/viewDetailList/' + dvId, + method: 'get', + loading: false + }) +} + +export const getComponentInfo = dvId => { + return request.get({ + url: '/panel/view/getComponentInfo/' + dvId, + loading: false + }) +} + +export const export2AppCheck = params => { + return request.post({ + url: '/dataVisualization/export2AppCheck', + data: params, + loading: true + }) +} + +export const queryOuterParamsDsInfo = async dvId => { + return request.get({ + url: '/outerParams/queryDsWithVisualizationId/' + dvId, + method: 'get', + loading: false + }) +} + +export const queryShareBaseApi = () => { + return request.get({ + url: '/sysParameter/shareBase', + loading: false + }) +} diff --git a/frontend/src/api/data-visualization/visualization/linkJump.ts b/frontend/src/api/data-visualization/visualization/linkJump.ts new file mode 100644 index 0000000..615401f --- /dev/null +++ b/frontend/src/api/data-visualization/visualization/linkJump.ts @@ -0,0 +1,57 @@ +import request from '@/data-visualization/config/axios' + +export function getTableFieldWithViewId(viewId) { + return request.get({ + url: '/linkJump/getTableFieldWithViewId/' + viewId + }) +} +export function queryWithViewId(dvId, viewId) { + return request.get({ + url: '/linkJump/queryWithViewId/' + dvId + '/' + viewId + }) +} + +export function updateJumpSet(requestInfo) { + return request.post({ + url: '/linkJump/updateJumpSet', + data: requestInfo, + loading: true + }) +} +export function queryTargetVisualizationJumpInfo(requestInfo) { + return request.post({ + url: '/linkJump/queryTargetVisualizationJumpInfo', + data: requestInfo, + loading: true + }) +} + +export function queryVisualizationJumpInfo(dvId) { + return request.get({ + url: '/linkJump/queryVisualizationJumpInfo/' + dvId, + loading: false + }) +} + +export function viewTableDetailList(dvId) { + return request.get({ + url: '/linkJump/viewTableDetailList/' + dvId, + loading: false + }) +} + +export function updateJumpSetActive(requestInfo) { + return request.post({ + url: '/linkJump/updateJumpSetActive', + data: requestInfo, + loading: true + }) +} + +export function removeJumpSet(requestInfo) { + return request.post({ + url: '/linkJump/removeJumpSet', + data: requestInfo, + loading: true + }) +} diff --git a/frontend/src/api/data-visualization/visualization/linkage.ts b/frontend/src/api/data-visualization/visualization/linkage.ts new file mode 100644 index 0000000..41d50dd --- /dev/null +++ b/frontend/src/api/data-visualization/visualization/linkage.ts @@ -0,0 +1,17 @@ +import request from '@/data-visualization/config/axios' + +export const getViewLinkageGather = data => + request.post({ url: '/linkage/getViewLinkageGather', data }) + +export const getViewLinkageGatherArray = data => + request.post({ url: '/linkage/getViewLinkageGatherArray', data }) + +export const saveLinkage = data => request.post({ url: '/linkage/saveLinkage', data }) + +export const getPanelAllLinkageInfo = dvId => + request.get({ url: '/linkage/getVisualizationAllLinkageInfo/' + dvId }) + +export const updateLinkageActive = data => + request.post({ url: '/linkage/updateLinkageActive', data }) + +export const removeLinkage = data => request.post({ url: '/linkage/removeLinkage', data }) diff --git a/frontend/src/assets/img/error.png b/frontend/src/assets/img/error.png new file mode 100644 index 0000000..41c5464 Binary files /dev/null and b/frontend/src/assets/img/error.png differ diff --git a/frontend/src/assets/img/none.png b/frontend/src/assets/img/none.png new file mode 100644 index 0000000..358dfdd Binary files /dev/null and b/frontend/src/assets/img/none.png differ diff --git a/frontend/src/assets/img/nothing-input.png b/frontend/src/assets/img/nothing-input.png new file mode 100644 index 0000000..7b1fa54 Binary files /dev/null and b/frontend/src/assets/img/nothing-input.png differ diff --git a/frontend/src/assets/img/nothing-none-black.png b/frontend/src/assets/img/nothing-none-black.png new file mode 100644 index 0000000..c33136a Binary files /dev/null and b/frontend/src/assets/img/nothing-none-black.png differ diff --git a/frontend/src/assets/img/nothing-select.png b/frontend/src/assets/img/nothing-select.png new file mode 100644 index 0000000..90e6251 Binary files /dev/null and b/frontend/src/assets/img/nothing-select.png differ diff --git a/frontend/src/assets/img/nothing-table.png b/frontend/src/assets/img/nothing-table.png new file mode 100644 index 0000000..700664e Binary files /dev/null and b/frontend/src/assets/img/nothing-table.png differ diff --git a/frontend/src/assets/img/nothing-tree.png b/frontend/src/assets/img/nothing-tree.png new file mode 100644 index 0000000..6eb9b2b Binary files /dev/null and b/frontend/src/assets/img/nothing-tree.png differ diff --git a/frontend/src/assets/svg/401.svg b/frontend/src/assets/svg/401.svg new file mode 100644 index 0000000..d02779f --- /dev/null +++ b/frontend/src/assets/svg/401.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/403.svg b/frontend/src/assets/svg/403.svg new file mode 100644 index 0000000..96b2bad --- /dev/null +++ b/frontend/src/assets/svg/403.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/API-ds.svg b/frontend/src/assets/svg/API-ds.svg new file mode 100644 index 0000000..799ecaa --- /dev/null +++ b/frontend/src/assets/svg/API-ds.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/Apache Hive.svg b/frontend/src/assets/svg/Apache Hive.svg new file mode 100644 index 0000000..f0e0d8d --- /dev/null +++ b/frontend/src/assets/svg/Apache Hive.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/Checkbox.svg b/frontend/src/assets/svg/Checkbox.svg new file mode 100644 index 0000000..0645e22 --- /dev/null +++ b/frontend/src/assets/svg/Checkbox.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/DM.svg b/frontend/src/assets/svg/DM.svg new file mode 100644 index 0000000..1d74425 --- /dev/null +++ b/frontend/src/assets/svg/DM.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/DataEase.svg b/frontend/src/assets/svg/DataEase.svg new file mode 100644 index 0000000..aa2b44b --- /dev/null +++ b/frontend/src/assets/svg/DataEase.svg @@ -0,0 +1,17 @@ + + + + Layer 1 + + + + + + + + + + + GIS-BI开发平台 + + diff --git a/frontend/src/assets/svg/DataEase1.svg b/frontend/src/assets/svg/DataEase1.svg new file mode 100644 index 0000000..3fe02e0 --- /dev/null +++ b/frontend/src/assets/svg/DataEase1.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/Elasticsearch.svg b/frontend/src/assets/svg/Elasticsearch.svg new file mode 100644 index 0000000..3cec9c9 --- /dev/null +++ b/frontend/src/assets/svg/Elasticsearch.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/svg/Excel-ds.svg b/frontend/src/assets/svg/Excel-ds.svg new file mode 100644 index 0000000..dc9c761 --- /dev/null +++ b/frontend/src/assets/svg/Excel-ds.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/Frame.svg b/frontend/src/assets/svg/Frame.svg new file mode 100644 index 0000000..4f4ae04 --- /dev/null +++ b/frontend/src/assets/svg/Frame.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/KingBase.svg b/frontend/src/assets/svg/KingBase.svg new file mode 100644 index 0000000..e69e04e --- /dev/null +++ b/frontend/src/assets/svg/KingBase.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/svg/Kylin.svg b/frontend/src/assets/svg/Kylin.svg new file mode 100644 index 0000000..33f0b66 --- /dev/null +++ b/frontend/src/assets/svg/Kylin.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/Maxcompute.svg b/frontend/src/assets/svg/Maxcompute.svg new file mode 100644 index 0000000..54a3935 --- /dev/null +++ b/frontend/src/assets/svg/Maxcompute.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/PDF.svg b/frontend/src/assets/svg/PDF.svg new file mode 100644 index 0000000..f04b7a2 --- /dev/null +++ b/frontend/src/assets/svg/PDF.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/Presto.svg b/frontend/src/assets/svg/Presto.svg new file mode 100644 index 0000000..6b2fe09 --- /dev/null +++ b/frontend/src/assets/svg/Presto.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/StarRocks-ds.svg b/frontend/src/assets/svg/StarRocks-ds.svg new file mode 100644 index 0000000..5b52c1a --- /dev/null +++ b/frontend/src/assets/svg/StarRocks-ds.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/TiDB-ds.svg b/frontend/src/assets/svg/TiDB-ds.svg new file mode 100644 index 0000000..29dd41f --- /dev/null +++ b/frontend/src/assets/svg/TiDB-ds.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/active-btn_copilot.svg b/frontend/src/assets/svg/active-btn_copilot.svg new file mode 100644 index 0000000..4cc82f4 --- /dev/null +++ b/frontend/src/assets/svg/active-btn_copilot.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/add.svg b/frontend/src/assets/svg/add.svg new file mode 100644 index 0000000..3ffd8d4 --- /dev/null +++ b/frontend/src/assets/svg/add.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/add_white.svg b/frontend/src/assets/svg/add_white.svg new file mode 100644 index 0000000..4179e8d --- /dev/null +++ b/frontend/src/assets/svg/add_white.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/adds.svg b/frontend/src/assets/svg/adds.svg new file mode 100644 index 0000000..8c0b9ab --- /dev/null +++ b/frontend/src/assets/svg/adds.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/all-msg.svg b/frontend/src/assets/svg/all-msg.svg new file mode 100644 index 0000000..320a433 --- /dev/null +++ b/frontend/src/assets/svg/all-msg.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/appearance.svg b/frontend/src/assets/svg/appearance.svg new file mode 100644 index 0000000..9e5e052 --- /dev/null +++ b/frontend/src/assets/svg/appearance.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/area-dark.svg b/frontend/src/assets/svg/area-dark.svg new file mode 100644 index 0000000..e11e43a --- /dev/null +++ b/frontend/src/assets/svg/area-dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/area-origin.svg b/frontend/src/assets/svg/area-origin.svg new file mode 100644 index 0000000..23d0636 --- /dev/null +++ b/frontend/src/assets/svg/area-origin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/area-stack-dark.svg b/frontend/src/assets/svg/area-stack-dark.svg new file mode 100644 index 0000000..5db66fd --- /dev/null +++ b/frontend/src/assets/svg/area-stack-dark.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/area-stack-origin.svg b/frontend/src/assets/svg/area-stack-origin.svg new file mode 100644 index 0000000..23d0636 --- /dev/null +++ b/frontend/src/assets/svg/area-stack-origin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/area-stack.svg b/frontend/src/assets/svg/area-stack.svg new file mode 100644 index 0000000..ca9c9f9 --- /dev/null +++ b/frontend/src/assets/svg/area-stack.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/area.svg b/frontend/src/assets/svg/area.svg new file mode 100644 index 0000000..10275d9 --- /dev/null +++ b/frontend/src/assets/svg/area.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/association.svg b/frontend/src/assets/svg/association.svg new file mode 100644 index 0000000..5d8243d --- /dev/null +++ b/frontend/src/assets/svg/association.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/auth.svg b/frontend/src/assets/svg/auth.svg new file mode 100644 index 0000000..162b25f --- /dev/null +++ b/frontend/src/assets/svg/auth.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/authentication.svg b/frontend/src/assets/svg/authentication.svg new file mode 100644 index 0000000..f5268c8 --- /dev/null +++ b/frontend/src/assets/svg/authentication.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/bar-dark.svg b/frontend/src/assets/svg/bar-dark.svg new file mode 100644 index 0000000..0cd6aac --- /dev/null +++ b/frontend/src/assets/svg/bar-dark.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/bar-group-dark.svg b/frontend/src/assets/svg/bar-group-dark.svg new file mode 100644 index 0000000..1ab3629 --- /dev/null +++ b/frontend/src/assets/svg/bar-group-dark.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/bar-group-origin.svg b/frontend/src/assets/svg/bar-group-origin.svg new file mode 100644 index 0000000..21e1af0 --- /dev/null +++ b/frontend/src/assets/svg/bar-group-origin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/bar-group-stack-dark.svg b/frontend/src/assets/svg/bar-group-stack-dark.svg new file mode 100644 index 0000000..313c473 --- /dev/null +++ b/frontend/src/assets/svg/bar-group-stack-dark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/bar-group-stack-origin.svg b/frontend/src/assets/svg/bar-group-stack-origin.svg new file mode 100644 index 0000000..21e1af0 --- /dev/null +++ b/frontend/src/assets/svg/bar-group-stack-origin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/bar-group-stack.svg b/frontend/src/assets/svg/bar-group-stack.svg new file mode 100644 index 0000000..0e98db5 --- /dev/null +++ b/frontend/src/assets/svg/bar-group-stack.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/bar-group.svg b/frontend/src/assets/svg/bar-group.svg new file mode 100644 index 0000000..11bf7b6 --- /dev/null +++ b/frontend/src/assets/svg/bar-group.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/bar-horizontal-dark.svg b/frontend/src/assets/svg/bar-horizontal-dark.svg new file mode 100644 index 0000000..cab5303 --- /dev/null +++ b/frontend/src/assets/svg/bar-horizontal-dark.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/bar-horizontal-origin.svg b/frontend/src/assets/svg/bar-horizontal-origin.svg new file mode 100644 index 0000000..e8af2b9 --- /dev/null +++ b/frontend/src/assets/svg/bar-horizontal-origin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/bar-horizontal.svg b/frontend/src/assets/svg/bar-horizontal.svg new file mode 100644 index 0000000..56192e8 --- /dev/null +++ b/frontend/src/assets/svg/bar-horizontal.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/bar-origin.svg b/frontend/src/assets/svg/bar-origin.svg new file mode 100644 index 0000000..21e1af0 --- /dev/null +++ b/frontend/src/assets/svg/bar-origin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/bar-range-dark.svg b/frontend/src/assets/svg/bar-range-dark.svg new file mode 100644 index 0000000..484d1e1 --- /dev/null +++ b/frontend/src/assets/svg/bar-range-dark.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/bar-range-origin.svg b/frontend/src/assets/svg/bar-range-origin.svg new file mode 100644 index 0000000..26ef1d7 --- /dev/null +++ b/frontend/src/assets/svg/bar-range-origin.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/bar-range.svg b/frontend/src/assets/svg/bar-range.svg new file mode 100644 index 0000000..2f3e88e --- /dev/null +++ b/frontend/src/assets/svg/bar-range.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/bar-stack-dark.svg b/frontend/src/assets/svg/bar-stack-dark.svg new file mode 100644 index 0000000..a19cd7d --- /dev/null +++ b/frontend/src/assets/svg/bar-stack-dark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/bar-stack-horizontal-dark.svg b/frontend/src/assets/svg/bar-stack-horizontal-dark.svg new file mode 100644 index 0000000..ea9c4da --- /dev/null +++ b/frontend/src/assets/svg/bar-stack-horizontal-dark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/bar-stack-horizontal-origin.svg b/frontend/src/assets/svg/bar-stack-horizontal-origin.svg new file mode 100644 index 0000000..e8af2b9 --- /dev/null +++ b/frontend/src/assets/svg/bar-stack-horizontal-origin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/bar-stack-horizontal.svg b/frontend/src/assets/svg/bar-stack-horizontal.svg new file mode 100644 index 0000000..e25d185 --- /dev/null +++ b/frontend/src/assets/svg/bar-stack-horizontal.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/bar-stack-origin.svg b/frontend/src/assets/svg/bar-stack-origin.svg new file mode 100644 index 0000000..21e1af0 --- /dev/null +++ b/frontend/src/assets/svg/bar-stack-origin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/bar-stack.svg b/frontend/src/assets/svg/bar-stack.svg new file mode 100644 index 0000000..b1700a3 --- /dev/null +++ b/frontend/src/assets/svg/bar-stack.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/bar.svg b/frontend/src/assets/svg/bar.svg new file mode 100644 index 0000000..218592b --- /dev/null +++ b/frontend/src/assets/svg/bar.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/bidirectional-bar-dark.svg b/frontend/src/assets/svg/bidirectional-bar-dark.svg new file mode 100644 index 0000000..f95fe09 --- /dev/null +++ b/frontend/src/assets/svg/bidirectional-bar-dark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/bidirectional-bar-origin.svg b/frontend/src/assets/svg/bidirectional-bar-origin.svg new file mode 100644 index 0000000..122c82e --- /dev/null +++ b/frontend/src/assets/svg/bidirectional-bar-origin.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/bidirectional-bar.svg b/frontend/src/assets/svg/bidirectional-bar.svg new file mode 100644 index 0000000..31f5b63 --- /dev/null +++ b/frontend/src/assets/svg/bidirectional-bar.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/board_1.svg b/frontend/src/assets/svg/board_1.svg new file mode 100644 index 0000000..aa87694 --- /dev/null +++ b/frontend/src/assets/svg/board_1.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/svg/board_2.svg b/frontend/src/assets/svg/board_2.svg new file mode 100644 index 0000000..53b5b2d --- /dev/null +++ b/frontend/src/assets/svg/board_2.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/board_3.svg b/frontend/src/assets/svg/board_3.svg new file mode 100644 index 0000000..17b9237 --- /dev/null +++ b/frontend/src/assets/svg/board_3.svg @@ -0,0 +1,2 @@ + + diff --git a/frontend/src/assets/svg/board_4.svg b/frontend/src/assets/svg/board_4.svg new file mode 100644 index 0000000..d3bcad9 --- /dev/null +++ b/frontend/src/assets/svg/board_4.svg @@ -0,0 +1,2 @@ + + diff --git a/frontend/src/assets/svg/board_5.svg b/frontend/src/assets/svg/board_5.svg new file mode 100644 index 0000000..1cba354 --- /dev/null +++ b/frontend/src/assets/svg/board_5.svg @@ -0,0 +1,2 @@ + + diff --git a/frontend/src/assets/svg/board_6.svg b/frontend/src/assets/svg/board_6.svg new file mode 100644 index 0000000..9ef2983 --- /dev/null +++ b/frontend/src/assets/svg/board_6.svg @@ -0,0 +1,2 @@ + + diff --git a/frontend/src/assets/svg/board_7.svg b/frontend/src/assets/svg/board_7.svg new file mode 100644 index 0000000..376cbf9 --- /dev/null +++ b/frontend/src/assets/svg/board_7.svg @@ -0,0 +1,2 @@ + + diff --git a/frontend/src/assets/svg/board_8.svg b/frontend/src/assets/svg/board_8.svg new file mode 100644 index 0000000..5b0c850 --- /dev/null +++ b/frontend/src/assets/svg/board_8.svg @@ -0,0 +1,2 @@ + + diff --git a/frontend/src/assets/svg/board_9.svg b/frontend/src/assets/svg/board_9.svg new file mode 100644 index 0000000..ba50537 --- /dev/null +++ b/frontend/src/assets/svg/board_9.svg @@ -0,0 +1,2 @@ + + diff --git a/frontend/src/assets/svg/btn_copilot.svg b/frontend/src/assets/svg/btn_copilot.svg new file mode 100644 index 0000000..56b3df8 --- /dev/null +++ b/frontend/src/assets/svg/btn_copilot.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/btn_oidc.svg b/frontend/src/assets/svg/btn_oidc.svg new file mode 100644 index 0000000..8d02149 --- /dev/null +++ b/frontend/src/assets/svg/btn_oidc.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/bubble-map-dark.svg b/frontend/src/assets/svg/bubble-map-dark.svg new file mode 100644 index 0000000..18e8ad1 --- /dev/null +++ b/frontend/src/assets/svg/bubble-map-dark.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/bubble-map-origin.svg b/frontend/src/assets/svg/bubble-map-origin.svg new file mode 100644 index 0000000..e400ccb --- /dev/null +++ b/frontend/src/assets/svg/bubble-map-origin.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/bubble-map.svg b/frontend/src/assets/svg/bubble-map.svg new file mode 100644 index 0000000..beaff8c --- /dev/null +++ b/frontend/src/assets/svg/bubble-map.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/button_right.svg b/frontend/src/assets/svg/button_right.svg new file mode 100644 index 0000000..7972225 --- /dev/null +++ b/frontend/src/assets/svg/button_right.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/calculate.svg b/frontend/src/assets/svg/calculate.svg new file mode 100644 index 0000000..600832e --- /dev/null +++ b/frontend/src/assets/svg/calculate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/cancel_release.svg b/frontend/src/assets/svg/cancel_release.svg new file mode 100644 index 0000000..30c5911 --- /dev/null +++ b/frontend/src/assets/svg/cancel_release.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/chart-download.svg b/frontend/src/assets/svg/chart-download.svg new file mode 100644 index 0000000..75e7cb8 --- /dev/null +++ b/frontend/src/assets/svg/chart-download.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/chart-mix-dark.svg b/frontend/src/assets/svg/chart-mix-dark.svg new file mode 100644 index 0000000..88e0a54 --- /dev/null +++ b/frontend/src/assets/svg/chart-mix-dark.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/svg/chart-mix-dual-line-dark.svg b/frontend/src/assets/svg/chart-mix-dual-line-dark.svg new file mode 100644 index 0000000..c3819a1 --- /dev/null +++ b/frontend/src/assets/svg/chart-mix-dual-line-dark.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/chart-mix-dual-line-origin.svg b/frontend/src/assets/svg/chart-mix-dual-line-origin.svg new file mode 100644 index 0000000..43f590b --- /dev/null +++ b/frontend/src/assets/svg/chart-mix-dual-line-origin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/chart-mix-dual-line.svg b/frontend/src/assets/svg/chart-mix-dual-line.svg new file mode 100644 index 0000000..f2b52d5 --- /dev/null +++ b/frontend/src/assets/svg/chart-mix-dual-line.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/chart-mix-group-dark.svg b/frontend/src/assets/svg/chart-mix-group-dark.svg new file mode 100644 index 0000000..a921991 --- /dev/null +++ b/frontend/src/assets/svg/chart-mix-group-dark.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/svg/chart-mix-group-origin.svg b/frontend/src/assets/svg/chart-mix-group-origin.svg new file mode 100644 index 0000000..a833627 --- /dev/null +++ b/frontend/src/assets/svg/chart-mix-group-origin.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/chart-mix-group.svg b/frontend/src/assets/svg/chart-mix-group.svg new file mode 100644 index 0000000..bd4a096 --- /dev/null +++ b/frontend/src/assets/svg/chart-mix-group.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/svg/chart-mix-origin.svg b/frontend/src/assets/svg/chart-mix-origin.svg new file mode 100644 index 0000000..f4ed2f0 --- /dev/null +++ b/frontend/src/assets/svg/chart-mix-origin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/chart-mix-stack-dark.svg b/frontend/src/assets/svg/chart-mix-stack-dark.svg new file mode 100644 index 0000000..b4edc0e --- /dev/null +++ b/frontend/src/assets/svg/chart-mix-stack-dark.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/chart-mix-stack-origin.svg b/frontend/src/assets/svg/chart-mix-stack-origin.svg new file mode 100644 index 0000000..1800cfc --- /dev/null +++ b/frontend/src/assets/svg/chart-mix-stack-origin.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/chart-mix-stack.svg b/frontend/src/assets/svg/chart-mix-stack.svg new file mode 100644 index 0000000..4fd947c --- /dev/null +++ b/frontend/src/assets/svg/chart-mix-stack.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/chart-mix.svg b/frontend/src/assets/svg/chart-mix.svg new file mode 100644 index 0000000..60e8e1a --- /dev/null +++ b/frontend/src/assets/svg/chart-mix.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/svg/chart-table.svg b/frontend/src/assets/svg/chart-table.svg new file mode 100644 index 0000000..5d8d412 --- /dev/null +++ b/frontend/src/assets/svg/chart-table.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/circle-packing-dark.svg b/frontend/src/assets/svg/circle-packing-dark.svg new file mode 100644 index 0000000..0e66315 --- /dev/null +++ b/frontend/src/assets/svg/circle-packing-dark.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/circle-packing-origin.svg b/frontend/src/assets/svg/circle-packing-origin.svg new file mode 100644 index 0000000..f71ca31 --- /dev/null +++ b/frontend/src/assets/svg/circle-packing-origin.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/circle-packing.svg b/frontend/src/assets/svg/circle-packing.svg new file mode 100644 index 0000000..73380f1 --- /dev/null +++ b/frontend/src/assets/svg/circle-packing.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/ck-ds.svg b/frontend/src/assets/svg/ck-ds.svg new file mode 100644 index 0000000..0e843c9 --- /dev/null +++ b/frontend/src/assets/svg/ck-ds.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/svg/clock.svg b/frontend/src/assets/svg/clock.svg new file mode 100644 index 0000000..543c400 --- /dev/null +++ b/frontend/src/assets/svg/clock.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/combinationpage.svg b/frontend/src/assets/svg/combinationpage.svg new file mode 100644 index 0000000..035f735 --- /dev/null +++ b/frontend/src/assets/svg/combinationpage.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/copilot.svg b/frontend/src/assets/svg/copilot.svg new file mode 100644 index 0000000..9752905 --- /dev/null +++ b/frontend/src/assets/svg/copilot.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/custom_sort.svg b/frontend/src/assets/svg/custom_sort.svg new file mode 100644 index 0000000..5e18dde --- /dev/null +++ b/frontend/src/assets/svg/custom_sort.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/dark_1.svg b/frontend/src/assets/svg/dark_1.svg new file mode 100644 index 0000000..be47b1a --- /dev/null +++ b/frontend/src/assets/svg/dark_1.svg @@ -0,0 +1,2 @@ + diff --git a/frontend/src/assets/svg/dashboard.svg b/frontend/src/assets/svg/dashboard.svg new file mode 100644 index 0000000..4153757 --- /dev/null +++ b/frontend/src/assets/svg/dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/data-reference.svg b/frontend/src/assets/svg/data-reference.svg new file mode 100644 index 0000000..b294b7c --- /dev/null +++ b/frontend/src/assets/svg/data-reference.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/database.svg b/frontend/src/assets/svg/database.svg new file mode 100644 index 0000000..30bcf30 --- /dev/null +++ b/frontend/src/assets/svg/database.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dataset-outline.svg b/frontend/src/assets/svg/dataset-outline.svg new file mode 100644 index 0000000..ed872dd --- /dev/null +++ b/frontend/src/assets/svg/dataset-outline.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dataset-task.svg b/frontend/src/assets/svg/dataset-task.svg new file mode 100644 index 0000000..65f3ed4 --- /dev/null +++ b/frontend/src/assets/svg/dataset-task.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/dataset_params.svg b/frontend/src/assets/svg/dataset_params.svg new file mode 100644 index 0000000..9175e1c --- /dev/null +++ b/frontend/src/assets/svg/dataset_params.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/datasource.svg b/frontend/src/assets/svg/datasource.svg new file mode 100644 index 0000000..71af710 --- /dev/null +++ b/frontend/src/assets/svg/datasource.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/datasoure_add_dataset.svg b/frontend/src/assets/svg/datasoure_add_dataset.svg new file mode 100644 index 0000000..4ddd6c2 --- /dev/null +++ b/frontend/src/assets/svg/datasoure_add_dataset.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/datasoure_details.svg b/frontend/src/assets/svg/datasoure_details.svg new file mode 100644 index 0000000..e1fd198 --- /dev/null +++ b/frontend/src/assets/svg/datasoure_details.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/db-de.svg b/frontend/src/assets/svg/db-de.svg new file mode 100644 index 0000000..ee44820 --- /dev/null +++ b/frontend/src/assets/svg/db-de.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/db-more-web.svg b/frontend/src/assets/svg/db-more-web.svg new file mode 100644 index 0000000..9bc701c --- /dev/null +++ b/frontend/src/assets/svg/db-more-web.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/db2-ds.svg b/frontend/src/assets/svg/db2-ds.svg new file mode 100644 index 0000000..35810ee --- /dev/null +++ b/frontend/src/assets/svg/db2-ds.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/de-api-new.svg b/frontend/src/assets/svg/de-api-new.svg new file mode 100644 index 0000000..3d73385 --- /dev/null +++ b/frontend/src/assets/svg/de-api-new.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/de-copy.svg b/frontend/src/assets/svg/de-copy.svg new file mode 100644 index 0000000..e885735 --- /dev/null +++ b/frontend/src/assets/svg/de-copy.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/de-db-new.svg b/frontend/src/assets/svg/de-db-new.svg new file mode 100644 index 0000000..07ffb0f --- /dev/null +++ b/frontend/src/assets/svg/de-db-new.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/de-delete.svg b/frontend/src/assets/svg/de-delete.svg new file mode 100644 index 0000000..2c122f8 --- /dev/null +++ b/frontend/src/assets/svg/de-delete.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/de-ds-error.svg b/frontend/src/assets/svg/de-ds-error.svg new file mode 100644 index 0000000..581a013 --- /dev/null +++ b/frontend/src/assets/svg/de-ds-error.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/de-ds-move.svg b/frontend/src/assets/svg/de-ds-move.svg new file mode 100644 index 0000000..5f256f8 --- /dev/null +++ b/frontend/src/assets/svg/de-ds-move.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/de-ds-rename.svg b/frontend/src/assets/svg/de-ds-rename.svg new file mode 100644 index 0000000..b3b369e --- /dev/null +++ b/frontend/src/assets/svg/de-ds-rename.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/de-ds-trash.svg b/frontend/src/assets/svg/de-ds-trash.svg new file mode 100644 index 0000000..fd8f126 --- /dev/null +++ b/frontend/src/assets/svg/de-ds-trash.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/de-ds-warning.svg b/frontend/src/assets/svg/de-ds-warning.svg new file mode 100644 index 0000000..2f7ac5a --- /dev/null +++ b/frontend/src/assets/svg/de-ds-warning.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/de-excel-new.svg b/frontend/src/assets/svg/de-excel-new.svg new file mode 100644 index 0000000..5a9d85a --- /dev/null +++ b/frontend/src/assets/svg/de-excel-new.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/de-json.svg b/frontend/src/assets/svg/de-json.svg new file mode 100644 index 0000000..9e62a6c --- /dev/null +++ b/frontend/src/assets/svg/de-json.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/de-move.svg b/frontend/src/assets/svg/de-move.svg new file mode 100644 index 0000000..ffa1904 --- /dev/null +++ b/frontend/src/assets/svg/de-move.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/de-search.svg b/frontend/src/assets/svg/de-search.svg new file mode 100644 index 0000000..0986c29 --- /dev/null +++ b/frontend/src/assets/svg/de-search.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/de-sql-new.svg b/frontend/src/assets/svg/de-sql-new.svg new file mode 100644 index 0000000..cf83467 --- /dev/null +++ b/frontend/src/assets/svg/de-sql-new.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/de-union-new.svg b/frontend/src/assets/svg/de-union-new.svg new file mode 100644 index 0000000..2a04a4b --- /dev/null +++ b/frontend/src/assets/svg/de-union-new.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/de_pwd_invisible.svg b/frontend/src/assets/svg/de_pwd_invisible.svg new file mode 100644 index 0000000..6342b71 --- /dev/null +++ b/frontend/src/assets/svg/de_pwd_invisible.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/de_pwd_visible.svg b/frontend/src/assets/svg/de_pwd_visible.svg new file mode 100644 index 0000000..9b21969 --- /dev/null +++ b/frontend/src/assets/svg/de_pwd_visible.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/default_avatar.svg b/frontend/src/assets/svg/default_avatar.svg new file mode 100644 index 0000000..25c4de5 --- /dev/null +++ b/frontend/src/assets/svg/default_avatar.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/delete.svg b/frontend/src/assets/svg/delete.svg new file mode 100644 index 0000000..a0456c6 --- /dev/null +++ b/frontend/src/assets/svg/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/display-setting.svg b/frontend/src/assets/svg/display-setting.svg new file mode 100644 index 0000000..3f59054 --- /dev/null +++ b/frontend/src/assets/svg/display-setting.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/doc.svg b/frontend/src/assets/svg/doc.svg new file mode 100644 index 0000000..c34965c --- /dev/null +++ b/frontend/src/assets/svg/doc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/docs.svg b/frontend/src/assets/svg/docs.svg new file mode 100644 index 0000000..f699a59 --- /dev/null +++ b/frontend/src/assets/svg/docs.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/doris-ds.svg b/frontend/src/assets/svg/doris-ds.svg new file mode 100644 index 0000000..6d12f37 --- /dev/null +++ b/frontend/src/assets/svg/doris-ds.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/drag.svg b/frontend/src/assets/svg/drag.svg new file mode 100644 index 0000000..cf76a2e --- /dev/null +++ b/frontend/src/assets/svg/drag.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/driver-de.svg b/frontend/src/assets/svg/driver-de.svg new file mode 100644 index 0000000..e83b507 --- /dev/null +++ b/frontend/src/assets/svg/driver-de.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/ds-api.svg b/frontend/src/assets/svg/ds-api.svg new file mode 100644 index 0000000..479f848 --- /dev/null +++ b/frontend/src/assets/svg/ds-api.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/ds-custom.svg b/frontend/src/assets/svg/ds-custom.svg new file mode 100644 index 0000000..fa99b94 --- /dev/null +++ b/frontend/src/assets/svg/ds-custom.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/ds-db.svg b/frontend/src/assets/svg/ds-db.svg new file mode 100644 index 0000000..8b96d0d --- /dev/null +++ b/frontend/src/assets/svg/ds-db.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/ds-excel.svg b/frontend/src/assets/svg/ds-excel.svg new file mode 100644 index 0000000..5bf5ed7 --- /dev/null +++ b/frontend/src/assets/svg/ds-excel.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/ds-sql.svg b/frontend/src/assets/svg/ds-sql.svg new file mode 100644 index 0000000..10c7673 --- /dev/null +++ b/frontend/src/assets/svg/ds-sql.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/ds-union.svg b/frontend/src/assets/svg/ds-union.svg new file mode 100644 index 0000000..5428e82 --- /dev/null +++ b/frontend/src/assets/svg/ds-union.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/dv-ai-window-max.svg b/frontend/src/assets/svg/dv-ai-window-max.svg new file mode 100644 index 0000000..65f2115 --- /dev/null +++ b/frontend/src/assets/svg/dv-ai-window-max.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-ai-window-min.svg b/frontend/src/assets/svg/dv-ai-window-min.svg new file mode 100644 index 0000000..58794a4 --- /dev/null +++ b/frontend/src/assets/svg/dv-ai-window-min.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-ai.svg b/frontend/src/assets/svg/dv-ai.svg new file mode 100644 index 0000000..7927339 --- /dev/null +++ b/frontend/src/assets/svg/dv-ai.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/dv-bar-enlarge.svg b/frontend/src/assets/svg/dv-bar-enlarge.svg new file mode 100644 index 0000000..1926160 --- /dev/null +++ b/frontend/src/assets/svg/dv-bar-enlarge.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/dv-bar-unLinkage.svg b/frontend/src/assets/svg/dv-bar-unLinkage.svg new file mode 100644 index 0000000..e66f5a5 --- /dev/null +++ b/frontend/src/assets/svg/dv-bar-unLinkage.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/dv-batch.svg b/frontend/src/assets/svg/dv-batch.svg new file mode 100644 index 0000000..c5429c8 --- /dev/null +++ b/frontend/src/assets/svg/dv-batch.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-batch_white.svg b/frontend/src/assets/svg/dv-batch_white.svg new file mode 100644 index 0000000..bac2bf8 --- /dev/null +++ b/frontend/src/assets/svg/dv-batch_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-copy-dark.svg b/frontend/src/assets/svg/dv-copy-dark.svg new file mode 100644 index 0000000..9a1c506 --- /dev/null +++ b/frontend/src/assets/svg/dv-copy-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-copy.svg b/frontend/src/assets/svg/dv-copy.svg new file mode 100644 index 0000000..5d26c4f --- /dev/null +++ b/frontend/src/assets/svg/dv-copy.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-dashboard-spine-mobile.svg b/frontend/src/assets/svg/dv-dashboard-spine-mobile.svg new file mode 100644 index 0000000..a7be939 --- /dev/null +++ b/frontend/src/assets/svg/dv-dashboard-spine-mobile.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/dv-dashboard-spine.svg b/frontend/src/assets/svg/dv-dashboard-spine.svg new file mode 100644 index 0000000..1da45d6 --- /dev/null +++ b/frontend/src/assets/svg/dv-dashboard-spine.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/dv-dashboard.svg b/frontend/src/assets/svg/dv-dashboard.svg new file mode 100644 index 0000000..999a080 --- /dev/null +++ b/frontend/src/assets/svg/dv-dashboard.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/dv-dashboard_white.svg b/frontend/src/assets/svg/dv-dashboard_white.svg new file mode 100644 index 0000000..03b8d2b --- /dev/null +++ b/frontend/src/assets/svg/dv-dashboard_white.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/dv-delete.svg b/frontend/src/assets/svg/dv-delete.svg new file mode 100644 index 0000000..79faadf --- /dev/null +++ b/frontend/src/assets/svg/dv-delete.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-details.svg b/frontend/src/assets/svg/dv-details.svg new file mode 100644 index 0000000..7680bf9 --- /dev/null +++ b/frontend/src/assets/svg/dv-details.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-drag-tips.svg b/frontend/src/assets/svg/dv-drag-tips.svg new file mode 100644 index 0000000..21902f0 --- /dev/null +++ b/frontend/src/assets/svg/dv-drag-tips.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/dv-edit.svg b/frontend/src/assets/svg/dv-edit.svg new file mode 100644 index 0000000..f91779b --- /dev/null +++ b/frontend/src/assets/svg/dv-edit.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-empty.svg b/frontend/src/assets/svg/dv-empty.svg new file mode 100644 index 0000000..88843ac --- /dev/null +++ b/frontend/src/assets/svg/dv-empty.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/svg/dv-expand-down.svg b/frontend/src/assets/svg/dv-expand-down.svg new file mode 100644 index 0000000..e0d1dfa --- /dev/null +++ b/frontend/src/assets/svg/dv-expand-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-expand-right.svg b/frontend/src/assets/svg/dv-expand-right.svg new file mode 100644 index 0000000..99cffc3 --- /dev/null +++ b/frontend/src/assets/svg/dv-expand-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-eye-close.svg b/frontend/src/assets/svg/dv-eye-close.svg new file mode 100644 index 0000000..5cec700 --- /dev/null +++ b/frontend/src/assets/svg/dv-eye-close.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-filter-show.svg b/frontend/src/assets/svg/dv-filter-show.svg new file mode 100644 index 0000000..438f8d1 --- /dev/null +++ b/frontend/src/assets/svg/dv-filter-show.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-filter.svg b/frontend/src/assets/svg/dv-filter.svg new file mode 100644 index 0000000..d064197 --- /dev/null +++ b/frontend/src/assets/svg/dv-filter.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-folder.svg b/frontend/src/assets/svg/dv-folder.svg new file mode 100644 index 0000000..57fdb02 --- /dev/null +++ b/frontend/src/assets/svg/dv-folder.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/dv-head-more.svg b/frontend/src/assets/svg/dv-head-more.svg new file mode 100644 index 0000000..69f69a6 --- /dev/null +++ b/frontend/src/assets/svg/dv-head-more.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-hidden.svg b/frontend/src/assets/svg/dv-hidden.svg new file mode 100644 index 0000000..722ce8c --- /dev/null +++ b/frontend/src/assets/svg/dv-hidden.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/dv-info.svg b/frontend/src/assets/svg/dv-info.svg new file mode 100644 index 0000000..ba5b133 --- /dev/null +++ b/frontend/src/assets/svg/dv-info.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/dv-link-target.svg b/frontend/src/assets/svg/dv-link-target.svg new file mode 100644 index 0000000..da22305 --- /dev/null +++ b/frontend/src/assets/svg/dv-link-target.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-lock.svg b/frontend/src/assets/svg/dv-lock.svg new file mode 100644 index 0000000..2610f94 --- /dev/null +++ b/frontend/src/assets/svg/dv-lock.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-material.svg b/frontend/src/assets/svg/dv-material.svg new file mode 100644 index 0000000..e1cb013 --- /dev/null +++ b/frontend/src/assets/svg/dv-material.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/dv-max.svg b/frontend/src/assets/svg/dv-max.svg new file mode 100644 index 0000000..693745b --- /dev/null +++ b/frontend/src/assets/svg/dv-max.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/svg/dv-media.svg b/frontend/src/assets/svg/dv-media.svg new file mode 100644 index 0000000..2bdfeb2 --- /dev/null +++ b/frontend/src/assets/svg/dv-media.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/dv-min.svg b/frontend/src/assets/svg/dv-min.svg new file mode 100644 index 0000000..6eca09e --- /dev/null +++ b/frontend/src/assets/svg/dv-min.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-more-com.svg b/frontend/src/assets/svg/dv-more-com.svg new file mode 100644 index 0000000..6ef3534 --- /dev/null +++ b/frontend/src/assets/svg/dv-more-com.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/dv-more-time-clock.svg b/frontend/src/assets/svg/dv-more-time-clock.svg new file mode 100644 index 0000000..bf7ddcb --- /dev/null +++ b/frontend/src/assets/svg/dv-more-time-clock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/dv-more.svg b/frontend/src/assets/svg/dv-more.svg new file mode 100644 index 0000000..67b3a62 --- /dev/null +++ b/frontend/src/assets/svg/dv-more.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-move.svg b/frontend/src/assets/svg/dv-move.svg new file mode 100644 index 0000000..7f173c2 --- /dev/null +++ b/frontend/src/assets/svg/dv-move.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-new-folder.svg b/frontend/src/assets/svg/dv-new-folder.svg new file mode 100644 index 0000000..ebe4f9a --- /dev/null +++ b/frontend/src/assets/svg/dv-new-folder.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/dv-new.svg b/frontend/src/assets/svg/dv-new.svg new file mode 100644 index 0000000..76ac290 --- /dev/null +++ b/frontend/src/assets/svg/dv-new.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-no-img.svg b/frontend/src/assets/svg/dv-no-img.svg new file mode 100644 index 0000000..ea98493 --- /dev/null +++ b/frontend/src/assets/svg/dv-no-img.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/dv-nothing.svg b/frontend/src/assets/svg/dv-nothing.svg new file mode 100644 index 0000000..45830fe --- /dev/null +++ b/frontend/src/assets/svg/dv-nothing.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/dv-params.svg b/frontend/src/assets/svg/dv-params.svg new file mode 100644 index 0000000..b38d58f --- /dev/null +++ b/frontend/src/assets/svg/dv-params.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-picture-real.svg b/frontend/src/assets/svg/dv-picture-real.svg new file mode 100644 index 0000000..efcf6fe --- /dev/null +++ b/frontend/src/assets/svg/dv-picture-real.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-picture-show.svg b/frontend/src/assets/svg/dv-picture-show.svg new file mode 100644 index 0000000..03f5f28 --- /dev/null +++ b/frontend/src/assets/svg/dv-picture-show.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-picture.svg b/frontend/src/assets/svg/dv-picture.svg new file mode 100644 index 0000000..03f5f28 --- /dev/null +++ b/frontend/src/assets/svg/dv-picture.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-preview-download.svg b/frontend/src/assets/svg/dv-preview-download.svg new file mode 100644 index 0000000..cfaf8c2 --- /dev/null +++ b/frontend/src/assets/svg/dv-preview-download.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/dv-preview-inner.svg b/frontend/src/assets/svg/dv-preview-inner.svg new file mode 100644 index 0000000..d50a951 --- /dev/null +++ b/frontend/src/assets/svg/dv-preview-inner.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-preview-outer.svg b/frontend/src/assets/svg/dv-preview-outer.svg new file mode 100644 index 0000000..58e8056 --- /dev/null +++ b/frontend/src/assets/svg/dv-preview-outer.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-preview.svg b/frontend/src/assets/svg/dv-preview.svg new file mode 100644 index 0000000..bdf80b8 --- /dev/null +++ b/frontend/src/assets/svg/dv-preview.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-rename.svg b/frontend/src/assets/svg/dv-rename.svg new file mode 100644 index 0000000..8547ef7 --- /dev/null +++ b/frontend/src/assets/svg/dv-rename.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/dv-reposition.svg b/frontend/src/assets/svg/dv-reposition.svg new file mode 100644 index 0000000..dd0c617 --- /dev/null +++ b/frontend/src/assets/svg/dv-reposition.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/dv-richText.svg b/frontend/src/assets/svg/dv-richText.svg new file mode 100644 index 0000000..e87faf7 --- /dev/null +++ b/frontend/src/assets/svg/dv-richText.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-ruler.svg b/frontend/src/assets/svg/dv-ruler.svg new file mode 100644 index 0000000..46f262a --- /dev/null +++ b/frontend/src/assets/svg/dv-ruler.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-screen-new.svg b/frontend/src/assets/svg/dv-screen-new.svg new file mode 100644 index 0000000..a249590 --- /dev/null +++ b/frontend/src/assets/svg/dv-screen-new.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/dv-screen-spine.svg b/frontend/src/assets/svg/dv-screen-spine.svg new file mode 100644 index 0000000..5da4106 --- /dev/null +++ b/frontend/src/assets/svg/dv-screen-spine.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/dv-scroll-text.svg b/frontend/src/assets/svg/dv-scroll-text.svg new file mode 100644 index 0000000..897afec --- /dev/null +++ b/frontend/src/assets/svg/dv-scroll-text.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/dv-share.svg b/frontend/src/assets/svg/dv-share.svg new file mode 100644 index 0000000..6f76a9b --- /dev/null +++ b/frontend/src/assets/svg/dv-share.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-show.svg b/frontend/src/assets/svg/dv-show.svg new file mode 100644 index 0000000..3690059 --- /dev/null +++ b/frontend/src/assets/svg/dv-show.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-sort-asc.svg b/frontend/src/assets/svg/dv-sort-asc.svg new file mode 100644 index 0000000..253a3ce --- /dev/null +++ b/frontend/src/assets/svg/dv-sort-asc.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/dv-sort-desc.svg b/frontend/src/assets/svg/dv-sort-desc.svg new file mode 100644 index 0000000..4b66661 --- /dev/null +++ b/frontend/src/assets/svg/dv-sort-desc.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/dv-style-activeFont.svg b/frontend/src/assets/svg/dv-style-activeFont.svg new file mode 100644 index 0000000..8fe1bc3 --- /dev/null +++ b/frontend/src/assets/svg/dv-style-activeFont.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-style-activeFontSize.svg b/frontend/src/assets/svg/dv-style-activeFontSize.svg new file mode 100644 index 0000000..3259b0c --- /dev/null +++ b/frontend/src/assets/svg/dv-style-activeFontSize.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/dv-style-backgroundColor.svg b/frontend/src/assets/svg/dv-style-backgroundColor.svg new file mode 100644 index 0000000..84188fe --- /dev/null +++ b/frontend/src/assets/svg/dv-style-backgroundColor.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-style-blur.svg b/frontend/src/assets/svg/dv-style-blur.svg new file mode 100644 index 0000000..7acdb45 --- /dev/null +++ b/frontend/src/assets/svg/dv-style-blur.svg @@ -0,0 +1,12 @@ + + + 模糊修复 + + + + + + + + + diff --git a/frontend/src/assets/svg/dv-style-borderColor.svg b/frontend/src/assets/svg/dv-style-borderColor.svg new file mode 100644 index 0000000..3cb03b1 --- /dev/null +++ b/frontend/src/assets/svg/dv-style-borderColor.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-style-borderRadius.svg b/frontend/src/assets/svg/dv-style-borderRadius.svg new file mode 100644 index 0000000..165add9 --- /dev/null +++ b/frontend/src/assets/svg/dv-style-borderRadius.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-style-borderSize.svg b/frontend/src/assets/svg/dv-style-borderSize.svg new file mode 100644 index 0000000..517b816 --- /dev/null +++ b/frontend/src/assets/svg/dv-style-borderSize.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-style-borderStyle.svg b/frontend/src/assets/svg/dv-style-borderStyle.svg new file mode 100644 index 0000000..58789ef --- /dev/null +++ b/frontend/src/assets/svg/dv-style-borderStyle.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-style-color.svg b/frontend/src/assets/svg/dv-style-color.svg new file mode 100644 index 0000000..99855ff --- /dev/null +++ b/frontend/src/assets/svg/dv-style-color.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-style-fontFamily.svg b/frontend/src/assets/svg/dv-style-fontFamily.svg new file mode 100644 index 0000000..4f6721e --- /dev/null +++ b/frontend/src/assets/svg/dv-style-fontFamily.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-style-fontSize.svg b/frontend/src/assets/svg/dv-style-fontSize.svg new file mode 100644 index 0000000..15adad7 --- /dev/null +++ b/frontend/src/assets/svg/dv-style-fontSize.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-style-fontWeight.svg b/frontend/src/assets/svg/dv-style-fontWeight.svg new file mode 100644 index 0000000..5bb6f78 --- /dev/null +++ b/frontend/src/assets/svg/dv-style-fontWeight.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/dv-style-headBorderActiveColor.svg b/frontend/src/assets/svg/dv-style-headBorderActiveColor.svg new file mode 100644 index 0000000..5b558eb --- /dev/null +++ b/frontend/src/assets/svg/dv-style-headBorderActiveColor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/dv-style-headBorderColor.svg b/frontend/src/assets/svg/dv-style-headBorderColor.svg new file mode 100644 index 0000000..21e8be5 --- /dev/null +++ b/frontend/src/assets/svg/dv-style-headBorderColor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/dv-style-headFontActiveColor.svg b/frontend/src/assets/svg/dv-style-headFontActiveColor.svg new file mode 100644 index 0000000..4c12a4e --- /dev/null +++ b/frontend/src/assets/svg/dv-style-headFontActiveColor.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-style-headFontColor.svg b/frontend/src/assets/svg/dv-style-headFontColor.svg new file mode 100644 index 0000000..0e897c0 --- /dev/null +++ b/frontend/src/assets/svg/dv-style-headFontColor.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-style-headHorizontalPosition.svg b/frontend/src/assets/svg/dv-style-headHorizontalPosition.svg new file mode 100644 index 0000000..82aab47 --- /dev/null +++ b/frontend/src/assets/svg/dv-style-headHorizontalPosition.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-style-letterSpacing.svg b/frontend/src/assets/svg/dv-style-letterSpacing.svg new file mode 100644 index 0000000..cedd710 --- /dev/null +++ b/frontend/src/assets/svg/dv-style-letterSpacing.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-style-lineHeight.svg b/frontend/src/assets/svg/dv-style-lineHeight.svg new file mode 100644 index 0000000..a310698 --- /dev/null +++ b/frontend/src/assets/svg/dv-style-lineHeight.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-style-opacity.svg b/frontend/src/assets/svg/dv-style-opacity.svg new file mode 100644 index 0000000..38a7187 --- /dev/null +++ b/frontend/src/assets/svg/dv-style-opacity.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-style-scroll-speed.svg b/frontend/src/assets/svg/dv-style-scroll-speed.svg new file mode 100644 index 0000000..e0634c5 --- /dev/null +++ b/frontend/src/assets/svg/dv-style-scroll-speed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/dv-style-tab-head.svg b/frontend/src/assets/svg/dv-style-tab-head.svg new file mode 100644 index 0000000..23d91ef --- /dev/null +++ b/frontend/src/assets/svg/dv-style-tab-head.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-style-textAlign.svg b/frontend/src/assets/svg/dv-style-textAlign.svg new file mode 100644 index 0000000..8206d93 --- /dev/null +++ b/frontend/src/assets/svg/dv-style-textAlign.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-tab-show.svg b/frontend/src/assets/svg/dv-tab-show.svg new file mode 100644 index 0000000..c67a1a5 --- /dev/null +++ b/frontend/src/assets/svg/dv-tab-show.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-tab.svg b/frontend/src/assets/svg/dv-tab.svg new file mode 100644 index 0000000..5625982 --- /dev/null +++ b/frontend/src/assets/svg/dv-tab.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-text.svg b/frontend/src/assets/svg/dv-text.svg new file mode 100644 index 0000000..ff6d406 --- /dev/null +++ b/frontend/src/assets/svg/dv-text.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-unlock.svg b/frontend/src/assets/svg/dv-unlock.svg new file mode 100644 index 0000000..a8b53de --- /dev/null +++ b/frontend/src/assets/svg/dv-unlock.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-up-arrow.svg b/frontend/src/assets/svg/dv-up-arrow.svg new file mode 100644 index 0000000..f6f4348 --- /dev/null +++ b/frontend/src/assets/svg/dv-up-arrow.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/dv-use-template.svg b/frontend/src/assets/svg/dv-use-template.svg new file mode 100644 index 0000000..cae2630 --- /dev/null +++ b/frontend/src/assets/svg/dv-use-template.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-video.svg b/frontend/src/assets/svg/dv-video.svg new file mode 100644 index 0000000..1700f7f --- /dev/null +++ b/frontend/src/assets/svg/dv-video.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/dv-view.svg b/frontend/src/assets/svg/dv-view.svg new file mode 100644 index 0000000..1909312 --- /dev/null +++ b/frontend/src/assets/svg/dv-view.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/dv_mobile_layout.svg b/frontend/src/assets/svg/dv_mobile_layout.svg new file mode 100644 index 0000000..b242b08 --- /dev/null +++ b/frontend/src/assets/svg/dv_mobile_layout.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/edit-done.svg b/frontend/src/assets/svg/edit-done.svg new file mode 100644 index 0000000..e2049b6 --- /dev/null +++ b/frontend/src/assets/svg/edit-done.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/edit-in.svg b/frontend/src/assets/svg/edit-in.svg new file mode 100644 index 0000000..95428d1 --- /dev/null +++ b/frontend/src/assets/svg/edit-in.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/edit.svg b/frontend/src/assets/svg/edit.svg new file mode 100644 index 0000000..dc80a3e --- /dev/null +++ b/frontend/src/assets/svg/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/email-task.svg b/frontend/src/assets/svg/email-task.svg new file mode 100644 index 0000000..be6e900 --- /dev/null +++ b/frontend/src/assets/svg/email-task.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/embedded.svg b/frontend/src/assets/svg/embedded.svg new file mode 100644 index 0000000..251744a --- /dev/null +++ b/frontend/src/assets/svg/embedded.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/es-ds.svg b/frontend/src/assets/svg/es-ds.svg new file mode 100644 index 0000000..60e5577 --- /dev/null +++ b/frontend/src/assets/svg/es-ds.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/example.svg b/frontend/src/assets/svg/example.svg new file mode 100644 index 0000000..46f42b5 --- /dev/null +++ b/frontend/src/assets/svg/example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/exclamationmark.svg b/frontend/src/assets/svg/exclamationmark.svg new file mode 100644 index 0000000..612b620 --- /dev/null +++ b/frontend/src/assets/svg/exclamationmark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/exclamationmark2.svg b/frontend/src/assets/svg/exclamationmark2.svg new file mode 100644 index 0000000..6e45d42 --- /dev/null +++ b/frontend/src/assets/svg/exclamationmark2.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/exit-fullscreen.svg b/frontend/src/assets/svg/exit-fullscreen.svg new file mode 100644 index 0000000..485c128 --- /dev/null +++ b/frontend/src/assets/svg/exit-fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/eye-open.svg b/frontend/src/assets/svg/eye-open.svg new file mode 100644 index 0000000..88dcc98 --- /dev/null +++ b/frontend/src/assets/svg/eye-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/eye.svg b/frontend/src/assets/svg/eye.svg new file mode 100644 index 0000000..4865e0e --- /dev/null +++ b/frontend/src/assets/svg/eye.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/field_location.svg b/frontend/src/assets/svg/field_location.svg new file mode 100644 index 0000000..7def81f --- /dev/null +++ b/frontend/src/assets/svg/field_location.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/field_text.svg b/frontend/src/assets/svg/field_text.svg new file mode 100644 index 0000000..16fa778 --- /dev/null +++ b/frontend/src/assets/svg/field_text.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/field_time.svg b/frontend/src/assets/svg/field_time.svg new file mode 100644 index 0000000..b5c0c4e --- /dev/null +++ b/frontend/src/assets/svg/field_time.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/field_url.svg b/frontend/src/assets/svg/field_url.svg new file mode 100644 index 0000000..4d6d540 --- /dev/null +++ b/frontend/src/assets/svg/field_url.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/field_value.svg b/frontend/src/assets/svg/field_value.svg new file mode 100644 index 0000000..b907e4f --- /dev/null +++ b/frontend/src/assets/svg/field_value.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/filter-center.svg b/frontend/src/assets/svg/filter-center.svg new file mode 100644 index 0000000..0cf66b6 --- /dev/null +++ b/frontend/src/assets/svg/filter-center.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/filter-h-center.svg b/frontend/src/assets/svg/filter-h-center.svg new file mode 100644 index 0000000..366d8c7 --- /dev/null +++ b/frontend/src/assets/svg/filter-h-center.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/filter-h-left.svg b/frontend/src/assets/svg/filter-h-left.svg new file mode 100644 index 0000000..d7f5a89 --- /dev/null +++ b/frontend/src/assets/svg/filter-h-left.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/filter-h-right.svg b/frontend/src/assets/svg/filter-h-right.svg new file mode 100644 index 0000000..21100e0 --- /dev/null +++ b/frontend/src/assets/svg/filter-h-right.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/filter-params.svg b/frontend/src/assets/svg/filter-params.svg new file mode 100644 index 0000000..ebe0221 --- /dev/null +++ b/frontend/src/assets/svg/filter-params.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/filter-top.svg b/frontend/src/assets/svg/filter-top.svg new file mode 100644 index 0000000..1d4da9e --- /dev/null +++ b/frontend/src/assets/svg/filter-top.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/filter.svg b/frontend/src/assets/svg/filter.svg new file mode 100644 index 0000000..d533bd3 --- /dev/null +++ b/frontend/src/assets/svg/filter.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/flow-map-dark.svg b/frontend/src/assets/svg/flow-map-dark.svg new file mode 100644 index 0000000..ab44292 --- /dev/null +++ b/frontend/src/assets/svg/flow-map-dark.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/flow-map-origin.svg b/frontend/src/assets/svg/flow-map-origin.svg new file mode 100644 index 0000000..5651cb0 --- /dev/null +++ b/frontend/src/assets/svg/flow-map-origin.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/flow-map.svg b/frontend/src/assets/svg/flow-map.svg new file mode 100644 index 0000000..72469aa --- /dev/null +++ b/frontend/src/assets/svg/flow-map.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/folder.svg b/frontend/src/assets/svg/folder.svg new file mode 100644 index 0000000..8216001 --- /dev/null +++ b/frontend/src/assets/svg/folder.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/form.svg b/frontend/src/assets/svg/form.svg new file mode 100644 index 0000000..dcbaa18 --- /dev/null +++ b/frontend/src/assets/svg/form.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/fullscreen.svg b/frontend/src/assets/svg/fullscreen.svg new file mode 100644 index 0000000..0e86b6f --- /dev/null +++ b/frontend/src/assets/svg/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/funnel-dark.svg b/frontend/src/assets/svg/funnel-dark.svg new file mode 100644 index 0000000..4ded995 --- /dev/null +++ b/frontend/src/assets/svg/funnel-dark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/funnel-origin.svg b/frontend/src/assets/svg/funnel-origin.svg new file mode 100644 index 0000000..e784314 --- /dev/null +++ b/frontend/src/assets/svg/funnel-origin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/funnel.svg b/frontend/src/assets/svg/funnel.svg new file mode 100644 index 0000000..ad8ad47 --- /dev/null +++ b/frontend/src/assets/svg/funnel.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/gauge-dark.svg b/frontend/src/assets/svg/gauge-dark.svg new file mode 100644 index 0000000..336e172 --- /dev/null +++ b/frontend/src/assets/svg/gauge-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/gauge-origin.svg b/frontend/src/assets/svg/gauge-origin.svg new file mode 100644 index 0000000..8fe48ce --- /dev/null +++ b/frontend/src/assets/svg/gauge-origin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/gauge.svg b/frontend/src/assets/svg/gauge.svg new file mode 100644 index 0000000..336e172 --- /dev/null +++ b/frontend/src/assets/svg/gauge.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/graphical-circular.svg b/frontend/src/assets/svg/graphical-circular.svg new file mode 100644 index 0000000..cdffe09 --- /dev/null +++ b/frontend/src/assets/svg/graphical-circular.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/graphical-rect.svg b/frontend/src/assets/svg/graphical-rect.svg new file mode 100644 index 0000000..1bbb69c --- /dev/null +++ b/frontend/src/assets/svg/graphical-rect.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/graphical-triangle.svg b/frontend/src/assets/svg/graphical-triangle.svg new file mode 100644 index 0000000..74a6439 --- /dev/null +++ b/frontend/src/assets/svg/graphical-triangle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/group-3400.svg b/frontend/src/assets/svg/group-3400.svg new file mode 100644 index 0000000..d0712e3 --- /dev/null +++ b/frontend/src/assets/svg/group-3400.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/group.svg b/frontend/src/assets/svg/group.svg new file mode 100644 index 0000000..8216001 --- /dev/null +++ b/frontend/src/assets/svg/group.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/heat-map-dark.svg b/frontend/src/assets/svg/heat-map-dark.svg new file mode 100644 index 0000000..a5aff12 --- /dev/null +++ b/frontend/src/assets/svg/heat-map-dark.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/heat-map-origin.svg b/frontend/src/assets/svg/heat-map-origin.svg new file mode 100644 index 0000000..3bf0491 --- /dev/null +++ b/frontend/src/assets/svg/heat-map-origin.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/heat-map.svg b/frontend/src/assets/svg/heat-map.svg new file mode 100644 index 0000000..7e8a1c8 --- /dev/null +++ b/frontend/src/assets/svg/heat-map.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/icon-alarmclock.svg b/frontend/src/assets/svg/icon-alarmclock.svg new file mode 100644 index 0000000..dfb628f --- /dev/null +++ b/frontend/src/assets/svg/icon-alarmclock.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon-contacts.svg b/frontend/src/assets/svg/icon-contacts.svg new file mode 100644 index 0000000..f8cf9ae --- /dev/null +++ b/frontend/src/assets/svg/icon-contacts.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon-draft.svg b/frontend/src/assets/svg/icon-draft.svg new file mode 100644 index 0000000..7d757c9 --- /dev/null +++ b/frontend/src/assets/svg/icon-draft.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon-filter.svg b/frontend/src/assets/svg/icon-filter.svg new file mode 100644 index 0000000..c613a73 --- /dev/null +++ b/frontend/src/assets/svg/icon-filter.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon-group.svg b/frontend/src/assets/svg/icon-group.svg new file mode 100644 index 0000000..6255652 --- /dev/null +++ b/frontend/src/assets/svg/icon-group.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon-image-upload.svg b/frontend/src/assets/svg/icon-image-upload.svg new file mode 100644 index 0000000..f4033e4 --- /dev/null +++ b/frontend/src/assets/svg/icon-image-upload.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/icon-image.svg b/frontend/src/assets/svg/icon-image.svg new file mode 100644 index 0000000..f67e6c6 --- /dev/null +++ b/frontend/src/assets/svg/icon-image.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/icon-laser.svg b/frontend/src/assets/svg/icon-laser.svg new file mode 100644 index 0000000..5df3a64 --- /dev/null +++ b/frontend/src/assets/svg/icon-laser.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon-lock.svg b/frontend/src/assets/svg/icon-lock.svg new file mode 100644 index 0000000..65cf212 --- /dev/null +++ b/frontend/src/assets/svg/icon-lock.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon-maybe.svg b/frontend/src/assets/svg/icon-maybe.svg new file mode 100644 index 0000000..c2c801b --- /dev/null +++ b/frontend/src/assets/svg/icon-maybe.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon-maybe_outlined.svg b/frontend/src/assets/svg/icon-maybe_outlined.svg new file mode 100644 index 0000000..ac3de0e --- /dev/null +++ b/frontend/src/assets/svg/icon-maybe_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon-more.svg b/frontend/src/assets/svg/icon-more.svg new file mode 100644 index 0000000..76178f1 --- /dev/null +++ b/frontend/src/assets/svg/icon-more.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon-quicksetting.svg b/frontend/src/assets/svg/icon-quicksetting.svg new file mode 100644 index 0000000..cf705e8 --- /dev/null +++ b/frontend/src/assets/svg/icon-quicksetting.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon-setting.svg b/frontend/src/assets/svg/icon-setting.svg new file mode 100644 index 0000000..857e45a --- /dev/null +++ b/frontend/src/assets/svg/icon-setting.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon-stream.svg b/frontend/src/assets/svg/icon-stream.svg new file mode 100644 index 0000000..f0624b7 --- /dev/null +++ b/frontend/src/assets/svg/icon-stream.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon-video.svg b/frontend/src/assets/svg/icon-video.svg new file mode 100644 index 0000000..4568a1e --- /dev/null +++ b/frontend/src/assets/svg/icon-video.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon/ctrl/close.svg b/frontend/src/assets/svg/icon/ctrl/close.svg new file mode 100644 index 0000000..14f93b6 --- /dev/null +++ b/frontend/src/assets/svg/icon/ctrl/close.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon/outline/notification.svg b/frontend/src/assets/svg/icon/outline/notification.svg new file mode 100644 index 0000000..25b9723 --- /dev/null +++ b/frontend/src/assets/svg/icon/outline/notification.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_Batch_outlined.svg b/frontend/src/assets/svg/icon_Batch_outlined.svg new file mode 100644 index 0000000..21f6c5d --- /dev/null +++ b/frontend/src/assets/svg/icon_Batch_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_Invalid_colorful.svg b/frontend/src/assets/svg/icon_Invalid_colorful.svg new file mode 100644 index 0000000..bcfc7cb --- /dev/null +++ b/frontend/src/assets/svg/icon_Invalid_colorful.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_add-dictionary_outlined.svg b/frontend/src/assets/svg/icon_add-dictionary_outlined.svg new file mode 100644 index 0000000..b2e9708 --- /dev/null +++ b/frontend/src/assets/svg/icon_add-dictionary_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_add-entry_outlined.svg b/frontend/src/assets/svg/icon_add-entry_outlined.svg new file mode 100644 index 0000000..443fda7 --- /dev/null +++ b/frontend/src/assets/svg/icon_add-entry_outlined.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/icon_add-folder_outlined.svg b/frontend/src/assets/svg/icon_add-folder_outlined.svg new file mode 100644 index 0000000..066b59f --- /dev/null +++ b/frontend/src/assets/svg/icon_add-folder_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_add_outlined-1.svg b/frontend/src/assets/svg/icon_add_outlined-1.svg new file mode 100644 index 0000000..12c99c3 --- /dev/null +++ b/frontend/src/assets/svg/icon_add_outlined-1.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_add_outlined.svg b/frontend/src/assets/svg/icon_add_outlined.svg new file mode 100644 index 0000000..f3364bb --- /dev/null +++ b/frontend/src/assets/svg/icon_add_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_adjustment_outlined.svg b/frontend/src/assets/svg/icon_adjustment_outlined.svg new file mode 100644 index 0000000..d07b213 --- /dev/null +++ b/frontend/src/assets/svg/icon_adjustment_outlined.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/icon_admin_outlined.svg b/frontend/src/assets/svg/icon_admin_outlined.svg new file mode 100644 index 0000000..40404d0 --- /dev/null +++ b/frontend/src/assets/svg/icon_admin_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_api-outlined.svg b/frontend/src/assets/svg/icon_api-outlined.svg new file mode 100644 index 0000000..d006314 --- /dev/null +++ b/frontend/src/assets/svg/icon_api-outlined.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/icon_api.svg b/frontend/src/assets/svg/icon_api.svg new file mode 100644 index 0000000..4caf125 --- /dev/null +++ b/frontend/src/assets/svg/icon_api.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_app_outlined.svg b/frontend/src/assets/svg/icon_app_outlined.svg new file mode 100644 index 0000000..3548abb --- /dev/null +++ b/frontend/src/assets/svg/icon_app_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_arrow-right_outlined.svg b/frontend/src/assets/svg/icon_arrow-right_outlined.svg new file mode 100644 index 0000000..e7020ea --- /dev/null +++ b/frontend/src/assets/svg/icon_arrow-right_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_assigned_outlined.svg b/frontend/src/assets/svg/icon_assigned_outlined.svg new file mode 100644 index 0000000..9f0f15f --- /dev/null +++ b/frontend/src/assets/svg/icon_assigned_outlined.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_attachment_outlined.svg b/frontend/src/assets/svg/icon_attachment_outlined.svg new file mode 100644 index 0000000..1b93ae1 --- /dev/null +++ b/frontend/src/assets/svg/icon_attachment_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_bold_outlined.svg b/frontend/src/assets/svg/icon_bold_outlined.svg new file mode 100644 index 0000000..19cd7a7 --- /dev/null +++ b/frontend/src/assets/svg/icon_bold_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_bottom-align_outlined.svg b/frontend/src/assets/svg/icon_bottom-align_outlined.svg new file mode 100644 index 0000000..d9de589 --- /dev/null +++ b/frontend/src/assets/svg/icon_bottom-align_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_calendar-calculated_outlined-1.svg b/frontend/src/assets/svg/icon_calendar-calculated_outlined-1.svg new file mode 100644 index 0000000..f80b8c0 --- /dev/null +++ b/frontend/src/assets/svg/icon_calendar-calculated_outlined-1.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/icon_calendar-calculated_outlined.svg b/frontend/src/assets/svg/icon_calendar-calculated_outlined.svg new file mode 100644 index 0000000..e7c22eb --- /dev/null +++ b/frontend/src/assets/svg/icon_calendar-calculated_outlined.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/icon_calendar_outlined.svg b/frontend/src/assets/svg/icon_calendar_outlined.svg new file mode 100644 index 0000000..c7f73df --- /dev/null +++ b/frontend/src/assets/svg/icon_calendar_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_cancel_store.svg b/frontend/src/assets/svg/icon_cancel_store.svg new file mode 100644 index 0000000..f14c04f --- /dev/null +++ b/frontend/src/assets/svg/icon_cancel_store.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/icon_card_outlined.svg b/frontend/src/assets/svg/icon_card_outlined.svg new file mode 100644 index 0000000..ca7111b --- /dev/null +++ b/frontend/src/assets/svg/icon_card_outlined.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/icon_center-alignment_outlined.svg b/frontend/src/assets/svg/icon_center-alignment_outlined.svg new file mode 100644 index 0000000..d524c3a --- /dev/null +++ b/frontend/src/assets/svg/icon_center-alignment_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_chart-line-c.svg b/frontend/src/assets/svg/icon_chart-line-c.svg new file mode 100644 index 0000000..6afc7dc --- /dev/null +++ b/frontend/src/assets/svg/icon_chart-line-c.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_chart-line.svg b/frontend/src/assets/svg/icon_chart-line.svg new file mode 100644 index 0000000..9a42cdd --- /dev/null +++ b/frontend/src/assets/svg/icon_chart-line.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_clear_outlined.svg b/frontend/src/assets/svg/icon_clear_outlined.svg new file mode 100644 index 0000000..ab015c6 --- /dev/null +++ b/frontend/src/assets/svg/icon_clear_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_close_filled.svg b/frontend/src/assets/svg/icon_close_filled.svg new file mode 100644 index 0000000..ca6a974 --- /dev/null +++ b/frontend/src/assets/svg/icon_close_filled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_close_outlined.svg b/frontend/src/assets/svg/icon_close_outlined.svg new file mode 100644 index 0000000..124221e --- /dev/null +++ b/frontend/src/assets/svg/icon_close_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_collect_filled.svg b/frontend/src/assets/svg/icon_collect_filled.svg new file mode 100644 index 0000000..f55a99e --- /dev/null +++ b/frontend/src/assets/svg/icon_collect_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_collection_outlined.svg b/frontend/src/assets/svg/icon_collection_outlined.svg new file mode 100644 index 0000000..b5a0a4d --- /dev/null +++ b/frontend/src/assets/svg/icon_collection_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_copy_filled.svg b/frontend/src/assets/svg/icon_copy_filled.svg new file mode 100644 index 0000000..f3a23c6 --- /dev/null +++ b/frontend/src/assets/svg/icon_copy_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_copy_outlined.svg b/frontend/src/assets/svg/icon_copy_outlined.svg new file mode 100644 index 0000000..ec20e00 --- /dev/null +++ b/frontend/src/assets/svg/icon_copy_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_customize.svg b/frontend/src/assets/svg/icon_customize.svg new file mode 100644 index 0000000..ff3780d --- /dev/null +++ b/frontend/src/assets/svg/icon_customize.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/icon_dashboard.svg b/frontend/src/assets/svg/icon_dashboard.svg new file mode 100644 index 0000000..b726772 --- /dev/null +++ b/frontend/src/assets/svg/icon_dashboard.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_dashboard_outlined-c.svg b/frontend/src/assets/svg/icon_dashboard_outlined-c.svg new file mode 100644 index 0000000..630be3d --- /dev/null +++ b/frontend/src/assets/svg/icon_dashboard_outlined-c.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_dashboard_outlined.svg b/frontend/src/assets/svg/icon_dashboard_outlined.svg new file mode 100644 index 0000000..3435cd2 --- /dev/null +++ b/frontend/src/assets/svg/icon_dashboard_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_data-visualization.svg b/frontend/src/assets/svg/icon_data-visualization.svg new file mode 100644 index 0000000..8303575 --- /dev/null +++ b/frontend/src/assets/svg/icon_data-visualization.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/icon_database-alert_filled.svg b/frontend/src/assets/svg/icon_database-alert_filled.svg new file mode 100644 index 0000000..d5429b1 --- /dev/null +++ b/frontend/src/assets/svg/icon_database-alert_filled.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/icon_database-fail_filled.svg b/frontend/src/assets/svg/icon_database-fail_filled.svg new file mode 100644 index 0000000..9e8c6df --- /dev/null +++ b/frontend/src/assets/svg/icon_database-fail_filled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_database.svg b/frontend/src/assets/svg/icon_database.svg new file mode 100644 index 0000000..b07810e --- /dev/null +++ b/frontend/src/assets/svg/icon_database.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_database_outlined.svg b/frontend/src/assets/svg/icon_database_outlined.svg new file mode 100644 index 0000000..b19453c --- /dev/null +++ b/frontend/src/assets/svg/icon_database_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_dataset.svg b/frontend/src/assets/svg/icon_dataset.svg new file mode 100644 index 0000000..da26700 --- /dev/null +++ b/frontend/src/assets/svg/icon_dataset.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_dataset_outlined.svg b/frontend/src/assets/svg/icon_dataset_outlined.svg new file mode 100644 index 0000000..ad16256 --- /dev/null +++ b/frontend/src/assets/svg/icon_dataset_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_db_filled.svg b/frontend/src/assets/svg/icon_db_filled.svg new file mode 100644 index 0000000..3cf911b --- /dev/null +++ b/frontend/src/assets/svg/icon_db_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_delete-trash_outlined.svg b/frontend/src/assets/svg/icon_delete-trash_outlined.svg new file mode 100644 index 0000000..c71a843 --- /dev/null +++ b/frontend/src/assets/svg/icon_delete-trash_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_describe_outlined.svg b/frontend/src/assets/svg/icon_describe_outlined.svg new file mode 100644 index 0000000..cfabccf --- /dev/null +++ b/frontend/src/assets/svg/icon_describe_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_dialpad_outlined.svg b/frontend/src/assets/svg/icon_dialpad_outlined.svg new file mode 100644 index 0000000..8398158 --- /dev/null +++ b/frontend/src/assets/svg/icon_dialpad_outlined.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_disorde-list_outlined.svg b/frontend/src/assets/svg/icon_disorde-list_outlined.svg new file mode 100644 index 0000000..312fa84 --- /dev/null +++ b/frontend/src/assets/svg/icon_disorde-list_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_divider_outlined.svg b/frontend/src/assets/svg/icon_divider_outlined.svg new file mode 100644 index 0000000..71575af --- /dev/null +++ b/frontend/src/assets/svg/icon_divider_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_doc-replace_outlined.svg b/frontend/src/assets/svg/icon_doc-replace_outlined.svg new file mode 100644 index 0000000..9b9398a --- /dev/null +++ b/frontend/src/assets/svg/icon_doc-replace_outlined.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/icon_done_outlined.svg b/frontend/src/assets/svg/icon_done_outlined.svg new file mode 100644 index 0000000..4c60343 --- /dev/null +++ b/frontend/src/assets/svg/icon_done_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_down-right_outlined.svg b/frontend/src/assets/svg/icon_down-right_outlined.svg new file mode 100644 index 0000000..8feb48f --- /dev/null +++ b/frontend/src/assets/svg/icon_down-right_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_down_outlined-1.svg b/frontend/src/assets/svg/icon_down_outlined-1.svg new file mode 100644 index 0000000..7daaede --- /dev/null +++ b/frontend/src/assets/svg/icon_down_outlined-1.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_down_outlined.svg b/frontend/src/assets/svg/icon_down_outlined.svg new file mode 100644 index 0000000..c34cbfb --- /dev/null +++ b/frontend/src/assets/svg/icon_down_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_download_outlined.svg b/frontend/src/assets/svg/icon_download_outlined.svg new file mode 100644 index 0000000..dca3c20 --- /dev/null +++ b/frontend/src/assets/svg/icon_download_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_drag_outlined.svg b/frontend/src/assets/svg/icon_drag_outlined.svg new file mode 100644 index 0000000..0244241 --- /dev/null +++ b/frontend/src/assets/svg/icon_drag_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_drilling_outlined.svg b/frontend/src/assets/svg/icon_drilling_outlined.svg new file mode 100644 index 0000000..6686858 --- /dev/null +++ b/frontend/src/assets/svg/icon_drilling_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_drive_filled.svg b/frontend/src/assets/svg/icon_drive_filled.svg new file mode 100644 index 0000000..b9fb354 --- /dev/null +++ b/frontend/src/assets/svg/icon_drive_filled.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_edit_outlined.svg b/frontend/src/assets/svg/icon_edit_outlined.svg new file mode 100644 index 0000000..817c15c --- /dev/null +++ b/frontend/src/assets/svg/icon_edit_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_effects_outlined.svg b/frontend/src/assets/svg/icon_effects_outlined.svg new file mode 100644 index 0000000..6d798aa --- /dev/null +++ b/frontend/src/assets/svg/icon_effects_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_excel.svg b/frontend/src/assets/svg/icon_excel.svg new file mode 100644 index 0000000..d215044 --- /dev/null +++ b/frontend/src/assets/svg/icon_excel.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_excel_outlined.svg b/frontend/src/assets/svg/icon_excel_outlined.svg new file mode 100644 index 0000000..fa0542a --- /dev/null +++ b/frontend/src/assets/svg/icon_excel_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_expand-down_filled.svg b/frontend/src/assets/svg/icon_expand-down_filled.svg new file mode 100644 index 0000000..9c96d7e --- /dev/null +++ b/frontend/src/assets/svg/icon_expand-down_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_expand-left_filled.svg b/frontend/src/assets/svg/icon_expand-left_filled.svg new file mode 100644 index 0000000..0a102b2 --- /dev/null +++ b/frontend/src/assets/svg/icon_expand-left_filled.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_expand-right_filled.svg b/frontend/src/assets/svg/icon_expand-right_filled.svg new file mode 100644 index 0000000..804bf01 --- /dev/null +++ b/frontend/src/assets/svg/icon_expand-right_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_file-add_outlined.svg b/frontend/src/assets/svg/icon_file-add_outlined.svg new file mode 100644 index 0000000..669ede9 --- /dev/null +++ b/frontend/src/assets/svg/icon_file-add_outlined.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/icon_file-doc_colorful.svg b/frontend/src/assets/svg/icon_file-doc_colorful.svg new file mode 100644 index 0000000..7371ef7 --- /dev/null +++ b/frontend/src/assets/svg/icon_file-doc_colorful.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_file-excel_colorful.svg b/frontend/src/assets/svg/icon_file-excel_colorful.svg new file mode 100644 index 0000000..8355979 --- /dev/null +++ b/frontend/src/assets/svg/icon_file-excel_colorful.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_file-font_colorful.svg b/frontend/src/assets/svg/icon_file-font_colorful.svg new file mode 100644 index 0000000..21e43cf --- /dev/null +++ b/frontend/src/assets/svg/icon_file-font_colorful.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_folder_filled.svg b/frontend/src/assets/svg/icon_folder_filled.svg new file mode 100644 index 0000000..149f804 --- /dev/null +++ b/frontend/src/assets/svg/icon_folder_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_font-color_outlined.svg b/frontend/src/assets/svg/icon_font-color_outlined.svg new file mode 100644 index 0000000..f5f1798 --- /dev/null +++ b/frontend/src/assets/svg/icon_font-color_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_font.svg b/frontend/src/assets/svg/icon_font.svg new file mode 100644 index 0000000..769b6a5 --- /dev/null +++ b/frontend/src/assets/svg/icon_font.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_form_outlined.svg b/frontend/src/assets/svg/icon_form_outlined.svg new file mode 100644 index 0000000..b688bb3 --- /dev/null +++ b/frontend/src/assets/svg/icon_form_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_form_outlined_white.svg b/frontend/src/assets/svg/icon_form_outlined_white.svg new file mode 100644 index 0000000..b6087c9 --- /dev/null +++ b/frontend/src/assets/svg/icon_form_outlined_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_free.svg b/frontend/src/assets/svg/icon_free.svg new file mode 100644 index 0000000..96d3018 --- /dev/null +++ b/frontend/src/assets/svg/icon_free.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_full-association.svg b/frontend/src/assets/svg/icon_full-association.svg new file mode 100644 index 0000000..f880448 --- /dev/null +++ b/frontend/src/assets/svg/icon_full-association.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/icon_functions_outlined.svg b/frontend/src/assets/svg/icon_functions_outlined.svg new file mode 100644 index 0000000..af8273a --- /dev/null +++ b/frontend/src/assets/svg/icon_functions_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_graphical.svg b/frontend/src/assets/svg/icon_graphical.svg new file mode 100644 index 0000000..78871f6 --- /dev/null +++ b/frontend/src/assets/svg/icon_graphical.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/icon_gridlines_outlined.svg b/frontend/src/assets/svg/icon_gridlines_outlined.svg new file mode 100644 index 0000000..0b5cd51 --- /dev/null +++ b/frontend/src/assets/svg/icon_gridlines_outlined.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_h1_outlined.svg b/frontend/src/assets/svg/icon_h1_outlined.svg new file mode 100644 index 0000000..82868fe --- /dev/null +++ b/frontend/src/assets/svg/icon_h1_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_h2_outlined.svg b/frontend/src/assets/svg/icon_h2_outlined.svg new file mode 100644 index 0000000..fba5686 --- /dev/null +++ b/frontend/src/assets/svg/icon_h2_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_h3_outlined.svg b/frontend/src/assets/svg/icon_h3_outlined.svg new file mode 100644 index 0000000..dbb7149 --- /dev/null +++ b/frontend/src/assets/svg/icon_h3_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_hn_outlined.svg b/frontend/src/assets/svg/icon_hn_outlined.svg new file mode 100644 index 0000000..853b5fd --- /dev/null +++ b/frontend/src/assets/svg/icon_hn_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_horizontal-align_outlined.svg b/frontend/src/assets/svg/icon_horizontal-align_outlined.svg new file mode 100644 index 0000000..6e96d2d --- /dev/null +++ b/frontend/src/assets/svg/icon_horizontal-align_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_info_colorful.svg b/frontend/src/assets/svg/icon_info_colorful.svg new file mode 100644 index 0000000..cb82d21 --- /dev/null +++ b/frontend/src/assets/svg/icon_info_colorful.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_info_filled.svg b/frontend/src/assets/svg/icon_info_filled.svg new file mode 100644 index 0000000..34439a4 --- /dev/null +++ b/frontend/src/assets/svg/icon_info_filled.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_info_outlined.svg b/frontend/src/assets/svg/icon_info_outlined.svg new file mode 100644 index 0000000..eebdd2f --- /dev/null +++ b/frontend/src/assets/svg/icon_info_outlined.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_intersect.svg b/frontend/src/assets/svg/icon_intersect.svg new file mode 100644 index 0000000..4513716 --- /dev/null +++ b/frontend/src/assets/svg/icon_intersect.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/icon_into-item_outlined.svg b/frontend/src/assets/svg/icon_into-item_outlined.svg new file mode 100644 index 0000000..296a79a --- /dev/null +++ b/frontend/src/assets/svg/icon_into-item_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_invisible_outlined.svg b/frontend/src/assets/svg/icon_invisible_outlined.svg new file mode 100644 index 0000000..8f2869c --- /dev/null +++ b/frontend/src/assets/svg/icon_invisible_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_italic_outlined.svg b/frontend/src/assets/svg/icon_italic_outlined.svg new file mode 100644 index 0000000..db85e3e --- /dev/null +++ b/frontend/src/assets/svg/icon_italic_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_left-align_outlined.svg b/frontend/src/assets/svg/icon_left-align_outlined.svg new file mode 100644 index 0000000..b03c48c --- /dev/null +++ b/frontend/src/assets/svg/icon_left-align_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_left-alignment_outlined.svg b/frontend/src/assets/svg/icon_left-alignment_outlined.svg new file mode 100644 index 0000000..13ffb12 --- /dev/null +++ b/frontend/src/assets/svg/icon_left-alignment_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_left-association.svg b/frontend/src/assets/svg/icon_left-association.svg new file mode 100644 index 0000000..2a20b41 --- /dev/null +++ b/frontend/src/assets/svg/icon_left-association.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/icon_left_outlined.svg b/frontend/src/assets/svg/icon_left_outlined.svg new file mode 100644 index 0000000..a102147 --- /dev/null +++ b/frontend/src/assets/svg/icon_left_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_letter-spacing_outlined.svg b/frontend/src/assets/svg/icon_letter-spacing_outlined.svg new file mode 100644 index 0000000..2648904 --- /dev/null +++ b/frontend/src/assets/svg/icon_letter-spacing_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_link-calculated_outlined-1.svg b/frontend/src/assets/svg/icon_link-calculated_outlined-1.svg new file mode 100644 index 0000000..14e4764 --- /dev/null +++ b/frontend/src/assets/svg/icon_link-calculated_outlined-1.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/icon_link-calculated_outlined.svg b/frontend/src/assets/svg/icon_link-calculated_outlined.svg new file mode 100644 index 0000000..0d93deb --- /dev/null +++ b/frontend/src/assets/svg/icon_link-calculated_outlined.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/icon_link-record.svg b/frontend/src/assets/svg/icon_link-record.svg new file mode 100644 index 0000000..5ba2d69 --- /dev/null +++ b/frontend/src/assets/svg/icon_link-record.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_link-record_outlined-1.svg b/frontend/src/assets/svg/icon_link-record_outlined-1.svg new file mode 100644 index 0000000..f7feacf --- /dev/null +++ b/frontend/src/assets/svg/icon_link-record_outlined-1.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_link-record_outlined.svg b/frontend/src/assets/svg/icon_link-record_outlined.svg new file mode 100644 index 0000000..77a322b --- /dev/null +++ b/frontend/src/assets/svg/icon_link-record_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_loading_outlined.svg b/frontend/src/assets/svg/icon_loading_outlined.svg new file mode 100644 index 0000000..1ef953a --- /dev/null +++ b/frontend/src/assets/svg/icon_loading_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_local-calculated_outlined-1.svg b/frontend/src/assets/svg/icon_local-calculated_outlined-1.svg new file mode 100644 index 0000000..e05567c --- /dev/null +++ b/frontend/src/assets/svg/icon_local-calculated_outlined-1.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_local-calculated_outlined.svg b/frontend/src/assets/svg/icon_local-calculated_outlined.svg new file mode 100644 index 0000000..a11bb57 --- /dev/null +++ b/frontend/src/assets/svg/icon_local-calculated_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_local.svg b/frontend/src/assets/svg/icon_local.svg new file mode 100644 index 0000000..0c7c0d7 --- /dev/null +++ b/frontend/src/assets/svg/icon_local.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_local_outlined.svg b/frontend/src/assets/svg/icon_local_outlined.svg new file mode 100644 index 0000000..b4a534e --- /dev/null +++ b/frontend/src/assets/svg/icon_local_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_logs_outlined-1.svg b/frontend/src/assets/svg/icon_logs_outlined-1.svg new file mode 100644 index 0000000..353e1a1 --- /dev/null +++ b/frontend/src/assets/svg/icon_logs_outlined-1.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_logs_outlined.svg b/frontend/src/assets/svg/icon_logs_outlined.svg new file mode 100644 index 0000000..9cf28e2 --- /dev/null +++ b/frontend/src/assets/svg/icon_logs_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_magnify_outlined.svg b/frontend/src/assets/svg/icon_magnify_outlined.svg new file mode 100644 index 0000000..5893f97 --- /dev/null +++ b/frontend/src/assets/svg/icon_magnify_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_member-add_outlined.svg b/frontend/src/assets/svg/icon_member-add_outlined.svg new file mode 100644 index 0000000..da2b8ef --- /dev/null +++ b/frontend/src/assets/svg/icon_member-add_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_member_filled.svg b/frontend/src/assets/svg/icon_member_filled.svg new file mode 100644 index 0000000..ce50081 --- /dev/null +++ b/frontend/src/assets/svg/icon_member_filled.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/svg/icon_mfa_reset.svg b/frontend/src/assets/svg/icon_mfa_reset.svg new file mode 100644 index 0000000..194a3b8 --- /dev/null +++ b/frontend/src/assets/svg/icon_mfa_reset.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/icon_minify_outlined.svg b/frontend/src/assets/svg/icon_minify_outlined.svg new file mode 100644 index 0000000..2037d7f --- /dev/null +++ b/frontend/src/assets/svg/icon_minify_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_moments-categories_outlined.svg b/frontend/src/assets/svg/icon_moments-categories_outlined.svg new file mode 100644 index 0000000..bdd434c --- /dev/null +++ b/frontend/src/assets/svg/icon_moments-categories_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_more-vertical_outlined.svg b/frontend/src/assets/svg/icon_more-vertical_outlined.svg new file mode 100644 index 0000000..a97197c --- /dev/null +++ b/frontend/src/assets/svg/icon_more-vertical_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_more-vertical_outlined_white.svg b/frontend/src/assets/svg/icon_more-vertical_outlined_white.svg new file mode 100644 index 0000000..f6cd13c --- /dev/null +++ b/frontend/src/assets/svg/icon_more-vertical_outlined_white.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_more_outlined.svg b/frontend/src/assets/svg/icon_more_outlined.svg new file mode 100644 index 0000000..f6fec92 --- /dev/null +++ b/frontend/src/assets/svg/icon_more_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_msg_fill.svg b/frontend/src/assets/svg/icon_msg_fill.svg new file mode 100644 index 0000000..7ce39cc --- /dev/null +++ b/frontend/src/assets/svg/icon_msg_fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/icon_multi-line_outlined.svg b/frontend/src/assets/svg/icon_multi-line_outlined.svg new file mode 100644 index 0000000..24c4b1c --- /dev/null +++ b/frontend/src/assets/svg/icon_multi-line_outlined.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_new-item_outlined.svg b/frontend/src/assets/svg/icon_new-item_outlined.svg new file mode 100644 index 0000000..3c99da7 --- /dev/null +++ b/frontend/src/assets/svg/icon_new-item_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_notification_filled.svg b/frontend/src/assets/svg/icon_notification_filled.svg new file mode 100644 index 0000000..8e17a11 --- /dev/null +++ b/frontend/src/assets/svg/icon_notification_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_notification_outlined.svg b/frontend/src/assets/svg/icon_notification_outlined.svg new file mode 100644 index 0000000..236a152 --- /dev/null +++ b/frontend/src/assets/svg/icon_notification_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_number-calculated_outlined-1.svg b/frontend/src/assets/svg/icon_number-calculated_outlined-1.svg new file mode 100644 index 0000000..0d18a77 --- /dev/null +++ b/frontend/src/assets/svg/icon_number-calculated_outlined-1.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_number-calculated_outlined.svg b/frontend/src/assets/svg/icon_number-calculated_outlined.svg new file mode 100644 index 0000000..434fa04 --- /dev/null +++ b/frontend/src/assets/svg/icon_number-calculated_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_number_outlined.svg b/frontend/src/assets/svg/icon_number_outlined.svg new file mode 100644 index 0000000..d6d7e66 --- /dev/null +++ b/frontend/src/assets/svg/icon_number_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_operation-analysis_outlined.svg b/frontend/src/assets/svg/icon_operation-analysis_outlined.svg new file mode 100644 index 0000000..6a792ff --- /dev/null +++ b/frontend/src/assets/svg/icon_operation-analysis_outlined.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/icon_orde-list_outlined.svg b/frontend/src/assets/svg/icon_orde-list_outlined.svg new file mode 100644 index 0000000..66c8ed5 --- /dev/null +++ b/frontend/src/assets/svg/icon_orde-list_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_organization_outlined.svg b/frontend/src/assets/svg/icon_organization_outlined.svg new file mode 100644 index 0000000..38e011d --- /dev/null +++ b/frontend/src/assets/svg/icon_organization_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_params_setting.svg b/frontend/src/assets/svg/icon_params_setting.svg new file mode 100644 index 0000000..3dcde7c --- /dev/null +++ b/frontend/src/assets/svg/icon_params_setting.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/icon_pc_fullscreen.svg b/frontend/src/assets/svg/icon_pc_fullscreen.svg new file mode 100644 index 0000000..8c2bdac --- /dev/null +++ b/frontend/src/assets/svg/icon_pc_fullscreen.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/icon_pc_outlined.svg b/frontend/src/assets/svg/icon_pc_outlined.svg new file mode 100644 index 0000000..b5ce9a2 --- /dev/null +++ b/frontend/src/assets/svg/icon_pc_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_pc_outlined_copy.svg b/frontend/src/assets/svg/icon_pc_outlined_copy.svg new file mode 100644 index 0000000..b5ce9a2 --- /dev/null +++ b/frontend/src/assets/svg/icon_pc_outlined_copy.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_phone_outlined.svg b/frontend/src/assets/svg/icon_phone_outlined.svg new file mode 100644 index 0000000..92ef5b1 --- /dev/null +++ b/frontend/src/assets/svg/icon_phone_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_pie_outlined-c.svg b/frontend/src/assets/svg/icon_pie_outlined-c.svg new file mode 100644 index 0000000..898dd65 --- /dev/null +++ b/frontend/src/assets/svg/icon_pie_outlined-c.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_play-round_filled.svg b/frontend/src/assets/svg/icon_play-round_filled.svg new file mode 100644 index 0000000..54bcfcc --- /dev/null +++ b/frontend/src/assets/svg/icon_play-round_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_play-round_outlined.svg b/frontend/src/assets/svg/icon_play-round_outlined.svg new file mode 100644 index 0000000..c39cecb --- /dev/null +++ b/frontend/src/assets/svg/icon_play-round_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_play_round_outlined_white.svg b/frontend/src/assets/svg/icon_play_round_outlined_white.svg new file mode 100644 index 0000000..4ee6d01 --- /dev/null +++ b/frontend/src/assets/svg/icon_play_round_outlined_white.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_plugin_outlined.svg b/frontend/src/assets/svg/icon_plugin_outlined.svg new file mode 100644 index 0000000..6be9844 --- /dev/null +++ b/frontend/src/assets/svg/icon_plugin_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_preferences_outlined.svg b/frontend/src/assets/svg/icon_preferences_outlined.svg new file mode 100644 index 0000000..4ea7279 --- /dev/null +++ b/frontend/src/assets/svg/icon_preferences_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_pull-left_outlined.svg b/frontend/src/assets/svg/icon_pull-left_outlined.svg new file mode 100644 index 0000000..848ddb3 --- /dev/null +++ b/frontend/src/assets/svg/icon_pull-left_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_pull-right_outlined.svg b/frontend/src/assets/svg/icon_pull-right_outlined.svg new file mode 100644 index 0000000..86d2767 --- /dev/null +++ b/frontend/src/assets/svg/icon_pull-right_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_qr_outlined.svg b/frontend/src/assets/svg/icon_qr_outlined.svg new file mode 100644 index 0000000..8fb87a4 --- /dev/null +++ b/frontend/src/assets/svg/icon_qr_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_radio_outlined.svg b/frontend/src/assets/svg/icon_radio_outlined.svg new file mode 100644 index 0000000..2206cec --- /dev/null +++ b/frontend/src/assets/svg/icon_radio_outlined.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_redo_outlined.svg b/frontend/src/assets/svg/icon_redo_outlined.svg new file mode 100644 index 0000000..7330b74 --- /dev/null +++ b/frontend/src/assets/svg/icon_redo_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_refresh_outlined.svg b/frontend/src/assets/svg/icon_refresh_outlined.svg new file mode 100644 index 0000000..5765c40 --- /dev/null +++ b/frontend/src/assets/svg/icon_refresh_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_rename_outlined.svg b/frontend/src/assets/svg/icon_rename_outlined.svg new file mode 100644 index 0000000..962cc5f --- /dev/null +++ b/frontend/src/assets/svg/icon_rename_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_replace_outlined.svg b/frontend/src/assets/svg/icon_replace_outlined.svg new file mode 100644 index 0000000..446d290 --- /dev/null +++ b/frontend/src/assets/svg/icon_replace_outlined.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/svg/icon_reset_outlined.svg b/frontend/src/assets/svg/icon_reset_outlined.svg new file mode 100644 index 0000000..4610736 --- /dev/null +++ b/frontend/src/assets/svg/icon_reset_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_resetpassword.svg b/frontend/src/assets/svg/icon_resetpassword.svg new file mode 100644 index 0000000..c8d6bf3 --- /dev/null +++ b/frontend/src/assets/svg/icon_resetpassword.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_right-align_outlined.svg b/frontend/src/assets/svg/icon_right-align_outlined.svg new file mode 100644 index 0000000..d4a5fe7 --- /dev/null +++ b/frontend/src/assets/svg/icon_right-align_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_right-alignment_outlined.svg b/frontend/src/assets/svg/icon_right-alignment_outlined.svg new file mode 100644 index 0000000..6f9357f --- /dev/null +++ b/frontend/src/assets/svg/icon_right-alignment_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_right-association.svg b/frontend/src/assets/svg/icon_right-association.svg new file mode 100644 index 0000000..f4a0659 --- /dev/null +++ b/frontend/src/assets/svg/icon_right-association.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/icon_right_outlined.svg b/frontend/src/assets/svg/icon_right_outlined.svg new file mode 100644 index 0000000..3ac3478 --- /dev/null +++ b/frontend/src/assets/svg/icon_right_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_scroll_filled.svg b/frontend/src/assets/svg/icon_scroll_filled.svg new file mode 100644 index 0000000..7169587 --- /dev/null +++ b/frontend/src/assets/svg/icon_scroll_filled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_search-outline_outlined.svg b/frontend/src/assets/svg/icon_search-outline_outlined.svg new file mode 100644 index 0000000..dde44c1 --- /dev/null +++ b/frontend/src/assets/svg/icon_search-outline_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_search.svg b/frontend/src/assets/svg/icon_search.svg new file mode 100644 index 0000000..fa50c16 --- /dev/null +++ b/frontend/src/assets/svg/icon_search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/icon_security.svg b/frontend/src/assets/svg/icon_security.svg new file mode 100644 index 0000000..9e2635b --- /dev/null +++ b/frontend/src/assets/svg/icon_security.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/icon_share-label_filled.svg b/frontend/src/assets/svg/icon_share-label_filled.svg new file mode 100644 index 0000000..db0cb09 --- /dev/null +++ b/frontend/src/assets/svg/icon_share-label_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_share-label_outlined.svg b/frontend/src/assets/svg/icon_share-label_outlined.svg new file mode 100644 index 0000000..9538478 --- /dev/null +++ b/frontend/src/assets/svg/icon_share-label_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_side-expand_outlined.svg b/frontend/src/assets/svg/icon_side-expand_outlined.svg new file mode 100644 index 0000000..8278cbb --- /dev/null +++ b/frontend/src/assets/svg/icon_side-expand_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_side-fold_outlined.svg b/frontend/src/assets/svg/icon_side-fold_outlined.svg new file mode 100644 index 0000000..5a46a35 --- /dev/null +++ b/frontend/src/assets/svg/icon_side-fold_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_single-line_outlined.svg b/frontend/src/assets/svg/icon_single-line_outlined.svg new file mode 100644 index 0000000..5cd8e7b --- /dev/null +++ b/frontend/src/assets/svg/icon_single-line_outlined.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_sort-a-to-z_outlined.svg b/frontend/src/assets/svg/icon_sort-a-to-z_outlined.svg new file mode 100644 index 0000000..332c076 --- /dev/null +++ b/frontend/src/assets/svg/icon_sort-a-to-z_outlined.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/icon_sort-z-to-a_outlined.svg b/frontend/src/assets/svg/icon_sort-z-to-a_outlined.svg new file mode 100644 index 0000000..960d320 --- /dev/null +++ b/frontend/src/assets/svg/icon_sort-z-to-a_outlined.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/icon_sort_outlined.svg b/frontend/src/assets/svg/icon_sort_outlined.svg new file mode 100644 index 0000000..5063832 --- /dev/null +++ b/frontend/src/assets/svg/icon_sort_outlined.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_sort_priority.svg b/frontend/src/assets/svg/icon_sort_priority.svg new file mode 100644 index 0000000..04ac42d --- /dev/null +++ b/frontend/src/assets/svg/icon_sort_priority.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/icon_sql.svg b/frontend/src/assets/svg/icon_sql.svg new file mode 100644 index 0000000..38e658c --- /dev/null +++ b/frontend/src/assets/svg/icon_sql.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_sql_outlined.svg b/frontend/src/assets/svg/icon_sql_outlined.svg new file mode 100644 index 0000000..c873600 --- /dev/null +++ b/frontend/src/assets/svg/icon_sql_outlined.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/icon_sql_outlined_1.svg b/frontend/src/assets/svg/icon_sql_outlined_1.svg new file mode 100644 index 0000000..f3cda84 --- /dev/null +++ b/frontend/src/assets/svg/icon_sql_outlined_1.svg @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_stretch_outlined.svg b/frontend/src/assets/svg/icon_stretch_outlined.svg new file mode 100644 index 0000000..416c92c --- /dev/null +++ b/frontend/src/assets/svg/icon_stretch_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_style-set_outlined.svg b/frontend/src/assets/svg/icon_style-set_outlined.svg new file mode 100644 index 0000000..83d7a73 --- /dev/null +++ b/frontend/src/assets/svg/icon_style-set_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_succeed_colorful.svg b/frontend/src/assets/svg/icon_succeed_colorful.svg new file mode 100644 index 0000000..b8f976a --- /dev/null +++ b/frontend/src/assets/svg/icon_succeed_colorful.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_succeed_filled.svg b/frontend/src/assets/svg/icon_succeed_filled.svg new file mode 100644 index 0000000..6945b1b --- /dev/null +++ b/frontend/src/assets/svg/icon_succeed_filled.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_switch_outlined.svg b/frontend/src/assets/svg/icon_switch_outlined.svg new file mode 100644 index 0000000..80ae0f6 --- /dev/null +++ b/frontend/src/assets/svg/icon_switch_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_sync-play-round_filled.svg b/frontend/src/assets/svg/icon_sync-play-round_filled.svg new file mode 100644 index 0000000..6f81e8f --- /dev/null +++ b/frontend/src/assets/svg/icon_sync-play-round_filled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_sync-play-round_outlined.svg b/frontend/src/assets/svg/icon_sync-play-round_outlined.svg new file mode 100644 index 0000000..0a4d202 --- /dev/null +++ b/frontend/src/assets/svg/icon_sync-play-round_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_sync_close_log_details.svg b/frontend/src/assets/svg/icon_sync_close_log_details.svg new file mode 100644 index 0000000..7c0dc4c --- /dev/null +++ b/frontend/src/assets/svg/icon_sync_close_log_details.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_sync_datasource.svg b/frontend/src/assets/svg/icon_sync_datasource.svg new file mode 100644 index 0000000..1e3fc9d --- /dev/null +++ b/frontend/src/assets/svg/icon_sync_datasource.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_sync_free.svg b/frontend/src/assets/svg/icon_sync_free.svg new file mode 100644 index 0000000..9e36d03 --- /dev/null +++ b/frontend/src/assets/svg/icon_sync_free.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_sync_log_number.svg b/frontend/src/assets/svg/icon_sync_log_number.svg new file mode 100644 index 0000000..49869e0 --- /dev/null +++ b/frontend/src/assets/svg/icon_sync_log_number.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_sync_logs_outlined.svg b/frontend/src/assets/svg/icon_sync_logs_outlined.svg new file mode 100644 index 0000000..6dd112e --- /dev/null +++ b/frontend/src/assets/svg/icon_sync_logs_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_sync_progress.svg b/frontend/src/assets/svg/icon_sync_progress.svg new file mode 100644 index 0000000..d8ddb73 --- /dev/null +++ b/frontend/src/assets/svg/icon_sync_progress.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_sync_target_to_datasource.svg b/frontend/src/assets/svg/icon_sync_target_to_datasource.svg new file mode 100644 index 0000000..bc4d526 --- /dev/null +++ b/frontend/src/assets/svg/icon_sync_target_to_datasource.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_sync_task_number.svg b/frontend/src/assets/svg/icon_sync_task_number.svg new file mode 100644 index 0000000..bce4a1e --- /dev/null +++ b/frontend/src/assets/svg/icon_sync_task_number.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_take-action_outlined.svg b/frontend/src/assets/svg/icon_take-action_outlined.svg new file mode 100644 index 0000000..e1891fc --- /dev/null +++ b/frontend/src/assets/svg/icon_take-action_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_team-add_outlined.svg b/frontend/src/assets/svg/icon_team-add_outlined.svg new file mode 100644 index 0000000..c85fe1e --- /dev/null +++ b/frontend/src/assets/svg/icon_team-add_outlined.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_template.svg b/frontend/src/assets/svg/icon_template.svg new file mode 100644 index 0000000..639de0a --- /dev/null +++ b/frontend/src/assets/svg/icon_template.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_template_colorful.svg b/frontend/src/assets/svg/icon_template_colorful.svg new file mode 100644 index 0000000..789b3a0 --- /dev/null +++ b/frontend/src/assets/svg/icon_template_colorful.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/icon_template_outlined.svg b/frontend/src/assets/svg/icon_template_outlined.svg new file mode 100644 index 0000000..8320bf8 --- /dev/null +++ b/frontend/src/assets/svg/icon_template_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_text-box_outlined.svg b/frontend/src/assets/svg/icon_text-box_outlined.svg new file mode 100644 index 0000000..1b23b1c --- /dev/null +++ b/frontend/src/assets/svg/icon_text-box_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_text-calculated_outlined-1.svg b/frontend/src/assets/svg/icon_text-calculated_outlined-1.svg new file mode 100644 index 0000000..7b4227e --- /dev/null +++ b/frontend/src/assets/svg/icon_text-calculated_outlined-1.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_text-calculated_outlined.svg b/frontend/src/assets/svg/icon_text-calculated_outlined.svg new file mode 100644 index 0000000..9019c36 --- /dev/null +++ b/frontend/src/assets/svg/icon_text-calculated_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_text_outlined.svg b/frontend/src/assets/svg/icon_text_outlined.svg new file mode 100644 index 0000000..13047e6 --- /dev/null +++ b/frontend/src/assets/svg/icon_text_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_time_outlined.svg b/frontend/src/assets/svg/icon_time_outlined.svg new file mode 100644 index 0000000..a5d09a9 --- /dev/null +++ b/frontend/src/assets/svg/icon_time_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_title-left-align_outlined.svg b/frontend/src/assets/svg/icon_title-left-align_outlined.svg new file mode 100644 index 0000000..27f7c7f --- /dev/null +++ b/frontend/src/assets/svg/icon_title-left-align_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_title-top-align_outlined.svg b/frontend/src/assets/svg/icon_title-top-align_outlined.svg new file mode 100644 index 0000000..5d3b042 --- /dev/null +++ b/frontend/src/assets/svg/icon_title-top-align_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_todo_outlined.svg b/frontend/src/assets/svg/icon_todo_outlined.svg new file mode 100644 index 0000000..633b9b1 --- /dev/null +++ b/frontend/src/assets/svg/icon_todo_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_top-align_outlined.svg b/frontend/src/assets/svg/icon_top-align_outlined.svg new file mode 100644 index 0000000..695c4e9 --- /dev/null +++ b/frontend/src/assets/svg/icon_top-align_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_undo_outlined.svg b/frontend/src/assets/svg/icon_undo_outlined.svg new file mode 100644 index 0000000..1154ecd --- /dev/null +++ b/frontend/src/assets/svg/icon_undo_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_up-left_outlined.svg b/frontend/src/assets/svg/icon_up-left_outlined.svg new file mode 100644 index 0000000..766e655 --- /dev/null +++ b/frontend/src/assets/svg/icon_up-left_outlined.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/icon_upload_outlined.svg b/frontend/src/assets/svg/icon_upload_outlined.svg new file mode 100644 index 0000000..c13777d --- /dev/null +++ b/frontend/src/assets/svg/icon_upload_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_url_outlined.svg b/frontend/src/assets/svg/icon_url_outlined.svg new file mode 100644 index 0000000..4d6d540 --- /dev/null +++ b/frontend/src/assets/svg/icon_url_outlined.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/icon_vertical-align_outlined.svg b/frontend/src/assets/svg/icon_vertical-align_outlined.svg new file mode 100644 index 0000000..8bd7b60 --- /dev/null +++ b/frontend/src/assets/svg/icon_vertical-align_outlined.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_view-list_outlined.svg b/frontend/src/assets/svg/icon_view-list_outlined.svg new file mode 100644 index 0000000..406d5a9 --- /dev/null +++ b/frontend/src/assets/svg/icon_view-list_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_viewinchat_outlined.svg b/frontend/src/assets/svg/icon_viewinchat_outlined.svg new file mode 100644 index 0000000..3d76192 --- /dev/null +++ b/frontend/src/assets/svg/icon_viewinchat_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_visible_outlined.svg b/frontend/src/assets/svg/icon_visible_outlined.svg new file mode 100644 index 0000000..47b8391 --- /dev/null +++ b/frontend/src/assets/svg/icon_visible_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_warning_colorful.svg b/frontend/src/assets/svg/icon_warning_colorful.svg new file mode 100644 index 0000000..dae340c --- /dev/null +++ b/frontend/src/assets/svg/icon_warning_colorful.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_warning_colorful_red.svg b/frontend/src/assets/svg/icon_warning_colorful_red.svg new file mode 100644 index 0000000..a0ffc79 --- /dev/null +++ b/frontend/src/assets/svg/icon_warning_colorful_red.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/icon_warning_filled.svg b/frontend/src/assets/svg/icon_warning_filled.svg new file mode 100644 index 0000000..045912f --- /dev/null +++ b/frontend/src/assets/svg/icon_warning_filled.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/icon_webhook.svg b/frontend/src/assets/svg/icon_webhook.svg new file mode 100644 index 0000000..2a614b6 --- /dev/null +++ b/frontend/src/assets/svg/icon_webhook.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/icon_yes_outlined.svg b/frontend/src/assets/svg/icon_yes_outlined.svg new file mode 100644 index 0000000..6de4ef4 --- /dev/null +++ b/frontend/src/assets/svg/icon_yes_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/iconfont.svg b/frontend/src/assets/svg/iconfont.svg new file mode 100644 index 0000000..261f9cb --- /dev/null +++ b/frontend/src/assets/svg/iconfont.svg @@ -0,0 +1,71 @@ + + + + + +Created by iconfont + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/impala-ds.svg b/frontend/src/assets/svg/impala-ds.svg new file mode 100644 index 0000000..cb1290f --- /dev/null +++ b/frontend/src/assets/svg/impala-ds.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/indicator-dark.svg b/frontend/src/assets/svg/indicator-dark.svg new file mode 100644 index 0000000..71ec539 --- /dev/null +++ b/frontend/src/assets/svg/indicator-dark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/indicator-origin.svg b/frontend/src/assets/svg/indicator-origin.svg new file mode 100644 index 0000000..f37dd9f --- /dev/null +++ b/frontend/src/assets/svg/indicator-origin.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/indicator.svg b/frontend/src/assets/svg/indicator.svg new file mode 100644 index 0000000..71ec539 --- /dev/null +++ b/frontend/src/assets/svg/indicator.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/inner-join.svg b/frontend/src/assets/svg/inner-join.svg new file mode 100644 index 0000000..8b689f4 --- /dev/null +++ b/frontend/src/assets/svg/inner-join.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/international.svg b/frontend/src/assets/svg/international.svg new file mode 100644 index 0000000..e9b56ee --- /dev/null +++ b/frontend/src/assets/svg/international.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/join-join.svg b/frontend/src/assets/svg/join-join.svg new file mode 100644 index 0000000..da22305 --- /dev/null +++ b/frontend/src/assets/svg/join-join.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/label.svg b/frontend/src/assets/svg/label.svg new file mode 100644 index 0000000..227ed39 --- /dev/null +++ b/frontend/src/assets/svg/label.svg @@ -0,0 +1,2 @@ + + diff --git a/frontend/src/assets/svg/language.svg b/frontend/src/assets/svg/language.svg new file mode 100644 index 0000000..143b3a6 --- /dev/null +++ b/frontend/src/assets/svg/language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/left-join.svg b/frontend/src/assets/svg/left-join.svg new file mode 100644 index 0000000..b4ecde3 --- /dev/null +++ b/frontend/src/assets/svg/left-join.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/line-dark.svg b/frontend/src/assets/svg/line-dark.svg new file mode 100644 index 0000000..c3819a1 --- /dev/null +++ b/frontend/src/assets/svg/line-dark.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/line-origin.svg b/frontend/src/assets/svg/line-origin.svg new file mode 100644 index 0000000..43f590b --- /dev/null +++ b/frontend/src/assets/svg/line-origin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/line.svg b/frontend/src/assets/svg/line.svg new file mode 100644 index 0000000..f2b52d5 --- /dev/null +++ b/frontend/src/assets/svg/line.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/link-back.svg b/frontend/src/assets/svg/link-back.svg new file mode 100644 index 0000000..88339b6 --- /dev/null +++ b/frontend/src/assets/svg/link-back.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/link-down.svg b/frontend/src/assets/svg/link-down.svg new file mode 100644 index 0000000..f0aaca2 --- /dev/null +++ b/frontend/src/assets/svg/link-down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/link.svg b/frontend/src/assets/svg/link.svg new file mode 100644 index 0000000..48197ba --- /dev/null +++ b/frontend/src/assets/svg/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/liquid-dark.svg b/frontend/src/assets/svg/liquid-dark.svg new file mode 100644 index 0000000..694cb16 --- /dev/null +++ b/frontend/src/assets/svg/liquid-dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/liquid-origin.svg b/frontend/src/assets/svg/liquid-origin.svg new file mode 100644 index 0000000..396cb7a --- /dev/null +++ b/frontend/src/assets/svg/liquid-origin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/liquid.svg b/frontend/src/assets/svg/liquid.svg new file mode 100644 index 0000000..2b99d91 --- /dev/null +++ b/frontend/src/assets/svg/liquid.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/lock_closed.svg b/frontend/src/assets/svg/lock_closed.svg new file mode 100644 index 0000000..cea933c --- /dev/null +++ b/frontend/src/assets/svg/lock_closed.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/lock_closed_switch.svg b/frontend/src/assets/svg/lock_closed_switch.svg new file mode 100644 index 0000000..ff11256 --- /dev/null +++ b/frontend/src/assets/svg/lock_closed_switch.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/lock_open.svg b/frontend/src/assets/svg/lock_open.svg new file mode 100644 index 0000000..331563a --- /dev/null +++ b/frontend/src/assets/svg/lock_open.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/lock_open_back.svg b/frontend/src/assets/svg/lock_open_back.svg new file mode 100644 index 0000000..a531f6c --- /dev/null +++ b/frontend/src/assets/svg/lock_open_back.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/lock_other_open.svg b/frontend/src/assets/svg/lock_other_open.svg new file mode 100644 index 0000000..0645e22 --- /dev/null +++ b/frontend/src/assets/svg/lock_other_open.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/log.svg b/frontend/src/assets/svg/log.svg new file mode 100644 index 0000000..2c1a670 --- /dev/null +++ b/frontend/src/assets/svg/log.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/logo.svg b/frontend/src/assets/svg/logo.svg new file mode 100644 index 0000000..366fe68 --- /dev/null +++ b/frontend/src/assets/svg/logo.svg @@ -0,0 +1,20 @@ + + + + background + + + + Layer 1 + + GIS-BI开发平台 + + \ No newline at end of file diff --git a/frontend/src/assets/svg/logo1.svg b/frontend/src/assets/svg/logo1.svg new file mode 100644 index 0000000..6acaee2 --- /dev/null +++ b/frontend/src/assets/svg/logo1.svg @@ -0,0 +1,21 @@ + + + diff --git a/frontend/src/assets/svg/logo_cas.svg b/frontend/src/assets/svg/logo_cas.svg new file mode 100644 index 0000000..7224246 --- /dev/null +++ b/frontend/src/assets/svg/logo_cas.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/logo_dingtalk.svg b/frontend/src/assets/svg/logo_dingtalk.svg new file mode 100644 index 0000000..c392456 --- /dev/null +++ b/frontend/src/assets/svg/logo_dingtalk.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/logo_lark.svg b/frontend/src/assets/svg/logo_lark.svg new file mode 100644 index 0000000..0eaf8df --- /dev/null +++ b/frontend/src/assets/svg/logo_lark.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/logo_ldap.svg b/frontend/src/assets/svg/logo_ldap.svg new file mode 100644 index 0000000..57deedf --- /dev/null +++ b/frontend/src/assets/svg/logo_ldap.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/logo_oauth.svg b/frontend/src/assets/svg/logo_oauth.svg new file mode 100644 index 0000000..89ef68c --- /dev/null +++ b/frontend/src/assets/svg/logo_oauth.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/logo_wechat-work.svg b/frontend/src/assets/svg/logo_wechat-work.svg new file mode 100644 index 0000000..99ac1d3 --- /dev/null +++ b/frontend/src/assets/svg/logo_wechat-work.svg @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/map-dark.svg b/frontend/src/assets/svg/map-dark.svg new file mode 100644 index 0000000..cc136f3 --- /dev/null +++ b/frontend/src/assets/svg/map-dark.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/map-origin.svg b/frontend/src/assets/svg/map-origin.svg new file mode 100644 index 0000000..3c37937 --- /dev/null +++ b/frontend/src/assets/svg/map-origin.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/map.svg b/frontend/src/assets/svg/map.svg new file mode 100644 index 0000000..34578f5 --- /dev/null +++ b/frontend/src/assets/svg/map.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/map_mini.svg b/frontend/src/assets/svg/map_mini.svg new file mode 100644 index 0000000..44ccd35 --- /dev/null +++ b/frontend/src/assets/svg/map_mini.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/map_old.svg b/frontend/src/assets/svg/map_old.svg new file mode 100644 index 0000000..721251a --- /dev/null +++ b/frontend/src/assets/svg/map_old.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/mariadb-ds.svg b/frontend/src/assets/svg/mariadb-ds.svg new file mode 100644 index 0000000..ce664d6 --- /dev/null +++ b/frontend/src/assets/svg/mariadb-ds.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/market-expand.svg b/frontend/src/assets/svg/market-expand.svg new file mode 100644 index 0000000..278a7ef --- /dev/null +++ b/frontend/src/assets/svg/market-expand.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/market-retract.svg b/frontend/src/assets/svg/market-retract.svg new file mode 100644 index 0000000..64fd486 --- /dev/null +++ b/frontend/src/assets/svg/market-retract.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/menu.svg b/frontend/src/assets/svg/menu.svg new file mode 100644 index 0000000..e4360a0 --- /dev/null +++ b/frontend/src/assets/svg/menu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/menuconfig_treeadd_white.svg b/frontend/src/assets/svg/menuconfig_treeadd_white.svg new file mode 100644 index 0000000..bd5e1d9 --- /dev/null +++ b/frontend/src/assets/svg/menuconfig_treeadd_white.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/mobile-checkbox.svg b/frontend/src/assets/svg/mobile-checkbox.svg new file mode 100644 index 0000000..2bee03e --- /dev/null +++ b/frontend/src/assets/svg/mobile-checkbox.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/mobile/icon_dashboard_filled.svg b/frontend/src/assets/svg/mobile/icon_dashboard_filled.svg new file mode 100644 index 0000000..01d33d9 --- /dev/null +++ b/frontend/src/assets/svg/mobile/icon_dashboard_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/mobile/icon_dashboard_outlined.svg b/frontend/src/assets/svg/mobile/icon_dashboard_outlined.svg new file mode 100644 index 0000000..0c87145 --- /dev/null +++ b/frontend/src/assets/svg/mobile/icon_dashboard_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/mobile/icon_home_filled.svg b/frontend/src/assets/svg/mobile/icon_home_filled.svg new file mode 100644 index 0000000..cc62af8 --- /dev/null +++ b/frontend/src/assets/svg/mobile/icon_home_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/mobile/icon_home_outlined.svg b/frontend/src/assets/svg/mobile/icon_home_outlined.svg new file mode 100644 index 0000000..efda93d --- /dev/null +++ b/frontend/src/assets/svg/mobile/icon_home_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/mobile/icon_member_filled.svg b/frontend/src/assets/svg/mobile/icon_member_filled.svg new file mode 100644 index 0000000..4563c32 --- /dev/null +++ b/frontend/src/assets/svg/mobile/icon_member_filled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/mobile/icon_member_outlined.svg b/frontend/src/assets/svg/mobile/icon_member_outlined.svg new file mode 100644 index 0000000..393e75d --- /dev/null +++ b/frontend/src/assets/svg/mobile/icon_member_outlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/mongo-ds.svg b/frontend/src/assets/svg/mongo-ds.svg new file mode 100644 index 0000000..d68949b --- /dev/null +++ b/frontend/src/assets/svg/mongo-ds.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/more_v.svg b/frontend/src/assets/svg/more_v.svg new file mode 100644 index 0000000..68c0c57 --- /dev/null +++ b/frontend/src/assets/svg/more_v.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/move.svg b/frontend/src/assets/svg/move.svg new file mode 100644 index 0000000..3135c7d --- /dev/null +++ b/frontend/src/assets/svg/move.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/msg-fill.svg b/frontend/src/assets/svg/msg-fill.svg new file mode 100644 index 0000000..7ce39cc --- /dev/null +++ b/frontend/src/assets/svg/msg-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/msg-notice.svg b/frontend/src/assets/svg/msg-notice.svg new file mode 100644 index 0000000..d13c585 --- /dev/null +++ b/frontend/src/assets/svg/msg-notice.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/msg-setting.svg b/frontend/src/assets/svg/msg-setting.svg new file mode 100644 index 0000000..4dca3b8 --- /dev/null +++ b/frontend/src/assets/svg/msg-setting.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/mysql-ds.svg b/frontend/src/assets/svg/mysql-ds.svg new file mode 100644 index 0000000..3fe231d --- /dev/null +++ b/frontend/src/assets/svg/mysql-ds.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/mysql-frame.svg b/frontend/src/assets/svg/mysql-frame.svg new file mode 100644 index 0000000..af23e7a --- /dev/null +++ b/frontend/src/assets/svg/mysql-frame.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/svg/nested.svg b/frontend/src/assets/svg/nested.svg new file mode 100644 index 0000000..06713a8 --- /dev/null +++ b/frontend/src/assets/svg/nested.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/no-join.svg b/frontend/src/assets/svg/no-join.svg new file mode 100644 index 0000000..ff09469 --- /dev/null +++ b/frontend/src/assets/svg/no-join.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/no_result.svg b/frontend/src/assets/svg/no_result.svg new file mode 100644 index 0000000..2621d6b --- /dev/null +++ b/frontend/src/assets/svg/no_result.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/non-existent.svg b/frontend/src/assets/svg/non-existent.svg new file mode 100644 index 0000000..db9a67d --- /dev/null +++ b/frontend/src/assets/svg/non-existent.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/none_select.svg b/frontend/src/assets/svg/none_select.svg new file mode 100644 index 0000000..ebafc75 --- /dev/null +++ b/frontend/src/assets/svg/none_select.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/notification.svg b/frontend/src/assets/svg/notification.svg new file mode 100644 index 0000000..4f7b116 --- /dev/null +++ b/frontend/src/assets/svg/notification.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/oracle-ds.svg b/frontend/src/assets/svg/oracle-ds.svg new file mode 100644 index 0000000..04efc9e --- /dev/null +++ b/frontend/src/assets/svg/oracle-ds.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/svg/org.svg b/frontend/src/assets/svg/org.svg new file mode 100644 index 0000000..0930bc5 --- /dev/null +++ b/frontend/src/assets/svg/org.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/other_material.svg b/frontend/src/assets/svg/other_material.svg new file mode 100644 index 0000000..8298dfa --- /dev/null +++ b/frontend/src/assets/svg/other_material.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/other_material_board.svg b/frontend/src/assets/svg/other_material_board.svg new file mode 100644 index 0000000..9ef3dc3 --- /dev/null +++ b/frontend/src/assets/svg/other_material_board.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/other_material_icon.svg b/frontend/src/assets/svg/other_material_icon.svg new file mode 100644 index 0000000..31dba89 --- /dev/null +++ b/frontend/src/assets/svg/other_material_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/other_media.svg b/frontend/src/assets/svg/other_media.svg new file mode 100644 index 0000000..66eb002 --- /dev/null +++ b/frontend/src/assets/svg/other_media.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/other_setting.svg b/frontend/src/assets/svg/other_setting.svg new file mode 100644 index 0000000..be94938 --- /dev/null +++ b/frontend/src/assets/svg/other_setting.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/other_text.svg b/frontend/src/assets/svg/other_text.svg new file mode 100644 index 0000000..408d02c --- /dev/null +++ b/frontend/src/assets/svg/other_text.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/outer-params-filter.svg b/frontend/src/assets/svg/outer-params-filter.svg new file mode 100644 index 0000000..81d9a23 --- /dev/null +++ b/frontend/src/assets/svg/outer-params-filter.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/page-line.svg b/frontend/src/assets/svg/page-line.svg new file mode 100644 index 0000000..224b1e0 --- /dev/null +++ b/frontend/src/assets/svg/page-line.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/panel-mobile-publish.svg b/frontend/src/assets/svg/panel-mobile-publish.svg new file mode 100644 index 0000000..99ce7a4 --- /dev/null +++ b/frontend/src/assets/svg/panel-mobile-publish.svg @@ -0,0 +1,18 @@ + + + + + + + + Layer 1 + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/panel-mobile-unpublished-back.svg b/frontend/src/assets/svg/panel-mobile-unpublished-back.svg new file mode 100644 index 0000000..919694e --- /dev/null +++ b/frontend/src/assets/svg/panel-mobile-unpublished-back.svg @@ -0,0 +1,19 @@ + + + + + + + + Layer 1 + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/panel-mobile-unpublished.svg b/frontend/src/assets/svg/panel-mobile-unpublished.svg new file mode 100644 index 0000000..0f89934 --- /dev/null +++ b/frontend/src/assets/svg/panel-mobile-unpublished.svg @@ -0,0 +1,18 @@ + + + + + + + + Layer 1 + + + + + + + + + + diff --git a/frontend/src/assets/svg/panel-publish.svg b/frontend/src/assets/svg/panel-publish.svg new file mode 100644 index 0000000..4153757 --- /dev/null +++ b/frontend/src/assets/svg/panel-publish.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/panel-unpublished-back.svg b/frontend/src/assets/svg/panel-unpublished-back.svg new file mode 100644 index 0000000..ac5887e --- /dev/null +++ b/frontend/src/assets/svg/panel-unpublished-back.svg @@ -0,0 +1,10 @@ + + + Layer 1 + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/panel-unpublished.svg b/frontend/src/assets/svg/panel-unpublished.svg new file mode 100644 index 0000000..a7dae86 --- /dev/null +++ b/frontend/src/assets/svg/panel-unpublished.svg @@ -0,0 +1,9 @@ + + + + Layer 1 + + + + + diff --git a/frontend/src/assets/svg/panel.svg b/frontend/src/assets/svg/panel.svg new file mode 100644 index 0000000..4153757 --- /dev/null +++ b/frontend/src/assets/svg/panel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/people.svg b/frontend/src/assets/svg/people.svg new file mode 100644 index 0000000..26d9c99 --- /dev/null +++ b/frontend/src/assets/svg/people.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/peoples.svg b/frontend/src/assets/svg/peoples.svg new file mode 100644 index 0000000..3a899eb --- /dev/null +++ b/frontend/src/assets/svg/peoples.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/percentage-bar-stack-dark.svg b/frontend/src/assets/svg/percentage-bar-stack-dark.svg new file mode 100644 index 0000000..d8065d8 --- /dev/null +++ b/frontend/src/assets/svg/percentage-bar-stack-dark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/percentage-bar-stack-horizontal-dark.svg b/frontend/src/assets/svg/percentage-bar-stack-horizontal-dark.svg new file mode 100644 index 0000000..636c610 --- /dev/null +++ b/frontend/src/assets/svg/percentage-bar-stack-horizontal-dark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/percentage-bar-stack-horizontal-origin.svg b/frontend/src/assets/svg/percentage-bar-stack-horizontal-origin.svg new file mode 100644 index 0000000..e8af2b9 --- /dev/null +++ b/frontend/src/assets/svg/percentage-bar-stack-horizontal-origin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/percentage-bar-stack-horizontal.svg b/frontend/src/assets/svg/percentage-bar-stack-horizontal.svg new file mode 100644 index 0000000..4c88a91 --- /dev/null +++ b/frontend/src/assets/svg/percentage-bar-stack-horizontal.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/percentage-bar-stack-origin.svg b/frontend/src/assets/svg/percentage-bar-stack-origin.svg new file mode 100644 index 0000000..21e1af0 --- /dev/null +++ b/frontend/src/assets/svg/percentage-bar-stack-origin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/percentage-bar-stack.svg b/frontend/src/assets/svg/percentage-bar-stack.svg new file mode 100644 index 0000000..0e3d8df --- /dev/null +++ b/frontend/src/assets/svg/percentage-bar-stack.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/perission_role_blue.svg b/frontend/src/assets/svg/perission_role_blue.svg new file mode 100644 index 0000000..3834a19 --- /dev/null +++ b/frontend/src/assets/svg/perission_role_blue.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/permission_del_blue.svg b/frontend/src/assets/svg/permission_del_blue.svg new file mode 100644 index 0000000..4193ad9 --- /dev/null +++ b/frontend/src/assets/svg/permission_del_blue.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/permission_del_white.svg b/frontend/src/assets/svg/permission_del_white.svg new file mode 100644 index 0000000..ff46ddd --- /dev/null +++ b/frontend/src/assets/svg/permission_del_white.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/permission_edit_blue.svg b/frontend/src/assets/svg/permission_edit_blue.svg new file mode 100644 index 0000000..adf3ed0 --- /dev/null +++ b/frontend/src/assets/svg/permission_edit_blue.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/permission_edit_white.svg b/frontend/src/assets/svg/permission_edit_white.svg new file mode 100644 index 0000000..bb73d33 --- /dev/null +++ b/frontend/src/assets/svg/permission_edit_white.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/permission_table_del_blue.svg b/frontend/src/assets/svg/permission_table_del_blue.svg new file mode 100644 index 0000000..2a7da29 --- /dev/null +++ b/frontend/src/assets/svg/permission_table_del_blue.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/permission_table_edit_blue.svg b/frontend/src/assets/svg/permission_table_edit_blue.svg new file mode 100644 index 0000000..4af238f --- /dev/null +++ b/frontend/src/assets/svg/permission_table_edit_blue.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/pg-ds.svg b/frontend/src/assets/svg/pg-ds.svg new file mode 100644 index 0000000..9a5bb6b --- /dev/null +++ b/frontend/src/assets/svg/pg-ds.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/picture-group-dark.svg b/frontend/src/assets/svg/picture-group-dark.svg new file mode 100644 index 0000000..d57adf7 --- /dev/null +++ b/frontend/src/assets/svg/picture-group-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/picture-group-origin.svg b/frontend/src/assets/svg/picture-group-origin.svg new file mode 100644 index 0000000..6fa4f5b --- /dev/null +++ b/frontend/src/assets/svg/picture-group-origin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/picture-group.svg b/frontend/src/assets/svg/picture-group.svg new file mode 100644 index 0000000..d57adf7 --- /dev/null +++ b/frontend/src/assets/svg/picture-group.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/pie-dark.svg b/frontend/src/assets/svg/pie-dark.svg new file mode 100644 index 0000000..58c0090 --- /dev/null +++ b/frontend/src/assets/svg/pie-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/pie-donut-dark.svg b/frontend/src/assets/svg/pie-donut-dark.svg new file mode 100644 index 0000000..070d265 --- /dev/null +++ b/frontend/src/assets/svg/pie-donut-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/pie-donut-origin.svg b/frontend/src/assets/svg/pie-donut-origin.svg new file mode 100644 index 0000000..b719ba8 --- /dev/null +++ b/frontend/src/assets/svg/pie-donut-origin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/pie-donut-rose-dark.svg b/frontend/src/assets/svg/pie-donut-rose-dark.svg new file mode 100644 index 0000000..1e51b0e --- /dev/null +++ b/frontend/src/assets/svg/pie-donut-rose-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/pie-donut-rose-origin.svg b/frontend/src/assets/svg/pie-donut-rose-origin.svg new file mode 100644 index 0000000..b719ba8 --- /dev/null +++ b/frontend/src/assets/svg/pie-donut-rose-origin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/pie-donut-rose.svg b/frontend/src/assets/svg/pie-donut-rose.svg new file mode 100644 index 0000000..1e51b0e --- /dev/null +++ b/frontend/src/assets/svg/pie-donut-rose.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/pie-donut.svg b/frontend/src/assets/svg/pie-donut.svg new file mode 100644 index 0000000..1139b8e --- /dev/null +++ b/frontend/src/assets/svg/pie-donut.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/pie-origin.svg b/frontend/src/assets/svg/pie-origin.svg new file mode 100644 index 0000000..67d3730 --- /dev/null +++ b/frontend/src/assets/svg/pie-origin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/pie-rose-dark.svg b/frontend/src/assets/svg/pie-rose-dark.svg new file mode 100644 index 0000000..f432787 --- /dev/null +++ b/frontend/src/assets/svg/pie-rose-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/svg/pie-rose-origin.svg b/frontend/src/assets/svg/pie-rose-origin.svg new file mode 100644 index 0000000..a83b8cc --- /dev/null +++ b/frontend/src/assets/svg/pie-rose-origin.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/svg/pie-rose.svg b/frontend/src/assets/svg/pie-rose.svg new file mode 100644 index 0000000..8dc1b10 --- /dev/null +++ b/frontend/src/assets/svg/pie-rose.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/svg/pie.svg b/frontend/src/assets/svg/pie.svg new file mode 100644 index 0000000..58c0090 --- /dev/null +++ b/frontend/src/assets/svg/pie.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/platform.svg b/frontend/src/assets/svg/platform.svg new file mode 100644 index 0000000..859d0f9 --- /dev/null +++ b/frontend/src/assets/svg/platform.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/plugin-3dmap.svg b/frontend/src/assets/svg/plugin-3dmap.svg new file mode 100644 index 0000000..f8b3e8b --- /dev/null +++ b/frontend/src/assets/svg/plugin-3dmap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/plugin-3dpie.svg b/frontend/src/assets/svg/plugin-3dpie.svg new file mode 100644 index 0000000..84dcb8f --- /dev/null +++ b/frontend/src/assets/svg/plugin-3dpie.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/plugin-dameng.svg b/frontend/src/assets/svg/plugin-dameng.svg new file mode 100644 index 0000000..dade3a8 --- /dev/null +++ b/frontend/src/assets/svg/plugin-dameng.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/svg/plugin-default.svg b/frontend/src/assets/svg/plugin-default.svg new file mode 100644 index 0000000..6bdef21 --- /dev/null +++ b/frontend/src/assets/svg/plugin-default.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/plugin.svg b/frontend/src/assets/svg/plugin.svg new file mode 100644 index 0000000..5151777 --- /dev/null +++ b/frontend/src/assets/svg/plugin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/plugins-new.svg b/frontend/src/assets/svg/plugins-new.svg new file mode 100644 index 0000000..09668d9 --- /dev/null +++ b/frontend/src/assets/svg/plugins-new.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/plugins.svg b/frontend/src/assets/svg/plugins.svg new file mode 100644 index 0000000..de353db --- /dev/null +++ b/frontend/src/assets/svg/plugins.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/progress-bar-dark.svg b/frontend/src/assets/svg/progress-bar-dark.svg new file mode 100644 index 0000000..2f0ea80 --- /dev/null +++ b/frontend/src/assets/svg/progress-bar-dark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/progress-bar-origin.svg b/frontend/src/assets/svg/progress-bar-origin.svg new file mode 100644 index 0000000..7b61319 --- /dev/null +++ b/frontend/src/assets/svg/progress-bar-origin.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/progress-bar.svg b/frontend/src/assets/svg/progress-bar.svg new file mode 100644 index 0000000..c75e98a --- /dev/null +++ b/frontend/src/assets/svg/progress-bar.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/pwd_invisible.svg b/frontend/src/assets/svg/pwd_invisible.svg new file mode 100644 index 0000000..661ebba --- /dev/null +++ b/frontend/src/assets/svg/pwd_invisible.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/pwd_visible.svg b/frontend/src/assets/svg/pwd_visible.svg new file mode 100644 index 0000000..4e7ba5e --- /dev/null +++ b/frontend/src/assets/svg/pwd_visible.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/quadrant-dark.svg b/frontend/src/assets/svg/quadrant-dark.svg new file mode 100644 index 0000000..b78cb06 --- /dev/null +++ b/frontend/src/assets/svg/quadrant-dark.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/quadrant-origin.svg b/frontend/src/assets/svg/quadrant-origin.svg new file mode 100644 index 0000000..b79493c --- /dev/null +++ b/frontend/src/assets/svg/quadrant-origin.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/quadrant.svg b/frontend/src/assets/svg/quadrant.svg new file mode 100644 index 0000000..9f06298 --- /dev/null +++ b/frontend/src/assets/svg/quadrant.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/radar-dark.svg b/frontend/src/assets/svg/radar-dark.svg new file mode 100644 index 0000000..34169cb --- /dev/null +++ b/frontend/src/assets/svg/radar-dark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/radar-origin.svg b/frontend/src/assets/svg/radar-origin.svg new file mode 100644 index 0000000..f4813cc --- /dev/null +++ b/frontend/src/assets/svg/radar-origin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/radar.svg b/frontend/src/assets/svg/radar.svg new file mode 100644 index 0000000..a711496 --- /dev/null +++ b/frontend/src/assets/svg/radar.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/read-msg.svg b/frontend/src/assets/svg/read-msg.svg new file mode 100644 index 0000000..55ae260 --- /dev/null +++ b/frontend/src/assets/svg/read-msg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/redshift-ds.svg b/frontend/src/assets/svg/redshift-ds.svg new file mode 100644 index 0000000..38ab4bc --- /dev/null +++ b/frontend/src/assets/svg/redshift-ds.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/assets/svg/reference-field.svg b/frontend/src/assets/svg/reference-field.svg new file mode 100644 index 0000000..b311d14 --- /dev/null +++ b/frontend/src/assets/svg/reference-field.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/reference-play.svg b/frontend/src/assets/svg/reference-play.svg new file mode 100644 index 0000000..5a70624 --- /dev/null +++ b/frontend/src/assets/svg/reference-play.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/svg/reference-setting-white.svg b/frontend/src/assets/svg/reference-setting-white.svg new file mode 100644 index 0000000..a5f22f1 --- /dev/null +++ b/frontend/src/assets/svg/reference-setting-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/reference-setting.svg b/frontend/src/assets/svg/reference-setting.svg new file mode 100644 index 0000000..74107b6 --- /dev/null +++ b/frontend/src/assets/svg/reference-setting.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/reference-table.svg b/frontend/src/assets/svg/reference-table.svg new file mode 100644 index 0000000..96a9732 --- /dev/null +++ b/frontend/src/assets/svg/reference-table.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/relation_arrow_icon.svg b/frontend/src/assets/svg/relation_arrow_icon.svg new file mode 100644 index 0000000..9dd3a40 --- /dev/null +++ b/frontend/src/assets/svg/relation_arrow_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/release.svg b/frontend/src/assets/svg/release.svg new file mode 100644 index 0000000..2cc2ff9 --- /dev/null +++ b/frontend/src/assets/svg/release.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/report.svg b/frontend/src/assets/svg/report.svg new file mode 100644 index 0000000..50204b1 --- /dev/null +++ b/frontend/src/assets/svg/report.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/reset_password_blue.svg b/frontend/src/assets/svg/reset_password_blue.svg new file mode 100644 index 0000000..0028219 --- /dev/null +++ b/frontend/src/assets/svg/reset_password_blue.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/rich-text-dark.svg b/frontend/src/assets/svg/rich-text-dark.svg new file mode 100644 index 0000000..ff6d406 --- /dev/null +++ b/frontend/src/assets/svg/rich-text-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/rich-text-origin.svg b/frontend/src/assets/svg/rich-text-origin.svg new file mode 100644 index 0000000..5c1c6f2 --- /dev/null +++ b/frontend/src/assets/svg/rich-text-origin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/rich-text.svg b/frontend/src/assets/svg/rich-text.svg new file mode 100644 index 0000000..cea3f7e --- /dev/null +++ b/frontend/src/assets/svg/rich-text.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/richTextView.svg b/frontend/src/assets/svg/richTextView.svg new file mode 100644 index 0000000..91d5c98 --- /dev/null +++ b/frontend/src/assets/svg/richTextView.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/right-join.svg b/frontend/src/assets/svg/right-join.svg new file mode 100644 index 0000000..0938320 --- /dev/null +++ b/frontend/src/assets/svg/right-join.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/role.svg b/frontend/src/assets/svg/role.svg new file mode 100644 index 0000000..ae889b1 --- /dev/null +++ b/frontend/src/assets/svg/role.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/round_play.svg b/frontend/src/assets/svg/round_play.svg new file mode 100644 index 0000000..cc27381 --- /dev/null +++ b/frontend/src/assets/svg/round_play.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/sankey-dark.svg b/frontend/src/assets/svg/sankey-dark.svg new file mode 100644 index 0000000..e296f88 --- /dev/null +++ b/frontend/src/assets/svg/sankey-dark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/sankey-origin.svg b/frontend/src/assets/svg/sankey-origin.svg new file mode 100644 index 0000000..bb2363d --- /dev/null +++ b/frontend/src/assets/svg/sankey-origin.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/sankey.svg b/frontend/src/assets/svg/sankey.svg new file mode 100644 index 0000000..5645f5d --- /dev/null +++ b/frontend/src/assets/svg/sankey.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/scatter-dark.svg b/frontend/src/assets/svg/scatter-dark.svg new file mode 100644 index 0000000..384794b --- /dev/null +++ b/frontend/src/assets/svg/scatter-dark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/scatter-origin.svg b/frontend/src/assets/svg/scatter-origin.svg new file mode 100644 index 0000000..0a32054 --- /dev/null +++ b/frontend/src/assets/svg/scatter-origin.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/scatter.svg b/frontend/src/assets/svg/scatter.svg new file mode 100644 index 0000000..3ad42c3 --- /dev/null +++ b/frontend/src/assets/svg/scatter.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/scatterpage.svg b/frontend/src/assets/svg/scatterpage.svg new file mode 100644 index 0000000..2f13374 --- /dev/null +++ b/frontend/src/assets/svg/scatterpage.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/svg/scene.svg b/frontend/src/assets/svg/scene.svg new file mode 100644 index 0000000..037555d --- /dev/null +++ b/frontend/src/assets/svg/scene.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/scroll-text.svg b/frontend/src/assets/svg/scroll-text.svg new file mode 100644 index 0000000..5847b4a --- /dev/null +++ b/frontend/src/assets/svg/scroll-text.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/size.svg b/frontend/src/assets/svg/size.svg new file mode 100644 index 0000000..ddb25b8 --- /dev/null +++ b/frontend/src/assets/svg/size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/sort-asc.svg b/frontend/src/assets/svg/sort-asc.svg new file mode 100644 index 0000000..d0bcfa2 --- /dev/null +++ b/frontend/src/assets/svg/sort-asc.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/sort-desc.svg b/frontend/src/assets/svg/sort-desc.svg new file mode 100644 index 0000000..23f1d4a --- /dev/null +++ b/frontend/src/assets/svg/sort-desc.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/sqlServer-ds.svg b/frontend/src/assets/svg/sqlServer-ds.svg new file mode 100644 index 0000000..2f2b31a --- /dev/null +++ b/frontend/src/assets/svg/sqlServer-ds.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/stock-line-dark.svg b/frontend/src/assets/svg/stock-line-dark.svg new file mode 100644 index 0000000..1c8c31f --- /dev/null +++ b/frontend/src/assets/svg/stock-line-dark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/stock-line-origin.svg b/frontend/src/assets/svg/stock-line-origin.svg new file mode 100644 index 0000000..087dafc --- /dev/null +++ b/frontend/src/assets/svg/stock-line-origin.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/stock-line.svg b/frontend/src/assets/svg/stock-line.svg new file mode 100644 index 0000000..3319059 --- /dev/null +++ b/frontend/src/assets/svg/stock-line.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/src/assets/svg/style-underline.svg b/frontend/src/assets/svg/style-underline.svg new file mode 100644 index 0000000..c8da46f --- /dev/null +++ b/frontend/src/assets/svg/style-underline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/symbolic-map-dark.svg b/frontend/src/assets/svg/symbolic-map-dark.svg new file mode 100644 index 0000000..87d9516 --- /dev/null +++ b/frontend/src/assets/svg/symbolic-map-dark.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/symbolic-map-origin.svg b/frontend/src/assets/svg/symbolic-map-origin.svg new file mode 100644 index 0000000..0198307 --- /dev/null +++ b/frontend/src/assets/svg/symbolic-map-origin.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/symbolic-map.svg b/frontend/src/assets/svg/symbolic-map.svg new file mode 100644 index 0000000..62d72b1 --- /dev/null +++ b/frontend/src/assets/svg/symbolic-map.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/sync.svg b/frontend/src/assets/svg/sync.svg new file mode 100644 index 0000000..ce1fb9c --- /dev/null +++ b/frontend/src/assets/svg/sync.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/sys-param.svg b/frontend/src/assets/svg/sys-param.svg new file mode 100644 index 0000000..f6c6f02 --- /dev/null +++ b/frontend/src/assets/svg/sys-param.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/sys-parameter.svg b/frontend/src/assets/svg/sys-parameter.svg new file mode 100644 index 0000000..40404d0 --- /dev/null +++ b/frontend/src/assets/svg/sys-parameter.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/sys-relationship.svg b/frontend/src/assets/svg/sys-relationship.svg new file mode 100644 index 0000000..ec0619f --- /dev/null +++ b/frontend/src/assets/svg/sys-relationship.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/sys-setting.svg b/frontend/src/assets/svg/sys-setting.svg new file mode 100644 index 0000000..a85a89c --- /dev/null +++ b/frontend/src/assets/svg/sys-setting.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/sys-tools.svg b/frontend/src/assets/svg/sys-tools.svg new file mode 100644 index 0000000..5c94639 --- /dev/null +++ b/frontend/src/assets/svg/sys-tools.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/system.svg b/frontend/src/assets/svg/system.svg new file mode 100644 index 0000000..9333c60 --- /dev/null +++ b/frontend/src/assets/svg/system.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/svg/t-heatmap-dark.svg b/frontend/src/assets/svg/t-heatmap-dark.svg new file mode 100644 index 0000000..0e550c0 --- /dev/null +++ b/frontend/src/assets/svg/t-heatmap-dark.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/t-heatmap-origin.svg b/frontend/src/assets/svg/t-heatmap-origin.svg new file mode 100644 index 0000000..a19b1d0 --- /dev/null +++ b/frontend/src/assets/svg/t-heatmap-origin.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/t-heatmap.svg b/frontend/src/assets/svg/t-heatmap.svg new file mode 100644 index 0000000..b6c8656 --- /dev/null +++ b/frontend/src/assets/svg/t-heatmap.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/tab-title.svg b/frontend/src/assets/svg/tab-title.svg new file mode 100644 index 0000000..4b1acac --- /dev/null +++ b/frontend/src/assets/svg/tab-title.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/table-info-dark.svg b/frontend/src/assets/svg/table-info-dark.svg new file mode 100644 index 0000000..113c08c --- /dev/null +++ b/frontend/src/assets/svg/table-info-dark.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/table-info-origin.svg b/frontend/src/assets/svg/table-info-origin.svg new file mode 100644 index 0000000..76f26d0 --- /dev/null +++ b/frontend/src/assets/svg/table-info-origin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/table-info.svg b/frontend/src/assets/svg/table-info.svg new file mode 100644 index 0000000..04abc22 --- /dev/null +++ b/frontend/src/assets/svg/table-info.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/table-normal-dark.svg b/frontend/src/assets/svg/table-normal-dark.svg new file mode 100644 index 0000000..43e37e6 --- /dev/null +++ b/frontend/src/assets/svg/table-normal-dark.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/table-normal-origin.svg b/frontend/src/assets/svg/table-normal-origin.svg new file mode 100644 index 0000000..76f26d0 --- /dev/null +++ b/frontend/src/assets/svg/table-normal-origin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/table-normal.svg b/frontend/src/assets/svg/table-normal.svg new file mode 100644 index 0000000..9fa0254 --- /dev/null +++ b/frontend/src/assets/svg/table-normal.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/table-pivot-dark.svg b/frontend/src/assets/svg/table-pivot-dark.svg new file mode 100644 index 0000000..28f6a20 --- /dev/null +++ b/frontend/src/assets/svg/table-pivot-dark.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/table-pivot-origin.svg b/frontend/src/assets/svg/table-pivot-origin.svg new file mode 100644 index 0000000..76f26d0 --- /dev/null +++ b/frontend/src/assets/svg/table-pivot-origin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/table-pivot.svg b/frontend/src/assets/svg/table-pivot.svg new file mode 100644 index 0000000..9a4fdca --- /dev/null +++ b/frontend/src/assets/svg/table-pivot.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/table.svg b/frontend/src/assets/svg/table.svg new file mode 100644 index 0000000..0e3dc9d --- /dev/null +++ b/frontend/src/assets/svg/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/task.svg b/frontend/src/assets/svg/task.svg new file mode 100644 index 0000000..575ba4c --- /dev/null +++ b/frontend/src/assets/svg/task.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/text-dark.svg b/frontend/src/assets/svg/text-dark.svg new file mode 100644 index 0000000..71ec539 --- /dev/null +++ b/frontend/src/assets/svg/text-dark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/text.svg b/frontend/src/assets/svg/text.svg new file mode 100644 index 0000000..568144a --- /dev/null +++ b/frontend/src/assets/svg/text.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/threshold.svg b/frontend/src/assets/svg/threshold.svg new file mode 100644 index 0000000..d3dc3de --- /dev/null +++ b/frontend/src/assets/svg/threshold.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/threshold_full.svg b/frontend/src/assets/svg/threshold_full.svg new file mode 100644 index 0000000..011c456 --- /dev/null +++ b/frontend/src/assets/svg/threshold_full.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/src/assets/svg/title-bold.svg b/frontend/src/assets/svg/title-bold.svg new file mode 100644 index 0000000..41d9d36 --- /dev/null +++ b/frontend/src/assets/svg/title-bold.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/title-center.svg b/frontend/src/assets/svg/title-center.svg new file mode 100644 index 0000000..0a9a460 --- /dev/null +++ b/frontend/src/assets/svg/title-center.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/title-italic.svg b/frontend/src/assets/svg/title-italic.svg new file mode 100644 index 0000000..af8f5ec --- /dev/null +++ b/frontend/src/assets/svg/title-italic.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/title-left.svg b/frontend/src/assets/svg/title-left.svg new file mode 100644 index 0000000..f297076 --- /dev/null +++ b/frontend/src/assets/svg/title-left.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/title-right.svg b/frontend/src/assets/svg/title-right.svg new file mode 100644 index 0000000..f94767b --- /dev/null +++ b/frontend/src/assets/svg/title-right.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/title-v-bottom.svg b/frontend/src/assets/svg/title-v-bottom.svg new file mode 100644 index 0000000..7406462 --- /dev/null +++ b/frontend/src/assets/svg/title-v-bottom.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/title-v-center.svg b/frontend/src/assets/svg/title-v-center.svg new file mode 100644 index 0000000..e12bc04 --- /dev/null +++ b/frontend/src/assets/svg/title-v-center.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/title-v-top.svg b/frontend/src/assets/svg/title-v-top.svg new file mode 100644 index 0000000..3f67f23 --- /dev/null +++ b/frontend/src/assets/svg/title-v-top.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/toolbox-data_fill.svg b/frontend/src/assets/svg/toolbox-data_fill.svg new file mode 100644 index 0000000..c4c2f14 --- /dev/null +++ b/frontend/src/assets/svg/toolbox-data_fill.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/toolbox-icon_template.svg b/frontend/src/assets/svg/toolbox-icon_template.svg new file mode 100644 index 0000000..5a67c71 --- /dev/null +++ b/frontend/src/assets/svg/toolbox-icon_template.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/toolbox-log.svg b/frontend/src/assets/svg/toolbox-log.svg new file mode 100644 index 0000000..14d4bb3 --- /dev/null +++ b/frontend/src/assets/svg/toolbox-log.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/top-doc-default.svg b/frontend/src/assets/svg/top-doc-default.svg new file mode 100644 index 0000000..92f4c2c --- /dev/null +++ b/frontend/src/assets/svg/top-doc-default.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/top-enterprise-trial.svg b/frontend/src/assets/svg/top-enterprise-trial.svg new file mode 100644 index 0000000..810340f --- /dev/null +++ b/frontend/src/assets/svg/top-enterprise-trial.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/top-help-doc.svg b/frontend/src/assets/svg/top-help-doc.svg new file mode 100644 index 0000000..1be5f64 --- /dev/null +++ b/frontend/src/assets/svg/top-help-doc.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/svg/top-product-bbs.svg b/frontend/src/assets/svg/top-product-bbs.svg new file mode 100644 index 0000000..542e257 --- /dev/null +++ b/frontend/src/assets/svg/top-product-bbs.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/top-tech-video.svg b/frontend/src/assets/svg/top-tech-video.svg new file mode 100644 index 0000000..c184dcb --- /dev/null +++ b/frontend/src/assets/svg/top-tech-video.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/top-technology.svg b/frontend/src/assets/svg/top-technology.svg new file mode 100644 index 0000000..f9ae658 --- /dev/null +++ b/frontend/src/assets/svg/top-technology.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/tree.svg b/frontend/src/assets/svg/tree.svg new file mode 100644 index 0000000..dd4b7dd --- /dev/null +++ b/frontend/src/assets/svg/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/treemap-dark.svg b/frontend/src/assets/svg/treemap-dark.svg new file mode 100644 index 0000000..73bdeb3 --- /dev/null +++ b/frontend/src/assets/svg/treemap-dark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/treemap-origin.svg b/frontend/src/assets/svg/treemap-origin.svg new file mode 100644 index 0000000..cbece3a --- /dev/null +++ b/frontend/src/assets/svg/treemap-origin.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/treemap.svg b/frontend/src/assets/svg/treemap.svg new file mode 100644 index 0000000..73bdeb3 --- /dev/null +++ b/frontend/src/assets/svg/treemap.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/src/assets/svg/unlock.svg b/frontend/src/assets/svg/unlock.svg new file mode 100644 index 0000000..69531f1 --- /dev/null +++ b/frontend/src/assets/svg/unlock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/unpublished.svg b/frontend/src/assets/svg/unpublished.svg new file mode 100644 index 0000000..639db52 --- /dev/null +++ b/frontend/src/assets/svg/unpublished.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/unread-msg.svg b/frontend/src/assets/svg/unread-msg.svg new file mode 100644 index 0000000..86c571f --- /dev/null +++ b/frontend/src/assets/svg/unread-msg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/user-img.svg b/frontend/src/assets/svg/user-img.svg new file mode 100644 index 0000000..13a0b27 --- /dev/null +++ b/frontend/src/assets/svg/user-img.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/user-reci.svg b/frontend/src/assets/svg/user-reci.svg new file mode 100644 index 0000000..a6d141f --- /dev/null +++ b/frontend/src/assets/svg/user-reci.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/user.svg b/frontend/src/assets/svg/user.svg new file mode 100644 index 0000000..0ba0716 --- /dev/null +++ b/frontend/src/assets/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/user_connect_white.svg b/frontend/src/assets/svg/user_connect_white.svg new file mode 100644 index 0000000..bc107b3 --- /dev/null +++ b/frontend/src/assets/svg/user_connect_white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/variable.svg b/frontend/src/assets/svg/variable.svg new file mode 100644 index 0000000..12f4475 --- /dev/null +++ b/frontend/src/assets/svg/variable.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/visual-star.svg b/frontend/src/assets/svg/visual-star.svg new file mode 100644 index 0000000..8dd3729 --- /dev/null +++ b/frontend/src/assets/svg/visual-star.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/warn-tree.svg b/frontend/src/assets/svg/warn-tree.svg new file mode 100644 index 0000000..7fdc95f --- /dev/null +++ b/frontend/src/assets/svg/warn-tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/waterfall-dark.svg b/frontend/src/assets/svg/waterfall-dark.svg new file mode 100644 index 0000000..986f246 --- /dev/null +++ b/frontend/src/assets/svg/waterfall-dark.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/waterfall-origin.svg b/frontend/src/assets/svg/waterfall-origin.svg new file mode 100644 index 0000000..035107b --- /dev/null +++ b/frontend/src/assets/svg/waterfall-origin.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/svg/waterfall.svg b/frontend/src/assets/svg/waterfall.svg new file mode 100644 index 0000000..567b96e --- /dev/null +++ b/frontend/src/assets/svg/waterfall.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/assets/svg/watermark.svg b/frontend/src/assets/svg/watermark.svg new file mode 100644 index 0000000..d8d907f --- /dev/null +++ b/frontend/src/assets/svg/watermark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/web-msg.svg b/frontend/src/assets/svg/web-msg.svg new file mode 100644 index 0000000..1334c9d --- /dev/null +++ b/frontend/src/assets/svg/web-msg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/svg/wizard_enterprise.svg b/frontend/src/assets/svg/wizard_enterprise.svg new file mode 100644 index 0000000..2bdfdb7 --- /dev/null +++ b/frontend/src/assets/svg/wizard_enterprise.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/wizard_help.svg b/frontend/src/assets/svg/wizard_help.svg new file mode 100644 index 0000000..9094e24 --- /dev/null +++ b/frontend/src/assets/svg/wizard_help.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/wizard_main_bg_inner.svg b/frontend/src/assets/svg/wizard_main_bg_inner.svg new file mode 100644 index 0000000..87c44d4 --- /dev/null +++ b/frontend/src/assets/svg/wizard_main_bg_inner.svg @@ -0,0 +1,266 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/wizard_quick_start.svg b/frontend/src/assets/svg/wizard_quick_start.svg new file mode 100644 index 0000000..6970599 --- /dev/null +++ b/frontend/src/assets/svg/wizard_quick_start.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/word-cloud-dark.svg b/frontend/src/assets/svg/word-cloud-dark.svg new file mode 100644 index 0000000..5727796 --- /dev/null +++ b/frontend/src/assets/svg/word-cloud-dark.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/svg/word-cloud-origin.svg b/frontend/src/assets/svg/word-cloud-origin.svg new file mode 100644 index 0000000..470ddd5 --- /dev/null +++ b/frontend/src/assets/svg/word-cloud-origin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/svg/word-cloud.svg b/frontend/src/assets/svg/word-cloud.svg new file mode 100644 index 0000000..2264bf1 --- /dev/null +++ b/frontend/src/assets/svg/word-cloud.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/data-collect/index.es.js b/frontend/src/data-collect/index.es.js index cf8760a..3aa350b 100644 --- a/frontend/src/data-collect/index.es.js +++ b/frontend/src/data-collect/index.es.js @@ -52106,10 +52106,10 @@ var o1 = { exports: {} }; }); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. - + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. - + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, @@ -53000,8 +53000,7 @@ var o1 = { exports: {} }; } function b(y, E, x) { var k = x.css, R = x.media, M = x.sourceMap; - if (R ? y.setAttribute("media", R) : y.removeAttribute("media"), M && typeof btoa < "u" && (k += ` -/*# sourceMappingURL=data:application/json;base64,`.concat(btoa(unescape(encodeURIComponent(JSON.stringify(M)))), " */")), y.styleSheet) + if (R ? y.setAttribute("media", R) : y.removeAttribute("media"), M && typeof btoa < "u" && ((null)), y.styleSheet) y.styleSheet.cssText = k; else { for (; y.firstChild; ) @@ -53638,7 +53637,7 @@ var o1 = { exports: {} }; }); }; }, function(i, r) { - i.exports = ` + i.exports = ` \v\f\r \xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF`; }, function(i, r, n) { var a = n(60), s = n(18), u = n(48); @@ -53971,7 +53970,7 @@ var o1 = { exports: {} }; } } }), Z.length) { - E.customAlert(k("\u56FE\u7247\u9A8C\u8BC1\u672A\u901A\u8FC7") + `: + E.customAlert(k("\u56FE\u7247\u9A8C\u8BC1\u672A\u901A\u8FC7") + `: ` + Z.join(` `), "warning"); return; @@ -57659,8 +57658,8 @@ var o1 = { exports: {} }; var B = R.childNodes(); if ((B == null ? void 0 : B.getNodeName()) === "IMG") { var T = (P = (M = k.selection.getSelectionContainerElem()) === null || M === void 0 ? void 0 : M.children()) === null || P === void 0 ? void 0 : P.elems[0].children[0]; - k.cmd.do("insertHTML", `"); } else { var D, V = R.elems[0], W = V.innerHTML, G = V.parentElement; @@ -58093,10 +58092,10 @@ var o1 = { exports: {} }; }, { title: b.i18next.t("menus.panelMenus.video.\u63D2\u5165\u89C6\u9891"), tpl: `
- "/>
@@ -58153,7 +58152,7 @@ var o1 = { exports: {} }; Y.push(we); } }), ue.length) { - R.customAlert(P("\u89C6\u9891\u9A8C\u8BC1\u672A\u901A\u8FC7") + `: + R.customAlert(P("\u89C6\u9891\u9A8C\u8BC1\u672A\u901A\u8FC7") + `: ` + ue.join(` `), "warning"); return; diff --git a/frontend/src/data-public/Dashboard.vue b/frontend/src/data-public/Dashboard.vue index d832f33..fd8d4c9 100644 --- a/frontend/src/data-public/Dashboard.vue +++ b/frontend/src/data-public/Dashboard.vue @@ -91,7 +91,18 @@ FormCreat 编辑面板
- + +
+ + GIS大屏 +
+
+ +
+ + 数据看板 +
+
@@ -128,7 +139,6 @@ const stats = reactive({ dictCount: 0 }) -// 最近活动 const activities = ref([ { id: 1, diff --git a/frontend/src/data-public/FormCreateDesigner.vue b/frontend/src/data-public/FormCreateDesigner.vue index 74a5a47..af4730b 100644 --- a/frontend/src/data-public/FormCreateDesigner.vue +++ b/frontend/src/data-public/FormCreateDesigner.vue @@ -14,7 +14,11 @@ import { onMounted, ref } from 'vue'; import { ElMessage } from 'element-plus-secondary' import formCreate from '@/data-collect/render/element-plus/form-create.es.js' + import { useRoute } from 'vue-router' +// import { moduleById,moduleUpdate } from '@/api/application/module' +// const basePath = import.meta.env.VITE_API_BASEPATH + const route = useRoute() const designerForm = formCreate.factory(); debugger diff --git a/frontend/src/data-visualization/DvPreview.vue b/frontend/src/data-visualization/DvPreview.vue new file mode 100644 index 0000000..c6f6cb1 --- /dev/null +++ b/frontend/src/data-visualization/DvPreview.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/frontend/src/data-visualization/canvas/DeCanvas.vue b/frontend/src/data-visualization/canvas/DeCanvas.vue new file mode 100644 index 0000000..9603124 --- /dev/null +++ b/frontend/src/data-visualization/canvas/DeCanvas.vue @@ -0,0 +1,365 @@ + + + + + diff --git a/frontend/src/data-visualization/chart/components/editor/common/ChartTemplateInfo.vue b/frontend/src/data-visualization/chart/components/editor/common/ChartTemplateInfo.vue new file mode 100644 index 0000000..8774c12 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/editor/common/ChartTemplateInfo.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/frontend/src/data-visualization/chart/components/editor/common/TableTooltip.vue b/frontend/src/data-visualization/chart/components/editor/common/TableTooltip.vue new file mode 100644 index 0000000..727a46f --- /dev/null +++ b/frontend/src/data-visualization/chart/components/editor/common/TableTooltip.vue @@ -0,0 +1,80 @@ + + + diff --git a/frontend/src/data-visualization/chart/components/editor/util/StringUtils.ts b/frontend/src/data-visualization/chart/components/editor/util/StringUtils.ts new file mode 100644 index 0000000..9883ed8 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/editor/util/StringUtils.ts @@ -0,0 +1,56 @@ +// 替换所有 标准模板格式 为 $panelName$ +export function pdfTemplateReplaceAll(content, source, target) { + const pattern = '\\$' + source + '\\$' + content = content.replace(new RegExp(pattern, 'gm'), target) + return content +} + +export function randomRange(min, max) { + let returnStr = '' + const range = max ? Math.round(Math.random() * (max - min)) + min : min + const charStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' + + for (let i = 0; i < range; i++) { + const index = Math.round(Math.random() * (charStr.length - 1)) + returnStr += charStr.substring(index, index + 1) + } + return returnStr +} + +export function equalsAny(target, ...sources) { + for (let i = 0; i < sources.length; i++) { + if (target === sources[i]) { + return true + } + } + return false +} + +export function includesAny(target, ...sources) { + if (!target || !sources) { + return false + } + for (let i = 0; i < sources.length; i++) { + if (target.includes(sources[i])) { + return true + } + } + return false +} + +// 替换字符串中的国际化内容, 格式为$t('xxx') +export function replaceInlineI18n(rawString) { + const res = [] + const reg = /\$t\('([\w.]+)'\)/gm + let tmp + if (!rawString) { + return res + } + while ((tmp = reg.exec(rawString)) !== null) { + res.push(tmp) + } + res.forEach(tmp => { + rawString = rawString.replaceAll(tmp[0], tmp[1]) + }) + return rawString +} diff --git a/frontend/src/data-visualization/chart/components/editor/util/chart.ts b/frontend/src/data-visualization/chart/components/editor/util/chart.ts new file mode 100644 index 0000000..22aa597 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/editor/util/chart.ts @@ -0,0 +1,1759 @@ +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { deepCopy } from '@/data-visualization/utils/utils' +import { formatterItem } from '@/data-visualization/chart/components/js/formatter' +const { t } = useI18n() + +export const DEFAULT_COLOR_CASE: DeepPartial = { + basicStyle: { + colorScheme: 'default', + colors: [ + '#1E90FF', + '#90EE90', + '#00CED1', + '#E2BD84', + '#7A90E0', + '#3BA272', + '#2BE7FF', + '#0A8ADA', + '#FFD700' + ], + alpha: 100, + gradient: false, + mapStyle: 'normal', + areaBaseColor: '#FFFFFF', + areaBorderColor: '#303133', + gaugeStyle: 'default', + tableBorderColor: '#E6E7E4', + tableScrollBarColor: 'rgba(0, 0, 0, 0.15)', + zoomButtonColor: '#aaa', + zoomBackground: '#fff' + }, + misc: { + flowMapConfig: { + lineConfig: { + mapLineAnimate: true, + mapLineGradient: false, + mapLineSourceColor: '#1E90FF', + mapLineTargetColor: '#90EE90' + } + }, + nameFontColor: '#000000', + valueFontColor: '#5470c6' + }, + tableHeader: { + tableHeaderBgColor: '#1E90FF', + tableHeaderCornerBgColor: '#1E90FF', + tableHeaderColBgColor: '#1E90FF', + tableHeaderFontColor: '#000000', + tableHeaderCornerFontColor: '#000000', + tableHeaderColFontColor: '#000000' + }, + tableCell: { + tableItemBgColor: '#FFFFFF', + tableFontColor: '#000000', + tableItemSubBgColor: '#EEEEEE' + } +} + +export const DEFAULT_COLOR_CASE_LIGHT: DeepPartial = { + basicStyle: { + colorScheme: 'default', + colors: [ + '#1E90FF', + '#90EE90', + '#00CED1', + '#E2BD84', + '#7A90E0', + '#3BA272', + '#2BE7FF', + '#0A8ADA', + '#FFD700' + ], + alpha: 100, + gradient: false, + mapStyle: 'normal', + areaBaseColor: '#FFFFFF', + areaBorderColor: '#303133', + gaugeStyle: 'default', + tableBorderColor: '#E6E7E4', + tableScrollBarColor: 'rgba(0, 0, 0, 0.15)', + zoomButtonColor: '#aaa', + zoomBackground: '#fff' + }, + misc: { + flowMapConfig: { + lineConfig: { + mapLineAnimate: true, + mapLineGradient: false, + mapLineSourceColor: '#146C94', + mapLineTargetColor: '#576CBC' + } + }, + nameFontColor: '#000000', + valueFontColor: '#5470c6' + }, + tableHeader: { + tableHeaderBgColor: '#1E90FF', + tableHeaderCornerBgColor: '#1E90FF', + tableHeaderColBgColor: '#1E90FF', + tableHeaderFontColor: '#000000', + tableHeaderCornerFontColor: '#000000', + tableHeaderColFontColor: '#000000' + }, + tableCell: { + tableItemBgColor: '#FFFFFF', + tableFontColor: '#000000', + tableItemSubBgColor: '#EEEEEE' + } +} + +export const DEFAULT_COLOR_CASE_DARK: DeepPartial = { + basicStyle: { + colorScheme: 'default', + colors: [ + '#1E90FF', + '#90EE90', + '#00CED1', + '#E2BD84', + '#7A90E0', + '#3BA272', + '#2BE7FF', + '#0A8ADA', + '#FFD700' + ], + alpha: 100, + gradient: false, + mapStyle: 'darkblue', + areaBaseColor: '#5470C6', + areaBorderColor: '#EBEEF5', + gaugeStyle: 'default', + tableBorderColor: '#CCCCCC', + tableScrollBarColor: 'rgba(255, 255, 255, 0.5)', + zoomButtonColor: '#fff', + zoomBackground: '#000' + }, + misc: { + flowMapConfig: { + lineConfig: { + mapLineGradient: false, + mapLineSourceColor: '#146C94', + mapLineTargetColor: '#576CBC' + } + }, + nameFontColor: '#ffffff', + valueFontColor: '#5470c6' + }, + tableHeader: { + tableHeaderBgColor: '#1E90FF', + tableHeaderCornerBgColor: '#1E90FF', + tableHeaderColBgColor: '#1E90FF', + tableHeaderFontColor: '#FFFFFF', + tableHeaderCornerFontColor: '#FFFFFF', + tableHeaderColFontColor: '#FFFFFF' + }, + tableCell: { + tableItemBgColor: '#131E42', + tableFontColor: '#ffffff', + tableItemSubBgColor: '#EEEEEE' + } +} + +export const TAB_COMMON_STYLE_BASE = { + headPosition: 'left' +} +export const TAB_COMMON_STYLE_LIGHT = { + ...TAB_COMMON_STYLE_BASE, + headFontColor: '#000000', + headFontActiveColor: '#000000', + headBorderColor: '#ffffff', + headBorderActiveColor: '#ffffff' +} +export const TAB_COMMON_STYLE_DARK = { + ...TAB_COMMON_STYLE_BASE, + headFontColor: '#ffffff', + headFontActiveColor: '#ffffff', + headBorderColor: '#000000', + headBorderActiveColor: '#000000' +} + +export const SENIOR_STYLE_SETTING_LIGHT = { + linkageIconColor: '#a6a6a6', + drillLayerColor: '#a6a6a6', + pagerColor: '#a6a6a6' +} + +export const SENIOR_STYLE_SETTING_DARK = { + linkageIconColor: '#ffffff', + drillLayerColor: '#ffffff', + pagerColor: '#ffffff' +} + +export const FILTER_COMMON_STYLE_BASE = { + layout: 'horizontal', + titleLayout: 'left' +} + +export const FILTER_COMMON_STYLE_LIGHT = { + ...FILTER_COMMON_STYLE_BASE, + labelColor: '#1f2329', + titleColor: '#1f2329', + color: '#1f2329', + borderColor: '#bbbfc4', + text: '#1f2329', + bgColor: '#FFFFFF' +} + +export const FILTER_COMMON_STYLE_DARK = { + ...FILTER_COMMON_STYLE_BASE, + labelColor: '#ffffff', + titleColor: '#ffffff', + color: '#FFFFFF', + borderColor: '#484747', + text: '#AFAFAF', + bgColor: '#131C42' +} + +export const DEFAULT_TAB_COLOR_CASE_BASE = { + headPosition: 'left' +} + +export const DEFAULT_TAB_COLOR_CASE_DARK = { + ...DEFAULT_TAB_COLOR_CASE_BASE, + headFontColor: '#FFFFFF', + headFontActiveColor: '#FFFFFF', + headBorderColor: '#131E42', + headBorderActiveColor: '#131E42' +} + +export const DEFAULT_TAB_COLOR_CASE_LIGHT = { + ...DEFAULT_TAB_COLOR_CASE_BASE, + headFontColor: '#OOOOOO', + headFontActiveColor: '#OOOOOO', + headBorderColor: '#OOOOOO', + headBorderActiveColor: '#OOOOOO' +} + +export const DEFAULT_MISC: ChartMiscAttr = { + pieInnerRadius: 0, + pieOuterRadius: 80, + radarShape: 'polygon', + radarSize: 80, + gaugeMinType: 'fix', + gaugeMinField: { + id: '', + summary: '' + }, + gaugeMin: 0, + gaugeMaxType: 'dynamic', + gaugeMaxField: { + id: '', + summary: '' + }, + gaugeMax: undefined, + gaugeStartAngle: 225, + gaugeEndAngle: -45, + nameFontSize: 18, + valueFontSize: 18, + nameValueSpace: 10, + valueFontColor: '#5470c6', + valueFontFamily: 'Microsoft YaHei', + valueFontIsBolder: false, + valueFontIsItalic: false, + valueLetterSpace: 0, + valueFontShadow: false, + showName: true, + nameFontColor: '#000000', + nameFontFamily: 'Microsoft YaHei', + nameFontIsBolder: false, + nameFontIsItalic: false, + nameLetterSpace: '0', + nameFontShadow: false, + treemapWidth: 80, + treemapHeight: 80, + liquidMax: undefined, + liquidMaxType: 'dynamic', + liquidMaxField: { + id: '', + summary: '' + }, + liquidSize: 80, + liquidShape: 'circle', + hPosition: 'center', + vPosition: 'center', + mapPitch: 0, + wordSizeRange: [8, 32], + wordSpacing: 6, + mapAutoLegend: true, + mapLegendMax: 0, + mapLegendMin: 0, + mapLegendNumber: 9, + mapLegendRangeType: 'quantize', + mapLegendCustomRange: [], + flowMapConfig: { + lineConfig: { + mapLineAnimate: true, + mapLineType: 'arc', + mapLineWidth: 1, + mapLineAnimateDuration: 3, + mapLineGradient: false, + mapLineSourceColor: '#1E90FF', + mapLineTargetColor: '#90EE90', + alpha: 100 + }, + pointConfig: { + text: { + color: '#146C94', + fontSize: 10 + }, + point: { + color: '#146C94', + size: 4, + animate: false, + speed: 0.01 + } + } + }, + wordCloudAxisValueRange: { + auto: true, + min: 0, + max: 0, + fieldId: undefined + } +} + +export const DEFAULT_MARK = { + fieldId: '', + conditions: [] +} +export const DEFAULT_LABEL: ChartLabelAttr = { + show: false, + childrenShow: true, + position: 'top', + color: '#909399', + fontSize: 12, + formatter: '', + labelLine: { + show: true + }, + labelFormatter: formatterItem, + reserveDecimalCount: 2, + labelShadow: false, + labelBgColor: '', + labelShadowColor: '', + quotaLabelFormatter: formatterItem, + showDimension: true, + showQuota: false, + showProportion: true, + seriesLabelFormatter: [], + conversionTag: { + show: false, + precision: 2, + text: t('chart.conversion_rate') + }, + showTotal: false, + totalFontSize: 12, + totalColor: '#FFF', + totalFormatter: formatterItem, + showStackQuota: false, + fullDisplay: false, + proportionSeriesFormatter: { + show: false, + color: '#000', + fontSize: 12, + formatterCfg: { + decimalCount: 2 + } + } +} +export const DEFAULT_TOOLTIP: ChartTooltipAttr = { + show: true, + trigger: 'item', + confine: true, + fontSize: 12, + color: '#909399', + tooltipFormatter: formatterItem, + backgroundColor: '#ffffff', + seriesTooltipFormatter: [], + carousel: { + enable: false, + stayTime: 3, + intervalTime: 0 + } +} +export const DEFAULT_TABLE_TOTAL: ChartTableTotalAttr = { + row: { + showGrandTotals: true, + showSubTotals: true, + reverseLayout: false, + reverseSubLayout: false, + label: t('chart.total_show'), + subLabel: t('chart.sub_total_show'), + subTotalsDimensions: [], + subTotalsDimensionsNew: true, + calcTotals: { + aggregation: 'SUM', + cfg: [] + }, + calcSubTotals: { + aggregation: 'SUM', + cfg: [] + }, + totalSort: 'none', + totalSortField: '' + }, + col: { + showGrandTotals: true, + showSubTotals: true, + reverseLayout: false, + reverseSubLayout: false, + label: t('chart.total_show'), + subLabel: t('chart.sub_total_show'), + subTotalsDimensions: [], + calcTotals: { + aggregation: 'SUM', + cfg: [] + }, + calcSubTotals: { + aggregation: 'SUM', + cfg: [] + }, + totalSort: 'none', // asc,desc + totalSortField: '' + } +} +export const DEFAULT_TABLE_HEADER: ChartTableHeaderAttr = { + indexLabel: t('relation.index'), + showIndex: false, + tableHeaderAlign: 'left', + tableHeaderCornerAlign: 'left', + tableHeaderColAlign: 'left', + tableHeaderBgColor: '#1E90FF', + tableHeaderCornerBgColor: '#1E90FF', + tableHeaderColBgColor: '#1E90FF', + tableHeaderFontColor: '#000000', + tableHeaderCornerFontColor: '#000000', + tableHeaderColFontColor: '#000000', + tableTitleFontSize: 12, + tableTitleCornerFontSize: 12, + tableTitleColFontSize: 12, + tableTitleHeight: 36, + tableHeaderSort: false, + showColTooltip: false, + showRowTooltip: false, + showTableHeader: true, + showHorizonBorder: true, + showVerticalBorder: true, + isItalic: false, + isCornerItalic: false, + isColItalic: false, + isBolder: true, + isCornerBolder: true, + isColBolder: true, + headerGroup: false, + headerGroupConfig: { + columns: [], + meta: [] + } +} +export const DEFAULT_TABLE_CELL: ChartTableCellAttr = { + tableFontColor: '#000000', + tableItemAlign: 'right', + tableItemBgColor: '#FFFFFF', + tableItemFontSize: 12, + tableItemHeight: 36, + enableTableCrossBG: false, + tableItemSubBgColor: '#EEEEEE', + showTooltip: false, + showHorizonBorder: true, + showVerticalBorder: true, + isItalic: false, + isBolder: false, + tableFreeze: false, + tableColumnFreezeHead: 0, + tableRowFreezeHead: 0, + mergeCells: true +} +export const DEFAULT_TITLE_STYLE: ChartTextStyle = { + show: true, + fontSize: 16, + color: '#ffffff', + hPosition: 'left', + vPosition: 'top', + isItalic: false, + isBolder: true, + remarkShow: false, + remark: '', + remarkBackgroundColor: '#ffffff', + fontFamily: '', + letterSpace: '0', + fontShadow: false +} + +export const DEFAULT_INDICATOR_STYLE: ChartIndicatorStyle = { + show: true, + fontSize: 20, + color: '#5470C6ff', + hPosition: 'center', + vPosition: 'center', + isItalic: false, + isBolder: true, + fontFamily: 'Microsoft YaHei', + letterSpace: 0, + fontShadow: false, + backgroundColor: '', + + suffixEnable: true, + suffix: '', + suffixFontSize: 14, + suffixColor: '#5470C6ff', + suffixIsItalic: false, + suffixIsBolder: true, + suffixFontFamily: 'Microsoft YaHei', + suffixLetterSpace: 0, + suffixFontShadow: false +} +export const DEFAULT_INDICATOR_NAME_STYLE: ChartIndicatorNameStyle = { + show: true, + fontSize: 18, + color: '#ffffffff', + isItalic: false, + isBolder: true, + fontFamily: 'Microsoft YaHei', + letterSpace: 0, + fontShadow: false, + nameValueSpacing: 0 +} + +export const DEFAULT_TITLE_STYLE_BASE: ChartTextStyle = { + show: true, + fontSize: 16, + hPosition: 'left', + vPosition: 'top', + isItalic: false, + isBolder: true, + remarkShow: false, + remark: '', + fontFamily: '', + letterSpace: '0', + fontShadow: false, + color: '', + remarkBackgroundColor: '' +} + +export const DEFAULT_TITLE_STYLE_LIGHT = { + ...DEFAULT_TITLE_STYLE_BASE, + color: '#000000', + remarkBackgroundColor: '#ffffff' +} + +export const DEFAULT_TITLE_STYLE_DARK = { + ...DEFAULT_TITLE_STYLE_BASE, + color: '#FFFFFF', + remarkBackgroundColor: '#5A5C62' +} + +export const DEFAULT_LEGEND_STYLE: ChartLegendStyle = { + show: true, + hPosition: 'center', + vPosition: 'bottom', + orient: 'horizontal', + icon: 'circle', + color: '#333333', + fontSize: 12, + size: 4 +} + +export const DEFAULT_LEGEND_STYLE_BASE: ChartLegendStyle = { + show: true, + hPosition: 'center', + vPosition: 'bottom', + orient: 'horizontal', + icon: 'circle', + color: '#333333', + fontSize: 12, + size: 4 +} + +export const DEFAULT_LEGEND_STYLE_LIGHT: ChartLegendStyle = { + ...DEFAULT_LEGEND_STYLE_BASE, + color: '#333333', + fontSize: 12 +} + +export const DEFAULT_LEGEND_STYLE_DARK: ChartLegendStyle = { + ...DEFAULT_LEGEND_STYLE_BASE, + color: '#ffffff', + fontSize: 12 +} + +export const DEFAULT_MARGIN_STYLE = { + marginModel: 'auto', + marginTop: 40, + marginBottom: 44, + marginLeft: 15, + marginRight: 10 +} + +export const DEFAULT_XAXIS_STYLE: ChartAxisStyle = { + show: true, + position: 'bottom', + nameShow: false, + name: '', + color: '#333333', + fontSize: 12, + axisLabel: { + show: true, + color: '#333333', + fontSize: 12, + rotate: 0, + formatter: '{value}', + lengthLimit: 10 + }, + axisLine: { + show: true, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + splitLine: { + show: false, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + axisValue: { + auto: true, + min: 10, + max: 100, + split: 10, + splitCount: 10 + }, + axisLabelFormatter: { + type: 'auto', + unit: 1, + suffix: '', + decimalCount: 2, + thousandSeparator: true + } +} +export const DEFAULT_YAXIS_STYLE: ChartAxisStyle = { + show: true, + position: 'left', + nameShow: false, + name: '', + color: '#333333', + fontSize: 12, + axisLabel: { + show: true, + color: '#333333', + fontSize: 12, + rotate: 0, + formatter: '{value}', + lengthLimit: 10 + }, + axisLine: { + show: false, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + splitLine: { + show: true, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + axisValue: { + auto: true, + min: 10, + max: 100, + split: 10, + splitCount: 10 + }, + axisLabelFormatter: { + type: 'auto', + unit: 1, + suffix: '', + decimalCount: 2, + thousandSeparator: true + } +} +export const DEFAULT_YAXIS_EXT_STYLE: ChartAxisStyle = { + show: true, + position: 'right', + name: '', + color: '#333333', + fontSize: 12, + axisLabel: { + show: true, + color: '#333333', + fontSize: 12, + rotate: 0, + formatter: '{value}' + }, + axisLine: { + show: false, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + splitLine: { + show: false, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + axisValue: { + auto: true, + min: 10, + max: 100, + split: 10, + splitCount: 10 + }, + axisLabelFormatter: { + type: 'auto', + unit: 1, + suffix: '', + decimalCount: 2, + thousandSeparator: true + } +} +export const DEFAULT_BACKGROUND_COLOR = { + color: '#ffffff', + alpha: 0, + borderRadius: 0 +} +export const DEFAULT_MISC_STYLE: ChartMiscStyle = { + showName: false, + color: '#999', + fontSize: 12, + axisColor: '#999', + splitNumber: 5, + axisLine: { + show: true, + lineStyle: { + color: '#999999', + width: 1, + type: 'solid' + } + }, + axisTick: { + show: false, + length: 5, + lineStyle: { + color: '#999999', + width: 1, + type: 'solid' + } + }, + axisLabel: { + show: false, + rotate: 0, + margin: 8, + color: '#999999', + fontSize: '12', + formatter: '{value}' + }, + splitLine: { + show: true, + lineStyle: { + color: '#999999', + width: 1, + type: 'solid' + } + }, + splitArea: { + show: true + }, + axisValue: { + auto: true, + min: 10, + max: 100, + split: 10, + splitCount: 10 + } +} +export const DEFAULT_FUNCTION_CFG: ChartFunctionCfg = { + sliderShow: false, + sliderRange: [0, 10], + sliderBg: '#FFFFFF', + sliderFillBg: '#BCD6F1', + sliderTextColor: '#999999', + emptyDataStrategy: 'breakLine', + emptyDataCustomValue: '', + emptyDataFieldCtrl: [] +} +export const DEFAULT_ASSIST_LINE_CFG: ChartAssistLineCfg = { + enable: false, + assistLine: [] +} +export const DEFAULT_THRESHOLD: ChartThreshold = { + enable: false, + gaugeThreshold: '', + liquidThreshold: '', + labelThreshold: [], + tableThreshold: [], + textLabelThreshold: [], + lineLabelThreshold: [] +} +export const DEFAULT_SCROLL: ScrollCfg = { + open: false, + row: 1, + interval: 2000, + step: 50 +} + +export const DEFAULT_BUBBLE_ANIMATE: BubbleCfg = { + enable: false, + speed: 1, + rings: 1, + type: 'wave' +} + +export const DEFAULT_QUADRANT_STYLE: QuadrantAttr = { + lineStyle: { + stroke: '#aaa', + lineWidth: 1, + opacity: 0.5 + }, + regionStyle: [ + { + fill: '#fdfcfc', + fillOpacity: 0 + }, + { + fill: '#fafdfa', + fillOpacity: 0 + }, + { + fill: '#fdfcfc', + fillOpacity: 0 + }, + { + fill: '#fafdfa', + fillOpacity: 0 + } + ], + labels: [ + { + content: '', + style: { + fill: '#000000', + fillOpacity: 0.5, + fontSize: 14 + } + }, + { + content: '', + style: { + fill: '#000000', + fillOpacity: 0.5, + fontSize: 14 + } + }, + { + content: '', + style: { + fill: '#000000', + fillOpacity: 0.5, + fontSize: 14 + } + }, + { + content: '', + style: { + fill: '#000000', + fillOpacity: 0.5, + fontSize: 14 + } + } + ] +} + +export const COLOR_PANEL = [ + '#FF4500', + '#FF8C00', + '#FFD700', + '#71AE46', + '#00CED1', + '#1E90FF', + '#C71585', + '#999999', + '#000000', + '#FFFFFF' +] + +export const COLOR_CASES = [ + { + name: t('chart.color_default'), + value: 'default', + colors: [ + '#1E90FF', + '#90EE90', + '#00CED1', + '#E2BD84', + '#7A90E0', + '#3BA272', + '#2BE7FF', + '#0A8ADA', + '#FFD700' + ] + }, + { + name: t('chart.color_retro'), + value: 'retro', + colors: [ + '#0780cf', + '#765005', + '#fa6d1d', + '#0e2c82', + '#b6b51f', + '#da1f18', + '#701866', + '#f47a75', + '#009db2' + ] + }, + { + name: t('chart.color_elegant'), + value: 'elegant', + colors: [ + '#95a2ff', + '#fa8080', + '#ffc076', + '#fae768', + '#87e885', + '#3cb9fc', + '#73abf5', + '#cb9bff', + '#434348' + ] + }, + { + name: t('chart.color_future'), + value: 'future', + colors: [ + '#63b2ee', + '#76da91', + '#f8cb7f', + '#f89588', + '#7cd6cf', + '#9192ab', + '#7898e1', + '#efa666', + '#eddd86' + ] + }, + { + name: t('chart.color_gradual'), + value: 'gradual', + colors: [ + '#71ae46', + '#96b744', + '#c4cc38', + '#ebe12a', + '#eab026', + '#e3852b', + '#d85d2a', + '#ce2626', + '#ac2026' + ] + }, + { + name: t('chart.color_simple'), + value: 'simple', + colors: [ + '#929fff', + '#9de0ff', + '#ffa897', + '#af87fe', + '#7dc3fe', + '#bb60b2', + '#433e7c', + '#f47a75', + '#009db2' + ] + }, + { + name: t('chart.color_business'), + value: 'business', + colors: [ + '#194f97', + '#555555', + '#bd6b08', + '#00686b', + '#c82d31', + '#625ba1', + '#898989', + '#9c9800', + '#007f54' + ] + }, + { + name: t('chart.color_gentle'), + value: 'gentle', + colors: [ + '#5b9bd5', + '#ed7d31', + '#70ad47', + '#ffc000', + '#4472c4', + '#91d024', + '#b235e6', + '#02ae75', + '#5b9bd5' + ] + }, + { + name: t('chart.color_technology'), + value: 'technology', + colors: [ + '#05f8d6', + '#0082fc', + '#fdd845', + '#22ed7c', + '#09b0d3', + '#1d27c9', + '#f9e264', + '#f47a75', + '#009db2' + ] + }, + { + name: t('chart.color_light'), + value: 'light', + colors: [ + '#884898', + '#808080', + '#82ae46', + '#00a3af', + '#ef8b07', + '#007bbb', + '#9d775f', + '#fae800', + '#5f9b3c' + ] + }, + { + name: t('chart.color_classical'), + value: 'classical', + colors: [ + '#007bbb', + '#ffdb4f', + '#dd4b4b', + '#2ca9e1', + '#ef8b07', + '#4a488e', + '#82ae46', + '#dd4b4b', + '#bb9581' + ] + }, + { + name: t('chart.color_fresh'), + value: 'fresh', + colors: [ + '#5f9b3c', + '#75c24b', + '#83d65f', + '#aacf53', + '#c7dc68', + '#d8e698', + '#e0ebaf', + '#bbc8e6', + '#e5e5e5' + ] + }, + { + name: t('chart.color_energy'), + value: 'energy', + colors: [ + '#ef8b07', + '#2a83a2', + '#f07474', + '#c55784', + '#274a78', + '#7058a3', + '#0095d9', + '#75c24b', + '#808080' + ] + }, + { + name: t('chart.color_red'), + value: 'red', + colors: [ + '#ff0000', + '#ef8b07', + '#4c6cb3', + '#f8e944', + '#69821b', + '#9c5ec3', + '#00ccdf', + '#f07474', + '#bb9581' + ] + }, + { + name: t('chart.color_fast'), + value: 'fast', + colors: [ + '#fae800', + '#00c039', + '#0482dc', + '#bb9581', + '#ff7701', + '#9c5ec3', + '#00ccdf', + '#00c039', + '#ff7701' + ] + }, + { + name: t('chart.color_spiritual'), + value: 'spiritual', + colors: [ + '#00a3af', + '#4da798', + '#57baaa', + '#62d0bd', + '#6ee4d0', + '#86e7d6', + '#aeede1', + '#bde1e6', + '#e5e5e5' + ] + } +] + +export const BASE_ECHARTS_SELECT = { + itemStyle: { + shadowBlur: 2 + } +} + +export const CHART_FONT_FAMILY_ORIGIN = [ + { name: t('chart.font_family_ya_hei'), value: 'Microsoft YaHei' }, + { name: t('chart.font_family_song_ti'), value: 'SimSun, "Songti SC", STSong' }, + { name: t('chart.font_family_hei_ti'), value: 'SimHei, Helvetica' }, + { name: t('chart.font_family_kai_ti'), value: 'KaiTi, "Kaiti SC", STKaiti' } +] + +export const CHART_FONT_FAMILY_MAP_TRANS = { + 'Microsoft YaHei': 'Microsoft YaHei', + 'SimSun, "Songti SC", STSong': 'SimSun', + 'SimHei, Helvetica': 'SimHei', + 'KaiTi, "Kaiti SC", STKaiti': 'KaiTi' +} + +export const CHART_FONT_FAMILY = [ + { name: t('chart.font_family_ya_hei'), value: 'Microsoft YaHei' }, + { name: t('chart.font_family_song_ti'), value: 'SimSun' }, + { name: t('chart.font_family_hei_ti'), value: 'SimHei' }, + { name: t('chart.font_family_kai_ti'), value: 'KaiTi' } +] + +export const CHART_FONT_FAMILY_MAP:any = { + 'Microsoft YaHei': 'Microsoft YaHei', + SimSun: 'SimSun, "Songti SC", STSong', + SimHei: 'SimHei, Helvetica', + KaiTi: 'KaiTi, "Kaiti SC", STKaiti' +} + +export const CHART_FONT_LETTER_SPACE = [ + { name: '0px', value: 0 }, + { name: '1px', value: 1 }, + { name: '2px', value: 2 }, + { name: '3px', value: 3 }, + { name: '4px', value: 4 }, + { name: '5px', value: 5 }, + { name: '6px', value: 6 }, + { name: '7px', value: 7 }, + { name: '8px', value: 8 }, + { name: '9px', value: 9 }, + { name: '10px', value: 10 } +] + +export const NOT_SUPPORT_PAGE_DATASET = [ + 'kylin', + 'sqlServer', + 'es', + 'presto', + 'ds_doris', + 'StarRocks', + 'impala' +] + +export const SUPPORT_Y_M = ['y', 'y_M', 'y_M_d'] + +export const DEFAULT_MAP = { + mapPitch: 0, + lineType: 'line', + lineWidth: 1, + lineAnimate: true, + lineAnimateDuration: 4, + lineAnimateInterval: 0.5, + lineAnimateTrailLength: 0.1 +} + +export const CHART_TYPE_CONFIGS = [ + { + category: 'quota', + title: t('chart.chart_type_quota'), + display: 'show', + details: [ + { + render: 'antv', + category: 'quota', + value: 'gauge', + title: t('chart.chart_gauge'), + icon: 'gauge' + }, + { + render: 'antv', + category: 'quota', + value: 'liquid', + title: t('chart.chart_liquid'), + icon: 'liquid' + }, + { + render: 'custom', + category: 'quota', + value: 'indicator', + title: t('chart.chart_indicator'), + icon: 'indicator' + } + ] + }, + { + category: 'table', + title: t('chart.chart_type_table'), + display: 'show', + details: [ + { + render: 'antv', + category: 'table', + value: 'table-info', + title: t('chart.chart_table_info'), + icon: 'table-info' + }, + { + render: 'antv', + category: 'table', + value: 'table-normal', + title: t('chart.chart_table_normal'), + icon: 'table-normal' + }, + { + render: 'antv', + category: 'table', + value: 'table-pivot', + title: t('chart.chart_table_pivot'), + icon: 'table-pivot' + }, + { + render: 'antv', + category: 'table', + value: 't-heatmap', + title: t('chart.chart_table_heatmap'), + icon: 't-heatmap' + } + ] + }, + { + category: 'trend', + title: t('chart.chart_type_trend'), + display: 'show', + details: [ + { + render: 'antv', + category: 'trend', + value: 'line', + title: t('chart.chart_line'), + icon: 'line' + }, + { + render: 'antv', + category: 'trend', + value: 'area', + title: t('chart.chart_area'), + icon: 'area' + }, + { + render: 'antv', + category: 'trend', + value: 'area-stack', + title: t('chart.chart_area_stack'), + icon: 'area-stack' + } + ] + }, + { + category: 'compare', + title: t('chart.chart_type_compare'), + display: 'show', + details: [ + { + render: 'antv', + category: 'compare', + value: 'bar', + title: t('chart.chart_bar'), + icon: 'bar' + }, + { + render: 'antv', + category: 'compare', + value: 'bar-stack', + title: t('chart.chart_bar_stack'), + icon: 'bar-stack' + }, + { + render: 'antv', + category: 'compare', + value: 'percentage-bar-stack', + title: t('chart.chart_percentage_bar_stack'), + icon: 'percentage-bar-stack' + }, + { + render: 'antv', + category: 'compare', + value: 'bar-group', + title: t('chart.chart_bar_group'), + icon: 'bar-group' + }, + { + render: 'antv', + category: 'compare', + value: 'bar-group-stack', + title: t('chart.chart_bar_group_stack'), + icon: 'bar-group-stack' + }, + { + render: 'antv', + category: 'compare', + value: 'waterfall', + title: t('chart.chart_waterfall'), + icon: 'waterfall' + }, + { + render: 'antv', + category: 'compare', + value: 'bar-horizontal', + title: t('chart.chart_bar_horizontal'), + icon: 'bar-horizontal' + }, + { + render: 'antv', + category: 'compare', + value: 'bar-stack-horizontal', + title: t('chart.chart_bar_stack_horizontal'), + icon: 'bar-stack-horizontal' + }, + { + render: 'antv', + category: 'compare', + value: 'percentage-bar-stack-horizontal', + title: t('chart.chart_percentage_bar_stack_horizontal'), + icon: 'percentage-bar-stack-horizontal' + }, + { + render: 'antv', + category: 'compare', + value: 'bar-range', + title: t('chart.chart_bar_range'), + icon: 'bar-range' + }, + { + render: 'antv', + category: 'compare', + value: 'bidirectional-bar', + title: t('chart.chart_bidirectional_bar'), + icon: 'bidirectional-bar' + }, + { + render: 'antv', + category: 'compare', + value: 'progress-bar', + title: t('chart.chart_progress_bar'), + icon: 'progress-bar' + }, + { + render: 'antv', + category: 'trend', + value: 'stock-line', + title: t('chart.chart_stock_line'), + icon: 'stock-line' + } + ] + }, + { + category: 'distribute', + title: t('chart.chart_type_distribute'), + display: 'show', + details: [ + { + render: 'antv', + category: 'distribute', + value: 'pie', + title: t('chart.chart_pie'), + icon: 'pie' + }, + { + render: 'antv', + category: 'distribute', + value: 'pie-donut', + title: t('chart.chart_pie_donut'), + icon: 'pie-donut' + }, + { + render: 'antv', + category: 'distribute', + value: 'pie-rose', + title: t('chart.chart_pie_rose'), + icon: 'pie-rose' + }, + { + render: 'antv', + category: 'distribute', + value: 'pie-donut-rose', + title: t('chart.chart_pie_donut_rose'), + icon: 'pie-donut-rose' + }, + { + render: 'antv', + category: 'chart.chart_type_distribute', + value: 'radar', + title: t('chart.chart_radar'), + icon: 'radar' + }, + { + render: 'antv', + category: 'distribute', + value: 'treemap', + title: t('chart.chart_treemap'), + icon: 'treemap' + }, + { + render: 'antv', + category: 'distribute', + value: 'word-cloud', + title: t('chart.chart_word_cloud'), + icon: 'word-cloud' + } + ] + }, + { + category: 'map', + title: t('chart.chart_type_space'), + display: 'show', + details: [ + { + render: 'antv', + category: 'map', + value: 'map', + title: t('chart.chart_map'), + icon: 'map' + }, + { + render: 'antv', + category: 'map', + value: 'bubble-map', + title: t('chart.chart_bubble_map'), + icon: 'bubble-map' + }, + { + render: 'antv', + category: 'map', + value: 'flow-map', + title: t('chart.chart_flow_map'), + icon: 'flow-map' + }, + { + render: 'antv', + category: 'map', + value: 'heat-map', + title: t('chart.chart_heat_map'), + icon: 'heat-map' + }, + { + render: 'antv', + category: 'map', + value: 'symbolic-map', + title: t('chart.chart_symbolic_map'), + icon: 'symbolic-map' + } + ] + }, + { + category: 'relation', + title: t('chart.chart_type_relation'), + display: 'show', + details: [ + { + render: 'antv', + category: 'distribute', + value: 'scatter', + title: t('chart.chart_scatter'), + icon: 'scatter' + }, + { + render: 'antv', + category: 'distribute', + value: 'quadrant', + title: t('chart.chart_quadrant'), + icon: 'quadrant' + }, + { + render: 'antv', + category: 'distribute', + value: 'funnel', + title: t('chart.chart_funnel'), + icon: 'funnel' + }, + { + render: 'antv', + category: 'relation', + value: 'sankey', + title: t('chart.chart_sankey'), + icon: 'sankey' + }, + { + render: 'antv', + category: 'distribute', + value: 'circle-packing', + title: t('chart.chart_circle_packing'), + icon: 'circle-packing' + } + ] + }, + { + category: 'dual_axes', + title: t('chart.chart_type_dual_axes'), + display: 'show', + details: [ + { + render: 'antv', + category: 'dual_axes', + value: 'chart-mix', + title: t('chart.chart_mix'), + icon: 'chart-mix' + }, + { + render: 'antv', + category: 'dual_axes', + value: 'chart-mix-group', + title: t('chart.chart_mix_group_column'), + icon: 'chart-mix-group' + }, + { + render: 'antv', + category: 'dual_axes', + value: 'chart-mix-stack', + title: t('chart.chart_mix_stack_column'), + icon: 'chart-mix-stack' + }, + { + render: 'antv', + category: 'dual_axes', + value: 'chart-mix-dual-line', + title: t('chart.chart_mix_dual_line'), + icon: 'chart-mix-dual-line' + } + ] + }, + { + category: 'other', + title: t('datasource.other'), + display: 'hidden', + details: [ + { + render: 'custom', + category: 'quota', + value: 'rich-text', + title: t('visualization.rich_text'), + icon: 'rich-text' + }, + { + render: 'custom', + category: 'quota', + value: 'picture-group', + title: t('visualization.picture_group'), + icon: 'picture-group' + } + ] + } +] + +export const DEFAULT_BASIC_STYLE: ChartBasicStyle = { + alpha: 100, + tableBorderColor: '#CCCCCC', + tableScrollBarColor: '#1f23294d', + tableColumnMode: 'adapt', + tableColumnWidth: 100, + tableFieldWidth: [], + tablePageMode: 'page', + tablePageStyle: 'simple', + tablePageSize: 20, + gaugeStyle: 'default', + colorScheme: 'default', + colors: [ + '#5470c6', + '#91cc75', + '#fac858', + '#ee6666', + '#73c0de', + '#3ba272', + '#fc8452', + '#9a60b4', + '#ea7ccc' + ], + mapVendor: 'amap', + gradient: false, + lineWidth: 2, + lineSymbol: 'circle', + lineSymbolSize: 4, + lineSmooth: true, + barDefault: true, + radiusColumnBar: 'rightAngle', + columnBarRightAngleRadius: 20, + columnWidthRatio: 60, + barWidth: 40, + barGap: 0.4, + lineType: 'solid', + scatterSymbol: 'circle', + scatterSymbolSize: 8, + radarShape: 'polygon', + mapStyle: 'normal', + heatMapType: 'heatmap', + heatMapIntensity: 2, + heatMapRadius: 20, + areaBorderColor: '#EBEEF5', + areaBaseColor: '#ffffff', + mapSymbolOpacity: 0.7, + mapSymbolStrokeWidth: 2, + mapSymbol: 'circle', + mapSymbolSize: 6, + radius: 80, + innerRadius: 60, + showZoom: true, + zoomButtonColor: '#aaa', + zoomBackground: '#fff', + tableLayoutMode: 'grid', + calcTopN: false, + topN: 5, + topNLabel: t('datasource.other'), + gaugeAxisLine: true, + gaugePercentLabel: true, + showSummary: false, + summaryLabel: t('chart.total_show'), + seriesColor: [], + layout: 'horizontal', + mapSymbolSizeMin: 4, + mapSymbolSizeMax: 30, + showLabel: true, + mapStyleUrl: '', + autoFit: true, + mapCenter: { + longitude: 117.232, + latitude: 39.354 + }, + zoomLevel: 7, + customIcon: '', + showHoverStyle: true, + autoWrap: false, + maxLines: 3, + radarShowPoint: true, + radarPointSize: 4, + radarAreaColor: true, + circleBorderColor: '#fff', + circleBorderWidth: 0, + circlePadding: 0 +} + +export const BASE_VIEW_CONFIG = { + id: '', // 图表id + title: t('data_set.view'), + sceneId: 0, // 仪表板id + tableId: '', // 数据集id + type: 'bar', + render: 'antv', + resultCount: 1000, + resultMode: 'custom', + refreshViewEnable: false, + refreshTime: 5, + refreshUnit: 'minute', + xAxis: [], + xAxisExt: [], + yAxis: [], + yAxisExt: [], + extStack: [], + drillFields: [], + viewFields: [], + extBubble: [], + extLabel: [], + extTooltip: [], + customFilter: {}, + sortPriority: [], + customAttr: { + basicStyle: DEFAULT_BASIC_STYLE, + misc: DEFAULT_MISC, + label: DEFAULT_LABEL, + tooltip: DEFAULT_TOOLTIP, + tableTotal: DEFAULT_TABLE_TOTAL, + tableHeader: DEFAULT_TABLE_HEADER, + tableCell: DEFAULT_TABLE_CELL, + indicator: DEFAULT_INDICATOR_STYLE, + indicatorName: DEFAULT_INDICATOR_NAME_STYLE, + map: { + id: '', + level: 'world' + } + }, + customStyle: { + text: DEFAULT_TITLE_STYLE, + legend: DEFAULT_LEGEND_STYLE, + xAxis: DEFAULT_XAXIS_STYLE, + yAxis: DEFAULT_YAXIS_STYLE, + yAxisExt: DEFAULT_YAXIS_EXT_STYLE, + misc: DEFAULT_MISC_STYLE + }, + senior: { + functionCfg: DEFAULT_FUNCTION_CFG, + assistLineCfg: DEFAULT_ASSIST_LINE_CFG, + threshold: DEFAULT_THRESHOLD, + scrollCfg: DEFAULT_SCROLL, + areaMapping: {}, + bubbleCfg: DEFAULT_BUBBLE_ANIMATE + }, + flowMapStartName: [], + flowMapEndName: [] +} + +export function getScaleValue(propValue, scale) { + const propValueTemp = Math.round(propValue * scale) + return propValueTemp > 1 ? propValueTemp : 1 +} + +export function getViewConfig(name) { + let viewConfigResult = null + CHART_TYPE_CONFIGS.forEach(category => { + category.details.forEach(viewConfig => { + if (viewConfig.value === name) { + viewConfigResult = deepCopy(viewConfig) + } + }) + }) + return viewConfigResult +} diff --git a/frontend/src/data-visualization/chart/components/editor/util/dataVisualization.ts b/frontend/src/data-visualization/chart/components/editor/util/dataVisualization.ts new file mode 100644 index 0000000..93b6919 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/editor/util/dataVisualization.ts @@ -0,0 +1,142 @@ +export const VIEW_DETAILS_BASH_STYLE = + '{"id":"view-dialog-details-001","title":"图表明细","sceneId":0,"tableId":"1692381412250939392","type":"table-info","render":"antv","resultCount":1000,"resultMode":"all","refreshViewEnable":false,"refreshTime":5,"refreshUnit":"minute","xAxis":[{"id":"1692330126490","datasourceId":"1691734038709071872","datasetTableId":"7098147058204282880","datasetGroupId":"1692381412250939392","chartId":null,"originName":"月","name":"月","dbFieldName":null,"description":null,"dataeaseName":"f_dd62e53a9192cdf4","groupType":"d","type":"ANY","precision":null,"scale":null,"deType":0,"deExtractType":0,"extField":0,"checked":true,"columnIndex":null,"lastSyncTime":null,"dateFormat":null,"dateFormatType":null,"fieldShortName":"f_dd62e53a9192cdf4","summary":null,"sort":"none","dateStyle":"y_M_d","datePattern":"date_sub","chartType":null,"compareCalc":null,"logic":null,"filterType":null,"index":null,"formatterCfg":null,"chartShowName":null,"filter":[],"customSort":null,"busiType":null},{"id":"1692330126489","datasourceId":"1691734038709071872","datasetTableId":"7098147058204282880","datasetGroupId":"1692381412250939392","chartId":null,"originName":"年份","name":"年份","dbFieldName":null,"description":null,"dataeaseName":"f_190480c43bdda8df","groupType":"q","type":"BIGINT","precision":null,"scale":null,"deType":2,"deExtractType":2,"extField":0,"checked":true,"columnIndex":null,"lastSyncTime":null,"dateFormat":null,"dateFormatType":null,"fieldShortName":"f_190480c43bdda8df","summary":"sum","sort":"none","dateStyle":"y_M_d","datePattern":"date_sub","chartType":"bar","compareCalc":{"type":"none","resultData":"percent","field":null,"custom":null},"logic":null,"filterType":null,"index":null,"formatterCfg":{"type":"auto","unit":1,"suffix":"","decimalCount":2,"thousandSeparator":true},"chartShowName":null,"filter":[],"customSort":null,"busiType":null}],"xAxisExt":[],"yAxis":[],"yAxisExt":[],"extStack":[],"drillFields":[],"viewFields":[],"extBubble":[],"extLabel":[],"extTooltip":[],"customFilter":{},"customAttr":{"basicStyle":{"alpha":100,"tableBorderColor":"#E6E7E4","tableScrollBarColor":"#00000024","tableColumnMode":"adapt","tableColumnWidth":100,"tablePageMode":"pull","tablePageSize":20,"gaugeStyle":"default","colorScheme":"default","colors":["#5470c6","#91cc75","#fac858","#ee6666","#73c0de","#3ba272","#fc8452","#9a60b4","#ea7ccc"],"mapVendor":"amap","gradient":false,"lineWidth":2,"lineSymbol":"circle","lineSymbolSize":4,"lineSmooth":true,"barDefault":true,"barWidth":40,"barGap":0.4,"lineType":"solid","scatterSymbol":"circle","scatterSymbolSize":8,"radarShape":"polygon","mapStyle":"normal","areaBorderColor":"#303133","suspension":true,"areaBaseColor":"#FFFFFF","mapSymbolOpacity":0.7,"mapSymbolStrokeWidth":2,"mapSymbol":"circle","mapSymbolSize":20,"radius":100,"innerRadius":60},"misc":{"pieInnerRadius":0,"pieOuterRadius":80,"radarShape":"polygon","radarSize":80,"gaugeMinType":"fix","gaugeMinField":{"id":"","summary":""},"gaugeMin":0,"gaugeMaxType":"fix","gaugeMaxField":{"id":"","summary":""},"gaugeMax":100,"gaugeStartAngle":225,"gaugeEndAngle":-45,"nameFontSize":18,"valueFontSize":18,"nameValueSpace":10,"valueFontColor":"#5470c6","valueFontFamily":"Microsoft YaHei","valueFontIsBolder":false,"valueFontIsItalic":false,"valueLetterSpace":0,"valueFontShadow":false,"showName":true,"nameFontColor":"#000000","nameFontFamily":"Microsoft YaHei","nameFontIsBolder":false,"nameFontIsItalic":false,"nameLetterSpace":"0","nameFontShadow":false,"treemapWidth":80,"treemapHeight":80,"liquidMax":100,"liquidMaxType":"fix","liquidMaxField":{"id":"","summary":""},"liquidSize":80,"liquidShape":"circle","hPosition":"center","vPosition":"center","mapPitch":0,"mapLineType":"arc","mapLineWidth":1,"mapLineAnimateDuration":3,"mapLineGradient":false,"mapLineSourceColor":"#146C94","mapLineTargetColor":"#576CBC"},"label":{"show":false,"position":"top","color":"#000000","fontSize":10,"formatter":"","labelLine":{"show":true},"labelFormatter":{"type":"auto","unit":1,"suffix":"","decimalCount":2,"thousandSeparator":true},"reserveDecimalCount":2,"labelShadow":false,"labelBgColor":"","labelShadowColor":"","quotaLabelFormatter":{"type":"auto","unit":1,"suffix":"","decimalCount":2,"thousandSeparator":true},"showDimension":true,"showQuota":false,"showProportion":true,"seriesLabelFormatter":[]},"tooltip":{"show":true,"trigger":"item","confine":true,"fontSize":10,"color":"#000000","tooltipFormatter":{"type":"auto","unit":1,"suffix":"","decimalCount":2,"thousandSeparator":true},"backgroundColor":"#FFFFFF","seriesTooltipFormatter":[]},"tableTotal":{"row":{"showGrandTotals":true,"showSubTotals":true,"reverseLayout":false,"reverseSubLayout":false,"label":"总计","subLabel":"小计","subTotalsDimensions":[],"calcTotals":{"aggregation":"SUM"},"calcSubTotals":{"aggregation":"SUM"},"totalSort":"none","totalSortField":""},"col":{"showGrandTotals":true,"showSubTotals":true,"reverseLayout":false,"reverseSubLayout":false,"label":"总计","subLabel":"小计","subTotalsDimensions":[],"calcTotals":{"aggregation":"SUM"},"calcSubTotals":{"aggregation":"SUM"},"totalSort":"none","totalSortField":""}},"tableHeader":{"indexLabel":"序号","showIndex":false,"tableHeaderAlign":"left","tableHeaderBgColor":"#F5F6F7","tableHeaderFontColor":"#646A73","tableTitleFontSize":14,"tableTitleHeight":36},"tableCell":{"tableFontColor":"#1F2329","tableItemAlign":"right","tableItemBgColor":"#FFFFFF","tableItemFontSize":14,"tableItemHeight":36},"map":{"id":"","level":"world"}},"customStyle":{"text":{"show":false,"fontSize":"18","hPosition":"left","vPosition":"top","isItalic":false,"isBolder":true,"remarkShow":false,"remark":"","fontFamily":"Microsoft YaHei","letterSpace":"0","fontShadow":false,"color":"#000000","remarkBackgroundColor":"#ffffff"},"legend":{"show":true,"hPosition":"center","vPosition":"bottom","orient":"horizontal","icon":"circle","color":"#000000","fontSize":12},"xAxis":{"show":true,"position":"bottom","name":"","color":"#000000","fontSize":12,"axisLabel":{"show":true,"color":"#000000","fontSize":12,"rotate":0,"formatter":"{value}"},"axisLine":{"show":true,"lineStyle":{"color":"#cccccc","width":1,"style":"solid"}},"splitLine":{"show":false,"lineStyle":{"color":"#CCCCCC","width":1,"style":"solid"}},"axisValue":{"auto":true,"min":10,"max":100,"split":10,"splitCount":10},"axisLabelFormatter":{"type":"auto","unit":1,"suffix":"","decimalCount":2,"thousandSeparator":true}},"yAxis":{"show":true,"position":"left","name":"","color":"#000000","fontSize":12,"axisLabel":{"show":true,"color":"#000000","fontSize":12,"rotate":0,"formatter":"{value}"},"axisLine":{"show":false,"lineStyle":{"color":"#cccccc","width":1,"style":"solid"}},"splitLine":{"show":true,"lineStyle":{"color":"#CCCCCC","width":1,"style":"solid"}},"axisValue":{"auto":true,"min":10,"max":100,"split":10,"splitCount":10},"axisLabelFormatter":{"type":"auto","unit":1,"suffix":"","decimalCount":2,"thousandSeparator":true}},"yAxisExt":{"show":true,"position":"right","name":"","color":"#000000","fontSize":12,"axisLabel":{"show":true,"color":"#000000","fontSize":12,"rotate":0,"formatter":"{value}"},"axisLine":{"show":false,"lineStyle":{"color":"#cccccc","width":1,"style":"solid"}},"splitLine":{"show":true,"lineStyle":{"color":"#CCCCCC","width":1,"style":"solid"}},"axisValue":{"auto":true,"min":null,"max":null,"split":null,"splitCount":null},"axisLabelFormatter":{"type":"auto","unit":1,"suffix":"","decimalCount":2,"thousandSeparator":true}},"misc":{"showName":false,"color":"#000000","fontSize":12,"axisColor":"#999","splitNumber":5,"axisLine":{"show":true,"lineStyle":{"color":"#CCCCCC","width":1,"type":"solid"}},"axisTick":{"show":false,"length":5,"lineStyle":{"color":"#000000","width":1,"type":"solid"}},"axisLabel":{"show":false,"rotate":0,"margin":8,"color":"#000000","fontSize":"12","formatter":"{value}"},"splitLine":{"show":true,"lineStyle":{"color":"#CCCCCC","width":1,"type":"solid"}},"splitArea":{"show":true}}},"senior":{"functionCfg":{"sliderShow":false,"sliderRange":[0,10],"sliderBg":"#FFFFFF","sliderFillBg":"#BCD6F1","sliderTextColor":"#999999","emptyDataStrategy":"breakLine","emptyDataFieldCtrl":[]},"assistLine":[],"threshold":{"gaugeThreshold":"","labelThreshold":[],"tableThreshold":[],"textLabelThreshold":[]},"scrollCfg":{"open":false,"row":1,"interval":2000,"step":50}},"chartExtRequest":{"user":"1","filter":[],"drill":[],"resultCount":1000,"resultMode":"all"}}' + +import { + DEFAULT_COLOR_CASE_DARK, + DEFAULT_COLOR_CASE_LIGHT, + DEFAULT_TITLE_STYLE_DARK, + DEFAULT_TITLE_STYLE_LIGHT, + FILTER_COMMON_STYLE_DARK, + FILTER_COMMON_STYLE_LIGHT, + SENIOR_STYLE_SETTING_DARK, + SENIOR_STYLE_SETTING_LIGHT, + TAB_COMMON_STYLE_DARK, + TAB_COMMON_STYLE_LIGHT +} from '@/data-visualization/chart/components/editor/util/chart' +import { + COMMON_COMPONENT_BACKGROUND_DARK, + COMMON_COMPONENT_BACKGROUND_LIGHT, + COMMON_COMPONENT_BACKGROUND_SCREEN_DARK +} from '@/data-visualization/custom-component/component-list' + +export const PANEL_CHART_INFO_LIGHT = { + chartTitle: DEFAULT_TITLE_STYLE_LIGHT, + chartColor: DEFAULT_COLOR_CASE_LIGHT, + chartCommonStyle: COMMON_COMPONENT_BACKGROUND_LIGHT, + filterStyle: FILTER_COMMON_STYLE_LIGHT, + tabStyle: TAB_COMMON_STYLE_LIGHT, + seniorStyleSetting: SENIOR_STYLE_SETTING_LIGHT +} + +export const PANEL_CHART_INFO_DARK = { + chartTitle: DEFAULT_TITLE_STYLE_DARK, + chartColor: DEFAULT_COLOR_CASE_DARK, + chartCommonStyle: COMMON_COMPONENT_BACKGROUND_DARK, + filterStyle: FILTER_COMMON_STYLE_DARK, + tabStyle: TAB_COMMON_STYLE_DARK, + seniorStyleSetting: SENIOR_STYLE_SETTING_DARK +} + +export const PANEL_CHART_INFO_SCREEN_DARK = { + ...PANEL_CHART_INFO_DARK, + chartCommonStyle: COMMON_COMPONENT_BACKGROUND_SCREEN_DARK +} + +export const MOBILE_SETTING_BASE = { + customSetting: false, + imageUrl: null, + backgroundType: 'image' +} + +export const MOBILE_SETTING_LIGHT = { + ...MOBILE_SETTING_BASE, + color: '#000' +} + +export const MOBILE_SETTING_DARK = { + ...MOBILE_SETTING_BASE, + color: '#fff' +} + +export const DEFAULT_DASHBOARD_STYLE_BASE = { + gap: 'yes', + gapSize: 5, + showGrid: false, + matrixBase: 4, // 当前matrix的基数 (是pcMatrixCount的几倍) + resultMode: 'all', // 图表结果显示模式 all 图表 custom 仪表板自定义 + resultCount: 1000 // 图表结果显示条数 +} + +export const DEFAULT_DASHBOARD_STYLE_LIGHT = { + ...DEFAULT_DASHBOARD_STYLE_BASE, + themeColor: 'light', + mobileSetting: MOBILE_SETTING_LIGHT +} + +export const DEFAULT_DASHBOARD_STYLE_DARK = { + ...DEFAULT_DASHBOARD_STYLE_BASE, + themeColor: 'dark', + mobileSetting: MOBILE_SETTING_DARK +} + +export const DEFAULT_CANVAS_STYLE_DATA_BASE = { + width: 1920, + height: 1080, + refreshBrowserEnable: false, // 开启浏览器刷新(默认关闭) + refreshBrowserUnit: 'minute', // 仪表板刷新时间带外 默认 分钟 + refreshBrowserTime: 5, // 仪表板刷新时间 默认5分钟 + refreshViewEnable: false, // 开启图表刷新(默认关闭) + refreshViewLoading: true, // 仪表板图表loading提示 + refreshUnit: 'minute', // 仪表板刷新时间带外 默认 分钟 + refreshTime: 5, // 仪表板刷新时间 默认5分钟 + popupAvailable: true, // 弹窗区域是否可用 默认为true + popupButtonAvailable: true, // 弹框区域显示按钮是否可用 默认为true + suspensionButtonAvailable: false, // 悬浮按钮是否可用 默认false + screenAdaptor: 'widthFirst', // 屏幕适配方式 widthFirst=宽度优先 heightFirst=高度优先 full=铺满全屏 keepSize=不缩放 + dashboardAdaptor: 'keepHeightAndWidth', //仪表板预览展示适配方式 keepHeightAndWidth=高度宽度独立缩放(默认模式),withWidth=跟随宽度 + scale: 60, + scaleWidth: 60, + scaleHeight: 60, + backgroundColorSelect: true, + backgroundImageEnable: false, + backgroundType: 'backgroundColor', // 废弃 + background: '', + openCommonStyle: true, + opacity: 1, // 废弃 + fontSize: 14, + fontFamily: 'PingFang' //字体设置 默认PingFang +} + +// 基础亮色主题 +export const DEFAULT_CANVAS_STYLE_DATA_LIGHT = { + ...DEFAULT_CANVAS_STYLE_DATA_BASE, + // 页面全局数据 + themeId: '10001', + color: '#000000', + backgroundColor: '#f5f6f7', + dashboard: DEFAULT_DASHBOARD_STYLE_LIGHT, + component: PANEL_CHART_INFO_LIGHT +} + +// 基础暗色主题 +export const DEFAULT_CANVAS_STYLE_DATA_DARK = { + ...DEFAULT_CANVAS_STYLE_DATA_BASE, + // 页面全局数据 + themeId: '10002', + color: '#fff', + backgroundColor: '#020408', + dashboard: DEFAULT_DASHBOARD_STYLE_DARK, + component: PANEL_CHART_INFO_DARK +} + +// 大屏基础暗色主题 +export const DEFAULT_CANVAS_STYLE_DATA_SCREEN_DARK = { + ...DEFAULT_CANVAS_STYLE_DATA_DARK, + component: PANEL_CHART_INFO_SCREEN_DARK +} + +// 基础主题 +export const BASE_THEMES = { + light: DEFAULT_CANVAS_STYLE_DATA_LIGHT, + dark: DEFAULT_CANVAS_STYLE_DATA_DARK +} diff --git a/frontend/src/data-visualization/chart/components/js/extremumUitl.ts b/frontend/src/data-visualization/chart/components/js/extremumUitl.ts new file mode 100644 index 0000000..6413f62 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/extremumUitl.ts @@ -0,0 +1,392 @@ +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { hexToRgba, parseJson } from '@/data-visualization/chart/components/js/util' +import { isEmpty } from 'lodash-es' + +export const clearExtremum = chart => { + // 清除图表标注 + const pointElement = document.getElementById(chartPointParentId(chart)) + if (pointElement) { + pointElement.remove() + pointElement.parentNode?.removeChild(pointElement) + } +} + +/** + * 判断给定的RGBA字符串表示的颜色是亮色还是暗色 + * 通过计算RGB颜色值的加权平均值(灰度值),判断颜色的明暗 + * 如果给定的字符串不包含有效的RGBA值,则原样返回该字符串 + * + * @param rgbaString 一个RGBA颜色字符串,例如 "rgba(255, 255, 255, 1)" + * @param greyValue 灰度值默认128 + * @returns 如果计算出的灰度值大于等于128,则返回true,表示亮色;否则返回false,表示暗色。 + * 如果rgbaString不包含有效的RGBA值,则返回原字符串 + */ +const isColorLight = (rgbaString: string, greyValue = 128) => { + const lastRGBA = getRgbaColorLastRgba(rgbaString) + if (!isEmpty(lastRGBA)) { + // 计算灰度值的公式 + const grayLevel = lastRGBA.r * 0.299 + lastRGBA.g * 0.587 + lastRGBA.b * 0.114 + return grayLevel >= greyValue + } else { + return false + } +} + +/** + * 从给定的rgba颜色字符串中提取最后一个rgba值 + * @param rgbaString 包含一个或多个rgba颜色值的字符串 + * @returns 返回最后一个解析出的rgba对象,如果未找到rgba值,则返回null + */ +const getRgbaColorLastRgba = (rgbaString: string) => { + const rgbaPattern = /rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/g + let match: string[] + let lastRGBA = null + while ((match = rgbaPattern.exec(rgbaString)) !== null) { + const r = parseInt(match[1]) + const g = parseInt(match[2]) + const b = parseInt(match[3]) + const a = parseFloat(match[4]) + lastRGBA = { r, g, b, a } + } + return lastRGBA +} + +function createExtremumDiv(id, value, formatterCfg, chart) { + // 空值不处理 + if (!value && value != 0) { + return + } + // 装标注的div + const parentElement = document.getElementById(chartPointParentId(chart)) + if (parentElement) { + // 标注div + const oldElement = document.getElementById(id) + if (oldElement) { + oldElement.remove() + oldElement.parentNode?.removeChild(oldElement) + } + const div = document.createElement('div') + div.id = id + div.className = 'child' + div.setAttribute( + 'style', + `width: auto; + height: auto; + border-radius: 2px; + position: relative; + padding: 4px 5px 4px 5px; + display:none; + transform: translateX(-50%); + opacity: 1; + transition: opacity 0.2s ease-in-out; + white-space:nowrap;` + ) + div.textContent = valueFormatter(value, formatterCfg) + const span = document.createElement('span') + span.setAttribute( + 'style', + `width: 0px; + height: 0px; + border: 4px solid transparent; + border-top-color: red; + position: absolute; + left: calc(50% - 4px); + margin-top:4px;` + ) + div.appendChild(span) + parentElement.appendChild(div) + } +} +/** + * 没有子类别字段的图表 + * @param chart + */ +const noChildrenFieldChart = chart => { + return ['area', 'bar'].includes(chart.type) +} + +/** + * 支持最值图表的折线图,面积图,柱状图,分组柱状图 + * @param chart + */ +const supportExtremumChartType = chart => { + return ['line', 'area', 'bar', 'bar-group'].includes(chart.type) +} + +const chartContainerId = chart => { + return chart.container + '_' +} + +const chartPointParentId = chart => { + return chart.container + '_point_' + chart.id + '_' +} + +function removeDivsWithPrefix(parentDivId, prefix) { + const parentDiv = document.getElementById(parentDivId) + if (!parentDiv) { + console.error('Parent div not found') + return + } + const childDivs = parentDiv.getElementsByTagName('div') + for (let i = childDivs.length - 1; i >= 0; i--) { + const div = childDivs[i] + if (div.id && div.id.startsWith(prefix)) { + div.parentNode.removeChild(div) + } + } +} + +export const extremumEvt = (newChart, chart, _options, container) => { + chart.container = container + if (!supportExtremumChartType(chart)) { + clearExtremum(chart) + return + } + const { label: labelAttr } = parseJson(chart.customAttr) + const { yAxis } = parseJson(chart) + newChart.once('beforerender', ev => { + ev.view.on('beforepaint', () => { + newChart.chart.geometries[0]?.beforeMappingData.forEach(i => { + i.forEach(item => { + delete item._origin.EXTREME + }) + const { minItem, maxItem } = findMinMax(i.filter(item => item._origin.value)) + if (!minItem || !maxItem) { + return + } + let showExtremum = false + if (noChildrenFieldChart(chart) || yAxis.length > 1) { + const seriesLabelFormatter = labelAttr.seriesLabelFormatter.find( + d => + (d.chartShowName ? d.chartShowName : d.name === minItem._origin.category) || + (d.chartShowName ? d.chartShowName : d.name === maxItem._origin.category) + ) + showExtremum = seriesLabelFormatter?.showExtremum + } else { + if (['bar-group'].includes(chart.type)) { + showExtremum = labelAttr.showExtremum + } else { + showExtremum = labelAttr.seriesLabelFormatter[0]?.showExtremum + } + } + if (showExtremum) { + minItem._origin.EXTREME = true + maxItem._origin.EXTREME = true + } + }) + }) + newChart.chart.geometries[0].on('afteranimate', () => { + createExtremumPoint(chart, ev) + }) + }) + newChart.on('legend-item:click', ev => { + const legendHideData = ev.view + .getController('legend') + .components[0].component.cfg.items.filter(l => l.unchecked) + if (legendHideData.length > 0) { + legendHideData.forEach(l => { + const seriesKey = chartContainerId(chart) + chartPointParentId(chart) + l.id + removeDivsWithPrefix(chartPointParentId(chart), seriesKey) + }) + } + }) +} + +const findMinMax = (data): { minItem; maxItem } => { + return data.reduce( + ({ minItem, maxItem }, currentItem) => { + if (minItem === undefined || currentItem._origin.value < minItem._origin.value) { + minItem = currentItem + } + if (maxItem === undefined || currentItem._origin.value > maxItem._origin.value) { + maxItem = currentItem + } + delete minItem?._origin.EXTREME + delete maxItem?._origin.EXTREME + return { minItem, maxItem } + }, + { minItem: undefined, maxItem: undefined } + ) +} +export const createExtremumPoint = (chart, ev) => { + // 获取标注样式 + const { label: labelAttr, basicStyle } = parseJson(chart.customAttr) + const pointSize = basicStyle.lineSymbolSize + const { yAxis } = parseJson(chart) + clearExtremum(chart) + // 创建标注父元素 + const divParentElement = document.getElementById(chartPointParentId(chart)) + if (!divParentElement) { + const divParent = document.createElement('div') + divParent.id = chartPointParentId(chart) + divParent.style.position = 'fixed' + divParent.style.zIndex = '1' + divParent.style.opacity = '0' + divParent.style.transition = 'opacity 0.2s ease-in-out' + // 将父标注加入到图表中 + const containerElement = document.getElementById(chart.container) + containerElement.insertBefore(divParent, containerElement.firstChild) + // 处理最值闪烁的问题 + let opacity = 0 + const animate = () => { + // 增加不透明度 + opacity += 0.19 + if (opacity >= 1) { + cancelAnimationFrame(animationFrameId) + return + } + divParent.style.opacity = opacity + '' + animationFrameId = requestAnimationFrame(animate) + } + let animationFrameId = requestAnimationFrame(animate) + } + let geometriesDataArray = [] + // 获取数据点 + const intervalPoint = ev.view + .getGeometries() + .find((intervalItem: { type: string }) => intervalItem.type === 'interval') + if (intervalPoint) { + geometriesDataArray = intervalPoint.dataArray + } + const pointPoint = ev.view + .getGeometries() + .find((pointItem: { type: string }) => pointItem.type === 'point') + if (pointPoint) { + geometriesDataArray = pointPoint.dataArray + } + performChunk(geometriesDataArray, pointObjList => { + if (pointObjList && pointObjList.length > 0) { + const pointObj = pointObjList[0] + const [minItem, maxItem] = pointObjList.filter(i => i._origin.EXTREME) + let attr + let showExtremum = false + if (noChildrenFieldChart(chart) || yAxis.length > 1) { + const seriesLabelFormatter = labelAttr.seriesLabelFormatter.find(d => + d.chartShowName + ? d.chartShowName === pointObj._origin.category + : d.name === pointObj._origin.category + ) + showExtremum = seriesLabelFormatter?.showExtremum + attr = seriesLabelFormatter + } else { + if (['bar-group'].includes(chart.type)) { + showExtremum = labelAttr.showExtremum + } else { + showExtremum = labelAttr.seriesLabelFormatter[0]?.showExtremum + attr = labelAttr.seriesLabelFormatter[0] + } + } + const fontSize = attr ? attr.fontSize : labelAttr.fontSize + if (!minItem) { + return + } + const maxKey = + chartContainerId(chart) + + chartPointParentId(chart) + + pointObj._origin.category + + '_' + + (maxItem ? maxItem._origin.value : minItem._origin.value) + const minKey = + chartContainerId(chart) + + chartPointParentId(chart) + + pointObj._origin.category + + '_' + + minItem._origin.value + // 最值标注 + if (showExtremum && labelAttr.show) { + if (maxItem) { + createExtremumDiv( + maxKey, + maxItem._origin.value, + attr ? attr.formatterCfg : labelAttr.labelFormatter, + chart + ) + } + createExtremumDiv( + minKey, + minItem._origin.value, + attr ? attr.formatterCfg : labelAttr.labelFormatter, + chart + ) + pointObjList.forEach(point => { + const pointElement = document.getElementById( + chartContainerId(chart) + + chartPointParentId(chart) + + point._origin.category + + '_' + + point._origin.value + ) + if (pointElement && point._origin.EXTREME) { + pointElement.style.position = 'absolute' + const top = + (point.y[1] ? point.y[1] : point.y) - (fontSize + (pointSize ? pointSize : 0) + 12) + pointElement.style.top = top + 'px' + pointElement.style.left = point.x + 'px' + pointElement.style.zIndex = '10' + pointElement.style.fontSize = fontSize + 'px' + pointElement.style.lineHeight = fontSize + 'px' + // 渐变颜色时需要获取最后一个rgba的值作为背景 + const color = point.color.startsWith('#') + ? hexToRgba(point.color, basicStyle.alpha / 100) + : getRgbaColorLastRgba(point.color) + const { r, b, g, a } = color + pointElement.style.backgroundColor = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')' + pointElement.style.color = isColorLight(point.color) ? '#000' : '#fff' + pointElement.children[0]['style'].borderTopColor = + 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')' + pointElement.style.display = 'table' + // 显示箭头 + const childNode = pointElement.childNodes[1] + // 最值在数据点下方显示 + const translateYValue = Math.ceil(point.y + Math.abs(Math.floor(top)) + 6) + // 最值dom高度超过50%时,最值dom向下 + if (top < 0 && (Math.abs(top) / point.y) * 100 >= 50) { + pointElement.style.transform = `translateX(-50%) translateY(${translateYValue}px)` + childNode.style.marginTop = '-12px' + childNode.style.transform = 'rotate(180deg)' + } else { + childNode.style.display = 'block' + } + } + }) + } else { + removeDivElement(maxKey) + removeDivElement(minKey) + } + } + }) +} + +function removeDivElement(key) { + const element = document.getElementById(key) + if (element) { + element.remove() + element.parentNode?.removeChild(element) + } +} + +/** + * 用于分批处理数据,利用requestIdleCallback在浏览器空闲期间执行任务,避免阻塞主线程 + * @param dataList + * @param taskHandler + */ +function performChunk(dataList, taskHandler) { + if (typeof dataList === 'number') { + dataList = { length: dataList } + } + if (dataList.length === 0) return + let i = 0 + function _run() { + if (i >= dataList.length) return + // 请求浏览器空闲期间执行的回调函数 + requestIdleCallback(idle => { + // 在当前空闲期间内尽可能多地处理任务,直到时间耗尽或所有任务处理完毕 + while (idle.timeRemaining() > 0 && i < dataList.length) { + taskHandler(dataList[i], i) + i++ + } + _run() + }) + } + _run() +} diff --git a/frontend/src/data-visualization/chart/components/js/formatter.ts b/frontend/src/data-visualization/chart/components/js/formatter.ts new file mode 100644 index 0000000..a297fbb --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/formatter.ts @@ -0,0 +1,107 @@ +import { Datum } from '@antv/g2plot' + +export const formatterItem = { + type: 'auto', // auto,value,percent + unit: 1, // 换算单位 + suffix: '', // 单位后缀 + decimalCount: 2, // 小数位数 + thousandSeparator: true // 千分符 +} + +// 单位list +export const unitType = [ + { name: 'unit_none', value: 1 }, + { name: 'unit_thousand', value: 1000 }, + { name: 'unit_ten_thousand', value: 10000 }, + { name: 'unit_million', value: 1000000 }, + { name: 'unit_hundred_million', value: 100000000 } +] + +// 格式化方式 +export const formatterType = [ + { name: 'value_formatter_auto', value: 'auto' }, + { name: 'value_formatter_value', value: 'value' }, + { name: 'value_formatter_percent', value: 'percent' } +] + +export function valueFormatter(value, formatter) { + if (value === null || value === undefined) { + return null + } + // 1.unit 2.decimal 3.thousand separator and suffix + let result + if (formatter.type === 'auto') { + result = transSeparatorAndSuffix(transUnit(value, formatter), formatter) + } else if (formatter.type === 'value') { + result = transSeparatorAndSuffix( + transDecimal(transUnit(value, formatter), formatter), + formatter + ) + } else if (formatter.type === 'percent') { + value = value * 100 + result = transSeparatorAndSuffix(transDecimal(value, formatter), formatter) + } else { + result = value + } + return result +} + +function transUnit(value, formatter) { + return value / formatter.unit +} + +function transDecimal(value, formatter) { + const resultV = value.toFixed(formatter.decimalCount) + if (Object.is(parseFloat(resultV), -0)) { + return resultV.slice(1) + } + return resultV +} + +function transSeparatorAndSuffix(value, formatter) { + let str = value + '' + if (str.match(/^(\d)(\.\d)?e-(\d)/)) { + str = value.toFixed(18).replace(/\.?0+$/, '') + } + if (formatter.thousandSeparator) { + const thousandsReg = /(\d)(?=(\d{3})+$)/g + const numArr = str.split('.') + numArr[0] = numArr[0].replace(thousandsReg, '$1,') + str = numArr.join('.') + } + if (formatter.type === 'percent') { + str += '%' + //百分比没有后缀,直接返回 + return str + } else { + if (formatter.unit === 1000) { + str += '千' + } else if (formatter.unit === 10000) { + str += '万' + } else if (formatter.unit === 1000000) { + str += '百万' + } else if (formatter.unit === 100000000) { + str += '亿' + } + } + return str + formatter.suffix.replace(/(^\s*)|(\s*$)/g, '') +} + +export function singleDimensionTooltipFormatter(param: Datum, chart: Chart, prop = 'category') { + let res + const yAxis = chart.yAxis + const obj = { name: param[prop], value: param.value } + for (let i = 0; i < yAxis.length; i++) { + const f = yAxis[i] + if (f.name === param[prop]) { + if (f.formatterCfg) { + res = valueFormatter(param.value, f.formatterCfg) + } else { + res = valueFormatter(param.value, formatterItem) + } + break + } + } + obj.value = res ?? '' + return obj +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/bar/bar.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/bar/bar.ts new file mode 100644 index 0000000..3d25530 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/bar/bar.ts @@ -0,0 +1,691 @@ +import type { Column, ColumnOptions } from '@antv/g2plot/esm/plots/column' +import { cloneDeep, each, groupBy, isEmpty } from 'lodash-es' +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import { + flow, + hexColorToRGBA, + hexToRgba, + parseJson, + setUpGroupSeriesColor, + setUpStackSeriesColor +} from '@/data-visualization/chart/components/js/util' +import type { Datum } from '@antv/g2plot' +import { formatterItem, valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { + BAR_AXIS_TYPE, + BAR_EDITOR_PROPERTY, + BAR_EDITOR_PROPERTY_INNER +} from '@/data-visualization/chart/components/js/panel/charts/bar/common' +import { + configPlotTooltipEvent, + getLabel, + getPadding, + getTooltipContainer, + setGradientColor, + TOOLTIP_TPL +} from '@/data-visualization/chart/components/js/panel/common/common_antv' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { DEFAULT_BASIC_STYLE, DEFAULT_LABEL } from '@/data-visualization/chart/components/editor/util/chart' +import { clearExtremum, extremumEvt } from '@/data-visualization/chart/components/js/extremumUitl' +import { Group } from '@antv/g-canvas' + +const { t } = useI18n() +const DEFAULT_DATA: any[] = [] +/** + * 柱状图 + */ +export class Bar extends G2PlotChartView { + properties = BAR_EDITOR_PROPERTY + propertyInner = { + ...BAR_EDITOR_PROPERTY_INNER, + 'basic-style-selector': [...BAR_EDITOR_PROPERTY_INNER['basic-style-selector'], 'seriesColor'], + 'label-selector': ['vPosition', 'seriesLabelFormatter', 'showExtremum'], + 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'], + 'y-axis-selector': [...BAR_EDITOR_PROPERTY_INNER['y-axis-selector'], 'axisLabelFormatter'] + } + protected baseOptions: ColumnOptions = { + xField: 'field', + yField: 'value', + seriesField: 'category', + isGroup: true, + data: [] + } + + axis: AxisType[] = [...BAR_AXIS_TYPE] + axisConfig = { + ...this['axisConfig'], + xAxis: { + name: `${t('chart.drag_block_type_axis')} / ${t('chart.dimension')}`, + type: 'd' + }, + yAxis: { + name: `${t('chart.drag_block_value_axis')} / ${t('chart.quota')}`, + type: 'q' + } + } + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (!chart?.data?.data?.length) { + chart.container = container + clearExtremum(chart) + return + } + const data = cloneDeep(drawOptions.chart.data?.data) + const initOptions: ColumnOptions = { + ...this.baseOptions, + appendPadding: getPadding(chart), + data + } + const options: ColumnOptions = this.setupOptions(chart, initOptions) + let newChart = null + const { Column: ColumnClass } = await import('@antv/g2plot/esm/plots/column') + newChart = new ColumnClass(container, options) + newChart.on('interval:click', action) + extremumEvt(newChart, chart, options, container) + configPlotTooltipEvent(chart, newChart) + return newChart + } + + protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions { + const tmpOptions = super.configLabel(chart, options) + if (!tmpOptions.label) { + return { + ...tmpOptions, + label: false + } + } + const { label: labelAttr } = parseJson(chart.customAttr) + const formatterMap = labelAttr.seriesLabelFormatter?.reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) + // 默认是灰色 + tmpOptions.label.style.fill = DEFAULT_LABEL.color + const label = { + fields: [], + ...tmpOptions.label, + formatter: (data: Datum, _point) => { + if (data.EXTREME) { + return '' + } + if (!labelAttr.seriesLabelFormatter?.length) { + return data.value + } + const labelCfg = formatterMap?.[data.quotaList[0].id] as SeriesFormatter + if (!labelCfg) { + return data.value + } + if (!labelCfg.show) { + return + } + const value = valueFormatter(data.value, labelCfg.formatterCfg) + const group = new Group({}) + group.addShape({ + type: 'text', + attrs: { + x: 0, + y: 0, + text: value, + textAlign: 'start', + textBaseline: 'top', + fontSize: labelCfg.fontSize, + fontFamily: chart.fontFamily, + fill: labelCfg.color + } + }) + return group + }, + position: data => { + if (data.value < 0) { + if (tmpOptions.label?.position === 'top') { + return 'bottom' + } + if (tmpOptions.label?.position === 'bottom') { + return 'top' + } + } + return tmpOptions.label?.position + } + } + return { + ...tmpOptions, + label + } + } + + protected configTooltip(chart: Chart, options: ColumnOptions): ColumnOptions { + return super.configMultiSeriesTooltip(chart, options) + } + + protected configBasicStyle(chart: Chart, options: ColumnOptions): ColumnOptions { + const basicStyle = parseJson(chart.customAttr).basicStyle + if (basicStyle.gradient) { + let color = basicStyle.colors + color = color.map(ele => { + const tmp = hexColorToRGBA(ele, basicStyle.alpha) + return setGradientColor(tmp, true, 270) + }) + options = { + ...options, + color + } + } + if (basicStyle.radiusColumnBar === 'roundAngle') { + const columnStyle = { + radius: [ + basicStyle.columnBarRightAngleRadius, + basicStyle.columnBarRightAngleRadius, + basicStyle.columnBarRightAngleRadius, + basicStyle.columnBarRightAngleRadius + ] + } + options = { + ...options, + columnStyle + } + } + let columnWidthRatio + const _v = basicStyle.columnWidthRatio ?? DEFAULT_BASIC_STYLE.columnWidthRatio + if (_v >= 1 && _v <= 100) { + columnWidthRatio = _v / 100.0 + } else if (_v < 1) { + columnWidthRatio = 1 / 100.0 + } else if (_v > 100) { + columnWidthRatio = 1 + } + if (columnWidthRatio) { + options.columnWidthRatio = columnWidthRatio + } + + return options + } + + protected configYAxis(chart: Chart, options: ColumnOptions): ColumnOptions { + const tmpOptions = super.configYAxis(chart, options) + if (!tmpOptions.yAxis) { + return tmpOptions + } + const yAxis = parseJson(chart.customStyle).yAxis + const axisValue = yAxis.axisValue + if (tmpOptions.yAxis.label) { + tmpOptions.yAxis.label.formatter = value => { + return valueFormatter(value, yAxis.axisLabelFormatter) + } + } + if (!axisValue?.auto) { + const axis = { + yAxis: { + ...tmpOptions.yAxis, + min: axisValue.min, + max: axisValue.max, + minLimit: axisValue.min, + maxLimit: axisValue.max, + tickCount: axisValue.splitCount + } + } + return { ...tmpOptions, ...axis } + } + return tmpOptions + } + + protected setupOptions(chart: Chart, options: ColumnOptions): ColumnOptions { + return flow( + this.addConditionsStyleColorToData, + this.configTheme, + this.configEmptyDataStrategy, + this.configColor, + this.configBasicStyle, + this.configLabel, + this.configTooltip, + this.configLegend, + this.configXAxis, + this.configYAxis, + this.configSlider, + this.configAnalyse, + this.configBarConditions + )(chart, options, {}, this) + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + chart.senior.functionCfg.emptyDataStrategy = 'ignoreData' + return chart + } + + constructor(name = 'bar', defaultData = DEFAULT_DATA) { + super(name, defaultData) + } +} + +/** + * 堆叠柱状图 + */ +export class StackBar extends Bar { + properties = BAR_EDITOR_PROPERTY.filter(ele => ele !== 'threshold') + propertyInner = { + ...this['propertyInner'], + 'label-selector': [ + ...BAR_EDITOR_PROPERTY_INNER['label-selector'], + 'vPosition', + 'showTotal', + 'totalColor', + 'totalFontSize', + 'totalFormatter', + 'showStackQuota' + ], + 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter', 'show'] + } + protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions { + let label = getLabel(chart) + if (!label) { + return options + } + options = { ...options, label } + const { label: labelAttr } = parseJson(chart.customAttr) + if (labelAttr.showStackQuota || labelAttr.showStackQuota === undefined) { + label.style.fill = labelAttr.color + label = { + ...label, + formatter: function (param: Datum) { + return valueFormatter(param.value, labelAttr.labelFormatter) + } + } + } else { + label = false + } + if (labelAttr.showTotal) { + const formatterCfg = labelAttr.labelFormatter ?? formatterItem + each(groupBy(options.data, 'field'), (values, key) => { + const total = values.reduce((a, b) => a + b.value, 0) + const value = valueFormatter(total, formatterCfg) + if (!options.annotations) { + options = { + ...options, + annotations: [] + } + } + options.annotations.push({ + type: 'text', + position: [key, total], + content: `${value}`, + style: { + textAlign: 'center', + fontSize: labelAttr.fontSize, + fill: labelAttr.color + }, + offsetY: -(parseInt(labelAttr.fontSize as unknown as string) / 2) + }) + }) + } + return { + ...options, + label + } + } + + protected configTooltip(chart: Chart, options: ColumnOptions): ColumnOptions { + const tooltipAttr = parseJson(chart.customAttr).tooltip + if (!tooltipAttr.show) { + return { + ...options, + tooltip: false + } + } + const tooltip = { + formatter: (param: Datum) => { + const name = isEmpty(param.category) ? param.field : param.category + const obj = { name, value: param.value } + const res = valueFormatter(param.value, tooltipAttr.tooltipFormatter) + obj.value = res ?? '' + return obj + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + return { + ...options, + tooltip + } + } + + protected configColor(chart: Chart, options: ColumnOptions): ColumnOptions { + return this.configStackColor(chart, options) + } + + protected configData(chart: Chart, options: ColumnOptions): ColumnOptions { + const { xAxis, extStack, yAxis } = chart + const mainSort = xAxis.some(axis => axis.sort !== 'none') + const subSort = extStack.some(axis => axis.sort !== 'none') + if (mainSort || subSort) { + return options + } + const quotaSort = yAxis?.[0].sort !== 'none' + if (!quotaSort || !extStack.length || !yAxis.length) { + return options + } + const { data } = options + const mainAxisValueMap = data.reduce((p, n) => { + p[n.field] = p[n.field] ? p[n.field] + n.value : n.value || 0 + return p + }, {}) + const sort = yAxis[0].sort + data.sort((p, n) => { + if (sort === 'asc') { + return mainAxisValueMap[p.field] - mainAxisValueMap[n.field] + } else { + return mainAxisValueMap[n.field] - mainAxisValueMap[p.field] + } + }) + return options + } + + public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + return setUpStackSeriesColor(chart, data) + } + + protected setupOptions(chart: Chart, options: ColumnOptions): ColumnOptions { + return flow( + this.configTheme, + this.configEmptyDataStrategy, + this.configData, + this.configColor, + this.configBasicStyle, + this.configLabel, + this.configTooltip, + this.configLegend, + this.configXAxis, + this.configYAxis, + this.configSlider, + this.configAnalyse + )(chart, options, {}, this) + } + + constructor(name = 'bar-stack') { + super(name) + this.baseOptions = { + ...this.baseOptions, + isStack: true, + isGroup: false, + meta: { + category: { + type: 'cat' + } + } + } + this.axis = [...this.axis, 'extStack'] + } +} + +/** + * 分组柱状图 + */ +export class GroupBar extends StackBar { + properties = BAR_EDITOR_PROPERTY + propertyInner = { + ...this['propertyInner'], + 'label-selector': [...BAR_EDITOR_PROPERTY_INNER['label-selector'], 'vPosition', 'showExtremum'] + } + axisConfig = { + ...this['axisConfig'], + yAxis: { + name: `${t('chart.drag_block_value_axis')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + } + } + + protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions { + const tmpLabel = getLabel(chart) + if (!tmpLabel) { + return options + } + const baseOptions = { ...options, label: tmpLabel } + const { label: labelAttr } = parseJson(chart.customAttr) + baseOptions.label.style.fill = labelAttr.color + const label = { + ...baseOptions.label, + formatter: function (param: Datum, _point) { + if (param.EXTREME) { + return '' + } + const value = valueFormatter(param.value, labelAttr.labelFormatter) + return labelAttr.childrenShow ? value : null + } + } + return { + ...baseOptions, + label + } + } + + protected configColor(chart: Chart, options: ColumnOptions): ColumnOptions { + return this.configGroupColor(chart, options) + } + + public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + return setUpGroupSeriesColor(chart, data) + } + + protected setupOptions(chart: Chart, options: ColumnOptions): ColumnOptions { + return flow( + this.addConditionsStyleColorToData, + this.configTheme, + this.configEmptyDataStrategy, + this.configColor, + this.configBasicStyle, + this.configLabel, + this.configTooltip, + this.configLegend, + this.configXAxis, + this.configYAxis, + this.configSlider, + this.configAnalyse, + this.configBarConditions + )(chart, options, {}, this) + } + + constructor(name = 'bar-group') { + super(name) + this.baseOptions = { + ...this.baseOptions, + isGroup: true, + isStack: false, + meta: { + category: { + type: 'cat' + } + } + } + this.axis = [...BAR_AXIS_TYPE, 'xAxisExt'] + } +} + +/** + * 分组堆叠柱状图 + */ +export class GroupStackBar extends StackBar { + propertyInner = { + ...this['propertyInner'], + 'label-selector': [...BAR_EDITOR_PROPERTY_INNER['label-selector'], 'vPosition'] + } + protected configTheme(chart: Chart, options: ColumnOptions): ColumnOptions { + const baseOptions = super.configTheme(chart, options) + const baseTheme = baseOptions.theme as object + const theme = { + ...baseTheme, + innerLabels: { + offset: 0 + } + } + return { + ...options, + theme + } + } + + protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions { + const tmpLabel = getLabel(chart) + if (!tmpLabel) { + return options + } + const baseOptions = { ...options, label: tmpLabel } + const { label: labelAttr } = parseJson(chart.customAttr) + baseOptions.label.style.fill = labelAttr.color + const label = { + ...baseOptions.label, + formatter: function (param: Datum) { + return valueFormatter(param.value, labelAttr.labelFormatter) + } + } + return { + ...baseOptions, + label + } + } + + protected configTooltip(chart: Chart, options: ColumnOptions): ColumnOptions { + const tooltipAttr = parseJson(chart.customAttr).tooltip + if (!tooltipAttr.show) { + return { + ...options, + tooltip: false + } + } + const tooltip = { + fields: [], + formatter: (param: Datum) => { + const obj = { name: `${param.category} - ${param.group}`, value: param.value } + obj.value = valueFormatter(param.value, tooltipAttr.tooltipFormatter) + return obj + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + return { + ...options, + tooltip + } + } + + protected setupOptions(chart: Chart, options: ColumnOptions): ColumnOptions { + return flow( + this.configTheme, + this.configEmptyDataStrategy, + this.configColor, + this.configBasicStyle, + this.configLabel, + this.configTooltip, + this.configLegend, + this.configXAxis, + this.configYAxis, + this.configSlider, + this.configAnalyse + )(chart, options, {}, this) + } + + constructor(name = 'bar-group-stack') { + super(name) + this.baseOptions = { + ...this.baseOptions, + isGroup: true, + groupField: 'group' + } + this.axis = [...this.axis, 'xAxisExt', 'extStack'] + } +} + +/** + * 百分比堆叠柱状图 + */ +export class PercentageStackBar extends GroupStackBar { + propertyInner = { + ...this['propertyInner'], + 'label-selector': ['color', 'fontSize', 'vPosition', 'reserveDecimalCount'], + 'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'show'] + } + protected configLabel(chart: Chart, options: ColumnOptions): ColumnOptions { + const baseOptions = super.configLabel(chart, options) + if (!baseOptions.label) { + return baseOptions + } + const { customAttr } = chart + const l = parseJson(customAttr).label + const label = { + ...baseOptions.label, + formatter: function (param: Datum) { + if (!param.value) { + return '0%' + } + return (Math.round(param.value * 10000) / 100).toFixed(l.reserveDecimalCount) + '%' + } + } + return { + ...baseOptions, + label + } + } + + protected configTooltip(chart: Chart, options: ColumnOptions): ColumnOptions { + const tooltipAttr = parseJson(chart.customAttr).tooltip + if (!tooltipAttr.show) { + return { + ...options, + tooltip: { + showContent: false + } + } + } + const { customAttr } = chart + const l = parseJson(customAttr).label + const tooltip = { + formatter: (param: Datum) => { + const obj = { name: param.category, value: param.value } + obj.value = (Math.round(param.value * 10000) / 100).toFixed(l.reserveDecimalCount) + '%' + return obj + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + return { + ...options, + tooltip + } + } + protected setupOptions(chart: Chart, options: ColumnOptions): ColumnOptions { + return flow( + this.configTheme, + this.configEmptyDataStrategy, + this.configColor, + this.configBasicStyle, + this.configLabel, + this.configTooltip, + this.configLegend, + this.configXAxis, + this.configYAxis, + this.configSlider, + this.configAnalyse + )(chart, options, {}, this) + } + constructor() { + super('percentage-bar-stack') + this.baseOptions = { + ...this.baseOptions, + isStack: true, + isPercent: true, + isGroup: false, + groupField: undefined, + meta: { + category: { + type: 'cat' + } + } + } + this.axis = [...BAR_AXIS_TYPE, 'extStack'] + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/bar/bidirectional-bar.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/bar/bidirectional-bar.ts new file mode 100644 index 0000000..41456d0 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/bar/bidirectional-bar.ts @@ -0,0 +1,582 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import { cloneDeep, defaultTo, isEmpty, map } from 'lodash-es' +import { + configAxisLabelLengthLimit, + configPlotTooltipEvent, + getPadding, + getTooltipContainer, + getTooltipItemConditionColor, + getYAxis, + getYAxisExt, + setGradientColor, + TOOLTIP_TPL, + addConditionsStyleColorToData +} from '@/data-visualization/chart/components/js/panel/common/common_antv' +import type { + BidirectionalBar as G2BidirectionalBar, + BidirectionalBarOptions +} from '@antv/g2plot/esm/plots/bidirectional-bar' +import { flow, hexColorToRGBA, parseJson } from '@/data-visualization/chart/components/js/util' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import type { Options } from '@antv/g2plot/esm' +import { Group } from '@antv/g-canvas' + +const { t } = useI18n() +/** + * 对称柱状图 + */ + +export class BidirectionalHorizontalBar extends G2PlotChartView< + BidirectionalBarOptions, + G2BidirectionalBar +> { + axisConfig = { + ...this['axisConfig'], + xAxis: { + name: `${t('chart.drag_block_type_axis')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1 + }, + yAxis: { + name: `${t('chart.drag_block_value_axis')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + }, + yAxisExt: { + name: `${t('chart.drag_block_value_axis_ext')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + } + } + axis: AxisType[] = ['xAxis', 'yAxis', 'yAxisExt', 'filter', 'drill', 'extLabel', 'extTooltip'] + properties: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'x-axis-selector', + 'dual-y-axis-selector', + 'title-selector', + 'legend-selector', + 'label-selector', + 'tooltip-selector', + 'function-cfg', + 'jump-set', + 'linkage', + 'threshold' + ] + propertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + 'basic-style-selector': ['colors', 'alpha', 'gradient', 'layout', 'radiusColumnBar'], + 'x-axis-selector': ['position', 'axisLabel', 'axisLine', 'splitLine'], + 'dual-y-axis-selector': [ + 'name', + 'position', + 'color', + 'fontSize', + 'axisLabel', + 'axisLine', + 'splitLine', + 'axisValue', + 'axisLabelFormatter' + ], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'legend-selector': ['icon', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition'], + 'function-cfg': ['emptyDataStrategy'], + 'label-selector': ['hPosition', 'vPosition', 'seriesLabelFormatter'], + 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'], + threshold: ['lineThreshold'] + } + + selectorSpec: EditorSelectorSpec = { + ...this['selectorSpec'], + 'dual-y-axis-selector': { + title: `${t('chart.xAxis')}` + }, + 'x-axis-selector': { + title: `${t('chart.yAxis')}` + } + } + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (!chart.data?.data?.length) { + return + } + // data + const data = cloneDeep(chart.data.data) + const data1 = defaultTo(data[0]?.data, []) + const data2 = map(defaultTo(data[1]?.data, []), d => { + return { + ...d, + category: d.field, + value: data1.find(item => item.field === d.field)?.value, + valueExt: d.value + } + }) + // options + const initOptions: BidirectionalBarOptions = { + xField: 'field', + data: data2, + xAxis: { + label: { + style: {} + }, + position: 'bottom' + }, + yField: ['value', 'valueExt'], + appendPadding: getPadding(chart), + meta: { + field: { + type: 'cat' + } + } + } + const customOptions = this.setupOptions(chart, initOptions) + const options = { + ...customOptions + } + const xAxis = chart.xAxis + if (xAxis?.length === 1 && xAxis[0].deType === 1) { + const values = data2.map(item => item.field) + options.meta = { + field: { + type: 'cat', + values: values.reverse() + } + } + } + const { BidirectionalBar: BidirectionalBarClass } = await import( + '@antv/g2plot/esm/plots/bidirectional-bar' + ) + // 开始渲染 + const newChart = new BidirectionalBarClass(container, options) + + newChart.on('interval:click', action) + newChart.on('element:click', ev => { + const sourceData = newChart.options.data.filter( + item => + item.field === ev.data.data.field && + item[ev.data.data['series-field-key']] === ev.data.data[ev.data.data['series-field-key']] + ) + ev.data.data = { + ...ev.data.data, + ...sourceData[0] + } + }) + configPlotTooltipEvent(chart, newChart) + configAxisLabelLengthLimit(chart, newChart) + return newChart + } + + protected configBasicStyle( + chart: Chart, + options: BidirectionalBarOptions + ): BidirectionalBarOptions { + const basicStyle = parseJson(chart.customAttr).basicStyle + if (basicStyle.gradient) { + const color = basicStyle.colors?.map((ele, index) => { + const tmp = hexColorToRGBA(ele, basicStyle.alpha) + let angle = 180 - index * 180 + // 垂直固定角度 + if (basicStyle.layout === 'vertical') { + if (index === 0) { + angle = 280 + } + if (index === 1) { + angle = 90 + } + } + return setGradientColor(tmp, true, angle) + }) + options = { + ...options, + color + } + } + options = { + ...options, + layout: basicStyle.layout + } + if (basicStyle.radiusColumnBar === 'roundAngle') { + const barStyle = { + radius: [ + basicStyle.columnBarRightAngleRadius, + basicStyle.columnBarRightAngleRadius, + basicStyle.columnBarRightAngleRadius, + basicStyle.columnBarRightAngleRadius + ] + } + options = { + ...options, + barStyle + } + } + return options + } + + protected configXAxis(chart: Chart, options: BidirectionalBarOptions): BidirectionalBarOptions { + const tmpOptions = super.configXAxis(chart, options) + if (!tmpOptions.xAxis) { + return tmpOptions + } + if (tmpOptions.xAxis.label) { + delete tmpOptions.xAxis.label.style.textAlign + } + return tmpOptions + } + + protected configTooltip(chart: Chart, options: BidirectionalBarOptions): BidirectionalBarOptions { + const customAttr: DeepPartial = parseJson(chart.customAttr) + const tooltipAttr = customAttr.tooltip + if (!tooltipAttr.show) { + return { + ...options, + tooltip: false + } + } + const yAxis = cloneDeep(chart.yAxis) + const yAxisExt = cloneDeep(chart.yAxisExt) + const formatterMap = tooltipAttr.seriesTooltipFormatter + ?.filter(i => i.show) + .reduce((pre, next) => { + pre[next.seriesId] = next + return pre + }, {}) as Record + const optionsData = cloneDeep(options.data) + const yaxisObj = item => { + const param = item.data + let yaxis = yAxis[0] + let axisType = 'yAxis' + if (param['series-field-key'] === 'valueExt') { + yaxis = yAxisExt[0] + axisType = 'yAxisExt' + } + return { + id: yaxis.id, + name: yaxis.name, + axisType: axisType, + value: param[param['series-field-key']] + } + } + const tooltip: BidirectionalBarOptions['tooltip'] = { + shared: true, + showTitle: true, + customItems(originalItems) { + if (!tooltipAttr.seriesTooltipFormatter?.length) { + return originalItems + } + const result = [] + originalItems + .filter(item => { + const obj = yaxisObj(item) + return formatterMap[obj.id + '-' + obj.axisType] + }) + .forEach(item => { + const obj = yaxisObj(item) + const formatter = formatterMap[obj.id + '-' + obj.axisType] + const value = valueFormatter(parseFloat(item.value as string), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + const color = getTooltipItemConditionColor(item) + result.push({ ...item, name, value, color }) + }) + const dynamicTooltipValue = optionsData.find( + d => d.field === originalItems[0]['title'] + )?.dynamicTooltipValue + if (dynamicTooltipValue.length > 0) { + dynamicTooltipValue.forEach(dy => { + const q = tooltipAttr.seriesTooltipFormatter.filter(i => i.id === dy.fieldId) + if (q && q.length > 0) { + const value = valueFormatter(parseFloat(dy.value as string), q[0].formatterCfg) + const name = isEmpty(q[0].chartShowName) ? q[0].name : q[0].chartShowName + result.push({ color: 'grey', name, value }) + } + }) + } + return result + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + return { + ...options, + tooltip + } + } + + protected configLegend(chart: Chart, options: BidirectionalBarOptions): BidirectionalBarOptions { + const o = super.configLegend(chart, options) + if (o.legend) { + o.legend.itemName.formatter = (_text: string, _item: any, index: number) => { + const yaxis = chart.yAxis[0] + const yaxisExt = chart.yAxisExt[0] + if (index === 0) { + return yaxis.chartShowName ? yaxis.chartShowName : yaxis.name + } + return yaxisExt.chartShowName ? yaxisExt.chartShowName : yaxisExt.name + } + } + return o + } + + protected configYAxis(chart: Chart, options: BidirectionalBarOptions): BidirectionalBarOptions { + const yAxis = getYAxis(chart) + let yAxisExt = getYAxisExt(chart) + + const tempOption = { + ...options + } + + if (!yAxis) { + //左右轴都要隐藏 + yAxisExt = false + tempOption['yAxis'] = { + value: false, + valueExt: false + } + } else { + tempOption['yAxis'] = { + value: undefined, + valueExt: undefined + } + } + const layoutHorizontal = options.layout === 'horizontal' + // 处理横轴标题方向不对 + if (yAxis && yAxis['title'] && layoutHorizontal) { + yAxis['title'].autoRotate = false + } + const yAxisTmp = parseJson(chart.customStyle).yAxis + if (yAxis['label']) { + yAxis['label'].formatter = value => { + return valueFormatter(value, yAxisTmp.axisLabelFormatter) + } + } + const axisValue = yAxisTmp.axisValue + if (!axisValue?.auto) { + tempOption.yAxis.value = { + ...yAxis, + min: axisValue.min, + max: axisValue.max, + minLimit: axisValue.min, + maxLimit: axisValue.max, + tickCount: axisValue.splitCount + } + } else { + tempOption.yAxis.value = yAxis + } + + const yAxisExtTmp = parseJson(chart.customStyle).yAxisExt + if (yAxisExt['label']) { + yAxisExt['label'].formatter = value => { + return valueFormatter(value, yAxisExtTmp.axisLabelFormatter) + } + } + const axisExtValue = yAxisExtTmp.axisValue + if (!axisExtValue?.auto) { + tempOption.yAxis.valueExt = { + ...yAxisExt, + min: axisExtValue.min, + max: axisExtValue.max, + minLimit: axisExtValue.min, + maxLimit: axisExtValue.max, + tickCount: axisExtValue.splitCount + } + } else { + tempOption.yAxis.valueExt = yAxisExt + } + + return tempOption + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + chart.customStyle.yAxis = { + ...chart.customStyle.yAxis, + position: 'left' + } + chart.customStyle.yAxisExt = { + ...chart.customStyle.yAxisExt, + position: 'left', + splitLine: chart.customStyle.yAxis.splitLine + } + chart.customAttr.label = { + ...chart.customAttr.label, + position: 'right' + } + chart.customAttr.basicStyle.layout = 'horizontal' + return chart + } + + protected configLabel(chart: Chart, options: BidirectionalBarOptions): BidirectionalBarOptions { + let label + const yAxis = chart.yAxis + const yAxisExt = chart.yAxisExt + const labelAttr = parseJson(chart.customAttr).label + const formatterMap = labelAttr.seriesLabelFormatter?.reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) + let customAttr: DeepPartial + const layoutHorizontal = options.layout === 'horizontal' + if (chart.customAttr) { + customAttr = parseJson(chart.customAttr) + // label + if (customAttr.label) { + const l = customAttr.label + if (l.show) { + const layout = [] + if (!labelAttr.fullDisplay) { + const tmpOptions = super.configLabel(chart, options) + layout.push(...tmpOptions.label.layout) + } + label = { + position: l.position, + layout, + style: { + fill: l.color, + fontSize: l.fontSize, + fontFamily: chart.fontFamily + }, + formatter: param => { + let yaxis = yAxis[0] + let res = param.value + if (param['series-field-key'] === 'valueExt') { + yaxis = yAxisExt[0] + } + const value = param[param['series-field-key']] + const labelCfg = formatterMap?.[yaxis.id] as SeriesFormatter + if (yaxis.formatterCfg) { + res = valueFormatter(value, yaxis.formatterCfg) + } + if (!labelCfg) { + return res + } + if (!labelCfg.show) { + return + } + if (labelCfg) { + res = valueFormatter(value, labelCfg.formatterCfg) + } else { + res = valueFormatter(value, l.labelFormatter) + } + const group = new Group({}) + const isValue = param['series-field-key'] === 'value' + const textAlign = isValue && layoutHorizontal ? 'end' : 'start' + const isMiddle = label.position === 'middle' + group.addShape({ + type: 'text', + attrs: { + x: + isValue && layoutHorizontal && !isMiddle + ? -6 + : !isValue && layoutHorizontal && !isMiddle + ? 6 + : 0, + y: + isValue && !layoutHorizontal && !isMiddle + ? -8 + : !isValue && !layoutHorizontal && !isMiddle + ? 8 + : 0, + text: res, + textAlign: label.position === 'middle' ? 'start' : textAlign, + textBaseline: 'top', + fontSize: labelCfg.fontSize, + fontFamily: chart.fontFamily, + fill: labelCfg.color + } + }) + return group + } + } + } else { + label = false + } + } + } + if (!layoutHorizontal) { + if (label.position === 'left') { + label.position = 'bottom' + } + if (label.position === 'right') { + label.position = 'top' + } + } + return { ...options, label } + } + + protected configEmptyDataStrategy( + chart: Chart, + options: BidirectionalBarOptions + ): BidirectionalBarOptions { + const { data } = options as unknown as Options + if (!data?.length) { + return options + } + const strategy = parseJson(chart.senior).functionCfg.emptyDataStrategy + if (strategy === 'ignoreData') { + const emptyFields = data + .filter(obj => obj['value'] === null || obj['valueExt'] === null) + .map(obj => obj['field']) + return { + ...options, + data: data.filter(obj => { + if (emptyFields.includes(obj['field'])) { + return false + } + return true + }) + } + } + const updateValues = (strategy: 'breakLine' | 'setZero', data: any[]) => { + data.forEach(obj => { + if (obj['value'] === null) { + obj['value'] = strategy === 'breakLine' ? null : 0 + } + if (obj['valueExt'] === null) { + obj['valueExt'] = strategy === 'breakLine' ? null : 0 + } + }) + } + if (strategy === 'breakLine' || strategy === 'setZero') { + updateValues(strategy, data) + } + return options + } + + protected setupOptions(chart: Chart, options: BidirectionalBarOptions) { + return flow( + this.addConditionsStyleColorToData, + this.configTheme, + this.configBasicStyle, + this.configLabel, + this.configTooltip, + this.configLegend, + this.configXAxis, + this.configYAxis, + this.configAnalyse, + this.configSlider, + this.configEmptyDataStrategy, + this.configBarConditions + )(chart, options) + } + + constructor() { + super('bidirectional-bar', []) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/bar/common.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/bar/common.ts new file mode 100644 index 0000000..6421d18 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/bar/common.ts @@ -0,0 +1,84 @@ +export const BAR_EDITOR_PROPERTY: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'label-selector', + 'tooltip-selector', + 'x-axis-selector', + 'y-axis-selector', + 'title-selector', + 'legend-selector', + 'function-cfg', + 'assist-line', + 'jump-set', + 'linkage', + 'threshold' +] +export const BAR_RANGE_EDITOR_PROPERTY: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'label-selector', + 'tooltip-selector', + 'x-axis-selector', + 'y-axis-selector', + 'title-selector', + 'legend-selector', + 'function-cfg', + 'jump-set', + 'linkage', + 'threshold' +] + +export const BAR_EDITOR_PROPERTY_INNER: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + 'basic-style-selector': ['colors', 'alpha', 'gradient', 'radiusColumnBar', 'columnWidthRatio'], + 'label-selector': ['fontSize', 'color', 'labelFormatter'], + 'tooltip-selector': ['fontSize', 'color', 'tooltipFormatter', 'show'], + 'x-axis-selector': [ + 'name', + 'color', + 'fontSize', + 'axisLine', + 'splitLine', + 'axisForm', + 'axisLabel', + 'position' + ], + 'y-axis-selector': [ + 'name', + 'color', + 'fontSize', + 'axisValue', + 'axisLine', + 'splitLine', + 'axisForm', + 'axisLabel', + 'position' + ], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'legend-selector': ['icon', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition'], + 'function-cfg': ['slider', 'emptyDataStrategy'], + threshold: ['lineThreshold'] +} + +export const BAR_AXIS_TYPE: AxisType[] = [ + 'xAxis', + 'yAxis', + 'filter', + 'drill', + 'extLabel', + 'extTooltip' +] diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/bar/horizontal-bar.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/bar/horizontal-bar.ts new file mode 100644 index 0000000..12e8ec9 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/bar/horizontal-bar.ts @@ -0,0 +1,501 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import type { Bar, BarOptions } from '@antv/g2plot/esm/plots/bar' +import { + configAxisLabelLengthLimit, + configPlotTooltipEvent, + getPadding, + getTooltipContainer, + setGradientColor, + TOOLTIP_TPL +} from '@/data-visualization/chart/components/js/panel/common/common_antv' +import { cloneDeep } from 'lodash-es' +import { + flow, + hexColorToRGBA, + parseJson, + setUpStackSeriesColor +} from '@/data-visualization/chart/components/js/util' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { + BAR_AXIS_TYPE, + BAR_EDITOR_PROPERTY, + BAR_EDITOR_PROPERTY_INNER +} from '@/data-visualization/chart/components/js/panel/charts/bar/common' +import type { Datum } from '@antv/g2plot/esm/types/common' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { DEFAULT_BASIC_STYLE, DEFAULT_LABEL } from '@/data-visualization/chart/components/editor/util/chart' +import { Group } from '@antv/g-canvas' + +const { t } = useI18n() +const DEFAULT_DATA = [] + +/** + * 条形图 + */ +export class HorizontalBar extends G2PlotChartView { + axisConfig = { + ...this['axisConfig'], + xAxis: { + name: `${t('chart.drag_block_type_axis')} / ${t('chart.dimension')}`, + type: 'd' + }, + yAxis: { + name: `${t('chart.drag_block_value_axis')} / ${t('chart.quota')}`, + type: 'q' + } + } + properties = BAR_EDITOR_PROPERTY + propertyInner = { + ...BAR_EDITOR_PROPERTY_INNER, + 'basic-style-selector': [...BAR_EDITOR_PROPERTY_INNER['basic-style-selector'], 'seriesColor'], + 'label-selector': ['hPosition', 'seriesLabelFormatter'], + 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'], + 'x-axis-selector': [ + ...BAR_EDITOR_PROPERTY_INNER['x-axis-selector'], + 'axisLabelFormatter', + 'axisValue' + ], + 'y-axis-selector': [ + 'name', + 'color', + 'fontSize', + 'axisLine', + 'splitLine', + 'axisForm', + 'axisLabel', + 'position', + 'showLengthLimit' + ] + } + axis: AxisType[] = [...BAR_AXIS_TYPE] + protected baseOptions: BarOptions = { + data: [], + xField: 'value', + yField: 'field', + seriesField: 'category', + isGroup: true + } + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (!chart.data?.data?.length) { + return + } + // data + const data = cloneDeep(chart.data.data) + + // options + const initOptions: BarOptions = { + ...this.baseOptions, + appendPadding: getPadding(chart), + data + } + + const options = this.setupOptions(chart, initOptions) + + const { Bar } = await import('@antv/g2plot/esm/plots/bar') + // 开始渲染 + const newChart = new Bar(container, options) + + newChart.on('interval:click', action) + configPlotTooltipEvent(chart, newChart) + configAxisLabelLengthLimit(chart, newChart) + return newChart + } + + protected configXAxis(chart: Chart, options: BarOptions): BarOptions { + const tmpOptions = super.configXAxis(chart, options) + if (!tmpOptions.xAxis) { + return tmpOptions + } + const xAxis = parseJson(chart.customStyle).xAxis + const axisValue = xAxis.axisValue + if (tmpOptions.xAxis.label) { + tmpOptions.xAxis.label.formatter = value => { + return valueFormatter(value, xAxis.axisLabelFormatter) + } + } + if (tmpOptions.xAxis.position === 'top') { + tmpOptions.xAxis.position = 'left' + } + if (tmpOptions.xAxis.position === 'bottom') { + tmpOptions.xAxis.position = 'right' + } + if (!axisValue?.auto) { + const axis = { + xAxis: { + ...tmpOptions.xAxis, + min: axisValue.min, + max: axisValue.max, + minLimit: axisValue.min, + maxLimit: axisValue.max, + tickCount: axisValue.splitCount + } + } + return { ...tmpOptions, ...axis } + } + return tmpOptions + } + + protected configTooltip(chart: Chart, options: BarOptions): BarOptions { + return super.configMultiSeriesTooltip(chart, options) + } + + protected configBasicStyle(chart: Chart, options: BarOptions): BarOptions { + const basicStyle = parseJson(chart.customAttr).basicStyle + if (basicStyle.gradient) { + let color = basicStyle.colors + color = color.map(ele => { + const tmp = hexColorToRGBA(ele, basicStyle.alpha) + return setGradientColor(tmp, true) + }) + options = { + ...options, + color + } + } + if (basicStyle.radiusColumnBar === 'roundAngle') { + const barStyle = { + radius: [ + basicStyle.columnBarRightAngleRadius, + basicStyle.columnBarRightAngleRadius, + basicStyle.columnBarRightAngleRadius, + basicStyle.columnBarRightAngleRadius + ] + } + options = { + ...options, + barStyle + } + } + + let barWidthRatio + const _v = basicStyle.columnWidthRatio ?? DEFAULT_BASIC_STYLE.columnWidthRatio + if (_v >= 1 && _v <= 100) { + barWidthRatio = _v / 100.0 + } else if (_v < 1) { + barWidthRatio = 1 / 100.0 + } else if (_v > 100) { + barWidthRatio = 1 + } + if (barWidthRatio) { + options.barWidthRatio = barWidthRatio + } + + return options + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + const { customAttr, senior } = chart + const { label } = customAttr + if (!['left', 'middle', 'right'].includes(label.position)) { + label.position = 'middle' + } + senior.functionCfg.emptyDataStrategy = 'ignoreData' + return chart + } + + protected configLabel(chart: Chart, options: BarOptions): BarOptions { + const tmpOptions = super.configLabel(chart, options) + if (!tmpOptions.label) { + return { + ...tmpOptions, + label: false + } + } + const labelAttr = parseJson(chart.customAttr).label + const formatterMap = labelAttr.seriesLabelFormatter?.reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) + // 默认灰色 + tmpOptions.label.style.fill = DEFAULT_LABEL.color + const label = { + fields: [], + ...tmpOptions.label, + formatter: (data: Datum) => { + if (!labelAttr.seriesLabelFormatter?.length) { + return data.value + } + const labelCfg = formatterMap?.[data.quotaList[0].id] as SeriesFormatter + if (!labelCfg) { + return data.value + } + if (!labelCfg.show) { + return + } + const value = valueFormatter(data.value, labelCfg.formatterCfg) + const group = new Group({}) + group.addShape({ + type: 'text', + attrs: { + x: 0, + y: 0, + text: value, + textAlign: 'start', + textBaseline: 'top', + fontSize: labelCfg.fontSize, + fontFamily: chart.fontFamily, + fill: labelCfg.color + } + }) + return group + } + } + return { + ...tmpOptions, + label + } + } + + protected configYAxis(chart: Chart, options: BarOptions): BarOptions { + const tmpOptions = super.configYAxis(chart, options) + if (!tmpOptions.yAxis) { + return tmpOptions + } + if (tmpOptions.yAxis.position === 'left') { + tmpOptions.yAxis.position = 'bottom' + } + if (tmpOptions.yAxis.position === 'right') { + tmpOptions.yAxis.position = 'top' + } + return tmpOptions + } + + protected setupOptions(chart: Chart, options: BarOptions): BarOptions { + return flow( + this.addConditionsStyleColorToData, + this.configTheme, + this.configEmptyDataStrategy, + this.configColor, + this.configBasicStyle, + this.configLabel, + this.configTooltip, + this.configLegend, + this.configXAxis, + this.configYAxis, + this.configSlider, + this.configAnalyseHorizontal, + this.configBarConditions + )(chart, options, {}, this) + } + + constructor(name = 'bar-horizontal') { + super(name, DEFAULT_DATA) + } +} + +/** + * 堆叠条形图 + */ +export class HorizontalStackBar extends HorizontalBar { + properties = BAR_EDITOR_PROPERTY.filter(ele => ele !== 'threshold') + axisConfig = { + ...this['axisConfig'], + extStack: { + name: `${t('chart.stack_item')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1, + allowEmpty: true + } + } + propertyInner = { + ...this['propertyInner'], + 'label-selector': ['color', 'fontSize', 'hPosition', 'labelFormatter'], + 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter', 'show'] + } + protected configLabel(chart: Chart, options: BarOptions): BarOptions { + const baseOptions = super.configLabel(chart, options) + if (!baseOptions.label) { + return baseOptions + } + const { label: labelAttr } = parseJson(chart.customAttr) + baseOptions.label.style.fill = labelAttr.color + const label = { + ...baseOptions.label, + formatter: function (param: Datum) { + return valueFormatter(param.value, labelAttr.labelFormatter) + } + } + return { + ...baseOptions, + label + } + } + + protected configTooltip(chart: Chart, options: BarOptions): BarOptions { + const tooltipAttr = parseJson(chart.customAttr).tooltip + if (!tooltipAttr.show) { + return { + ...options, + tooltip: false + } + } + const tooltip = { + formatter: (param: Datum) => { + const obj = { name: param.category, value: param.value } + const res = valueFormatter(param.value, tooltipAttr.tooltipFormatter) + obj.value = res ?? '' + return obj + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + return { + ...options, + tooltip + } + } + protected configColor(chart: Chart, options: BarOptions): BarOptions { + return this.configStackColor(chart, options) + } + public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + return setUpStackSeriesColor(chart, data) + } + + protected configData(chart: Chart, options: BarOptions): BarOptions { + const { xAxis, extStack, yAxis } = chart + const mainSort = xAxis.some(axis => axis.sort !== 'none') + const subSort = extStack.some(axis => axis.sort !== 'none') + if (mainSort || subSort) { + return options + } + const quotaSort = yAxis?.[0]?.sort !== 'none' + if (!quotaSort || !extStack.length || !yAxis.length) { + return options + } + const { data } = options + const mainAxisValueMap = data.reduce((p, n) => { + p[n.field] = p[n.field] ? p[n.field] + n.value : n.value || 0 + return p + }, {}) + const sort = yAxis[0].sort + data.sort((p, n) => { + if (sort === 'asc') { + return mainAxisValueMap[p.field] - mainAxisValueMap[n.field] + } else { + return mainAxisValueMap[n.field] - mainAxisValueMap[p.field] + } + }) + return options + } + + protected setupOptions(chart: Chart, options: BarOptions): BarOptions { + return flow( + this.configTheme, + this.configEmptyDataStrategy, + this.configData, + this.configColor, + this.configBasicStyle, + this.configLabel, + this.configTooltip, + this.configLegend, + this.configXAxis, + this.configYAxis, + this.configSlider, + this.configAnalyseHorizontal + )(chart, options, {}, this) + } + + constructor(name = 'bar-stack-horizontal') { + super(name) + this.baseOptions = { + ...this.baseOptions, + isGroup: false, + isStack: true, + meta: { + category: { + type: 'cat' + } + } + } + this.axis = [...this.axis, 'extStack'] + } +} + +/** + * 百分比堆叠条形图 + */ +export class HorizontalPercentageStackBar extends HorizontalStackBar { + propertyInner = { + ...this['propertyInner'], + 'label-selector': ['color', 'fontSize', 'hPosition', 'reserveDecimalCount'], + 'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'show'] + } + protected configLabel(chart: Chart, options: BarOptions): BarOptions { + const baseOptions = super.configLabel(chart, options) + if (!baseOptions.label) { + return baseOptions + } + const { customAttr } = chart + const l = parseJson(customAttr).label + const label = { + ...baseOptions.label, + formatter: function (param: Datum) { + if (!param.value) { + return '0%' + } + return (Math.round(param.value * 10000) / 100).toFixed(l.reserveDecimalCount) + '%' + } + } + return { + ...baseOptions, + label + } + } + + protected configTooltip(chart: Chart, options: BarOptions): BarOptions { + const tooltipAttr = parseJson(chart.customAttr).tooltip + if (!tooltipAttr.show) { + return { + ...options, + tooltip: { + showContent: false + } + } + } + const { customAttr } = chart + const l = parseJson(customAttr).label + const tooltip = { + formatter: (param: Datum) => { + const obj = { name: param.category, value: param.value } + obj.value = (Math.round(param.value * 10000) / 100).toFixed(l.reserveDecimalCount) + '%' + return obj + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + return { + ...options, + tooltip + } + } + protected setupOptions(chart: Chart, options: BarOptions): BarOptions { + return flow( + this.configTheme, + this.configEmptyDataStrategy, + this.configColor, + this.configBasicStyle, + this.configLabel, + this.configTooltip, + this.configLegend, + this.configXAxis, + this.configYAxis, + this.configSlider, + this.configAnalyseHorizontal + )(chart, options, {}, this) + } + + constructor() { + super('percentage-bar-stack-horizontal') + this.baseOptions = { + ...this.baseOptions, + isPercent: true + } + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/bar/progress-bar.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/bar/progress-bar.ts new file mode 100644 index 0000000..b9399cf --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/bar/progress-bar.ts @@ -0,0 +1,389 @@ +import { G2PlotChartView, G2PlotDrawOptions } from '../../types/impl/g2plot' +import { flow, hexColorToRGBA, parseJson } from '../../../util' +import { + configAxisLabelLengthLimit, + configPlotTooltipEvent, + getTooltipContainer, + getTooltipItemConditionColor, + setGradientColor, + TOOLTIP_TPL +} from '../../common/common_antv' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import type { Bar as G2Progress, BarOptions } from '@antv/g2plot/esm/plots/bar' +import { + BAR_AXIS_TYPE, + BAR_EDITOR_PROPERTY_INNER +} from '@/data-visualization/chart/components/js/panel/charts/bar/common' +import { cloneDeep, defaultTo } from 'lodash-es' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { Options } from '@antv/g2plot/esm' +import { DEFAULT_BASIC_STYLE } from '@/data-visualization/chart/components/editor/util/chart' + +const { t } = useI18n() + +export class ProgressBar extends G2PlotChartView { + axisConfig = { + xAxis: { + name: `${t('chart.form_type')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1 + }, + yAxis: { + name: `${t('chart.progress_target')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + }, + yAxisExt: { + name: `${t('chart.progress_current')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + } + } + properties: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'label-selector', + 'tooltip-selector', + 'y-axis-selector', + 'title-selector', + 'function-cfg', + 'jump-set', + 'linkage', + 'threshold' + ] + propertyInner = { + ...BAR_EDITOR_PROPERTY_INNER, + 'legend-selector': null, + 'background-overall-component': ['all'], + 'border-style': ['all'], + 'basic-style-selector': ['colors', 'alpha', 'gradient', 'radiusColumnBar', 'columnWidthRatio'], + 'label-selector': ['hPosition', 'color', 'fontSize', 'showQuota', 'showProportion'], + 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter', 'show'], + 'y-axis-selector': [ + 'name', + 'color', + 'fontSize', + 'axisForm', + 'axisLabel', + 'position', + 'showLengthLimit' + ], + 'function-cfg': ['emptyDataStrategy'], + threshold: ['lineThreshold'] + } + axis: AxisType[] = [...BAR_AXIS_TYPE, 'yAxisExt'] + protected baseOptions: BarOptions = { + data: [], + xField: 'progress', + yField: 'title', + seriesField: 'type', + isGroup: false, + isPercent: true, + isStack: true, + xAxis: false, + appendPadding: [0, 0, 10, 0] + } + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (!chart.data?.data?.length) { + return + } + const getCompletionRate = (target: number, current: number) => { + if (target === 0) { + return 100 + } + // 目标为正 当前为负 + if (target > 0 && current < 0) { + return 0 + } + // 目标为负 当前为正 正向 + if ((target < 0 && current > 0) || (target < 0 && current === 0)) { + return (2 - current / target) * 100 + } + // 目标与当前都为正 + if (target > 0 && current > 0) { + return (current / target) * 100 + } + // 目标与当前都为负 负向小于0为0 + if (target < 0 && current < 0) { + const completionRate = (2 - current / target) * 100 + return Number(Math.max(completionRate, 0).toFixed(2)) + } + return 0 + } + // data + const sourceData: Array = cloneDeep(chart.data.data) + const data1 = defaultTo(sourceData[0]?.data, []) + const data2 = defaultTo(sourceData[1]?.data, []) + const currentData = data2.map(item => { + const progress = getCompletionRate(data1.find(i => i.field === item.field)?.value, item.value) + return { + ...item, + type: 'current', + title: item.field, + id: item.quotaList[0].id, + originalValue: item.value, + originalProgress: progress, + progress: progress >= 100 ? 100 : progress + } + }) + const targetData = data1.map(item => { + const progress = 100 - currentData.find(i => i.title === item.field)?.progress + return { + ...item, + type: 'target', + title: item.field, + id: item.quotaList[0].id, + originalValue: item.value, + progress: progress + } + }) + // options + const initOptions: BarOptions = { + ...this.baseOptions, + data: currentData.concat(targetData).flat() + } + const options = this.setupOptions(chart, initOptions) + + const { Bar: G2Progress } = await import('@antv/g2plot/esm/plots/bar') + // 开始渲染 + const newChart = new G2Progress(container, options) + + newChart.on('interval:click', action) + configPlotTooltipEvent(chart, newChart) + configAxisLabelLengthLimit(chart, newChart) + return newChart + } + protected configBasicStyle(chart: Chart, options: BarOptions): BarOptions { + const basicStyle = parseJson(chart.customAttr).basicStyle + let color1 = basicStyle.colors?.map((ele, index) => { + if (index === 1) { + return hexColorToRGBA(ele, basicStyle.alpha > 10 ? 10 : basicStyle.alpha) + } else { + return hexColorToRGBA(ele, basicStyle.alpha) + } + }) + if (basicStyle.gradient) { + color1 = color1.map((ele, _index) => { + return setGradientColor(ele, true, 0) + }) + } + options = { + ...options, + color: datum => { + if (datum.type === 'target') { + return 'rgba(0, 0, 0, 0)' + } + return color1[0] + }, + barBackground: { + style: { + fill: color1[1] + } + } + } + if (basicStyle.radiusColumnBar === 'roundAngle') { + const barStyle = { + radius: [ + basicStyle.columnBarRightAngleRadius, + basicStyle.columnBarRightAngleRadius, + basicStyle.columnBarRightAngleRadius, + basicStyle.columnBarRightAngleRadius + ] + } + options = { + ...options, + barStyle + } + } + + let barWidthRatio + const _v = basicStyle.columnWidthRatio ?? DEFAULT_BASIC_STYLE.columnWidthRatio + if (_v >= 1 && _v <= 100) { + barWidthRatio = _v / 100.0 + } else if (_v < 1) { + barWidthRatio = 1 / 100.0 + } else if (_v > 100) { + barWidthRatio = 1 + } + if (barWidthRatio) { + options.barWidthRatio = barWidthRatio + } + + return options + } + protected configTooltip(chart: Chart, options: BarOptions): BarOptions { + const tooltipAttr = parseJson(chart.customAttr).tooltip + if (!tooltipAttr.show) { + return { + ...options, + tooltip: { + showContent: false + } + } + } + const yAxis = cloneDeep(chart.yAxis)[0] + const yAxisExt = cloneDeep(chart.yAxisExt)[0] + return { + ...options, + tooltip: { + showContent: true, + domStyles: { + 'g2-tooltip-marker': null + }, + customItems(originalItems) { + const result = [] + originalItems.forEach(item => { + if (item.data) { + const value = valueFormatter(item.data.value, tooltipAttr.tooltipFormatter) + if (item.data.id === yAxis.id) { + result.push({ + ...item, + marker: false, + name: yAxis.chartShowName ? yAxis.chartShowName : yAxis.name, + value: value + }) + } + if (item.data.id === yAxisExt.id) { + result.push({ + ...item, + marker: false, + name: yAxisExt.chartShowName ? yAxisExt.chartShowName : yAxisExt.name, + value: value + }) + } + } + }) + return result.length == 0 ? originalItems : result + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + } + } + + protected configLabel(chart: Chart, options: BarOptions): BarOptions { + const baseOptions = super.configLabel(chart, options) + if (!baseOptions.label) return baseOptions + if (!baseOptions.label.layout?.[0]) { + baseOptions.label.layout = [{ type: 'limit-in-canvas' }] + } + const { label: labelAttr } = parseJson(chart.customAttr) + baseOptions.label.style.fill = labelAttr.color + const label = { + ...baseOptions.label, + content: item => { + if (item.type === 'target') return '' + let text = '' + if (labelAttr.showQuota) text += valueFormatter(item.value, labelAttr.quotaLabelFormatter) + if (labelAttr.showProportion) { + let proportion = item.originalProgress.toFixed(labelAttr.reserveDecimalCount) + '%' + if (labelAttr.showQuota) { + proportion = ` (${proportion}) ` + } + text += proportion + } + return text + } + } + if (label.position === 'top') label.position = 'right' + return { ...baseOptions, label } + } + protected configYAxis(chart: Chart, options: BarOptions): BarOptions { + const baseOption = super.configYAxis(chart, options) + if (!baseOption.yAxis) { + return baseOption + } + if (baseOption.yAxis.position === 'left') { + baseOption.yAxis.position = 'bottom' + } + if (baseOption.yAxis.position === 'right') { + baseOption.yAxis.position = 'top' + } + return baseOption + } + setupDefaultOptions(chart: ChartObj): ChartObj { + chart.customStyle.yAxis = { + ...chart.customStyle.yAxis, + position: 'left', + axisLine: { + show: false, + lineStyle: chart.customStyle.yAxis.axisLine.lineStyle + }, + splitLine: { + show: false, + lineStyle: chart.customStyle.yAxis.axisLine.lineStyle + } + } + chart.customStyle.legend.show = false + chart.customAttr.label.show = true + chart.customAttr.label.position = 'right' + chart.customAttr.label.showQuota = false + chart.customAttr.label.showProportion = true + return chart + } + + protected configLegend(chart: Chart, options: BarOptions): BarOptions { + const o = super.configLegend(chart, options) + return { + ...o, + legend: false + } + } + + protected configEmptyDataStrategy(chart: Chart, options: BarOptions): BarOptions { + const { data } = options as unknown as Options + if (!data?.length) { + return options + } + const strategy = parseJson(chart.senior).functionCfg.emptyDataStrategy + if (strategy === 'ignoreData') { + const emptyFields = data.filter(obj => obj['value'] === null).map(obj => obj['field']) + return { + ...options, + data: data.filter(obj => { + if (emptyFields.includes(obj['field'])) { + return false + } + return true + }) + } + } + if (strategy === 'breakLine') { + data.forEach(obj => { + if (obj['value'] === null) { + obj['value'] = null + } + }) + } + if (strategy === 'setZero') { + data.forEach(obj => { + if (obj['value'] === null) { + obj['value'] = 0 + } + }) + } + return options + } + + protected setupOptions(chart: Chart, options: BarOptions): BarOptions { + return flow( + this.addConditionsStyleColorToData, + this.configTheme, + this.configBasicStyle, + this.configLabel, + this.configTooltip, + this.configLegend, + this.configYAxis, + this.configEmptyDataStrategy, + this.configBarConditions + )(chart, options) + } + + constructor() { + super('progress-bar', []) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/bar/range-bar.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/bar/range-bar.ts new file mode 100644 index 0000000..5db5326 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/bar/range-bar.ts @@ -0,0 +1,434 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import type { Bar, BarOptions } from '@antv/g2plot/esm/plots/bar' +import { + configAxisLabelLengthLimit, + configPlotTooltipEvent, + getPadding, + getTooltipContainer, + setGradientColor, + TOOLTIP_TPL +} from '@/data-visualization/chart/components/js/panel/common/common_antv' +import { cloneDeep, find } from 'lodash-es' +import { flow, hexColorToRGBA, parseJson } from '@/data-visualization/chart/components/js/util' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { + BAR_AXIS_TYPE, + BAR_RANGE_EDITOR_PROPERTY, + BAR_EDITOR_PROPERTY_INNER +} from '@/data-visualization/chart/components/js/panel/charts/bar/common' +import { Datum } from '@antv/g2plot/esm/types/common' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { DEFAULT_BASIC_STYLE } from '@/data-visualization/chart/components/editor/util/chart' + +const { t } = useI18n() +const DEFAULT_DATA = [] + +/** + * 区间条形图 + */ +export class RangeBar extends G2PlotChartView { + axisConfig = { + xAxis: { + name: `${t('chart.drag_block_type_axis')} / ${t('chart.dimension')}`, + type: 'd' + }, + yAxis: { + name: `${t('chart.drag_block_value_start')} / ${t('chart.time_dimension_or_quota')}`, + limit: 1, + type: 'q' + }, + yAxisExt: { + name: `${t('chart.drag_block_value_end')} / ${t('chart.time_dimension_or_quota')}`, + limit: 1, + type: 'q' + } + } + properties = BAR_RANGE_EDITOR_PROPERTY.filter(p => p !== 'threshold') + propertyInner = { + ...BAR_EDITOR_PROPERTY_INNER, + 'label-selector': ['hPosition', 'color', 'fontSize', 'labelFormatter', 'showGap'], + 'tooltip-selector': [ + 'fontSize', + 'color', + 'backgroundColor', + 'tooltipFormatter', + 'showGap', + 'show' + ], + 'x-axis-selector': [...BAR_EDITOR_PROPERTY_INNER['x-axis-selector'], 'axisLabelFormatter'], + 'y-axis-selector': [ + 'name', + 'color', + 'fontSize', + 'axisLine', + 'splitLine', + 'axisForm', + 'axisLabel', + 'position', + 'showLengthLimit' + ] + } + axis: AxisType[] = [...BAR_AXIS_TYPE, 'yAxisExt'] + protected baseOptions: BarOptions = { + data: [], + xField: 'values', + yField: 'field', + colorField: 'category', + isGroup: true + } + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (!chart.data?.data?.length) { + return + } + // data + const data: Array = cloneDeep(chart.data.data) + + data.forEach(d => { + d.tempId = (Math.random() * 10000000).toString() + }) + + const ifAggregate = !!chart.aggregate + + const isDate = !!chart.data.isDate + + const axis = chart.yAxis ?? chart.yAxisExt ?? [] + let dateFormat: string + const dateSplit = axis[0]?.datePattern === 'date_split' ? '/' : '-' + switch (axis[0]?.dateStyle) { + case 'y': + dateFormat = 'YYYY' + break + case 'y_M': + dateFormat = 'YYYY' + dateSplit + 'MM' + break + case 'y_M_d': + dateFormat = 'YYYY' + dateSplit + 'MM' + dateSplit + 'DD' + break + // case 'H_m_s': + // dateFormat = 'HH:mm:ss' + // break + case 'y_M_d_H': + dateFormat = 'YYYY' + dateSplit + 'MM' + dateSplit + 'DD' + ' HH' + break + case 'y_M_d_H_m': + dateFormat = 'YYYY' + dateSplit + 'MM' + dateSplit + 'DD' + ' HH:mm' + break + case 'y_M_d_H_m_s': + dateFormat = 'YYYY' + dateSplit + 'MM' + dateSplit + 'DD' + ' HH:mm:ss' + break + default: + dateFormat = 'YYYY-MM-dd HH:mm:ss' + } + + const minTime = chart.data.minTime + const maxTime = chart.data.maxTime + + const minNumber = chart.data.min + const maxNumber = chart.data.max + + // options + const initOptions: BarOptions = { + ...this.baseOptions, + appendPadding: getPadding(chart), + data, + seriesField: isDate ? (ifAggregate ? 'category' : undefined) : 'category', + isGroup: isDate ? !ifAggregate : false, + isStack: isDate ? !ifAggregate : false, + meta: isDate + ? { + values: { + type: 'time', + min: minTime, + max: maxTime, + mask: dateFormat + }, + tempId: { + key: true + } + } + : { + values: { + min: minNumber, + max: maxNumber, + mask: dateFormat + }, + tempId: { + key: true + } + } + } + + const options = this.setupOptions(chart, initOptions) + + const { Bar: BarClass } = await import('@antv/g2plot/esm/plots/bar') + // 开始渲染 + const newChart = new BarClass(container, options) + + newChart.on('interval:click', action) + configPlotTooltipEvent(chart, newChart) + configAxisLabelLengthLimit(chart, newChart) + return newChart + } + + protected configXAxis(chart: Chart, options: BarOptions): BarOptions { + const tmpOptions = super.configXAxis(chart, options) + if (!tmpOptions.xAxis) { + return tmpOptions + } + const xAxis = parseJson(chart.customStyle).xAxis + const axisValue = xAxis.axisValue + const isDate = !!chart.data.isDate + if (tmpOptions.xAxis.label) { + tmpOptions.xAxis.label.formatter = value => { + if (isDate) { + return value + } + return valueFormatter(value, xAxis.axisLabelFormatter) + } + } + if (tmpOptions.xAxis.position === 'top') { + tmpOptions.xAxis.position = 'left' + } + if (tmpOptions.xAxis.position === 'bottom') { + tmpOptions.xAxis.position = 'right' + } + if (!axisValue?.auto) { + const axis = { + xAxis: { + ...tmpOptions.xAxis, + min: axisValue.min, + max: axisValue.max, + minLimit: axisValue.min, + maxLimit: axisValue.max, + tickCount: axisValue.splitCount + } + } + return { ...tmpOptions, ...axis } + } + return tmpOptions + } + + protected configTooltip(chart: Chart, options: BarOptions): BarOptions { + const isDate = !!chart.data.isDate + let tooltip + let customAttr: DeepPartial + if (chart.customAttr) { + customAttr = parseJson(chart.customAttr) + // tooltip + if (customAttr.tooltip) { + const t = JSON.parse(JSON.stringify(customAttr.tooltip)) + if (t.show) { + tooltip = { + fields: ['values', 'field', 'gap'], + formatter: function (param: Datum) { + let res + if (isDate) { + res = param.values[0] + ' ~ ' + param.values[1] + if (t.showGap) { + res = res + ' (' + param.gap + ')' + } + } else { + res = + valueFormatter(param.values[0], t.tooltipFormatter) + + ' ~ ' + + valueFormatter(param.values[1], t.tooltipFormatter) + if (t.showGap) { + res = res + ' (' + valueFormatter(param.gap, t.tooltipFormatter) + ')' + } + } + return { value: res, values: param.values, name: param.field } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + } else { + tooltip = false + } + } + } + return { ...options, tooltip } + } + + protected configBasicStyle(chart: Chart, options: BarOptions): BarOptions { + const isDate = !!chart.data.isDate + const ifAggregate = !!chart.aggregate + const basicStyle = parseJson(chart.customAttr).basicStyle + + if (isDate && !ifAggregate) { + const customColors = [] + const groups = [] + for (let i = 0; i < chart.data.data.length; i++) { + const name = chart.data.data[i].field + if (groups.indexOf(name) < 0) { + groups.push(name) + } + } + for (let i = 0; i < groups.length; i++) { + const s = groups[i] + customColors.push({ + name: s, + color: basicStyle.colors[i % basicStyle.colors.length], + isCustom: false + }) + } + const color = obj => { + const colorObj = find(customColors, o => { + return o.name === obj.field + }) + if (colorObj === undefined) { + return undefined + } + const color = hexColorToRGBA(colorObj.color, basicStyle.alpha) + if (basicStyle.gradient) { + return setGradientColor(color, true) + } else { + return color + } + } + + options = { + ...options, + color + } + } else { + if (basicStyle.gradient) { + let color = basicStyle.colors + color = color.map(ele => { + const tmp = hexColorToRGBA(ele, basicStyle.alpha) + return setGradientColor(tmp, true) + }) + options = { + ...options, + color + } + } + } + if (basicStyle.radiusColumnBar === 'roundAngle') { + const barStyle = { + radius: [ + basicStyle.columnBarRightAngleRadius, + basicStyle.columnBarRightAngleRadius, + basicStyle.columnBarRightAngleRadius, + basicStyle.columnBarRightAngleRadius + ] + } + options = { + ...options, + barStyle + } + } + let barWidthRatio + const _v = basicStyle.columnWidthRatio ?? DEFAULT_BASIC_STYLE.columnWidthRatio + if (_v >= 1 && _v <= 100) { + barWidthRatio = _v / 100.0 + } else if (_v < 1) { + barWidthRatio = 1 / 100.0 + } else if (_v > 100) { + barWidthRatio = 1 + } + if (barWidthRatio) { + options.barWidthRatio = barWidthRatio + } + + return options + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + const { customAttr, senior } = chart + const { label } = customAttr + if (!['left', 'middle', 'right'].includes(label.position)) { + label.position = 'middle' + } + senior.functionCfg.emptyDataStrategy = 'ignoreData' + return chart + } + + protected configLabel(chart: Chart, options: BarOptions): BarOptions { + const isDate = !!chart.data.isDate + const ifAggregate = !!chart.aggregate + + const tmpOptions = super.configLabel(chart, options) + if (!tmpOptions.label) { + return { + ...tmpOptions, + label: false + } + } + const labelAttr = parseJson(chart.customAttr).label + + if (isDate && !ifAggregate) { + if (!tmpOptions.label.layout) { + tmpOptions.label.layout = [] + } + tmpOptions.label.layout.push({ type: 'interval-hide-overlap' }) + tmpOptions.label.layout.push({ type: 'limit-in-plot', cfg: { action: 'hide' } }) + } + + const label = { + fields: [], + ...tmpOptions.label, + formatter: (param: Datum) => { + let res + if (isDate) { + if (labelAttr.showGap) { + res = param.gap + } else { + res = param.values[0] + ' ~ ' + param.values[1] + } + } else { + if (labelAttr.showGap) { + res = valueFormatter(param.gap, labelAttr.labelFormatter) + } else { + res = + valueFormatter(param.values[0], labelAttr.labelFormatter) + + ' ~ ' + + valueFormatter(param.values[1], labelAttr.labelFormatter) + } + } + return res + } + } + return { + ...tmpOptions, + label + } + } + + protected configYAxis(chart: Chart, options: BarOptions): BarOptions { + const tmpOptions = super.configYAxis(chart, options) + if (!tmpOptions.yAxis) { + return tmpOptions + } + if (tmpOptions.yAxis.position === 'left') { + tmpOptions.yAxis.position = 'bottom' + } + if (tmpOptions.yAxis.position === 'right') { + tmpOptions.yAxis.position = 'top' + } + return tmpOptions + } + + protected setupOptions(chart: Chart, options: BarOptions): BarOptions { + return flow( + this.configTheme, + this.configBasicStyle, + this.configLabel, + this.configTooltip, + this.configLegend, + this.configXAxis, + this.configYAxis, + this.configSlider, + this.configEmptyDataStrategy + )(chart, options) + } + + constructor(name = 'bar-range') { + super(name, DEFAULT_DATA) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/bar/waterfall.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/bar/waterfall.ts new file mode 100644 index 0000000..f827924 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/bar/waterfall.ts @@ -0,0 +1,337 @@ +import type { WaterfallOptions, Waterfall as G2Waterfall } from '@antv/g2plot/esm/plots/waterfall' +import { G2PlotChartView, G2PlotDrawOptions } from '../../types/impl/g2plot' +import { flow, hexColorToRGBA, parseJson } from '../../../util' +import { valueFormatter } from '../../../formatter' +import { + configAxisLabelLengthLimit, + configPlotTooltipEvent, + getPadding, + getTooltipContainer, + getTooltipItemConditionColor, + getTooltipSeriesTotalMap, + setGradientColor, + TOOLTIP_TPL +} from '../../common/common_antv' +import { isEmpty } from 'lodash-es' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { DEFAULT_BASIC_STYLE } from '@/data-visualization/chart/components/editor/util/chart' +const { t } = useI18n() + +/** + * 瀑布图 + */ +export class Waterfall extends G2PlotChartView { + properties: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'label-selector', + 'tooltip-selector', + 'title-selector', + 'legend-selector', + 'x-axis-selector', + 'y-axis-selector', + 'threshold' + ] + propertyInner: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + 'basic-style-selector': ['colors', 'alpha', 'gradient', 'columnWidthRatio'], + 'label-selector': ['fontSize', 'color', 'vPosition', 'labelFormatter'], + 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'legend-selector': ['icon', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition'], + 'x-axis-selector': [ + 'position', + 'name', + 'color', + 'fontSize', + 'axisLine', + 'splitLine', + 'axisForm', + 'axisLabel' + ], + 'y-axis-selector': [ + 'position', + 'name', + 'color', + 'fontSize', + 'axisValue', + 'splitLine', + 'axisForm', + 'axisLabel', + 'axisLabelFormatter', + 'showLengthLimit' + ], + threshold: ['lineThreshold'] + } + axis: AxisType[] = ['xAxis', 'yAxis', 'filter', 'drill', 'extLabel', 'extTooltip'] + axisConfig = { + xAxis: { + name: `${t('chart.drag_block_type_axis')} / ${t('chart.dimension')}`, + type: 'd' + }, + yAxis: { + name: `${t('chart.drag_block_value_axis')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + } + } + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (!chart.data?.data) { + return + } + const data = chart.data.data + const baseOptions = { + data, + xField: 'field', + yField: 'value', + seriesField: 'category', + appendPadding: getPadding(chart) + } + const options = this.setupOptions(chart, baseOptions) + const { Waterfall: G2Waterfall } = await import('@antv/g2plot/esm/plots/waterfall') + const newChart = new G2Waterfall(container, options) + newChart.on('interval:click', action) + configPlotTooltipEvent(chart, newChart) + configAxisLabelLengthLimit(chart, newChart) + return newChart + } + + protected configMeta(chart: Chart, options: WaterfallOptions): WaterfallOptions { + const yAxis = chart.yAxis + const meta: WaterfallOptions['meta'] = { + field: { + type: 'cat' + } + } + if (!yAxis?.length) { + return { + ...options, + meta + } + } + const f = yAxis[0] + const yAxisStyle = parseJson(chart.customStyle).yAxis + meta.value = { + alias: f.name, + formatter: (value: number) => { + return valueFormatter(value, yAxisStyle.axisLabelFormatter) + } + } + return { + ...options, + meta + } + } + + protected configBasicStyle(chart: Chart, options: WaterfallOptions): WaterfallOptions { + const customAttr = parseJson(chart.customAttr) + const { colors, gradient, alpha } = customAttr.basicStyle + const [risingColorRgba, fallingColorRgba, totalColorRgba] = colors + + let columnWidthRatio + const _v = customAttr.basicStyle.columnWidthRatio ?? DEFAULT_BASIC_STYLE.columnWidthRatio + if (_v >= 1 && _v <= 100) { + columnWidthRatio = _v / 100.0 + } else if (_v < 1) { + columnWidthRatio = 1 / 100.0 + } else if (_v > 100) { + columnWidthRatio = 1 + } + if (columnWidthRatio) { + options.columnWidthRatio = columnWidthRatio + } + + return { + ...options, + total: { + label: t('chart.waterfall_total'), + style: { + fill: setGradientColor(hexColorToRGBA(totalColorRgba, alpha), gradient, 270) + } + }, + risingFill: setGradientColor(hexColorToRGBA(risingColorRgba, alpha), gradient, 270), + fallingFill: setGradientColor(hexColorToRGBA(fallingColorRgba, alpha), gradient, 270) + } + } + + protected configYAxis(chart: Chart, options: WaterfallOptions): WaterfallOptions { + const tmpOptions = super.configYAxis(chart, options) + if (!tmpOptions.yAxis) { + return tmpOptions + } + const yAxis = parseJson(chart.customStyle).yAxis + const axisValue = yAxis.axisValue + if (!axisValue?.auto) { + const axis = { + yAxis: { + ...tmpOptions.yAxis, + min: axisValue.min, + max: axisValue.max, + minLimit: axisValue.min, + maxLimit: axisValue.max, + tickCount: axisValue.splitCount + } + } + return { ...tmpOptions, ...axis } + } + return tmpOptions + } + + protected configTooltip(chart: Chart, options: WaterfallOptions): WaterfallOptions { + const customAttr: DeepPartial = parseJson(chart.customAttr) + const tooltipAttr = customAttr.tooltip + const yAxis = chart.yAxis + if (!tooltipAttr.show) { + return { + ...options, + tooltip: false + } + } + const formatterMap = tooltipAttr.seriesTooltipFormatter + ?.filter(i => i.show) + .reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) as Record + const totalMap = getTooltipSeriesTotalMap(options.data) + const tooltip: WaterfallOptions['tooltip'] = { + showTitle: true, + customItems(originalItems) { + if (!tooltipAttr.seriesTooltipFormatter?.length) { + return originalItems + } + const result = [] + const head = originalItems[0] + // 汇总 + if (!head.data.quotaList) { + Object.keys(formatterMap).forEach(id => { + const formatter = formatterMap[id] + let tmpValue = totalMap[id] + let color = 'grey' + if (id === yAxis[0].id) { + tmpValue = head.data.value + color = head.color + } + const value = valueFormatter(tmpValue, formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + if (id === yAxis[0].id) { + result.unshift({ color, name, value }) + return + } + result.push({ color, name, value }) + }) + return result + } + originalItems + .filter(item => formatterMap[item.data.quotaList[0].id]) + .forEach(item => { + const formatter = formatterMap[item.data.quotaList[0].id] + const itemValue = (item.value + '').replace(/,/g, '') + formatter.formatterCfg.type = 'value' + const value = valueFormatter(parseFloat(itemValue), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ ...item, name, value }) + }) + head.data.dynamicTooltipValue?.forEach(item => { + const formatter = formatterMap[item.fieldId] + if (formatter) { + const itemValue = (item.value + '').replace(/,/g, '') + const value = valueFormatter(parseFloat(itemValue), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ color: 'grey', name, value }) + } + }) + result.forEach(item => { + const color = getTooltipItemConditionColor(item) + item.color = color + }) + return result + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + return { + ...options, + tooltip + } + } + + protected configLegend(chart: Chart, options: WaterfallOptions): WaterfallOptions { + const tmp = super.configLegend(chart, options) + if (!tmp.legend) { + return tmp + } + const customAttr = parseJson(chart.customAttr) + const { colors, gradient, alpha } = customAttr.basicStyle + const [risingColorRgba, fallingColorRgba, totalColorRgba] = colors + return { + ...tmp, + legend: { + ...tmp.legend, + items: [ + { + name: t('chart.increase'), + value: '', + marker: { + style: { + fill: setGradientColor(hexColorToRGBA(risingColorRgba, alpha), gradient, 270) + } + } + }, + { + name: t('chart.decrease'), + value: '', + marker: { + style: { + fill: setGradientColor(hexColorToRGBA(fallingColorRgba, alpha), gradient, 270) + } + } + }, + { + name: t('chart.waterfall_total'), + value: '', + marker: { + style: { + fill: setGradientColor(hexColorToRGBA(totalColorRgba, alpha), gradient, 270) + } + } + } + ] + } + } + } + + protected setupOptions(chart: Chart, options: WaterfallOptions): WaterfallOptions { + return flow( + this.addConditionsStyleColorToData, + this.configTheme, + this.configLegend, + this.configBasicStyle, + this.configLabel, + this.configTooltip, + this.configXAxis, + this.configYAxis, + this.configMeta, + this.configBarConditions + )(chart, options) + } + + constructor() { + super('waterfall', []) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/line/area.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/line/area.ts new file mode 100644 index 0000000..28a3fc6 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/line/area.ts @@ -0,0 +1,396 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import type { Area as G2Area, AreaOptions } from '@antv/g2plot/esm/plots/area' +import { + configPlotTooltipEvent, + getPadding, + getTooltipContainer, + setGradientColor, + TOOLTIP_TPL +} from '@/data-visualization/chart/components/js/panel/common/common_antv' +import { cloneDeep } from 'lodash-es' +import { + flow, + getLineConditions, + getLineLabelColorByCondition, + hexColorToRGBA, + parseJson, + setUpStackSeriesColor +} from '@/data-visualization/chart/components/js/util' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { + LINE_AXIS_TYPE, + LINE_EDITOR_PROPERTY, + LINE_EDITOR_PROPERTY_INNER +} from '@/data-visualization/chart/components/js/panel/charts/line/common' +import { Label } from '@antv/g2plot/lib/types/label' +import { Datum } from '@antv/g2plot/esm/types/common' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { DEFAULT_LABEL } from '@/data-visualization/chart/components/editor/util/chart' +import { clearExtremum, extremumEvt } from '@/data-visualization/chart/components/js/extremumUitl' +import { Group } from '@antv/g-canvas' + +const { t } = useI18n() +const DEFAULT_DATA = [] +export class Area extends G2PlotChartView { + properties = LINE_EDITOR_PROPERTY + propertyInner = { + ...LINE_EDITOR_PROPERTY_INNER, + 'basic-style-selector': [ + ...LINE_EDITOR_PROPERTY_INNER['basic-style-selector'], + 'gradient', + 'seriesColor' + ], + 'label-selector': ['seriesLabelVPosition', 'seriesLabelFormatter', 'showExtremum'], + 'tooltip-selector': [ + ...LINE_EDITOR_PROPERTY_INNER['tooltip-selector'], + 'seriesTooltipFormatter' + ] + } + axis: AxisType[] = [...LINE_AXIS_TYPE] + axisConfig = { + ...this['axisConfig'], + xAxis: { + name: `${t('chart.drag_block_type_axis')} / ${t('chart.dimension')}`, + type: 'd' + }, + yAxis: { + name: `${t('chart.drag_block_value_axis')} / ${t('chart.quota')}`, + type: 'q' + } + } + baseOptions: AreaOptions = { + data: [], + xField: 'field', + yField: 'value', + seriesField: 'category', + isStack: false, + interactions: [ + { + type: 'legend-active', + cfg: { + start: [{ trigger: 'legend-item:mouseenter', action: ['element-active:reset'] }], + end: [{ trigger: 'legend-item:mouseleave', action: ['element-active:reset'] }] + } + }, + { + type: 'legend-filter', + cfg: { + start: [ + { + trigger: 'legend-item:click', + action: [ + 'list-unchecked:toggle', + 'data-filter:filter', + 'element-active:reset', + 'element-highlight:reset' + ] + } + ] + } + }, + { + type: 'active-region', + cfg: { + start: [{ trigger: 'element:mousemove', action: 'active-region:show' }], + end: [{ trigger: 'element:mouseleave', action: 'active-region:hide' }] + } + } + ] + } + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (!chart.data?.data?.length) { + chart.container = container + clearExtremum(chart) + return + } + // data + const data = cloneDeep(chart.data.data) + + const initOptions: AreaOptions = { + ...this.baseOptions, + data, + appendPadding: getPadding(chart) + } + // options + const options = this.setupOptions(chart, initOptions) + const { Area: G2Area } = await import('@antv/g2plot/esm/plots/area') + // 开始渲染 + const newChart = new G2Area(container, options) + + newChart.on('point:click', action) + extremumEvt(newChart, chart, options, container) + configPlotTooltipEvent(chart, newChart) + return newChart + } + + protected configLabel(chart: Chart, options: AreaOptions): AreaOptions { + const tmpOptions = super.configLabel(chart, options) + if (!tmpOptions.label) { + return { + ...tmpOptions, + label: false + } + } + const { label: labelAttr, basicStyle } = parseJson(chart.customAttr) + const conditions = getLineConditions(chart) + const formatterMap = labelAttr.seriesLabelFormatter?.reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) + tmpOptions.label.style.fill = DEFAULT_LABEL.color + const label = { + fields: [], + ...tmpOptions.label, + layout: labelAttr.fullDisplay ? [{ type: 'limit-in-plot' }] : tmpOptions.label.layout, + formatter: (data: Datum, _point) => { + if (data.EXTREME) { + return '' + } + if (!labelAttr.seriesLabelFormatter?.length) { + return data.value + } + const labelCfg = formatterMap?.[data.quotaList[0].id] as SeriesFormatter + if (!labelCfg) { + return data.value + } + if (!labelCfg.show) { + return + } + const position = + labelCfg.position === 'top' + ? -2 - basicStyle.lineSymbolSize + : 10 + basicStyle.lineSymbolSize + const value = valueFormatter(data.value, labelCfg.formatterCfg) + const color = + getLineLabelColorByCondition(conditions, data.value, data.quotaList[0].id) || + labelCfg.color + const group = new Group({}) + group.addShape({ + type: 'text', + attrs: { + x: 0, + y: position, + text: value, + textAlign: 'start', + textBaseline: 'top', + fontSize: labelCfg.fontSize, + fontFamily: chart.fontFamily, + fill: color + } + }) + return group + } + } + return { + ...tmpOptions, + label + } + } + + protected configBasicStyle(chart: Chart, options: AreaOptions): AreaOptions { + // size + const customAttr: DeepPartial = parseJson(chart.customAttr) + const s: DeepPartial = JSON.parse(JSON.stringify(customAttr.basicStyle)) + const smooth = s.lineSmooth + const point = { + size: s.lineSymbolSize, + shape: s.lineSymbol + } + const line = { + style: { + lineWidth: s.lineWidth + } + } + // custom color + const { colors, alpha } = customAttr.basicStyle + const areaColors = [...colors, ...colors] + let areaStyle + if (customAttr.basicStyle.gradient) { + const colorMap = new Map() + const yAxis = parseJson(chart.customStyle).yAxis + const axisValue = yAxis.axisValue + const start = + !axisValue?.auto && axisValue.min && axisValue.max ? axisValue.min / axisValue.max : 0 + areaStyle = item => { + let ele: string + const key = `${item.field}-${item.category}` + if (colorMap.has(key)) { + ele = colorMap.get(key) + } else { + ele = areaColors.shift() + colorMap.set(key, ele) + } + if (ele) { + return { + fill: setGradientColor(hexColorToRGBA(ele, alpha), true, 270, start) + } + } + return { + fill: 'rgba(255,255,255,0)' + } + } + } + return { + ...options, + smooth, + line, + point, + areaStyle + } + } + + protected configYAxis(chart: Chart, options: AreaOptions): AreaOptions { + const tmpOptions = super.configYAxis(chart, options) + if (!tmpOptions.yAxis) { + return tmpOptions + } + const yAxis = parseJson(chart.customStyle).yAxis + if (tmpOptions.yAxis.label) { + tmpOptions.yAxis.label.formatter = value => { + return valueFormatter(value, yAxis.axisLabelFormatter) + } + } + const axisValue = yAxis.axisValue + if (!axisValue?.auto) { + const axis = { + yAxis: { + ...tmpOptions.yAxis, + min: axisValue.min, + max: axisValue.max, + minLimit: axisValue.min, + maxLimit: axisValue.max, + tickCount: axisValue.splitCount + } + } + return { ...tmpOptions, ...axis } + } + return tmpOptions + } + + protected configTooltip(chart: Chart, options: AreaOptions): AreaOptions { + return super.configMultiSeriesTooltip(chart, options) + } + + protected setupOptions(chart: Chart, options: AreaOptions): AreaOptions { + return flow( + this.configTheme, + this.configEmptyDataStrategy, + this.configColor, + this.configLabel, + this.configTooltip, + this.configBasicStyle, + this.configLegend, + this.configXAxis, + this.configYAxis, + this.configSlider, + this.configAnalyse, + this.configConditions + )(chart, options, {}, this) + } + + constructor(name = 'area') { + super(name, DEFAULT_DATA) + } +} + +/** + * 堆叠面积图 + */ +export class StackArea extends Area { + propertyInner = { + ...this['propertyInner'], + 'label-selector': ['vPosition', 'fontSize', 'color', 'labelFormatter'], + 'tooltip-selector': ['fontSize', 'color', 'tooltipFormatter', 'show'] + } + axisConfig = { + ...this['axisConfig'], + extStack: { + name: `${t('chart.stack_item')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1, + allowEmpty: true + } + } + protected configLabel(chart: Chart, options: AreaOptions): AreaOptions { + const { label: labelAttr, basicStyle } = parseJson(chart.customAttr) + if (!labelAttr?.show) { + return { + ...options, + label: false + } + } + const layout = [] + if (!labelAttr.fullDisplay) { + const tmpOptions = super.configLabel(chart, options) + layout.push(...tmpOptions.label.layout) + } else { + layout.push({ type: 'limit-in-plot' }) + } + const position = + labelAttr.position === 'top' ? -2 - basicStyle.lineSymbolSize : 8 + basicStyle.lineSymbolSize + const label: Label = { + position: labelAttr.position as any, + offsetY: position, + layout, + style: { + fill: labelAttr.color, + fontSize: labelAttr.fontSize + }, + formatter: function (param: Datum) { + return valueFormatter(param.value, labelAttr.labelFormatter) + } + } + return { ...options, label } + } + + public setupDefaultOptions(chart: ChartObj): ChartObj { + chart.senior.functionCfg.emptyDataStrategy = 'ignoreData' + return chart + } + + protected configColor(chart: Chart, options: AreaOptions): AreaOptions { + return this.configStackColor(chart, options) + } + public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + return setUpStackSeriesColor(chart, data) + } + protected configTooltip(chart: Chart, options: AreaOptions): AreaOptions { + const customAttr: DeepPartial = parseJson(chart.customAttr) + const tooltipAttr = customAttr.tooltip + if (!tooltipAttr.show) { + return { + ...options, + tooltip: false + } + } + const tooltip = { + formatter: function (param: Datum) { + const obj = { + name: param.category, + value: valueFormatter(param.value, tooltipAttr.tooltipFormatter) + } + return obj + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + return { ...options, tooltip } + } + + constructor() { + super('area-stack') + this.baseOptions = { + ...this.baseOptions, + isStack: true + } + delete this.propertyInner.threshold + this.properties = this.properties.filter(item => item !== 'threshold') + this.axis.push('extStack') + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/line/common.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/line/common.ts new file mode 100644 index 0000000..ffc4241 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/line/common.ts @@ -0,0 +1,74 @@ +export const LINE_EDITOR_PROPERTY: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'x-axis-selector', + 'y-axis-selector', + 'title-selector', + 'legend-selector', + 'label-selector', + 'tooltip-selector', + 'assist-line', + 'function-cfg', + 'jump-set', + 'linkage', + 'threshold' +] +export const LINE_EDITOR_PROPERTY_INNER: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + 'label-selector': ['fontSize', 'color'], + 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'show'], + 'basic-style-selector': [ + 'colors', + 'alpha', + 'lineWidth', + 'lineSymbol', + 'lineSymbolSize', + 'lineSmooth' + ], + 'x-axis-selector': [ + 'name', + 'color', + 'fontSize', + 'position', + 'axisLabel', + 'axisLine', + 'splitLine' + ], + 'y-axis-selector': [ + 'name', + 'color', + 'fontSize', + 'position', + 'axisLabel', + 'axisLine', + 'splitLine', + 'axisValue', + 'axisLabelFormatter' + ], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'legend-selector': ['icon', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition'], + 'function-cfg': ['slider', 'emptyDataStrategy'], + threshold: ['lineThreshold'] +} + +export const LINE_AXIS_TYPE: AxisType[] = [ + 'xAxis', + 'yAxis', + 'drill', + 'filter', + 'extLabel', + 'extTooltip' +] diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/line/line.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/line/line.ts new file mode 100644 index 0000000..d128c6c --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/line/line.ts @@ -0,0 +1,377 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import type { Line as G2Line, LineOptions } from '@antv/g2plot/esm/plots/line' +import { + configPlotTooltipEvent, + getPadding, + getTooltipContainer, + TOOLTIP_TPL +} from '../../common/common_antv' +import { + flow, + getLineConditions, + getLineLabelColorByCondition, + hexColorToRGBA, + parseJson, + setUpGroupSeriesColor +} from '@/data-visualization/chart/components/js/util' +import { cloneDeep, defaults, isEmpty } from 'lodash-es' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { + LINE_AXIS_TYPE, + LINE_EDITOR_PROPERTY, + LINE_EDITOR_PROPERTY_INNER +} from '@/data-visualization/chart/components/js/panel/charts/line/common' +import type { Datum } from '@antv/g2plot/esm/types/common' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { DEFAULT_LABEL, DEFAULT_LEGEND_STYLE } from '@/data-visualization/chart/components/editor/util/chart' +import { clearExtremum, extremumEvt } from '@/data-visualization/chart/components/js/extremumUitl' +import { Group } from '@antv/g-canvas' + +const { t } = useI18n() +const DEFAULT_DATA = [] +/** + * 折线图 + */ +export class Line extends G2PlotChartView { + properties = LINE_EDITOR_PROPERTY + propertyInner = { + ...LINE_EDITOR_PROPERTY_INNER, + 'basic-style-selector': [...LINE_EDITOR_PROPERTY_INNER['basic-style-selector'], 'seriesColor'], + 'label-selector': ['seriesLabelVPosition', 'seriesLabelFormatter', 'showExtremum'], + 'tooltip-selector': [ + ...LINE_EDITOR_PROPERTY_INNER['tooltip-selector'], + 'seriesTooltipFormatter' + ] + } + axis: AxisType[] = [...LINE_AXIS_TYPE, 'xAxisExt'] + axisConfig = { + ...this['axisConfig'], + xAxis: { + name: `${t('chart.drag_block_type_axis')} / ${t('chart.dimension')}`, + type: 'd' + }, + xAxisExt: { + name: `${t('chart.chart_group')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1, + allowEmpty: true + }, + yAxis: { + name: `${t('chart.drag_block_value_axis')} / ${t('chart.quota')}`, + type: 'q' + } + } + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, action, container } = drawOptions + if (!chart.data?.data?.length) { + chart.container = container + clearExtremum(chart) + return + } + const data = cloneDeep(chart.data.data) + // custom color + const customAttr = parseJson(chart.customAttr) + const color = customAttr.basicStyle.colors + // options + const initOptions: LineOptions = { + data, + xField: 'field', + yField: 'value', + seriesField: 'category', + appendPadding: getPadding(chart), + color, + interactions: [ + { + type: 'legend-active', + cfg: { + start: [{ trigger: 'legend-item:mouseenter', action: ['element-active:reset'] }], + end: [{ trigger: 'legend-item:mouseleave', action: ['element-active:reset'] }] + } + }, + { + type: 'legend-filter', + cfg: { + start: [ + { + trigger: 'legend-item:click', + action: [ + 'list-unchecked:toggle', + 'data-filter:filter', + 'element-active:reset', + 'element-highlight:reset' + ] + } + ] + } + }, + { + type: 'active-region', + cfg: { + start: [{ trigger: 'element:mousemove', action: 'active-region:show' }], + end: [{ trigger: 'element:mouseleave', action: 'active-region:hide' }] + } + } + ] + } + const options = this.setupOptions(chart, initOptions) + const { Line: G2Line } = await import('@antv/g2plot/esm/plots/line') + // 开始渲染 + const newChart = new G2Line(container, options) + + newChart.on('point:click', action) + extremumEvt(newChart, chart, options, container) + configPlotTooltipEvent(chart, newChart) + return newChart + } + + protected configLabel(chart: Chart, options: LineOptions): LineOptions { + const tmpOptions = super.configLabel(chart, options) + if (!tmpOptions.label) { + return { + ...tmpOptions, + label: false + } + } + const { label: labelAttr, basicStyle } = parseJson(chart.customAttr) + const conditions = getLineConditions(chart) + const formatterMap = labelAttr.seriesLabelFormatter?.reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) + tmpOptions.label.style.fill = DEFAULT_LABEL.color + const label = { + fields: [], + ...tmpOptions.label, + layout: labelAttr.fullDisplay ? [{ type: 'limit-in-plot' }] : tmpOptions.label.layout, + formatter: (data: Datum, _point) => { + if (data.EXTREME) { + return '' + } + if (!labelAttr.seriesLabelFormatter?.length) { + return data.value + } + const labelCfg = formatterMap?.[data.quotaList[0].id] as SeriesFormatter + if (!labelCfg) { + return data.value + } + if (!labelCfg.show) { + return + } + const position = + labelCfg.position === 'top' + ? -2 - basicStyle.lineSymbolSize + : 10 + basicStyle.lineSymbolSize + const value = valueFormatter(data.value, labelCfg.formatterCfg) + const color = + getLineLabelColorByCondition(conditions, data.value, data.quotaList[0].id) || + labelCfg.color + const group = new Group({}) + group.addShape({ + type: 'text', + attrs: { + x: 0, + y: position, + text: value, + textAlign: 'start', + textBaseline: 'top', + fontSize: labelCfg.fontSize, + fontFamily: chart.fontFamily, + fill: color + } + }) + return group + } + } + return { + ...tmpOptions, + label + } + } + + protected configBasicStyle(chart: Chart, options: LineOptions): LineOptions { + // size + const customAttr: DeepPartial = parseJson(chart.customAttr) + const s = JSON.parse(JSON.stringify(customAttr.basicStyle)) + const smooth = s.lineSmooth + const point = { + size: s.lineSymbolSize, + shape: s.lineSymbol + } + const lineStyle = { + lineWidth: s.lineWidth + } + return { + ...options, + smooth, + point, + lineStyle + } + } + + protected configCustomColors(chart: Chart, options: LineOptions): LineOptions { + const basicStyle = parseJson(chart.customAttr).basicStyle + const color = basicStyle.colors.map(item => hexColorToRGBA(item, basicStyle.alpha)) + return { + ...options, + color + } + } + + protected configYAxis(chart: Chart, options: LineOptions): LineOptions { + const tmpOptions = super.configYAxis(chart, options) + if (!tmpOptions.yAxis) { + return tmpOptions + } + const yAxis = parseJson(chart.customStyle).yAxis + if (tmpOptions.yAxis.label) { + tmpOptions.yAxis.label.formatter = value => { + return valueFormatter(value, yAxis.axisLabelFormatter) + } + } + const axisValue = yAxis.axisValue + if (!axisValue?.auto) { + const axis = { + yAxis: { + ...tmpOptions.yAxis, + min: axisValue.min, + max: axisValue.max, + minLimit: axisValue.min, + maxLimit: axisValue.max, + tickCount: axisValue.splitCount + } + } + return { ...tmpOptions, ...axis } + } + return tmpOptions + } + + protected configTooltip(chart: Chart, options: LineOptions): LineOptions { + const customAttr: DeepPartial = parseJson(chart.customAttr) + const tooltipAttr = customAttr.tooltip + if (!tooltipAttr.show) { + return { + ...options, + tooltip: false + } + } + const xAxisExt = chart.xAxisExt + const formatterMap = tooltipAttr.seriesTooltipFormatter + ?.filter(i => i.show) + .reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) as Record + const tooltip: LineOptions['tooltip'] = { + showTitle: true, + customItems(originalItems) { + if (!tooltipAttr.seriesTooltipFormatter?.length) { + return originalItems + } + const head = originalItems[0] + // 非原始数据 + if (!head.data.quotaList) { + return originalItems + } + const result = [] + originalItems + .filter(item => formatterMap[item.data.quotaList[0].id]) + .forEach(item => { + const formatter = formatterMap[item.data.quotaList[0].id] + const value = valueFormatter(parseFloat(item.value as string), formatter.formatterCfg) + let name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + if (xAxisExt?.length > 0) { + name = item.data.category + } + result.push({ ...item, name, value }) + }) + head.data.dynamicTooltipValue?.forEach(item => { + const formatter = formatterMap[item.fieldId] + if (formatter) { + const value = valueFormatter(parseFloat(item.value), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ color: 'grey', name, value }) + } + }) + return result + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + return { + ...options, + tooltip + } + } + public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + return setUpGroupSeriesColor(chart, data) + } + protected configLegend(chart: Chart, options: LineOptions): LineOptions { + const optionTmp = super.configLegend(chart, options) + if (!optionTmp.legend) { + return optionTmp + } + const xAxisExt = chart.xAxisExt[0] + if (xAxisExt?.customSort?.length > 0) { + // 图例自定义排序 + const sort = xAxisExt.customSort ?? [] + if (sort?.length) { + // 用值域限定排序,有可能出现新数据但是未出现在图表上,所以这边要遍历一下子维度,加到后面,让新数据显示出来 + const data = optionTmp.data + data?.forEach(d => { + const cat = d['category'] + if (cat && !sort.includes(cat)) { + sort.push(cat) + } + }) + optionTmp.meta = { + ...optionTmp.meta, + category: { + type: 'cat', + values: sort + } + } + } + } + + const customStyle = parseJson(chart.customStyle) + let size + if (customStyle && customStyle.legend) { + size = defaults(JSON.parse(JSON.stringify(customStyle.legend)), DEFAULT_LEGEND_STYLE).size + } else { + size = DEFAULT_LEGEND_STYLE.size + } + + optionTmp.legend.marker.style = style => { + return { + r: size, + fill: style.stroke + } + } + return optionTmp + } + protected setupOptions(chart: Chart, options: LineOptions): LineOptions { + return flow( + this.configTheme, + this.configEmptyDataStrategy, + this.configGroupColor, + this.configLabel, + this.configTooltip, + this.configBasicStyle, + this.configCustomColors, + this.configLegend, + this.configXAxis, + this.configYAxis, + this.configSlider, + this.configAnalyse, + this.configConditions + )(chart, options) + } + + constructor(name = 'line') { + super(name, DEFAULT_DATA) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/line/stock-line.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/line/stock-line.ts new file mode 100644 index 0000000..625ad65 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/line/stock-line.ts @@ -0,0 +1,718 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import type { Mix, MixOptions } from '@antv/g2plot/esm/plots/mix' +import { flow, hexColorToRGBA, parseJson } from '@/data-visualization/chart/components/js/util' +import { LINE_EDITOR_PROPERTY_INNER } from '@/data-visualization/chart/components/js/panel/charts/line/common' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import type { Options } from '@antv/g2plot/esm' +import { MixOptions } from '@antv/g2plot' + +const { t } = useI18n() +const DEFAULT_DATA = [] +/** + * K线图 + */ +export class StockLine extends G2PlotChartView { + properties: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'legend-selector', + 'x-axis-selector', + 'y-axis-selector', + 'title-selector', + 'tooltip-selector', + 'function-cfg', + 'jump-set', + 'linkage' + ] + propertyInner = { + ...LINE_EDITOR_PROPERTY_INNER, + 'function-cfg': ['emptyDataStrategy'], + 'y-axis-selector': [ + 'name', + 'color', + 'fontSize', + 'position', + 'axisLabel', + 'axisLine', + 'splitLine', + 'axisLabelFormatter' + ], + 'legend-selector': ['fontSize', 'color', 'show'] + } + axis: AxisType[] = ['xAxis', 'yAxis', 'filter', 'extLabel', 'extTooltip'] + axisConfig = { + xAxis: { + name: `${t('common.component.date')} / ${t('chart.dimension')}`, + limit: 1, + type: 'd' + }, + yAxis: { + name: `${t('chart.k_line_yaxis_tip')} / ${t('chart.quota')}`, + limit: 4, + type: 'q' + } + } + + /** + * 计算收盘价平均值 + * @param data + * @param dayCount + * @param chart + */ + calculateMovingAverage = (data, dayCount, chart) => { + const xAxis = chart.xAxis + const yAxis = chart.yAxis + // 时间字段 + const xAxisDataeaseName = xAxis[0].dataeaseName + // 收盘价字段 + const yAxisDataeaseName = yAxis[1].dataeaseName + const result = [] + for (let i = 0; i < data.length; i++) { + if (i < dayCount) { + result.push({ + [xAxisDataeaseName]: data[i][xAxisDataeaseName], + value: null + }) + } else { + const sum = data + .slice(i - dayCount + 1, i + 1) + .reduce((sum, item) => sum + item[yAxisDataeaseName], 0) + result.push({ + [xAxisDataeaseName]: data[i][xAxisDataeaseName], + value: parseFloat((sum / dayCount).toFixed(3)) + }) + } + } + return result + } + + /** + * 获取数据集合中对象属性值的最大最小值 + * @param data + */ + calculateMinMax = data => { + return data.reduce( + (acc, current) => { + // 获取 current 对象的所有属性值 + const values = Object.values(current) + // 过滤出数字值 + const numericValues: any[] = values.filter(value => typeof value === 'number') + // 找到 current 对象的数字属性值中的最大值和最小值 + // 如果存在数字值,则计算当前对象的最大值和最小值 + if (numericValues.length > 0) { + const currentMax = Math.max(...numericValues) + const currentMin = Math.min(...numericValues) + // 更新全局最大值和最小值 + acc.maxValue = Math.max(acc.maxValue, currentMax) + acc.minValue = Math.min(acc.minValue, currentMin) + } + return acc + }, + { maxValue: Number.NEGATIVE_INFINITY, minValue: Number.POSITIVE_INFINITY } + ) + } + + /** + * 注册图表事件 + * @param data + * @param plot + * @param averagesLineData + */ + registerEvent = (data, plot, averagesLineData) => { + // 监听图例点击事件,显示隐藏 + let risingVisible = true + plot.on('legend-item:click', evt => { + const { value } = evt.target.get('delegateObject').item + if (value === 'k') { + risingVisible = !risingVisible + plot.chart.geometries.forEach(geom => { + if (geom.type === 'schema') { + geom.changeVisible(risingVisible) + } + }) + } else { + const lines = plot.chart.geometries.filter(item => item.type === 'line') + const points = plot.chart.geometries.filter(item => item.type === 'point') + let lineIndex = 0 + for (const key of averagesLineData.keys()) { + lineIndex++ + if (key === value) { + lines[lineIndex - 1].changeVisible(!lines[lineIndex - 1].visible) + points[lineIndex - 1].changeVisible(!points[lineIndex - 1].visible) + } + } + } + }) + // 监听图表渲染事件 + plot.on('afterrender', e => { + let first = false + if (plot.chart.options.slider.start === 0.5 && plot.chart.options.slider.end === 1) { + first = true + } + if (e.view?.options?.scales) { + const startIndex = Math.floor(0.5 * data.length) + const endIndex = Math.ceil(1 * data.length) + const filteredData = data.slice(startIndex, endIndex) + const { maxValue, minValue } = this.calculateMinMax( + first ? filteredData : e.view.filteredData + ) + const a = e.view.options.scales + Object.keys(a).forEach(item => { + if (a[item].max) { + a[item].max = maxValue + } + if (a[item].min) { + a[item].min = minValue + } + }) + } + }) + // 监听图例组点击事件,设置缩放 + plot.on('legend-item-group:click', e => { + if (e.view?.options?.scales) { + const { maxValue, minValue } = this.calculateMinMax(e.view.filteredData) + const a = e.view.options.scales + Object.keys(a).forEach(item => { + if (a[item].max) { + a[item].max = maxValue + } + if (a[item].min) { + a[item].min = minValue + } + }) + } + }) + // 监听滑块事件,设置缩放 + plot.on('slider:valuechanged', e => { + const start = e.gEvent.currentTarget.cfg.component.cfg.start + const end = e.gEvent.currentTarget.cfg.component.cfg.end + plot.chart.options.slider.start = start + plot.chart.options.slider.end = end + const startIndex = Math.floor(start * data.length) + const endIndex = Math.ceil(end * data.length) + const filteredData = data.slice(startIndex, endIndex) + const { maxValue, minValue } = this.calculateMinMax(filteredData) + const a = e.view.options.scales + Object.keys(a).forEach(item => { + if (a[item].max) { + a[item].max = maxValue + } + if (a[item].min) { + a[item].min = minValue + } + }) + }) + } + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, action, container } = drawOptions + if (!chart.data?.data?.length) { + return + } + const xAxis = chart.xAxis + const yAxis = chart.yAxis + if (yAxis.length != 4) { + return + } + const basicStyle = parseJson(chart.customAttr).basicStyle + const colors = [] + const alpha = basicStyle.alpha + basicStyle.colors.forEach(ele => { + colors.push(hexColorToRGBA(ele, alpha)) + }) + const data = parseJson(chart.data?.tableRow) + + // 时间字段 + const xAxisDataeaseName = xAxis[0].dataeaseName + const averages = [5, 10, 20, 60, 120, 180] + const legendItems: any[] = [ + { + name: '日K', + value: 'k', + marker: { + symbol: (x, y, r) => { + const width = r * 1 + const height = r + return [ + // 矩形框 + ['M', x - width - 1 / 2, y - height / 2], + ['L', x + width + 1 / 2, y - height / 2], + ['L', x + width + 1 / 2, y + height / 2], + ['L', x - width - 1 / 2, y + height / 2], + ['Z'], + // 中线 + ['M', x, y + 10 / 2], + ['L', x, y - 10 / 2] + ] + }, + style: { fill: 'red', stroke: 'red', lineWidth: 2 } + } + } + ] + // 计算均线数据 + const averagesLineData = new Map() + averages.forEach(item => { + averagesLineData.set('ma' + item, this.calculateMovingAverage(data, item, chart)) + }) + + // 将均线数据设置到主数据中 + data.forEach((item: any) => { + const date = item[xAxisDataeaseName] + for (const [key, value] of averagesLineData) { + item[key] = value.find(m => m[xAxisDataeaseName] === date)?.value + } + }) + + const averageLines: any[] = [] + let index = 0 + const start = 0.5 + const end = 1 + const startIndex = Math.floor(start * data.length) + const endIndex = Math.ceil(end * data.length) + const filteredData = data.slice(startIndex, endIndex) + const { maxValue, minValue } = this.calculateMinMax(filteredData) + for (const key of averagesLineData.keys()) { + index++ + averageLines.push({ + type: 'line', + top: true, + options: { + smooth: false, + xField: xAxisDataeaseName, + yField: key, + color: colors[index - 1], + xAxis: null, + yAxis: { + label: false, + min: minValue, + max: maxValue, + grid: null, + line: null + }, + lineStyle: { + lineWidth: 2 + } + } + }) + legendItems.push({ + name: key.toUpperCase(), + value: key, + marker: { symbol: 'hyphen', style: { stroke: colors[index - 1], lineWidth: 2 } } + }) + } + const axis = chart.xAxis ?? [] + let dateFormat: string + const dateSplit = axis[0]?.datePattern === 'date_split' ? '/' : '-' + switch (axis[0]?.dateStyle) { + case 'y': + dateFormat = 'YYYY' + break + case 'y_M': + dateFormat = 'YYYY' + dateSplit + 'MM' + break + case 'y_M_d': + dateFormat = 'YYYY' + dateSplit + 'MM' + dateSplit + 'DD' + break + // case 'H_m_s': + // dateFormat = 'HH:mm:ss' + // break + case 'y_M_d_H': + dateFormat = 'YYYY' + dateSplit + 'MM' + dateSplit + 'DD' + ' HH' + break + case 'y_M_d_H_m': + dateFormat = 'YYYY' + dateSplit + 'MM' + dateSplit + 'DD' + ' HH:mm' + break + case 'y_M_d_H_m_s': + dateFormat = 'YYYY' + dateSplit + 'MM' + dateSplit + 'DD' + ' HH:mm:ss' + break + default: + dateFormat = 'YYYY-MM-dd HH:mm:ss' + } + const option = this.setupOptions(chart, { + data, + slider: { + start: 0.5, + end: 1, + textStyle: { + fontFamily: chart.fontFamily + } + }, + plots: [ + { + type: 'stock', + top: true, + + options: { + meta: { + [xAxisDataeaseName]: { + mask: dateFormat + } + }, + stockStyle: { + stroke: 'black', + lineWidth: 0.5 + }, + yAxis: { + label: {}, + position: 'left', + min: minValue, + max: maxValue + }, + xField: xAxisDataeaseName, + yField: [ + yAxis[0].dataeaseName, + yAxis[1].dataeaseName, + yAxis[2].dataeaseName, + yAxis[3].dataeaseName + ], + legend: { + position: 'top', + custom: true, + items: legendItems + } + } + }, + ...averageLines + ] + }) + const { Mix: MixClass } = await import('@antv/g2plot/esm/plots/mix') + const plot = new MixClass(container, option) + this.registerEvent(data, plot, averagesLineData) + plot.on('schema:click', evt => { + const selectSchema = evt.data.data[xAxisDataeaseName] + const paramData = parseJson(chart.data?.data) + const selectData = paramData.filter(item => item.field === selectSchema) + const quotaList = [] + selectData.forEach(item => { + quotaList.push({ ...item.quotaList[0], value: item.value }) + }) + if (selectData.length) { + const param = { + x: evt.x, + y: evt.y, + data: { + data: { + ...evt.data.data, + value: quotaList[0].value, + name: selectSchema, + dimensionList: selectData[0].dimensionList, + quotaList: quotaList + } + } + } + action(param) + } + }) + return plot + } + + protected configBasicStyle(chart: Chart, options: MixOptions): MixOptions { + // size + const customAttr: DeepPartial = parseJson(chart.customAttr) + const s = JSON.parse(JSON.stringify(customAttr.basicStyle)) + const smooth = s.lineSmooth + const point = { + size: s.lineSymbolSize, + shape: s.lineSymbol + } + const lineStyle = { + lineWidth: s.lineWidth + } + const plots = [] + options.plots.forEach(item => { + if (item.type === 'stock') { + plots.push({ ...item }) + } + if (item.type === 'line') { + plots.push({ ...item, options: { ...item.options, smooth, point, lineStyle } }) + } + }) + return { + ...options, + plots + } + } + + protected configTooltip(chart: Chart, options: MixOptions): MixOptions { + const tooltipAttr = parseJson(chart.customAttr).tooltip + const xAxis = chart.xAxis + const newPlots = [] + const linePlotList = options.plots.filter(item => item.type === 'line') + linePlotList.forEach(item => { + newPlots.push(item) + }) + const stockPlot = options.plots.filter(item => item.type === 'stock')[0] + if (!tooltipAttr.show) { + const stockOption = { + ...stockPlot.options, + tooltip: { + showContent: false + } + } + newPlots.push({ ...stockPlot, options: stockOption }) + return { + ...options, + plots: newPlots + } + } + + const showFiled = chart.data.fields + const customTooltipItems = originalItems => { + const formattedItems = originalItems.map(item => { + const fieldObj = showFiled.find(q => q.dataeaseName === item.name) + const displayName = fieldObj?.chartShowName || fieldObj?.name || item.name + const formattedName = displayName.startsWith('ma') ? displayName.toUpperCase() : displayName + tooltipAttr.tooltipFormatter.decimalCount = 3 + const formattedValue = valueFormatter(item.value, tooltipAttr.tooltipFormatter) + + return { + ...item, + name: formattedName, + value: formattedValue, + color: item.color + } + }) + + const hasKLine = formattedItems.some(item => !item.name.startsWith('MA')) + const kLines = formattedItems.filter(item => !item.name.startsWith('MA')) + return hasKLine + ? [ + { name: '日K', value: '', marker: true, color: kLines[0]?.color }, + ...kLines, + ...formattedItems.filter(item => item.name.startsWith('MA')) + ] + : formattedItems + } + const formatTooltipItem = (item: any) => { + const size = item.name.startsWith('MA') || !item.value ? 10 : 5 + const markerMarginRight = item.name.startsWith('MA') || !item.value ? 5 : 9 + const markerMarginLeft = item.name.startsWith('MA') || !item.value ? 0 : 2 + return ` +
  • +
    + +
    +
    + ${item.name} + ${item.name.startsWith('MA') && item.value === '0' ? '-' : item.value} +
    +
  • + ` + } + const generateCustomTooltipContent = (title: string, items: Array) => { + return ` +
    +
    ${title}
    +
      + ${items.map(formatTooltipItem).join('')} +
    +
    + ` + } + const stockOption = { + ...stockPlot.options, + tooltip: { + showMarkers: true, + showCrosshairs: true, + showNil: true, + crosshairs: { + follow: true, + text: (axisType, value, data) => { + if (axisType === 'y') { + return { content: value ? value.toFixed(0) : value } + } + return { content: data[0].title, position: 'end' } + } + }, + showContent: true, + customItems: customTooltipItems, + customContent: generateCustomTooltipContent + } + } + newPlots.push({ ...stockPlot, options: stockOption }) + return { + ...options, + plots: newPlots + } + } + + protected configXAxis(chart: Chart, options: MixOptions): MixOptions { + const xAxisOptions = super.configXAxis(chart, options) + if (!xAxisOptions) { + return options + } + const newPlots = [] + const linePlotList = options.plots.filter(item => item.type === 'line') + + const stockPlot = options.plots.filter(item => item.type === 'stock')[0] + const newStockPlot = { + ...stockPlot, + options: { + ...stockPlot.options, + xAxis: xAxisOptions['xAxis'] + ? { + ...stockPlot.options['xAxis'], + ...xAxisOptions['xAxis'] + } + : { + label: false, + line: null + } + } + } + newPlots.push(newStockPlot) + linePlotList.forEach(item => { + newPlots.push(item) + }) + return { + ...options, + plots: newPlots + } + } + protected configYAxis(chart: Chart, options: MixOptions): MixOptions { + const yAxisOptions = super.configYAxis(chart, options) + if (!yAxisOptions) { + return options + } + const yAxis = parseJson(chart.customStyle).yAxis + const newPlots = [] + const linePlotList = options.plots.filter(item => item.type === 'line') + + const stockPlot = options.plots.filter(item => item.type === 'stock')[0] + let label = false + if (yAxisOptions['yAxis'].label) { + label = { + ...yAxisOptions['yAxis'].label, + formatter: value => { + return valueFormatter(value, yAxis.axisLabelFormatter) + } + } + } + const newStockPlot = { + ...stockPlot, + options: { + ...stockPlot.options, + yAxis: label + ? { + ...stockPlot.options['yAxis'], + ...yAxisOptions['yAxis'], + label + } + : { + ...yAxisOptions['yAxis'], + label, + grid: null, + line: null + } + } + } + newPlots.push(newStockPlot) + linePlotList.forEach(item => { + newPlots.push(item) + }) + return { + ...options, + plots: newPlots + } + } + + protected customConfigEmptyDataStrategy(chart: Chart, options: MixOptions): MixOptions { + const { data } = options as unknown as Options + if (!data?.length) { + return options + } + const strategy = parseJson(chart.senior).functionCfg.emptyDataStrategy + if (strategy === 'ignoreData') { + for (let i = data.length - 1; i >= 0; i--) { + const item = data[i] + Object.keys(item).forEach(key => { + if (key.startsWith('f_') && item[key] === null) { + data.splice(i, 1) + } + }) + } + } + const updateValues = (strategy: 'breakLine' | 'setZero', data: any[]) => { + data.forEach(obj => { + Object.keys(obj).forEach(key => { + if (key.startsWith('f_') && obj[key] === null) { + obj[key] = strategy === 'breakLine' ? null : 0 + } + }) + }) + } + if (strategy === 'breakLine' || strategy === 'setZero') { + updateValues(strategy, data) + } + return options + } + protected configLegend(chart: Chart, options: MixOptions): MixOptions { + let legend = {} + let customStyle: CustomStyle + const stockPlot = options.plots.filter(item => item.type === 'stock')[0] + if (chart.customStyle) { + customStyle = parseJson(chart.customStyle) + // legend + if (customStyle.legend) { + const l = JSON.parse(JSON.stringify(customStyle.legend)) + if (l.show) { + legend = { + ...stockPlot.options.legend, + itemName: { + style: { + fill: l.color, + fontSize: l.fontSize + } + } + } + } else { + legend = false + } + } + } + const newPlots = [] + const stockOption = { + ...stockPlot.options, + legend: legend + } + const linePlotList = options.plots.filter(item => item.type === 'line') + linePlotList.forEach(item => { + newPlots.push(item) + }) + newPlots.push({ ...stockPlot, options: stockOption }) + return { + ...options, + plots: newPlots + } + } + + protected setupOptions(chart: Chart, options: MixOptions): MixOptions { + return flow( + this.configTheme, + this.configBasicStyle, + this.configXAxis, + this.configYAxis, + this.configTooltip, + this.configLegend, + this.customConfigEmptyDataStrategy + )(chart, options) + } + + constructor(name = 'stock-line') { + super(name, DEFAULT_DATA) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/liquid/liquid.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/liquid/liquid.ts new file mode 100644 index 0000000..9ca812c --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/liquid/liquid.ts @@ -0,0 +1,221 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import type { Liquid as G2Liquid, LiquidOptions } from '@antv/g2plot/esm/plots/liquid' +import { flow, hexColorToRGBA, parseJson } from '@/data-visualization/chart/components/js/util' +import { DEFAULT_MISC } from '@/data-visualization/chart/components/editor/util/chart' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' + +const { t } = useI18n() +const DEFAULT_LIQUID_DATA = [] +/** + * 水波图 + */ +export class Liquid extends G2PlotChartView { + properties: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'label-selector', + 'misc-selector', + 'title-selector', + 'threshold' + ] + propertyInner: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + 'basic-style-selector': ['colors', 'alpha'], + 'label-selector': ['fontSize', 'color', 'labelFormatter'], + 'misc-selector': ['liquidShape', 'liquidSize', 'liquidMaxType', 'liquidMaxField'], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + threshold: ['liquidThreshold'] + } + axis: AxisType[] = ['yAxis', 'filter'] + axisConfig: AxisConfig = { + yAxis: { + name: `${t('chart.drag_block_progress')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + } + } + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (!chart.data?.series || !chart.yAxis.length) { + return + } + const initOptions: LiquidOptions = { + percent: 0 + } + const options = this.setupOptions(chart, initOptions) + const { Liquid: G2Liquid } = await import('@antv/g2plot/esm/plots/liquid') + const newChart = new G2Liquid(container, options) + newChart.on('afterrender', () => { + action({ + from: 'liquid', + data: { + type: 'liquid', + max: chart.data?.series[0]?.data[0] + } + }) + }) + // 处理空数据, 只要有一个指标是空数据,就不显示图表 + const hasNoneData = chart.data?.series.some(s => !s.data?.[0]) + this.configEmptyDataStyle(newChart, hasNoneData ? [] : [1], container) + if (hasNoneData) { + return + } + return newChart + } + + protected configTheme(chart: Chart, options: LiquidOptions): LiquidOptions { + const customAttr = parseJson(chart.customAttr) + const colors: string[] = [] + if (customAttr.basicStyle) { + const basicStyle = customAttr.basicStyle + basicStyle.colors.forEach(ele => { + colors.push(hexColorToRGBA(ele, basicStyle.alpha)) + }) + } + const customStyle = parseJson(chart.customStyle) + let bgColor + if (customStyle.background) { + bgColor = hexColorToRGBA(customStyle.background.color, customStyle.background.alpha) + } + const theme = { + styleSheet: { + brandColor: colors[0], + paletteQualitative10: colors, + paletteQualitative20: colors, + backgroundColor: bgColor + } + } + return { ...options, theme } + } + + protected configMisc(chart: Chart, options: LiquidOptions): LiquidOptions { + const customAttr = parseJson(chart.customAttr) + let value = 0 + if (chart.data.series.length > 0) { + value = chart.data.series[0].data[0] + } + let max, radius, shape + if (customAttr.misc) { + const misc = customAttr.misc + const defaultLiquidMax = chart.data?.series[chart.data?.series.length - 1]?.data[0] + if (misc.liquidMaxType === 'dynamic') { + max = defaultLiquidMax + } else { + max = misc.liquidMax ? misc.liquidMax : defaultLiquidMax + } + radius = (misc.liquidSize ? misc.liquidSize : DEFAULT_MISC.liquidSize) / 100 + shape = misc.liquidShape ?? DEFAULT_MISC.liquidShape + } + const size: LiquidOptions = { + percent: value / max, + radius: radius, + shape: shape + } + return { ...options, ...size } + } + + protected configLabel(chart: Chart, options: LiquidOptions): LiquidOptions { + const customAttr = parseJson(chart.customAttr) + const originVal = options.percent + // 数值过大图表会异常,大于 1 无意义 + if (originVal > 1) { + options = { + ...options, + percent: 1 + } + } + if (!customAttr.label?.show) { + return { + ...options, + statistic: { + content: false + } + } + } + const label = customAttr.label + const labelFormatter = label.labelFormatter + return { + ...options, + statistic: { + content: { + style: { + fontSize: label.fontSize.toString() + 'px', + color: label.color + }, + formatter: () => { + return valueFormatter(originVal, labelFormatter) + } + } + } + } + } + + protected configThreshold(chart: Chart, options: LiquidOptions): LiquidOptions { + const senior = parseJson(chart.senior) + if (senior?.threshold?.enable) { + const { liquidThreshold } = senior?.threshold + if (liquidThreshold) { + const { paletteQualitative10: colors } = (options.theme as any).styleSheet + const liquidStyle = () => { + const thresholdArr = liquidThreshold.split(',') + let index = 0 + thresholdArr.forEach((v, i) => { + if (options.percent > parseFloat(v) / 100) { + index = i + 1 + } + }) + return { + fill: colors[index % colors.length], + stroke: colors[index % colors.length] + } + } + return { ...options, liquidStyle } + } + } + return options + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + chart.customAttr.label = { + ...chart.customAttr.label, + fontSize: 12, + show: true, + labelFormatter: { + type: 'percent', + thousandSeparator: true, + decimalCount: 2 + } + } + return chart + } + + protected setupOptions(chart: Chart, options: LiquidOptions): LiquidOptions { + return flow( + this.configTheme, + this.configMisc, + this.configLabel, + this.configThreshold + )(chart, options) + } + constructor() { + super('liquid', DEFAULT_LIQUID_DATA) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/map/bubble-map.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/map/bubble-map.ts new file mode 100644 index 0000000..d9554e9 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/map/bubble-map.ts @@ -0,0 +1,514 @@ +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { + L7PlotChartView, + L7PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/l7plot' +import { Choropleth, ChoroplethOptions } from '@antv/l7plot/dist/esm/plots/choropleth' +import { Dot, DotOptions, IPlotLayer } from '@antv/l7plot' +import { + MAP_AXIS_TYPE, + MAP_EDITOR_PROPERTY, + MAP_EDITOR_PROPERTY_INNER, + MapMouseEvent +} from '@/data-visualization/chart/components/js/panel/charts/map/common' +import { flow, getGeoJsonFile, hexColorToRGBA, parseJson } from '@/data-visualization/chart/components/js/util' +import { cloneDeep, isEmpty } from 'lodash-es' +import { FeatureCollection } from '@antv/l7plot/dist/esm/plots/choropleth/types' +import { + handleGeoJson, + mapRendered, + mapRendering +} from '@/data-visualization/chart/components/js/panel/common/common_antv' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { deepCopy } from '@/data-visualization/utils/utils' +import { configCarouselTooltip } from '@/data-visualization/chart/components/js/panel/charts/map/tooltip-carousel' +import { getCustomGeoArea } from '@/api/data-visualization/map' +import { TextLayer } from '@antv/l7plot/dist/esm' +import { centroid } from '@turf/centroid' + +const { t } = useI18n() + +/** + * 气泡地图 + */ +export class BubbleMap extends L7PlotChartView { + properties: EditorProperty[] = [...MAP_EDITOR_PROPERTY, 'bubble-animate'] + propertyInner = { + ...MAP_EDITOR_PROPERTY_INNER, + 'tooltip-selector': [...MAP_EDITOR_PROPERTY_INNER['tooltip-selector'], 'carousel'], + 'basic-style-selector': [...MAP_EDITOR_PROPERTY_INNER['basic-style-selector'], 'areaBaseColor'] + } + axis = MAP_AXIS_TYPE + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.area')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1 + }, + yAxis: { + name: `${t('chart.bubble_size')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + } + } + constructor() { + super('bubble-map') + } + + async drawChart(drawOption: L7PlotDrawOptions): Promise { + const { chart, level, areaId, container, action, scope } = drawOption + if (!areaId) { + return + } + chart.container = container + let geoJson = {} as FeatureCollection + let customSubArea: CustomGeoSubArea[] = [] + let data = chart.data?.data + if (areaId.startsWith('custom_')) { + customSubArea = (await getCustomGeoArea(areaId)).data || [] + customSubArea.forEach(a => (a.scopeArr = a.scope?.split(',') || [])) + geoJson = cloneDeep(await getGeoJsonFile('156')) + const areaNameMap = geoJson.features.reduce((p, n) => { + p['156' + n.properties.adcode] = n.properties.name + return p + }, {}) + const { areaMapping } = parseJson(chart.senior) + const areaMap = customSubArea.reduce((p, n) => { + const mappedName = areaMapping?.[areaId]?.[n.name] + if (mappedName) { + n.name = mappedName + } + p[n.name] = n + n.scopeArr = n.scope?.split(',') || [] + return p + }, {}) + const fakeData = [] + data?.forEach(d => { + const area = areaMap[d.name] + if (area) { + area.scopeArr.forEach(adcode => { + fakeData.push({ + ...d, + name: areaNameMap[adcode], + field: areaNameMap[adcode], + scope: area.scopeArr, + areaName: d.name + }) + }) + } + }) + data = fakeData + } else { + if (scope) { + geoJson = cloneDeep(await getGeoJsonFile('156')) + geoJson.features = geoJson.features.filter(f => scope.includes('156' + f.properties.adcode)) + } else { + geoJson = cloneDeep(await getGeoJsonFile(areaId)) + } + } + let options: ChoroplethOptions = { + preserveDrawingBuffer: true, + map: { + type: 'mapbox', + style: 'blank' + }, + geoArea: { + type: 'geojson' + }, + source: { + data: data || [], + joinBy: { + sourceField: 'name', + geoField: 'name', + geoData: geoJson + } + }, + viewLevel: { + level, + adcode: 'all' + }, + autoFit: true, + chinaBorder: false, + color: { + field: 'value' + }, + style: { + opacity: 1, + lineWidth: 0.6, + lineOpacity: 1 + }, + label: { + field: '_DE_LABEL_', + style: { + textAnchor: 'center' + } + }, + tooltip: {}, + legend: false, + // 禁用线上地图数据 + customFetchGeoData: () => null + } + const context: Record = { drawOption, geoJson, customSubArea } + options = this.setupOptions(chart, options, context) + + const tooltip = deepCopy(options.tooltip) + options = { ...options, tooltip: { ...tooltip, showComponent: false } } + const view = new Choropleth(container, options) + const dotLayer = this.getDotLayer(chart, geoJson, drawOption, customSubArea) + if (!areaId.startsWith('custom_')) { + dotLayer.options = { ...dotLayer.options, tooltip } + } + this.configZoomButton(chart, view) + mapRendering(container) + view.once('loaded', () => { + // 修改地图鼠标样式为默认 + view.scene.map._canvasContainer.lastElementChild.style.cursor = 'default' + const { layers } = context + if (layers) { + layers.forEach(l => { + view.addLayer(l) + }) + } + dotLayer.addToScene(view.scene) + dotLayer.once('add', () => { + mapRendered(container) + }) + view.scene.map['keyboard'].disable() + dotLayer.on('dotLayer:click', (ev: MapMouseEvent) => { + const data = ev.feature.properties + let adcode, scope + if (areaId.startsWith('custom_')) { + adcode = '156' + const area = customSubArea.find(a => a.name === data.name) + scope = area?.scopeArr + } else { + adcode = view.currentDistrictData.features.find( + i => i.properties.name === ev.feature.properties.name + )?.properties.adcode + } + action({ + x: ev.x, + y: ev.y, + data: { + data, + extra: { adcode, scope } + } + }) + }) + dotLayer.once('loaded', () => { + chart.container = container + configCarouselTooltip(chart, view, data || [], null, customSubArea, drawOption) + }) + }) + return view + } + + private getDotLayer( + chart: Chart, + geoJson: FeatureCollection, + drawOption: L7PlotDrawOptions, + customSubArea: CustomGeoSubArea[] + ): IPlotLayer { + const { areaId } = drawOption + const { basicStyle, tooltip } = parseJson(chart.customAttr) + const { bubbleCfg } = parseJson(chart.senior) + const { offsetHeight, offsetWidth } = document.getElementById(drawOption.container) + const dotData = [] + const options: DotOptions = { + source: { + data: dotData, + parser: { + type: 'json', + x: 'x', + y: 'y' + } + }, + shape: 'circle', + size: { + field: 'size', + value: [5, Math.min(offsetHeight, offsetWidth) / 20] + }, + visible: true, + zIndex: 0.05, + color: hexColorToRGBA(basicStyle.colors[0], basicStyle.alpha), + name: 'bubbleLayer', + style: { + opacity: 1 + }, + state: { + active: { color: 'rgba(30,90,255,1)' } + }, + tooltip: { + showComponent: tooltip.show + } + } + if (areaId.startsWith('custom_')) { + const geoJsonMap = geoJson.features.reduce((p, n) => { + if (n.properties['adcode']) { + p['156' + n.properties['adcode']] = n + } + return p + }, {}) + const { areaMapping } = parseJson(chart.senior) + const customAreaMap = customSubArea.reduce((p, n) => { + const mappedName = areaMapping?.[areaId]?.[n.name] + if (mappedName) { + n.name = mappedName + } + p[n.name] = n + return p + }, {}) + chart.data?.data?.forEach(d => { + const area = customAreaMap[d.name] + if (area) { + const areaJsonArr = [] + area.scopeArr?.forEach(adcode => { + const json = geoJsonMap[adcode] + json && areaJsonArr.push(json) + }) + if (areaJsonArr.length) { + const areaJson: FeatureCollection = { + type: 'FeatureCollection', + features: areaJsonArr + } + const center = centroid(areaJson) + // 轮播用 + area.centroid = [center.geometry.coordinates[0], center.geometry.coordinates[1]] + dotData.push({ + name: area.name, + size: d.value, + properties: d, + x: center.geometry.coordinates[0], + y: center.geometry.coordinates[1] + }) + } + } + }) + if (options.tooltip && options.tooltip.showComponent) { + options.tooltip.items = ['name', 'adcode', 'value'] + options.tooltip.customTitle = ({ name }) => { + return name + } + const formatterMap = tooltip.seriesTooltipFormatter + ?.filter(i => i.show) + .reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) as Record + options.tooltip.customItems = originalItem => { + const result = [] + if (isEmpty(formatterMap)) { + return result + } + const head = originalItem.properties + const formatter = formatterMap[head.quotaList?.[0]?.id] + if (!isEmpty(formatter)) { + const originValue = parseFloat(head.value as string) + const value = valueFormatter(originValue, formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ ...head, name, value: `${value ?? ''}` }) + } + head.dynamicTooltipValue?.forEach(item => { + const formatter = formatterMap[item.fieldId] + if (formatter) { + const value = valueFormatter(parseFloat(item.value), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) + ? formatter.name + : formatter.chartShowName + result.push({ color: 'grey', name, value: `${value ?? ''}` }) + } + }) + return result + } + options.tooltip.domStyles = { + 'l7plot-tooltip': { + 'background-color': tooltip.backgroundColor, + 'font-size': `${tooltip.fontSize}px`, + 'line-height': 1.6 + }, + 'l7plot-tooltip__name': { + color: tooltip.color + }, + 'l7plot-tooltip__value': { + color: tooltip.color + }, + 'l7plot-tooltip__title': { + color: tooltip.color + } + } + } + } else { + const areaMap = chart.data?.data?.reduce((obj, value) => { + obj[value['field']] = { value: value.value, data: value } + return obj + }, {}) + geoJson.features.forEach(item => { + const name = item.properties['name'] + if (areaMap?.[name]?.value) { + dotData.push({ + x: item.properties['centroid'][0], + y: item.properties['centroid'][1], + size: areaMap[name].value, + properties: areaMap[name].data, + name: name + }) + } + }) + } + if (bubbleCfg && bubbleCfg.enable) { + return new Dot({ + ...options, + size: { + field: 'size', + value: [10, Math.min(offsetHeight, offsetWidth) / 10] + }, + animate: { + enable: true, + speed: bubbleCfg.speed, + rings: bubbleCfg.rings + } + }) + } + return new Dot(options) + } + + private configBasicStyle( + chart: Chart, + options: ChoroplethOptions, + context: Record + ): ChoroplethOptions { + const { areaId }: L7PlotDrawOptions = context.drawOption + const geoJson: FeatureCollection = context.geoJson + const { basicStyle, label } = parseJson(chart.customAttr) + const senior = parseJson(chart.senior) + const curAreaNameMapping = senior.areaMapping?.[areaId] + handleGeoJson(geoJson, curAreaNameMapping) + options.color = basicStyle.areaBaseColor + if (!chart.data?.data?.length || !geoJson?.features?.length) { + options.label && (options.label.field = 'name') + return options + } + const data = chart.data.data + const areaMap = data.reduce((obj, value) => { + obj[value['field']] = value.value + return obj + }, {}) + geoJson.features.forEach(item => { + const name = item.properties['name'] + // trick, maybe move to configLabel, here for perf + if (label.show) { + const content = [] + if (label.showDimension) { + content.push(name) + } + if (label.showQuota) { + areaMap[name] && content.push(valueFormatter(areaMap[name], label.quotaLabelFormatter)) + } + item.properties['_DE_LABEL_'] = content.join('\n\n') + } + }) + return options + } + + protected configCustomArea( + chart: Chart, + options: ChoroplethOptions, + context: Record + ): ChoroplethOptions { + const { drawOption, customSubArea, geoJson } = context + if (!drawOption.areaId.startsWith('custom_')) { + return options + } + const customAttr = parseJson(chart.customAttr) + const { label } = customAttr + const data = chart.data?.data + const areaMap = data?.reduce((obj, value) => { + obj[value['field']] = value + return obj + }, {}) + //处理label + options.label = { + visible: false + } + if (label.show) { + const geoJsonMap = geoJson.features.reduce((p, n) => { + if (n.properties['adcode']) { + p['156' + n.properties['adcode']] = n + } + return p + }, {}) + const { areaMapping } = parseJson(chart.senior) + const labelLocation = [] + customSubArea.forEach(area => { + const areaJsonArr = [] + area.scopeArr?.forEach(adcode => { + const json = geoJsonMap[adcode] + json && areaJsonArr.push(json) + }) + if (areaJsonArr.length) { + const areaJson: FeatureCollection = { + type: 'FeatureCollection', + features: areaJsonArr + } + const content = [] + if (label.showDimension) { + const mappedName = areaMapping?.[drawOption.areaId]?.[area.name] + if (mappedName) { + area.name = mappedName + } + content.push(area.name) + } + if (label.showQuota) { + areaMap[area.name] && + content.push(valueFormatter(areaMap[area.name].value, label.quotaLabelFormatter)) + } + const center = centroid(areaJson) + labelLocation.push({ + name: content.join('\n\n'), + x: center.geometry.coordinates[0], + y: center.geometry.coordinates[1] + }) + } + }) + const areaLabelLayer = new TextLayer({ + name: 'areaLabelLayer', + source: { + data: labelLocation, + parser: { + type: 'json', + x: 'x', + y: 'y' + } + }, + field: 'name', + zIndex: 0.06, + style: { + fill: label.color, + fontSize: label.fontSize, + opacity: 1, + fontWeight: 'bold', + textAnchor: 'center', + textAllowOverlap: label.fullDisplay, + padding: !label.fullDisplay ? [2, 2] : undefined + } + }) + context.layers = [areaLabelLayer] + } + return options + } + + protected setupOptions( + chart: Chart, + options: ChoroplethOptions, + context: Record + ): ChoroplethOptions { + return flow( + this.configEmptyDataStrategy, + this.configLabel, + this.configStyle, + this.configTooltip, + this.configBasicStyle, + this.configCustomArea + )(chart, options, context, this) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/map/common.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/map/common.ts new file mode 100644 index 0000000..7589f4d --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/map/common.ts @@ -0,0 +1,56 @@ +export const MAP_EDITOR_PROPERTY: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'title-selector', + 'label-selector', + 'tooltip-selector', + 'function-cfg', + 'map-mapping', + 'jump-set', + 'linkage' +] + +export const MAP_EDITOR_PROPERTY_INNER: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + 'basic-style-selector': ['colors', 'alpha', 'areaBorderColor', 'zoom'], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'label-selector': [ + 'color', + 'fontSize', + 'labelBgColor', + 'labelShadow', + 'labelShadowColor', + 'showDimension', + 'showQuota' + ], + 'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'seriesTooltipFormatter', 'show'], + 'function-cfg': ['emptyDataStrategy'], + 'map-mapping': [''] +} + +export const MAP_AXIS_TYPE: AxisType[] = [ + 'xAxis', + 'yAxis', + 'area', + 'drill', + 'filter', + 'extLabel', + 'extTooltip' +] + +export declare type MapMouseEvent = MouseEvent & { + feature: GeoJSON.Feature +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/map/flow-map.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/map/flow-map.ts new file mode 100644 index 0000000..2d11189 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/map/flow-map.ts @@ -0,0 +1,411 @@ +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { + L7ChartView, + L7Config, + L7DrawConfig, + L7Wrapper +} from '@/data-visualization/chart/components/js/panel/types/impl/l7' +import { MAP_EDITOR_PROPERTY_INNER } from '@/data-visualization/chart/components/js/panel/charts/map/common' +import { hexColorToRGBA, parseJson } from '@/data-visualization/chart/components/js/util' +import { deepCopy } from '@/data-visualization/utils/utils' +import { GaodeMap } from '@antv/l7-maps' +import { Scene } from '@antv/l7-scene' +import { LineLayer } from '@antv/l7-layers' +import { PointLayer } from '@antv/l7-layers' +import { mapRendered, mapRendering } from '@/data-visualization/chart/components/js/panel/common/common_antv' +import { DEFAULT_BASIC_STYLE } from '@/data-visualization/chart/components/editor/util/chart' +const { t } = useI18n() + +/** + * 流向地图 + */ +export class FlowMap extends L7ChartView { + properties: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'title-selector', + 'flow-map-line-selector', + 'flow-map-point-selector', + 'bubble-animate' + ] + propertyInner: EditorPropertyInner = { + ...MAP_EDITOR_PROPERTY_INNER, + 'basic-style-selector': [ + 'mapBaseStyle', + 'mapLineStyle', + 'zoom', + 'showLabel', + 'autoFit', + 'mapCenter', + 'zoomLevel' + ] + } + axis: AxisType[] = ['xAxis', 'xAxisExt', 'filter', 'flowMapStartName', 'flowMapEndName', 'yAxis'] + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.start_coordinates')} / ${t('chart.dimension')}`, + type: 'd', + limit: 2 + }, + xAxisExt: { + name: `${t('chart.end_coordinates')} / ${t('chart.dimension')}`, + type: 'd', + limit: 2 + }, + flowMapStartName: { + name: `${t('chart.start_name')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1, + allowEmpty: true + }, + flowMapEndName: { + name: `${t('chart.end_name')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1, + allowEmpty: true + }, + yAxis: { + name: `${t('chart.flow_map_line_width')} / ${t('chart.quota')}`, + type: 'q', + limit: 1, + tooltip: t('chart.flow_map_line_width_tip'), + allowEmpty: true + } + } + constructor() { + super('flow-map', []) + } + + async drawChart(drawOption: L7DrawConfig) { + const { chart, container } = drawOption + const containerDom = document.getElementById(container) + const rect = containerDom?.getBoundingClientRect() + if (rect?.height <= 0) { + return new L7Wrapper(drawOption.chartObj?.getScene(), []) + } + const xAxis = deepCopy(chart.xAxis) + const xAxisExt = deepCopy(chart.xAxisExt) + const { basicStyle, misc } = deepCopy(parseJson(chart.customAttr)) + + let center: [number, number] = [ + DEFAULT_BASIC_STYLE.mapCenter.longitude, + DEFAULT_BASIC_STYLE.mapCenter.latitude + ] + if (basicStyle.autoFit === false) { + center = [basicStyle.mapCenter.longitude, basicStyle.mapCenter.latitude] + } + let mapStyle = basicStyle.mapStyleUrl + if (basicStyle.mapStyle !== 'custom') { + mapStyle = `amap://styles/${basicStyle.mapStyle ? basicStyle.mapStyle : 'normal'}` + } + const mapKey = await this.getMapKey() + // 底层 + const chartObj = drawOption.chartObj as unknown as L7Wrapper + let scene = chartObj?.getScene() + if(scene){ + if (scene.getLayers()?.length) { + await scene.removeAllLayer() + scene.setPitch(misc.mapPitch) + } + } + if (mapStyle.indexOf('Satellite') == -1) { + scene = new Scene({ + id: container, + logoVisible: false, + map: new GaodeMap({ + token: mapKey?.key ?? undefined, + style: mapStyle, + pitch: misc.mapPitch, + center, + zoom: basicStyle.autoFit === false ? basicStyle.zoomLevel : undefined, + showLabel: !(basicStyle.showLabel === false), + WebGLParams: { + preserveDrawingBuffer: true + } + }) + }) + }else{ + scene = new Scene({ + id: container, + logoVisible: false, + map: new GaodeMap({ + token: mapKey?.key ?? undefined, + style: mapStyle, + features: ['bg', 'road'], // 必须开启路网层 + plugin: ['AMap.TileLayer.Satellite'], // 显式声明卫星图层 + WebGLParams: { + preserveDrawingBuffer: true + } + }) + }) + } + // if (!scene) { + // scene = new Scene({ + // id: container, + // logoVisible: false, + // map: new GaodeMap({ + // token: mapKey?.key ?? undefined, + // style: mapStyle, + // pitch: misc.mapPitch, + // center: basicStyle.autoFit === false ? center : undefined, + // zoom: basicStyle.autoFit === false ? basicStyle.zoomLevel : undefined, + // showLabel: !(basicStyle.showLabel === false), + // WebGLParams: { + // preserveDrawingBuffer: true + // } + // }) + // }) + // } else { + // if (scene.getLayers()?.length) { + // await scene.removeAllLayer() + // scene.setPitch(misc.mapPitch) + // scene.setMapStyle(mapStyle) + // scene.map.showLabel = !(basicStyle.showLabel === false) + // } + // if (basicStyle.autoFit === false) { + // scene.setZoomAndCenter(basicStyle.zoomLevel, center) + // } + // } + mapRendering(container) + if (mapStyle.indexOf('Satellite') == -1) { + scene.once('loaded', () => { + mapRendered(container) + }) + } else { + scene.once('loaded', () => { + // 创建卫星图层实例 + const satelliteLayer = new AMap.TileLayer.Satellite() + // 与矢量图层叠加显示 + satelliteLayer.setMap(scene.map) + mapRendered(container) + }) + } + // scene.once('loaded', () => { + // mapRendered(container) + // }) + this.configZoomButton(chart, scene) + if (xAxis?.length < 2 || xAxisExt?.length < 2) { + return new L7Wrapper(scene, undefined) + } + const configList = [] + configList.push(this.lineConfig(chart, xAxis, xAxisExt, basicStyle, misc)) + this.startAndEndNameConfig(chart, xAxis, xAxisExt, misc, configList) + this.pointConfig(chart, xAxis, xAxisExt, misc, configList) + configList[0].once('inited', () => { + mapRendered(container) + }) + return new L7Wrapper(scene, configList) + } + + lineConfig = (chart, xAxis, xAxisExt, basicStyle, misc) => { + const flowLineStyle = { + type: misc.flowMapConfig.lineConfig.mapLineType, + size: + misc.flowMapConfig.lineConfig.mapLineType === 'line' + ? misc.flowMapConfig.lineConfig.mapLineWidth / 2 + : misc.flowMapConfig.lineConfig.mapLineWidth, + animate: misc.flowMapConfig.lineConfig.mapLineAnimate, + animateDuration: misc.flowMapConfig.lineConfig.mapLineAnimateDuration, + gradient: misc.flowMapConfig.lineConfig.mapLineGradient, + sourceColor: misc.flowMapConfig.lineConfig.mapLineSourceColor, + targetColor: misc.flowMapConfig.lineConfig.mapLineTargetColor, + alpha: misc.flowMapConfig.lineConfig.alpha + } + const colorsWithAlpha = basicStyle.colors.map(color => + hexColorToRGBA(color, misc.flowMapConfig.lineConfig.alpha) + ) + flowLineStyle.sourceColor = colorsWithAlpha[0] + flowLineStyle.targetColor = colorsWithAlpha[1] + // 线条粗细 + let lineWidthField = null + const yAxis = deepCopy(chart.yAxis) + if (yAxis.length > 0) { + lineWidthField = yAxis[0].dataeaseName + } + // 线条颜色 + let lineColorField = null + const yAxisExt = deepCopy(chart.yAxisExt) + if (yAxisExt.length > 0) { + lineColorField = yAxisExt[0].dataeaseName + } + const asteriskField = '*' + const data = [] + chart.data?.tableRow.forEach(item => { + const newKey = 'f_record' + const newObj = Object.keys(item).reduce((acc, key) => { + if (key === asteriskField) { + acc[newKey] = item[key] + } else { + acc[key] = item[key] + } + return acc + }, {}) + data.push(newObj) + }) + const config: L7Config = new LineLayer({ + name: 'line', + blend: 'normal', + autoFit: !(basicStyle.autoFit === false) + }) + .source(data, { + parser: { + type: 'json', + x: xAxis[0].dataeaseName, + y: xAxis[1].dataeaseName, + x1: xAxisExt[0].dataeaseName, + y1: xAxisExt[1].dataeaseName + } + }) + .size(flowLineStyle.size) + .shape(flowLineStyle.type) + .animate({ + enable: flowLineStyle.animate, + duration: flowLineStyle.animateDuration, + interval: 1, + trailLength: 1 + }) + + if (lineWidthField) { + config.size(lineWidthField === asteriskField ? 'f_record' : lineWidthField, [1, 10]) + } + if (lineColorField) { + config.style({ + opacity: flowLineStyle.alpha / 100 + }) + config.color(lineColorField) + } else { + if (flowLineStyle.gradient) { + config.style({ + sourceColor: flowLineStyle.sourceColor, + targetColor: flowLineStyle.targetColor, + opacity: flowLineStyle.alpha / 100 + }) + } else { + config + .style({ + opacity: flowLineStyle.alpha / 100 + }) + .color(flowLineStyle.sourceColor) + } + } + + return config + } + + startAndEndNameConfig = (chart, xAxis, xAxisExt, misc, configList) => { + const flowMapStartName = deepCopy(chart.flowMapStartName) + const flowMapEndName = deepCopy(chart.flowMapEndName) + const textColor = misc.flowMapConfig.pointConfig.text.color + const textFontSize = misc.flowMapConfig.pointConfig.text.fontSize + const has = new Map() + if (flowMapStartName?.length > 0) { + const startTextLayer = new PointLayer() + .source(chart.data?.tableRow, { + parser: { + type: 'json', + x: xAxis[0].dataeaseName, + y: xAxis[1].dataeaseName + } + }) + .shape(flowMapStartName[0].dataeaseName, args => { + if (has.has('from-' + args)) { + return '' + } + has.set('from-' + args, args) + return args + }) + .size(textFontSize) + .color(textColor) + .style({ + textAnchor: 'top', // 文本相对锚点的位置 center|left|right|top|bottom|top-left + textOffset: [0, 0], // 文本相对锚点的偏移量 [水平, 垂直] + spacing: 2, // 字符间距 + padding: [1, 1], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近 + textAllowOverlap: true, + fontFamily: chart.fontFamily ? chart.fontFamily : undefined + }) + configList.push(startTextLayer) + } + if (flowMapEndName?.length > 0) { + const endTextLayer = new PointLayer() + .source(chart.data?.tableRow, { + parser: { + type: 'json', + x: xAxisExt[0].dataeaseName, + y: xAxisExt[1].dataeaseName + } + }) + .shape(flowMapEndName[0].dataeaseName, args => { + if (has.has('from-' + args) || has.has('to-' + args)) { + return '' + } + has.set('to-' + args, args) + return args + }) + .size(textFontSize) + .color(textColor) + .style({ + textAnchor: 'top', // 文本相对锚点的位置 center|left|right|top|bottom|top-left + textOffset: [0, 0], // 文本相对锚点的偏移量 [水平, 垂直] + spacing: 2, // 字符间距 + padding: [1, 1], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近 + textAllowOverlap: true, + fontFamily: chart.fontFamily ? chart.fontFamily : undefined + }) + configList.push(endTextLayer) + } + } + + pointConfig = (chart, xAxis, xAxisExt, misc, configList) => { + const color = misc.flowMapConfig.pointConfig.point.color + const size = misc.flowMapConfig.pointConfig.point.size + const { bubbleCfg } = parseJson(chart.senior) + const fromDefaultPointLayer = new PointLayer({ zIndex: -1 }) + .source(chart.data?.tableRow, { + parser: { + type: 'json', + x: xAxis[0].dataeaseName, + y: xAxis[1].dataeaseName + } + }) + .shape('circle') + .size(size) + .color(color) + .style({ + blur: 0.6 + }) + const toDefaultPointLayer = new PointLayer({ zIndex: -1 }) + .source(chart.data?.tableRow, { + parser: { + type: 'json', + x: xAxisExt[0].dataeaseName, + y: xAxisExt[1].dataeaseName + } + }) + .shape('circle') + .size(size) + .color(color) + .style({ + blur: 0.6 + }) + if (bubbleCfg && bubbleCfg.enable) { + const animate = { + enable: true, + speed: bubbleCfg.speed, + rings: bubbleCfg.rings + } + fromDefaultPointLayer.size(size * 2) + fromDefaultPointLayer.animate(animate) + toDefaultPointLayer.size(size * 2) + toDefaultPointLayer.animate(animate) + } + configList.push(fromDefaultPointLayer) + configList.push(toDefaultPointLayer) + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + chart.customAttr.misc.flowMapConfig.lineConfig.mapLineAnimate = true + return chart + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/map/heat-map.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/map/heat-map.ts new file mode 100644 index 0000000..3701bbd --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/map/heat-map.ts @@ -0,0 +1,186 @@ +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { + L7ChartView, + L7Config, + L7DrawConfig, + L7Wrapper +} from '@/data-visualization/chart/components/js/panel/types/impl/l7' +import { MAP_EDITOR_PROPERTY_INNER } from '@/data-visualization/chart/components/js/panel/charts/map/common' +import { flow, parseJson } from '@/data-visualization/chart/components/js/util' +import { deepCopy } from '@/data-visualization/utils/utils' +import { GaodeMap } from '@antv/l7-maps' +import { Scene } from '@antv/l7-scene' +import { HeatmapLayer } from '@antv/l7-layers' +import { DEFAULT_BASIC_STYLE } from '@/data-visualization/chart/components/editor/util/chart' +import { mapRendered, mapRendering } from '@/data-visualization/chart/components/js/panel/common/common_antv' +const { t } = useI18n() + +/** + * 流向地图 + */ +export class HeatMap extends L7ChartView { + properties: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'title-selector' + ] + propertyInner: EditorPropertyInner = { + ...MAP_EDITOR_PROPERTY_INNER, + 'basic-style-selector': [ + 'colors', + 'heatMapStyle', + 'zoom', + 'showLabel', + 'autoFit', + 'mapCenter', + 'zoomLevel' + ] + } + axis: AxisType[] = ['xAxis', 'yAxis', 'filter'] + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.longitude_and_latitude')} / ${t('chart.dimension')}`, + type: 'd', + limit: 2 + }, + yAxis: { + name: `${t('chart.chart_data')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + } + } + constructor() { + super('heat-map', []) + } + + async drawChart(drawOption: L7DrawConfig) { + const { chart, container } = drawOption + const containerDom = document.getElementById(container) + const rect = containerDom?.getBoundingClientRect() + if (rect?.height <= 0) { + return new L7Wrapper(drawOption.chartObj?.getScene(), []) + } + const xAxis = deepCopy(chart.xAxis) + const yAxis = deepCopy(chart.yAxis) + let basicStyle: DeepPartial + let miscStyle: DeepPartial + if (chart.customAttr) { + basicStyle = parseJson(chart.customAttr).basicStyle + miscStyle = parseJson(chart.customAttr).misc + } + let center: [number, number] = [ + DEFAULT_BASIC_STYLE.mapCenter.longitude, + DEFAULT_BASIC_STYLE.mapCenter.latitude + ] + if (basicStyle.autoFit === false) { + center = [basicStyle.mapCenter.longitude, basicStyle.mapCenter.latitude] + } + let mapStyle = basicStyle.mapStyleUrl + if (basicStyle.mapStyle !== 'custom') { + mapStyle = `amap://styles/${basicStyle.mapStyle ? basicStyle.mapStyle : 'normal'}` + } + const mapKey = await this.getMapKey() + // 底层 + const chartObj = drawOption.chartObj as unknown as L7Wrapper + let scene = chartObj?.getScene() + if(scene){ + if (scene.getLayers()?.length) { + await scene.removeAllLayer() + scene.setPitch(miscStyle.mapPitch) + } + } + + if (mapStyle.indexOf('Satellite') == -1) { + scene = new Scene({ + id: container, + logoVisible: false, + map: new GaodeMap({ + token: mapKey?.key ?? undefined, + style: mapStyle, + pitch: miscStyle.mapPitch, + center, + zoom: basicStyle.autoFit === false ? basicStyle.zoomLevel : undefined, + showLabel: !(basicStyle.showLabel === false), + WebGLParams: { + preserveDrawingBuffer: true + } + }) + }) + } else { + scene = new Scene({ + id: container, + logoVisible: false, + map: new GaodeMap({ + token: mapKey?.key ?? undefined, + style: mapStyle, + features: ['bg', 'road'], // 必须开启路网层 + plugin: ['AMap.TileLayer.Satellite'], // 显式声明卫星图层 + WebGLParams: { + preserveDrawingBuffer: true + } + }) + }) + } + + // } else { + // // if (scene.getLayers()?.length) { + // await scene.removeAllLayer() + // scene.setPitch(miscStyle.mapPitch) + // scene.setMapStyle(mapStyle) + // scene.map.showLabel = !(basicStyle.showLabel === false) + // if (basicStyle.autoFit === false) { + // scene.setZoomAndCenter(basicStyle.zoomLevel, center) + // } + // // } + // } + mapRendering(container) + if (mapStyle.indexOf('Satellite') == -1) { + scene.once('loaded', () => { + mapRendered(container) + }) + } else { + scene.once('loaded', () => { + // 创建卫星图层实例 + const satelliteLayer = new AMap.TileLayer.Satellite() + // 与矢量图层叠加显示 + satelliteLayer.setMap(scene.map) + mapRendered(container) + }) + } + + this.configZoomButton(chart, scene) + if (xAxis?.length < 2 || yAxis?.length < 1) { + return new L7Wrapper(scene, undefined) + } + const config: L7Config = new HeatmapLayer({ + name: 'line', + blend: 'normal', + autoFit: !(basicStyle.autoFit === false) + }) + .source(chart.data?.data, { + parser: { + type: 'json', + x: 'x', + y: 'y' + } + }) + .size('value', [0, 1.0]) // weight映射通道 + .shape(basicStyle.heatMapType ?? DEFAULT_BASIC_STYLE.heatMapType) + + config.style({ + intensity: basicStyle.heatMapIntensity ?? DEFAULT_BASIC_STYLE.heatMapIntensity, + radius: basicStyle.heatMapRadius ?? DEFAULT_BASIC_STYLE.heatMapRadius, + rampColors: { + colors: basicStyle.colors.reverse(), + positions: [0, 0.11, 0.22, 0.33, 0.44, 0.55, 0.66, 0.77, 0.88, 1.0] + } + }) + + return new L7Wrapper(scene, config) + } + + protected setupOptions(chart: Chart, config: L7Config): L7Config { + return flow(this.configEmptyDataStrategy)(chart, config) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/map/map.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/map/map.ts new file mode 100644 index 0000000..51ceea3 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/map/map.ts @@ -0,0 +1,617 @@ +import { + L7PlotChartView, + L7PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/l7plot' +import type { Choropleth, ChoroplethOptions } from '@antv/l7plot/dist/esm/plots/choropleth' +import { + filterChartDataByRange, + flow, + getDynamicColorScale, + getGeoJsonFile, + hexColorToRGBA, + parseJson, + getMaxAndMinValueByData, + filterEmptyMinValue +} from '@/data-visualization/chart/components/js/util' +import { + handleGeoJson, + mapRendered, + mapRendering +} from '@/data-visualization/chart/components/js/panel/common/common_antv' +import type { FeatureCollection } from '@antv/l7plot/dist/esm/plots/choropleth/types' +import { cloneDeep, defaultsDeep, isEmpty } from 'lodash-es' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { valueFormatter } from '../../../formatter' +import { + MAP_AXIS_TYPE, + MAP_EDITOR_PROPERTY, + MAP_EDITOR_PROPERTY_INNER, + MapMouseEvent +} from '@/data-visualization/chart/components/js/panel/charts/map/common' +import type { CategoryLegendListItem } from '@antv/l7plot-component/dist/lib/types/legend' +import createDom from '@antv/dom-util/esm/create-dom' +import { + CONTAINER_TPL, + ITEM_TPL, + LIST_CLASS +} from '@antv/l7plot-component/dist/esm/legend/category/constants' +import substitute from '@antv/util/esm/substitute' +import { configCarouselTooltip } from '@/data-visualization/chart/components/js/panel/charts/map/tooltip-carousel' +import { getCustomGeoArea } from '@/api/data-visualization/map' +import { centroid } from '@turf/centroid' +import { TextLayer } from '@antv/l7plot/dist/esm' + +const { t } = useI18n() + +/** + * 地图 + */ +export class Map extends L7PlotChartView { + properties: EditorProperty[] = [...MAP_EDITOR_PROPERTY, 'legend-selector'] + propertyInner: EditorPropertyInner = { + ...MAP_EDITOR_PROPERTY_INNER, + 'basic-style-selector': [ + 'colors', + 'alpha', + 'areaBorderColor', + 'areaBaseColor', + 'zoom', + 'gradient-color' + ], + 'legend-selector': ['icon', 'fontSize', 'color'], + 'tooltip-selector': [...MAP_EDITOR_PROPERTY_INNER['tooltip-selector'], 'carousel'] + } + axis = MAP_AXIS_TYPE + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.area')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1 + }, + yAxis: { + name: `${t('chart.chart_data')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + } + } + + constructor() { + super('map', []) + } + + async drawChart(drawOption: L7PlotDrawOptions): Promise { + const { chart, level, container, action, scope } = drawOption + const { areaId } = drawOption + if (!areaId) { + return + } + chart.container = container + let sourceData = JSON.parse(JSON.stringify(chart.data?.data || [])) + const { misc } = parseJson(chart.customAttr) + const { legend } = parseJson(chart.customStyle) + let geoJson = {} as FeatureCollection + // 自定义区域,去除非区域数据,优先级最高 + let customSubArea: CustomGeoSubArea[] = [] + if (areaId.startsWith('custom_')) { + customSubArea = (await getCustomGeoArea(areaId)).data || [] + geoJson = cloneDeep(await getGeoJsonFile('156')) + const areaNameMap = geoJson.features.reduce((p, n) => { + p['156' + n.properties.adcode] = n.properties.name + return p + }, {}) + const { areaMapping } = parseJson(chart.senior) + const areaMap = customSubArea.reduce((p, n) => { + const mappedName = areaMapping?.[areaId]?.[n.name] + if (mappedName) { + n.name = mappedName + } + p[n.name] = n + n.scopeArr = n.scope?.split(',') || [] + return p + }, {}) + const fakeData = [] + sourceData.forEach(d => { + const area = areaMap[d.name] + if (area) { + area.scopeArr.forEach(adcode => { + fakeData.push({ + ...d, + name: areaNameMap[adcode], + field: areaNameMap[adcode], + scope: area.scopeArr, + areaName: d.name + }) + }) + } + }) + sourceData = fakeData + } else { + if (scope) { + geoJson = cloneDeep(await getGeoJsonFile('156')) + geoJson.features = geoJson.features.filter(f => scope.includes('156' + f.properties.adcode)) + } else { + geoJson = cloneDeep(await getGeoJsonFile(areaId)) + } + } + let data = [] + // 自定义图例 + if (!misc.mapAutoLegend && legend.show) { + let minValue = misc.mapLegendMin + let maxValue = misc.mapLegendMax + let legendNumber = 9 + if (misc.mapLegendRangeType === 'custom') { + maxValue = 0 + minValue = 0 + legendNumber = misc.mapLegendNumber + } + getMaxAndMinValueByData(sourceData, 'value', maxValue, minValue, (max, min) => { + maxValue = max + minValue = min + action({ + from: 'map', + data: { + max: maxValue, + min: minValue ?? filterEmptyMinValue(sourceData, 'value'), + legendNumber: legendNumber + } + }) + }) + data = filterChartDataByRange(sourceData, maxValue, minValue) + } else { + data = sourceData + } + let options: ChoroplethOptions = { + preserveDrawingBuffer: true, + map: { + type: 'mapbox', + style: 'blank' + }, + geoArea: { + type: 'geojson' + }, + source: { + data: data, + joinBy: { + sourceField: 'name', + geoField: 'name', + geoData: geoJson + } + }, + viewLevel: { + level, + adcode: 'all' + }, + autoFit: true, + chinaBorder: false, + color: { + field: 'value' + }, + style: { + opacity: 1, + lineWidth: 0.6, + lineOpacity: 1 + }, + label: { + field: '_DE_LABEL_', + style: { + textAnchor: 'center' + } + }, + state: { + active: { stroke: 'green', lineWidth: 1 } + }, + tooltip: {}, + // 禁用线上地图数据 + customFetchGeoData: () => null + } + const context: Record = { drawOption, geoJson, customSubArea } + options = this.setupOptions(chart, options, context) + const { Choropleth } = await import('@antv/l7plot/dist/esm/plots/choropleth') + const view = new Choropleth(container, options) + this.configZoomButton(chart, view) + mapRendering(container) + view.once('loaded', () => { + mapRendered(container) + const { layers } = context + if (layers) { + layers.forEach(l => { + view.addLayer(l) + }) + } + view.scene.map['keyboard'].disable() + view.on('fillAreaLayer:click', (ev: MapMouseEvent) => { + const data = ev.feature.properties + if (areaId.startsWith('custom_')) { + data.name = data.areaName + data.adcode = '156' + } + action({ + x: ev.x, + y: ev.y, + data: { + data, + extra: { adcode: data.adcode, scope: data.scope } + } + }) + }) + chart.container = container + configCarouselTooltip(chart, view, data, null, customSubArea, drawOption) + }) + return view + } + + private configBasicStyle( + chart: Chart, + options: ChoroplethOptions, + context: Record + ): ChoroplethOptions { + const { areaId }: L7PlotDrawOptions = context.drawOption + const geoJson: FeatureCollection = context.geoJson + const { basicStyle, label, misc } = parseJson(chart.customAttr) + const senior = parseJson(chart.senior) + const curAreaNameMapping = senior.areaMapping?.[areaId] + handleGeoJson(geoJson, curAreaNameMapping) + options.color = { + field: 'value', + value: [basicStyle.colors[0]], + scale: { + type: 'quantize', + unknown: basicStyle.areaBaseColor + } + } + if (!chart.data?.data?.length || !geoJson?.features?.length) { + options.label && (options.label.field = 'name') + return options + } + const sourceData = options.source.data + const colors = basicStyle.colors.map(item => hexColorToRGBA(item, basicStyle.alpha)) + const { legend } = parseJson(chart.customStyle) + let data = sourceData + let colorScale = [] + let minValue = misc.mapLegendMin + let maxValue = misc.mapLegendMax + let mapLegendNumber = misc.mapLegendNumber + if (legend.show) { + getMaxAndMinValueByData(sourceData, 'value', maxValue, minValue, (max, min) => { + maxValue = max + minValue = min + mapLegendNumber = 9 + }) + // 非自动,过滤数据 + if (!misc.mapAutoLegend) { + data = filterChartDataByRange(sourceData, maxValue, minValue) + } else { + mapLegendNumber = 9 + } + // 定义最大值、最小值、区间数量和对应的颜色 + colorScale = getDynamicColorScale(minValue, maxValue, mapLegendNumber, colors) + } else { + colorScale = colors + } + const areaMap = data.reduce((obj, value) => { + obj[value['field']] = value.value + return obj + }, {}) + geoJson.features.forEach(item => { + const name = item.properties['name'] + // trick, maybe move to configLabel, here for perf + if (label.show) { + const content = [] + if (label.showDimension) { + content.push(name) + } + if (label.showQuota) { + areaMap[name] && content.push(valueFormatter(areaMap[name], label.quotaLabelFormatter)) + } + item.properties['_DE_LABEL_'] = content.join('\n\n') + } + }) + if (colorScale.length) { + options.color['value'] = colorScale.map(item => (item.color ? item.color : item)) + if (colorScale[0].value && !misc.mapAutoLegend) { + options.color['scale']['domain'] = [ + minValue ?? filterEmptyMinValue(sourceData, 'value'), + maxValue + ] + } + } + return options + } + + // 内部函数 创建自定义图例的内容 + private createLegendCustomContent = showItems => { + const containerDom = createDom(CONTAINER_TPL) as HTMLElement + const listDom = containerDom.getElementsByClassName(LIST_CLASS)[0] as HTMLElement + showItems.forEach(item => { + let value = '-' + if (item.value !== '') { + if (Array.isArray(item.value)) { + item.value.forEach((v, i) => { + item.value[i] = Number.isNaN(v) || v === 'NaN' ? 'NaN' : parseFloat(v).toFixed(0) + }) + value = item.value.join('-') + } else { + const tmp = item.value as string + value = Number.isNaN(tmp) || tmp === 'NaN' ? 'NaN' : parseFloat(tmp).toFixed(0) + } + } + const substituteObj = { ...item, value } + + const domStr = substitute(ITEM_TPL, substituteObj) + const itemDom = createDom(domStr) + // 给 legend 形状用的 + itemDom.style.setProperty('--bgColor', item.color) + listDom.appendChild(itemDom) + }) + return listDom + } + + private customConfigLegend( + chart: Chart, + options: ChoroplethOptions, + context: Record + ): ChoroplethOptions { + const { basicStyle, misc } = parseJson(chart.customAttr) + const colors = basicStyle.colors.map(item => hexColorToRGBA(item, basicStyle.alpha)) + if (basicStyle.suspension === false && basicStyle.showZoom === undefined) { + return options + } + const { legend } = parseJson(chart.customStyle) + if (!legend.show) { + return options + } + const LEGEND_SHAPE_STYLE_MAP = { + circle: { + borderRadius: '50%' + }, + square: {}, + triangle: { + border: 'unset', + borderLeft: '5px solid transparent', + borderRight: '5px solid transparent', + borderBottom: '10px solid var(--bgColor)', + background: 'unset' + }, + diamond: { + transform: 'rotate(45deg)' + } + } + const customLegend = { + position: 'bottomleft', + domStyles: { + 'l7plot-legend__category-value': { + fontSize: legend.fontSize + 'px', + color: legend.color, + 'font-family': chart.fontFamily ? chart.fontFamily : undefined + }, + 'l7plot-legend__category-marker': { + ...LEGEND_SHAPE_STYLE_MAP[legend.icon], + width: legend.size + 'px', + height: legend.size + 'px', + ...(legend.icon === 'triangle' + ? { + ...LEGEND_SHAPE_STYLE_MAP[legend.icon]['triangle'], + borderLeft: `${legend.size / 2}px solid transparent`, + borderRight: `${legend.size / 2}px solid transparent`, + borderBottom: `${legend.size}px solid var(--bgColor)` + } + : { border: '0.01px solid #f4f4f4' }), + ...(legend.icon === 'diamond' + ? { + transform: 'rotate(45deg)', + marginBottom: `${legend.size / 4}px` + } + : {}) + } + } + } + // 不是自动图例、自定义图例区间、不是下钻时 + if (!misc.mapAutoLegend && misc.mapLegendRangeType === 'custom' && !chart.drill) { + // 获取图例区间数据 + const items = [] + // 区间数组 + const ranges = misc.mapLegendCustomRange + .slice(0, -1) + .map((item, index) => [item, misc.mapLegendCustomRange[index + 1]]) + ranges.forEach((range, index) => { + const tmpRange = [range[0], range[1]] + const colorIndex = index % colors.length + // 当区间第一个值小于最小值时,颜色取地图底色 + const isLessThanMin = range[0] < ranges[0][0] && range[1] < ranges[0][0] + let rangeColor = colors[colorIndex] + if (isLessThanMin) { + rangeColor = hexColorToRGBA(basicStyle.areaBaseColor, basicStyle.alpha) + } + items.push({ + value: tmpRange, + color: rangeColor + }) + }) + customLegend['customContent'] = (_: string, _items: CategoryLegendListItem[]) => { + if (items?.length) { + return this.createLegendCustomContent(items) + } + return '' + } + options.color['value'] = ({ value }) => { + const item = items.find(item => value >= item.value[0] && value <= item.value[1]) + return item ? item.color : hexColorToRGBA(basicStyle.areaBaseColor, basicStyle.alpha) + } + options.color.scale.domain = [ranges[0][0], ranges[ranges.length - 1][1]] + } else { + customLegend['customContent'] = (_: string, items: CategoryLegendListItem[]) => { + const showItems = items?.length > 30 ? items.slice(0, 30) : items + if (showItems?.length) { + return this.createLegendCustomContent(showItems) + } + return '' + } + } + // 下钻时按照数据值计算图例 + if (chart.drill) { + getMaxAndMinValueByData(options.source.data, 'value', 0, 0, (max, min) => { + options.color.scale.domain = [min, max] + }) + } + defaultsDeep(options, { legend: customLegend }) + return options + } + + protected configCustomArea( + chart: Chart, + options: ChoroplethOptions, + context: Record + ): ChoroplethOptions { + const { drawOption, customSubArea, geoJson } = context + if (!drawOption.areaId.startsWith('custom_')) { + return options + } + const customAttr = parseJson(chart.customAttr) + const { label } = customAttr + const data = chart.data.data + const areaMap = data?.reduce((obj, value) => { + obj[value['field']] = value + return obj + }, {}) + const geoJsonMap = geoJson.features.reduce((p, n) => { + if (n.properties['adcode']) { + p['156' + n.properties['adcode']] = n + } + return p + }, {}) + customSubArea.forEach(area => { + const areaJsonArr = [] + area.scopeArr?.forEach(adcode => { + const json = geoJsonMap[adcode] + json && areaJsonArr.push(json) + }) + if (areaJsonArr.length) { + const areaJson: FeatureCollection = { + type: 'FeatureCollection', + features: areaJsonArr + } + const center = centroid(areaJson) + // 轮播用 + area.centroid = [center.geometry.coordinates[0], center.geometry.coordinates[1]] + } + }) + //处理label + options.label = { + visible: false + } + if (label.show) { + const labelLocation = [] + customSubArea.forEach(area => { + if (area.centroid) { + const content = [] + if (label.showDimension) { + content.push(area.name) + } + if (label.showQuota) { + areaMap[area.name] && + content.push(valueFormatter(areaMap[area.name].value, label.quotaLabelFormatter)) + } + labelLocation.push({ + name: content.join('\n\n'), + x: area.centroid[0], + y: area.centroid[1] + }) + } + }) + const areaLabelLayer = new TextLayer({ + name: 'areaLabelLayer', + source: { + data: labelLocation, + parser: { + type: 'json', + x: 'x', + y: 'y' + } + }, + field: 'name', + style: { + fill: label.color, + fontSize: label.fontSize, + opacity: 1, + fontWeight: 'bold', + textAnchor: 'center', + textAllowOverlap: label.fullDisplay, + padding: !label.fullDisplay ? [2, 2] : undefined + } + }) + context.layers = [areaLabelLayer] + } + // 处理tooltip + const subAreaMap = customSubArea.reduce((p, n) => { + n.scopeArr.forEach(a => { + p[a] = n.name + }) + return p + }, {}) + if (options.tooltip && options.tooltip.showComponent) { + options.tooltip.items = ['name', 'adcode', 'value'] + options.tooltip.customTitle = ({ name, adcode }) => { + adcode = '156' + adcode + return subAreaMap[adcode] ?? name + } + const tooltip = customAttr.tooltip + const formatterMap = tooltip.seriesTooltipFormatter + ?.filter(i => i.show) + .reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) as Record + options.tooltip.customItems = originalItem => { + const result = [] + if (isEmpty(formatterMap)) { + return result + } + const head = originalItem.properties + const { adcode } = head + const areaName = subAreaMap['156' + adcode] + const valItem = areaMap[areaName] + if (!valItem) { + return result + } + const formatter = formatterMap[valItem.quotaList?.[0]?.id] + if (!isEmpty(formatter)) { + const originValue = parseFloat(valItem.value as string) + const value = valueFormatter(originValue, formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ ...valItem, name, value: `${value ?? ''}` }) + } + valItem.dynamicTooltipValue?.forEach(item => { + const formatter = formatterMap[item.fieldId] + if (formatter) { + const value = valueFormatter(parseFloat(item.value), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ color: 'grey', name, value: `${value ?? ''}` }) + } + }) + return result + } + } + return options + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + chart.customAttr.basicStyle.areaBaseColor = '#f4f4f4' + return chart + } + + protected setupOptions( + chart: Chart, + options: ChoroplethOptions, + context: Record + ): ChoroplethOptions { + return flow( + this.configEmptyDataStrategy, + this.configLabel, + this.configStyle, + this.configTooltip, + this.configBasicStyle, + this.customConfigLegend, + this.configCustomArea + )(chart, options, context, this) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/map/symbolic-map.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/map/symbolic-map.ts new file mode 100644 index 0000000..8e16a7a --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/map/symbolic-map.ts @@ -0,0 +1,645 @@ +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { + L7ChartView, + L7Config, + L7DrawConfig, + L7Wrapper +} from '@/data-visualization/chart/components/js/panel/types/impl/l7' +import { MAP_EDITOR_PROPERTY_INNER } from '@/data-visualization/chart/components/js/panel/charts/map/common' +import { + getColorFormAlphaColor, + hexColorToRGBA, + parseJson, + svgStrToUrl +} from '@/data-visualization/chart/components/js/util' +import { deepCopy } from '@/data-visualization/utils/utils' +import { GaodeMap } from '@antv/l7-maps' +import { Scene } from '@antv/l7-scene' +import { PointLayer } from '@antv/l7-layers' +import { LayerPopup, Popup } from '@antv/l7' +import { mapRendered, mapRendering } from '@/data-visualization/chart/components/js/panel/common/common_antv' +import { configCarouselTooltip } from '@/data-visualization/chart/components/js/panel/charts/map/tooltip-carousel' +import { DEFAULT_BASIC_STYLE } from '@/data-visualization/chart/components/editor/util/chart' +import { filter } from 'lodash-es' +const { t } = useI18n() + +/** + * 符号地图 + */ +export class SymbolicMap extends L7ChartView { + properties: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'symbolic-style-selector', + 'title-selector', + 'label-selector', + 'tooltip-selector', + 'threshold' + ] + propertyInner: EditorPropertyInner = { + ...MAP_EDITOR_PROPERTY_INNER, + 'basic-style-selector': [ + 'colors', + 'alpha', + 'mapBaseStyle', + 'zoom', + 'showLabel', + 'autoFit', + 'mapCenter', + 'zoomLevel' + ], + 'symbolic-style-selector': ['symbolicMapStyle'], + 'label-selector': ['color', 'fontSize', 'showFields', 'customContent'], + 'tooltip-selector': [ + 'color', + 'fontSize', + 'showFields', + 'customContent', + 'show', + 'backgroundColor', + 'carousel' + ], + threshold: ['lineThreshold'] + } + axis: AxisType[] = ['xAxis', 'xAxisExt', 'extBubble', 'filter', 'extLabel', 'extTooltip'] + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.symbolic_map_coordinates')} / ${t('chart.dimension')}`, + type: 'd', + limit: 2 + }, + xAxisExt: { + name: `${t('chart.color')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1, + allowEmpty: true + }, + extBubble: { + name: `${t('chart.bubble_size')} / ${t('chart.quota')}`, + type: 'q', + limit: 1, + tooltip: t('chart.symbolic_map_bubble_size_tip'), + allowEmpty: true + } + } + constructor() { + super('symbolic-map', []) + } + + async drawChart(drawOption: L7DrawConfig) { + const { chart, container, action } = drawOption + const containerDom = document.getElementById(container) + const rect = containerDom?.getBoundingClientRect() + if (rect?.height <= 0) { + return new L7Wrapper(drawOption.chartObj?.getScene(), []) + } + const xAxis = deepCopy(chart.xAxis) + let basicStyle + let miscStyle + if (chart.customAttr) { + basicStyle = parseJson(chart.customAttr).basicStyle + miscStyle = parseJson(chart.customAttr).misc + } + + let mapStyle = basicStyle.mapStyleUrl + if (basicStyle.mapStyle !== 'custom') { + mapStyle = `amap://styles/${basicStyle.mapStyle ? basicStyle.mapStyle : 'normal'}` + } + const mapKey = await this.getMapKey() + let center: [number, number] = [ + DEFAULT_BASIC_STYLE.mapCenter.longitude, + DEFAULT_BASIC_STYLE.mapCenter.latitude + ] + if (basicStyle.autoFit === false) { + center = [basicStyle.mapCenter.longitude, basicStyle.mapCenter.latitude] + } + // 联动时,聚焦到数据点,多个取第一个 + if ( + chart.chartExtRequest?.linkageFilters?.length && + xAxis?.length === 2 && + chart.data?.tableRow.length + ) { + // 经度 + const lng = chart.data?.tableRow?.[0][chart.xAxis[0].dataeaseName] + // 纬度 + const lat = chart.data?.tableRow?.[0][chart.xAxis[1].dataeaseName] + center = [lng, lat] + } + const chartObj = drawOption.chartObj as unknown as L7Wrapper + let scene = chartObj?.getScene() + if (!scene) { + scene = new Scene({ + id: container, + logoVisible: false, + map: new GaodeMap({ + token: mapKey?.key ?? undefined, + style: mapStyle, + pitch: miscStyle.mapPitch, + center, + zoom: basicStyle.autoFit === false ? basicStyle.zoomLevel : undefined, + showLabel: !(basicStyle.showLabel === false), + WebGLParams: { + preserveDrawingBuffer: true + } + }) + }) + } else { + if (scene.getLayers()?.length) { + await scene.removeAllLayer() + scene.setPitch(miscStyle.mapPitch) + scene.setMapStyle(mapStyle) + scene.map.showLabel = !(basicStyle.showLabel === false) + } + if (basicStyle.autoFit === false) { + scene.setZoomAndCenter(basicStyle.zoomLevel, center) + } + } + mapRendering(container) + scene.once('loaded', () => { + mapRendered(container) + }) + this.configZoomButton(chart, scene) + if (xAxis?.length < 2) { + return new L7Wrapper(scene, undefined) + } + const configList: L7Config[] = [] + const symbolicLayer = await this.buildSymbolicLayer(chart, scene) + configList.push(symbolicLayer) + const tooltipLayer = this.buildTooltip(chart, container, symbolicLayer, scene) + if (tooltipLayer) { + scene.addPopup(tooltipLayer) + } + this.buildLabel(chart, configList) + symbolicLayer.on('inited', () => { + chart.container = container + configCarouselTooltip(chart, symbolicLayer, symbolicLayer.sourceOption.data, scene) + }) + symbolicLayer.on('click', ev => { + const data = ev.feature + const dimensionList = [] + const quotaList = [] + chart.data.fields.forEach((item, index) => { + Object.keys(data).forEach(key => { + if (key.startsWith('f_') && item.dataeaseName === key) { + if (index === 0) { + dimensionList.push({ + id: item.id, + dataeaseName: item.dataeaseName, + value: data[key] + }) + } else { + quotaList.push({ + id: item.id, + dataeaseName: item.dataeaseName, + value: data[key] + }) + } + } + }) + }) + action({ + x: ev.x, + y: ev.y, + data: { + data: { + ...data, + value: quotaList[0].value, + name: dimensionList[0].id, + dimensionList: dimensionList, + quotaList: quotaList + } + } + }) + }) + + return new L7Wrapper(scene, configList) + } + + /** + * 构建符号图层 + * @param chart + */ + buildSymbolicLayer = async (chart, scene: Scene) => { + const { basicStyle } = parseJson(chart.customAttr) as ChartAttr + const xAxis = deepCopy(chart.xAxis) + const xAxisExt = deepCopy(chart.xAxisExt) + const extBubble = deepCopy(chart.extBubble) + const { + mapSymbolOpacity, + mapSymbolSize, + mapSymbol, + mapSymbolStrokeWidth, + colors, + alpha, + mapSymbolSizeMin, + mapSymbolSizeMax + } = deepCopy(basicStyle) + const colorsWithAlpha = colors.map(color => hexColorToRGBA(color, alpha)) + let colorIndex = 0 + // 存储已分配的颜色 + const colorAssignments = new Map() + const sizeKey = extBubble.length > 0 ? extBubble[0].dataeaseName : '' + + //条件颜色 + const { threshold } = parseJson(chart.senior) + let conditions = [] + if (threshold.enable) { + conditions = threshold.lineThreshold ?? [] + } + const extBubbleIds = chart.extBubble.map(i => i.id) + conditions = filter(conditions, c => extBubbleIds.includes(c.fieldId)) + + const baseColor = colorsWithAlpha[0] + const baseColorList = [] + + const data = chart.data?.tableRow + ? chart.data.tableRow.map((item, index) => { + item['_index'] = '_index' + index + // 颜色标识 + const identifier = item[xAxisExt[0]?.dataeaseName] + // 检查该标识是否已有颜色分配,如果没有则分配 + let color = colorAssignments.get(identifier) + if (!color) { + color = colorsWithAlpha[colorIndex++ % colorsWithAlpha.length] + // 记录分配的颜色 + colorAssignments.set(identifier, color) + } + + baseColorList[index] = color + + if (conditions.length > 0) { + for (let i = 0; i < conditions.length; i++) { + const c = conditions[i] + const value = item[c.field.dataeaseName] + for (const t of c.conditions) { + const v = t.value + + //保存一下颜色到map + const _color = getColorFormAlphaColor(t.color) + + if (t.term === 'between') { + const start = parseFloat(t.min) + const end = parseFloat(t.max) + if (start <= value && value <= end) { + color = hexColorToRGBA(_color, alpha) + baseColorList[index] = color + } + } else if ('lt' === t.term) { + if (value < v) { + color = hexColorToRGBA(_color, alpha) + baseColorList[index] = color + } + } else if ('le' === t.term) { + if (value <= v) { + color = hexColorToRGBA(_color, alpha) + baseColorList[index] = color + } + } else if ('gt' === t.term) { + if (value > v) { + color = hexColorToRGBA(_color, alpha) + baseColorList[index] = color + } + } else if ('ge' === t.term) { + if (value >= v) { + color = hexColorToRGBA(_color, alpha) + baseColorList[index] = color + } + } else if ('eq' === t.term) { + if (value === v) { + color = hexColorToRGBA(_color, alpha) + baseColorList[index] = color + } + } else if ('not_eq' === t.term) { + if (value !== v) { + color = hexColorToRGBA(_color, alpha) + baseColorList[index] = color + } + } + } + } + } + + return { + ...item, + color, + size: parseInt(item[sizeKey]) ?? mapSymbolSize, + name: identifier + } + }) + : [] + const pointLayer = new PointLayer({ autoFit: !(basicStyle.autoFit === false) }) + .source(data, { + parser: { + type: 'json', + x: xAxis[0].dataeaseName, + y: xAxis[1].dataeaseName + } + }) + .active(true) + if (xAxisExt[0]?.dataeaseName) { + if (basicStyle.mapSymbol === 'custom' && basicStyle.customIcon) { + // 图片无法改色 + if (basicStyle.customIcon.startsWith('data')) { + scene.removeImage('customIcon') + await scene.addImage('customIcon', basicStyle.customIcon) + pointLayer.shape('customIcon') + } else { + const parser = new DOMParser() + for (let index = 0; index < Math.min(baseColorList.length, colorIndex + 1); index++) { + const color = baseColorList[index] + const fillRegex = /(fill="[^"]*")/g + const svgStr = basicStyle.customIcon.replace(fillRegex, '') + const doc = parser.parseFromString(svgStr, 'image/svg+xml') + const svgEle = doc.documentElement + svgEle.setAttribute('fill', color) + scene.removeImage(`icon-${color}`) + await scene.addImage(`icon-${color}`, svgStrToUrl(svgEle.outerHTML)) + } + pointLayer.shape('color', c => { + return `icon-${c}` + }) + } + } else { + pointLayer.shape(mapSymbol).color('_index', baseColorList) + pointLayer.style({ + stroke: { + field: 'color' + }, + strokeWidth: mapSymbolStrokeWidth, + opacity: mapSymbolOpacity / 10 + }) + } + } else { + if (basicStyle.mapSymbol === 'custom' && basicStyle.customIcon) { + scene.removeImage('customIcon') + if (basicStyle.customIcon.startsWith('data')) { + await scene.addImage('customIcon', basicStyle.customIcon) + pointLayer.shape('customIcon') + } else { + const parser = new DOMParser() + const color = baseColor + const fillRegex = /(fill="[^"]*")/g + const svgStr = basicStyle.customIcon.replace(fillRegex, '') + const doc = parser.parseFromString(svgStr, 'image/svg+xml') + const svgEle = doc.documentElement + svgEle.setAttribute('fill', color) + await scene.addImage(`customIcon`, svgStrToUrl(svgEle.outerHTML)) + pointLayer.shape('customIcon') + } + } else { + pointLayer + .shape(mapSymbol) + .color('_index', baseColorList) + .style({ + stroke: { + field: 'color' + }, + strokeWidth: mapSymbolStrokeWidth, + opacity: mapSymbolOpacity / 10 + }) + } + } + if (sizeKey) { + pointLayer.size('size', [mapSymbolSizeMin, mapSymbolSizeMax]) + } else { + pointLayer.size(mapSymbolSize) + } + return pointLayer + } + + /** + * 合并详情到 map + * @param details + * @returns {Map} + */ + mergeDetailsToMap = details => { + const resultMap = new Map() + details.forEach(item => { + Object.entries(item).forEach(([key, value]) => { + if (resultMap.has(key)) { + const existingValue = resultMap.get(key) + if (existingValue !== value) { + resultMap.set(key, `${existingValue}, ${value}`) + } + } else { + resultMap.set(key, value) + } + }) + }) + return resultMap + } + + /** + * 清除 popup + * @param container + */ + clearPopup = container => { + const containerElement = document.getElementById(container) + containerElement?.querySelectorAll('.l7-popup').forEach((element: Element) => element.remove()) + } + + /** + * 构建 tooltip + * @param chart + * @param pointLayer + */ + buildTooltip = (chart, container, pointLayer, scene) => { + const customAttr = chart.customAttr ? parseJson(chart.customAttr) : null + this.clearPopup(container) + if (customAttr?.tooltip?.show) { + const { tooltip } = deepCopy(customAttr) + let showFields = tooltip.showFields || [] + if (!tooltip.showFields || tooltip.showFields.length === 0) { + showFields = [ + ...chart.xAxisExt.map(i => `${i.dataeaseName}@${i.name}`), + ...chart.xAxis.map(i => `${i.dataeaseName}@${i.name}`) + ] + } + // 修改背景色 + const styleId = 'tooltip-' + container + const styleElement = document.getElementById(styleId) + if (styleElement) { + styleElement.remove() + styleElement.parentNode?.removeChild(styleElement) + } + const style = document.createElement('style') + style.id = styleId + style.innerHTML = ` + #${container} .l7-popup-content { + background-color: ${tooltip.backgroundColor} !important; + padding: 6px 10px 6px; + line-height: 1.6; + border-top-left-radius: 3px; + } + #${container} .l7-popup-tip { + border-top-color: ${tooltip.backgroundColor} !important; + } + ` + document.head.appendChild(style) + const htmlPrefix = `
    ` + const htmlSuffix = '
    ' + const containerElement = document.getElementById(container) + if (containerElement) { + containerElement.addEventListener('mousemove', event => { + const rect = containerElement.getBoundingClientRect() + const mouseX = event.clientX - rect.left + const mouseY = event.clientY - rect.top + const tooltipElement = containerElement.getElementsByClassName('l7-popup') + for (let i = 0; i < tooltipElement?.length; i++) { + const element = tooltipElement[i] as HTMLElement + element.firstElementChild.style.display = 'none' + element.style.transform = 'translate(15px, 12px)' + const isNearRightEdge = + containerElement.clientWidth - mouseX <= element.clientWidth + 10 + const isNearBottomEdge = containerElement.clientHeight - mouseY <= element.clientHeight + let transform = '' + if (isNearRightEdge) { + transform += 'translateX(-120%) translateY(15%) ' + } + if (isNearBottomEdge) { + transform += 'translateX(15%) translateY(-80%) ' + } + if (transform) { + element.style.transform = transform.trim() + } + } + }) + } + pointLayer.on('touchend', e => { + if (e.lngLat) { + const fieldData = { + ...e.feature, + ...Object.fromEntries(this.mergeDetailsToMap(e.feature.details ?? [])) + } + const content = this.buildTooltipContent(tooltip, fieldData, showFields) + const popup = new Popup({ + lngLat: e.lngLat, + title: '', + closeButton: false, + closeOnClick: true, + html: `${htmlPrefix}${content}${htmlSuffix}` + }) + scene.addPopup(popup) + } + }) + return new LayerPopup({ + anchor: 'top-left', + className: 'l7-popup-' + container, + items: [ + { + layer: pointLayer, + customContent: item => { + const fieldData = { + ...item, + ...Object.fromEntries(this.mergeDetailsToMap(item.details)) + } + const content = this.buildTooltipContent(tooltip, fieldData, showFields) + return `${htmlPrefix}${content}${htmlSuffix}` + } + } + ], + trigger: 'hover' + }) + } + return undefined + } + + /** + * 构建 tooltip 内容 + * @param tooltip + * @param fieldData + * @param showFields + * @returns {string} + */ + buildTooltipContent = (tooltip, fieldData, showFields) => { + let content = `` + if (tooltip.customContent) { + content = tooltip.customContent + showFields.forEach(field => { + content = content.replace(`\${${field.split('@')[1]}}`, fieldData[field.split('@')[0]]) + }) + } else { + showFields.forEach(field => { + content += `${field.split('@')[1]}: ${ + fieldData[field.split('@')[0]] + }
    ` + }) + } + return content.replace(/\n/g, '
    ') + } + + /** + * 构建 label + * @param chart + * @param configList + */ + buildLabel = (chart, configList) => { + const xAxis = deepCopy(chart.xAxis) + + const customAttr = chart.customAttr ? parseJson(chart.customAttr) : null + if (customAttr?.label?.show) { + const { label } = customAttr + const data = chart.data?.tableRow || [] + let showFields = label.showFields || [] + if (!label.showFields || label.showFields.length === 0) { + showFields = [ + ...chart.xAxisExt.map(i => `${i.dataeaseName}@${i.name}`), + ...chart.xAxis.map(i => `${i.dataeaseName}@${i.name}`) + ] + } + data.forEach(item => { + const fieldData = { + ...item, + ...Object.fromEntries(this.mergeDetailsToMap(item.details)) + } + let content = label.customContent || '' + + if (content) { + showFields.forEach(field => { + const [fieldKey, fieldName] = field.split('@') + content = content.replace(`\${${fieldName}}`, fieldData[fieldKey]) + }) + } else { + content = showFields.map(field => fieldData[field.split('@')[0]]).join(',') + } + + content = content.replace(/\n/g, '') + item.textLayerContent = content + }) + + configList.push( + new PointLayer() + .source(data, { + parser: { + type: 'json', + x: xAxis[0].dataeaseName, + y: xAxis[1].dataeaseName + } + }) + .shape('textLayerContent', 'text') + .color(label.color) + .size(label.fontSize) + .style({ + textAllowOverlap: label.fullDisplay, + textAnchor: 'center', + textOffset: [0, 0], + fontFamily: chart.fontFamily ? chart.fontFamily : undefined + }) + ) + } + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + chart.customAttr.label = { + ...chart.customAttr.label, + show: false + } + chart.customAttr.basicStyle = { + ...chart.customAttr.basicStyle, + mapSymbolOpacity: 5, + mapStyle: 'normal' + } + return chart + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/map/tooltip-carousel.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/map/tooltip-carousel.ts new file mode 100644 index 0000000..31616a8 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/map/tooltip-carousel.ts @@ -0,0 +1,657 @@ +import { Popup } from '@antv/l7' +import { Plot } from '@antv/l7plot/dist/lib/core/plot' +import isEmpty from 'lodash-es/isEmpty' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { parseJson } from '@/data-visualization/chart/components/js/util' +import { Scene } from '@antv/l7-scene' +import { deepCopy } from '@/data-visualization/utils/utils' + +export const configCarouselTooltip = (chart, view, data, scene, customSubArea?, drawOption?) => { + if (['bubble-map', 'map'].includes(chart.type)) { + data = view.source.data.dataArray + ?.filter(i => i.dimensionList?.length > 0) + .reduce((acc, current) => { + const existingItem = acc.find(obj => { + if (drawOption?.areaId?.startsWith('custom_')) { + return obj.areaName === current.areaName + } else { + return obj.name === current.name || (obj.adcode && obj.adcode === current.adcode) + } + }) + if (!existingItem) { + acc.push(current) + } + return acc + }, []) + } + if (carouselManagerInstances[chart.container]) { + const instances = carouselManagerInstances[chart.container] + instances.update(scene, chart, view, data, customSubArea, drawOption) + } else { + new CarouselManager(scene, chart, view, data, customSubArea, drawOption) + } +} +export const carouselManagerInstances: { [key: string]: CarouselManager } = {} + +/** + * 轮播管理类 + */ +export class CarouselManager { + /** + * 停留时长定时器 + * @private + */ + private popupTimeoutId: number | null = null + /** + * 轮播间隔定时器 + * @private + */ + private popupIntervalId: number | null = null + /** + * 是否暂停轮播 + * @private + */ + private isPaused = false + /** + * 当前显示的数据索引 + * @private + */ + private currentIndex = 0 + /** + * 地图实例,气泡地图用 + * @private + */ + private scene: Scene + private chart: Chart + /** + * 轮播弹窗的位置数据 + * @private + */ + private view: Plot + private data: any[] + /** + * 停留时长 + * @private + */ + private stayTime: number + /** + * 轮播间隔 + * @private + */ + private intervalTime: number + /** + * 轮播弹窗 + * @private + */ + private popup: Popup + + /** + * 自定义区域列表 + * @private + */ + private customSubArea: CustomGeoSubArea[] + + /** + * 渲染参数 + * @private + */ + private drawOption: L7PlotDrawOptions + + // 保存事件监听函数的引用 + private onMouseEnterHandler: () => void + private onMouseLeaveHandler: () => void + private onVisibilityChangeHandler: () => void + + constructor(scene, chart, view, data: any[], customSubArea, drawOption?) { + // 绑定事件处理函数 + this.onMouseEnterHandler = this.pauseCarouselPopups.bind(this) + this.onMouseLeaveHandler = this.resumeCarouselPopups.bind(this) + this.onVisibilityChangeHandler = this.handleVisibilityChange.bind(this) + this.clearExistingTimers = this.clearExistingTimers.bind(this) + this.init(scene, chart, view, data, customSubArea, drawOption) + } + + /** + * 更新轮播弹窗对象内容 + * @param scene + * @param chart + * @param view + * @param data + * @param customSubArea + */ + public update(scene, chart, view, data: any[], customSubArea, drawOption?) { + this.init(scene, chart, view, data, customSubArea, drawOption) + } + + /** + * 初始化轮播弹窗 + * @param scene + * @param chart + * @param view + * @param data + * @private + */ + private init(scene, chart, view, data: any[], customSubArea, drawOption?) { + this.view = view + this.chart = chart + this.scene = scene + this.data = data + this.popup = null + this.currentIndex = 0 + this.customSubArea = customSubArea + this.drawOption = drawOption + this.clearPreviousInstance(this.chart.container) + if ( + this.chart.customAttr?.tooltip?.show && + this.chart.customAttr?.tooltip?.carousel?.enable && + this.data.length > 0 + ) { + this.popup = new Popup({ closeButton: false, maxWidth: 600 }) + const carousel = this.chart.customAttr?.tooltip?.carousel + this.stayTime = carousel.stayTime * 1000 + this.intervalTime = carousel.intervalTime * 1000 + this.startCarouselPopups() + const divElement = document.getElementById(this.chart.container) + divElement.addEventListener('mouseenter', this.pauseCarouselPopups) + divElement.addEventListener('mouseleave', this.resumeCarouselPopups) + // 移动端符号地图不支持mouseenter和mouseleave事件,这里特殊处理一下 + if (this.chart.type === 'symbolic-map') { + // 监听符号触摸事件, 暂停轮播 + scene?.getLayers()?.[0]?.addListener('touchend', () => { + this.pauseCarouselPopups() + }) + // 地图空白区域触摸事件, 启动轮播 + scene?.getMapCanvasContainer()?.addEventListener('touchend', () => { + this.resumeCarouselPopups() + }) + } + // 监听页面可见性变化 + document.addEventListener('visibilitychange', this.handleVisibilityChange) + carouselManagerInstances[this.chart.container] = this + } + } + + private handleVisibilityChange = (): void => { + if (document.hidden) { + this.clearPreviousInstance(this.chart.container) + } else { + this.startCarouselPopups() + } + } + + /** + * 清除之前的实例数据 + * @param containerId + * @private + */ + private clearPreviousInstance(containerId: string): void { + if (carouselManagerInstances[containerId]) { + const instance = carouselManagerInstances[containerId] + this.clearExistingTimers() + instance.popup?.remove() + instance.removeStyle() + } + } + + /** + * 开始轮播 + * @private + */ + private startCarouselPopups(): void { + this.clearExistingTimers() + this.carouselPopups() + } + + /** + * 鼠标移入暂停轮播 + */ + private pauseCarouselPopups = (): void => { + if (this.popup) { + this.popup?.remove() + } + this.removeStyle() + this.isPaused = true + this.clearExistingTimers() + } + + /** + * 鼠标移出开始轮播 + */ + private resumeCarouselPopups = (): void => { + if (this.isPaused) { + this.isPaused = false + this.startCarouselPopups() + } + } + + /** + * 管理轮播弹窗的显示 + * + * 此方法用于处理轮播弹窗的显示逻辑它会根据当前的索引显示对应的弹窗, + * 并在一定时间后自动移除当前弹窗并显示下一个弹窗 + * + * @private + */ + private carouselPopups(): void { + const showPopup = (index: number): void => { + this.removeStyle() + const containerElement = document.getElementById(this.chart.container) + if (containerElement) { + if (this.chart.type === 'symbolic-map') { + // 轮播进行时,隐藏隐藏鼠标悬浮的tooltip + const mouseTooltip = containerElement.getElementsByClassName( + 'l7-popup-' + this.chart.container + ) + for (const tooltip of Array.from(mouseTooltip)) { + const tooltipElement = tooltip as HTMLElement + tooltipElement.classList.add('l7-popup-hide') + } + this.createSymbolicMapPopup(index) + } else { + if (this.chart.type === 'map') { + // 轮播进行时,隐藏隐藏鼠标悬浮的tooltip + const mouseTooltip = containerElement.getElementsByClassName('l7plot-tooltip-container') + for (const tooltip of Array.from(mouseTooltip)) { + const tooltipElement = tooltip as HTMLElement + tooltipElement.style.display = 'none' + } + } + this.createPopup(index) + } + this.clearExistingTimers() + this.popupTimeoutId = window.setTimeout(() => { + this.currentIndex++ + this.popup?.remove() + this.cancelHighlightLayer(index) + if (this.currentIndex >= this.data.length) { + this.currentIndex = 0 + } + this.popupIntervalId = window.setTimeout(() => { + showPopup(this.currentIndex) + }, this.intervalTime) + }, this.stayTime) + } else { + this.clearExistingTimers() + } + } + + showPopup(this.currentIndex) + } + + /** + * 清除定时器 + * @private + */ + private readonly clearExistingTimers = (): void => { + if (this.popupTimeoutId !== null) { + clearTimeout(this.popupTimeoutId) + this.popupTimeoutId = 0 + } + if (this.popupIntervalId !== null) { + clearInterval(this.popupIntervalId) + this.popupIntervalId = 0 + } + } + + /** + * 移除样式 + * 每次创建弹窗前移除之前的样式 + * @private + */ + private removeStyle(): void { + const styleToRemove = document.getElementById('style-' + this.chart.container) + if (styleToRemove) { + styleToRemove.remove() + styleToRemove.parentNode?.removeChild(styleToRemove) + } + } + + /** + * 创建弹窗信息 + * @param index + * @private + */ + private createPopup(index: number): void { + const tooltipStyle = this.view.tooltip.options.domStyles + const tooltipBackgroundColor = tooltipStyle['l7plot-tooltip']['background-color'] + const tooltipFontSize = tooltipStyle['l7plot-tooltip']['font-size'] + const style = document.createElement('style') + style.id = 'style-' + this.chart.container + style.innerHTML = ` + #${this.chart.container} .l7-popup-content { + background-color: ${tooltipBackgroundColor} !important; + font-size: ${tooltipFontSize}; + padding: 10px 10px 6px; + line-height: 1.6; + } + #${this.chart.container} .l7-popup-tip { + border-top-color: ${tooltipBackgroundColor} !important; + } + ` + document.head.appendChild(style) + + const popupData = this.getPopupData(index) + if (popupData.data) { + let tooltipItem = '' + this.getTooltipItems(popupData.data).forEach(fieldData => { + tooltipItem += ` +
  • + ${fieldData.name} + ${fieldData.value} +
  • ` + }) + + const html = ` +
    +
    ${popupData.data.name}
    +
      + ${tooltipItem} +
    +
    + ` + + this.popup.setLngLat({ lng: popupData.centroid[0], lat: popupData.centroid[1] }) + this.popup.setHTML(html) + this.popup.closeButton = false + this.view.addLayer(this.popup) + // 地图层高亮 + this.view.scene + .getLayers() + ?.find(i => i.name === 'highlightLayer') + ?.setData(this.getActiveData(index)) + if (this.chart.type === 'bubble-map') { + // 气泡地图高亮 + const { _id } = this.view.scene + .getLayers() + ?.find(i => i.name === 'bubbleLayer') + ?.layerSource.data.dataArray.find(i => i.name === this.data[index].name) + this.view.scene + .getLayers() + ?.find(i => i.name === 'bubbleLayer' && i.coordCenter) + ?.setActive(_id, { color: 'rgba(30,90,255,1)' }) + } + } + } + + private getActiveData(index): any { + if (this.drawOption?.areaId?.startsWith('custom_')) { + const result = { + type: 'FeatureCollection', + features: [] + } + const area = this.customSubArea.find(a => a.name === this.data[index].areaName) + const areaMap = this.view.currentDistrictData.features.reduce((p, n) => { + p['156' + n.properties.adcode] = n + return p + }, {}) + area?.scopeArr?.forEach(s => { + if (areaMap[s]) { + result.features.push(areaMap[s]) + } + }) + return result + } + return { + type: 'FeatureCollection', + features: [ + this.view.currentDistrictData.features.find( + i => i.properties.name === this.data[index].name + ) + ] + } + } + + /** + * 获取弹窗信息,包括原始数据及位置信息 + * @param index + * @private + */ + private getPopupData(index: number): any { + if (this.drawOption?.areaId?.startsWith('custom_')) { + const data = this.data[index] + const area = this.customSubArea?.find(a => a.name === data.areaName) + data.name = data.areaName + return { + data, + centroid: area.centroid + } + } else { + return { + data: this.data[index], + centroid: this.view.currentDistrictData.features.find( + i => i.properties.name === this.data[index].name + )?.properties.centroid + } + } + } + + /** + * 将对象转换为 CSS 属性 + * @param obj + * @private + */ + private objectToSemicolonSeparated(obj: any): string { + let result = '' + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + result += `${this.convertToSnakeCase(key)}:${obj[key]};` + } + } + return result + } + + private cancelHighlightLayer(index?: number): void { + this.view.scene + ?.getLayers() + ?.find(i => i.name === 'highlightLayer') + ?.setData({ type: 'FeatureCollection', features: [] }) + if (this.chart.type === 'bubble-map') { + const { _id } = this.view.scene + ?.getLayers() + ?.find(i => i.name === 'bubbleLayer') + ?.layerSource.data.dataArray.find(i => i.name === this.data[index].name) + this.view.scene + .getLayers() + ?.find(i => i.name === 'bubbleLayer' && i.coordCenter) + ?.setActive(_id, { + color: this.view.scene + .getLayers() + .find(i => i.name === 'bubbleLayer') + .styleAttributeService.getLayerStyleAttribute('color').scale.field + }) + } + if (this.chart.type === 'symbolic-map') { + const lngField = this.chart.xAxis[0].dataeaseName + const latField = this.chart.xAxis[1].dataeaseName + const { _id } = this.scene + ?.getLayers() + ?.find(i => i.type === 'PointLayer') + ?.layerSource.data.dataArray.find(i => { + const targetLng = this.data[index][lngField] + const targetLat = this.data[index][latField] + return i[lngField] === targetLng && i[latField] === targetLat + }) + this.scene + .getLayers() + ?.find(i => i.type === 'PointLayer' && i.coordCenter) + ?.setActive(_id, { + color: this.scene + .getLayers() + .find(i => i.type === 'PointLayer') + .styleAttributeService.getLayerStyleAttribute('color').scale.field + }) + } + } + + /** + * 将驼峰式命名转换为蛇形命名 + * @param str + * @private + */ + private convertToSnakeCase(str: string): string { + return str.replace(/([A-Z])/g, match => '-' + match.toLowerCase()) + } + + /** + * 获取弹窗字段信息 + * 与tooltip要显示的内容一致 + * @param data + * @private + */ + private getTooltipItems(data) { + const result = [] + const customAttr = parseJson(this.chart.customAttr) + const tooltip = customAttr.tooltip + const formatterMap = tooltip.seriesTooltipFormatter + ?.filter(i => i.show) + .reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) as Record + if (isEmpty(formatterMap)) { + return result + } + const head = data + const formatter = formatterMap[head.quotaList?.[0]?.id] + if (!isEmpty(formatter)) { + const originValue = parseFloat(head.value as string) + const value = valueFormatter(originValue, formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ ...head, name, value: `${value ?? ''}` }) + } + head.dynamicTooltipValue?.forEach(item => { + const formatter = formatterMap[item.fieldId] + if (formatter) { + const value = valueFormatter(parseFloat(item.value), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ color: 'grey', name, value: `${value ?? ''}` }) + } + }) + return result + } + + /** + * 符号地图特殊处理,tooltip的配置可自定义显示内容 + * @param index + * @private + */ + private createSymbolicMapPopup(index): void { + const buildTooltip = () => { + const customAttr = this.chart.customAttr ? parseJson(this.chart.customAttr) : null + if (customAttr?.tooltip?.show) { + if (!this.popup) { + return undefined + } + const { tooltip } = deepCopy(customAttr) + let showFields = tooltip.showFields || [] + if (!tooltip.showFields || tooltip.showFields.length === 0) { + showFields = [ + ...this.chart.xAxisExt.map(i => `${i.dataeaseName}@${i.name}`), + ...this.chart.xAxis.map(i => `${i.dataeaseName}@${i.name}`) + ] + } + const style = document.createElement('style') + style.id = 'style-' + this.chart.container + style.innerHTML = ` + #${this.chart.container} .l7-popup-content { + background-color: ${tooltip.backgroundColor} !important; + padding: 6px 10px 6px; + line-height: 1.6; + } + #${this.chart.container} .l7-popup-tip { + border-top-color: ${tooltip.backgroundColor} !important; + } + ` + document.head.appendChild(style) + const lngField = this.chart.xAxis[0].dataeaseName + const latField = this.chart.xAxis[1].dataeaseName + const htmlPrefix = `
    ` + const htmlSuffix = '
    ' + const data = this.view.sourceOption.data[index] + if (data && data.details?.length) { + const fieldData = { + ...data, + ...Object.fromEntries(mergeDetailsToMap(data.details)) + } + const content = buildTooltipContent(tooltip, fieldData, showFields) + const html = `${htmlPrefix}${content}${htmlSuffix}` + this.popup.setLngLat({ + lng: data[lngField], + lat: data[latField] + }) + this.popup.setHTML(html) + this.popup.closeButton = false + this.scene.addPopup(this.popup) + this.popup.addTo(this.scene) + const { _id } = this.scene + .getLayers() + ?.find(i => i.type === 'PointLayer') + ?.layerSource.data.dataArray.find(i => { + const targetLng = this.data[index][lngField] + const targetLat = this.data[index][latField] + return i[lngField] === targetLng && i[latField] === targetLat + }) + this.scene + .getLayers() + ?.find(i => i.type === 'PointLayer' && i.coordCenter) + ?.setActive(_id, { color: 'rgba(30,90,255,1)' }) + } + } + return undefined + } + + /** + * 构建 tooltip 内容 + * @param tooltip + * @param fieldData + * @param showFields + * @returns {string} + */ + const buildTooltipContent = (tooltip, fieldData, showFields) => { + let content = '' + if (tooltip.customContent) { + content = tooltip.customContent + showFields.forEach(field => { + content = content.replace(`\${${field.split('@')[1]}}`, fieldData[field.split('@')[0]]) + }) + } else { + showFields.forEach(field => { + content += `${field.split('@')[1]}: ${ + fieldData[field.split('@')[0]] + }
    ` + }) + } + return content.replace(/\n/g, '
    ') + } + /** + * 合并详情到 map + * @param details + * @returns {Map} + */ + const mergeDetailsToMap = details => { + const resultMap = new Map() + details.forEach(item => { + Object.entries(item).forEach(([key, value]) => { + if (resultMap.has(key)) { + const existingValue = resultMap.get(key) + if (existingValue !== value) { + resultMap.set(key, `${existingValue}, ${value}`) + } + } else { + resultMap.set(key, value) + } + }) + }) + return resultMap + } + buildTooltip() + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/others/chart-mix-common.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/others/chart-mix-common.ts new file mode 100644 index 0000000..403f47c --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/others/chart-mix-common.ts @@ -0,0 +1,119 @@ +import { DEFAULT_BASIC_STYLE } from '@/data-visualization/chart/components/editor/util/chart' + +export const CHART_MIX_EDITOR_PROPERTY: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'dual-basic-style-selector', + 'x-axis-selector', + 'dual-y-axis-selector', + 'title-selector', + 'legend-selector', + 'label-selector', + 'tooltip-selector', + 'assist-line', + 'function-cfg', + 'jump-set', + 'linkage' +] +export const CHART_MIX_EDITOR_PROPERTY_INNER: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + 'label-selector': ['fontSize', 'color'], + 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'show'], + 'dual-basic-style-selector': [ + 'colors', + 'alpha', + 'gradient', + 'lineWidth', + 'lineSymbol', + 'lineSymbolSize', + 'lineSmooth', + 'radiusColumnBar', + 'subSeriesColor', + 'seriesColor', + 'columnWidthRatio' + ], + 'x-axis-selector': [ + 'name', + 'color', + 'fontSize', + 'position', + 'axisLabel', + 'axisLine', + 'splitLine' + ], + 'dual-y-axis-selector': [ + 'name', + 'color', + 'fontSize', + 'axisLabel', + 'axisLine', + 'splitLine', + 'axisValue', + 'axisLabelFormatter' + ], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'legend-selector': ['icon', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition'], + 'function-cfg': ['emptyDataStrategy'] +} + +export const CHART_MIX_AXIS_TYPE: AxisType[] = [ + 'xAxis', + 'yAxis', + 'drill', + 'filter', + 'extLabel', + 'extTooltip' +] + +export const CHART_MIX_DEFAULT_BASIC_STYLE = { + ...DEFAULT_BASIC_STYLE, + subAlpha: 100, + subColorScheme: 'fast', + subSeriesColor: [], + subColors: [ + '#fae800', + '#00c039', + '#0482dc', + '#bb9581', + '#ff7701', + '#9c5ec3', + '#00ccdf', + '#00c039', + '#ff7701' + ], + leftLineWidth: 2, + leftLineSymbol: 'circle', + leftLineSymbolSize: 4, + leftLineSmooth: true +} + +export interface MixChartBasicStyle extends ChartBasicStyle { + subAlpha: number + subColors: string[] + subSeriesColor: { + /** + * 序列识别id,多指标就是轴id,分组或者堆叠就是类别值 + */ + id: string + /** + * 显示名称 + */ + name: string + /** + * 序列颜色 + */ + color: string + }[] +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/others/chart-mix.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/others/chart-mix.ts new file mode 100644 index 0000000..0d2627a --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/others/chart-mix.ts @@ -0,0 +1,1006 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import type { DualAxes, DualAxesOptions } from '@antv/g2plot/esm/plots/dual-axes' +import { + configPlotTooltipEvent, + getAnalyse, + getLabel, + getPadding, + getTooltipContainer, + getYAxis, + getYAxisExt, + setGradientColor, + TOOLTIP_TPL +} from '../../common/common_antv' +import { flow, hexColorToRGBA, parseJson } from '@/data-visualization/chart/components/js/util' +import { + cloneDeep, + isEmpty, + defaultTo, + map, + filter, + union, + defaultsDeep, + defaults +} from 'lodash-es' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { + CHART_MIX_AXIS_TYPE, + CHART_MIX_DEFAULT_BASIC_STYLE, + CHART_MIX_EDITOR_PROPERTY, + CHART_MIX_EDITOR_PROPERTY_INNER, + MixChartBasicStyle +} from './chart-mix-common' +import type { Datum } from '@antv/g2plot/esm/types/common' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { + DEFAULT_BASIC_STYLE, + DEFAULT_LABEL, + DEFAULT_LEGEND_STYLE +} from '@/data-visualization/chart/components/editor/util/chart' +import type { Options } from '@antv/g2plot/esm' +import { Group } from '@antv/g-canvas' + +const { t } = useI18n() +const DEFAULT_DATA = [] + +/** + * 柱线混合图 + */ +export class ColumnLineMix extends G2PlotChartView { + properties = CHART_MIX_EDITOR_PROPERTY + propertyInner = { + ...CHART_MIX_EDITOR_PROPERTY_INNER, + 'label-selector': ['vPosition', 'seriesLabelFormatter'], + 'tooltip-selector': [ + ...CHART_MIX_EDITOR_PROPERTY_INNER['tooltip-selector'], + 'seriesTooltipFormatter' + ] + } + axis: AxisType[] = [...CHART_MIX_AXIS_TYPE, 'xAxisExtRight', 'yAxisExt'] + axisConfig = { + xAxis: { + name: `${t('chart.drag_block_type_axis')} / ${t('chart.dimension')}`, + type: 'd' + }, + yAxis: { + name: `${t('chart.drag_block_value_axis_left')} / ${t('chart.column_quota')}`, + limit: 1, + type: 'q' + }, + extBubble: { + //用这个字段存放右轴分类 + name: `${t('chart.drag_block_type_axis_right')} / ${t('chart.dimension')}`, + limit: 1, + type: 'd', + allowEmpty: true + }, + yAxisExt: { + name: `${t('chart.drag_block_value_axis_right')} / ${t('chart.line_quota')}`, + limit: 1, + type: 'q', + allowEmpty: true + } + } + + protected getLeftType(): string { + return 'column' + } + protected getRightType(): string { + return 'line' + } + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, action, container } = drawOptions + if (!chart.data?.left?.data?.length && !chart.data?.right?.data?.length) { + return + } + const left = cloneDeep(chart.data?.left?.data) + const right = cloneDeep(chart.data?.right?.data) + + // const data1Type = (left[0]?.type === 'bar' ? 'column' : left[0]?.type) ?? 'column' + // const data2Type = (right[0]?.type === 'bar' ? 'column' : right[0]?.type) ?? 'column' + const data1Type = this.getLeftType() + const data2Type = this.getRightType() + + const isGroup = this.name === 'chart-mix-group' && chart.xAxisExt?.length > 0 + const isStack = this.name === 'chart-mix-stack' && chart.extStack?.length > 0 + const seriesField = 'category' + const seriesField2 = 'category' + + const data1 = defaultTo(left[0]?.data, []) + const data2 = map(defaultTo(right[0]?.data, []), d => { + return { + ...d, + valueExt: d.value + } + }) + + // options + const initOptions: DualAxesOptions = { + data: [data1, data2], + xField: 'field', + yField: ['value', 'valueExt'], //这里不能设置成一样的 + appendPadding: getPadding(chart), + geometryOptions: [ + { + geometry: data1Type, + color: [], + isGroup: isGroup, + isStack: isStack, + seriesField: seriesField + }, + { + geometry: data2Type, + color: [], + seriesField: seriesField2 + } + ], + interactions: [ + { + type: 'legend-active', + cfg: { + start: [{ trigger: 'legend-item:mouseenter', action: ['element-active:reset'] }], + end: [{ trigger: 'legend-item:mouseleave', action: ['element-active:reset'] }] + } + }, + { + type: 'legend-filter', + cfg: { + start: [ + { + trigger: 'legend-item:click', + action: [ + 'list-unchecked:toggle', + 'data-filter:filter', + 'element-active:reset', + 'element-highlight:reset' + ] + } + ] + } + }, + { + type: 'active-region' + } + ] + } + const options = this.setupOptions(chart, initOptions) + const { DualAxes } = await import('@antv/g2plot/esm/plots/dual-axes') + // 开始渲染 + const newChart = new DualAxes(container, options) + + newChart.on('point:click', action) + newChart.on('interval:click', action) + configPlotTooltipEvent(chart, newChart) + return newChart + } + + protected configLabel(chart: Chart, options: DualAxesOptions): DualAxesOptions { + const tempLabel = getLabel(chart) + const tmpOption = { ...options } + if (!tempLabel) { + if (tmpOption.geometryOptions) { + tmpOption.geometryOptions[0].label = false + tmpOption.geometryOptions[1].label = false + } + return tmpOption + } + + const labelAttr = parseJson(chart.customAttr).label + const axisFormatterMap = {} + labelAttr.seriesLabelFormatter?.forEach(attr => { + if (!axisFormatterMap[attr.axisType]) { + axisFormatterMap[attr.axisType] = [] + } + axisFormatterMap[attr.axisType].push(attr) + }) + const axisTypes = ['yAxis', 'yAxisExt'] + axisTypes.forEach(axisType => { + const formatterMap = axisFormatterMap[axisType]?.reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) + tempLabel.style.fill = DEFAULT_LABEL.color + const label = { + fields: [], + ...tempLabel, + offsetY: -8, + formatter: (data: Datum) => { + if (!labelAttr.seriesLabelFormatter?.length) { + return data.value + } + const labelCfg = formatterMap?.[data.quotaList[0].id] as SeriesFormatter + if (!labelCfg) { + return data.value + } + if (!labelCfg.show) { + return + } + const value = valueFormatter(data.value, labelCfg.formatterCfg) + const group = new Group({}) + group.addShape({ + type: 'text', + attrs: { + x: 0, + y: 0, + text: value, + textAlign: 'start', + textBaseline: 'top', + fontSize: labelCfg.fontSize, + fontFamily: chart.fontFamily, + fill: labelCfg.color + } + }) + return group + } + } + if (tmpOption.geometryOptions) { + if (axisType === 'yAxis') { + tmpOption.geometryOptions[0].label = label + } else if (axisType === 'yAxisExt') { + tmpOption.geometryOptions[1].label = label + } + } + }) + + return tmpOption + } + + protected configBasicStyle(chart: Chart, options: DualAxesOptions): DualAxesOptions { + // size + const customAttr: DeepPartial = parseJson(chart.customAttr) + const s = defaultsDeep( + JSON.parse(JSON.stringify(customAttr.basicStyle)), + CHART_MIX_DEFAULT_BASIC_STYLE + ) + const smooth = s.lineSmooth + const point = { + size: s.lineSymbolSize, + shape: s.lineSymbol, + style: { + stroke: hexColorToRGBA('#FFFFFF', s.subAlpha) + } + } + const lineStyle = { + lineWidth: s.lineWidth + } + const leftSmooth = s.leftLineSmooth + const leftPoint = { + size: s.leftLineSymbolSize, + shape: s.leftLineSymbol, + style: { + stroke: hexColorToRGBA('#FFFFFF', s.alpha) + } + } + const leftLineStyle = { + lineWidth: s.leftLineWidth + } + const tempOption = { + ...options, + smooth, + point, + lineStyle + } + if (tempOption.geometryOptions) { + tempOption.geometryOptions[0].smooth = leftSmooth + tempOption.geometryOptions[0].point = leftPoint + tempOption.geometryOptions[0].lineStyle = leftLineStyle + + tempOption.geometryOptions[1].smooth = smooth + tempOption.geometryOptions[1].point = point + tempOption.geometryOptions[1].lineStyle = lineStyle + + if (s.radiusColumnBar === 'roundAngle') { + const columnStyle = { + radius: [ + s.columnBarRightAngleRadius, + s.columnBarRightAngleRadius, + s.columnBarRightAngleRadius, + s.columnBarRightAngleRadius + ] + } + tempOption.geometryOptions[0].columnStyle = columnStyle + tempOption.geometryOptions[1].columnStyle = columnStyle + } + } + + let columnWidthRatio + const _v = s.columnWidthRatio ?? DEFAULT_BASIC_STYLE.columnWidthRatio + if (_v >= 1 && _v <= 100) { + columnWidthRatio = _v / 100.0 + } else if (_v < 1) { + columnWidthRatio = 1 / 100.0 + } else if (_v > 100) { + columnWidthRatio = 1 + } + if (columnWidthRatio) { + tempOption.geometryOptions[0].columnWidthRatio = columnWidthRatio + } + + if (super.name !== 'chart-mix-dual-line') { + tempOption.geometryOptions[0].appendPadding = getPadding(chart) + } + + return tempOption + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + const { customAttr, senior } = chart + if ( + senior.functionCfg.emptyDataStrategy == undefined || + senior.functionCfg.emptyDataStrategy === 'ignoreData' + ) { + senior.functionCfg.emptyDataStrategy = 'breakLine' + } + return chart + } + + protected configCustomColors(chart: Chart, options: DualAxesOptions): DualAxesOptions { + const tempOption = { + ...options + } + const basicStyle = parseJson(chart.customAttr).basicStyle as MixChartBasicStyle + + const { seriesColor } = basicStyle + if (seriesColor?.length) { + const seriesMap = seriesColor.reduce((p, n) => { + p[n.id] = n + return p + }, {}) + const { yAxis } = chart + yAxis?.forEach((axis, index) => { + const curAxisColor = seriesMap[axis.id] + if (curAxisColor) { + if (index + 1 > basicStyle.colors.length) { + basicStyle.colors.push(curAxisColor.color) + } else { + basicStyle.colors[index] = curAxisColor.color + } + } + }) + } + //左轴 + const color = basicStyle.colors.map(ele => { + const tmp = hexColorToRGBA(ele, basicStyle.alpha) + if (basicStyle.gradient) { + return setGradientColor(tmp, true, 270) + } else { + return tmp + } + }) + tempOption.geometryOptions[0].color = color + + return tempOption + } + + protected configSubCustomColors(chart: Chart, options: DualAxesOptions): DualAxesOptions { + const tempOption = { + ...options + } + const basicStyle = defaultsDeep( + parseJson(chart.customAttr).basicStyle as MixChartBasicStyle, + cloneDeep(CHART_MIX_DEFAULT_BASIC_STYLE) + ) + //右轴 + const { subSeriesColor } = basicStyle + if (subSeriesColor?.length) { + const { yAxisExt, extBubble } = chart + const seriesMap = subSeriesColor.reduce((p, n) => { + p[n.id] = n + return p + }, {}) + const { data } = options as unknown as Options + if (extBubble?.length) { + const seriesSet = new Set() + data[1]?.forEach(d => d.category !== null && seriesSet.add(d.category)) + const tmp = [...seriesSet] + tmp.forEach((c, i) => { + const curAxisColor = seriesMap[c as string] + if (curAxisColor) { + if (i + 1 > basicStyle.subColors.length) { + basicStyle.subColors.push(curAxisColor.color) + } else { + basicStyle.subColors[i] = curAxisColor.color + } + } + }) + } else { + yAxisExt?.forEach((axis, index) => { + const curAxisColor = seriesMap[axis.id] + if (curAxisColor) { + if (index + 1 > basicStyle.subColors.length) { + basicStyle.subColors.push(curAxisColor.color) + } else { + basicStyle.subColors[index] = curAxisColor.color + } + } + }) + } + } + const subColor = basicStyle.subColors.map(c => { + const cc = hexColorToRGBA(c, basicStyle.subAlpha) + return cc + }) + tempOption.geometryOptions[1].color = subColor + + return tempOption + } + + public setupSubSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + const result: ChartBasicStyle['seriesColor'] = [] + const seriesSet = new Set() + const colors = chart.customAttr.basicStyle.subColors ?? CHART_MIX_DEFAULT_BASIC_STYLE.subColors + const { yAxisExt, extBubble } = chart + if (extBubble?.length) { + data?.forEach(d => { + if (d.value === null || d.category === null || seriesSet.has(d.category)) { + return + } + seriesSet.add(d.category) + result.push({ + id: d.category, + name: d.category, + color: colors[(seriesSet.size - 1) % colors.length] + }) + }) + } else { + yAxisExt?.forEach(axis => { + if (seriesSet.has(axis.id)) { + return + } + seriesSet.add(axis.id) + result.push({ + id: axis.id, + name: axis.chartShowName ?? axis.name, + color: colors[(seriesSet.size - 1) % colors.length] + }) + }) + } + return result + } + + protected configYAxis(chart: Chart, options: DualAxesOptions): DualAxesOptions { + const yAxis = getYAxis(chart) + const yAxisExt = getYAxisExt(chart) + + const tempOption = { + ...options + } + + tempOption.yAxis = {} + if (!yAxis) { + //左右轴都要隐藏 + tempOption.yAxis.value = false + } else { + tempOption.yAxis.value = undefined + yAxis.position = 'left' + + const yAxisTmp = parseJson(chart.customStyle).yAxis + if (yAxis.label) { + yAxis.label.style.textAlign = 'end' + yAxis.label.formatter = value => { + return valueFormatter(value, yAxisTmp.axisLabelFormatter) + } + } + const axisValue = yAxisTmp.axisValue + if (!axisValue?.auto) { + tempOption.yAxis.value = { + ...yAxis, + min: axisValue.min, + max: axisValue.max, + minLimit: axisValue.min, + maxLimit: axisValue.max, + tickCount: axisValue.splitCount + } + } else { + tempOption.yAxis.value = yAxis + } + } + + if (!yAxisExt) { + //左右轴都要隐藏 + tempOption.yAxis.valueExt = false + } else { + tempOption.yAxis.valueExt = undefined + yAxisExt.position = 'right' + + const yAxisExtTmp = parseJson(chart.customStyle).yAxisExt + if (yAxisExt.label) { + yAxisExt.label.style.textAlign = 'start' + yAxisExt.label.formatter = value => { + return valueFormatter(value, yAxisExtTmp.axisLabelFormatter) + } + } + const axisExtValue = yAxisExtTmp.axisValue + if (!axisExtValue?.auto) { + tempOption.yAxis.valueExt = { + ...yAxisExt, + min: axisExtValue.min, + max: axisExtValue.max, + minLimit: axisExtValue.min, + maxLimit: axisExtValue.max, + tickCount: axisExtValue.splitCount + } + } else { + tempOption.yAxis.valueExt = yAxisExt + } + } + + return tempOption + } + + protected configTooltip(chart: Chart, options: DualAxesOptions): DualAxesOptions { + const customAttr: DeepPartial = parseJson(chart.customAttr) + const tooltipAttr = customAttr.tooltip + if (!tooltipAttr.show) { + return { + ...options, + tooltip: false + } + } + const formatterMap = tooltipAttr.seriesTooltipFormatter + ?.filter(i => i.show) + .reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) as Record + const tooltip: DualAxesOptions['tooltip'] = { + shared: true, + showTitle: true, + customItems(originalItems) { + if (!tooltipAttr.seriesTooltipFormatter?.length) { + return originalItems + } + const head = originalItems[0] + // 非原始数据 + if (!head.data.quotaList) { + return originalItems + } + const result = [] + originalItems + .filter(item => formatterMap[item.data.quotaList[0].id]) + .forEach(item => { + const formatter = formatterMap[item.data.quotaList[0].id] + const value = valueFormatter(parseFloat(item.value as string), formatter.formatterCfg) + const name = item.data.category + + result.push({ ...item, name, value }) + }) + head.data.dynamicTooltipValue?.forEach(item => { + const formatter = formatterMap[item.fieldId] + if (formatter) { + const value = valueFormatter(parseFloat(item.value), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ color: 'grey', name, value }) + } + }) + return result + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + return { + ...options, + tooltip + } + } + + protected configLegend(chart: Chart, options: DualAxesOptions): DualAxesOptions { + const o = super.configLegend(chart, options) + if (o.legend) { + const left = cloneDeep(chart.data?.left?.data) + const right = cloneDeep(chart.data?.right?.data) + + o.legend.itemName.formatter = (text: string, item: any, index: number) => { + let name = undefined + if (item.viewId === 'left-axes-view' && text === 'value') { + name = left[0]?.categories[0] + } else if (item.viewId === 'right-axes-view' && text === 'valueExt') { + name = right[0]?.categories[0] + } + item.id = item.id + '__' + index //防止重复的图例出现问题,但是左右轴如果有相同的怎么办 + if (name === undefined) { + return text + } else { + return name + } + } + + const customStyle = parseJson(chart.customStyle) + let size + if (customStyle && customStyle.legend) { + size = defaults(JSON.parse(JSON.stringify(customStyle.legend)), DEFAULT_LEGEND_STYLE).size + } else { + size = DEFAULT_LEGEND_STYLE.size + } + + o.legend.marker.style = style => { + const fill = style.fill ?? style.stroke + return { + r: size, + fill + } + } + } + return o + } + + protected configAnalyse(chart: Chart, options: DualAxesOptions): DualAxesOptions { + chart.data.dynamicAssistLines = union( + defaultTo(chart.data?.left?.dynamicAssistLines, []), + defaultTo(chart.data?.right?.dynamicAssistLines, []) + ) + const list = getAnalyse(chart) + const annotations = { + value: filter(list, l => l.yAxisType === 'left'), + valueExt: filter(list, l => l.yAxisType === 'right') + } + return { ...options, annotations } + } + + protected setupOptions(chart: Chart, options: DualAxesOptions): DualAxesOptions { + return flow( + this.configTheme, + this.configLabel, + this.configTooltip, + this.configBasicStyle, + this.configCustomColors, + this.configSubCustomColors, + this.configLegend, + this.configXAxis, + this.configYAxis, + this.configAnalyse, + this.configEmptyDataStrategy + )(chart, options) + } + + constructor(name = 'chart-mix') { + super(name, DEFAULT_DATA) + } +} + +export class GroupColumnLineMix extends ColumnLineMix { + axis: AxisType[] = [...this['axis'], 'xAxisExt'] + propertyInner = { + ...CHART_MIX_EDITOR_PROPERTY_INNER, + 'label-selector': ['vPosition', 'seriesLabelFormatter'], + 'tooltip-selector': [ + ...CHART_MIX_EDITOR_PROPERTY_INNER['tooltip-selector'], + 'seriesTooltipFormatter' + ] + } + axisConfig = { + ...this['axisConfig'], + xAxisExt: { + name: `${t('chart.chart_group')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1, + allowEmpty: true + } + } + + protected configCustomColors(chart: Chart, options: DualAxesOptions): DualAxesOptions { + const tempOption = { + ...options + } + const basicStyle = parseJson(chart.customAttr).basicStyle as MixChartBasicStyle + + const { seriesColor } = basicStyle + if (seriesColor?.length) { + const seriesMap = seriesColor.reduce((p, n) => { + p[n.id] = n + return p + }, {}) + const { yAxis, xAxisExt } = chart + const { data } = options as unknown as Options + if (xAxisExt?.length) { + const seriesSet = new Set() + data[0]?.forEach(d => d.category !== null && seriesSet.add(d.category)) + const tmp = [...seriesSet] + tmp.forEach((c, i) => { + const curAxisColor = seriesMap[c as string] + if (curAxisColor) { + if (i + 1 > basicStyle.colors.length) { + basicStyle.colors.push(curAxisColor.color) + } else { + basicStyle.colors[i] = curAxisColor.color + } + } + }) + } else { + yAxis?.forEach((axis, index) => { + const curAxisColor = seriesMap[axis.id] + if (curAxisColor) { + if (index + 1 > basicStyle.colors.length) { + basicStyle.colors.push(curAxisColor.color) + } else { + basicStyle.colors[index] = curAxisColor.color + } + } + }) + } + } + //左轴 + const color = basicStyle.colors.map(ele => { + const tmp = hexColorToRGBA(ele, basicStyle.alpha) + if (basicStyle.gradient) { + return setGradientColor(tmp, true, 270) + } else { + return tmp + } + }) + tempOption.geometryOptions[0].color = color + + return tempOption + } + + public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + const result: ChartBasicStyle['seriesColor'] = [] + const seriesSet = new Set() + const colors = chart.customAttr.basicStyle.colors + const { yAxis, xAxisExt } = chart + if (xAxisExt?.length) { + data?.forEach(d => { + if (d.value === null || d.category === null || seriesSet.has(d.category)) { + return + } + seriesSet.add(d.category) + result.push({ + id: d.category, + name: d.category, + color: colors[(seriesSet.size - 1) % colors.length] + }) + }) + } else { + yAxis?.forEach(axis => { + if (seriesSet.has(axis.id)) { + return + } + seriesSet.add(axis.id) + result.push({ + id: axis.id, + name: axis.chartShowName ?? axis.name, + color: colors[(seriesSet.size - 1) % colors.length] + }) + }) + } + return result + } + + constructor(name = 'chart-mix-group') { + super(name) + } +} +export class StackColumnLineMix extends ColumnLineMix { + axis: AxisType[] = [...this['axis'], 'extStack'] + propertyInner = { + ...CHART_MIX_EDITOR_PROPERTY_INNER, + 'label-selector': ['vPosition', 'seriesLabelFormatter'], + 'tooltip-selector': [ + ...CHART_MIX_EDITOR_PROPERTY_INNER['tooltip-selector'], + 'seriesTooltipFormatter' + ] + } + axisConfig = { + ...this['axisConfig'], + extStack: { + name: `${t('chart.stack_item')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1, + allowEmpty: true + } + } + + protected configCustomColors(chart: Chart, options: DualAxesOptions): DualAxesOptions { + const tempOption = { + ...options + } + const basicStyle = parseJson(chart.customAttr).basicStyle as MixChartBasicStyle + + const { seriesColor } = basicStyle + if (seriesColor?.length) { + const seriesMap = seriesColor.reduce((p, n) => { + p[n.id] = n + return p + }, {}) + const { yAxis, extStack } = chart + const { data } = options as unknown as Options + if (extStack?.length) { + const seriesSet = new Set() + data[0]?.forEach(d => d.category !== null && seriesSet.add(d.category)) + const tmp = [...seriesSet] + tmp.forEach((c, i) => { + const curAxisColor = seriesMap[c as string] + if (curAxisColor) { + if (i + 1 > basicStyle.colors.length) { + basicStyle.colors.push(curAxisColor.color) + } else { + basicStyle.colors[i] = curAxisColor.color + } + } + }) + } else { + yAxis?.forEach((axis, index) => { + const curAxisColor = seriesMap[axis.id] + if (curAxisColor) { + if (index + 1 > basicStyle.colors.length) { + basicStyle.colors.push(curAxisColor.color) + } else { + basicStyle.colors[index] = curAxisColor.color + } + } + }) + } + } + //左轴 + const color = basicStyle.colors.map(ele => { + const tmp = hexColorToRGBA(ele, basicStyle.alpha) + if (basicStyle.gradient) { + return setGradientColor(tmp, true, 270) + } else { + return tmp + } + }) + tempOption.geometryOptions[0].color = color + + return tempOption + } + + public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + const result: ChartBasicStyle['seriesColor'] = [] + const seriesSet = new Set() + const colors = chart.customAttr.basicStyle.colors + const { yAxis, extStack } = chart + if (extStack?.length) { + data?.forEach(d => { + if (d.value === null || d.category === null || seriesSet.has(d.category)) { + return + } + seriesSet.add(d.category) + result.push({ + id: d.category, + name: d.category, + color: colors[(seriesSet.size - 1) % colors.length] + }) + }) + } else { + yAxis?.forEach(axis => { + if (seriesSet.has(axis.id)) { + return + } + seriesSet.add(axis.id) + result.push({ + id: axis.id, + name: axis.chartShowName ?? axis.name, + color: colors[(seriesSet.size - 1) % colors.length] + }) + }) + } + return result + } + + constructor(name = 'chart-mix-stack') { + super(name) + } +} + +export class DualLineMix extends ColumnLineMix { + axis: AxisType[] = [...this['axis'], 'xAxisExt'] + propertyInner = { + ...CHART_MIX_EDITOR_PROPERTY_INNER, + 'label-selector': ['seriesLabelFormatter'], + 'tooltip-selector': [ + ...CHART_MIX_EDITOR_PROPERTY_INNER['tooltip-selector'], + 'seriesTooltipFormatter' + ] + } + axisConfig = { + ...this['axisConfig'], + xAxisExt: { + name: `${t('chart.drag_block_type_axis_left')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1, + allowEmpty: true + } + } + + protected getLeftType(): string { + return 'line' + } + + protected configCustomColors(chart: Chart, options: DualAxesOptions): DualAxesOptions { + const tempOption = { + ...options + } + const basicStyle = parseJson(chart.customAttr).basicStyle as MixChartBasicStyle + + const { seriesColor } = basicStyle + if (seriesColor?.length) { + const seriesMap = seriesColor.reduce((p, n) => { + p[n.id] = n + return p + }, {}) + const { yAxis, xAxisExt } = chart + const { data } = options as unknown as Options + if (xAxisExt?.length) { + const seriesSet = new Set() + data[0]?.forEach(d => d.category !== null && seriesSet.add(d.category)) + const tmp = [...seriesSet] + tmp.forEach((c, i) => { + const curAxisColor = seriesMap[c as string] + if (curAxisColor) { + if (i + 1 > basicStyle.colors.length) { + basicStyle.colors.push(curAxisColor.color) + } else { + basicStyle.colors[i] = curAxisColor.color + } + } + }) + } else { + yAxis?.forEach((axis, index) => { + const curAxisColor = seriesMap[axis.id] + if (curAxisColor) { + if (index + 1 > basicStyle.colors.length) { + basicStyle.colors.push(curAxisColor.color) + } else { + basicStyle.colors[index] = curAxisColor.color + } + } + }) + } + } + //左轴 + const color = basicStyle.colors.map(ele => { + const tmp = hexColorToRGBA(ele, basicStyle.alpha) + if (basicStyle.gradient) { + return setGradientColor(tmp, true, 270) + } else { + return tmp + } + }) + tempOption.geometryOptions[0].color = color + + return tempOption + } + + public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + const result: ChartBasicStyle['seriesColor'] = [] + const seriesSet = new Set() + const colors = chart.customAttr.basicStyle.colors + const { yAxis, xAxisExt } = chart + if (xAxisExt?.length) { + data?.forEach(d => { + if (d.value === null || d.category === null || seriesSet.has(d.category)) { + return + } + seriesSet.add(d.category) + result.push({ + id: d.category, + name: d.category, + color: colors[(seriesSet.size - 1) % colors.length] + }) + }) + } else { + yAxis?.forEach(axis => { + if (seriesSet.has(axis.id)) { + return + } + seriesSet.add(axis.id) + result.push({ + id: axis.id, + name: axis.chartShowName ?? axis.name, + color: colors[(seriesSet.size - 1) % colors.length] + }) + }) + } + return result + } + + constructor(name = 'chart-mix-dual-line') { + super(name) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/others/circle-packing.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/others/circle-packing.ts new file mode 100644 index 0000000..ab6bbad --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/others/circle-packing.ts @@ -0,0 +1,279 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import type { + CirclePacking as G2CirclePacking, + CirclePackingOptions +} from '@antv/g2plot/esm/plots/circle-packing' +import { flow, parseJson } from '@/data-visualization/chart/components/js/util' +import { getPadding } from '@/data-visualization/chart/components/js/panel/common/common_antv' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import type { Datum } from '@antv/g2plot/esm/types/common' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { cloneDeep } from 'lodash-es' + +const { t } = useI18n() +const DEFAULT_DATA = [] +/** + * 圆形填充图 + */ +export class CirclePacking extends G2PlotChartView { + properties: EditorProperty[] = [ + 'basic-style-selector', + 'background-overall-component', + 'border-style', + 'label-selector', + 'legend-selector', + 'title-selector', + 'tooltip-selector', + 'jump-set', + 'linkage' + ] + propertyInner: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + 'basic-style-selector': ['colors', 'alpha', 'circleBorderStyle'], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'function-cfg': ['emptyDataStrategy'], + 'label-selector': ['color', 'fontSize'], + 'legend-selector': ['icon', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition'], + 'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'tooltipFormatter', 'show'] + } + axis: AxisType[] = ['xAxis', 'yAxis', 'filter', 'drill'] + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.circle_packing_name')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1 + }, + yAxis: { + name: `${t('chart.circle_packing_value')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + } + } + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (chart?.data?.data?.length) { + // data + const data = chart.data.data + const { xAxis, yAxis, drillFields } = chart + const ySort = yAxis[0]?.sort ?? 'none' + const sort = { + sort: (a, b) => + ySort === 'asc' ? a.value - b.value : ySort === 'desc' ? b.value - a.value : 0 + } + // 将数据转为圆形填充图数据格式 + const getCirclePackingData = () => { + const result = [{ name: t('commons.all'), children: [] }] + const addNode = (nodes, item) => { + const node = { ...item, name: item.name, children: [] } + nodes.push(node) + } + data.forEach(item => addNode(result[0].children, item)) + return result[0] + } + // options + const initOptions: CirclePackingOptions = { + data: getCirclePackingData(), + appendPadding: getPadding(chart), + hierarchyConfig: { + ...(ySort === 'none' ? {} : sort) + }, + interactions: [ + { + type: 'legend-active', + cfg: { + start: [{ trigger: 'legend-item:mouseenter', action: ['element-active:reset'] }], + end: [{ trigger: 'legend-item:mouseleave', action: ['element-active:reset'] }] + } + }, + { + type: 'legend-filter', + cfg: { + start: [ + { + trigger: 'legend-item:click', + action: [ + 'list-unchecked:toggle', + 'data-filter:filter', + 'element-active:reset', + 'element-highlight:reset' + ] + } + ] + } + } + ] + } + const options = this.setupOptions(chart, initOptions) + const { CirclePacking: G2CirclePacking } = await import( + '@antv/g2plot/esm/plots/circle-packing' + ) + const newChart = new G2CirclePacking(container, options) + newChart.on('point:click', param => { + const pointData = param?.data?.data + if (pointData?.name === t('commons.all')) { + return + } + const actionParams = { + x: param.x, + y: param.y, + data: { + data: { + ...pointData + } + } + } + action(actionParams) + }) + return newChart + } + } + + protected configBasicStyle(chart: Chart, options: CirclePackingOptions): CirclePackingOptions { + // size + const customAttr: DeepPartial = parseJson(chart.customAttr) + const s = JSON.parse(JSON.stringify(customAttr.basicStyle)) + // 圆形边框样式 + const pointStyle = { + stroke: s.circleBorderColor, + lineWidth: s.circleBorderWidth ?? 0 + } + const padding = s.circlePadding + return { + ...options, + hierarchyConfig: { + ...options.hierarchyConfig, + padding: typeof padding === 'number' && !isNaN(padding) ? padding / 100 : 0 + }, + pointStyle + } + } + + protected configLabel(chart: Chart, options: CirclePackingOptions): CirclePackingOptions { + const tmpOptions = super.configLabel(chart, options) + if (!tmpOptions.label) { + return { + ...tmpOptions, + label: false + } + } + const { label: labelAttr } = parseJson(chart.customAttr) + const label = { + ...tmpOptions.label, + textAlign: 'center', + offsetY: 5, + layout: labelAttr.fullDisplay ? [{ type: 'limit-in-plot' }] : tmpOptions.label.layout, + formatter: (d: Datum, _point) => { + return d.children.length === 0 ? d.name : '' + } + } + return { + ...tmpOptions, + label + } + } + + protected configTooltip(chart: Chart, options: CirclePackingOptions): CirclePackingOptions { + const temOptions = super.configTooltip(chart, options) + if (!temOptions.tooltip) { + return temOptions + } + const tooltipAttr = parseJson(chart.customAttr).tooltip + return { + ...temOptions, + tooltip: { + ...temOptions, + fields: ['name', 'value'], + formatter: d => { + let value = d.value + if (tooltipAttr.tooltipFormatter) { + value = valueFormatter(value, tooltipAttr.tooltipFormatter) + } + return { name: d.name, value } + } + } + } + } + configEmptyDataStrategy(chart: Chart, options: CirclePackingOptions): CirclePackingOptions { + const { functionCfg } = parseJson(chart.senior) + const emptyDataStrategy = functionCfg.emptyDataStrategy + const setChildren = children => { + if (emptyDataStrategy === 'ignoreData') { + for (let i = children.length - 1; i >= 0; i--) { + let isNotNullChildren = [] + if (children[i].children?.length) { + isNotNullChildren = children[i].children.filter(item => item.value !== null) + } + if (children[i].children?.length && isNotNullChildren.length) { + setChildren(children[i].children) + } + if (children[i]?.hasOwnProperty('value') && children[i].value === null) { + children.splice(i, 1) + } + if (!children[i]?.hasOwnProperty('value') && isNotNullChildren.length === 0) { + children.splice(i, 1) + } + } + } else { + for (let i = children.length - 1; i >= 0; i--) { + let isNotNullChildren = [] + if (children[i].children?.length) { + isNotNullChildren = children[i].children.filter(item => item.value !== null) + if (!isNotNullChildren.length) { + children[i].children = [] + continue + } + } + setChildren(children[i].children) + } + } + } + const data = cloneDeep(options.data.children) + setChildren(data) + options.data.children = data + return options + } + setupDefaultOptions(chart: ChartObj): ChartObj { + const { customAttr, customStyle, senior } = chart + const { label, basicStyle } = customAttr + const { legend } = customStyle + senior.functionCfg.emptyDataStrategy = 'ignoreData' + customAttr.label = { + ...label, + show: true + } + legend.show = false + basicStyle.circleBorderWidth = 0 + basicStyle.circleBorderColor = '#fff' + basicStyle.circlePadding = 0 + return chart + } + protected setupOptions(chart: Chart, options: CirclePackingOptions): CirclePackingOptions { + return flow( + this.configTheme, + this.configEmptyDataStrategy, + this.configBasicStyle, + this.configLabel, + this.configTooltip, + this.configLegend + )(chart, options) + } + + constructor() { + super('circle-packing', DEFAULT_DATA) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/others/funnel.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/others/funnel.ts new file mode 100644 index 0000000..2120b69 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/others/funnel.ts @@ -0,0 +1,215 @@ +import type { FunnelOptions, Funnel as G2Funnel } from '@antv/g2plot/esm/plots/funnel' +import { G2PlotChartView, G2PlotDrawOptions } from '../../types/impl/g2plot' +import { flow, parseJson, setUpSingleDimensionSeriesColor } from '@/data-visualization/chart/components/js/util' +import { configPlotTooltipEvent, getPadding } from '../../common/common_antv' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { Datum } from '@antv/g2plot/esm/types/common' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' + +const { t } = useI18n() + +/** + * 漏斗图 + */ +export class Funnel extends G2PlotChartView { + properties: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'label-selector', + 'tooltip-selector', + 'title-selector', + 'legend-selector', + 'jump-set', + 'linkage' + ] + propertyInner: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + 'basic-style-selector': ['colors', 'alpha', 'seriesColor'], + 'label-selector': ['fontSize', 'color', 'hPosition', 'showQuota', 'conversionTag'], + 'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'seriesTooltipFormatter', 'show'], + 'title-selector': [ + 'show', + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'legend-selector': ['icon', 'orient', 'color', 'fontSize', 'hPosition', 'vPosition'] + } + axis: AxisType[] = ['xAxis', 'yAxis', 'filter', 'drill', 'extLabel', 'extTooltip'] + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.drag_block_funnel_split')} / ${t('chart.dimension')}`, + type: 'd' + }, + yAxis: { + name: `${t('chart.drag_block_funnel_width')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + } + } + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (!chart.data?.data) { + return + } + const data = chart.data.data + const baseOptions: FunnelOptions = { + data, + xField: 'field', + yField: 'value', + appendPadding: getPadding(chart), + interactions: [ + { + type: 'legend-active', + cfg: { + start: [{ trigger: 'legend-item:mouseenter', action: ['element-active:reset'] }], + end: [{ trigger: 'legend-item:mouseleave', action: ['element-active:reset'] }] + } + }, + { + type: 'legend-filter', + cfg: { + start: [ + { + trigger: 'legend-item:click', + action: [ + 'list-unchecked:toggle', + 'data-filter:filter', + 'element-active:reset', + 'element-highlight:reset' + ] + } + ] + } + }, + { + type: 'tooltip', + cfg: { + start: [{ trigger: 'interval:mousemove', action: 'tooltip:show' }], + end: [{ trigger: 'interval:mouseleave', action: 'tooltip:hide' }] + } + } + ], + meta: { + field: { + type: 'cat' + } + } + } + const options = this.setupOptions(chart, baseOptions) + const { Funnel: G2Funnel } = await import('@antv/g2plot/esm/plots/funnel') + const newChart = new G2Funnel(container, options) + newChart.on('interval:click', action) + configPlotTooltipEvent(chart, newChart) + return newChart + } + + protected configLabel(chart: Chart, options: FunnelOptions): FunnelOptions { + let label + let conversionTag + let customAttr: DeepPartial + if (chart.customAttr) { + customAttr = parseJson(chart.customAttr) + const showQuota = customAttr.label.showQuota + const l = customAttr.label + if (customAttr.label?.show) { + // label + if (showQuota) { + const layout = [] + if (!l.fullDisplay) { + layout.push(...[{ type: 'hide-overlap' }, { type: 'limit-in-plot' }]) + } + label = { + position: l.position, + layout, + style: { + fill: l.color, + fontSize: l.fontSize + }, + formatter: function (param: Datum) { + return valueFormatter(param.value, l.quotaLabelFormatter) + } + } + const position = label.position + if (position === 'right') { + label.offsetX = -40 + } + } + // 转化率 + const conversionTagAtt = parseJson(chart.customAttr).label.conversionTag + if (conversionTagAtt && conversionTagAtt.show) { + conversionTag = { + style: { + fill: l.color, + fontSize: l.fontSize + }, + formatter: datum => { + if (!datum['$$conversion$$'][0]) { + return `${conversionTagAtt.text ?? ''} -` + } + const rate = ( + (datum['$$conversion$$'][1] / datum['$$conversion$$'][0]) * + 100 + ).toFixed(conversionTagAtt.precision) + return `${conversionTagAtt.text ?? ''} ${rate}%` + } + } + } + } + return { + ...options, + label, + conversionTag, + maxSize: conversionTag ? 0.8 : 1 + } + } + return options + } + + public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + return setUpSingleDimensionSeriesColor(chart, data) + } + protected setupOptions(chart: Chart, options: FunnelOptions): FunnelOptions { + return flow( + this.configTheme, + this.configSingleDimensionColor, + this.configLabel, + this.configMultiSeriesTooltip, + this.configLegend + )(chart, options) + } + setupDefaultOptions(chart: ChartObj): ChartObj { + const { customAttr, customStyle } = chart + const { label } = customAttr + if (!['left', 'middle', 'right'].includes(label.position)) { + label.position = 'middle' + } + customAttr.label = { + ...label, + show: true, + showQuota: true, + conversionTag: { + show: false, + precision: 2, + text: t('chart.conversion_rate') + } + } + const { legend } = customStyle + legend.show = false + return chart + } + + constructor() { + super('funnel', []) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/others/gauge.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/others/gauge.ts new file mode 100644 index 0000000..1a98729 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/others/gauge.ts @@ -0,0 +1,350 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import type { Gauge as G2Gauge, GaugeOptions } from '@antv/g2plot/esm/plots/gauge' +import { flow, parseJson } from '@/data-visualization/chart/components/js/util' +import { + DEFAULT_LABEL, + DEFAULT_MISC, + DEFAULT_THRESHOLD, + getScaleValue +} from '@/data-visualization/chart/components/editor/util/chart' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { getPadding, setGradientColor } from '@/data-visualization/chart/components/js/panel/common/common_antv' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { merge } from 'lodash-es' + +const { t } = useI18n() + +const DEFAULT_DATA = [] +export class Gauge extends G2PlotChartView { + properties: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'label-selector', + 'misc-selector', + 'title-selector', + 'threshold' + ] + propertyInner: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + 'basic-style-selector': ['colors', 'alpha', 'gradient', 'gaugeAxisLine', 'gaugePercentLabel'], + 'label-selector': ['fontSize', 'color', 'labelFormatter'], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'misc-selector': [ + 'gaugeMinType', + 'gaugeMinField', + 'gaugeMin', + 'gaugeMaxType', + 'gaugeMaxField', + 'gaugeMax', + 'gaugeStartAngle', + 'gaugeEndAngle' + ], + threshold: ['gaugeThreshold'] + } + axis: AxisType[] = ['yAxis', 'filter'] + axisConfig: AxisConfig = { + yAxis: { + name: `${t('chart.drag_block_gauge_angel')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + } + } + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, scale, action } = drawOptions + if (!chart.data?.series || !chart.yAxis.length) { + return + } + // options + const initOptions: GaugeOptions = { + percent: 0, + appendPadding: getPadding(chart), + axis: { + tickInterval: 0.2, + label: { + style: { + fontSize: getScaleValue(12, scale) // 刻度值字体大小 + } + }, + tickLine: { + length: getScaleValue(12, scale) * -1, // 刻度线长度 + style: { + lineWidth: getScaleValue(1, scale) // 刻度线宽度 + } + }, + subTickLine: { + count: 4, // 子刻度数 + length: getScaleValue(6, scale) * -1, // 子刻度线长度 + style: { + lineWidth: getScaleValue(1, scale) // 子刻度线宽度 + } + } + } + } + const options = this.setupOptions(chart, initOptions, { scale }) + const { Gauge: G2Gauge } = await import('@antv/g2plot/esm/plots/gauge') + const newChart = new G2Gauge(container, options) + newChart.on('afterrender', () => { + action({ + from: 'gauge', + data: { + type: 'gauge', + max: chart.data?.series[0]?.data[0] + } + }) + }) + const hasNoneData = chart.data?.series.some(s => !s.data?.[0]) + this.configEmptyDataStyle(newChart, hasNoneData ? [] : [1], container) + if (hasNoneData) { + return + } + return newChart + } + + protected configMisc( + chart: Chart, + options: GaugeOptions, + context: Record + ): GaugeOptions { + const customAttr = parseJson(chart.customAttr) + const data = chart.data.series[0].data[0] + let min, max, startAngle, endAngle + if (customAttr.misc) { + const misc = customAttr.misc + if (misc.gaugeMinType === 'dynamic' && misc.gaugeMaxType === 'dynamic') { + min = chart.data?.series[chart.data?.series.length - 2]?.data[0] + max = chart.data?.series[chart.data?.series.length - 1]?.data[0] + } else if (misc.gaugeMinType !== 'dynamic' && misc.gaugeMaxType === 'dynamic') { + min = misc.gaugeMin || misc.gaugeMin === 0 ? misc.gaugeMin : DEFAULT_MISC.gaugeMin + max = chart.data?.series[chart.data?.series.length - 1]?.data[0] + } else if (misc.gaugeMinType === 'dynamic' && misc.gaugeMaxType !== 'dynamic') { + min = chart.data?.series[chart.data?.series.length - 1]?.data[0] + max = misc.gaugeMax ? misc.gaugeMax : DEFAULT_MISC.gaugeMax + } else { + min = misc.gaugeMin || misc.gaugeMin === 0 ? misc.gaugeMin : DEFAULT_MISC.gaugeMin + max = misc.gaugeMax + ? misc.gaugeMax + : chart.data?.series[chart.data?.series.length - 1]?.data[0] + } + startAngle = (misc.gaugeStartAngle * Math.PI) / 180 + endAngle = (misc.gaugeEndAngle * Math.PI) / 180 + context.min = min + context.max = max + } + const percent = (parseFloat(data) - parseFloat(min)) / (parseFloat(max) - parseFloat(min)) + const tmp = { + percent, + startAngle, + endAngle + } + return { ...options, ...tmp } + } + + private configRange( + chart: Chart, + options: GaugeOptions, + context: Record + ): GaugeOptions { + const { scale } = context + const range = [0] + let index = 0 + let flag = false + let hasThreshold = false + const theme = options.theme as any + + if (chart.senior) { + const senior = parseJson(chart.senior) + const threshold = senior.threshold ?? DEFAULT_THRESHOLD + if (threshold.enable && threshold.gaugeThreshold) { + hasThreshold = true + const arr = threshold.gaugeThreshold.split(',') + for (let i = 0; i < arr.length; i++) { + const ele = arr[i] + const p = parseFloat(ele) / 100 + range.push(p) + if (!flag && options.percent <= p) { + flag = true + index = i + } + } + if (!flag) { + index = arr.length + } + } + } + range.push(1) + let rangOptions + if (hasThreshold) { + rangOptions = { + range: { + color: theme.styleSheet.paletteQualitative10, + ticks: range + }, + indicator: { + pointer: { + style: { + stroke: + theme.styleSheet.paletteQualitative10[ + index % theme.styleSheet.paletteQualitative10.length + ] + } + }, + pin: { + style: { + stroke: + theme.styleSheet.paletteQualitative10[ + index % theme.styleSheet.paletteQualitative10.length + ], + r: getScaleValue(10, scale) + } + } + } + } + } else { + rangOptions = { + indicator: { + pin: { + style: { + r: getScaleValue(10, scale) + } + } + } + } + } + const customAttr = parseJson(chart.customAttr) + if (customAttr.basicStyle.gradient) { + const colorList = (theme.styleSheet?.paletteQualitative10 || []).map(ele => { + return setGradientColor(ele, true) + }) + if (!rangOptions.range) { + rangOptions.range = { + color: colorList + } + } else { + rangOptions.range.color = colorList + } + } + return { ...options, ...rangOptions } + } + + protected configLabel( + chart: Chart, + options: GaugeOptions, + context?: Record + ): GaugeOptions { + const customAttr = parseJson(chart.customAttr) + const data = chart.data.series[0].data[0] + let labelTitle: GaugeOptions['statistic']['title'] = false + let labelContent: GaugeOptions['statistic']['content'] = false + const label = customAttr.label + const labelFormatter = label.labelFormatter ?? DEFAULT_LABEL.labelFormatter + if (label.show && label.childrenShow) { + labelTitle = { + style: { + fontSize: `${label.fontSize}px`, + color: label.color + }, + formatter: function () { + let value + if (labelFormatter.type === 'percent') { + value = options.percent + } else { + value = data + } + return valueFormatter(value, labelFormatter) + } + } as GaugeOptions['statistic']['title'] + } + const { min, max } = context + if (label.show && label.proportionSeriesFormatter.show) { + const proportionFormatter = label.proportionSeriesFormatter + labelContent = { + offsetY: proportionFormatter.fontSize + label.fontSize, + style: { + fontSize: `${proportionFormatter.fontSize}px`, + color: proportionFormatter.color + }, + formatter: function () { + const proportionValue = ((parseFloat(data) - min) / (max - min)) * 100 + return ( + t('chart.proportion') + + ': ' + + proportionValue.toFixed(proportionFormatter.formatterCfg.decimalCount) + + '%' + ) + } + } as GaugeOptions['statistic']['content'] + } + const statistic = { + title: labelTitle, + content: labelContent + } + const { gaugeAxisLine, gaugePercentLabel } = customAttr.basicStyle + const tmp = { + axis: { + label: { + formatter: v => { + if (gaugeAxisLine === false) { + return '' + } + if (gaugePercentLabel === false) { + const resultV = v === '0' ? min : v === '1' ? max : min + (max - min) * v + return labelFormatter.type === 'value' + ? valueFormatter(resultV, labelFormatter) + : resultV + } + return v === '0' ? v : v * 100 + '%' + } + } + } + } + options = merge(options, tmp) + return { ...options, statistic } + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + chart.customAttr.label = { + ...chart.customAttr.label, + show: true, + labelFormatter: { + type: 'value', + thousandSeparator: true, + decimalCount: 0, + unit: 1 + } + } + return chart + } + + protected setupOptions( + chart: Chart, + options: GaugeOptions, + context: Record + ): GaugeOptions { + return flow( + this.configTheme, + this.configMisc, + this.configLabel, + this.configRange + )(chart, options, context) + } + constructor() { + super('gauge', DEFAULT_DATA) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/others/indicator.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/others/indicator.ts new file mode 100644 index 0000000..c28611c --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/others/indicator.ts @@ -0,0 +1,76 @@ +import { AbstractChartView, ChartLibraryType, ChartRenderType } from '../../types' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { COLOR_CASES } from '@/data-visualization/chart/components/editor/util/chart' + +const { t } = useI18n() +/** + * 指标卡图表 + */ +export class IndicatorChartView extends AbstractChartView { + selectorSpec: EditorSelectorSpec + properties: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'title-selector', + 'indicator-value-selector', + 'indicator-name-selector', + 'threshold', + 'function-cfg' + ] + propertyInner: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'indicator-value-selector': [ + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'indicator-name-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'function-cfg': ['emptyDataStrategy'] + } + axis: AxisType[] = ['yAxis', 'filter'] + axisConfig: AxisConfig = { + yAxis: { + name: `${t('chart.quota')}`, + limit: 1 + } + } + setupDefaultOptions(chart: ChartObj): ChartObj { + const basicColors = COLOR_CASES[0].colors + chart.customAttr.basicStyle.colors = basicColors + chart.customAttr.indicator.color = basicColors[0] + chart.customAttr.indicatorName.color = basicColors[1] + return chart + } + + constructor() { + super(ChartRenderType.CUSTOM, ChartLibraryType.INDICATOR, 'indicator') + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/others/picture-group.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/others/picture-group.ts new file mode 100644 index 0000000..bd349c6 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/others/picture-group.ts @@ -0,0 +1,29 @@ +import { AbstractChartView, ChartLibraryType, ChartRenderType } from '../../types' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' + +const { t } = useI18n() +/** + * 图片组图表 + */ +export class PictureGroupView extends AbstractChartView { + properties: EditorProperty[] = ['background-overall-component', 'border-style', 'threshold'] + propertyInner: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + threshold: ['tableThreshold'] + } + axis: AxisType[] = ['xAxis', 'yAxis', 'filter'] + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.dimension')}`, + type: 'd' + }, + yAxis: { + name: `${t('chart.quota')}`, + type: 'q' + } + } + constructor() { + super(ChartRenderType.CUSTOM, ChartLibraryType.PICTURE_GROUP, 'picture-group') + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/others/quadrant.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/others/quadrant.ts new file mode 100644 index 0000000..d9d4e34 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/others/quadrant.ts @@ -0,0 +1,488 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import type { ScatterOptions, Scatter as G2Scatter } from '@antv/g2plot/esm/plots/scatter' +import { flow, parseJson, setUpSingleDimensionSeriesColor } from '../../../util' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { defaults, isEmpty, map } from 'lodash-es' +import { cloneDeep, defaultTo } from 'lodash-es' +import { + configAxisLabelLengthLimit, + configPlotTooltipEvent, + configYaxisTitleLengthLimit, + getTooltipContainer, + TOOLTIP_TPL +} from '../../common/common_antv' +import { DEFAULT_LEGEND_STYLE } from '@/data-visualization/chart/components/editor/util/chart' + +const { t } = useI18n() +/** + * 象限图 + */ +export class Quadrant extends G2PlotChartView { + properties: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'x-axis-selector', + 'y-axis-selector', + 'title-selector', + 'label-selector', + 'tooltip-selector', + 'legend-selector', + 'jump-set', + 'linkage', + 'quadrant-selector' + ] + propertyInner: EditorPropertyInner = { + 'basic-style-selector': [ + 'colors', + 'alpha', + 'scatterSymbol', + 'scatterSymbolSize', + 'seriesColor' + ], + 'label-selector': ['fontSize', 'color'], + 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'], + 'x-axis-selector': [ + 'position', + 'name', + 'color', + 'fontSize', + 'axisLine', + 'axisValue', + 'splitLine', + 'axisForm', + 'axisLabel', + 'axisLabelFormatter' + ], + 'y-axis-selector': [ + 'position', + 'name', + 'color', + 'fontSize', + 'axisValue', + 'axisLine', + 'splitLine', + 'axisForm', + 'axisLabel', + 'axisLabelFormatter' + ], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'legend-selector': ['icon', 'orient', 'color', 'fontSize', 'hPosition', 'vPosition'], + 'quadrant-selector': ['regionStyle', 'label', 'lineStyle'] + } + axis: AxisType[] = [ + 'xAxis', + 'yAxis', + 'yAxisExt', + 'extBubble', + 'filter', + 'drill', + 'extLabel', + 'extTooltip' + ] + axisConfig: AxisConfig = { + extBubble: { + name: `${t('chart.bubble_size')} / ${t('chart.quota')}`, + type: 'q', + limit: 1, + allowEmpty: true + }, + xAxis: { + name: `${t('chart.form_type')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1 + }, + yAxis: { + name: `${t('chart.x_axis')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + }, + yAxisExt: { + name: `${t('chart.y_axis')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + } + } + + public getFieldObject(chart: Chart) { + const colorFieldObj = { id: chart.xAxis[0]?.id, name: chart.xAxis[0]?.['originName'] } + const sizeFieldObj = { id: chart.extBubble[0]?.id, name: chart.extBubble[0]?.['originName'] } + const xFieldObj = { id: chart.yAxis[0]?.id, name: chart.yAxis[0]?.['originName'] } + const yFieldObj = { id: chart.yAxisExt[0]?.id, name: chart.yAxisExt[0]?.['originName'] } + return { colorFieldObj, sizeFieldObj, xFieldObj, yFieldObj } + } + public getUniqueObjects(arr: T[]): T[] { + return [...new Set(arr.map(JSON.stringify))].map(JSON.parse) as T[] + } + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action, quadrantDefaultBaseline } = drawOptions + if (!chart.data?.data) { + return + } + // data + const sourceData: Array = cloneDeep(chart.data.data) + const data1 = defaultTo(sourceData[0]?.data, []) + const data2 = defaultTo(sourceData[1]?.data, []) + const data3 = defaultTo(sourceData[2]?.data, []) + const xData = data1.map(item => { + return { + ...item, + id: item.quotaList[0]?.id, + field: item.field, + value: item.value + } + }) + const yData = data2.map(item => { + return { + ...item, + id: item.quotaList[0]?.id, + field: item.field, + value: item.value + } + }) + const eData = data3.map(item => { + return { + ...item, + id: item.quotaList[0]?.id, + field: item.field, + value: item.value + } + }) + // x轴基准线 默认值 + const xValues = xData.map(item => item.value) + const xBaseline = ((Math.max(...xValues) + Math.min(...xValues)) / 2).toFixed() + // y轴基准线 默认值 + const yValues = yData.map(item => item.value) + const yBaseline = ((Math.max(...yValues) + Math.min(...yValues)) / 2).toFixed() + const defaultBaselineQuadrant = { + ...chart.customAttr['quadrant'] + } + // 新建图表 + if (defaultBaselineQuadrant.xBaseline === undefined) { + // 默认基准线值 + defaultBaselineQuadrant.xBaseline = xBaseline + defaultBaselineQuadrant.yBaseline = yBaseline + } + const getQuotaList = d => { + const eQuotaList = eData.find(item => item.field === d.field)?.quotaList + const yQuotaList = yData.find(item => item.field === d.field)?.quotaList + if (JSON.stringify(eQuotaList) === JSON.stringify(yQuotaList)) { + return yQuotaList + } + return [...(eQuotaList || []), ...(yQuotaList || [])] + } + const data = map(defaultTo(xData, []), d => { + return { + ...d, + yAxis: d.value, + quotaList: getQuotaList(d), + yAxisExt: yData.find(item => item.field === d.field)?.value, + extBubble: eData.find(item => item.field === d.field)?.value + } + }) + const baseOptions: ScatterOptions = { + colorField: 'field', + meta: { + field: { + type: 'cat' + } + }, + quadrant: { + ...defaultBaselineQuadrant + }, + data: data, + xField: 'yAxis', + yField: 'yAxisExt', + appendPadding: 30, + pointStyle: { + fillOpacity: 0.8, + stroke: '#bbb' + } + } + chart.container = container + const options = this.setupOptions(chart, baseOptions) + const { Scatter: G2Scatter } = await import('@antv/g2plot/esm/plots/scatter') + const newChart = new G2Scatter(container, options) + newChart.on('point:click', action) + newChart.on('click', () => quadrantDefaultBaseline(defaultBaselineQuadrant)) + newChart.on('afterrender', () => quadrantDefaultBaseline(defaultBaselineQuadrant)) + const yAxis = parseJson(chart.customStyle).yAxis + if (yAxis?.name) { + configYaxisTitleLengthLimit(chart, newChart) + configAxisLabelLengthLimit(chart, newChart, 'axis-title') + } + configPlotTooltipEvent(chart, newChart) + return newChart + } + + protected configBasicStyle(chart: Chart, options: ScatterOptions): ScatterOptions { + const customAttr = parseJson(chart.customAttr) + const basicStyle = customAttr.basicStyle + if (chart.extBubble?.length) { + return { + ...options, + size: [4, 30], + sizeField: 'extBubble', + shape: basicStyle.scatterSymbol + } + } + return { + ...options, + size: basicStyle.scatterSymbolSize, + shape: basicStyle.scatterSymbol + } + } + + protected configXAxis(chart: Chart, options: ScatterOptions): ScatterOptions { + const tmpOptions = super.configXAxis(chart, options) + if (!tmpOptions.xAxis) { + return tmpOptions + } + const xAxis = parseJson(chart.customStyle).xAxis + if (tmpOptions.xAxis.label) { + tmpOptions.xAxis.label.formatter = value => { + return valueFormatter(value, xAxis.axisLabelFormatter) + } + } + const axisValue = xAxis.axisValue + if (!axisValue?.auto) { + const axis = { + xAxis: { + ...tmpOptions.xAxis, + min: axisValue.min, + max: axisValue.max, + minLimit: axisValue.min, + maxLimit: axisValue.max, + tickCount: axisValue.splitCount + } + } + return { ...tmpOptions, ...axis } + } + return tmpOptions + } + + protected configYAxis(chart: Chart, options: ScatterOptions): ScatterOptions { + const tmpOptions = super.configYAxis(chart, options) + if (!tmpOptions.yAxis) { + return tmpOptions + } + const yAxis = parseJson(chart.customStyle).yAxis + if (tmpOptions.yAxis.label) { + tmpOptions.yAxis.label.formatter = value => { + return valueFormatter(value, yAxis.axisLabelFormatter) + } + } + const axisValue = yAxis.axisValue + if (!axisValue?.auto) { + const axis = { + yAxis: { + ...tmpOptions.yAxis, + min: axisValue.min, + max: axisValue.max, + minLimit: axisValue.min, + maxLimit: axisValue.max, + tickCount: axisValue.splitCount + } + } + return { ...tmpOptions, ...axis } + } + return tmpOptions + } + + protected configLabel(chart: Chart, options: ScatterOptions): ScatterOptions { + let label + let customAttr: DeepPartial + if (chart.customAttr) { + customAttr = parseJson(chart.customAttr) + // label + if (customAttr.label) { + const l = customAttr.label + if (l.show) { + const layout = [] + if (!l.fullDisplay) { + layout.push({ type: 'hide-overlap' }) + layout.push({ type: 'limit-in-shape' }) + } + label = { + offset: 0, + style: { + fill: l.color, + fontSize: l.fontSize + }, + content: datum => { + return datum['name'] + }, + layout + } + } else { + label = false + } + } + } + return { ...options, label } + } + + protected configTooltip(chart: Chart, options: ScatterOptions): ScatterOptions { + const customAttr: DeepPartial = parseJson(chart.customAttr) + const tooltipAttr = customAttr.tooltip + const xAxisTitle = chart.xAxis[0] + const yAxisTitle = chart.yAxis[0] + const yAxisExtTitle = chart.yAxisExt[0] + if (!tooltipAttr.show || (!xAxisTitle && !yAxisTitle && !yAxisExtTitle)) { + return { + ...options, + tooltip: false + } + } + const formatterMap = tooltipAttr.seriesTooltipFormatter + ?.filter(i => i.show) + .reduce((pre, next) => { + pre[next['seriesId']] = next + return pre + }, {}) as Record + const optionsData = cloneDeep(options.data) + const tooltip: ScatterOptions['tooltip'] = { + showTitle: true, + title: (_title, datum) => { + return datum?.['name'] + }, + customItems(originalItems) { + if (!tooltipAttr.seriesTooltipFormatter?.length) { + return originalItems + } + const result = [] + originalItems.forEach(item => { + Object.keys(formatterMap).forEach(key => { + if (key.endsWith(item.name)) { + const formatter = formatterMap[key] + if (formatter) { + const value = + formatter.groupType === 'q' + ? valueFormatter(parseFloat(item.value as string), formatter.formatterCfg) + : item.value + const name = isEmpty(formatter.chartShowName) + ? formatter.name + : formatter.chartShowName + result.push({ color: item.color, name, value }) + } + } + }) + }) + const dynamicTooltipValue = optionsData.find( + d => d.field === originalItems[0]['title'] + )?.dynamicTooltipValue + if (dynamicTooltipValue.length > 0) { + dynamicTooltipValue.forEach(dy => { + const q = tooltipAttr.seriesTooltipFormatter.filter(i => i.id === dy.fieldId) + if (q && q.length > 0) { + const value = valueFormatter(parseFloat(dy.value as string), q[0].formatterCfg) + const name = isEmpty(q[0].chartShowName) ? q[0].name : q[0].chartShowName + result.push({ color: 'grey', name, value }) + } + }) + } + return result + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + return { + ...options, + tooltip + } + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + chart.customStyle.yAxis.splitLine = { + ...chart.customStyle.yAxis.splitLine, + show: false + } + chart.customStyle.yAxisExt.splitLine = { + ...chart.customStyle.yAxisExt.splitLine, + show: false + } + chart.customStyle.yAxis.axisLine = { + ...chart.customStyle.yAxis.axisLine, + show: true + } + chart.customStyle.yAxisExt.axisLine = { + ...chart.customStyle.yAxisExt.axisLine, + show: true + } + return chart + } + + protected configColor(chart: Chart, options: ScatterOptions): ScatterOptions { + const { xAxis, yAxis, yAxisExt } = chart + if (!(xAxis?.length && yAxis?.length && yAxisExt?.length)) { + return options + } + return this.configSingleDimensionColor(chart, options) + } + + public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + const { xAxis, yAxis, yAxisExt } = chart + if (!(xAxis?.length && yAxis?.length && yAxisExt?.length)) { + return [] + } + const tmp = data?.[0]?.data + return setUpSingleDimensionSeriesColor(chart, tmp) + } + + protected configLegend(chart: Chart, options: ScatterOptions): ScatterOptions { + const optionTmp = super.configLegend(chart, options) + if (!optionTmp.legend) { + return optionTmp + } + const customStyle = parseJson(chart.customStyle) + let size + if (customStyle && customStyle.legend) { + size = defaults(JSON.parse(JSON.stringify(customStyle.legend)), DEFAULT_LEGEND_STYLE).size + } else { + size = DEFAULT_LEGEND_STYLE.size + } + optionTmp.legend.marker.style = style => { + return { + r: size, + fill: style.fill + } + } + return optionTmp + } + + protected setupOptions(chart: Chart, options: ScatterOptions) { + return flow( + this.configTheme, + this.configColor, + this.configLabel, + this.configTooltip, + this.configLegend, + this.configXAxis, + this.configYAxis, + this.configAnalyse, + this.configSlider, + this.configBasicStyle + )(chart, options, {}, this) + } + + constructor() { + super('quadrant', []) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/others/radar.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/others/radar.ts new file mode 100644 index 0000000..4da7509 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/others/radar.ts @@ -0,0 +1,306 @@ +import type { RadarOptions, Radar as G2Radar } from '@antv/g2plot/esm/plots/radar' +import { G2PlotChartView, G2PlotDrawOptions } from '../../types/impl/g2plot' +import { flow, parseJson } from '../../../util' +import { configPlotTooltipEvent } from '../../common/common_antv' +import { valueFormatter } from '../../../formatter' +import type { Datum } from '@antv/g2plot/esm/types/common' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { DEFAULT_LABEL, DEFAULT_LEGEND_STYLE } from '@/data-visualization/chart/components/editor/util/chart' +import { Group } from '@antv/g-canvas' +import { defaults } from 'lodash-es' + +const { t } = useI18n() + +export class Radar extends G2PlotChartView { + properties: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'label-selector', + 'tooltip-selector', + 'title-selector', + 'legend-selector', + 'misc-style-selector', + 'jump-set', + 'linkage' + ] + propertyInner: EditorPropertyInner = { + 'basic-style-selector': [ + 'colors', + 'alpha', + 'radarShape', + 'seriesColor', + 'radarShowPoint', + 'radarPointSize', + 'radarAreaColor' + ], + 'label-selector': ['seriesLabelFormatter'], + 'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'seriesTooltipFormatter', 'show'], + 'misc-style-selector': ['showName', 'color', 'fontSize', 'axisColor', 'axisValue'], + 'title-selector': [ + 'show', + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'legend-selector': ['icon', 'orient', 'color', 'fontSize', 'hPosition', 'vPosition'] + } + selectorSpec: EditorSelectorSpec = { + ...this['selectorSpec'], + 'misc-style-selector': { + title: `${t('chart.tooltip_axis')}` + } + } + axis: AxisType[] = ['xAxis', 'yAxis', 'drill', 'filter', 'extLabel', 'extTooltip'] + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.drag_block_radar_label')} / ${t('chart.dimension')}`, + type: 'd' + }, + yAxis: { + name: `${t('chart.drag_block_radar_length')} / ${t('chart.quota')}`, + type: 'q' + } + } + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (!chart.data?.data) { + return + } + const data = chart.data.data + const baseOptions: RadarOptions = { + data, + xField: 'field', + yField: 'value', + seriesField: 'category', + appendPadding: [10, 10, 10, 10], + interactions: [ + { + type: 'legend-active', + cfg: { + start: [{ trigger: 'legend-item:mouseenter', action: ['element-active:reset'] }], + end: [{ trigger: 'legend-item:mouseleave', action: ['element-active:reset'] }] + } + }, + { + type: 'legend-filter', + cfg: { + start: [ + { + trigger: 'legend-item:click', + action: [ + 'list-unchecked:toggle', + 'data-filter:filter', + 'element-active:reset', + 'element-highlight:reset' + ] + } + ] + } + }, + { + type: 'active-region', + cfg: { + start: [{ trigger: 'point:mousemove', action: 'active-region:show' }], + end: [{ trigger: 'point:mouseleave', action: 'active-region:hide' }] + } + } + ] + } + const options = this.setupOptions(chart, baseOptions) + const { Radar: G2Radar } = await import('@antv/g2plot/esm/plots/radar') + const newChart = new G2Radar(container, options) + newChart.on('point:click', action) + if (options.label) { + newChart.on('label:click', e => { + action({ + x: e.x, + y: e.y, + data: { + data: e.target.attrs.data + } + }) + }) + } + configPlotTooltipEvent(chart, newChart) + return newChart + } + + protected configBasicStyle(chart: Chart, options: RadarOptions): RadarOptions { + const { radarShowPoint, radarPointSize, radarAreaColor } = parseJson( + chart.customAttr + ).basicStyle + const tempOptions: RadarOptions = {} + + if (radarShowPoint) { + tempOptions['point'] = { shape: 'circle', size: radarPointSize, style: { fill: null } } + } + if (radarAreaColor) { + tempOptions['area'] = {} + } + + return { ...options, ...tempOptions } + } + + protected configLabel(chart: Chart, options: RadarOptions): RadarOptions { + const tmpOptions = super.configLabel(chart, options) + if (!tmpOptions.label) { + return { + ...tmpOptions, + label: false + } + } + const labelAttr = parseJson(chart.customAttr).label + const formatterMap = labelAttr.seriesLabelFormatter?.reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) + tmpOptions.label.style.fill = DEFAULT_LABEL.color + // 自动旋转和标签自定义有冲突 + const label = { + fields: [], + ...tmpOptions.label, + autoRotate: false, + autoHide: true, + formatter: (data: Datum) => { + if (!labelAttr.seriesLabelFormatter?.length) { + return data.value + } + const labelCfg = formatterMap?.[data.quotaList[0].id] as SeriesFormatter + if (!labelCfg) { + return data.value + } + if (!labelCfg.show) { + return + } + const value = valueFormatter(data.value, labelCfg.formatterCfg) + const group = new Group({}) + group.addShape({ + type: 'text', + attrs: { + data, + x: 0, + y: 0, + text: value, + textAlign: 'start', + textBaseline: 'top', + fontSize: labelCfg.fontSize, + fill: labelCfg.color + } + }) + return group + } + } + return { + ...tmpOptions, + label + } + } + + protected configAxis(chart: Chart, options: RadarOptions): RadarOptions { + const customAttr = parseJson(chart.customAttr) + const customStyle = parseJson(chart.customStyle) + const basicStyle = customAttr.basicStyle + const misc = customStyle.misc + let label: any = { + style: { + fill: misc.color, + fontSize: misc.fontSize + } + } + if (!misc.showName) { + label = false + } + const xAxis = { + line: null, + tickLine: null, + label, + grid: { + line: { + style: { + stroke: misc.axisColor + } + } + } + } + const yAxis = { + label: null, + line: null, + tickLine: null, + grid: { + line: { + type: basicStyle.radarShape, + style: { + stroke: misc.axisColor + } + } + } + } + const axisValue = misc.axisValue + if (!axisValue?.auto) { + const axisYAxis = { + ...yAxis, + min: axisValue.min, + max: axisValue.max, + minLimit: axisValue.min, + maxLimit: axisValue.max, + tickCount: axisValue.splitCount + } + return { + ...options, + xAxis, + yAxis: axisYAxis + } + } + return { + ...options, + xAxis, + yAxis + } + } + + protected configLegend(chart: Chart, options: RadarOptions): RadarOptions { + const optionTmp = super.configLegend(chart, options) + if (!optionTmp.legend) { + return optionTmp + } + const customStyle = parseJson(chart.customStyle) + let size + if (customStyle && customStyle.legend) { + size = defaults(JSON.parse(JSON.stringify(customStyle.legend)), DEFAULT_LEGEND_STYLE).size + } else { + size = DEFAULT_LEGEND_STYLE.size + } + optionTmp.legend.marker.style = style => { + return { + r: size, + fill: style.stroke + } + } + return optionTmp + } + + protected setupOptions(chart: Chart, options: RadarOptions): RadarOptions { + return flow( + this.configTheme, + this.configColor, + this.configLabel, + this.configLegend, + this.configMultiSeriesTooltip, + this.configAxis, + this.configBasicStyle + )(chart, options) + } + + constructor() { + super('radar', []) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/others/rich-text.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/others/rich-text.ts new file mode 100644 index 0000000..e0fa370 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/others/rich-text.ts @@ -0,0 +1,37 @@ +import { AbstractChartView, ChartLibraryType, ChartRenderType } from '../../types' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' + +const { t } = useI18n() +/** + * 富文本图表 + */ +export class RichTextChartView extends AbstractChartView { + properties: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'threshold', + 'function-cfg' + ] + propertyInner: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + threshold: ['tableThreshold'], + 'function-cfg': ['emptyDataStrategy'] + } + axis: AxisType[] = ['xAxis', 'yAxis', 'filter'] + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.dimension')}`, + type: 'd', + allowEmpty: true + }, + yAxis: { + name: `${t('chart.quota')}`, + type: 'q', + allowEmpty: true + } + } + constructor() { + super(ChartRenderType.CUSTOM, ChartLibraryType.RICH_TEXT, 'rich-text') + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/others/sankey-common.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/others/sankey-common.ts new file mode 100644 index 0000000..d080ac6 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/others/sankey-common.ts @@ -0,0 +1,40 @@ +export const SANKEY_EDITOR_PROPERTY: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'label-selector', + 'tooltip-selector', + 'title-selector', + 'jump-set', + 'linkage' +] + +export const SANKEY_EDITOR_PROPERTY_INNER: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + 'basic-style-selector': ['colors', 'alpha', 'gradient'], + 'label-selector': ['fontSize', 'color', 'labelFormatter'], + 'tooltip-selector': ['fontSize', 'color', 'tooltipFormatter'], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'function-cfg': ['slider', 'emptyDataStrategy'] +} + +export const SANKEY_AXIS_TYPE: AxisType[] = [ + 'xAxis', + 'xAxisExt', + 'yAxis', + 'filter', + 'extLabel', + 'extTooltip' +] diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/others/sankey.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/others/sankey.ts new file mode 100644 index 0000000..783efa6 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/others/sankey.ts @@ -0,0 +1,285 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import type { Sankey, SankeyOptions } from '@antv/g2plot/esm/plots/sankey' +import { getPadding, setGradientColor } from '@/data-visualization/chart/components/js/panel/common/common_antv' +import { cloneDeep, get } from 'lodash-es' +import { flow, hexColorToRGBA, parseJson } from '@/data-visualization/chart/components/js/util' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' + +import { Datum } from '@antv/g2plot/esm/types/common' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { + SANKEY_AXIS_TYPE, + SANKEY_EDITOR_PROPERTY, + SANKEY_EDITOR_PROPERTY_INNER +} from '@/data-visualization/chart/components/js/panel/charts/others/sankey-common' + +const { t } = useI18n() +const DEFAULT_DATA = [] + +/** + * 桑基图 + */ +export class SankeyBar extends G2PlotChartView { + axisConfig = { + xAxis: { + name: `${t('chart.drag_block_type_axis_start')} / ${t('chart.dimension')}`, + limit: 1, + type: 'd' + }, + xAxisExt: { + name: `${t('chart.drag_block_type_axis_end')} / ${t('chart.dimension')}`, + limit: 1, + type: 'd', + allowEmpty: true + }, + yAxis: { + name: `${t('chart.chart_data')} / ${t('chart.quota')}`, + limit: 1, + type: 'q' + } + } + properties = SANKEY_EDITOR_PROPERTY + propertyInner = { + ...SANKEY_EDITOR_PROPERTY_INNER, + 'label-selector': ['color', 'fontSize'], + 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter', 'show'] + } + axis: AxisType[] = [...SANKEY_AXIS_TYPE] + protected baseOptions: SankeyOptions = { + data: [], + sourceField: 'source', + targetField: 'target', + weightField: 'value', + rawFields: ['dimensionList', 'quotaList'], + interactions: [ + { + type: 'legend-active', + cfg: { + start: [{ trigger: 'legend-item:mouseenter', action: ['element-active:reset'] }], + end: [{ trigger: 'legend-item:mouseleave', action: ['element-active:reset'] }] + } + }, + { + type: 'legend-filter', + cfg: { + start: [ + { + trigger: 'legend-item:click', + action: [ + 'list-unchecked:toggle', + 'data-filter:filter', + 'element-active:reset', + 'element-highlight:reset' + ] + } + ] + } + }, + { + type: 'tooltip', + cfg: { + start: [{ trigger: 'interval:mousemove', action: 'tooltip:show' }], + end: [{ trigger: 'interval:mouseleave', action: 'tooltip:hide' }] + } + }, + { + type: 'active-region', + cfg: { + start: [{ trigger: 'interval:mousemove', action: 'active-region:show' }], + end: [{ trigger: 'interval:mouseleave', action: 'active-region:hide' }] + } + } + ] + } + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (!chart.data?.data?.length) { + return + } + // data + const data: Array = cloneDeep(chart.data.data) + + data.forEach(d => { + if (d.dimensionList) { + if (d.dimensionList[0]) { + d.source = d.dimensionList[0].value + } + if (d.dimensionList[1]) { + d.target = d.dimensionList[1].value + } + } + }) + + // options + const initOptions: SankeyOptions = { + ...this.baseOptions, + appendPadding: getPadding(chart), + data, + nodeSort: (a, b) => { + // 这里是前端自己排序 + if (chart.yAxis && chart.yAxis[0]) { + if (chart.yAxis[0].sort === 'asc') { + return a.value - b.value + } else if (chart.yAxis[0].sort === 'desc') { + return b.value - a.value + } + } + + if (chart.xAxis && chart.xAxis[0] && a.sourceLinks.length > 0) { + if (chart.xAxis[0].sort === 'custom_sort' && chart.xAxis[0].customSort) { + return ( + chart.xAxis[0].customSort.indexOf(a.name) - chart.xAxis[0].customSort.indexOf(b.name) + ) + } else if (chart.xAxis[0].sort === 'asc') { + return a.name.localeCompare(b.name) + } else if (chart.xAxis[0].sort === 'desc') { + return b.name.localeCompare(a.name) + } + } + if (chart.xAxisExt && chart.xAxisExt[0] && a.targetLinks.length > 0) { + if (chart.xAxisExt[0].sort === 'custom_sort' && chart.xAxisExt[0].customSort) { + return ( + chart.xAxisExt[0].customSort.indexOf(a.name) - + chart.xAxisExt[0].customSort.indexOf(b.name) + ) + } else if (chart.xAxisExt[0].sort === 'asc') { + return a.name.localeCompare(b.name) + } else if (chart.xAxisExt[0].sort === 'desc') { + return b.name.localeCompare(a.name) + } + } + + return b.value - a.value + } + } + + const options = this.setupOptions(chart, initOptions) + const { Sankey } = await import('@antv/g2plot/esm/plots/sankey') + // 开始渲染 + const newChart = new Sankey(container, options) + + newChart.on('edge:click', action) + + return newChart + } + + protected configTooltip(chart: Chart, options: SankeyOptions): SankeyOptions { + let tooltip + let customAttr: DeepPartial + if (chart.customAttr) { + customAttr = parseJson(chart.customAttr) + // tooltip + if (customAttr.tooltip) { + const t = JSON.parse(JSON.stringify(customAttr.tooltip)) + if (t.show) { + tooltip = { + showTitle: false, + showMarkers: false, + shared: false, + // 内置:node 不显示 tooltip,edge 显示 tooltip + showContent: items => { + return !get(items, [0, 'data', 'isNode']) + }, + formatter: (datum: Datum) => { + const { source, target, value } = datum + return { + name: source + ' -> ' + target, + value: valueFormatter(value, t.tooltipFormatter) + } + } + } + } else { + tooltip = false + } + } + } + return { ...options, tooltip } + } + + protected configBasicStyle(chart: Chart, options: SankeyOptions): SankeyOptions { + const basicStyle = parseJson(chart.customAttr).basicStyle + + let color = basicStyle.colors + color = color.map(ele => { + const tmp = hexColorToRGBA(ele, basicStyle.alpha) + if (basicStyle.gradient) { + return setGradientColor(tmp, true) + } else { + return tmp + } + }) + + options = { + ...options, + color + } + return options + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + const { customAttr, senior } = chart + const { label } = customAttr + if (!['left', 'middle', 'right'].includes(label.position)) { + label.position = 'middle' + } + senior.functionCfg.emptyDataStrategy = 'ignoreData' + return chart + } + + protected configLabel(chart: Chart, options: SankeyOptions): SankeyOptions { + const labelAttr = parseJson(chart.customAttr).label + if (labelAttr.show) { + const layout = [] + if (!labelAttr.fullDisplay) { + layout.push(...[{ type: 'hide-overlap' }, { type: 'limit-in-canvas' }]) + } + const label = { + //...tmpOptions.label, + formatter: ({ name }) => name, + callback: (x: number[]) => { + const isLast = x[1] === 1 // 最后一列靠边的节点 + return { + style: { + fill: labelAttr.color, + fontSize: labelAttr.fontSize, + textAlign: isLast ? 'end' : 'start', + fontFamily: chart.fontFamily + }, + offsetX: isLast ? -8 : 8 + } + }, + layout + } + return { + ...options, + label + } + } else { + return { + ...options, + label: false + } + } + } + + protected setupOptions(chart: Chart, options: SankeyOptions): SankeyOptions { + return flow( + this.configTheme, + this.configBasicStyle, + this.configLabel, + this.configTooltip, + this.configLegend, + this.configSlider, + this.configAnalyseHorizontal, + this.configEmptyDataStrategy + )(chart, options) + } + + constructor(name = 'sankey') { + super(name, DEFAULT_DATA) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/others/scatter.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/others/scatter.ts new file mode 100644 index 0000000..a06684e --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/others/scatter.ts @@ -0,0 +1,298 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import type { ScatterOptions, Scatter as G2Scatter } from '@antv/g2plot/esm/plots/scatter' +import { flow, parseJson } from '../../../util' +import { valueFormatter } from '../../../formatter' +import { + configPlotTooltipEvent, + getPadding, + getTooltipContainer, + TOOLTIP_TPL +} from '../../common/common_antv' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { defaults, isEmpty } from 'lodash-es' +import { DEFAULT_LEGEND_STYLE } from '@/data-visualization/chart/components/editor/util/chart' + +const { t } = useI18n() +/** + * 散点图 + */ +export class Scatter extends G2PlotChartView { + properties: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'x-axis-selector', + 'y-axis-selector', + 'title-selector', + 'label-selector', + 'tooltip-selector', + 'legend-selector', + 'jump-set', + 'linkage' + ] + propertyInner: EditorPropertyInner = { + 'basic-style-selector': [ + 'colors', + 'alpha', + 'scatterSymbol', + 'scatterSymbolSize', + 'seriesColor' + ], + 'label-selector': ['fontSize', 'color', 'labelFormatter'], + 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'], + 'x-axis-selector': [ + 'position', + 'name', + 'color', + 'fontSize', + 'axisLine', + 'splitLine', + 'axisForm', + 'axisLabel' + ], + 'y-axis-selector': [ + 'position', + 'name', + 'color', + 'fontSize', + 'axisValue', + 'axisLine', + 'splitLine', + 'axisForm', + 'axisLabel', + 'axisLabelFormatter' + ], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'legend-selector': ['icon', 'orient', 'color', 'fontSize', 'hPosition', 'vPosition'] + } + axis: AxisType[] = ['xAxis', 'yAxis', 'extBubble', 'filter', 'drill', 'extLabel', 'extTooltip'] + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.drag_block_type_axis')} / ${t('chart.dimension')}`, + type: 'd' + }, + yAxis: { + ...this['axisConfig'].yAxis, + limit: undefined, + allowEmpty: false + }, + extBubble: { + name: `${t('chart.bubble_size')} / ${t('chart.quota')}`, + type: 'q', + limit: 1, + allowEmpty: true + } + } + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (!chart.data?.data) { + return + } + const data = chart.data.data + const baseOptions: ScatterOptions = { + data: data, + xField: 'field', + yField: 'value', + colorField: 'category', + meta: { + field: { + type: 'cat' + } + }, + appendPadding: getPadding(chart), + interactions: [ + { + type: 'legend-active', + cfg: { + start: [{ trigger: 'legend-item:mouseenter', action: ['element-active:reset'] }], + end: [{ trigger: 'legend-item:mouseleave', action: ['element-active:reset'] }] + } + }, + { + type: 'legend-filter', + cfg: { + start: [ + { + trigger: 'legend-item:click', + action: [ + 'list-unchecked:toggle', + 'data-filter:filter', + 'element-active:reset', + 'element-highlight:reset' + ] + } + ] + } + } + ] + } + const options = this.setupOptions(chart, baseOptions) + const { Scatter: G2Scatter } = await import('@antv/g2plot/esm/plots/scatter') + const newChart = new G2Scatter(container, options) + newChart.on('point:click', action) + configPlotTooltipEvent(chart, newChart) + return newChart + } + + protected configBasicStyle(chart: Chart, options: ScatterOptions): ScatterOptions { + const customAttr = parseJson(chart.customAttr) + const basicStyle = customAttr.basicStyle + if (chart.extBubble?.length) { + return { + ...options, + size: [5, 30], + sizeField: 'popSize', + shape: basicStyle.scatterSymbol + } + } + return { + ...options, + size: basicStyle.scatterSymbolSize, + shape: basicStyle.scatterSymbol + } + } + + protected configYAxis(chart: Chart, options: ScatterOptions): ScatterOptions { + const tmpOptions = super.configYAxis(chart, options) + if (!tmpOptions.yAxis) { + return tmpOptions + } + const yAxis = parseJson(chart.customStyle).yAxis + if (tmpOptions.yAxis.label) { + tmpOptions.yAxis.label.formatter = value => { + return valueFormatter(value, yAxis.axisLabelFormatter) + } + } + const axisValue = yAxis.axisValue + if (!axisValue?.auto) { + const axis = { + yAxis: { + ...tmpOptions.yAxis, + min: axisValue.min, + max: axisValue.max, + minLimit: axisValue.min, + maxLimit: axisValue.max, + tickCount: axisValue.splitCount + } + } + return { ...tmpOptions, ...axis } + } + return tmpOptions + } + + protected configTooltip(chart: Chart, options: ScatterOptions): ScatterOptions { + const customAttr: DeepPartial = parseJson(chart.customAttr) + const tooltipAttr = customAttr.tooltip + if (!tooltipAttr.show) { + return { + ...options, + tooltip: false + } + } + const formatterMap = tooltipAttr.seriesTooltipFormatter + ?.filter(i => i.show) + .reduce((pre, next) => { + pre[next.seriesId] = next + return pre + }, {}) as Record + const VALID_ITEMS = ['value', 'popSize'] + const tooltip: ScatterOptions['tooltip'] = { + showTitle: true, + customItems(originalItems) { + if (!tooltipAttr.seriesTooltipFormatter?.length) { + return originalItems + } + const head = originalItems[0] + // 非原始数据 + if (!head.data.quotaList) { + return originalItems + } + const result = [] + originalItems + .filter(item => VALID_ITEMS.includes(item.name)) + .forEach(item => { + let formatter = formatterMap[`${item.data.quotaList[0].id}-yAxis`] + if (item.name === 'popSize') { + formatter = formatterMap[`${item.data.quotaList[1].id}-extBubble`] + } + if (!formatter) { + return + } + const value = valueFormatter(parseFloat(item.value as string), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ ...item, name, value }) + }) + head.data.dynamicTooltipValue?.forEach(item => { + const formatter = formatterMap[item.fieldId] + if (formatter) { + const value = valueFormatter(parseFloat(item.value), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ color: 'grey', name, value }) + } + }) + return result + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + return { + ...options, + tooltip + } + } + + protected configLegend(chart: Chart, options: ScatterOptions): ScatterOptions { + const optionTmp = super.configLegend(chart, options) + if (!optionTmp.legend) { + return optionTmp + } + const customStyle = parseJson(chart.customStyle) + let size + if (customStyle && customStyle.legend) { + size = defaults(JSON.parse(JSON.stringify(customStyle.legend)), DEFAULT_LEGEND_STYLE).size + } else { + size = DEFAULT_LEGEND_STYLE.size + } + optionTmp.legend.marker.style = style => { + return { + r: size, + fill: style.fill + } + } + return optionTmp + } + + protected setupOptions(chart: Chart, options: ScatterOptions) { + return flow( + this.configTheme, + this.configColor, + this.configLabel, + this.configTooltip, + this.configLegend, + this.configXAxis, + this.configYAxis, + this.configAnalyse, + this.configSlider, + this.configBasicStyle + )(chart, options) + } + + constructor() { + super('scatter', []) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/others/treemap.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/others/treemap.ts new file mode 100644 index 0000000..98f0364 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/others/treemap.ts @@ -0,0 +1,246 @@ +import { TreemapOptions, Treemap as G2Treemap } from '@antv/g2plot/esm/plots/treemap' +import { G2PlotChartView, G2PlotDrawOptions } from '../../types/impl/g2plot' +import { flow, parseJson, setUpSingleDimensionSeriesColor } from '../../../util' +import { getPadding, getTooltipSeriesTotalMap } from '../../common/common_antv' +import { valueFormatter } from '../../../formatter' +import { Label } from '@antv/g2plot/lib/types/label' +import { Datum } from '@antv/g2plot/esm/types/common' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import isEmpty from 'lodash-es/isEmpty' + +const { t } = useI18n() + +/** + * 矩形树图 + */ +export class Treemap extends G2PlotChartView { + properties: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'title-selector', + 'legend-selector', + 'label-selector', + 'tooltip-selector', + 'jump-set', + 'linkage' + ] + propertyInner: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + 'basic-style-selector': ['colors', 'alpha', 'seriesColor'], + 'label-selector': ['fontSize', 'color', 'showDimension', 'showQuota', 'showProportion'], + 'legend-selector': ['icon', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition'], + 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ] + } + axis: AxisType[] = ['xAxis', 'yAxis', 'filter', 'drill', 'extLabel', 'extTooltip'] + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.drag_block_treemap_label')} / ${t('chart.dimension')}`, + type: 'd' + }, + yAxis: { + name: `${t('chart.drag_block_treemap_size')} / ${t('chart.quota')}`, + limit: 1 + } + } + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (!chart.data?.data?.length) { + return + } + const data = chart.data.data + const baseOptions = { + data: { + name: 'root', + children: data + }, + colorField: 'name', + appendPadding: getPadding(chart), + interactions: [ + { + type: 'legend-active', + cfg: { + start: [{ trigger: 'legend-item:mouseenter', action: ['element-active:reset'] }], + end: [{ trigger: 'legend-item:mouseleave', action: ['element-active:reset'] }] + } + }, + { + type: 'legend-filter', + cfg: { + start: [ + { + trigger: 'legend-item:click', + action: [ + 'list-unchecked:toggle', + 'data-filter:filter', + 'element-active:reset', + 'element-highlight:reset' + ] + } + ] + } + }, + { + type: 'tooltip', + cfg: { + start: [{ trigger: 'element:mousemove', action: 'tooltip:show' }], + end: [{ trigger: 'element:mouseleave', action: 'tooltip:hide' }] + } + } + ] + } + const options = this.setupOptions(chart, baseOptions) + const { Treemap: G2Treemap } = await import('@antv/g2plot/esm/plots/treemap') + const newChart = new G2Treemap(container, options) + newChart.on('polygon:click', action) + return newChart + } + protected configTooltip(chart: Chart, options: TreemapOptions): TreemapOptions { + const { tooltip: tooltipAttr, label } = parseJson(chart.customAttr) + const { yAxis } = chart + if (!tooltipAttr.show) { + return { + ...options, + tooltip: false + } + } + const reserveDecimalCount = label.reserveDecimalCount + const seriesTotalMap = getTooltipSeriesTotalMap(options.data.children) + const formatterMap = tooltipAttr.seriesTooltipFormatter + ?.filter(i => i.show) + .reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) as Record + const tooltip: TreemapOptions['tooltip'] = { + showTitle: true, + title: () => undefined, + customItems(originalItems) { + let tooltipItems = originalItems + if (tooltipAttr.seriesTooltipFormatter?.length) { + tooltipItems = originalItems.filter(item => formatterMap[item.data.quotaList[0].id]) + } + const result = [] + const head = originalItems[0] + tooltipItems.forEach(item => { + const formatter = formatterMap[item.data.quotaList[0].id] ?? yAxis[0] + const value = valueFormatter(parseFloat(item.value as string), formatter.formatterCfg) + // sync with label + const percent = ( + Math.round((item.data.value / item.data.path[1].value) * 10000) / 100 + ).toFixed(reserveDecimalCount) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ ...item, name, value: `${value ?? ''} (${percent}%)` }) + }) + head.data.dynamicTooltipValue?.forEach(item => { + const formatter = formatterMap[item.fieldId] + if (formatter) { + const total = seriesTotalMap[item.fieldId] + // sync with label + const percent = (Math.round((item.value / total) * 10000) / 100).toFixed( + reserveDecimalCount + ) + const value = valueFormatter(parseFloat(item.value), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ color: 'grey', name, value: `${value ?? ''} (${percent}%)` }) + } + }) + return result + } + } + return { + ...options, + tooltip + } + } + protected configLabel(chart: Chart, options: TreemapOptions): TreemapOptions { + const customAttr: DeepPartial = parseJson(chart.customAttr) + const labelAttr = customAttr.label + if (!labelAttr.show) { + return { + ...options, + label: false + } + } + const label: Label = { + style: { + fill: labelAttr.color, + fontSize: labelAttr.fontSize + }, + formatter: function (param: Datum) { + let res = param.value + const contentItems = [] + if (labelAttr.showDimension) { + contentItems.push(param.field) + } + if (labelAttr.showQuota) { + contentItems.push(valueFormatter(param.value, labelAttr.quotaLabelFormatter)) + } + if (labelAttr.showProportion) { + const percentage = `${(((param.value / param.parent.value) * 10000) / 100).toFixed( + labelAttr.reserveDecimalCount + )}%` + contentItems.push(percentage) + } + res = contentItems.join('\n') + return res + } + } + if (labelAttr.fullDisplay) { + label.layout = [{ type: 'limit-in-plot' }] + } + return { ...options, label } + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + const { customAttr, customStyle } = chart + const { label } = customAttr + customAttr.label = { + ...label, + show: true, + showDimension: true, + showProportion: true, + reserveDecimalCount: 2 + } + const { legend } = customStyle + legend.show = false + return chart + } + public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + data?.sort((a, b) => b.value - a.value) + return setUpSingleDimensionSeriesColor(chart, data) + } + protected configColor(chart: Chart, options: TreemapOptions): TreemapOptions { + const data = options.data.children + data.sort((a, b) => b.value - a.value) + const tmpOptions = this.configSingleDimensionColor(chart, { ...options, data }) + return { ...options, color: tmpOptions.color } + } + protected setupOptions(chart: Chart, options: TreemapOptions): TreemapOptions { + return flow( + this.configTheme, + this.configColor, + this.configLabel, + this.configTooltip, + this.configLegend + )(chart, options, {}, this) + } + + constructor() { + super('treemap', []) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/others/word-cloud.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/others/word-cloud.ts new file mode 100644 index 0000000..0baf181 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/others/word-cloud.ts @@ -0,0 +1,184 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import type { WordCloud as G2WordCloud, WordCloudOptions } from '@antv/g2plot/esm/plots/word-cloud' +import { + filterChartDataByRange, + flow, + getMaxAndMinValueByData, + parseJson +} from '@/data-visualization/chart/components/js/util' +import { getPadding } from '@/data-visualization/chart/components/js/panel/common/common_antv' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { isEmpty } from 'lodash-es' +import { DEFAULT_MISC } from '@/data-visualization/chart/components/editor/util/chart' + +const { t } = useI18n() +const DEFAULT_DATA = [] +/** + * 词云图 + */ +export class WordCloud extends G2PlotChartView { + properties: EditorProperty[] = [ + 'basic-style-selector', + 'background-overall-component', + 'border-style', + 'title-selector', + 'tooltip-selector', + 'misc-selector', + 'jump-set', + 'linkage' + ] + propertyInner: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + 'basic-style-selector': ['colors', 'alpha'], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'misc-selector': ['wordSizeRange', 'wordSpacing', 'wordCloudAxisValueRange'], + 'tooltip-selector': ['color', 'fontSize', 'backgroundColor', 'seriesTooltipFormatter', 'show'] + } + axis: AxisType[] = ['xAxis', 'yAxis', 'filter'] + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.drag_block_word_cloud_label')} / ${t('chart.dimension_or_quota')}`, + type: 'd', + limit: 1 + }, + yAxis: { + name: `${t('chart.drag_block_word_cloud_size')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + } + } + setDataRange = (action, maxValue, minValue) => { + action({ + from: 'word-cloud', + data: { + max: maxValue, + min: minValue + } + }) + } + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (chart?.data) { + // data + let data = chart.data.data + const { misc } = parseJson(chart.customAttr) + let minValue = 0 + let maxValue = 0 + if ( + !misc.wordCloudAxisValueRange?.auto && + misc.wordCloudAxisValueRange?.fieldId === chart.yAxis[0].id + ) { + minValue = misc.wordCloudAxisValueRange.min + maxValue = misc.wordCloudAxisValueRange.max + } + getMaxAndMinValueByData(data ?? [], 'value', maxValue, minValue, (max, min) => { + maxValue = max + minValue = min + }) + data = filterChartDataByRange(data ?? [], maxValue, minValue) + // options + const initOptions: WordCloudOptions = { + data: data, + wordField: 'field', + weightField: 'value', + colorField: 'field', + wordStyle: { + fontFamily: chart.fontFamily ? chart.fontFamily : 'Verdana', + fontSize: (misc.wordSizeRange ?? DEFAULT_MISC.wordSizeRange) as [number, number], + rotation: [0, 0], + padding: misc.wordSpacing ?? DEFAULT_MISC.wordSpacing + }, + random: () => 0.5, + appendPadding: getPadding(chart), + legend: false, + interactions: [] + } + const options = this.setupOptions(chart, initOptions) + const { WordCloud: G2WordCloud } = await import('@antv/g2plot/esm/plots/word-cloud') + const newChart = new G2WordCloud(container, options) + newChart.on('click', () => { + this.setDataRange(action, maxValue, minValue) + }) + newChart.on('afterrender', () => { + this.setDataRange(action, maxValue, minValue) + }) + newChart.on('point:click', param => { + action({ x: param.x, y: param.y, data: { data: param.data.data.datum } }) + }) + return newChart + } + } + + protected configTooltip(chart: Chart, options: WordCloudOptions): WordCloudOptions { + const customAttr: DeepPartial = parseJson(chart.customAttr) + const tooltipAttr = customAttr.tooltip + const yAxis = chart.yAxis + if (!tooltipAttr.show) { + return { + ...options, + tooltip: false + } + } + const formatterMap = tooltipAttr.seriesTooltipFormatter + ?.filter(i => i.show) + .reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) as Record + const tooltip: WordCloudOptions['tooltip'] = { + showTitle: true, + title: () => undefined, + customItems(originalItems) { + let tooltipItems = originalItems + if (tooltipAttr.seriesTooltipFormatter?.length) { + tooltipItems = originalItems.filter(item => formatterMap[item.data.datum.quotaList[0].id]) + } + const result = [] + const head = originalItems[0] + tooltipItems.forEach(item => { + const formatter = formatterMap[item.data.datum.quotaList[0].id] ?? yAxis[0] + const value = valueFormatter(item.value, formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ ...item, name, value }) + }) + head.data.datum.dynamicTooltipValue?.forEach(item => { + const formatter = formatterMap[item.fieldId] + if (formatter) { + const value = valueFormatter(parseFloat(item.value), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ color: 'grey', name, value }) + } + }) + return result + } + } + return { + ...options, + tooltip + } + } + + protected setupOptions(chart: Chart, options: WordCloudOptions): WordCloudOptions { + return flow(this.configTheme, this.configTooltip)(chart, options) + } + + constructor() { + super('word-cloud', DEFAULT_DATA) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/pie/common.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/pie/common.ts new file mode 100644 index 0000000..f8b808d --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/pie/common.ts @@ -0,0 +1,63 @@ +import { useI18n } from '@/data-visualization/hooks/web/useI18n' + +const { t } = useI18n() + +export const PIE_EDITOR_PROPERTY: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'title-selector', + 'legend-selector', + 'label-selector', + 'tooltip-selector', + 'jump-set', + 'linkage' +] +export const PIE_EDITOR_PROPERTY_INNER: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'border-style': ['all'], + 'label-selector': [ + 'fontSize', + 'color', + 'rPosition', + 'showDimension', + 'showQuota', + 'showProportion' + ], + 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'seriesTooltipFormatter', 'show'], + 'basic-style-selector': ['colors', 'alpha', 'radius', 'seriesColor'], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'legend-selector': ['icon', 'orient', 'fontSize', 'color', 'hPosition', 'vPosition'] +} + +export const PIE_AXIS_TYPE: AxisType[] = [ + 'xAxis', + 'yAxis', + 'drill', + 'filter', + 'extLabel', + 'extTooltip' +] + +export const PIE_AXIS_CONFIG: AxisConfig = { + xAxis: { + name: `${t('chart.drag_block_pie_label')} / ${t('chart.dimension')}`, + type: 'd' + }, + yAxis: { + name: `${t('chart.drag_block_pie_angle')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/pie/pie.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/pie/pie.ts new file mode 100644 index 0000000..0263080 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/pie/pie.ts @@ -0,0 +1,356 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import type { Pie as G2Pie, PieOptions } from '@antv/g2plot/esm/plots/pie' +import { + flow, + hexColorToRGBA, + parseJson, + setUpSingleDimensionSeriesColor +} from '@/data-visualization/chart/components/js/util' +import { + configPlotTooltipEvent, + getPadding, + getTooltipContainer, + getTooltipSeriesTotalMap, + TOOLTIP_TPL +} from '@/data-visualization/chart/components/js/panel/common/common_antv' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { + PIE_AXIS_CONFIG, + PIE_AXIS_TYPE, + PIE_EDITOR_PROPERTY, + PIE_EDITOR_PROPERTY_INNER +} from '@/data-visualization/chart/components/js/panel/charts/pie/common' +import type { Datum } from '@antv/g2plot/esm/types/common' +import { add } from 'mathjs' +import isEmpty from 'lodash-es/isEmpty' +import { cloneDeep } from 'lodash-es' + +const DEFAULT_DATA = [] +export class Pie extends G2PlotChartView { + axis: AxisType[] = PIE_AXIS_TYPE + properties = PIE_EDITOR_PROPERTY + propertyInner: EditorPropertyInner = { + ...PIE_EDITOR_PROPERTY_INNER, + 'basic-style-selector': ['colors', 'alpha', 'radius', 'topN', 'seriesColor'] + } + axisConfig = PIE_AXIS_CONFIG + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (!chart.data?.data?.length) { + return + } + // data + const data = chart.data.data + // custom color + const customAttr = parseJson(chart.customAttr) + const color = customAttr.basicStyle.colors.map(i => + hexColorToRGBA(i, customAttr.basicStyle.alpha) + ) + // options + const initOptions: PieOptions = { + data: data, + angleField: 'value', + colorField: 'field', + appendPadding: getPadding(chart), + color, + animation: false, + pieStyle: { + lineWidth: 0 + }, + statistic: { + title: false, + content: { + style: { + whiteSpace: 'pre-wrap', + overflow: 'hidden', + textOverflow: 'ellipsis' + }, + content: '' + } + }, + interactions: [ + { + type: 'legend-active', + cfg: { + start: [{ trigger: 'legend-item:mouseenter', action: ['element-active:reset'] }], + end: [{ trigger: 'legend-item:mouseleave', action: ['element-active:reset'] }] + } + }, + { + type: 'legend-filter', + cfg: { + start: [ + { + trigger: 'legend-item:click', + action: [ + 'list-unchecked:toggle', + 'data-filter:filter', + 'element-active:reset', + 'element-highlight:reset' + ] + } + ] + } + }, + { + type: 'tooltip', + cfg: { + start: [{ trigger: 'interval:mousemove', action: 'tooltip:show' }], + end: [{ trigger: 'interval:mouseleave', action: 'tooltip:hide' }] + } + }, + { + type: 'active-region', + cfg: { + start: [{ trigger: 'interval:mousemove', action: 'active-region:show' }], + end: [{ trigger: 'interval:mouseleave', action: 'active-region:hide' }] + } + } + ], + meta: { + field: { + type: 'cat' + } + } + } + const options = this.setupOptions(chart, initOptions) + const { Pie: G2Pie } = await import('@antv/g2plot/esm/plots/pie') + const newChart = new G2Pie(container, options) + newChart.on('interval:click', action) + configPlotTooltipEvent(chart, newChart) + return newChart + } + + protected configLabel(chart: Chart, options: PieOptions): PieOptions { + const { label: labelAttr } = parseJson(chart.customAttr) + if (!labelAttr?.show) { + return { + ...options, + label: false + } + } + const layout = [] + let textAlign = undefined + if (labelAttr.position === 'inner') { + textAlign = 'center' + if (labelAttr.fullDisplay) { + layout.push({ type: 'limit-in-plot' }) + } else { + layout.push({ type: 'limit-in-canvas' }) + layout.push({ type: 'hide-overlap' }) + } + } else { + if (!labelAttr.fullDisplay) { + layout.push({ type: 'limit-in-plot' }) + } + } + let labelType = labelAttr.position === 'outer' ? 'spider' : labelAttr.position + if (layout.length === 0) { + labelType = 'no' + } + const label = { + type: labelType, + textAlign, + layout, + autoRotate: false, + style: { + fill: labelAttr.color, + fontSize: labelAttr.fontSize + }, + formatter: (param: Datum) => { + let res = param.value + const contentItems = [] + if (labelAttr.showDimension) { + contentItems.push(param.field) + } + if (labelAttr.showQuota) { + contentItems.push(valueFormatter(param.value, labelAttr.quotaLabelFormatter)) + } + if (labelAttr.showProportion) { + const percentage = `${(Math.round(param.percent * 10000) / 100).toFixed( + labelAttr.reserveDecimalCount + )}%` + if (labelAttr.showDimension && labelAttr.showQuota) { + contentItems.push(`(${percentage})`) + } else { + contentItems.push(percentage) + } + } + res = contentItems.join(' ') + return res + } + } + return { ...options, label } + } + + protected configTooltip(chart: Chart, options: PieOptions): PieOptions { + const { tooltip: tooltipAttr, label } = parseJson(chart.customAttr) + const { yAxis } = chart + if (!tooltipAttr.show) { + return { + ...options, + tooltip: false + } + } + const reserveDecimalCount = label.reserveDecimalCount + const seriesTotalMap = getTooltipSeriesTotalMap(options.data) + // trick, cal total, maybe use scale of chart in plot instance + const total = options.data?.reduce((pre, next) => add(pre, next.value ?? 0), 0) + const formatterMap = tooltipAttr.seriesTooltipFormatter + ?.filter(i => i.show) + .reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) as Record + const tooltip: PieOptions['tooltip'] = { + showTitle: true, + title: () => undefined, + customItems(originalItems) { + let tooltipItems = originalItems + if (tooltipAttr.seriesTooltipFormatter?.length) { + tooltipItems = originalItems.filter(item => formatterMap[item.data.quotaList[0].id]) + } + const result = [] + const head = originalItems[0] + tooltipItems.forEach(item => { + const formatter = formatterMap[item.data.quotaList[0].id] ?? yAxis[0] + const originValue = parseFloat(item.value as string) + const value = valueFormatter(originValue, formatter.formatterCfg) + // sync with label + const percent = (Math.round((originValue / total) * 10000) / 100).toFixed( + reserveDecimalCount + ) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ ...item, name, value: `${value ?? ''} (${percent}%)` }) + }) + head.data.dynamicTooltipValue?.forEach(item => { + const formatter = formatterMap[item.fieldId] + if (formatter) { + const total = seriesTotalMap[item.fieldId] + // sync with label + const percent = (Math.round((item.value / total) * 10000) / 100).toFixed( + reserveDecimalCount + ) + const value = valueFormatter(parseFloat(item.value), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ color: 'grey', name, value: `${value ?? ''} (${percent}%)` }) + } + }) + return result + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + return { + ...options, + tooltip + } + } + + protected configBasicStyle(chart: Chart, options: PieOptions): PieOptions { + const customAttr = parseJson(chart.customAttr) + const { basicStyle } = customAttr + const { data } = options + if (data?.length && basicStyle.calcTopN && data.length > basicStyle.topN) { + data.sort((a, b) => b.value - a.value) + const otherItems = data.splice(basicStyle.topN) + const initOtherItem = { + ...data[0], + dynamicTooltipValue: [], + field: basicStyle.topNLabel, + name: basicStyle.topNLabel, + value: 0 + } + const dynamicTotalMap: Record = {} + otherItems.reduce((p, n) => { + p.value += n.value ?? 0 + n.dynamicTooltipValue?.forEach(val => { + dynamicTotalMap[val.fieldId] = (dynamicTotalMap[val.fieldId] || 0) + val.value + }) + return p + }, initOtherItem) + for (const key in dynamicTotalMap) { + initOtherItem.dynamicTooltipValue.push({ + fieldId: key, + value: dynamicTotalMap[key] + }) + } + data.push(initOtherItem) + } + return { + ...options, + radius: basicStyle.radius / 100 + } + } + setupDefaultOptions(chart: ChartObj): ChartObj { + const { customAttr, customStyle } = chart + const { label } = customAttr + if (!['inner', 'outer'].includes(label.position)) { + label.position = 'outer' + } + customAttr.label = { + ...label, + show: true, + showDimension: true, + showProportion: true, + reserveDecimalCount: 2 + } + const { legend } = customStyle + legend.show = false + return chart + } + + public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + data = cloneDeep(data) + const { calcTopN, topN, topNLabel } = chart.customAttr.basicStyle + if (data?.length && calcTopN && data.length > topN) { + data.sort((a, b) => b.value - a.value) + data.splice(topN) + data.push({ + field: topNLabel, + value: 0 + }) + } + return setUpSingleDimensionSeriesColor(chart, data) + } + + protected setupOptions(chart: Chart, options: PieOptions): PieOptions { + return flow( + this.configTheme, + this.configBasicStyle, + this.configSingleDimensionColor, + this.configLabel, + this.configTooltip, + this.configLegend + )(chart, options, {}, this) + } + + constructor(name = 'pie') { + super(name, DEFAULT_DATA) + } +} + +export class PieDonut extends Pie { + propertyInner: EditorPropertyInner = { + ...PIE_EDITOR_PROPERTY_INNER, + 'basic-style-selector': ['colors', 'alpha', 'radius', 'innerRadius', 'topN', 'seriesColor'] + } + protected configBasicStyle(chart: Chart, options: PieOptions): PieOptions { + const tmp = super.configBasicStyle(chart, options) + const { basicStyle } = parseJson(chart.customAttr) + return { + ...tmp, + radius: basicStyle.radius / 100, + innerRadius: basicStyle.innerRadius / 100 + } + } + + constructor() { + super('pie-donut') + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/pie/rose.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/pie/rose.ts new file mode 100644 index 0000000..0432ca7 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/pie/rose.ts @@ -0,0 +1,285 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import { RoseOptions, Rose as G2Rose } from '@antv/g2plot/esm/plots/rose' +import { + PIE_AXIS_CONFIG, + PIE_AXIS_TYPE, + PIE_EDITOR_PROPERTY, + PIE_EDITOR_PROPERTY_INNER +} from './common' +import { + configPlotTooltipEvent, + getPadding, + getTooltipContainer, + getTooltipSeriesTotalMap, + TOOLTIP_TPL +} from '@/data-visualization/chart/components/js/panel/common/common_antv' +import { parseJson, flow, setUpSingleDimensionSeriesColor } from '@/data-visualization/chart/components/js/util' +import { Label } from '@antv/g2plot/lib/types/label' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { Datum } from '@antv/g2plot/esm/types/common' +import { add } from 'mathjs' +import isEmpty from 'lodash-es/isEmpty' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +const { t } = useI18n() + +export class Rose extends G2PlotChartView { + axis: AxisType[] = PIE_AXIS_TYPE + properties: EditorProperty[] = PIE_EDITOR_PROPERTY + propertyInner: EditorPropertyInner = PIE_EDITOR_PROPERTY_INNER + axisConfig: AxisConfig = { + ...PIE_AXIS_CONFIG, + yAxis: { + name: `${t('chart.drag_block_pie_radius')} / ${t('chart.quota')}`, + type: 'q', + limit: 1 + } + } + + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + if (!chart?.data?.data?.length) { + return + } + // data + const data = chart.data.data + // options + const baseOptions: RoseOptions = { + data: data, + xField: 'field', + yField: 'value', + seriesField: 'field', + appendPadding: getPadding(chart), + interactions: [ + { + type: 'legend-active', + cfg: { + start: [{ trigger: 'legend-item:mouseenter', action: ['element-active:reset'] }], + end: [{ trigger: 'legend-item:mouseleave', action: ['element-active:reset'] }] + } + }, + { + type: 'legend-filter', + cfg: { + start: [ + { + trigger: 'legend-item:click', + action: [ + 'list-unchecked:toggle', + 'data-filter:filter', + 'element-active:reset', + 'element-highlight:reset' + ] + } + ] + } + }, + { + type: 'tooltip', + cfg: { + start: [{ trigger: 'interval:mousemove', action: 'tooltip:show' }], + end: [{ trigger: 'interval:mouseleave', action: 'tooltip:hide' }] + } + } + ], + meta: { + field: { + type: 'cat' + } + } + } + const options = this.setupOptions(chart, baseOptions) + + const { Rose: G2Rose } = await import('@antv/g2plot/esm/plots/rose') + // 开始渲染 + const plot = new G2Rose(container, options) + + plot.on('interval:click', action) + configPlotTooltipEvent(chart, plot) + return plot + } + + protected configBasicStyle(chart: Chart, options: RoseOptions): RoseOptions { + const { basicStyle } = parseJson(chart.customAttr) + return { + ...options, + radius: basicStyle.radius / 100 + } + } + + protected configLabel(chart: Chart, options: RoseOptions): RoseOptions { + const { label: labelAttr } = parseJson(chart.customAttr) + if (!labelAttr.show) { + return { + ...options, + label: false + } + } + const total = options.data?.reduce((pre, next) => add(pre, next.value ?? 0), 0) + const layout = [] + if (!labelAttr.fullDisplay) { + const tmpOptions = super.configLabel(chart, options) + layout.push(...tmpOptions.label.layout) + } + const labelOptions: Label = { + autoRotate: true, + layout, + style: { + fill: labelAttr.color, + fontSize: labelAttr.fontSize + }, + formatter: (param: Datum) => { + let res = param.value + const contentItems = [] + if (labelAttr.showDimension) { + contentItems.push(param.field) + } + if (labelAttr.showQuota) { + contentItems.push(valueFormatter(param.value, labelAttr.quotaLabelFormatter)) + } + if (labelAttr.showProportion) { + const percentage = `${(Math.round((param.value / total) * 10000) / 100).toFixed( + labelAttr.reserveDecimalCount + )}%` + if (labelAttr.showDimension && labelAttr.showQuota) { + contentItems.push(`(${percentage})`) + } else { + contentItems.push(percentage) + } + } + res = contentItems.join('\n') + return res + } + } + if (labelAttr.position === 'inner') { + labelOptions.offset = -10 + } + return { + ...options, + label: labelOptions + } + } + + protected configTooltip(chart: Chart, options: RoseOptions): RoseOptions { + const { tooltip: tooltipAttr, label } = parseJson(chart.customAttr) + const { yAxis } = chart + if (!tooltipAttr.show) { + return { + ...options, + tooltip: false + } + } + const reserveDecimalCount = label.reserveDecimalCount + const seriesTotalMap = getTooltipSeriesTotalMap(options.data) + // trick, cal total, maybe use scale of chart in plot instance + const total = options.data?.reduce((pre, next) => add(pre, next.value ?? 0), 0) + const formatterMap = tooltipAttr.seriesTooltipFormatter + ?.filter(i => i.show) + .reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) as Record + const tooltip: RoseOptions['tooltip'] = { + showTitle: true, + title: () => undefined, + customItems(originalItems) { + let tooltipItems = originalItems + if (tooltipAttr.seriesTooltipFormatter?.length) { + tooltipItems = originalItems.filter(item => formatterMap[item.data.quotaList[0].id]) + } + const result = [] + const head = originalItems[0] + tooltipItems.forEach(item => { + const formatter = formatterMap[item.data.quotaList[0].id] ?? yAxis[0] + const originValue = parseFloat(item.value as string) + const value = valueFormatter(originValue, formatter.formatterCfg) + // sync with label + const percent = (Math.round((originValue / total) * 10000) / 100).toFixed( + reserveDecimalCount + ) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ ...item, name, value: `${value ?? ''} (${percent}%)` }) + }) + head.data.dynamicTooltipValue?.forEach(item => { + const formatter = formatterMap[item.fieldId] + if (formatter) { + const total = seriesTotalMap[item.fieldId] + // sync with label + const percent = (Math.round((item.value / total) * 10000) / 100).toFixed( + reserveDecimalCount + ) + const value = valueFormatter(parseFloat(item.value), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ color: 'grey', name, value: `${value ?? ''} (${percent}%)` }) + } + }) + return result + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + return { + ...options, + tooltip + } + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + const { customAttr, customStyle } = chart + const { label } = customAttr + if (!['inner', 'outer'].includes(label.position)) { + label.position = 'outer' + } + customAttr.label = { + ...label, + show: true, + showDimension: true, + showProportion: true, + reserveDecimalCount: 2 + } + const { legend } = customStyle + legend.show = false + return chart + } + + public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + return setUpSingleDimensionSeriesColor(chart, data) + } + + protected setupOptions(chart: Chart, options: RoseOptions): RoseOptions { + return flow( + this.configBasicStyle, + this.configSingleDimensionColor, + this.configTheme, + this.configLabel, + this.configLegend, + this.configTooltip + )(chart, options) + } + + constructor(name = 'pie-rose') { + super(name, []) + } +} + +export class RoseDonut extends Rose { + propertyInner: EditorPropertyInner = { + ...PIE_EDITOR_PROPERTY_INNER, + 'basic-style-selector': ['colors', 'alpha', 'radius', 'innerRadius', 'seriesColor'] + } + protected configBasicStyle(chart: Chart, options: RoseOptions): RoseOptions { + const customAttr = parseJson(chart.customAttr) + return { + ...options, + radius: customAttr.basicStyle.radius / 100, + innerRadius: customAttr.basicStyle.innerRadius / 100 + } + } + + constructor() { + super('pie-donut-rose') + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/table/common.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/table/common.ts new file mode 100644 index 0000000..08b146e --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/table/common.ts @@ -0,0 +1,58 @@ +export const TABLE_EDITOR_PROPERTY: EditorProperty[] = [ + 'background-overall-component', + 'border-style', + 'basic-style-selector', + 'table-header-selector', + 'table-cell-selector', + 'title-selector', + 'tooltip-selector', + 'function-cfg', + 'threshold', + 'scroll-cfg', + 'jump-set', + 'linkage' +] +export const TABLE_EDITOR_PROPERTY_INNER: EditorPropertyInner = { + 'border-style': ['all'], + 'background-overall-component': ['all'], + 'basic-style-selector': ['tableColumnMode', 'tableBorderColor', 'tableScrollBarColor', 'alpha'], + 'table-header-selector': [ + 'tableHeaderBgColor', + 'tableTitleFontSize', + 'tableHeaderFontColor', + 'tableTitleHeight', + 'tableHeaderAlign', + 'showIndex', + 'indexLabel', + 'showColTooltip', + 'showHorizonBorder', + 'showVerticalBorder' + ], + 'table-cell-selector': [ + 'tableItemBgColor', + 'tableItemFontSize', + 'tableFontColor', + 'tableItemAlign', + 'tableItemHeight', + 'enableTableCrossBG', + 'tableItemSubBgColor', + 'showTooltip', + 'showHorizonBorder', + 'showVerticalBorder' + ], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'show'], + 'function-cfg': ['emptyDataStrategy'], + threshold: ['tableThreshold'] +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/table/t-heatmap.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/table/t-heatmap.ts new file mode 100644 index 0000000..8750439 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/table/t-heatmap.ts @@ -0,0 +1,362 @@ +import { + G2PlotChartView, + G2PlotDrawOptions +} from '@/data-visualization/chart/components/js/panel/types/impl/g2plot' +import type { Heatmap, HeatmapOptions } from '@antv/g2plot/esm/plots/heatmap' +import { flow, hexColorToRGBA, parseJson } from '@/data-visualization/chart/components/js/util' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { deepCopy } from '@/data-visualization/utils/utils' +import { cloneDeep } from 'lodash-es' +import { + configAxisLabelLengthLimit, + getPadding, + getXAxis, + getYAxis +} from '@/data-visualization/chart/components/js/panel/common/common_antv' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' + +const { t } = useI18n() +const DEFAULT_DATA = [] +/** + * 热力图 + */ +export class TableHeatmap extends G2PlotChartView { + properties: EditorProperty[] = [ + 'basic-style-selector', + 'background-overall-component', + 'label-selector', + 'legend-selector', + 'x-axis-selector', + 'y-axis-selector', + 'title-selector', + 'tooltip-selector', + 'jump-set', + 'linkage', + 'border-style' + ] + propertyInner: EditorPropertyInner = { + 'background-overall-component': ['all'], + 'basic-style-selector': ['colors'], + 'label-selector': ['fontSize', 'color'], + 'x-axis-selector': ['name', 'color', 'fontSize', 'position', 'axisLabel', 'axisLine'], + 'y-axis-selector': [ + 'name', + 'color', + 'fontSize', + 'position', + 'axisLabel', + 'axisLine', + 'showLengthLimit' + ], + 'title-selector': [ + 'title', + 'fontSize', + 'color', + 'hPosition', + 'isItalic', + 'isBolder', + 'remarkShow', + 'fontFamily', + 'letterSpace', + 'fontShadow' + ], + 'legend-selector': ['orient', 'color', 'fontSize', 'hPosition', 'vPosition'], + 'tooltip-selector': ['show', 'color', 'fontSize', 'backgroundColor'], + 'border-style': ['all'] + } + axis: AxisType[] = ['xAxis', 'xAxisExt', 'extColor', 'filter'] + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.x_axis')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1 + }, + xAxisExt: { + name: `${t('chart.y_axis')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1 + }, + extColor: { + name: `${t('chart.color')} / ${t('chart.dimension_or_quota')}`, + limit: 1 + } + } + protected getDefaultLength = (chart, l) => { + const containerDom = document.getElementById(chart.container) + const containerHeight = containerDom?.clientHeight || 100 + const containerWidth = containerDom?.clientWidth || 100 + let defaultLength = containerHeight - containerHeight * 0.5 + if (l.orient !== 'vertical') { + defaultLength = containerWidth - containerWidth * 0.5 + } + return defaultLength + } + protected sortData = (fieldObj, data) => { + const { deType, sort, customSort } = fieldObj + + if (sort === 'desc') { + if (deType === 0) { + return data.sort().reverse() + } else { + return data.sort((a, b) => b - a) + } + } else if (sort === 'asc') { + if (deType === 0) { + return data.sort() + } else { + return data.sort((a, b) => a - b) + } + } + + // 如果没有指定排序方式,直接返回原始数据或 customSort + return customSort && customSort.length > 0 ? customSort : data + } + async drawChart(drawOptions: G2PlotDrawOptions): Promise { + const { chart, container, action } = drawOptions + const xAxis = deepCopy(chart.xAxis) + const xAxisExt = deepCopy(chart.xAxisExt) + const extColor = deepCopy(chart.extColor) + if (!xAxis?.length || !xAxisExt?.length || !extColor?.length) { + return + } + const xField = xAxis[0].dataeaseName + const xFieldExt = xAxisExt[0].dataeaseName + const extColorField = extColor[0].dataeaseName + // data + const data = cloneDeep(chart.data.tableRow) + data.forEach(i => { + Object.keys(i).forEach(key => { + if (key === '*') { + i['@'] = i[key] + } + }) + }) + + // options + const initOptions: HeatmapOptions = { + data: data, + xField: xField, + yField: xFieldExt, + colorField: extColorField === '*' ? '@' : extColorField, + appendPadding: getPadding(chart), + meta: { + [xField]: { + type: 'cat', + values: this.sortData(xAxis[0], [...new Set(data.map(i => i[[xField]]))]) + }, + [xFieldExt]: { + type: 'cat', + values: this.sortData(xAxisExt[0], [...new Set(data.map(i => i[[xFieldExt]]))]).reverse() + } + }, + legend: { + layout: 'vertical', + position: 'right', + slidable: true, + label: { + align: 'left', + spacing: 10 + } + } + } + chart.container = container + const options = this.setupOptions(chart, initOptions) + const { Heatmap } = await import('@antv/g2plot/esm/plots/heatmap') + const newChart = new Heatmap(container, options) + newChart.on('plot:click', param => { + if (!param.data?.data) { + return + } + const pointData = param.data.data + const dimensionList = [] + chart.data.fields.forEach(item => { + Object.keys(pointData).forEach(key => { + if (key.startsWith('f_') && item.dataeaseName === key) { + dimensionList.push({ + id: item.id, + dataeaseName: item.dataeaseName, + value: pointData[key] + }) + } + }) + }) + action({ + x: param.data.x, + y: param.data.y, + data: { + data: { + ...param.data.data, + value: dimensionList[1].value, + name: dimensionList[1].id, + dimensionList: dimensionList, + quotaList: [dimensionList[1]] + } + } + }) + }) + newChart.on('afterrender', ev => { + const l = JSON.parse(JSON.stringify(parseJson(chart.customStyle).legend)) + if (l.show) { + const rail = ev.view.getController('legend').option[extColor[0].dataeaseName]?.['rail'] + if (rail) { + rail.defaultLength = this.getDefaultLength(chart, l) + } + } + }) + configAxisLabelLengthLimit(chart, newChart) + return newChart + } + + protected configBasicStyle(chart: Chart, options: HeatmapOptions): HeatmapOptions { + const basicStyle = parseJson(chart.customAttr).basicStyle + const color = basicStyle.colors?.map(ele => { + return hexColorToRGBA(ele, basicStyle.alpha) + }) + return { + ...options, + color + } + } + protected configTooltip(chart: Chart, options: HeatmapOptions): HeatmapOptions { + let tooltip + let customAttr: DeepPartial + if (chart.customAttr) { + customAttr = parseJson(chart.customAttr) + // tooltip + if (customAttr.tooltip) { + const extColor = deepCopy(chart.extColor) + const xAxisExt = deepCopy(chart.xAxisExt) + const tooltipFiledList = [xAxisExt, extColor] + const t = JSON.parse(JSON.stringify(customAttr.tooltip)) + if (t.show) { + tooltip = { + showTitle: true, + customItems(originalItems) { + const items = [] + const createItem = (fieldObj, items, originalItems) => { + const name = fieldObj?.chartShowName ? fieldObj?.chartShowName : fieldObj?.name + let value = originalItems[0].data[fieldObj.dataeaseName] + if (!isNaN(Number(value))) { + value = valueFormatter(value, fieldObj?.formatterCfg) + } + items.push({ + ...originalItems[0], + name: name, + value: value + }) + } + tooltipFiledList.forEach(field => { + createItem(field[0], items, originalItems) + }) + return items + } + } + } else { + tooltip = false + } + } + } + return { + ...options, + tooltip + } + } + + protected configXAxis(chart: Chart, options: HeatmapOptions): HeatmapOptions { + const xAxis = getXAxis(chart) + return { + ...options, + xAxis: xAxis ? { ...xAxis, grid: null } : false + } + } + + protected configYAxis(chart: Chart, options: HeatmapOptions): HeatmapOptions { + const yAxis = getYAxis(chart) + return { + ...options, + yAxis: yAxis ? { ...yAxis, grid: null } : false + } + } + + protected configLegend(chart: Chart, options: HeatmapOptions): HeatmapOptions { + const tmpOptions = super.configLegend(chart, options) + if (tmpOptions.legend) { + const l = JSON.parse(JSON.stringify(parseJson(chart.customStyle).legend)) + tmpOptions.legend.slidable = true + tmpOptions.legend.minHeight = 10 + tmpOptions.legend.minWidth = 10 + tmpOptions.legend.maxHeight = 600 + tmpOptions.legend.maxWidth = 600 + const containerDom = document.getElementById(chart.container) + const containerHeight = containerDom?.clientHeight || 100 + const containerWidth = containerDom?.clientWidth || 100 + let defaultLength = containerHeight - containerHeight * 0.5 + if (l.orient === 'vertical') { + tmpOptions.legend.offsetY = -5 + } else { + defaultLength = containerWidth - containerWidth * 0.5 + } + tmpOptions.legend.rail = { defaultLength: defaultLength } + tmpOptions.legend.label = { + spacing: 10, + style: { + fill: l.color, + fontSize: l.fontSize + } + } + } + return tmpOptions + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + chart.customStyle.legend.orient = 'vertical' + chart.customStyle.legend.vPosition = 'center' + chart.customStyle.legend.hPosition = 'right' + chart.customStyle.legend['rail'] = { defaultLength: 100 } + return chart + } + + protected configLabel(chart: Chart, options: HeatmapOptions): HeatmapOptions { + const tmpOptions = super.configLabel(chart, options) + if (tmpOptions.label) { + const extColor = deepCopy(chart.extColor) + const layout = [] + if (!tmpOptions.label.fullDisplay) { + layout.push(...tmpOptions.label.layout) + } + const label = { + ...tmpOptions.label, + position: 'middle', + layout, + formatter: data => { + const value = data[extColor[0]?.dataeaseName] + if (!isNaN(Number(value))) { + return valueFormatter(value, extColor[0]?.formatterCfg) + } + return value + } + } + return { + ...tmpOptions, + label + } + } + return tmpOptions + } + + protected setupOptions(chart: Chart, options: HeatmapOptions): HeatmapOptions { + return flow( + this.configTheme, + this.configXAxis, + this.configYAxis, + this.configBasicStyle, + this.configLegend, + this.configTooltip, + this.configLabel + )(chart, options) + } + + constructor() { + super('t-heatmap', DEFAULT_DATA) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/table/table-info.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/table/table-info.ts new file mode 100644 index 0000000..0485bcd --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/table/table-info.ts @@ -0,0 +1,510 @@ +import { + type LayoutResult, + S2DataConfig, + S2Event, + S2Options, + S2Theme, + ScrollbarPositionType, + TableColCell, + TableSheet, + ViewMeta +} from '@antv/s2' +import { formatterItem, valueFormatter } from '../../../formatter' +import { hexColorToRGBA, isAlphaColor, parseJson } from '../../../util' +import { S2ChartView, S2DrawOptions } from '../../types/impl/s2' +import { TABLE_EDITOR_PROPERTY, TABLE_EDITOR_PROPERTY_INNER } from './common' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { isEqual, isNumber, merge } from 'lodash-es' +import { + copyContent, + CustomDataCell, + CustomTableColCell, + getRowIndex, + calculateHeaderHeight, + SortTooltip, + configSummaryRow, + summaryRowStyle, + configEmptyDataStyle, + getLeafNodes, + getColumns +} from '@/data-visualization/chart/components/js/panel/common/common_table' + +const { t } = useI18n() +class ImageCell extends CustomDataCell { + protected drawTextShape(): void { + const img = new Image() + const { x, y, width, height, fieldValue } = this.meta + img.src = fieldValue as string + img.setAttribute('crossOrigin', 'anonymous') + img.onload = () => { + !this.cfg.children && (this.cfg.children = []) + const { width: imgWidth, height: imgHeight } = img + const ratio = Math.max(imgWidth / width, imgHeight / height) + // 不铺满,部分留白 + const imgShowWidth = (imgWidth / ratio) * 0.8 + const imgShowHeight = (imgHeight / ratio) * 0.8 + this.textShape = this.addShape('image', { + attrs: { + x: x + (imgShowWidth < width ? (width - imgShowWidth) / 2 : 0), + y: y + (imgShowHeight < height ? (height - imgShowHeight) / 2 : 0), + width: imgShowWidth, + height: imgShowHeight, + img + } + }) + } + } +} +/** + * 明细表 + */ +export class TableInfo extends S2ChartView { + properties = TABLE_EDITOR_PROPERTY + propertyInner = { + ...TABLE_EDITOR_PROPERTY_INNER, + 'table-header-selector': [ + ...TABLE_EDITOR_PROPERTY_INNER['table-header-selector'], + 'tableHeaderSort', + 'showTableHeader', + 'headerGroup' + ], + 'basic-style-selector': [ + 'tableColumnMode', + 'tableBorderColor', + 'tableScrollBarColor', + 'alpha', + 'tablePageMode', + 'showHoverStyle', + 'autoWrap', + 'showSummary', + 'summaryLabel' + ], + 'table-cell-selector': [ + ...TABLE_EDITOR_PROPERTY_INNER['table-cell-selector'], + 'tableFreeze', + 'tableColumnFreezeHead', + 'tableRowFreezeHead', + 'mergeCells' + ] + } + axis: AxisType[] = ['xAxis', 'filter', 'drill'] + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.drag_block_table_data_column')} / ${t('chart.dimension_or_quota')}` + } + } + + public drawChart(drawOption: S2DrawOptions): TableSheet { + const { container, chart, pageInfo, action, resizeAction } = drawOption + const containerDom = document.getElementById(container) + + // fields + let fields = chart.data?.fields ?? [] + const columns = [] + const meta = [] + const axisMap = chart.xAxis.reduce((pre, cur) => { + pre[cur.dataeaseName] = cur + return pre + }, {}) + const drillFieldMap = {} + if (chart.drill) { + // 下钻过滤字段 + const filterFields = chart.drillFilters.map(i => i.fieldId) + // 下钻入口的字段下标 + const drillFieldId = chart.drillFields[0].id + const drillFieldIndex = chart.xAxis.findIndex(ele => ele.id === drillFieldId) + // 当前下钻字段 + const curDrillFieldId = chart.drillFields[filterFields.length].id + const curDrillField = fields.find(ele => ele.id === curDrillFieldId) + filterFields.push(curDrillFieldId) + // 移除下钻字段,把当前下钻字段插入到下钻入口位置 + fields = fields.filter(ele => { + return !filterFields.includes(ele.id) + }) + drillFieldMap[curDrillField.dataeaseName] = chart.drillFields[0].dataeaseName + fields.splice(drillFieldIndex, 0, curDrillField) + } + fields.forEach(ele => { + const f = axisMap[ele.dataeaseName] + if (f?.hide === true) { + return + } + columns.push(ele.dataeaseName) + meta.push({ + field: ele.dataeaseName, + name: ele.chartShowName ?? ele.name, + formatter: function (value) { + if (!f) { + return value + } + if (value === null || value === undefined) { + return value + } + if (![2, 3].includes(f.deType) || !isNumber(value)) { + return value + } + let formatCfg = f.formatterCfg + if (!formatCfg) { + formatCfg = formatterItem + } + return valueFormatter(value, formatCfg) + } + }) + }) + const { basicStyle, tableCell, tableHeader, tooltip } = parseJson(chart.customAttr) + // 表头分组 + const { headerGroup, showTableHeader } = tableHeader + if (headerGroup && showTableHeader !== false) { + const { headerGroupConfig } = tableHeader + if (headerGroupConfig?.columns?.length) { + const allKeys = columns.map(c => drillFieldMap[c] || c) + const leafNodes = getLeafNodes(headerGroupConfig.columns as ColumnNode[]) + const leafKeys = leafNodes.map(c => c.key) + if (isEqual(leafKeys, allKeys)) { + if (Object.keys(drillFieldMap).length) { + const originField = Object.values(drillFieldMap)[0] + const drillField = Object.keys(drillFieldMap)[0] + const [drillCol] = getColumns([originField], headerGroupConfig.columns as ColumnNode[]) + drillCol.key = drillField + } + columns.splice(0, columns.length, ...headerGroupConfig.columns) + meta.push(...headerGroupConfig.meta) + } + } + } + // 空值处理 + const newData = this.configEmptyDataStrategy(chart) + // data config + const s2DataConfig: S2DataConfig = { + fields: { + columns: columns + }, + meta: meta, + data: newData + } + + // options + const s2Options: S2Options = { + width: containerDom.getBoundingClientRect().width, + height: containerDom.offsetHeight, + showSeriesNumber: tableHeader.showIndex, + conditions: this.configConditions(chart), + tooltip: { + getContainer: () => containerDom, + renderTooltip: sheet => new SortTooltip(sheet) + }, + interaction: { + hoverHighlight: !(basicStyle.showHoverStyle === false), + scrollbarPosition: newData.length + ? ScrollbarPositionType.CONTENT + : ScrollbarPositionType.CANVAS + } + } + s2Options.style = this.configStyle(chart, s2DataConfig) + // 自适应列宽模式下,URL 字段的宽度固定为 120 + if (basicStyle.tableColumnMode === 'adapt') { + const urlFields = fields.filter( + field => field.deType === 7 && !axisMap[field.dataeaseName]?.hide + ) + s2Options.style.colCfg.widthByFieldValue = urlFields?.reduce((p, n) => { + p[n.chartShowName ?? n.name] = 120 + return p + }, {}) + } + if (tableCell.tableFreeze && !tableCell.mergeCells) { + s2Options.frozenColCount = tableCell.tableColumnFreezeHead ?? 0 + s2Options.frozenRowCount = tableCell.tableRowFreezeHead ?? 0 + } + // 开启序号之后,第一列就是序号列,修改 label 即可 + if (s2Options.showSeriesNumber) { + let indexLabel = tableHeader.indexLabel + if (!indexLabel) { + indexLabel = '' + } + s2Options.layoutCoordinate = (_, __, col) => { + if (col.colIndex === 0 && col.rowIndex === 0) { + col.label = indexLabel + col.value = indexLabel + } + } + } + s2Options.dataCell = viewMeta => { + const field = fields.filter(f => f.dataeaseName === viewMeta.valueField)?.[0] + if (field?.deType === 7 && chart.showPosition !== 'dialog') { + return new ImageCell(viewMeta, viewMeta?.spreadsheet) + } + if (viewMeta.colIndex === 0 && s2Options.showSeriesNumber) { + if (tableCell.mergeCells) { + viewMeta.fieldValue = getRowIndex(s2Options.mergedCellsInfo, viewMeta) + } else { + viewMeta.fieldValue = + pageInfo.pageSize * (pageInfo.currentPage - 1) + viewMeta.rowIndex + 1 + } + } + // 配置文本自动换行参数 + viewMeta.autoWrap = tableCell.mergeCells ? false : basicStyle.autoWrap + viewMeta.maxLines = basicStyle.maxLines + return new CustomDataCell(viewMeta, viewMeta?.spreadsheet) + } + // tooltip + this.configTooltip(chart, s2Options) + // 合并单元格 + this.configMergeCells(chart, s2Options, s2DataConfig) + // 隐藏表头,保留顶部的分割线, 禁用表头横向 resize + if (tableHeader.showTableHeader === false) { + s2Options.style.colCfg.height = 1 + if (tableCell.showHorizonBorder === false) { + s2Options.style.colCfg.height = 0 + } + s2Options.interaction.resize = { + colCellVertical: false + } + s2Options.colCell = (node, sheet, config) => { + node.label = ' ' + return new TableColCell(node, sheet, config) + } + } else { + // header interaction + chart.container = container + this.configHeaderInteraction(chart, s2Options) + s2Options.colCell = (node, sheet, config) => { + // 配置文本自动换行参数 + node.autoWrap = tableCell.mergeCells ? false : basicStyle.autoWrap + node.maxLines = basicStyle.maxLines + return new CustomTableColCell(node, sheet, config) + } + } + // 总计 + configSummaryRow(chart, s2Options, newData, tableHeader, basicStyle, basicStyle.showSummary) + // 开始渲染 + const newChart = new TableSheet(containerDom, s2DataConfig, s2Options) + // 总计紧贴在单元格后面 + summaryRowStyle(newChart, newData, tableCell, tableHeader, basicStyle.showSummary) + // 开启自动换行 + if (basicStyle.autoWrap && !tableCell.mergeCells) { + // 调整表头宽度时,计算表头高度 + newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, info => { + calculateHeaderHeight(info, newChart, tableHeader, basicStyle, null) + }) + newChart.on(S2Event.LAYOUT_AFTER_HEADER_LAYOUT, (ev: LayoutResult) => { + const maxHeight = newChart.store.get('autoCalcHeight') as number + if (maxHeight) { + // 更新列的高度 + ev.colLeafNodes.forEach(n => (n.height = maxHeight)) + ev.colsHierarchy.height = maxHeight + newChart.store.set('autoCalcHeight', undefined) + } else { + if (ev.colLeafNodes?.length) { + const { value, width } = ev.colLeafNodes[0] + calculateHeaderHeight( + { info: { meta: { value }, resizedWidth: width } }, + newChart, + tableHeader, + basicStyle, + ev + ) + } + } + }) + } + // 自适应铺满 + if (basicStyle.tableColumnMode === 'adapt') { + newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, () => { + newChart.store.set('lastLayoutResult', newChart.facet.layoutResult) + }) + newChart.on(S2Event.LAYOUT_AFTER_HEADER_LAYOUT, (ev: LayoutResult) => { + const lastLayoutResult = newChart.store.get('lastLayoutResult') as LayoutResult + if (lastLayoutResult) { + // 拖动表头 resize + const widthByFieldValue = newChart.options.style?.colCfg?.widthByFieldValue + const lastLayoutWidthMap: Record = + lastLayoutResult?.colLeafNodes.reduce((p, n) => { + p[n.value] = widthByFieldValue?.[n.value] ?? n.width + return p + }, {}) || {} + const totalWidth = ev.colLeafNodes.reduce((p, n) => { + n.width = lastLayoutWidthMap[n.value] || n.width + n.x = p + return p + n.width + }, 0) + // 处理分组的单元格,宽度为所有叶子节点之和 + ev.colNodes.forEach(n => { + if (n.colIndex === -1) { + n.width = calcTreeWidth(n) + n.x = getStartPosition(n) + } + }) + ev.colsHierarchy.width = totalWidth + newChart.store.set('lastLayoutResult', undefined) + return + } + // 第一次渲染初始化,把图片字段固定为 120 进行计算 + const urlFields = fields + .filter(field => field.deType === 7 && !axisMap[field.dataeaseName]?.hide) + .map(f => f.dataeaseName) + const totalWidthWithImg = ev.colLeafNodes.reduce((p, n) => { + return p + (urlFields.includes(n.field) ? 120 : n.width) + }, 0) + const containerWidth = containerDom.getBoundingClientRect().width + if (containerWidth <= totalWidthWithImg) { + // 图库计算的布局宽度已经大于等于容器宽度,不需要再扩大,但是需要处理非整数宽度值,不然会出现透明细线 + ev.colLeafNodes.reduce((p, n) => { + n.width = Math.round(n.width) + n.x = p + return p + n.width + }, 0) + return + } + // 图片字段固定 120, 剩余宽度按比例均摊到其他字段进行扩大 + const totalWidthWithoutImg = ev.colLeafNodes.reduce((p, n) => { + return p + (urlFields.includes(n.field) ? 0 : n.width) + }, 0) + const restWidth = containerWidth - urlFields.length * 120 + const scale = restWidth / totalWidthWithoutImg + const totalWidth = ev.colLeafNodes.reduce((p, n) => { + n.width = urlFields.includes(n.field) ? 120 : Math.round(n.width * scale) + n.x = p + return p + n.width + }, 0) + // 处理分组的单元格,宽度为所有叶子节点之和 + ev.colNodes.forEach(n => { + if (n.colIndex === -1) { + n.width = calcTreeWidth(n) + n.x = getStartPosition(n) + } + }) + if (totalWidth > containerWidth) { + ev.colLeafNodes[ev.colLeafNodes.length - 1].width -= totalWidth - containerWidth + } + ev.colsHierarchy.width = containerWidth + }) + } + // 空数据时表格样式 + configEmptyDataStyle(newChart, basicStyle, newData, container) + // click + newChart.on(S2Event.DATA_CELL_CLICK, ev => { + const cell = newChart.getCell(ev.target) + const meta = cell.getMeta() as ViewMeta + const nameIdMap = fields.reduce((pre, next) => { + pre[next['dataeaseName']] = next['id'] + return pre + }, {}) + + const rowData = newChart.dataSet.getRowData(meta) + const dimensionList = [] + for (const key in rowData) { + if (nameIdMap[key]) { + dimensionList.push({ id: nameIdMap[key], value: rowData[key] }) + } + } + const param = { + x: ev.x, + y: ev.y, + data: { + dimensionList, + name: nameIdMap[meta.valueField], + sourceType: 'table-info', + quotaList: [] + } + } + action(param) + }) + // 合并的单元格直接复用数据单元格的事件 + newChart.on(S2Event.MERGED_CELLS_CLICK, e => newChart.emit(S2Event.DATA_CELL_CLICK, e)) + // tooltip + const { show } = tooltip + if (show) { + newChart.on(S2Event.COL_CELL_HOVER, event => this.showTooltip(newChart, event, meta)) + newChart.on(S2Event.DATA_CELL_HOVER, event => this.showTooltip(newChart, event, meta)) + newChart.on(S2Event.MERGED_CELLS_HOVER, event => this.showTooltip(newChart, event, meta)) + } + // header resize + newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, ev => resizeAction(ev)) + // right click + newChart.on(S2Event.GLOBAL_CONTEXT_MENU, event => copyContent(newChart, event, meta)) + // touch + this.configTouchEvent(newChart, drawOption, meta) + // theme + const customTheme = this.configTheme(chart) + newChart.setThemeCfg({ theme: customTheme }) + return newChart + } + + protected configTheme(chart: Chart): S2Theme { + const theme = super.configTheme(chart) + const { basicStyle, tableCell } = parseJson(chart.customAttr) + if (tableCell.mergeCells) { + const tableFontColor = hexColorToRGBA(tableCell.tableFontColor, basicStyle.alpha) + let tableItemBgColor = tableCell.tableItemBgColor + if (!isAlphaColor(tableItemBgColor)) { + tableItemBgColor = hexColorToRGBA(tableItemBgColor, basicStyle.alpha) + } + const { tableBorderColor } = basicStyle + const { tableItemAlign, tableItemFontSize } = tableCell + const fontStyle = tableCell.isItalic ? 'italic' : 'normal' + const fontWeight = tableCell.isBolder === false ? 'normal' : 'bold' + const mergeCellTheme: S2Theme = { + mergedCell: { + cell: { + backgroundColor: tableItemBgColor, + crossBackgroundColor: tableItemBgColor, + horizontalBorderColor: tableBorderColor, + verticalBorderColor: tableBorderColor, + horizontalBorderWidth: tableCell.showHorizonBorder ? 1 : 0, + verticalBorderWidth: tableCell.showVerticalBorder ? 1 : 0 + }, + bolderText: { + fill: tableFontColor, + textAlign: tableItemAlign, + fontSize: tableItemFontSize, + fontStyle, + fontWeight + }, + text: { + fill: tableFontColor, + textAlign: tableItemAlign, + fontSize: tableItemFontSize, + fontStyle, + fontWeight + }, + measureText: { + fill: tableFontColor, + textAlign: tableItemAlign, + fontSize: tableItemFontSize, + fontStyle, + fontWeight + }, + seriesText: { + fill: tableFontColor, + textAlign: tableItemAlign, + fontSize: tableItemFontSize, + fontStyle, + fontWeight + } + } + } + merge(theme, mergeCellTheme) + } + return theme + } + + constructor() { + super('table-info', []) + } +} + +function calcTreeWidth(node) { + if (!node.children?.length) { + return node.width + } + return node.children.reduce((pre, cur) => { + return pre + calcTreeWidth(cur) + }, 0) +} + +function getStartPosition(node) { + if (!node.children?.length) { + return node.x + } + return getStartPosition(node.children[0]) +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/table/table-normal.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/table/table-normal.ts new file mode 100644 index 0000000..327dd5b --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/table/table-normal.ts @@ -0,0 +1,300 @@ +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { formatterItem, valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { + configEmptyDataStyle, + configSummaryRow, + copyContent, + SortTooltip, + summaryRowStyle +} from '@/data-visualization/chart/components/js/panel/common/common_table' +import { S2ChartView, S2DrawOptions } from '@/data-visualization/chart/components/js/panel/types/impl/s2' +import { parseJson } from '@/data-visualization/chart/components/js/util' +import { + type LayoutResult, + S2DataConfig, + S2Event, + S2Options, + ScrollbarPositionType, + TableColCell, + TableSheet, + ViewMeta +} from '@antv/s2' +import { cloneDeep, isNumber } from 'lodash-es' +import { TABLE_EDITOR_PROPERTY, TABLE_EDITOR_PROPERTY_INNER } from './common' + +const { t } = useI18n() +/** + * 汇总表 + */ +export class TableNormal extends S2ChartView { + properties = TABLE_EDITOR_PROPERTY + propertyInner: EditorPropertyInner = { + ...TABLE_EDITOR_PROPERTY_INNER, + 'table-header-selector': [ + ...TABLE_EDITOR_PROPERTY_INNER['table-header-selector'], + 'tableHeaderSort', + 'showTableHeader' + ], + 'basic-style-selector': [ + ...TABLE_EDITOR_PROPERTY_INNER['basic-style-selector'], + 'showSummary', + 'summaryLabel', + 'showHoverStyle' + ], + 'table-cell-selector': [ + ...TABLE_EDITOR_PROPERTY_INNER['table-cell-selector'], + 'tableFreeze', + 'tableColumnFreezeHead', + 'tableRowFreezeHead' + ] + } + axis: AxisType[] = ['xAxis', 'yAxis', 'drill', 'filter'] + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.drag_block_table_data_column')} / ${t('chart.dimension')}`, + type: 'd' + }, + yAxis: { + name: `${t('chart.drag_block_table_data_column')} / ${t('chart.quota')}`, + type: 'q' + } + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + chart.xAxis = [] + return chart + } + + drawChart(drawOption: S2DrawOptions): TableSheet { + const { container, chart, action, resizeAction } = drawOption + const containerDom = document.getElementById(container) + if (!containerDom) return + + // fields + let fields = chart.data.fields + + const columns = [] + const meta = [] + if (chart.drill) { + // 下钻过滤字段 + const filterFields = chart.drillFilters.map(i => i.fieldId) + // 下钻入口的字段下标 + const drillFieldId = chart.drillFields[0].id + const drillFieldIndex = chart.xAxis.findIndex(ele => ele.id === drillFieldId) + // 当前下钻字段 + const curDrillFieldId = chart.drillFields[filterFields.length].id + const curDrillField = fields.filter(ele => ele.id === curDrillFieldId) + filterFields.push(curDrillFieldId) + // 移除下钻字段,把当前下钻字段插入到下钻入口位置 + fields = fields.filter(ele => { + return !filterFields.includes(ele.id) + }) + fields.splice(drillFieldIndex, 0, ...curDrillField) + } + const axisMap = [...chart.xAxis, ...chart.yAxis].reduce((pre, cur) => { + pre[cur.dataeaseName] = cur + return pre + }, {}) + // add drill list + fields.forEach(ele => { + const f = axisMap[ele.dataeaseName] + if (f?.hide === true) { + return + } + columns.push(ele.dataeaseName) + meta.push({ + field: ele.dataeaseName, + name: ele.chartShowName ?? ele.name, + formatter: function (value) { + if (!f) { + return value + } + if (value === null || value === undefined) { + return value + } + if (![2, 3].includes(f.deType) || !isNumber(value)) { + return value + } + let formatCfg = f.formatterCfg + if (!formatCfg) { + formatCfg = formatterItem + } + return valueFormatter(value, formatCfg) + } + }) + }) + + // 空值处理 + const newData = this.configEmptyDataStrategy(chart) + // data config + const s2DataConfig: S2DataConfig = { + fields: { + columns: columns + }, + meta: meta, + data: newData + } + + const { basicStyle, tableCell, tableHeader, tooltip } = parseJson(chart.customAttr) + // options + const s2Options: S2Options = { + width: containerDom.getBoundingClientRect().width, + height: containerDom.offsetHeight, + showSeriesNumber: tableHeader.showIndex, + conditions: this.configConditions(chart), + tooltip: { + getContainer: () => containerDom, + renderTooltip: sheet => new SortTooltip(sheet) + }, + interaction: { + hoverHighlight: !(basicStyle.showHoverStyle === false), + scrollbarPosition: newData.length + ? ScrollbarPositionType.CONTENT + : ScrollbarPositionType.CANVAS + } + } + // 列宽设置 + s2Options.style = this.configStyle(chart, s2DataConfig) + // 行列冻结 + if (tableCell.tableFreeze) { + s2Options.frozenColCount = tableCell.tableColumnFreezeHead ?? 0 + s2Options.frozenRowCount = tableCell.tableRowFreezeHead ?? 0 + } + // 开启序号之后,第一列就是序号列,修改 label 即可 + if (s2Options.showSeriesNumber) { + let indexLabel = tableHeader.indexLabel + if (!indexLabel) { + indexLabel = '' + } + s2Options.layoutCoordinate = (_, __, col) => { + if (col.colIndex === 0 && col.rowIndex === 0) { + col.label = indexLabel + col.value = indexLabel + } + } + } + // tooltip + this.configTooltip(chart, s2Options) + // 隐藏表头,保留顶部的分割线, 禁用表头横向 resize + if (tableHeader.showTableHeader === false) { + s2Options.style.colCfg.height = 1 + if (tableCell.showHorizonBorder === false) { + s2Options.style.colCfg.height = 0 + } + s2Options.interaction.resize = { + colCellVertical: false + } + s2Options.colCell = (node, sheet, config) => { + node.label = ' ' + return new TableColCell(node, sheet, config) + } + } else { + // header interaction + chart.container = container + this.configHeaderInteraction(chart, s2Options) + } + + // 总计 + configSummaryRow(chart, s2Options, newData, tableHeader, basicStyle, basicStyle.showSummary) + // 开始渲染 + const newChart = new TableSheet(containerDom, s2DataConfig, s2Options) + // 总计紧贴在单元格后面 + summaryRowStyle(newChart, newData, tableCell, tableHeader, basicStyle.showSummary) + // 自适应铺满 + if (basicStyle.tableColumnMode === 'adapt') { + newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, () => { + newChart.store.set('lastLayoutResult', newChart.facet.layoutResult) + }) + newChart.on(S2Event.LAYOUT_AFTER_HEADER_LAYOUT, (ev: LayoutResult) => { + const lastLayoutResult = newChart.store.get('lastLayoutResult') as LayoutResult + if (lastLayoutResult) { + // 拖动表头 resize + const widthByFieldValue = newChart.options.style?.colCfg?.widthByFieldValue + const lastLayoutWidthMap: Record = + lastLayoutResult?.colLeafNodes.reduce((p, n) => { + p[n.value] = widthByFieldValue?.[n.value] ?? n.width + return p + }, {}) || {} + const totalWidth = ev.colLeafNodes.reduce((p, n) => { + n.width = lastLayoutWidthMap[n.value] || n.width + n.x = p + return p + n.width + }, 0) + ev.colsHierarchy.width = totalWidth + newChart.store.set('lastLayoutResult', undefined) + return + } + const containerWidth = containerDom.getBoundingClientRect().width + const scale = containerWidth / ev.colsHierarchy.width + if (scale <= 1) { + // 图库计算的布局宽度已经大于等于容器宽度,不需要再扩大,但是需要处理非整数宽度值,不然会出现透明细线 + ev.colLeafNodes.reduce((p, n) => { + n.width = Math.round(n.width) + n.x = p + return p + n.width + }, 0) + return + } + const totalWidth = ev.colLeafNodes.reduce((p, n) => { + n.width = Math.round(n.width * scale) + n.x = p + return p + n.width + }, 0) + if (totalWidth > containerWidth) { + // 从最后一列减掉 + ev.colLeafNodes[ev.colLeafNodes.length - 1].width -= totalWidth - containerWidth + } + ev.colsHierarchy.width = containerWidth + }) + } + configEmptyDataStyle(newChart, basicStyle, newData, container) + // click + newChart.on(S2Event.DATA_CELL_CLICK, ev => { + const cell = newChart.getCell(ev.target) + const meta = cell.getMeta() as ViewMeta + const nameIdMap = fields.reduce((pre, next) => { + pre[next['dataeaseName']] = next['id'] + return pre + }, {}) + + const rowData = newChart.dataSet.getRowData(meta) + const dimensionList = [] + for (const key in rowData) { + if (nameIdMap[key]) { + dimensionList.push({ id: nameIdMap[key], value: rowData[key] }) + } + } + const param = { + x: ev.x, + y: ev.y, + data: { + dimensionList, + name: nameIdMap[meta.valueField], + sourceType: 'table-normal', + quotaList: [] + } + } + action(param) + }) + // tooltip + const { show } = tooltip + if (show) { + newChart.on(S2Event.COL_CELL_HOVER, event => this.showTooltip(newChart, event, meta)) + newChart.on(S2Event.DATA_CELL_HOVER, event => this.showTooltip(newChart, event, meta)) + } + // header resize + newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, ev => resizeAction(ev)) + // right click + newChart.on(S2Event.GLOBAL_CONTEXT_MENU, event => copyContent(newChart, event, meta)) + // touch + this.configTouchEvent(newChart, drawOption, meta) + // theme + const customTheme = this.configTheme(chart) + newChart.setThemeCfg({ theme: customTheme }) + + return newChart + } + constructor() { + super('table-normal', []) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/charts/table/table-pivot.ts b/frontend/src/data-visualization/chart/components/js/panel/charts/table/table-pivot.ts new file mode 100644 index 0000000..f7f695c --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/charts/table/table-pivot.ts @@ -0,0 +1,1003 @@ +import { + EXTRA_FIELD, + PivotSheet, + S2Event, + S2Options, + TOTAL_VALUE, + S2Theme, + Totals, + PivotDataSet, + Query, + VALUE_FIELD, + QueryDataType, + TotalStatus, + Aggregation, + S2DataConfig, + MergedCell +} from '@antv/s2' +import { formatterItem, valueFormatter } from '../../../formatter' +import { hexColorToRGBA, isAlphaColor, parseJson } from '../../../util' +import { S2ChartView, S2DrawOptions } from '../../types/impl/s2' +import { TABLE_EDITOR_PROPERTY_INNER } from './common' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { isNumber, keys, maxBy, merge, minBy, some, isEmpty, get } from 'lodash-es' +import { copyContent, CustomDataCell } from '../../common/common_table' +import Decimal from 'decimal.js' +import { DEFAULT_TABLE_HEADER } from '@/data-visualization/chart/components/editor/util/chart' + +type DataItem = Record + +const { t } = useI18n() + +class CustomPivotDataset extends PivotDataSet { + getTotalValue(query: Query, totalStatus?: TotalStatus) { + const { options } = this.spreadsheet + const effectiveStatus = some(totalStatus) + const status = effectiveStatus ? totalStatus : this.getTotalStatus(query) + const { aggregation, calcFunc } = + getAggregationAndCalcFuncByQuery(status, options?.totals) || {} + + // 聚合方式从用户配置的 s2Options.totals 取, 在触发前端兜底计算汇总逻辑时, 如果没有汇总的配置, 默认按 [求和] 计算,避免排序失效. + const defaultAggregation = + isEmpty(options?.totals) && !this.spreadsheet.isHierarchyTreeType() ? Aggregation.SUM : '' + const calcAction = calcActionByType[aggregation || defaultAggregation] + + // 前端计算汇总值 + if (calcAction || calcFunc) { + const data = this.getMultiData(query, { + queryType: QueryDataType.DetailOnly + }) + let totalValue: number + if (calcFunc) { + totalValue = calcFunc(query, data, this.spreadsheet, status) + } else if (calcAction) { + totalValue = calcAction(data, VALUE_FIELD) + } + + return { + ...query, + [VALUE_FIELD]: totalValue, + [query[EXTRA_FIELD]]: totalValue + } + } + } +} +/** + * 透视表 + */ +export class TablePivot extends S2ChartView { + properties: EditorProperty[] = [ + 'border-style', + 'background-overall-component', + 'basic-style-selector', + 'table-header-selector', + 'table-cell-selector', + 'table-total-selector', + 'title-selector', + 'tooltip-selector', + 'function-cfg', + 'threshold', + 'linkage', + 'jump-set' + ] + propertyInner = { + ...TABLE_EDITOR_PROPERTY_INNER, + 'table-header-selector': [ + 'tableHeaderBgColor', + 'tableTitleFontSize', + 'tableHeaderFontColor', + 'tableTitleHeight', + 'tableHeaderAlign', + 'showColTooltip', + 'showRowTooltip', + 'showHorizonBorder', + 'showVerticalBorder' + ], + 'table-total-selector': ['row', 'col'], + 'basic-style-selector': [ + 'tableColumnMode', + 'tableBorderColor', + 'tableScrollBarColor', + 'alpha', + 'tableLayoutMode', + 'showHoverStyle' + ] + } + axis: AxisType[] = ['xAxis', 'xAxisExt', 'yAxis', 'filter'] + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.table_pivot_row')} / ${t('chart.dimension')}`, + type: 'd' + }, + xAxisExt: { + name: `${t('chart.drag_block_table_data_column')} / ${t('chart.dimension')}`, + type: 'd', + allowEmpty: true + }, + yAxis: { + name: `${t('chart.drag_block_table_data_column')} / ${t('chart.quota')}`, + type: 'q' + } + } + + public drawChart(drawOption: S2DrawOptions): PivotSheet { + const { container, chart, chartObj, action } = drawOption + const containerDom = document.getElementById(container) + + const { xAxisExt: columnFields, xAxis: rowFields, yAxis: valueFields } = chart + const [c, r, v] = [columnFields, rowFields, valueFields].map(arr => + arr.map(i => i.dataeaseName) + ) + + // fields + const { fields, customCalc } = chart.data + if (!fields || fields.length === 0) { + if (chartObj) { + chartObj.destroy() + } + return + } + + const columns = [] + const meta = [] + + const valueFieldMap: Record = [ + ...chart.xAxis, + ...chart.xAxisExt, + ...chart.yAxis + ].reduce((p, n) => { + p[n.dataeaseName] = n + return p + }, {}) + fields.forEach(ele => { + const f = valueFieldMap[ele.dataeaseName] + columns.push(ele.dataeaseName) + meta.push({ + field: ele.dataeaseName, + name: ele.chartShowName ?? ele.name, + formatter: value => { + if (!f) { + return value + } + if (value === null || value === undefined) { + return value + } + if (![2, 3].includes(f.deType) || !isNumber(value)) { + return value + } + if (f.formatterCfg) { + return valueFormatter(value, f.formatterCfg) + } else { + return valueFormatter(value, formatterItem) + } + } + }) + }) + + // total config + const { basicStyle, tooltip, tableTotal } = parseJson(chart.customAttr) + if (!tableTotal.row.subTotalsDimensionsNew || tableTotal.row.subTotalsDimensions == undefined) { + tableTotal.row.subTotalsDimensions = r + } + tableTotal.col.subTotalsDimensions = c + + // 解析合计、小计排序 + const sortParams = [] + if ( + tableTotal.row.totalSort && + tableTotal.row.totalSort !== 'none' && + c.length > 0 && + tableTotal.row.showGrandTotals && + v.indexOf(tableTotal.row.totalSortField) > -1 + ) { + const sort = { + sortFieldId: c[0], + sortMethod: tableTotal.row.totalSort.toUpperCase(), + sortByMeasure: TOTAL_VALUE, + query: { + [EXTRA_FIELD]: tableTotal.row.totalSortField + } + } + sortParams.push(sort) + } + if ( + tableTotal.col.totalSort && + tableTotal.col.totalSort !== 'none' && + r.length > 0 && + tableTotal.col.showGrandTotals && + v.indexOf(tableTotal.col.totalSortField) > -1 + ) { + const sort = { + sortFieldId: r[0], + sortMethod: tableTotal.col.totalSort.toUpperCase(), + sortByMeasure: TOTAL_VALUE, + query: { + [EXTRA_FIELD]: tableTotal.col.totalSortField + } + } + sortParams.push(sort) + } + //列维度为空,行排序按照指标列来排序,取第一个有排序设置的指标 + if (!columnFields?.length) { + const sortField = valueFields?.find(v => !['none', 'custom_sort'].includes(v.sort)) + if (sortField) { + const sort = { + sortFieldId: r[0], + sortMethod: sortField.sort.toUpperCase(), + sortByMeasure: TOTAL_VALUE, + query: { + [EXTRA_FIELD]: sortField.dataeaseName + } + } + sortParams.push(sort) + } + } + // 自定义总计小计 + const totals = [ + tableTotal.row.calcTotals, + tableTotal.row.calcSubTotals, + tableTotal.col.calcTotals, + tableTotal.col.calcSubTotals + ] + const axisMap = { + row: chart.xAxis, + col: chart.xAxisExt, + quota: chart.yAxis + } + //树形模式下,列维度为空,行小计会变成列总计,特殊处理下 + if (basicStyle.tableLayoutMode === 'tree' && !chart.xAxisExt?.length) { + tableTotal.col.calcTotals = tableTotal.row.calcSubTotals + } + totals.forEach(total => { + if (total.cfg?.length) { + delete total.aggregation + const totalCfgMap = total.cfg.reduce((p, n) => { + p[n.dataeaseName] = n + return p + }, {}) + total.calcFunc = (query, data, _, status) => { + return customCalcFunc(query, data, status, chart, totalCfgMap, axisMap, customCalc) + } + } + }) + // 空值处理 + const newData = this.configEmptyDataStrategy(chart) + // data config + const s2DataConfig: S2DataConfig = { + fields: { + rows: r, + columns: c, + values: v + }, + meta: meta, + data: newData, + sortParams: sortParams + } + const s2Options: S2Options = { + width: containerDom.offsetWidth, + height: containerDom.offsetHeight, + totals: tableTotal as Totals, + conditions: this.configConditions(chart), + tooltip: { + getContainer: () => containerDom + }, + hierarchyType: basicStyle.tableLayoutMode ?? 'grid', + dataSet: spreadSheet => new CustomPivotDataset(spreadSheet), + interaction: { + hoverHighlight: !(basicStyle.showHoverStyle === false) + }, + dataCell: meta => { + return new CustomDataCell(meta, meta.spreadsheet) + } + } + // options + s2Options.style = this.configStyle(chart, s2DataConfig) + s2Options.style.hierarchyCollapse = true + // tooltip + this.configTooltip(chart, s2Options) + // 开始渲染 + const s2 = new PivotSheet(containerDom, s2DataConfig, s2Options as unknown as S2Options) + // tooltip + const { show } = tooltip + if (show) { + s2.on(S2Event.COL_CELL_HOVER, event => this.showTooltip(s2, event, meta)) + s2.on(S2Event.ROW_CELL_HOVER, event => this.showTooltip(s2, event, meta)) + s2.on(S2Event.DATA_CELL_HOVER, event => this.showTooltip(s2, event, meta)) + } + // empty data tip + configEmptyDataStyle(s2, newData) + // click + s2.on(S2Event.DATA_CELL_CLICK, ev => this.dataCellClickAction(chart, ev, s2, action)) + s2.on(S2Event.ROW_CELL_CLICK, ev => this.headerCellClickAction(chart, ev, s2, action)) + s2.on(S2Event.COL_CELL_CLICK, ev => this.headerCellClickAction(chart, ev, s2, action)) + // right click + s2.on(S2Event.GLOBAL_CONTEXT_MENU, event => copyContent(s2, event, meta)) + // touch + this.configTouchEvent(s2, drawOption, meta) + // theme + const customTheme = this.configTheme(chart) + s2.setThemeCfg({ theme: customTheme }) + + return s2 + } + private dataCellClickAction(chart: Chart, ev, s2Instance: PivotSheet, callback) { + const cell = s2Instance.getCell(ev.target) + const meta = cell.getMeta() + const nameIdMap = chart.data.fields.reduce((pre, next) => { + pre[next['dataeaseName']] = next['id'] + return pre + }, {}) + const rowData = { ...meta.rowQuery, ...meta.colQuery } + rowData[meta.valueField] = meta.fieldValue + const dimensionList = [] + for (const key in rowData) { + if (nameIdMap[key]) { + dimensionList.push({ id: nameIdMap[key], value: rowData[key] }) + } + } + const param = { + x: ev.x, + y: ev.y, + data: { + dimensionList, + name: nameIdMap[meta.valueField], + sourceType: 'table-pivot', + quotaList: [] + } + } + callback(param) + } + private headerCellClickAction(chart: Chart, ev, s2Instance: PivotSheet, callback) { + const cell = s2Instance.getCell(ev.target) + const meta = cell.getMeta() + const rowData = meta.query + const nameIdMap = chart.data.fields.reduce((pre, next) => { + pre[next['dataeaseName']] = next['id'] + return pre + }, {}) + const dimensionList = [] + for (const key in rowData) { + if (nameIdMap[key]) { + dimensionList.push({ id: nameIdMap[key], value: rowData[key] }) + } + } + const param = { + x: ev.x, + y: ev.y, + data: { + dimensionList, + name: nameIdMap[meta.valueField], + sourceType: 'table-pivot', + quotaList: [] + } + } + callback(param) + } + protected configTheme(chart: Chart): S2Theme { + const theme = super.configTheme(chart) + const { basicStyle, tableHeader } = parseJson(chart.customAttr) + let tableHeaderBgColor = tableHeader.tableHeaderBgColor + if (!isAlphaColor(tableHeaderBgColor)) { + tableHeaderBgColor = hexColorToRGBA(tableHeaderBgColor, basicStyle.alpha) + } + let tableHeaderCornerBgColor = + tableHeader.tableHeaderCornerBgColor ?? DEFAULT_TABLE_HEADER.tableHeaderCornerBgColor + if (!isAlphaColor(tableHeaderCornerBgColor)) { + tableHeaderCornerBgColor = hexColorToRGBA(tableHeaderCornerBgColor, basicStyle.alpha) + } + let tableHeaderColBgColor = + tableHeader.tableHeaderColBgColor ?? DEFAULT_TABLE_HEADER.tableHeaderColBgColor + if (!isAlphaColor(tableHeaderColBgColor)) { + tableHeaderColBgColor = hexColorToRGBA(tableHeaderColBgColor, basicStyle.alpha) + } + let tableBorderColor = basicStyle.tableBorderColor + if (!isAlphaColor(tableBorderColor)) { + tableBorderColor = hexColorToRGBA(tableBorderColor, basicStyle.alpha) + } + const tableHeaderColFontColor = hexColorToRGBA( + tableHeader.tableHeaderColFontColor, + basicStyle.alpha + ) + const tableHeaderCornerFontColor = hexColorToRGBA( + tableHeader.tableHeaderCornerFontColor, + basicStyle.alpha + ) + const colFontStyle = tableHeader.isColItalic ? 'italic' : 'normal' + const cornerFontStyle = tableHeader.isCornerItalic ? 'italic' : 'normal' + const colFontWeight = tableHeader.isColBolder === false ? 'normal' : 'bold' + const cornerFontWeight = tableHeader.isCornerBolder === false ? 'normal' : 'bold' + const pivotTheme = { + rowCell: { + cell: { + backgroundColor: tableHeaderColBgColor, + horizontalBorderColor: tableBorderColor, + verticalBorderColor: tableBorderColor + }, + text: { + fill: tableHeaderColFontColor, + fontSize: tableHeader.tableTitleColFontSize, + textAlign: tableHeader.tableHeaderColAlign, + textBaseline: 'top', + fontStyle: colFontStyle, + fontWeight: colFontWeight + }, + bolderText: { + fill: tableHeaderColFontColor, + fontSize: tableHeader.tableTitleColFontSize, + textAlign: tableHeader.tableHeaderColAlign, + fontStyle: colFontStyle, + fontWeight: colFontWeight + }, + measureText: { + fill: tableHeaderColFontColor, + fontSize: tableHeader.tableTitleColFontSize, + textAlign: tableHeader.tableHeaderColAlign, + fontStyle: colFontStyle, + fontWeight: colFontWeight + }, + seriesText: { + fill: tableHeaderColFontColor, + fontSize: tableHeader.tableTitleColFontSize, + textAlign: tableHeader.tableHeaderColAlign, + fontStyle: colFontStyle, + fontWeight: colFontWeight + } + }, + cornerCell: { + cell: { + backgroundColor: tableHeaderCornerBgColor + }, + text: { + fill: tableHeaderCornerFontColor, + fontSize: tableHeader.tableTitleCornerFontSize, + textAlign: tableHeader.tableHeaderCornerAlign, + fontStyle: cornerFontStyle, + fontWeight: cornerFontWeight + }, + bolderText: { + fill: tableHeaderCornerFontColor, + fontSize: tableHeader.tableTitleCornerFontSize, + textAlign: tableHeader.tableHeaderCornerAlign, + fontStyle: cornerFontStyle, + fontWeight: cornerFontWeight + }, + measureText: { + fill: tableHeaderCornerFontColor, + fontSize: tableHeader.tableTitleCornerFontSize, + textAlign: tableHeader.tableHeaderCornerAlign, + fontStyle: cornerFontStyle, + fontWeight: cornerFontWeight + } + } + } + merge(theme, pivotTheme) + if (tableHeader.showHorizonBorder === false) { + const tmp: S2Theme = { + cornerCell: { + cell: { + horizontalBorderColor: tableHeaderBgColor, + horizontalBorderWidth: 0 + } + }, + rowCell: { + cell: { + horizontalBorderColor: tableHeaderBgColor, + horizontalBorderWidth: 0 + } + } + } + merge(theme, tmp) + } + if (tableHeader.showVerticalBorder === false) { + const tmp: S2Theme = { + cornerCell: { + cell: { + verticalBorderColor: tableHeaderBgColor, + verticalBorderWidth: 0 + } + }, + rowCell: { + cell: { + verticalBorderColor: tableHeaderBgColor, + verticalBorderWidth: 0 + } + } + } + merge(theme, tmp) + } + return theme + } + + setupDefaultOptions(chart: ChartObj): ChartObj { + const { customAttr } = chart + if (customAttr.basicStyle.tableColumnMode === 'field') { + customAttr.basicStyle.tableColumnMode = 'custom' + } + return chart + } + + constructor() { + super('table-pivot', []) + } +} +function customCalcFunc(query, data, status, chart, totalCfgMap, axisMap, customCalc) { + if (!data?.length || !query[EXTRA_FIELD]) { + return 0 + } + const aggregation = totalCfgMap[query[EXTRA_FIELD]]?.aggregation || 'SUM' + switch (aggregation) { + case 'SUM': { + return data.reduce((p, n) => { + return p + parseFloat(n[query[EXTRA_FIELD]] ?? 0) + }, 0) + } + case 'AVG': { + const sum = data.reduce((p, n) => { + return p + parseFloat(n[query[EXTRA_FIELD]] ?? 0) + }, 0) + return sum / data.length + } + case 'MIN': { + const result = minBy(data, n => { + return parseFloat(n[query[EXTRA_FIELD]]) + }) + return result?.[query[EXTRA_FIELD]] + } + case 'MAX': { + const result = maxBy(data, n => { + return parseFloat(n[query[EXTRA_FIELD]]) + }) + return result?.[query[EXTRA_FIELD]] + } + case 'CUSTOM': { + const val = getCustomCalcResult(query, axisMap, chart, status, customCalc || {}) + if (val === '') { + return val + } + return parseFloat(val) + } + default: { + return data.reduce((p, n) => { + return p + parseFloat(n[query[EXTRA_FIELD]] ?? 0) + }, 0) + } + } +} + +function getTreeCustomCalcResult(query, axisMap, status: TotalStatus, customCalc) { + const quotaField = query[EXTRA_FIELD] + const { row, col } = axisMap + // 行列交叉总计 + if (status.isRowTotal && status.isColTotal) { + return customCalc.rowColTotal?.data?.[quotaField] + } + // 列总计 + if (status.isColTotal && !status.isRowSubTotal) { + const { colTotal, rowSubInColTotal } = customCalc + const path = getTreePath(query, row) + let val + if (path.length) { + const subLevel = getSubLevel(query, row) + if (subLevel + 1 === row.length && colTotal) { + path.push(quotaField) + val = get(colTotal.data, path) + } + if (subLevel + 1 < row.length && rowSubInColTotal) { + const data = rowSubInColTotal?.[subLevel]?.data + path.push(quotaField) + val = get(data, path) + } + } + return val + } + // 列小计 + if (status.isColSubTotal && !status.isRowTotal && !status.isRowSubTotal) { + const { colSubTotal } = customCalc + const subLevel = getSubLevel(query, col) + const rowPath = getTreePath(query, row) + const colPath = getTreePath(query, col) + const path = [...rowPath, ...colPath] + const data = colSubTotal?.[subLevel]?.data + let val + if (path.length && data) { + path.push(quotaField) + val = get(data, path) + } + return val + } + // 行总计 + if (status.isRowTotal && !status.isColSubTotal) { + const { rowTotal } = customCalc + const path = getTreePath(query, col) + let val + if (rowTotal) { + if (path.length) { + path.push(quotaField) + val = get(rowTotal.data, path) + } + // 列维度为空,行维度不为空 + if (!col.length && row.length) { + val = get(rowTotal.data, quotaField) + } + } + return val + } + // 行小计 + if (status.isRowSubTotal) { + // 列维度为空,行小计直接当成列总计 + if ( + (!status.isColTotal && !status.isColSubTotal) || + (!col.length && status.isColTotal && status.isRowSubTotal) + ) { + const { rowSubTotal } = customCalc + const rowLevel = getSubLevel(query, row) + const colPath = getTreePath(query, col) + const rowPath = getTreePath(query, row) + const path = [...colPath, ...rowPath] + const data = rowSubTotal?.[rowLevel]?.data + let val + if (path.length && rowSubTotal) { + path.push(quotaField) + val = get(data, path) + } + return val + } + } + // 行总计里面的列小计 + if (status.isRowTotal && status.isColSubTotal) { + const { colSubInRowTotal } = customCalc + const colLevel = getSubLevel(query, col) + const { data } = colSubInRowTotal?.[colLevel] + const colPath = getTreePath(query, col) + let val + if (colPath.length && colSubInRowTotal) { + colPath.push(quotaField) + val = get(data, colPath) + } + return val + } + // 列总计里面的行小计 + if (status.isColTotal && status.isRowSubTotal) { + const { rowSubInColTotal } = customCalc + const rowSubLevel = getSubLevel(query, row) + const data = rowSubInColTotal?.[rowSubLevel]?.data + const path = getTreePath(query, row) + let val + if (path.length && rowSubInColTotal) { + path.push(quotaField) + val = get(data, path) + } + return val + } + // 列小计里面的行小计 + if (status.isColSubTotal && status.isRowSubTotal) { + const { rowSubInColSub } = customCalc + const rowSubLevel = getSubLevel(query, row) + const colSubLevel = getSubLevel(query, col) + const data = rowSubInColSub?.[rowSubLevel]?.[colSubLevel]?.data + const rowPath = getTreePath(query, row) + const colPath = getTreePath(query, col) + const path = [...rowPath, ...colPath] + let val + if (path.length && rowSubInColSub) { + path.push(quotaField) + val = get(data, path) + } + return val + } + return NaN +} + +function getGridCustomCalcResult(query, axisMap, status: TotalStatus, customCalc) { + const quotaField = query[EXTRA_FIELD] + const { row, col } = axisMap + // 行列交叉总计 + if (status.isRowTotal && status.isColTotal) { + return customCalc.rowColTotal?.data?.[quotaField] + } + // 列总计 + if (status.isColTotal && !status.isRowSubTotal) { + const { colTotal } = customCalc + const path = getTreePath(query, row) + let val + if (path.length) { + if (colTotal) { + path.push(quotaField) + val = get(colTotal.data, path) + } + } + return val + } + // 列小计 + if (status.isColSubTotal && !status.isRowTotal && !status.isRowSubTotal) { + const { colSubTotal } = customCalc + const subLevel = getSubLevel(query, col) + const rowPath = getTreePath(query, row) + const colPath = getTreePath(query, col) + const path = [...rowPath, ...colPath] + const data = colSubTotal?.[subLevel]?.data + let val + if (path.length && data) { + path.push(quotaField) + val = get(data, path) + } + return val + } + // 行总计 + if (status.isRowTotal && !status.isColSubTotal) { + const { rowTotal } = customCalc + const path = getTreePath(query, col) + let val + if (rowTotal) { + if (path.length) { + path.push(quotaField) + val = get(rowTotal.data, path) + } + // 列维度为空,行维度不为空 + if (!col.length && row.length) { + val = get(rowTotal.data, quotaField) + } + } + return val + } + // 行小计 + if (status.isRowSubTotal && !status.isColTotal && !status.isColSubTotal) { + const { rowSubTotal } = customCalc + const rowLevel = getSubLevel(query, row) + const colPath = getTreePath(query, col) + const rowPath = getTreePath(query, row) + const path = [...colPath, ...rowPath] + const data = rowSubTotal?.[rowLevel]?.data + let val + if (path.length && rowSubTotal) { + path.push(quotaField) + val = get(data, path) + } + return val + } + // 行总计里面的列小计 + if (status.isRowTotal && status.isColSubTotal) { + const { colSubInRowTotal } = customCalc + const colLevel = getSubLevel(query, col) + const { data } = colSubInRowTotal?.[colLevel] + const colPath = getTreePath(query, col) + let val + if (colPath.length && colSubInRowTotal) { + colPath.push(quotaField) + val = get(data, colPath) + } + return val + } + // 列总计里面的行小计 + if (status.isColTotal && status.isRowSubTotal) { + const { rowSubInColTotal } = customCalc + const rowSubLevel = getSubLevel(query, row) + const data = rowSubInColTotal?.[rowSubLevel]?.data + const path = getTreePath(query, row) + let val + if (path.length && rowSubInColTotal) { + path.push(quotaField) + val = get(data, path) + } + return val + } + // 列小计里面的行小计 + if (status.isColSubTotal && status.isRowSubTotal) { + const { rowSubInColSub } = customCalc + const rowSubLevel = getSubLevel(query, row) + const colSubLevel = getSubLevel(query, col) + const data = rowSubInColSub?.[rowSubLevel]?.[colSubLevel]?.data + const rowPath = getTreePath(query, row) + const colPath = getTreePath(query, col) + const path = [...rowPath, ...colPath] + let val + if (path.length && rowSubInColSub) { + path.push(quotaField) + val = get(data, path) + } + return val + } +} +function getCustomCalcResult(query, axisMap, chart: ChartObj, status: TotalStatus, customCalc) { + const { tableLayoutMode } = chart.customAttr.basicStyle + if (tableLayoutMode === 'tree') { + return getTreeCustomCalcResult(query, axisMap, status, customCalc) + } + return getGridCustomCalcResult(query, axisMap, status, customCalc) +} + +function getSubLevel(query, axis) { + const fields: [] = axis.map(a => a.dataeaseName) + let subLevel = -1 + const queryFields = keys(query) + for (let i = fields.length - 1; i >= 0; i--) { + const field = fields[i] + const index = queryFields.findIndex(f => f === field) + if (index !== -1) { + subLevel++ + } + } + return subLevel +} + +function getTreePath(query, axis) { + const path = [] + const fields = keys(query) + axis.forEach(a => { + const index = fields.findIndex(f => f === a.dataeaseName) + if (index !== -1) { + path.push(query[a.dataeaseName]) + } + }) + return path +} + +function getAggregationAndCalcFuncByQuery(totalsStatus, totalsOptions) { + const { isRowTotal, isRowSubTotal, isColTotal, isColSubTotal } = totalsStatus + const { row, col } = totalsOptions || {} + const { calcTotals: rowCalcTotals = {}, calcSubTotals: rowCalcSubTotals = {} } = row || {} + const { calcTotals: colCalcTotals = {}, calcSubTotals: colCalcSubTotals = {} } = col || {} + + const getCalcTotals = (dimensionTotals: CalcTotals, isTotal: boolean) => { + if ((dimensionTotals.aggregation || dimensionTotals.calcFunc) && isTotal) { + return { + aggregation: dimensionTotals.aggregation, + calcFunc: dimensionTotals.calcFunc + } + } + } + + // 优先级: 列总计/小计 > 行总计/小计 + return ( + getCalcTotals(colCalcTotals, isColTotal) || + getCalcTotals(colCalcSubTotals, isColSubTotal) || + getCalcTotals(rowCalcTotals, isRowTotal) || + getCalcTotals(rowCalcSubTotals, isRowSubTotal) + ) +} + +export const isNotNumber = (value: unknown) => { + if (typeof value === 'number') { + return Number.isNaN(value) + } + if (!value) { + return true + } + if (typeof value === 'string') { + return Number.isNaN(Number(value)) + } + return true +} + +const processFieldValues = (data: DataItem[], field: string, filterIllegalValue = false) => { + if (!data?.length) { + return [] + } + + return data.reduce>((resultArr, item) => { + const fieldValue = get(item, field) + const notNumber = isNotNumber(fieldValue) + + if (filterIllegalValue && notNumber) { + // 过滤非法值 + return resultArr + } + + const val = notNumber ? 0 : fieldValue + resultArr.push(new Decimal(val)) + + return resultArr + }, []) +} + +export const getDataSumByField = (data: DataItem[], field: string): number => { + const fieldValues = processFieldValues(data, field) + if (!fieldValues.length) { + return 0 + } + + return Decimal.sum(...fieldValues).toNumber() +} + +export const getDataExtremumByField = ( + method: 'min' | 'max', + data: DataItem[], + field: string +): number => { + // 防止预处理时默认值 0 影响极值结果,处理时需过滤非法值 + const fieldValues = processFieldValues(data, field, true) + if (!fieldValues?.length) { + return + } + + return Decimal[method](...fieldValues).toNumber() +} + +export const getDataAvgByField = (data: DataItem[], field: string): number => { + const fieldValues = processFieldValues(data, field) + if (!fieldValues?.length) { + return 0 + } + + return Decimal.sum(...fieldValues) + .dividedBy(fieldValues.length) + .toNumber() +} + +const calcActionByType: { + [type in Aggregation]: (data: DataItem[], field: string) => number +} = { + [Aggregation.SUM]: getDataSumByField, + [Aggregation.MIN]: (data, field) => getDataExtremumByField('min', data, field), + [Aggregation.MAX]: (data, field) => getDataExtremumByField('max', data, field), + [Aggregation.AVG]: getDataAvgByField +} + +class EmptyDataCell extends MergedCell { + drawTextShape(): void { + this.meta.fieldValue = ' ' + super.drawTextShape() + const { rowHeader, columnHeader } = this.spreadsheet.facet + const offsetX = columnHeader.getConfig().viewportWidth / 2 + const offsetY = rowHeader.getConfig().viewportHeight / 2 + const style = this.getTextStyle() + const config = { + attrs: { + ...style, + x: offsetX, + y: offsetY, + text: t('data_set.no_data'), + opacity: 1, + textAlign: 'center', + textBaseline: 'middle' + } + } + this.addShape('text', config) + } + + protected drawBackgroundShape(): void { + const cellTheme = this.theme.dataCell.cell + cellTheme.backgroundColor = setColorOpacity(cellTheme.backgroundColor, 1) + super.drawBackgroundShape() + } +} + +export function setColorOpacity(color: string, opacity: number) { + if (color.indexOf('rgba') !== -1) { + const colorArr = color.split(',') + colorArr[3] = `${opacity})` + return colorArr.join(',') + } + if (color.indexOf('rgb') !== -1) { + return `${color.replace('rgb', 'rgba').replace(')', `,${opacity})`)}` + } + if (color.indexOf('#') !== -1) { + if (color.length === 7) { + return `${color}${Math.round(opacity * 255).toString(16)}` + } + if (color.length === 9) { + return color.slice(0, 7) + Math.round(opacity * 255).toString(16) + } + } + return color +} + +function configEmptyDataStyle(instance: PivotSheet, data: any[]) { + if (data?.length) { + return + } + instance.on(S2Event.LAYOUT_AFTER_RENDER, () => { + const { colLeafNodes, rowLeafNodes } = instance.facet?.layoutResult || {} + if (!colLeafNodes?.length || !rowLeafNodes?.length) { + return + } + const mergedCells = [] + colLeafNodes.forEach((_, colIndex) => { + rowLeafNodes.forEach((__, rowIndex) => { + mergedCells.push({ rowIndex, colIndex }) + }) + }) + instance.options.mergedCell = (s, c, m) => new EmptyDataCell(s, c, m) + instance.interaction.mergeCells(mergedCells) + }) +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/common/common_antv.ts b/frontend/src/data-visualization/chart/components/js/panel/common/common_antv.ts new file mode 100644 index 0000000..8bc7d32 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/common/common_antv.ts @@ -0,0 +1,1963 @@ +import { hexColorToRGBA, hexToRgba, measureText, parseJson } from '../../util' +import { + DEFAULT_BASIC_STYLE, + DEFAULT_LEGEND_STYLE, + DEFAULT_XAXIS_STYLE, + DEFAULT_YAXIS_EXT_STYLE, + DEFAULT_YAXIS_STYLE +} from '@/data-visualization/chart/components/editor/util/chart' +import { valueFormatter } from '@/data-visualization/chart/components/js/formatter' +import { AreaOptions, LabelOptions } from '@antv/l7plot' +import { TooltipOptions } from '@antv/l7plot/dist/lib/types/tooltip' +import { FeatureCollection } from '@antv/l7plot/dist/esm/plots/choropleth/types' +import { Datum } from '@antv/g2plot/esm/types/common' +import { Tooltip } from '@antv/g2plot/esm' +import { add } from 'mathjs' +import isEmpty from 'lodash-es/isEmpty' +import _ from 'lodash' +import type { LegendOptions } from '@antv/l7plot/dist/esm/types/legend' +import { CategoryLegendListItem } from '@antv/l7plot-component/dist/lib/types/legend' +import createDom from '@antv/dom-util/esm/create-dom' +import { + CONTAINER_TPL, + ITEM_TPL, + LIST_CLASS +} from '@antv/l7plot-component/dist/esm/legend/category/constants' +import substitute from '@antv/util/esm/substitute' +import type { Plot as L7Plot, PlotOptions } from '@antv/l7plot/dist/esm' +import { Zoom } from '@antv/l7' +import { DOM } from '@antv/l7-utils' +import { Scene } from '@antv/l7-scene' +import { type IZoomControlOption } from '@antv/l7-component' +import { PositionType } from '@antv/l7-core' +import { centroid } from '@turf/centroid' +import type { Plot } from '@antv/g2plot' +import type { PickOptions } from '@antv/g2plot/lib/core/plot' +import { defaults } from 'lodash-es' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +const { t: tI18n } = useI18n() +import { isMobile } from '@/data-visualization/utils/utils' + +export function getPadding(chart: Chart): number[] { + if (chart.drill) { + return [0, 10, 22, 10] + } else { + return [0, 10, 10, 10] + } +} +// color,label,tooltip,axis,legend,background +export function getTheme(chart: Chart) { + const colors = [] + let bgColor, + labelFontsize, + labelColor, + tooltipColor, + tooltipFontsize, + tooltipBackgroundColor, + legendColor, + legendFontsize + let customAttr: DeepPartial + if (chart.customAttr) { + customAttr = parseJson(chart.customAttr) + // color + if (customAttr.basicStyle) { + const b = JSON.parse(JSON.stringify(customAttr.basicStyle)) + b.colors.forEach(ele => { + colors.push(hexColorToRGBA(ele, b.alpha)) + }) + } + // label + if (customAttr.label) { + const l = JSON.parse(JSON.stringify(customAttr.label)) + labelFontsize = l.fontSize + labelColor = l.color + } + // tooltip + if (customAttr.tooltip) { + const t = JSON.parse(JSON.stringify(customAttr.tooltip)) + tooltipColor = t.color + tooltipFontsize = t.fontSize + tooltipBackgroundColor = t.backgroundColor + } + } + + let customStyle: DeepPartial + if (chart.customStyle) { + customStyle = parseJson(chart.customStyle) + // bg + if (customStyle.background) { + bgColor = hexColorToRGBA(customStyle.background.color, customStyle.background.alpha) + } + // legend + if (customStyle.legend) { + const l = customStyle.legend + legendColor = l.color + legendFontsize = l.fontSize + } + } + + const theme = { + styleSheet: { + brandColor: colors[0], + paletteQualitative10: colors, + paletteQualitative20: colors, + backgroundColor: bgColor + }, + labels: { + offset: 4, + style: { + fill: labelColor, + fontSize: labelFontsize + } + }, + innerLabels: { + offset: 4, + style: { + fill: labelColor, + fontSize: labelFontsize + } + }, + pieLabels: { + offset: 4, + style: { + fill: labelColor, + fontSize: labelFontsize + } + }, + components: { + tooltip: { + domStyles: { + 'g2-tooltip': { + color: tooltipColor, + fontSize: tooltipFontsize + 'px', + background: tooltipBackgroundColor, + boxShadow: '0 4px 8px 0 rgba(0, 0, 0, 0.1)', + 'z-index': 2000, + position: 'fixed' + }, + 'g2-tooltip-list-item': { + display: 'flex', + 'align-items': 'center' + }, + 'g2-tooltip-name': { + display: 'inline-block', + 'line-height': tooltipFontsize + 'px', + flex: 1 + }, + 'g2-tooltip-marker': { + 'min-width': '8px', + 'min-height': '8px' + } + } + }, + legend: { + common: { + itemName: { + style: { + fill: legendColor, + fontSize: legendFontsize + } + } + } + } + } + } + if (chart.fontFamily) { + theme.styleSheet.fontFamily = chart.fontFamily + } + return theme +} +// 通用label +export function getLabel(chart: Chart) { + let label + let customAttr: DeepPartial + if (chart.customAttr) { + customAttr = parseJson(chart.customAttr) + // label + if (customAttr.label) { + const l = customAttr.label + if (l.show) { + const layout = [] + if (!l.fullDisplay) { + if (chart.type === 'bar-stack') { + layout.push({ type: 'interval-hide-overlap' }) + } else if ( + chart.type.indexOf('-horizontal') > -1 || + [ + 'bidirectional-bar', + 'progress-bar', + 'pie-donut', + 'radar', + 'waterfall', + 't-heatmap', + 'bar' + ].includes(chart.type) + ) { + layout.push({ type: 'limit-in-canvas' }) + layout.push({ type: 'hide-overlap' }) + } else if (chart.type.includes('chart-mix')) { + layout.push({ type: 'limit-in-canvas' }) + layout.push({ type: 'limit-in-plot' }) + layout.push({ type: 'hide-overlap' }) + } else { + layout.push({ type: 'limit-in-plot' }) + layout.push({ type: 'hide-overlap' }) + } + } + label = { + position: l.position, + layout, + style: { + fill: l.color, + fontSize: l.fontSize, + fontFamily: chart.fontFamily + }, + formatter: function (param: Datum) { + return valueFormatter(param.value, l.labelFormatter) + } + } + } else { + label = false + } + } + } + return label +} +// 通用tooltip +export function getTooltip(chart: Chart) { + let tooltip + let customAttr: DeepPartial + if (chart.customAttr) { + customAttr = parseJson(chart.customAttr) + // tooltip + if (customAttr.tooltip) { + const t = JSON.parse(JSON.stringify(customAttr.tooltip)) + if (t.show) { + tooltip = { + formatter: function (param: Datum) { + const value = valueFormatter(param.value, t.tooltipFormatter) + return { name: param.field, value } + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + } else { + tooltip = false + } + } + } + return tooltip +} + +export function getMultiSeriesTooltip(chart: Chart) { + const customAttr: DeepPartial = parseJson(chart.customAttr) + const tooltipAttr = customAttr.tooltip + if (!tooltipAttr.show) { + return false + } + const formatterMap = tooltipAttr.seriesTooltipFormatter + ?.filter(i => i.show) + .reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) as Record + const tooltip: Tooltip = { + showTitle: true, + customItems(originalItems) { + if (!tooltipAttr.seriesTooltipFormatter?.length) { + return originalItems + } + const head = originalItems[0] + // 非原始数据 + if (!head.data.quotaList) { + return originalItems + } + const result = [] + originalItems + .filter(item => formatterMap[item.data.quotaList[0].id]) + .forEach(item => { + const formatter = formatterMap[item.data.quotaList[0].id] + const value = valueFormatter(parseFloat(item.value as string), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + const color = getTooltipItemConditionColor(item) + result.push({ ...item, name, value, ...(color ? { color } : {}) }) + }) + head.data.dynamicTooltipValue?.forEach(item => { + const formatter = formatterMap[item.fieldId] + if (formatter) { + const value = valueFormatter(parseFloat(item.value), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ color: 'grey', name, value }) + } + }) + return result + }, + container: getTooltipContainer(`tooltip-${chart.id}`), + itemTpl: TOOLTIP_TPL, + enterable: true + } + return tooltip +} +// 通用legend +export function getLegend(chart: Chart) { + let legend = {} + let customStyle: CustomStyle + if (chart.customStyle) { + customStyle = parseJson(chart.customStyle) + // legend + if (customStyle.legend) { + const l = defaults(JSON.parse(JSON.stringify(customStyle.legend)), DEFAULT_LEGEND_STYLE) + if (l.show) { + let offsetX, offsetY, position + const orient = l.orient + const legendSymbol = l.icon + // fix position + if (l.hPosition === 'center') { + position = l.vPosition === 'center' ? 'top' : l.vPosition + } else if (l.vPosition === 'center') { + position = l.hPosition === 'center' ? 'left' : l.hPosition + } else { + if (orient === 'horizontal') { + position = l.vPosition + '-' + l.hPosition + } else { + position = l.hPosition + '-' + l.vPosition + } + } + // fix offset + if (orient === 'horizontal') { + if (l.hPosition === 'left') { + offsetX = 16 + } else if (l.hPosition === 'right') { + offsetX = -16 + } else { + offsetX = 0 + } + if (l.vPosition === 'top') { + offsetY = 0 + } else if (l.vPosition === 'bottom') { + if (chart.drill) { + offsetY = -12 + } + } else { + offsetY = 0 + } + } else { + if (l.hPosition === 'left') { + offsetX = 10 + } else if (l.hPosition === 'right') { + offsetX = -10 + } else { + offsetX = 0 + } + if (l.vPosition === 'top') { + offsetY = 0 + } else if (l.vPosition === 'bottom') { + if (chart.drill) { + offsetY = -18 + } else { + offsetY = -10 + } + } else { + offsetY = 0 + } + } + + legend = { + layout: orient, + position: position, + offsetX: offsetX, + offsetY: offsetY, + marker: { + symbol: legendSymbol, + style: { + r: l.size + } + }, + itemName: { + style: { + fill: l.color, + fontSize: l.fontSize + } + }, + itemHeight: (l.fontSize > l.size * 2 ? l.fontSize : l.size * 2) + 4, + radio: false, + pageNavigator: { + marker: { + style: { + fill: 'rgba(0,0,0,0.65)', + stroke: 'rgba(192,192,192,0.52)', + size: l.size * 2 + } + }, + text: { + style: { + fill: l.color, + fontSize: l.fontSize + } + } + } + } + } else { + legend = false + } + } + } + return legend +} +// xAxis +export function getXAxis(chart: Chart) { + let axis: Record | boolean = {} + let customStyle: CustomStyle + if (chart.customStyle) { + customStyle = parseJson(chart.customStyle) + // legend + if (customStyle.xAxis) { + const a = JSON.parse(JSON.stringify(customStyle.xAxis)) + if (a.show) { + const title = + a.nameShow && a.name && a.name !== '' + ? { + text: a.name, + style: { + fill: a.color, + fontSize: a.fontSize + }, + spacing: 8 + } + : null + const grid = a.splitLine.show + ? { + line: { + style: { + stroke: a.splitLine.lineStyle.color, + lineWidth: a.splitLine.lineStyle.width, + lineDash: getLineDash(a.splitLine.lineStyle.style) + } + } + } + : null + const axisCfg = a.axisLine ? a.axisLine : DEFAULT_XAXIS_STYLE.axisLine + const line = axisCfg.show + ? { + style: { + stroke: axisCfg.lineStyle.color, + lineWidth: axisCfg.lineStyle.width, + lineDash: getLineDash(axisCfg.lineStyle.style) + } + } + : null + const tickLine = axisCfg.show + ? { + style: { + stroke: axisCfg.lineStyle.color, + lineWidth: axisCfg.lineStyle.width + } + } + : null + let textAlign = 'center' + const rotate = a.axisLabel.rotate + if (a.position === 'top') { + textAlign = rotate > 20 ? 'end' : rotate < -20 ? 'start' : 'center' + } + if (a.position === 'bottom') { + textAlign = rotate > 20 ? 'start' : rotate < -20 ? 'end' : 'center' + } + const label = a.axisLabel.show + ? { + rotate: (rotate * Math.PI) / 180, + style: { + fill: a.axisLabel.color, + fontSize: a.axisLabel.fontSize, + textAlign: textAlign + }, + formatter: value => { + return chart.type === 'bidirectional-bar' && value.length > a.axisLabel.lengthLimit + ? value.substring(0, a.axisLabel.lengthLimit) + '...' + : value + } + } + : null + + axis = { + position: a.position, + title, + grid, + label, + line, + tickLine + } + } else { + axis = false + } + } + } + return axis +} +// yAxis +export function getYAxis(chart: Chart) { + let axis: Record | boolean = {} + const yAxis = parseJson(chart.customStyle).yAxis + if (!yAxis.show) { + return false + } + const title = + yAxis.nameShow && yAxis.name && yAxis.name !== '' + ? { + text: yAxis.name, + style: { + fill: yAxis.color, + fontSize: yAxis.fontSize + }, + spacing: 8 + } + : null + const grid = yAxis.splitLine.show + ? { + line: { + style: { + stroke: yAxis.splitLine.lineStyle.color, + lineWidth: yAxis.splitLine.lineStyle.width, + lineDash: getLineDash(yAxis.splitLine.lineStyle.style) + } + } + } + : null + const axisCfg = yAxis.axisLine ? yAxis.axisLine : DEFAULT_YAXIS_STYLE.axisLine + const line = axisCfg.show + ? { + style: { + stroke: axisCfg.lineStyle.color, + lineWidth: axisCfg.lineStyle.width, + lineDash: getLineDash(axisCfg.lineStyle.style) + } + } + : null + const tickLine = axisCfg.show + ? { + style: { + stroke: axisCfg.lineStyle.color, + lineWidth: axisCfg.lineStyle.width + } + } + : null + const rotate = yAxis.axisLabel.rotate + let textAlign = 'end' + let textBaseline = 'middle' + if (yAxis.position === 'right') { + textAlign = 'start' + if (Math.abs(rotate) > 75) { + textAlign = 'center' + } + if (rotate > 75) { + textBaseline = 'bottom' + } + if (rotate < -75) { + textBaseline = 'top' + } + } + if (yAxis.position === 'left') { + if (Math.abs(rotate) > 75) { + textAlign = 'center' + } + if (rotate > 75) { + textBaseline = 'top' + } + if (rotate < -75) { + textBaseline = 'bottom' + } + } + const label = yAxis.axisLabel.show + ? { + rotate: (rotate * Math.PI) / 180, + style: { + fill: yAxis.axisLabel.color, + fontSize: yAxis.axisLabel.fontSize, + textBaseline, + textAlign + }, + formatter: value => { + return value.length > yAxis.axisLabel.lengthLimit + ? value.substring(0, yAxis.axisLabel.lengthLimit) + '...' + : value + } + } + : null + + axis = { + position: yAxis.position, + title, + grid, + label, + line, + tickLine, + nice: true + } + return axis +} + +export function getYAxisExt(chart: Chart) { + let axis: Record | boolean = {} + const yAxis = parseJson(chart.customStyle).yAxisExt + if (!yAxis.show) { + return false + } + const title = + yAxis.name && yAxis.name !== '' + ? { + text: yAxis.name, + style: { + fill: yAxis.color, + fontSize: yAxis.fontSize + }, + spacing: 8 + } + : null + const grid = yAxis.splitLine.show + ? { + line: { + style: { + stroke: yAxis.splitLine.lineStyle.color, + lineWidth: yAxis.splitLine.lineStyle.width, + lineDash: getLineDash(yAxis.splitLine.lineStyle.style) + } + } + } + : null + const axisCfg = yAxis.axisLine ? yAxis.axisLine : DEFAULT_YAXIS_STYLE.axisLine + const line = axisCfg.show + ? { + style: { + stroke: axisCfg.lineStyle.color, + lineWidth: axisCfg.lineStyle.width + } + } + : null + const tickLine = axisCfg.show + ? { + style: { + stroke: axisCfg.lineStyle.color + } + } + : null + const rotate = yAxis.axisLabel.rotate + let textAlign = 'end' + let textBaseline = 'middle' + if (yAxis.position === 'right') { + textAlign = 'start' + if (Math.abs(rotate) > 75) { + textAlign = 'center' + } + if (rotate > 75) { + textBaseline = 'bottom' + } + if (rotate < -75) { + textBaseline = 'top' + } + } + if (yAxis.position === 'left') { + if (Math.abs(rotate) > 75) { + textAlign = 'center' + } + if (rotate > 75) { + textBaseline = 'top' + } + if (rotate < -75) { + textBaseline = 'bottom' + } + } + const label = yAxis.axisLabel.show + ? { + rotate: (rotate * Math.PI) / 180, + style: { + fill: yAxis.axisLabel.color, + fontSize: yAxis.axisLabel.fontSize, + textBaseline, + textAlign + } + } + : null + + axis = { + position: yAxis.position, + title, + grid, + label, + line, + tickLine, + nice: true + } + return axis +} + +export function getSlider(chart: Chart) { + let cfg + const senior = parseJson(chart.senior) + if (senior.functionCfg) { + if (senior.functionCfg.sliderShow) { + cfg = { + start: senior.functionCfg.sliderRange[0] / 100, + end: senior.functionCfg.sliderRange[1] / 100 + } + + if (senior.functionCfg.sliderBg) { + cfg.backgroundStyle = { + fill: senior.functionCfg.sliderBg, + stroke: senior.functionCfg.sliderBg, + lineWidth: 1, + strokeOpacity: 0.5 + } + } + if (senior.functionCfg.sliderFillBg) { + cfg.foregroundStyle = { + fill: senior.functionCfg.sliderFillBg, + fillOpacity: 0.5 + } + } + if (senior.functionCfg.sliderTextColor) { + cfg.textStyle = { + fill: senior.functionCfg.sliderTextColor, + fontFamily: chart.fontFamily + } + cfg.handlerStyle = { + fill: senior.functionCfg.sliderTextColor, + fillOpacity: 0.5, + highLightFill: senior.functionCfg.sliderTextColor + } + } + } + } + return cfg +} + +export function getAnalyse(chart: Chart) { + const assistLine = [] + const senior = parseJson(chart.senior) + if (!senior.assistLineCfg?.enable) { + return assistLine + } + const assistLineArr = senior.assistLineCfg.assistLine + if (assistLineArr?.length > 0) { + const customStyle = parseJson(chart.customStyle) + let yAxisPosition, axisFormatterCfg, yAxisExtPosition, axisExtFormatterCfg + if (customStyle.yAxis) { + const a = JSON.parse(JSON.stringify(customStyle.yAxis)) + yAxisPosition = a.position + axisFormatterCfg = a.axisLabelFormatter + ? a.axisLabelFormatter + : DEFAULT_YAXIS_STYLE.axisLabelFormatter + } + if (customStyle.yAxisExt) { + const a = JSON.parse(JSON.stringify(customStyle.yAxisExt)) + yAxisExtPosition = a.position + axisExtFormatterCfg = a.axisLabelFormatter + ? a.axisLabelFormatter + : DEFAULT_YAXIS_EXT_STYLE.axisLabelFormatter + } + + const fixedLines = assistLineArr.filter(ele => ele.field === '0') + const dynamicLineFields = assistLineArr + .filter(ele => ele.field === '1') + .map(item => item.fieldId) + const quotaFields = _.filter(chart.yAxis, ele => ele.summary !== '' && ele.id !== '-1') + const quotaExtFields = _.filter(chart.yAxisExt, ele => ele.summary !== '' && ele.id !== '-1') + const dynamicLines = chart.data.dynamicAssistLines?.filter(item => { + return ( + dynamicLineFields?.includes(item.fieldId) && + (!!_.find(quotaFields, d => d.id === item.fieldId) || + (!!_.find(quotaExtFields, d => d.id === item.fieldId) && + chart.type.includes('chart-mix'))) + ) + }) + const lines = fixedLines.concat(dynamicLines || []) + lines.forEach(ele => { + const value = parseFloat(ele.value) + const content = + ele.name + + ' : ' + + valueFormatter(value, ele.yAxisType === 'left' ? axisFormatterCfg : axisExtFormatterCfg) + assistLine.push({ + type: 'line', + yAxisType: ele.yAxisType, + start: ['start', value], + end: ['end', value], + style: { + stroke: ele.color, + lineDash: getLineDash(ele.lineType) + } + }) + assistLine.push({ + type: 'text', + yAxisType: ele.yAxisType, + position: [ + (ele.yAxisType === 'left' ? yAxisPosition : yAxisExtPosition) === 'left' + ? 'start' + : 'end', + value + ], + content: content, + offsetY: -2, + offsetX: + (ele.yAxisType === 'left' ? yAxisPosition : yAxisExtPosition) === 'left' + ? 2 + : -10 * (content.length - 2), + style: { + textBaseline: 'bottom', + fill: ele.color, + fontSize: ele.fontSize ? ele.fontSize : 10 + } + }) + }) + } + return assistLine +} + +export function getAnalyseHorizontal(chart: Chart) { + const assistLine = [] + const senior = parseJson(chart.senior) + if (!senior.assistLineCfg?.enable) { + return assistLine + } + const assistLineArr = senior.assistLineCfg.assistLine + if (assistLineArr?.length > 0) { + const customStyle = parseJson(chart.customStyle) + let xAxisPosition, axisFormatterCfg + if (customStyle.xAxis) { + const a = JSON.parse(JSON.stringify(customStyle.xAxis)) + xAxisPosition = transAxisPosition(a.position) + axisFormatterCfg = a.axisLabelFormatter + ? a.axisLabelFormatter + : DEFAULT_XAXIS_STYLE.axisLabelFormatter + } + + const fixedLines = assistLineArr.filter(ele => ele.field === '0') + const dynamicLineFields = assistLineArr + .filter(ele => ele.field === '1') + .map(item => item.fieldId) + const quotaFields = _.filter(chart.yAxis, ele => ele.summary !== '' && ele.id !== '-1') + const dynamicLines = chart.data.dynamicAssistLines?.filter( + item => + dynamicLineFields?.includes(item.fieldId) && + !!_.find(quotaFields, d => d.id === item.fieldId) + ) + const lines = fixedLines.concat(dynamicLines || []) + + lines.forEach(ele => { + const value = parseFloat(ele.value) + const content = ele.name + ' : ' + valueFormatter(value, axisFormatterCfg) + assistLine.push({ + type: 'line', + start: ['start', value], + end: ['end', value], + style: { + stroke: ele.color, + lineDash: getLineDash(ele.lineType) + } + }) + assistLine.push({ + type: 'text', + position: ['start', value], + content: content, + offsetY: 5, + offsetX: 2, + rotate: Math.PI / 2, + style: { + textBaseline: 'bottom', + fill: ele.color, + fontSize: ele.fontSize ? ele.fontSize : 10 + } + }) + }) + } + return assistLine +} + +export function getLineDash(type) { + switch (type) { + case 'solid': + return [0, 0] + case 'dashed': + return [10, 8] + case 'dotted': + return [2, 2] + default: + return [0, 0] + } +} + +/** + * 将 RGBA 格式的颜色转换成 ANTV 支持的渐变色格式 + * @param rawColor 原始 RGBA 颜色 + * @param show + * @param angle 渐变角度 + * @param start 起始值 + */ +export function setGradientColor(rawColor: string, show = false, angle = 0, start = 0) { + const item = rawColor.split(',') + item.splice(3, 1, '0.3)') + let color: string + if (start == 0) { + color = `l(${angle}) 0:${item.join(',')} 1:${rawColor}` + } else if (start > 0) { + color = `l(${angle}) 0:rgba(255,255,255,0) ${start}:${item.join(',')} 1:${rawColor}` + } else { + color = `l(${angle}) 0:rgba(255,255,255,0) 0.1:${item.join(',')} 1:${rawColor}` + } + return show ? color : rawColor +} + +export function transAxisPosition(position: string): string { + switch (position) { + case 'top': + return 'left' + case 'bottom': + return 'right' + case 'left': + return 'bottom' + case 'right': + return 'top' + default: + return position + } +} + +export function configL7Label(chart: Chart): false | LabelOptions { + const customAttr = parseJson(chart.customAttr) + const label = customAttr.label + const style = { + fill: label.color, + fontSize: label.fontSize, + textAllowOverlap: true, + fontWeight: 'bold' + } + if (!label.fullDisplay) { + style.textAllowOverlap = false + style.padding = [2, 2] + } + if (chart.fontFamily) { + style.fontFamily = chart.fontFamily + } + return { + visible: label.show, + style + } +} + +export function configL7Style(chart: Chart): AreaOptions['style'] { + const customAttr = parseJson(chart.customAttr) + return { + stroke: customAttr.basicStyle.areaBorderColor + } +} + +export function configL7Tooltip(chart: Chart): TooltipOptions { + const customAttr = parseJson(chart.customAttr) + const tooltip = customAttr.tooltip + const formatterMap = tooltip.seriesTooltipFormatter + ?.filter(i => i.show) + .reduce((pre, next) => { + pre[next.id] = next + return pre + }, {}) as Record + const container = document.getElementById(chart.container) + if (container) { + container.addEventListener('mousemove', event => { + const rect = container.getBoundingClientRect() + const mouseX = event.clientX - rect.left + const mouseY = event.clientY - rect.top + const tooltipElement = container.getElementsByClassName('l7plot-tooltip-container') + for (let i = 0; i < tooltipElement?.length; i++) { + const element = tooltipElement[i] as HTMLElement + const isNearRightEdge = container.clientWidth - mouseX <= element.clientWidth + const isNearBottomEdge = container.clientHeight - mouseY <= element.clientHeight + let transform = '' + if (isNearRightEdge) { + transform += 'translateX(-120%) ' + } + if (isNearBottomEdge) { + transform += 'translateY(-100%) ' + } + if (transform) { + element.style.transform = transform.trim() + } + } + }) + } + return { + customTitle(data) { + return data.name + }, + customItems(originalItem) { + const result = [] + if (isEmpty(formatterMap)) { + return result + } + const head = originalItem.properties + const formatter = formatterMap[head.quotaList?.[0]?.id] + if (!isEmpty(formatter)) { + const originValue = parseFloat(head.value as string) + const value = valueFormatter(originValue, formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ ...head, name, value: `${value ?? ''}` }) + } + head.dynamicTooltipValue?.forEach(item => { + const formatter = formatterMap[item.fieldId] + if (formatter) { + const value = valueFormatter(parseFloat(item.value), formatter.formatterCfg) + const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName + result.push({ color: 'grey', name, value: `${value ?? ''}` }) + } + }) + return result + }, + showComponent: tooltip.show, + domStyles: { + 'l7plot-tooltip': { + 'background-color': tooltip.backgroundColor, + 'font-size': `${tooltip.fontSize}px`, + 'line-height': 1.6, + 'font-family': chart.fontFamily ? chart.fontFamily : undefined + }, + 'l7plot-tooltip__name': { + color: tooltip.color + }, + 'l7plot-tooltip__value': { + color: tooltip.color + }, + 'l7plot-tooltip__title': { + color: tooltip.color + } + } + } +} + +export function handleGeoJson(geoJson: FeatureCollection, nameMapping?: Record) { + geoJson.features.forEach(item => { + if (!item.properties['centroid']) { + if (item.properties['center']) { + item.properties['centroid'] = item.properties['center'] + } else { + const tmp = centroid(item.geometry) + item.properties['centroid'] = tmp.geometry.coordinates + } + } + let name = item.properties['name'] + if (nameMapping?.[name]) { + name = nameMapping[name] + item.properties['name'] = name + } + }) +} + +export function getTooltipSeriesTotalMap(data: any[]): Record { + const result = {} + data?.forEach(item => { + item.dynamicTooltipValue?.forEach(ele => { + if (!result[ele.fieldId]) { + result[ele.fieldId] = 0 + } + if (ele.value) { + result[ele.fieldId] = add(result[ele.fieldId], ele.value) + } + }) + }) + return result +} +const LEGEND_SHAPE_STYLE_MAP = { + circle: { + borderRadius: '50%' + }, + square: {}, + triangle: { + borderLeft: '5px solid transparent', + borderRight: '5px solid transparent', + borderBottom: '10px solid var(--bgColor)', + background: 'unset' + }, + diamond: { + transform: 'rotate(45deg)' + } +} +export function configL7Legend(chart: Chart): LegendOptions | false { + const { basicStyle } = parseJson(chart.customAttr) + if (basicStyle.suspension === false && basicStyle.showZoom === undefined) { + return false + } + const { legend } = parseJson(chart.customStyle) + if (!legend.show) { + return false + } + return { + position: 'bottomleft', + customContent: (_: string, items: CategoryLegendListItem[]) => { + const showItems = items?.length > 30 ? items.slice(0, 30) : items + if (showItems?.length) { + const containerDom = createDom(CONTAINER_TPL) as HTMLElement + const listDom = containerDom.getElementsByClassName(LIST_CLASS)[0] as HTMLElement + showItems.forEach(item => { + let value = '-' + if (item.value !== '') { + if (Array.isArray(item.value)) { + item.value.forEach((v, i) => { + item.value[i] = Number.isNaN(v) || v === 'NaN' ? 'NaN' : parseFloat(v).toFixed(0) + }) + value = item.value.join('-') + } else { + const tmp = item.value as string + value = Number.isNaN(tmp) || tmp === 'NaN' ? 'NaN' : parseFloat(tmp).toFixed(0) + } + } + const substituteObj = { ...item, value } + + const domStr = substitute(ITEM_TPL, substituteObj) + const itemDom = createDom(domStr) + // 给 legend 形状用的 + itemDom.style.setProperty('--bgColor', item.color) + listDom.appendChild(itemDom) + }) + return listDom + } + return '' + }, + domStyles: { + 'l7plot-legend__category-value': { + fontSize: legend.fontSize + 'px', + color: legend.color + }, + 'l7plot-legend__category-marker': { + ...LEGEND_SHAPE_STYLE_MAP[legend.icon] + } + } + } +} +const ZOOM_IN_BTN = + '' +const RESET_BTN = + '' +const ZOOM_OUT_BTN = + '' +export class CustomZoom extends Zoom { + resetButtonGroup(container) { + DOM.clearChildren(container) + this['zoomInButton'] = this['createButton']( + this.controlOption.zoomInText, + this.controlOption.zoomInTitle, + 'l7-button-control', + container, + this.zoomIn + ) + this['zoomResetButton'] = this['createButton']( + this.controlOption['resetText'], + 'Reset', + 'l7-button-control', + container, + () => { + if (this.controlOption['bounds']) { + this.mapsService.fitBounds(this.controlOption['bounds'], { animate: true }) + } else { + this.mapsService.setZoomAndCenter( + this.controlOption['initZoom'], + this.controlOption['center'] + ) + } + } + ) + if (this.controlOption.showZoom) { + this['zoomNumDiv'] = this['createButton']( + '0', + '', + 'l7-button-control l7-control-zoom__number', + container + ) + } + this['zoomOutButton'] = this['createButton']( + this.controlOption.zoomOutText, + this.controlOption.zoomOutTitle, + 'l7-button-control', + container, + this.zoomOut + ) + const { buttonBackground } = this.controlOption as any + const elements = [this['zoomResetButton'], this['zoomInButton'], this['zoomOutButton']] + if (buttonBackground) { + setStyle(elements, 'background', buttonBackground) + } + setStyle(elements, 'border-bottom', 'none') + this['updateDisabled']() + } + public getDefault(option: Partial) { + const { buttonColor } = option as any + let zoomInText = ZOOM_IN_BTN + let zoomOutText = ZOOM_OUT_BTN + let resetText = RESET_BTN + if (buttonColor) { + zoomInText = zoomInText.replace('${fill}', buttonColor) + zoomOutText = zoomOutText.replace('${fill}', buttonColor) + resetText = resetText.replace('${fill}', buttonColor) + } + return { + ...option, + position: PositionType.BOTTOMRIGHT, + name: 'zoom', + zoomInText, + zoomInTitle: 'Zoom in', + zoomOutText, + zoomOutTitle: 'Zoom out', + resetText, + showZoom: false + } as IZoomControlOption + } +} +export function configL7Zoom(chart: Chart, scene: Scene) { + const { basicStyle } = parseJson(chart.customAttr) + const zoomOption = scene?.getControlByName('zoom') + if (zoomOption) { + scene.removeControl(zoomOption) + } + if (shouldHideZoom(basicStyle)) { + return + } + if (!scene?.getControlByName('zoom')) { + if (!scene.map) { + scene.once('loaded', () => { + scene.map.on('complete', () => { + const initZoom = basicStyle.autoFit === false ? basicStyle.zoomLevel : scene.getZoom() + const center = + basicStyle.autoFit === false + ? [basicStyle.mapCenter.longitude, basicStyle.mapCenter.latitude] + : [scene.map.getCenter().lng, scene.map.getCenter().lat] + const newZoomOptions = { + initZoom: initZoom, + center: center, + buttonColor: basicStyle.zoomButtonColor, + buttonBackground: basicStyle.zoomBackground + } as any + scene.addControl(new CustomZoom(newZoomOptions)) + }) + }) + } else { + const newZoomOptions = { + buttonColor: basicStyle.zoomButtonColor, + buttonBackground: basicStyle.zoomBackground + } as any + if (basicStyle.autoFit === false) { + newZoomOptions.initZoom = basicStyle.zoomLevel + newZoomOptions.center = [basicStyle.mapCenter.longitude, basicStyle.mapCenter.latitude] + } else { + const coordinates: [][] = [] + if (chart.type === 'flow-map') { + const startAxis = chart.xAxis + const endAxis = chart.xAxisExt + if (startAxis?.length === 2) { + chart.data?.tableRow?.forEach(row => { + coordinates.push([row[startAxis[0].dataeaseName], row[startAxis[1].dataeaseName]]) + }) + } + if (endAxis?.length === 2) { + chart.data?.tableRow?.forEach(row => { + coordinates.push([row[endAxis[0].dataeaseName], row[endAxis[1].dataeaseName]]) + }) + } + } else { + const axis = chart.xAxis + if (axis?.length === 2) { + chart.data?.tableRow?.forEach(row => { + coordinates.push([row[axis[0].dataeaseName], row[axis[1].dataeaseName]]) + }) + } + } + newZoomOptions.bounds = calculateBounds(coordinates) + } + scene.addControl(new CustomZoom(newZoomOptions)) + } + } +} +/** + * 计算经纬度数据的边界点 + * @param coordinates 经纬度数组 [[lng, lat], [lng, lat], ...] + * @returns {[[number, number], [number, number]]} 返回东北角和西南角的坐标 + */ +export function calculateBounds(coordinates: number[][]): { + northEast: [number, number] + southWest: [number, number] +} { + if (!coordinates || coordinates.length === 0) { + return { + northEast: [180, 90], + southWest: [-180, -90] + } + } + + let maxLng = -180 + let minLng = 180 + let maxLat = -90 + let minLat = 90 + + coordinates.forEach(([lng, lat]) => { + maxLng = Math.max(maxLng, lng) + minLng = Math.min(minLng, lng) + maxLat = Math.max(maxLat, lat) + minLat = Math.min(minLat, lat) + }) + + return [ + [maxLng, maxLat], // 东北角坐标 + [minLng, minLat] // 西南角坐标 + ] +} + +export function configL7PlotZoom(chart: Chart, plot: L7Plot) { + const { basicStyle } = parseJson(chart.customAttr) + if (shouldHideZoom(basicStyle)) { + return + } + plot.once('loaded', () => { + const zoomOptions = { + initZoom: plot.scene.getZoom(), + center: plot.scene.getCenter(), + buttonColor: basicStyle.zoomButtonColor, + buttonBackground: basicStyle.zoomBackground + } as any + plot.scene.addControl(new CustomZoom(zoomOptions)) + }) +} + +function setStyle(elements: HTMLElement[], styleProp: string, value) { + elements.forEach(e => { + e.style[styleProp] = value + }) +} + +export function mapRendering(dom: HTMLElement | string) { + if (typeof dom === 'string') { + dom = document.getElementById(dom) + } + dom.classList.add('de-map-rendering') +} + +export function mapRendered(dom: HTMLElement | string) { + if (typeof dom === 'string') { + dom = document.getElementById(dom) + } + dom.classList.add('de-map-rendered') +} + +/** + * 隐藏缩放控件 + * @param basicStyle + */ +function shouldHideZoom(basicStyle: any): boolean { + return ( + (basicStyle.suspension === false && basicStyle.showZoom === undefined) || + basicStyle.showZoom === false + ) +} + +const G2_TOOLTIP_WRAPPER = 'g2-tooltip-wrapper' +export function getTooltipContainer(id) { + let wrapperDom = document.getElementById(G2_TOOLTIP_WRAPPER) + if (!wrapperDom) { + wrapperDom = document.createElement('div') + wrapperDom.id = G2_TOOLTIP_WRAPPER + document.body.appendChild(wrapperDom) + } + const curDom = document.getElementById(id) + if (curDom) { + curDom.remove() + } + const g2Tooltip = document.createElement('div') + g2Tooltip.setAttribute('id', id) + g2Tooltip.classList.add('g2-tooltip') + // 最多半屏,鼠标移入可滚动 + g2Tooltip.style.maxHeight = '50%' + isMobile() ? (g2Tooltip.style.maxWidth = '50%') : (g2Tooltip.style.maxWidth = '25%') + g2Tooltip.style.overflowY = 'auto' + g2Tooltip.style.display = 'none' + g2Tooltip.style.position = 'fixed' + g2Tooltip.style.left = '0px' + g2Tooltip.style.top = '0px' + const g2TooltipTitle = document.createElement('div') + g2TooltipTitle.classList.add('g2-tooltip-title') + g2Tooltip.appendChild(g2TooltipTitle) + + const g2TooltipList = document.createElement('ul') + g2TooltipList.classList.add('g2-tooltip-list') + g2Tooltip.appendChild(g2TooltipList) + const full = document.getElementsByClassName('fullscreen') + if (full.length) { + full.item(0).appendChild(g2Tooltip) + } else { + wrapperDom.appendChild(g2Tooltip) + } + return g2Tooltip +} +export function configPlotTooltipEvent>( + chart: Chart, + plot: P +) { + const { tooltip } = parseJson(chart.customAttr) + if (!tooltip.show) { + return + } + // 鼠标可移入, 移入之后保持显示, 移出之后隐藏 + plot.options.tooltip.container.addEventListener('mouseenter', e => { + e.target.style.visibility = 'visible' + e.target.style.display = 'block' + }) + plot.options.tooltip.container.addEventListener('mouseleave', e => { + e.target.style.visibility = 'hidden' + e.target.style.display = 'none' + }) + // 手动处理 tooltip 的显示和隐藏事件,需配合源码理解 + // https://github.com/antvis/G2/blob/master/src/chart/controller/tooltip.ts#showTooltip + plot.on('tooltip:show', () => { + const tooltipCtl = plot.chart.getController('tooltip') + if (!tooltipCtl) { + return + } + const event = plot.chart.interactions.tooltip?.context?.event + if (tooltipCtl.tooltip) { + // 处理视图放大后再关闭 tooltip 的 dom 被清除 + const container = tooltipCtl.tooltip.cfg.container + container.style.display = 'block' + const dom = document.getElementById(container.id) + if (!dom) { + const full = document.getElementsByClassName('fullscreen') + if (full.length) { + full.item(0).appendChild(container) + } else { + const wrapperDom = document.getElementById(G2_TOOLTIP_WRAPPER) + wrapperDom.appendChild(container) + } + } + } + plot.chart.getOptions().tooltip.follow = false + tooltipCtl.title = Math.random().toString() + plot.chart.getTheme().components.tooltip.x = event.clientX + plot.chart.getTheme().components.tooltip.y = event.clientY + }) + // https://github.com/antvis/G2/blob/master/src/chart/controller/tooltip.ts#hideTooltip + plot.on('plot:leave', () => { + const tooltipCtl = plot.chart.getController('tooltip') + if (!tooltipCtl) { + return + } + plot.chart.getOptions().tooltip.follow = true + const container = tooltipCtl.tooltip?.cfg?.container + if (container) { + container.style.display = 'none' + } + tooltipCtl.hideTooltip() + }) + // 移动端处理,关闭其他图表的提示 + plot.on('plot:touchstart', () => { + const wrapperDom = document.getElementById(G2_TOOLTIP_WRAPPER) + if (wrapperDom) { + const tooltipCtl = plot.chart.getController('tooltip') + if (!tooltipCtl) { + return + } + const container = tooltipCtl.tooltip.cfg.container + for (const ele of wrapperDom.children) { + if (container.id !== ele.id) { + ele.style.display = 'none' + } + } + } + }) +} + +export const TOOLTIP_TPL = + '
  • ' + + '' + + '{name}:' + + '{value}' + + '
  • ' + +export function getConditions(chart: Chart) { + const { threshold } = parseJson(chart.senior) + const annotations = [] + if (!threshold.enable || chart.type === 'area-stack' || chart.type === 'symbolic-map') + return annotations + const conditions = threshold.lineThreshold ?? [] + const yAxisIds = chart.yAxis.map(i => i.id) + for (const field of conditions) { + if (!yAxisIds.includes(field.fieldId)) { + continue + } + for (const t of field.conditions) { + const annotation = { + type: 'regionFilter', + start: ['start', 'median'], + end: ['end', 'min'], + color: t.color + } + // 加中线 + const annotationLine = { + type: 'line', + start: ['start', t.value], + end: ['end', t.value], + style: { + stroke: t.color, + lineDash: [2, 2] + } + } + if (t.term === 'between') { + annotation.start = ['start', parseFloat(t.min)] + annotation.end = ['end', parseFloat(t.max)] + annotationLine.start = ['start', parseFloat(t.min)] + annotationLine.end = ['end', parseFloat(t.min)] + annotations.push(JSON.parse(JSON.stringify(annotationLine))) + annotationLine.start = ['start', parseFloat(t.max)] + annotationLine.end = ['end', parseFloat(t.max)] + annotations.push(annotationLine) + } else if (['lt', 'le'].includes(t.term)) { + annotation.start = ['start', t.value] + annotation.end = ['end', 'min'] + annotations.push(annotationLine) + } else if (['gt', 'ge'].includes(t.term)) { + annotation.start = ['start', t.value] + annotation.end = ['end', 'max'] + annotations.push(annotationLine) + } + annotations.push(annotation) + } + } + return annotations +} +const AXIS_LABEL_TOOLTIP_STYLE = { + transition: + 'left 0.4s cubic-bezier(0.23, 1, 0.32, 1) 0s, top 0.4s cubic-bezier(0.23, 1, 0.32, 1) 0s', + backgroundColor: 'rgb(255, 255, 255)', + boxShadow: 'rgb(174, 174, 174) 0px 0px 10px', + borderRadius: '3px', + padding: '8px 12px', + opacity: '0.95', + position: 'absolute', + visibility: 'visible' +} +const AXIS_LABEL_TOOLTIP_TPL = + '
    ' + '
    {title}
    ' + '
    ' +export function configAxisLabelLengthLimit(chart, plot, triggerObjName) { + // 设置触发事件的名称,如果未传入,则默认为 'axis-label' + const triggerName = triggerObjName || 'axis-label' + + // 判断是否是Y轴标题 + const isYaxisTitle = triggerName === 'axis-title' + + // 解析图表的自定义样式和属性 + const { customStyle, customAttr } = parseJson(chart) + const { lengthLimit, fontSize, color, show } = customStyle.yAxis.axisLabel + const { tooltip } = customAttr + + // 如果不是标题,判断没有设置长度限制、没有显示或Y轴不显示,或图表类型为双向条形图,则不执行后续操作 + if ( + !isYaxisTitle && + (!lengthLimit || !show || !customStyle.yAxis.show || chart.type === 'bidirectional-bar') + ) + return + + // 鼠标进入事件 + plot.on(triggerName + ':mouseenter', e => { + const field = e.target.cfg.delegateObject.component.cfg.field + const position = e.target.cfg.delegateObject.component.cfg.position + const isYaxis = position === 'left' || position === 'right' + + // 如果不是 'field' 或 'title',且不是Y轴,直接返回 + if (field !== 'field' && field !== 'title' && !isYaxis) return + + // 获取轴标签的实际内容 + const realContent = e.target.attrs.text + + // 不是标题时,判断标签长度小于限制或已经省略(以'...'结尾),则不显示 tooltip + if ( + isYaxisTitle ? false : realContent.length < lengthLimit || !(realContent.slice(-3) === '...') + ) + return + + // 获取当前鼠标事件的坐标 + const { x, y } = e + const parentNode = e.event.target.parentNode + + // 获取父节点中是否已有 tooltip + let labelTooltipDom = parentNode.getElementsByClassName('g2-axis-label-tooltip')[0] + + // 获取轴的标题 + const title = + e.target.cfg.delegateObject.item?.name || + e.target.cfg.delegateObject.axis.cfg.title.originalText + + // 如果没有 tooltip,创建新的 tooltip DOM 元素 + if (!labelTooltipDom) { + const domStr = substitute(AXIS_LABEL_TOOLTIP_TPL, { title }) + labelTooltipDom = createDom(domStr) + + // 设置 tooltip 的样式 + AXIS_LABEL_TOOLTIP_STYLE.backgroundColor = tooltip.backgroundColor + AXIS_LABEL_TOOLTIP_STYLE.boxShadow = `${tooltip.backgroundColor} 0px 0px 5px` + AXIS_LABEL_TOOLTIP_STYLE.maxWidth = '200px' + _.assign(labelTooltipDom.style, AXIS_LABEL_TOOLTIP_STYLE) + + // 将 tooltip 添加到父节点 + parentNode.appendChild(labelTooltipDom) + } else { + // 如果已有 tooltip,更新其标题并使其可见 + labelTooltipDom.getElementsByClassName('g2-tooltip-title')[0].innerHTML = title + labelTooltipDom.style.visibility = 'visible' + } + + // 获取父节点的尺寸和 tooltip 的尺寸 + const { height, width } = parentNode.getBoundingClientRect() + const { offsetHeight, offsetWidth } = labelTooltipDom + + // 如果 tooltip 的尺寸超出了父节点的尺寸,则将其位置重置为 (0, 0) + if (offsetHeight > height || offsetWidth > width) { + labelTooltipDom.style.left = labelTooltipDom.style.top = '0px' + return + } + + // 计算 tooltip 的初始位置 + const initPosition = { left: x + 10, top: y + 15 } + + // 调整位置,避免 tooltip 超出边界 + if (initPosition.left + offsetWidth > width) initPosition.left = width - offsetWidth - 10 + if (initPosition.top + offsetHeight > height) initPosition.top -= offsetHeight + 15 + + // 设置 tooltip 的位置和样式 + labelTooltipDom.style.left = `${initPosition.left}px` + labelTooltipDom.style.top = `${initPosition.top}px` + labelTooltipDom.style.color = color + labelTooltipDom.style.fontSize = `${fontSize}px` + }) + + // 鼠标离开事件 + plot.on(triggerName + ':mouseleave', e => { + const field = e.target.cfg.delegateObject.component.cfg.field + const position = e.target.cfg.delegateObject.component.cfg.position + const isYaxis = position === 'left' || position === 'right' + + // 如果不是 'field' 或 'title',且不是Y轴,直接返回 + if (field !== 'field' && field !== 'title' && !isYaxis) return + + // 获取轴标签的实际内容 + const realContent = e.target.attrs.text + + // 如果标签长度小于限制或已经省略(以'...'结尾),则不显示 tooltip + if ( + isYaxisTitle ? false : realContent.length < lengthLimit || !(realContent.slice(-3) === '...') + ) + return + + // 获取父节点中的 tooltip + const parentNode = e.event.target.parentNode + const labelTooltipDom = parentNode.getElementsByClassName('g2-axis-label-tooltip')[0] + + // 如果 tooltip 存在,隐藏它 + if (labelTooltipDom) labelTooltipDom.style.visibility = 'hidden' + }) +} + +/** + * y轴标题截取 + * @param chart + * @param plot + */ +export function configYaxisTitleLengthLimit(chart, plot) { + // 监听图表渲染前事件 + plot.on('beforerender', ev => { + // 获取图表的Y轴自定义样式 + const { yAxis } = parseJson(chart.customStyle) + + // 计算最大可用空间高度,80% 为最大高度比 + const maxHeightRatio = + 0.8 * (ev.view.canvas.cfg.height - (ev.view.canvas.cfg.height < 120 ? 60 : 30)) + + // 计算Y轴标题的每行高度 + const titleHeight = measureText( + chart, + yAxis.name, + { fontSize: yAxis.fontSize, fontFamily: chart.fontFamily }, + 'height' + ) + + // 用于存储截取后的标题 + let wrappedTitle = '' + + // 循环截取标题内容,直到超过最大高度 + for ( + let charIndex = 0; + charIndex < yAxis.name.length && (charIndex + 1) * titleHeight <= maxHeightRatio; + charIndex++ + ) { + wrappedTitle += yAxis.name[charIndex] + } + + // 如果标题被截断,添加省略号 + if (yAxis.name.length > wrappedTitle.length) { + wrappedTitle = + wrappedTitle.length > 2 + ? wrappedTitle.slice(0, wrappedTitle.length - 2) + '...' + : wrappedTitle + '...' + } + + // 更新Y轴标题的原始文本和截断后的文本 + ev.view.options.axes.yAxisExt.title.originalText = yAxis.name + ev.view.options.axes.yAxisExt.title.text = wrappedTitle + }) +} + +/** + * 调整原始数据options.data + * 添加conditionColor字段,用于保存符合条件的颜色 + * conditionColor 为数组,多个指标多个颜色,按照指标的顺序 + * @param chart + * @param options + */ +export const addConditionsStyleColorToData = (chart: Chart, options) => { + const { threshold } = parseJson(chart.senior) + if (!threshold.enable) return options + options.data.forEach(item => { + item['conditionColor'] = [] + // 条形图的值字段是xField,柱形图的值字段是yField + const valueField = chart.type === 'bar-horizontal' ? options.xField : options.yField + // 对称条形图区分左右值,value、 valueExt,quotaList只有一个 + if (chart.type === 'bidirectional-bar') { + valueField.forEach(value => { + const quotaList = value === 'value' ? chart.yAxis : chart.yAxisExt + const conditionColor = getColorByConditions([quotaList[0]?.id], item[value], chart) + if (conditionColor) { + item[item[options.xField] + '-' + value] = conditionColor + } + }) + } else if (item.quotaList?.length) { + const quotaList = item.quotaList.map(q => q.id) ?? [] + quotaList.forEach((q, index) => { + // 定义后,在 handleConditionsStyle 函数中使用 + let currentValue = item[valueField] + if (chart.type === 'progress-bar') { + currentValue = item['originalValue'] + } + const cColor = getColorByConditions([q], currentValue, chart) + if (cColor) { + item.conditionColor.push(cColor) + } else { + item.conditionColor = undefined + } + }) + } + }) + return options +} + +/** + * 辅助函数:获取颜色, 根据条件以及值计算 + * @param quotaList 指标列表 + * @param values 值 + */ +const getColorByConditions = (quotaList: [], values: number | number[], chart) => { + const { threshold } = parseJson(chart.senior) + const { basicStyle } = parseJson(chart.customAttr) + const currentValue = Array.isArray(values) ? values[1] - values[0] : values + if (!currentValue) return undefined + // 同样的指标只取最后一个 + const conditionMap = new Map() + for (const condition of threshold.lineThreshold ?? []) { + conditionMap.set(condition.fieldId, condition) + } + for (const condition of conditionMap.values()) { + if (chart.type === 'progress-bar' && chart.yAxisExt?.[0]?.id !== quotaList[0]) continue + if (!quotaList.includes(condition.fieldId) && chart.type !== 'waterfall') continue + for (const tc of condition.conditions) { + if ( + (tc.term === 'between' && currentValue >= tc.min && currentValue <= tc.max) || + (tc.term === 'lt' && currentValue < tc.value) || + (tc.term === 'le' && currentValue <= tc.value) || + (tc.term === 'gt' && currentValue > tc.value) || + (tc.term === 'ge' && currentValue >= tc.value) + ) { + let tmpColor = hexToRgba(tc.color, basicStyle.alpha) + if (basicStyle.gradient) { + let vhAngle = ['bar-horizontal', 'progress-bar'].includes(chart.type) ? 0 : 270 + if (chart.type === 'bidirectional-bar') { + const yAxis = chart.yAxis.find(item => item.id === condition.fieldId) + vhAngle = getBidirectionalAngle(basicStyle, yAxis ? 0 : 1) + } + tmpColor = setGradientColor(tmpColor, true, vhAngle) + } + return tmpColor + } + } + } +} + +/** + * 处理柱条图的样式 + * 柱条的颜色 + * 提示marker的颜色 + * 注: 原始options中tooltip已经配置了customItems,这里将会忽略 + * @param chart + * @param options + */ +export function handleConditionsStyle(chart: Chart, options: O) { + const { threshold } = parseJson(chart.senior) + if (!threshold.enable) return options + const { basicStyle } = parseJson(chart.customAttr) + // 该字段出处 addConditionsStyleColorToData + const colorField = 'conditionColor' + // 配置条件样式的颜色字段 + const rawFields = options.rawFields || [] + rawFields.push(colorField) + // 辅助函数:配置柱条样式颜色,条形图为barStyle,柱形图为columnStyle + const columnStyle = data => { + return { + ...options.columnStyle, + ...options.barStyle, + ...(data[colorField]?.[0] ? { fill: data[colorField][0] } : {}) + } + } + let newColor = undefined + if (chart.type === 'bidirectional-bar') { + rawFields.push(options.xField) + newColor = getBidirectionalBarColor(chart, basicStyle, options) + } else if (chart.type === 'waterfall') { + newColor = getWaterfallColor(basicStyle, chart) + } + const tmpOption = { + ...options, + rawFields, + columnStyle: columnStyle, + barStyle: columnStyle, + tooltip: { + ...options.tooltip, + ...(options.tooltip['customItems'] + ? {} + : { + customItems: originalItems => { + originalItems.forEach(item => { + if (item.data?.[colorField]) { + item.color = item.data[colorField][0] + } + }) + return originalItems + } + }) + }, + ...(newColor ? { color: newColor } : {}) + } + return tmpOption +} + +/** + * 配置瀑布图的color + * 瀑布color,这个图表固定为基础样式中颜色的前三个颜色,第一个为增加,第二个为减少,第三个为总计 + * @param basicStyle + * @param chart + */ +const getWaterfallColor = (basicStyle, chart) => { + const waterfallBasicColors = getBasicColors(chart, basicStyle, 270) + return data => { + if (data['$$isTotal$$']) return waterfallBasicColors[2] + const values = data['$$yField$$'] + const newColor = getColorByConditions([], values, chart) + return newColor ?? (values[1] > values[0] ? waterfallBasicColors[0] : waterfallBasicColors[1]) + } +} + +/** + * 配置对称条形图的color + * @param basicStyle + * @param options + */ +const getBidirectionalBarColor = (chart, basicStyle, options) => { + const basicColors = getBasicColors(chart, basicStyle, 270) + return ref => { + const obj = options.data.find(item => item[ref[options.xField] + '-' + ref['series-field-key']]) + if (obj) { + return obj[ref[options.xField] + '-' + ref['series-field-key']] + } + return ref['series-field-key'] === 'value' ? basicColors[0] : basicColors[1] + } +} + +/** + * 获取基础颜色 + * @param chart + * @param basicStyle + * @param angle + */ +const getBasicColors = (chart, basicStyle, angle) => { + const baseColors = [] + basicStyle.colors?.forEach((color, index) => { + if (chart.type === 'bidirectional-bar') { + baseColors.push( + setGradientColor( + hexToRgba(color, basicStyle.alpha), + true, + getBidirectionalAngle(basicStyle, index) + ) + ) + } else { + baseColors.push(setGradientColor(hexToRgba(color, basicStyle.alpha), true, angle)) + } + }) + return basicStyle.gradient ? baseColors : basicStyle.colors +} + +/** + * 获取对称条形图颜色的渐变角度 + * @param basicStyle + * @param index + */ +const getBidirectionalAngle = (basicStyle, index) => { + let vhAngle = 180 - index * 180 + if (basicStyle.layout === 'vertical') { + vhAngle = index === 0 ? 280 : 90 + } + return vhAngle +} + +/** + * tooltip验证条件样式中的颜色,有就使用,否则使用原始颜色 + * @param item + */ +export const getTooltipItemConditionColor = item => { + let color = item.color + if (item.data?.['conditionColor']) { + color = item.data['conditionColor'][0] + } + return color +} + +/** + * 配置空数据样式 + * @param newChart + * @param newData + * @param container + */ +export const configEmptyDataStyle = (newChart, newData, container) => { + /** + * 辅助函数:移除空数据dom + */ + const removeEmptyDom = () => { + const emptyElement = document.getElementById(container + '_empty') + if (emptyElement) { + emptyElement.parentElement.removeChild(emptyElement) + } + } + removeEmptyDom() + if (newData.length > 0) return + if (!newData.length) { + const emptyDom = document.createElement('div') + emptyDom.id = container + '_empty' + emptyDom.textContent = tI18n('data_set.no_data') + emptyDom.setAttribute( + 'style', + `position: absolute; + left: 45%; + top: 50%;` + ) + const parent = document.getElementById(container) + parent.insertBefore(emptyDom, parent.firstChild) + newChart.destroy() + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/common/common_table.ts b/frontend/src/data-visualization/chart/components/js/panel/common/common_table.ts new file mode 100644 index 0000000..16fb754 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/common/common_table.ts @@ -0,0 +1,2021 @@ +/* eslint-disable prettier/prettier */ +import { + copyString, + hexColorToRGBA, + isAlphaColor, + isTransparent, + parseJson, + resetRgbOpacity +} from '../../util' +import { + DEFAULT_BASIC_STYLE, + DEFAULT_TABLE_CELL, + DEFAULT_TABLE_HEADER +} from '@/data-visualization/chart/components/editor/util/chart' +import { + BaseTooltip, + DataCellBrushSelection, + FONT_FAMILY, + getAutoAdjustPosition, + getEmptyPlaceholder, + getPolygonPoints, + getTooltipDefaultOptions, + InteractionName, + InteractionStateName, + MergedCell, + MergedCellInfo, + type Meta, + type Node, + type PivotSheet, + renderPolygon, + renderText, + S2DataConfig, + S2Event, + S2Options, + S2Theme, + SERIES_NUMBER_FIELD, + setTooltipContainerStyle, + SHAPE_STYLE_MAP, + SpreadSheet, + Style, + TableColCell, + TableDataCell, + updateShapeAttr, + ViewMeta +} from '@antv/s2' +import { cloneDeep, filter, find, intersection, keys, merge, repeat } from 'lodash-es' +import { createVNode, render } from 'vue' +import TableTooltip from '@/data-visualization/chart/components/editor/common/TableTooltip.vue' +import Exceljs from 'exceljs' +import { saveAs } from 'file-saver' +import { ElMessage } from 'element-plus-secondary' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +const { t: i18nt } = useI18n() + +export function getCustomTheme(chart: Chart): S2Theme { + const headerColor = hexColorToRGBA( + DEFAULT_TABLE_HEADER.tableHeaderBgColor, + DEFAULT_BASIC_STYLE.alpha + ) + const headerAlign = DEFAULT_TABLE_HEADER.tableHeaderAlign + const itemColor = hexColorToRGBA(DEFAULT_TABLE_CELL.tableItemBgColor, DEFAULT_BASIC_STYLE.alpha) + const itemAlign = DEFAULT_TABLE_CELL.tableItemAlign + const borderColor = hexColorToRGBA( + DEFAULT_BASIC_STYLE.tableBorderColor, + DEFAULT_BASIC_STYLE.alpha + ) + const scrollBarColor = DEFAULT_BASIC_STYLE.tableScrollBarColor + const scrollBarHoverColor = resetRgbOpacity(scrollBarColor, 3) + const textFontFamily = + chart.fontFamily && chart.fontFamily !== 'inherit' ? chart.fontFamily : FONT_FAMILY + const theme: S2Theme = { + background: { + color: '#00000000' + }, + splitLine: { + horizontalBorderColor: borderColor, + horizontalBorderColorOpacity: 1, + horizontalBorderWidth: 1, + verticalBorderColor: borderColor, + verticalBorderColorOpacity: 1, + verticalBorderWidth: 1, + showShadow: false + }, + cornerCell: { + cell: { + backgroundColor: headerColor, + horizontalBorderColor: borderColor, + verticalBorderColor: borderColor + }, + text: { + fill: DEFAULT_TABLE_HEADER.tableHeaderFontColor, + fontSize: DEFAULT_TABLE_HEADER.tableTitleFontSize, + textAlign: headerAlign, + fontFamily: textFontFamily + }, + bolderText: { + fill: DEFAULT_TABLE_HEADER.tableHeaderFontColor, + fontSize: DEFAULT_TABLE_HEADER.tableTitleFontSize, + textAlign: headerAlign, + fontFamily: textFontFamily + }, + measureText: { + fill: DEFAULT_TABLE_HEADER.tableHeaderFontColor, + fontSize: DEFAULT_TABLE_HEADER.tableTitleFontSize, + textAlign: headerAlign, + fontFamily: textFontFamily + } + }, + rowCell: { + cell: { + backgroundColor: headerColor, + horizontalBorderColor: borderColor, + verticalBorderColor: borderColor + }, + text: { + fill: DEFAULT_TABLE_HEADER.tableHeaderFontColor, + fontSize: DEFAULT_TABLE_HEADER.tableTitleFontSize, + textAlign: headerAlign, + textBaseline: 'middle', + fontFamily: textFontFamily + }, + bolderText: { + fill: DEFAULT_TABLE_HEADER.tableHeaderFontColor, + fontSize: DEFAULT_TABLE_HEADER.tableTitleFontSize, + textAlign: headerAlign, + fontFamily: textFontFamily + }, + measureText: { + fill: DEFAULT_TABLE_HEADER.tableHeaderFontColor, + fontSize: DEFAULT_TABLE_HEADER.tableTitleFontSize, + textAlign: headerAlign, + fontFamily: textFontFamily + }, + seriesText: { + fill: DEFAULT_TABLE_CELL.tableItemBgColor, + fontSize: DEFAULT_TABLE_CELL.tableItemFontSize, + textAlign: itemAlign, + fontFamily: textFontFamily + } + }, + colCell: { + cell: { + backgroundColor: headerColor, + horizontalBorderColor: borderColor, + verticalBorderColor: borderColor + }, + text: { + fill: DEFAULT_TABLE_HEADER.tableHeaderFontColor, + fontSize: DEFAULT_TABLE_HEADER.tableTitleFontSize, + textAlign: headerAlign, + fontFamily: textFontFamily + }, + bolderText: { + fill: DEFAULT_TABLE_HEADER.tableHeaderFontColor, + fontSize: DEFAULT_TABLE_HEADER.tableTitleFontSize, + textAlign: headerAlign, + fontFamily: textFontFamily + }, + measureText: { + fill: DEFAULT_TABLE_HEADER.tableHeaderFontColor, + fontSize: DEFAULT_TABLE_HEADER.tableTitleFontSize, + textAlign: headerAlign, + fontFamily: textFontFamily + } + }, + dataCell: { + cell: { + backgroundColor: itemColor, + horizontalBorderColor: borderColor, + verticalBorderColor: borderColor + }, + text: { + fill: DEFAULT_TABLE_CELL.tableFontColor, + fontSize: DEFAULT_TABLE_CELL.tableItemFontSize, + textAlign: itemAlign, + fontFamily: textFontFamily + }, + bolderText: { + fill: DEFAULT_TABLE_CELL.tableFontColor, + fontSize: DEFAULT_TABLE_CELL.tableItemFontSize, + textAlign: itemAlign, + fontFamily: textFontFamily + }, + measureText: { + fill: DEFAULT_TABLE_CELL.tableFontColor, + fontSize: DEFAULT_TABLE_CELL.tableItemFontSize, + textAlign: headerAlign, + fontFamily: textFontFamily + } + }, + scrollBar: { + thumbColor: scrollBarColor, + thumbHoverColor: scrollBarHoverColor, + size: 8, + hoverSize: 12 + } + } + + let customAttr: DeepPartial + if (chart.customAttr) { + customAttr = parseJson(chart.customAttr) + const { basicStyle, tableHeader, tableCell } = customAttr + // basic + if (basicStyle) { + const tableBorderColor = basicStyle.tableBorderColor + const tableScrollBarColor = basicStyle.tableScrollBarColor + const tmpTheme: S2Theme = { + splitLine: { + horizontalBorderColor: tableBorderColor, + verticalBorderColor: tableBorderColor + }, + cornerCell: { + cell: { + horizontalBorderColor: tableBorderColor, + verticalBorderColor: tableBorderColor + } + }, + colCell: { + cell: { + horizontalBorderColor: tableBorderColor, + verticalBorderColor: tableBorderColor + } + }, + dataCell: { + cell: { + horizontalBorderColor: tableBorderColor, + verticalBorderColor: tableBorderColor, + interactionState: { + hoverFocus: { + borderOpacity: basicStyle.showHoverStyle === false ? 0 : 1 + } + } + } + }, + scrollBar: { + thumbColor: tableScrollBarColor, + thumbHoverColor: resetRgbOpacity(tableScrollBarColor, 1.5) + } + } + merge(theme, tmpTheme) + } + // header + if (tableHeader) { + const tableHeaderFontColor = hexColorToRGBA( + tableHeader.tableHeaderFontColor, + basicStyle.alpha + ) + let tableHeaderBgColor = tableHeader.tableHeaderBgColor + if (!isAlphaColor(tableHeaderBgColor)) { + tableHeaderBgColor = hexColorToRGBA(tableHeaderBgColor, basicStyle.alpha) + } + const fontStyle = tableHeader.isItalic ? 'italic' : 'normal' + const fontWeight = tableHeader.isBolder === false ? 'normal' : 'bold' + const { tableHeaderAlign, tableTitleFontSize } = tableHeader + const tmpTheme: S2Theme = { + cornerCell: { + cell: { + backgroundColor: tableHeaderBgColor + }, + bolderText: { + fill: tableHeaderFontColor, + fontSize: tableTitleFontSize, + textAlign: tableHeaderAlign, + fontStyle, + fontWeight, + fontFamily: textFontFamily + }, + text: { + fill: tableHeaderFontColor, + fontSize: tableTitleFontSize, + textAlign: tableHeaderAlign, + fontStyle, + fontWeight, + fontFamily: textFontFamily + }, + measureText: { + fill: tableHeaderFontColor, + fontSize: tableTitleFontSize, + textAlign: tableHeaderAlign, + fontStyle, + fontWeight, + fontFamily: textFontFamily + } + }, + colCell: { + cell: { + backgroundColor: tableHeaderBgColor + }, + bolderText: { + fill: tableHeaderFontColor, + fontSize: tableTitleFontSize, + textAlign: tableHeaderAlign, + fontStyle, + fontWeight, + fontFamily: textFontFamily + }, + text: { + fill: tableHeaderFontColor, + fontSize: tableTitleFontSize, + textAlign: tableHeaderAlign, + fontStyle, + fontWeight, + fontFamily: textFontFamily + }, + measureText: { + fill: tableHeaderFontColor, + fontSize: tableTitleFontSize, + textAlign: tableHeaderAlign, + fontStyle, + fontWeight, + fontFamily: textFontFamily + } + } + } + merge(theme, tmpTheme) + // 这边设置为 0 的话就会显示表头背景颜色,所以要判断一下表头是否关闭 + if (tableHeader.showHorizonBorder === false && tableHeader.showTableHeader !== false) { + const tmpTheme: S2Theme = { + splitLine: { + horizontalBorderColor: tableHeaderBgColor, + horizontalBorderWidth: 0, + horizontalBorderColorOpacity: 0 + }, + colCell: { + cell: { + horizontalBorderColor: tableHeaderBgColor, + horizontalBorderWidth: 0 + } + } + } + merge(theme, tmpTheme) + } + if (tableHeader.showVerticalBorder === false && tableHeader.showTableHeader !== false) { + const tmpTheme: S2Theme = { + splitLine: { + verticalBorderColor: tableHeaderBgColor, + verticalBorderWidth: 0, + verticalBorderColorOpacity: 0 + }, + colCell: { + cell: { + verticalBorderColor: tableHeaderBgColor, + verticalBorderWidth: 0 + } + }, + cornerCell: { + cell: { + verticalBorderColor: tableHeaderBgColor, + verticalBorderWidth: 0 + } + } + } + merge(theme, tmpTheme) + } + } + // cell + if (tableCell) { + const tableFontColor = hexColorToRGBA(tableCell.tableFontColor, basicStyle.alpha) + let tableItemBgColor = tableCell.tableItemBgColor + if (!isAlphaColor(tableItemBgColor)) { + tableItemBgColor = hexColorToRGBA(tableItemBgColor, basicStyle.alpha) + } + let tableItemSubBgColor = tableCell.tableItemSubBgColor + if (!isAlphaColor(tableItemSubBgColor)) { + tableItemSubBgColor = hexColorToRGBA(tableItemSubBgColor, basicStyle.alpha) + } + const fontStyle = tableCell.isItalic ? 'italic' : 'normal' + const fontWeight = tableCell.isBolder === false ? 'normal' : 'bold' + const { tableItemAlign, tableItemFontSize, enableTableCrossBG } = tableCell + const tmpTheme: S2Theme = { + rowCell: { + cell: { + backgroundColor: tableItemBgColor, + horizontalBorderColor: tableItemBgColor, + verticalBorderColor: tableItemBgColor + }, + bolderText: { + fill: tableFontColor, + textAlign: tableItemAlign, + fontSize: tableItemFontSize, + fontFamily: textFontFamily + }, + text: { + fill: tableFontColor, + textAlign: tableItemAlign, + fontSize: tableItemFontSize, + fontFamily: textFontFamily + }, + measureText: { + fill: tableFontColor, + textAlign: tableItemAlign, + fontSize: tableItemFontSize, + fontFamily: textFontFamily + }, + seriesText: { + fill: tableFontColor, + textAlign: tableItemAlign, + fontSize: tableItemFontSize, + fontFamily: textFontFamily + } + }, + dataCell: { + cell: { + crossBackgroundColor: + enableTableCrossBG && !tableCell.mergeCells ? tableItemSubBgColor : tableItemBgColor, + backgroundColor: tableItemBgColor + }, + bolderText: { + fill: tableFontColor, + textAlign: tableItemAlign, + fontSize: tableItemFontSize, + fontStyle, + fontWeight, + fontFamily: textFontFamily + }, + text: { + fill: tableFontColor, + textAlign: tableItemAlign, + fontSize: tableItemFontSize, + fontStyle, + fontWeight, + fontFamily: textFontFamily + }, + measureText: { + fill: tableFontColor, + textAlign: tableItemAlign, + fontSize: tableItemFontSize, + fontStyle, + fontWeight, + fontFamily: textFontFamily + }, + seriesText: { + fill: tableFontColor, + textAlign: tableItemAlign, + fontSize: tableItemFontSize, + fontStyle, + fontWeight, + fontFamily: textFontFamily + } + } + } + merge(theme, tmpTheme) + if (tableCell.showHorizonBorder === false) { + const tmpTheme: S2Theme = { + dataCell: { + cell: { + horizontalBorderColor: tableItemBgColor, + horizontalBorderWidth: 0 + } + } + } + merge(theme, tmpTheme) + } + if (tableCell.showVerticalBorder === false) { + const tmpTheme: S2Theme = { + splitLine: { + verticalBorderWidth: 0, + verticalBorderColorOpacity: 0 + }, + dataCell: { + cell: { + verticalBorderColor: tableItemBgColor, + verticalBorderWidth: 0 + } + } + } + merge(theme, tmpTheme) + } + } + } + + return theme +} + +export function getStyle(chart: Chart, dataConfig: S2DataConfig): Style { + const style: Style = {} + let customAttr: DeepPartial + if (chart.customAttr) { + customAttr = parseJson(chart.customAttr) + const { basicStyle, tableHeader, tableCell } = customAttr + style.colCfg = { + height: tableHeader.tableTitleHeight + } + style.cellCfg = { + height: tableCell.tableItemHeight + } + switch (basicStyle.tableColumnMode) { + case 'adapt': { + style.layoutWidthType = 'compact' + break + } + case 'field': { + delete style.layoutWidthType + const fieldMap = + basicStyle.tableFieldWidth?.reduce((p, n) => { + p[n.fieldId] = n + return p + }, {}) || {} + // 下钻字段使用入口字段的宽度 + if (chart.drill) { + const { xAxis } = parseJson(chart) + const curDrillField = chart.drillFields[chart.drillFilters.length] + const drillEnterFieldIndex = xAxis.findIndex( + item => item.id === chart.drillFilters[0].fieldId + ) + const drillEnterField = xAxis[drillEnterFieldIndex] + fieldMap[curDrillField.dataeaseName] = { + width: fieldMap[drillEnterField.dataeaseName]?.width + } + } + // 铺满 + const totalWidthPercent = dataConfig.meta?.reduce((p, n) => { + return p + (fieldMap[n.field]?.width ?? 10) + }, 0) + const fullFilled = parseInt(totalWidthPercent.toFixed(0)) === 100 + const widthArr = [] + style.colCfg.width = node => { + const width = node.spreadsheet.container.cfg.el.getBoundingClientRect().width + if (!basicStyle.tableFieldWidth?.length) { + const fieldsSize = chart.data.fields.length + const columnCount = tableHeader.showIndex ? fieldsSize + 1 : fieldsSize + return width / columnCount + } + const baseWidth = width / 100 + const tmpWidth = fieldMap[node.field] + ? fieldMap[node.field].width * baseWidth + : baseWidth * 10 + const resultWidth = parseInt(tmpWidth.toFixed(0)) + if (fullFilled) { + if (widthArr.length === dataConfig.meta.length - 1) { + const curTotalWidth = widthArr.reduce((p, n) => { + return p + n + }, 0) + const restWidth = width - curTotalWidth + widthArr.splice(0) + if (restWidth < resultWidth) { + return restWidth + } + } else { + widthArr.push(resultWidth) + } + } + return resultWidth + } + break + } + case 'custom': { + style.colCfg.width = basicStyle.tableColumnWidth + break + } + // 查看详情用,均分铺满 + default: { + delete style.layoutWidthType + style.colCfg.width = node => { + const width = node.spreadsheet.container.cfg.el.offsetWidth + const fieldsSize = node.spreadsheet.dataCfg.meta.length + if (!fieldsSize) { + return 0 + } + const columnCount = tableHeader.showIndex ? fieldsSize + 1 : fieldsSize + const minWidth = width / columnCount + return Math.max(minWidth, basicStyle.tableColumnWidth) + } + } + } + } + + return style +} + +export function getCurrentField(valueFieldList: Axis[], field: ChartViewField) { + let list = [] + let res = null + try { + list = parseJson(valueFieldList) + } catch (err) { + list = JSON.parse(JSON.stringify(valueFieldList)) + } + if (list) { + for (let i = 0; i < list.length; i++) { + const f = list[i] + if (field.dataeaseName === f.dataeaseName) { + res = f + break + } + } + } + + return res +} + +export function getConditions(chart: Chart) { + const { threshold } = parseJson(chart.senior) + if (!threshold.enable) { + return + } + const res = { + text: [], + background: [] + } + const conditions = threshold.tableThreshold ?? [] + + const dimFields = [...chart.xAxis, ...chart.xAxisExt].map(i => i.dataeaseName) + if (conditions?.length > 0) { + const { tableCell, basicStyle, tableHeader } = parseJson(chart.customAttr) + // 合并单元格时,班马纹失效 + const enableTableCrossBG = + chart.type === 'table-info' + ? tableCell.enableTableCrossBG && !tableCell.mergeCells + : tableCell.enableTableCrossBG + const valueColor = isAlphaColor(tableCell.tableFontColor) + ? tableCell.tableFontColor + : hexColorToRGBA(tableCell.tableFontColor, basicStyle.alpha) + const valueBgColor = enableTableCrossBG + ? null + : isAlphaColor(tableCell.tableItemBgColor) + ? tableCell.tableItemBgColor + : hexColorToRGBA(tableCell.tableItemBgColor, basicStyle.alpha) + const headerValueColor = tableHeader.tableHeaderFontColor + const headerValueBgColor = isAlphaColor(tableHeader.tableHeaderBgColor) + ? tableHeader.tableHeaderBgColor + : hexColorToRGBA(tableHeader.tableHeaderBgColor, basicStyle.alpha) + const filedValueMap = getFieldValueMap(chart) + for (let i = 0; i < conditions.length; i++) { + const field = conditions[i] + let defaultValueColor = valueColor + let defaultBgColor = valueBgColor + // 透视表表头颜色配置 + if (chart.type === 'table-pivot' && dimFields.includes(field.field.dataeaseName)) { + defaultValueColor = headerValueColor + defaultBgColor = headerValueBgColor + } + res.text.push({ + field: field.field.dataeaseName, + mapping(value, rowData) { + // 总计小计 + if (rowData?.isTotals) { + return null + } + // 表头 + if (rowData?.id && rowData?.field === rowData.id) { + return null + } + return { + fill: mappingColor(value, defaultValueColor, field, 'color', filedValueMap, rowData) + } + } + }) + res.background.push({ + field: field.field.dataeaseName, + mapping(value, rowData) { + if (rowData?.isTotals) { + return null + } + if (rowData?.id && rowData?.field === rowData.id) { + return null + } + const fill = mappingColor( + value, + defaultBgColor, + field, + 'backgroundColor', + filedValueMap, + rowData + ) + if (isTransparent(fill)) { + return null + } + return { fill } + } + }) + } + } + return res +} + +export function mappingColor(value, defaultColor, field, type, filedValueMap?, rowData?) { + let color = null + for (let i = 0; i < field.conditions.length; i++) { + let flag = false + const t = field.conditions[i] + let tv, max, min + if (t.type === 'dynamic') { + if (t.term === 'between') { + max = parseFloat(getValue(t.dynamicMaxField, filedValueMap, rowData)) + min = parseFloat(getValue(t.dynamicMinField, filedValueMap, rowData)) + } else { + tv = getValue(t.dynamicField, filedValueMap, rowData) + } + } else { + if (t.term === 'between') { + min = parseFloat(t.min) + max = parseFloat(t.max) + } else { + tv = t.value + } + } + if (field.field.deType === 2 || field.field.deType === 3 || field.field.deType === 4) { + tv = parseFloat(tv) + if (t.term === 'eq') { + if (value === tv) { + color = t[type] + flag = true + } + } else if (t.term === 'not_eq') { + if (value !== tv) { + color = t[type] + flag = true + } + } else if (t.term === 'lt') { + if (value < tv) { + color = t[type] + flag = true + } + } else if (t.term === 'gt') { + if (value > tv) { + color = t[type] + flag = true + } + } else if (t.term === 'le') { + if (value <= tv) { + color = t[type] + flag = true + } + } else if (t.term === 'ge') { + if (value >= tv) { + color = t[type] + flag = true + } + } else if (t.term === 'between') { + if (min <= value && value <= max) { + color = t[type] + flag = true + } + } else if (t.term === 'default') { + color = t[type] + flag = true + } + if (flag) { + break + } else if (i === field.conditions.length - 1) { + color = defaultColor + } + } else if (field.field.deType === 0 || field.field.deType === 5) { + if (t.term === 'eq') { + if (value === tv) { + color = t[type] + flag = true + } + } else if (t.term === 'not_eq') { + if (value !== tv) { + color = t[type] + flag = true + } + } else if (t.term === 'like') { + if (value.includes(tv)) { + color = t[type] + flag = true + } + } else if (t.term === 'not like') { + if (!value.includes(tv)) { + color = t[type] + flag = true + } + } else if (t.term === 'null') { + if (value === null || value === undefined || value === '') { + color = t[type] + flag = true + } + } else if (t.term === 'not_null') { + if (value !== null && value !== undefined && value !== '') { + color = t[type] + flag = true + } + } else if (t.term === 'default') { + color = t[type] + flag = true + } + if (flag) { + break + } else if (i === field.conditions.length - 1) { + color = defaultColor + } + } else { + // time + const fc = field.conditions[i] + tv = new Date(tv.replace(/-/g, '/') + ' GMT+8').getTime() + const v = new Date(value.replace(/-/g, '/') + ' GMT+8').getTime() + if (fc.term === 'eq') { + if (v === tv) { + color = fc[type] + flag = true + } + } else if (fc.term === 'not_eq') { + if (v !== tv) { + color = fc[type] + flag = true + } + } else if (fc.term === 'lt') { + if (v < tv) { + color = fc[type] + flag = true + } + } else if (fc.term === 'gt') { + if (v > tv) { + color = fc[type] + flag = true + } + } else if (fc.term === 'le') { + if (v <= tv) { + color = fc[type] + flag = true + } + } else if (fc.term === 'ge') { + if (v >= tv) { + color = fc[type] + flag = true + } + } else if (fc.term === 'default') { + color = fc[type] + flag = true + } + if (flag) { + break + } else if (i === field.conditions.length - 1) { + color = defaultColor + } + } + } + return color +} + +function getFieldValueMap(view) { + const fieldValueMap = {} + if (view.data && view.data.dynamicAssistLines && view.data.dynamicAssistLines.length > 0) { + view.data.dynamicAssistLines.forEach(ele => { + fieldValueMap[ele.summary + '-' + ele.fieldId] = ele.value + }) + } + return fieldValueMap +} + +function getValue(field, filedValueMap, rowData) { + if (field.summary === 'value') { + return rowData ? rowData[field.field?.dataeaseName] : undefined + } else { + return filedValueMap[field.summary + '-' + field.fieldId] + } +} + +export function handleTableEmptyStrategy(chart: Chart) { + let newData = (chart.data?.tableRow || []) as Record[] + let intersectionArr = [] + const senior = parseJson(chart.senior) + let emptyDataStrategy = senior?.functionCfg?.emptyDataStrategy + if (!emptyDataStrategy) { + emptyDataStrategy = 'breakLine' + } + const emptyDataFieldCtrl = senior?.functionCfg?.emptyDataFieldCtrl + if (emptyDataStrategy !== 'breakLine' && emptyDataFieldCtrl?.length && newData?.length) { + const deNames = keys(newData[0]) + intersectionArr = intersection(deNames, emptyDataFieldCtrl) + } + if (intersectionArr.length) { + newData = cloneDeep(newData) + for (let i = newData.length - 1; i >= 0; i--) { + for (let j = 0, tmp = intersectionArr.length; j < tmp; j++) { + const deName = intersectionArr[j] + if (newData[i][deName] === null) { + if (emptyDataStrategy === 'setZero') { + newData[i][deName] = 0 + } + if (emptyDataStrategy === 'ignoreData') { + newData = filter(newData, (_, index) => index !== i) + break + } + } + } + } + } + return newData +} +export class SortTooltip extends BaseTooltip { + show(showOptions) { + const { iconName } = showOptions + if (iconName) { + this.showSortTooltip(showOptions) + return + } + super.show(showOptions) + } + + showSortTooltip(showOptions) { + const { position, options, meta, event } = showOptions + const { enterable } = getTooltipDefaultOptions(options) + const { autoAdjustBoundary, adjustPosition } = this.spreadsheet.options.tooltip || {} + this.visible = true + this.options = showOptions + const container = this['getContainer']() + // 用 vue 手动 patch + const vNode = createVNode(TableTooltip, { + table: this.spreadsheet, + meta + }) + this.spreadsheet.tooltip.container.innerHTML = '' + const childElement = document.createElement('div') + this.spreadsheet.tooltip.container.appendChild(childElement) + render(vNode, childElement) + + const { x, y } = getAutoAdjustPosition({ + spreadsheet: this.spreadsheet, + position, + tooltipContainer: container, + autoAdjustBoundary + }) + + this.position = adjustPosition?.({ position: { x, y }, event }) ?? { + x, + y + } + + setTooltipContainerStyle(container, { + style: { + left: `${this.position?.x}px`, + top: `${this.position?.y}px`, + pointerEvents: enterable ? 'all' : 'none', + zIndex: 9999, + position: 'absolute', + color: 'black', + background: 'white', + fontSize: '16px' + }, + visible: true + }) + } +} +const SORT_DEFAULT = + '' +const SORT_UP = + '' +const SORT_DOWN = + '' + +function svg2Base64(svg) { + return `data:image/svg+xml;charset=utf-8;base64,${btoa(svg)}` +} + +export function configHeaderInteraction(chart: Chart, option: S2Options) { + const { tableHeaderFontColor, tableHeaderSort } = parseJson(chart.customAttr).tableHeader + if (!tableHeaderSort) { + return + } + const iconColor = tableHeaderFontColor ?? '#666' + const sortDefault = svg2Base64(SORT_DEFAULT.replace('{fill}', iconColor)) + const sortUp = svg2Base64(SORT_UP.replace('{fill}', iconColor)) + const sortDown = svg2Base64(SORT_DOWN.replace('{fill}', iconColor)) + // 防止缓存 + const randomSuffix = Math.random() + const sortIconMap = { + asc: `customSortUp${randomSuffix}`, + desc: `customSortDown${randomSuffix}` + } + option.customSVGIcons = [ + { + name: `customSortDefault${randomSuffix}`, + svg: sortDefault + }, + { + name: `customSortUp${randomSuffix}`, + svg: sortUp + }, + { + name: `customSortDown${randomSuffix}`, + svg: sortDown + } + ] + option.headerActionIcons = [ + { + iconNames: [ + `customSortDefault${randomSuffix}`, + `customSortUp${randomSuffix}`, + `customSortDown${randomSuffix}` + ], + belongsCell: 'colCell', + displayCondition: (meta, iconName) => { + if (meta.field === SERIES_NUMBER_FIELD) { + return false + } + // 分组 + if (meta.colIndex === -1) { + return false + } + const sortMethodMap = meta.spreadsheet.store.get('sortMethodMap') + const sortType = sortMethodMap?.[meta.field] + if (sortType) { + return iconName === sortIconMap[sortType] + } + return iconName === `customSortDefault${randomSuffix}` + }, + onClick: props => { + const { meta, event } = props + meta.spreadsheet.showTooltip({ + position: { + x: event.clientX, + y: event.clientY + }, + event, + ...props + }) + const parent = document.getElementById(chart.container) + if (parent?.childNodes?.length) { + const child = Array.from(parent.childNodes) + .filter(node => node.nodeType === Node.ELEMENT_NODE) + .find(node => node.classList.contains('antv-s2-tooltip-container')) + if (child) { + const left = child.offsetLeft + child.clientWidth + if (left > parent.offsetWidth) { + const newLeft = parent.offsetWidth - child.clientWidth - 10 + child.style.left = `${newLeft}px` + } + } + } + } + } + ] +} + +export function configTooltip(chart: Chart, option: S2Options) { + const { tooltip } = parseJson(chart.customAttr) + const textFontFamily = chart.fontFamily ? chart.fontFamily : FONT_FAMILY + option.tooltip = { + ...option.tooltip, + style: { + background: tooltip.backgroundColor, + fontSize: tooltip.fontSize + 'px', + fontFamily: textFontFamily, + color: tooltip.color, + boxShadow: 'rgba(0, 0, 0, 0.1) 0px 4px 8px 0px', + borderRadius: '3px', + padding: '4px 12px', + opacity: 0.95, + position: 'absolute' + }, + adjustPosition: ({ event }) => { + return getTooltipPosition(event) + } + } +} + +export function copyContent(s2Instance: SpreadSheet, event, fieldMeta) { + event.preventDefault() + const cell = s2Instance.getCell(event.target) + const valueField = cell.getMeta().valueField + const cellMeta = cell.getMeta() + const selectState = s2Instance.interaction.getState() + let content = '' + // 多选 + if (selectState.stateName === InteractionStateName.SELECTED) { + const { cells } = selectState + if (!cells?.length) { + return + } + if (cells.length === 1) { + const curCell = cells[0] + if (cell.getMeta().id === curCell.id) { + copyString(cellMeta.value + '', true) + } + s2Instance.interaction.clearState() + return + } + const brushSelection = s2Instance.interaction.interactions.get( + InteractionName.BRUSH_SELECTION + ) as DataCellBrushSelection + const selectedCells: TableDataCell[] = brushSelection.getScrollBrushRangeCells(cells) + selectedCells.sort((a, b) => { + const aMeta = a.getMeta() + const bMeta = b.getMeta() + if (aMeta.rowIndex !== bMeta.rowIndex) { + return aMeta.rowIndex - bMeta.rowIndex + } + return aMeta.colIndex - bMeta.colIndex + }) + // 点击已选的就复制,未选的就忽略 + let validClick = false + const matrix = selectedCells.reduce((p, n) => { + if ( + n.getMeta().colIndex === cellMeta.colIndex && + n.getMeta().rowIndex === cellMeta.rowIndex + ) { + validClick = true + } + const arr = p[n.getMeta().rowIndex] + if (!arr) { + p[n.getMeta().rowIndex] = [n] + } else { + arr.push(n) + } + return p + }, {}) as Record + if (validClick) { + keys(matrix).forEach(k => { + const arr = matrix[k] as TableDataCell[] + arr.forEach((cell, index) => { + const cellMeta = cell.getMeta() + const value = cellMeta.data?.[cellMeta.valueField] + const metaObj = find(fieldMeta, m => m.field === cellMeta.valueField) + let fieldVal = value?.toString() + if (metaObj) { + fieldVal = metaObj.formatter(value) + } + if (fieldVal === undefined || fieldVal === null) { + fieldVal = '' + } + if (index !== arr.length - 1) { + fieldVal += '\t' + } + content += fieldVal + }) + content = content + '\n' + }) + if (content) { + copyString(content, true) + } + } + s2Instance.interaction.clearState() + return + } + // 单元格 + if (cellMeta?.data) { + const value = cellMeta.data[valueField] + const metaObj = find(fieldMeta, m => m.field === valueField) + content = value?.toString() + if (metaObj) { + content = metaObj.formatter(value) + } + } else { + // 列头&行头 + const fieldMap = fieldMeta?.reduce((p, n) => { + p[n.field] = n.name + return p + }, {}) + content = cellMeta.value + if (fieldMap?.[content]) { + content = fieldMap[content] + } + } + if (content) { + copyString(content, true) + } +} + +function getTooltipPosition(event) { + const s2Instance = event.s2Instance + const { x, y } = event + const result = { x: x + 15, y } + if (!s2Instance) { + return result + } + const { height, width } = s2Instance.getCanvasElement().getBoundingClientRect() + const { offsetHeight, offsetWidth } = s2Instance.tooltip.getContainer() + if (offsetWidth > width) { + result.x = 0 + } + if (offsetHeight > height) { + result.y = 0 + } + if (!(result.x || result.y)) { + return result + } + if (result.x && result.x + offsetWidth > width) { + result.x -= result.x + offsetWidth - width + } + if (result.y) { + if (result.y > offsetHeight) { + if (result.y - offsetHeight >= 15) { + result.y -= offsetHeight + 15 + } else { + result.y = 0 + } + } else { + result.y += 15 + } + } + return result +} + +export async function exportGridPivot(instance: PivotSheet, chart: ChartObj) { + const { layoutResult } = instance.facet + const { meta, fields } = instance.dataCfg + const rowLength = fields?.rows?.length || 0 + const colLength = fields?.columns?.length || 0 + const colNums = layoutResult.colLeafNodes.length + rowLength + 1 + if (colNums > 16384) { + ElMessage.warning(i18nt('chart.pivot_export_invalid_col_exceed')) + return + } + const workbook = new Exceljs.Workbook() + const worksheet = workbook.addWorksheet(i18nt('chart.chart_data')) + const metaMap: Record = meta?.reduce((p, n) => { + if (n.field) { + p[n.field] = n + } + return p + }, {}) + // 角头 + fields.columns?.forEach((column, index) => { + const cell = worksheet.getCell(index + 1, 1) + cell.value = metaMap[column]?.name ?? column + cell.alignment = { vertical: 'middle', horizontal: 'center' } + if (rowLength >= 2) { + worksheet.mergeCells(index + 1, 1, index + 1, rowLength) + } + cell.border = { + right: { style: 'thick', color: { argb: '00000000' } } + } + }) + fields?.rows?.forEach((row, index) => { + const cell = worksheet.getCell(colLength + 1, index + 1) + cell.value = metaMap[row]?.name ?? row + cell.alignment = { vertical: 'middle', horizontal: 'center' } + cell.border = { + bottom: { style: 'thick', color: { argb: '00000000' } } + } + if (index === fields.rows.length - 1) { + cell.border.right = { style: 'thick', color: { argb: '00000000' } } + } + }) + // 行头 + const { rowLeafNodes, rowsHierarchy, rowNodes } = layoutResult + const maxColIndex = rowsHierarchy.maxLevel + 1 + const notLeafNodeHeightMap: Record = {} + rowLeafNodes.forEach(node => { + // 行头的高度由子节点相加决定,也就是行头子节点中包含的叶子节点数量 + let curNode = node.parent + while (curNode) { + const height = notLeafNodeHeightMap[curNode.id] ?? 0 + notLeafNodeHeightMap[curNode.id] = height + 1 + curNode = curNode.parent + } + const { rowIndex } = node + const writeRowIndex = rowIndex + 1 + colLength + 1 + const writeColIndex = node.level + 1 + const cell = worksheet.getCell(writeRowIndex, writeColIndex) + cell.value = node.label + cell.alignment = { vertical: 'middle', horizontal: 'center' } + if (writeColIndex < maxColIndex) { + worksheet.mergeCells(writeRowIndex, writeColIndex, writeRowIndex, maxColIndex) + } + cell.border = { + right: { style: 'thick', color: { argb: '00000000' } } + } + }) + + const getNodeStartRowIndex = (node: Node) => { + if (!node.children?.length) { + return node.rowIndex + 1 + } else { + return getNodeStartRowIndex(node.children[0]) + } + } + rowNodes?.forEach(node => { + if (node.isLeaf) { + return + } + const rowIndex = getNodeStartRowIndex(node) + const height = notLeafNodeHeightMap[node.id] + const writeRowIndex = rowIndex + colLength + 1 + const mergeColCount = node.children[0].level - node.level + const value = node.label + const cell = worksheet.getCell(writeRowIndex, node.level + 1) + cell.value = value + cell.alignment = { vertical: 'middle', horizontal: 'center' } + if (mergeColCount > 1 || height > 1) { + worksheet.mergeCells( + writeRowIndex, + node.level + 1, + writeRowIndex + height - 1, + node.level + mergeColCount + ) + } + }) + + // 列头 + const { colLeafNodes, colNodes, colsHierarchy } = layoutResult + const maxColHeight = colsHierarchy.maxLevel + 1 + const notLeafNodeWidthMap: Record = {} + colLeafNodes.forEach(node => { + // 列头的宽度由子节点相加决定,也就是列头子节点中包含的叶子节点数量 + let curNode = node.parent + while (curNode) { + const width = notLeafNodeWidthMap[curNode.id] ?? 0 + notLeafNodeWidthMap[curNode.id] = width + 1 + curNode = curNode.parent + } + const { colIndex } = node + const writeRowIndex = node.level + 1 + const writeColIndex = colIndex + 1 + rowLength + const cell = worksheet.getCell(writeRowIndex, writeColIndex) + let value = node.label + if (node.field === '$$extra$$' && metaMap[value]?.name) { + value = metaMap[value].name + } + cell.value = value + cell.alignment = { vertical: 'middle', horizontal: 'center' } + if (writeRowIndex < maxColHeight) { + worksheet.mergeCells(writeRowIndex, writeColIndex, maxColHeight, writeColIndex) + } + cell.border = { + bottom: { style: 'thick', color: { argb: '00000000' } } + } + }) + const getNodeStartColIndex = (node: Node) => { + if (!node.children?.length) { + return node.colIndex + 1 + } else { + return getNodeStartColIndex(node.children[0]) + } + } + colNodes.forEach(node => { + if (node.isLeaf) { + return + } + const colIndex = getNodeStartColIndex(node) + const width = notLeafNodeWidthMap[node.id] + const writeRowIndex = node.level + 1 + const mergeRowCount = node.children[0].level - node.level + const value = node.label + const writeColIndex = colIndex + rowLength + const cell = worksheet.getCell(writeRowIndex, writeColIndex) + cell.value = value + cell.alignment = { vertical: 'middle', horizontal: 'center' } + if (mergeRowCount > 1 || width > 1) { + worksheet.mergeCells( + writeRowIndex, + writeColIndex, + writeRowIndex + mergeRowCount - 1, + writeColIndex + width - 1 + ) + } + }) + // 单元格数据 + for (let rowIndex = 0; rowIndex < rowLeafNodes.length; rowIndex++) { + for (let colIndex = 0; colIndex < colLeafNodes.length; colIndex++) { + const dataCellMeta = layoutResult.getCellMeta(rowIndex, colIndex) + const { fieldValue } = dataCellMeta + if (fieldValue === 0 || fieldValue) { + const meta = metaMap[dataCellMeta.valueField] + const cell = worksheet.getCell(rowIndex + maxColHeight + 1, rowLength + colIndex + 1) + const value = meta?.formatter?.(fieldValue) || fieldValue.toString() + cell.alignment = { vertical: 'middle', horizontal: 'center' } + cell.value = value + } + } + } + const buffer = await workbook.xlsx.writeBuffer() + const dataBlob = new Blob([buffer], { + type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8' + }) + saveAs(dataBlob, `${chart.title ?? '透视表'}.xlsx`) +} + +export async function exportTreePivot(instance: PivotSheet, chart: ChartObj) { + const layoutResult = instance.facet.layoutResult + if (layoutResult.colLeafNodes.length + 2 > 16384) { + ElMessage.warning(i18nt('chart.pivot_export_invalid_col_exceed')) + return + } + const { meta, fields } = instance.dataCfg + const colLength = fields?.columns?.length || 0 + const workbook = new Exceljs.Workbook() + const worksheet = workbook.addWorksheet(i18nt('chart.chart_data')) + const metaMap: Record = meta?.reduce((p, n) => { + if (n.field) { + p[n.field] = n + } + return p + }, {}) + + // 角头 + fields.columns?.forEach((column, index) => { + const cell = worksheet.getCell(index + 1, 1) + cell.value = metaMap[column]?.name ?? column + cell.alignment = { vertical: 'middle', horizontal: 'center' } + cell.border = { + right: { style: 'thick', color: { argb: '00000000' } } + } + }) + const maxColHeight = layoutResult.colsHierarchy.maxLevel + 1 + const rowName = fields?.rows?.map(row => metaMap[row]?.name ?? row).join('/') + const cell = worksheet.getCell(colLength + 1, 1) + cell.value = rowName + cell.alignment = { vertical: 'middle', horizontal: 'center' } + cell.border = { + right: { style: 'thick', color: { argb: '00000000' } }, + bottom: { style: 'thick', color: { argb: '00000000' } } + } + //行头 + const { rowLeafNodes } = layoutResult + rowLeafNodes.forEach((node, index) => { + const cell = worksheet.getCell(maxColHeight + index + 1, 1) + cell.value = repeat(' ', node.level) + node.label + cell.alignment = { vertical: 'middle', horizontal: 'left' } + cell.border = { + right: { style: 'thick', color: { argb: '00000000' } } + } + }) + // 列头 + const notLeafNodeWidthMap: Record = {} + const { colLeafNodes } = layoutResult + colLeafNodes.forEach(node => { + let curNode = node.parent + while (curNode) { + const width = notLeafNodeWidthMap[curNode.id] ?? 0 + notLeafNodeWidthMap[curNode.id] = width + 1 + curNode = curNode.parent + } + const { colIndex } = node + const writeRowIndex = node.level + 1 + const writeColIndex = colIndex + 1 + 1 + const cell = worksheet.getCell(writeRowIndex, writeColIndex) + let value = node.label + if (node.field === '$$extra$$' && metaMap[value]?.name) { + value = metaMap[value].name + } + cell.value = value + cell.alignment = { vertical: 'middle', horizontal: 'center' } + if (writeRowIndex < maxColHeight) { + worksheet.mergeCells(writeRowIndex, writeColIndex, maxColHeight, writeColIndex) + } + cell.border = { + bottom: { style: 'thick', color: { argb: '00000000' } } + } + }) + const colNodes = layoutResult.colNodes + const getNodeStartIndex = (node: Node) => { + if (!node.children?.length) { + return node.colIndex + 1 + } else { + return getNodeStartIndex(node.children[0]) + } + } + colNodes.forEach(node => { + if (node.isLeaf) { + return + } + const colIndex = getNodeStartIndex(node) + const width = notLeafNodeWidthMap[node.id] + const writeRowIndex = node.level + 1 + const mergeRowCount = node.children[0].level - node.level + const writeColIndex = colIndex + 1 + const cell = worksheet.getCell(writeRowIndex, writeColIndex) + cell.value = node.label + cell.alignment = { vertical: 'middle', horizontal: 'center' } + if (mergeRowCount > 1 || width > 1) { + worksheet.mergeCells( + writeRowIndex, + writeColIndex, + writeRowIndex + mergeRowCount - 1, + writeColIndex + width - 1 + ) + } + }) + // 单元格数据 + for (let rowIndex = 0; rowIndex < rowLeafNodes.length; rowIndex++) { + for (let colIndex = 0; colIndex < colLeafNodes.length; colIndex++) { + const dataCellMeta = layoutResult.getCellMeta(rowIndex, colIndex) + const { fieldValue } = dataCellMeta + if (fieldValue === 0 || fieldValue) { + const meta = metaMap[dataCellMeta.valueField] + const cell = worksheet.getCell(rowIndex + maxColHeight + 1, colIndex + 1 + 1) + const value = meta?.formatter?.(fieldValue) || fieldValue.toString() + cell.alignment = { vertical: 'middle', horizontal: 'center' } + cell.value = value + } + } + } + const buffer = await workbook.xlsx.writeBuffer() + const dataBlob = new Blob([buffer], { + type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8' + }) + saveAs(dataBlob, `${chart.title ?? '透视表'}.xlsx`) +} +export async function exportPivotExcel(instance: PivotSheet, chart: ChartObj) { + const { fields } = instance.dataCfg + const rowLength = fields?.rows?.length || 0 + const valueLength = fields?.values?.length || 0 + if (!(rowLength && valueLength)) { + ElMessage.warning(i18nt('chart.pivot_export_invalid_field')) + return + } + if (chart.customAttr.basicStyle.tableLayoutMode !== 'tree') { + exportGridPivot(instance, chart) + } else { + exportTreePivot(instance, chart) + } +} + +export function configMergeCells(chart: Chart, options: S2Options, dataConfig: S2DataConfig) { + const { mergeCells } = parseJson(chart.customAttr).tableCell + const { showIndex } = parseJson(chart.customAttr).tableHeader + if (mergeCells) { + options.frozenColCount = 0 + options.frozenRowCount = 0 + const fields = chart.data.fields || [] + const fieldsMap = + fields.reduce((p, n) => { + p[n.dataeaseName] = n + return p + }, {}) || {} + const quotaIndex = dataConfig.meta.findIndex(m => fieldsMap[m.field]?.groupType === 'q') + const data = chart.data?.tableRow + if (quotaIndex === 0 || !data?.length) { + return + } + const mergedColInfo: number[][][] = [[[0, data.length - 1]]] + const mergedCellsInfo = [] + const axisToMerge = dataConfig.meta.filter((_, i) => i < quotaIndex || quotaIndex === -1) + axisToMerge.forEach((a, i) => { + const preMergedColInfo = mergedColInfo[i] + const curMergedColInfo = [] + mergedColInfo.push(curMergedColInfo) + preMergedColInfo.forEach(range => { + const [start, end] = range + let lastVal = data[start][a.field] + let lastIndex = start + for (let index = start; index <= end; index++) { + const curVal = data[index][a.field] + if (curVal !== lastVal || index === end) { + const curRange = index - lastIndex + if (curRange > 1 || (index === end && curRange === 1 && lastVal === curVal)) { + const tmpMergeCells = [] + const textIndex = curRange % 2 === 1 ? (curRange - 1) / 2 : curRange / 2 - 1 + for (let j = 0; j < curRange; j++) { + tmpMergeCells.push({ + colIndex: showIndex ? i + 1 : i, + rowIndex: lastIndex + j, + showText: j === textIndex + }) + } + if (index === end && lastVal === curVal) { + tmpMergeCells.push({ + colIndex: showIndex ? i + 1 : i, + rowIndex: index, + showText: false + }) + } + mergedCellsInfo.push(tmpMergeCells) + curMergedColInfo.push([ + lastIndex, + index === end && lastVal === curVal ? index : index - 1 + ]) + } + lastVal = curVal + lastIndex = index + } + } + }) + }) + if (showIndex) { + const indexMergedCells = mergedCellsInfo.filter(cells => cells[0].colIndex === 1) + indexMergedCells.forEach(cells => { + const tmpCells = cloneDeep(cells) + tmpCells.forEach(cell => { + cell.colIndex = 0 + }) + mergedCellsInfo.unshift(tmpCells) + }) + } + options.mergedCellsInfo = mergedCellsInfo + options.mergedCell = (sheet, cells, meta) => { + if (showIndex && meta.colIndex === 0) { + meta.fieldValue = getRowIndex(mergedCellsInfo, meta) + } + return new CustomMergedCell(sheet, cells, meta) + } + } +} + +export function getRowIndex(mergedCellsInfo: MergedCellInfo[][], meta: ViewMeta): number { + if (!mergedCellsInfo?.length) { + return meta.rowIndex + 1 + } + let curRangeStartIndex = meta.rowIndex + const lostCells = mergedCellsInfo.reduce((p, n) => { + if (n[0].colIndex !== 0) { + return p + } + const start = n[0].rowIndex + const end = n[n.length - 1].rowIndex + const lost = end - start + if (meta.rowIndex >= start && meta.rowIndex <= end) { + curRangeStartIndex = start + } + if (meta.rowIndex > end) { + return p + lost + } + return p + }, 0) + return curRangeStartIndex - lostCells + 1 +} +class CustomMergedCell extends MergedCell { + protected drawBackgroundShape() { + const allPoints = getPolygonPoints(this.cells) + // 处理条件样式,这里没有用透明度 + // 因为合并的单元格是单独的图层,透明度降低的话会显示底下未合并的单元格,需要单独处理被覆盖的单元格 + const { backgroundColor: fill, backgroundColorOpacity: fillOpacity } = this.getBackgroundColor() + const cellTheme = this.theme.dataCell.cell + this.backgroundShape = renderPolygon(this, { + points: allPoints, + stroke: cellTheme.horizontalBorderColor, + fill, + lineHeight: cellTheme.horizontalBorderWidth + }) + } +} + +export class CustomDataCell extends TableDataCell { + /** + * 重写这个方法是为了处理底部的汇总行取消 hover 状态时设置 border 为 1, + * 这样会导致单元格隐藏横边边框失败,出现一条白线 + */ + hideInteractionShape() { + this.stateShapes.forEach(shape => { + updateShapeAttr(shape, SHAPE_STYLE_MAP.backgroundOpacity, 0) + updateShapeAttr(shape, SHAPE_STYLE_MAP.backgroundColor, 'transparent') + updateShapeAttr(shape, SHAPE_STYLE_MAP.borderOpacity, 0) + updateShapeAttr(shape, SHAPE_STYLE_MAP.borderWidth, 0) + updateShapeAttr(shape, SHAPE_STYLE_MAP.borderColor, 'transparent') + }) + } + + /** + * 重写绘制文本内容的方法 + * @protected + */ + protected drawTextShape() { + if (this.meta.autoWrap) { + drawTextShape(this, false) + } else { + super.drawTextShape() + } + } +} + +export class CustomTableColCell extends TableColCell { + /** + * 重写是为了表头文本内容的换行 + * @protected + */ + protected drawTextShape() { + if (this.meta.autoWrap) { + drawTextShape(this, true) + } else { + super.drawTextShape() + } + } +} + +/** + * 绘制文本 换行 + * @param cell + * @param isHeader + */ +const drawTextShape = (cell, isHeader) => { + // 换行符 + const lineBreak = '\n' + // 省略号 + const ellipsis = '...' + // 用户配置的最大行数 + const maxLines = cell.meta.maxLines ?? 1 + const { + options: { placeholder } + } = cell.spreadsheet + const emptyPlaceholder = getEmptyPlaceholder(this, placeholder) + // 单元格文本 + const { formattedValue } = cell.getFormattedFieldValue() + // 获取文本样式 + const textStyle = cell.getTextStyle() + // 宽度能放几个字符,就放几个,放不下就换行 + let wrapText = getWrapText( + formattedValue ? formattedValue?.toString() : emptyPlaceholder, + textStyle, + cell.meta.width, + cell.spreadsheet + ) + const lines = wrapText.split(lineBreak) + let extraStyleFontSize = textStyle.fontSize + // 不是表头,处理文本高度和换行 + if (!isHeader) { + const textHeight = getWrapTextHeight( + wrapText.replaceAll(lineBreak, ''), + textStyle, + cell.spreadsheet, + maxLines + ) + const lineCountInCell = Math.floor(cell.meta.height / textHeight) + const wrapTextArr = lines.slice(0, lineCountInCell) + + // 根据行数调整换行后的文本内容 + wrapText = lineCountInCell < 1 ? ellipsis : wrapTextArr.join(lineBreak) || ellipsis + const resultWrapArr = wrapText.split(lineBreak) + // 控制最大行数 + if ( + !wrapText.endsWith(ellipsis) && + (lines.length > maxLines || lines.length > lineCountInCell) + ) { + // 第一行的字符个数 + const firstLineStrNumber = resultWrapArr[0].length + const temp = resultWrapArr.slice(0, Math.min(maxLines, lineCountInCell)) + // 修改最后一行的字符,按照第一行字符个数-1,修改最后一行的字符为... + temp[temp.length - 1] = temp[temp.length - 1].slice(0, firstLineStrNumber - 1) + ellipsis + wrapText = temp.join(lineBreak) + } + if (wrapText === ellipsis) { + extraStyleFontSize = 12 + } + } else { + const resultWrapArr = wrapText.split(lineBreak) + // 控制最大行数 + if (lines.length > maxLines) { + const temp = resultWrapArr.slice(0, maxLines) + // 第一行的字符个数 + const firstLineStrNumber = resultWrapArr[0].length + // 修改最后一行的字符 + temp[temp.length - 1] = temp[temp.length - 1].slice(0, firstLineStrNumber - 1) + ellipsis + wrapText = temp.join(lineBreak) + } + } + // 设置最终文本和其宽度 + cell.actualText = wrapText + cell.actualTextWidth = cell.spreadsheet.measureTextWidth(wrapText, textStyle) + + // 获取文本位置并渲染文本 + const position = cell.getTextPosition() + // 绘制文本 + cell.textShape = renderText(cell, [cell.textShape], position.x, position.y, wrapText, textStyle, { + fontSize: extraStyleFontSize + }) + + // 将文本形状添加到形状数组 + cell.textShapes.push(cell.textShape) +} + +/** + * 计算表头高度 + * @param info 单元格信息 + * @param newChart + * @param tableHeader 表头配置 + * @param basicStyle 表格基础样式 + * @param layoutResult + */ +export const calculateHeaderHeight = (info, newChart, tableHeader, basicStyle, layoutResult) => { + if (tableHeader.showTableHeader === false) return + const ev = layoutResult || newChart.facet.layoutResult + const maxLines = basicStyle.maxLines ?? 1 + const textStyle = { ...newChart.theme.cornerCell.text } + const sourceText = info.info.meta.value + let maxHeight = getWrapTextHeight( + getWrapText(sourceText, textStyle, info.info.resizedWidth, ev.spreadsheet), + textStyle, + ev.spreadsheet, + maxLines + ) + + // 获取最大高度的列,排除当前列 + const maxHeightCol = ev.colLeafNodes + .filter(n => n.colIndex !== info.info.meta.colIndex) + .reduce( + (maxHeightNode, currentNode) => { + const wrapTextHeight = getWrapTextHeight( + getWrapText(currentNode.value, textStyle, currentNode.width, currentNode.spreadsheet), + textStyle, + currentNode.spreadsheet, + maxLines + ) + return wrapTextHeight > maxHeightNode.height + ? { height: wrapTextHeight, colIndex: currentNode.colIndex } + : maxHeightNode + }, + { height: 0 } + ) + + // 使用最大高度 + maxHeight = Math.max(maxHeight, maxHeightCol.height) + textStyle.fontSize + 10.5 + + if (layoutResult) { + if (basicStyle.tableColumnMode === 'adapt') maxHeight -= textStyle.fontSize - 2 + ev.colLeafNodes.forEach(n => (n.height = maxHeight)) + ev.colsHierarchy.height = maxHeight + } + + newChart.store.set('autoCalcHeight', maxHeight) +} + +/** + * 获取换行文本 + * 累加字符串单个字符的宽度,超过单元格宽度时,添加换行 + * @param sourceText + * @param textStyle + * @param cellWidth + * @param spreadsheet + */ +const getWrapText = (sourceText, textStyle, cellWidth, spreadsheet) => { + if (!sourceText && sourceText !== 0) return '' + sourceText = sourceText.toString().trim() + const getTextWidth = text => spreadsheet.measureTextWidthRoughly(text, textStyle) + + let resultWrapText = '' + let restText = '' + let restTextWidth = 0 + for (let i = 0; i < sourceText.length; i++) { + const char = sourceText[i] + const charWidth = getTextWidth(char) + restTextWidth += charWidth + restText += char + // 中文时,需要单元格宽度减去16个文字宽度,否则会超出单元格宽度 + const cWidth = char.charCodeAt(0) >= 128 ? 12 : 8 + // 添加换行 + if (restTextWidth >= cellWidth - textStyle.fontSize - cWidth) { + // 最后一个字符不添加换行符 + resultWrapText += restText + (i !== sourceText.length - 1 ? '\n' : '') + restText = '' + restTextWidth = 0 + } + } + + resultWrapText += restText + return resultWrapText +} +/** + * 计算文本行高 + * @param wrapText + * @param textStyle + * @param spreadsheet + * @param maxLines 最大行数 + */ +const getWrapTextHeight = (wrapText, textStyle, spreadsheet, maxLines) => { + // 行内最高 + let maxHeight = 0 + // 获取最高字符的高度 + for (const char of wrapText) { + const h = textStyle.fontSize / (char.charCodeAt(0) >= 128 ? 5 : 2.5) + maxHeight = Math.max(maxHeight, spreadsheet.measureTextHeight(char, textStyle) + h) + } + // 行数 + const lines = wrapText.split('\n').length + return Math.min(lines, maxLines) * maxHeight +} + +/** + * 设置汇总行 + * @param chart + * @param s2Options + * @param newData + * @param tableHeader + * @param basicStyle + * @param showSummary + */ +export const configSummaryRow = ( + chart, + s2Options, + newData, + tableHeader, + basicStyle, + showSummary +) => { + if (!showSummary || !newData.length) return + // 设置汇总行高度和表头一致 + const heightByField = {} + heightByField[newData.length] = tableHeader.tableTitleHeight + s2Options.style.rowCfg = { heightByField } + // 计算汇总加入到数据里,冻结最后一行 + s2Options.frozenTrailingRowCount = 1 + const yAxis = chart.yAxis + const xAxis = chart.xAxis + const summaryObj = newData.reduce( + (p, n) => { + if (chart.type === 'table-info') { + xAxis + .filter(axis => [2, 3, 4].includes(axis.deType)) + .forEach(axis => { + p[axis.dataeaseName] = + (parseFloat(n[axis.dataeaseName]) || 0) + (parseFloat(p[axis.dataeaseName]) || 0) + }) + } else { + yAxis.forEach(axis => { + p[axis.dataeaseName] = + (parseFloat(n[axis.dataeaseName]) || 0) + (parseFloat(p[axis.dataeaseName]) || 0) + }) + } + return p + }, + { SUMMARY: true } + ) + newData.push(summaryObj) + s2Options.dataCell = viewMeta => { + // 配置文本自动换行参数 + viewMeta.autoWrap = basicStyle.autoWrap + viewMeta.maxLines = basicStyle.maxLines + if (viewMeta.rowIndex !== newData.length - 1) { + return new CustomDataCell(viewMeta, viewMeta.spreadsheet) + } + if (viewMeta.colIndex === 0) { + if (tableHeader.showIndex) { + viewMeta.fieldValue = basicStyle.summaryLabel ?? i18nt('chart.total_show') + } else { + if (xAxis.length) { + viewMeta.fieldValue = basicStyle.summaryLabel ?? i18nt('chart.total_show') + } + } + } + return new SummaryCell(viewMeta, viewMeta.spreadsheet) + } +} + +/** + * 汇总行样式,紧贴在单元格后面 + * @param newChart + * @param newData + * @param tableCell + * @param tableHeader + * @param showSummary + */ +export const summaryRowStyle = (newChart, newData, tableCell, tableHeader, showSummary) => { + if (!showSummary || !newData.length) return + newChart.on(S2Event.LAYOUT_BEFORE_RENDER, () => { + const showHeader = tableHeader.showTableHeader === true + // 不显示表头时,减少一个表头的高度 + const headerAndSummaryHeight = showHeader ? 2 : 1 + const totalHeight = + tableHeader.tableTitleHeight * headerAndSummaryHeight + + tableCell.tableItemHeight * (newData.length - 1) + if (totalHeight < newChart.options.height) { + // 6 是阴影高度 + newChart.options.height = + totalHeight < newChart.options.height - 6 ? totalHeight + 6 : totalHeight + } + }) +} + +export class SummaryCell extends CustomDataCell { + getTextStyle() { + const textStyle = cloneDeep(this.theme.colCell.bolderText) + textStyle.textAlign = this.theme.dataCell.text.textAlign + return textStyle + } + getBackgroundColor() { + const { backgroundColor, backgroundColorOpacity } = this.theme.colCell.cell + return { backgroundColor, backgroundColorOpacity } + } +} + +/** + * 配置空数据样式 + * @param newChart + * @param basicStyle + * @param newData + * @param container + */ +export const configEmptyDataStyle = (newChart, basicStyle, newData, container) => { + /** + * 辅助函数:移除空数据dom + */ + const removeEmptyDom = () => { + const emptyElement = document.getElementById(container + '_empty') + if (emptyElement) { + emptyElement.parentElement.removeChild(emptyElement) + } + } + removeEmptyDom() + if (newData.length) return + newChart.on(S2Event.LAYOUT_AFTER_HEADER_LAYOUT, ev => { + removeEmptyDom() + if (!newData.length) { + const emptyDom = document.createElement('div') + const left = Math.min(newChart.options.width, ev.colsHierarchy.width) / 2 - 32 + emptyDom.id = container + '_empty' + emptyDom.textContent = i18nt('data_set.no_data') + emptyDom.setAttribute( + 'style', + `position: absolute; + left: ${left}px; + top: 50%;` + ) + const parent = document.getElementById(container) + parent.insertBefore(emptyDom, parent.firstChild) + } + }) +} + +export const getLeafNodes = (tree: Array): ColumnNode[] => { + const result: ColumnNode[] = [] + const inorderTraversal = node => { + if (!node.children?.length) { + // 叶子节点,添加到结果数组 + result.push(node) + return + } + // 中序遍历 + for (let i = 0; i < node.children?.length; i++) { + inorderTraversal(node.children[i]) + } + } + + // 遍历树中所有节点 + tree.forEach(node => inorderTraversal(node)) + return result +} + +export const getColumns = (fields, cols: Array) => { + const result = [] + for (let i = 0; i < cols.length; i++) { + if (fields.includes(cols[i].key)) { + result.push(cols[i]) + } + if (cols[i].children?.length) { + result.push(...getColumns(fields, cols[i].children as Array)) + } + } + return result +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/index.ts b/frontend/src/data-visualization/chart/components/js/panel/index.ts new file mode 100644 index 0000000..4ecc42a --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/index.ts @@ -0,0 +1,32 @@ +import { AbstractChartView } from '@/data-visualization/chart/components/js/panel/types' +import { isParent } from '@/data-visualization/chart/components/js/util' + +class ChartViewManager { + private static CHART_VIEW_MAP = new Map>() + + public registerChartView(render: string, name: string, view: AbstractChartView) { + if (ChartViewManager.CHART_VIEW_MAP.has(render)) { + ChartViewManager.CHART_VIEW_MAP.get(render).set(name, view) + } else { + ChartViewManager.CHART_VIEW_MAP.set(render, new Map([[name, view]])) + } + } + + public getChartView(render: string, name: string): AbstractChartView { + return ChartViewManager.CHART_VIEW_MAP.get(render)?.get(name) + } +} + +const chartViewManager = new ChartViewManager() +// 批量自动注册图表,只要是 AbstractChartView 的子类都初始化然后存起来 +const charts = import.meta.glob(['./charts/**/*.ts', '!**/common.ts'], { eager: true }) +for (const chart in charts) { + const chartModule = charts[chart] + Object.getOwnPropertyNames(chartModule).forEach(prop => { + if (isParent(chartModule[prop], AbstractChartView)) { + const chartView = new chartModule[prop]() as AbstractChartView + chartViewManager.registerChartView(chartView.render, chartView.name, chartView) + } + }) +} +export default chartViewManager diff --git a/frontend/src/data-visualization/chart/components/js/panel/types/impl/g2plot.ts b/frontend/src/data-visualization/chart/components/js/panel/types/impl/g2plot.ts new file mode 100644 index 0000000..c8269b5 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/types/impl/g2plot.ts @@ -0,0 +1,207 @@ +import type { PickOptions } from '@antv/g2plot/esm/core/plot' +import type { Plot } from '@antv/g2plot/esm/core/plot' +import { + getAnalyse, + getAnalyseHorizontal, + getLabel, + getLegend, + getMultiSeriesTooltip, + getSlider, + getTheme, + getTooltip, + getXAxis, + getYAxis, + getConditions, + handleConditionsStyle, + addConditionsStyleColorToData, + configEmptyDataStyle +} from '@/data-visualization/chart/components/js/panel/common/common_antv' +import { + AntVAbstractChartView, + AntVDrawOptions, + ChartLibraryType, + ChartWrapper +} from '@/data-visualization/chart/components/js/panel/types' + +import { + getColor, + getGroupColor, + getSingleDimensionColor, + getStackColor, + handleEmptyDataStrategy, + setupSeriesColor +} from '../../../util' +import { Options } from '@antv/g2plot' + +export interface G2PlotDrawOptions extends AntVDrawOptions { + /** + * 缩放比例 + */ + scale?: number + /** + * 特殊处理,象限图设置分割线的默认值 + * @param args + */ + quadrantDefaultBaseline?: (...args: any) => void +} + +/** + * 图表对象包装类,一个图表里面可能有多个对象实例 + */ +export class G2PlotWrapper> extends ChartWrapper< + P | Array

    +> { + constructor(chartInstance: P | Array

    ) { + super() + this.chartInstance = chartInstance + } + destroy = () => { + if (!this.chartInstance) { + return + } + if (Array.isArray(this.chartInstance)) { + this.chartInstance?.forEach(p => p.destroy()) + } else { + this.chartInstance?.destroy() + } + } + + render = () => { + if (!this.chartInstance) { + return + } + if (Array.isArray(this.chartInstance)) { + this.chartInstance?.forEach(p => p.render()) + } else { + this.chartInstance?.render() + } + } +} +/** + * G2Plot 的图表抽象类 + */ +export abstract class G2PlotChartView< + O extends PickOptions = PickOptions, + P extends Plot = Plot +> extends AntVAbstractChartView { + /** + * 根据参数构建图表对象然后返回 + * @param drawOptions 图表配置参数 + * @return 生成的图表对象,类型为 Plot 的子类 + */ + public abstract drawChart(drawOptions: G2PlotDrawOptions

    ): G2PlotWrapper | P | Promise

    + + protected configTheme(chart: Chart, options: O): O { + const theme = getTheme(chart) + return { ...options, theme } + } + + protected configLabel(chart: Chart, options: O): O { + const label = getLabel(chart) + return { ...options, label } + } + + protected configMultiSeriesTooltip(chart: Chart, options: O): O { + const tooltip = getMultiSeriesTooltip(chart) + return { ...options, tooltip } + } + + protected configTooltip(chart: Chart, options: O): O { + const tooltip = getTooltip(chart) + return { ...options, tooltip } + } + protected configLegend(chart: Chart, options: O): O { + const legend = getLegend(chart) + return { ...options, legend } + } + + protected configXAxis(chart: Chart, options: O): O { + const xAxis = getXAxis(chart) + return { ...options, xAxis } + } + + protected configYAxis(chart: Chart, options: O): O { + const yAxis = getYAxis(chart) + return { ...options, yAxis } + } + + protected configSlider(chart: Chart, options: O): O { + const slider = getSlider(chart) + return { ...options, slider } + } + + protected configAnalyse(chart: Chart, options: O): O { + const annotations = getAnalyse(chart) + return { + ...options, + annotations: [...annotations, ...((options as unknown as Options).annotations || [])] + } + } + + protected configAnalyseHorizontal(chart: Chart, options: O): O { + const annotations = getAnalyseHorizontal(chart) + return { ...options, annotations } + } + + protected configEmptyDataStrategy(chart: Chart, options: O): O { + return handleEmptyDataStrategy(chart, options) + } + + protected configColor(chart: Chart, options: O): O { + const color = getColor(chart) + return { ...options, color } + } + + protected configGroupColor(chart: Chart, options: O): O { + const color = getGroupColor(chart, options) + return { ...options, color } + } + + protected configStackColor(chart: Chart, options: O): O { + const color = getStackColor(chart, options) + return { ...options, color } + } + + protected configSingleDimensionColor(chart: Chart, options: O): O { + const color = getSingleDimensionColor(chart, options) + return { ...options, color } + } + + public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + return setupSeriesColor(chart, data) + } + + public setupSubSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + return undefined + } + + protected configConditions(chart: Chart, options: O) { + const annotations = getConditions(chart) + return { + ...options, + annotations: [...annotations, ...((options as unknown as Options).annotations || [])] + } + } + + protected configBarConditions(chart: Chart, options: O) { + return handleConditionsStyle(chart, options) + } + + protected addConditionsStyleColorToData(chart: Chart, data: any[]) { + return addConditionsStyleColorToData(chart, data) + } + + protected configEmptyDataStyle(newChart, newData: any[], container: string) { + configEmptyDataStyle(newChart, newData, container) + } + + /** + * 流式配置公共参数,处理常用的配置,后续如果有其他通用配置也可以放进来,需要单独配置的属性在各个图表自行实现。 + * @param chart 数据库图表对象。 + * @param options 各个图表的参数,泛化的 Options,可以自行扩展,比如加个扩展 X 轴或者扩展 Y 轴字段。 + */ + protected abstract setupOptions(chart: Chart, options: O, context?: Record): O + protected constructor(name: string, defaultData: any[]) { + super(ChartLibraryType.G2_PLOT, name, defaultData) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/types/impl/l7.ts b/frontend/src/data-visualization/chart/components/js/panel/types/impl/l7.ts new file mode 100644 index 0000000..24bd6aa --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/types/impl/l7.ts @@ -0,0 +1,143 @@ +import type { Scene } from '@antv/l7-scene' +import { + AntVAbstractChartView, + AntVDrawOptions, + ChartLibraryType, + ChartWrapper +} from '@/data-visualization/chart/components/js/panel/types' +import { cloneDeep, defaultsDeep } from 'lodash-es' +import { parseJson } from '@/data-visualization/chart/components/js/util' +import type { ILayer } from '@antv/l7plot' +import { + configL7Label, + configL7Tooltip, + configL7Zoom +} from '@/data-visualization/chart/components/js/panel/common/common_antv' +// import { queryMapKeyApi } from '@/api/setting/sysParameter' +import { useMapStoreWithOut } from '@/data-visualization/store/modules/map' +const mapStore = useMapStoreWithOut() + +export type L7DrawConfig

    = AntVDrawOptions

    +export interface L7Config extends ILayer { + handleConfig?: (arg0: Scene) => void + [key: string]: string | any +} +export class L7Wrapper< + O extends L7Config | Array, + S extends Scene +> extends ChartWrapper { + private readonly config: O | Array + private readonly scene: S | null = null + + public getScene() { + return this.scene + } + constructor(scene: S, l7config: O | Array | undefined) { + super() + this.chartInstance = scene + this.config = l7config + this.scene = scene + } + destroy = () => { + if (!this.chartInstance) { + return + } + this.chartInstance?.destroy() + } + render = () => { + if (this.scene && this.config) { + if (this.scene.loaded) { + if (Array.isArray(this.config)) { + this.config?.forEach(p => { + this.handleConfig(p) + }) + } else { + this.handleConfig(this.config) + } + } else { + this.scene.on('loaded', () => { + if (Array.isArray(this.config)) { + this.config?.forEach(p => { + this.handleConfig(p) + }) + } else { + this.handleConfig(this.config) + } + }) + } + } + } + + handleConfig = (config: L7Config) => { + if (config) { + if (config.handleConfig) { + config.handleConfig?.(this.scene) + } else { + this.scene.addLayer(config) + } + } + } +} +export abstract class L7ChartView< + S extends Scene, + O extends L7Config +> extends AntVAbstractChartView { + public abstract drawChart(drawOption: L7DrawConfig): L7Wrapper | any + + protected configEmptyDataStrategy(chart: Chart, options: O): O { + const { functionCfg } = parseJson(chart.senior) + const emptyDataStrategy = functionCfg.emptyDataStrategy + if (!emptyDataStrategy || emptyDataStrategy === 'breakLine') { + return options + } + const data = cloneDeep(options.sourceOption.data) + if (emptyDataStrategy === 'setZero') { + data.forEach(item => { + item.value === null && (item.value = 0) + }) + } + if (emptyDataStrategy === 'ignoreData') { + for (let i = data.length - 1; i >= 0; i--) { + if (data[i].value === null) { + data.splice(i, 1) + } + } + } + options.sourceOption.data = data + return options + } + + protected configZoomButton(chart: Chart, plot: S) { + configL7Zoom(chart, plot) + } + + protected configLabel(chart: Chart, options: O): O { + const label = configL7Label(chart) + defaultsDeep(options.label, label) + return options + } + + protected configTooltip(chart: Chart, options: O): O { + const tooltip = configL7Tooltip(chart) + defaultsDeep(options.tooltip, tooltip) + return options + } + + protected constructor(name: string, defaultData: any[]) { + super(ChartLibraryType.L7, name, defaultData) + } + + protected getMapKey = async () => { + if (!mapStore.mapKey.key) { + // await queryMapKeyApi().then(res => mapStore.setKey(res.data)) + } + if (mapStore.mapKey.securityCode) { + window._AMapSecurityConfig = { + securityJsCode: mapStore.mapKey.securityCode + } + } + return mapStore.mapKey + } + + protected abstract setupOptions(chart: Chart, options: O): O +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/types/impl/l7plot.ts b/frontend/src/data-visualization/chart/components/js/panel/types/impl/l7plot.ts new file mode 100644 index 0000000..21f8b3b --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/types/impl/l7plot.ts @@ -0,0 +1,95 @@ +import type { ViewLevel } from '@antv/l7plot/dist/esm/plots/choropleth/types' +import type { FeatureCollection } from '@antv/l7plot/dist/esm/plots/choropleth/types' +import type { PlotOptions } from '@antv/l7plot/dist/esm/types/plot' +import type { Plot as L7Plot } from '@antv/l7plot/dist/esm/core/plot' +import { + configL7Label, + configL7Legend, + configL7PlotZoom, + configL7Style, + configL7Tooltip +} from '@/data-visualization/chart/components/js/panel/common/common_antv' +import { + AntVAbstractChartView, + AntVDrawOptions, + ChartLibraryType +} from '@/data-visualization/chart/components/js/panel/types' +import { cloneDeep, defaultsDeep } from 'lodash-es' +import { parseJson } from '@/data-visualization/chart/components/js/util' + +export interface L7PlotDrawOptions

    extends AntVDrawOptions

    { + areaId?: string + level?: ViewLevel['level'] + geoJson?: FeatureCollection + scope?: string[] +} +// S2 or others to be defined next +export abstract class L7PlotChartView< + O extends PlotOptions, + P extends L7Plot +> extends AntVAbstractChartView { + public abstract drawChart(drawOption: L7PlotDrawOptions

    ): P | Promise

    + + protected configLabel(chart: Chart, options: O): O { + const label = configL7Label(chart) + defaultsDeep(options.label, label) + return options + } + + protected configStyle(chart: Chart, options: O): O { + const style = configL7Style(chart) + defaultsDeep(options['style'], style) + return options + } + + protected configTooltip(chart: Chart, options: O): O { + const tooltip = configL7Tooltip(chart) + defaultsDeep(options.tooltip, tooltip) + return options + } + protected configLegend(chart: Chart, options: O): O { + const legend = configL7Legend(chart) + defaultsDeep(options, { legend }) + return options + } + protected configEmptyDataStrategy(chart: Chart, options: O): O { + const { functionCfg } = parseJson(chart.senior) + const emptyDataStrategy = functionCfg.emptyDataStrategy + if (!emptyDataStrategy || emptyDataStrategy === 'breakLine') { + return options + } + const data = cloneDeep(options.source.data) + if (emptyDataStrategy === 'setZero') { + data.forEach(item => { + item.value === null && (item.value = 0) + item.dynamicTooltipValue?.length > 0 && + item.dynamicTooltipValue.forEach(ele => { + ele.value === null && (ele.value = 0) + }) + }) + } + if (emptyDataStrategy === 'ignoreData') { + for (let i = data.length - 1; i >= 0; i--) { + if (data[i].value === null) { + data.splice(i, 1) + } + for (let j = data[i]?.dynamicTooltipValue?.length - 1; j >= 0; j--) { + if (data[i].dynamicTooltipValue[j].value === null) { + data[i].dynamicTooltipValue.splice(j, 1) + } + } + } + } + options.source.data = data + return options + } + + protected configZoomButton(chart: Chart, plot: P) { + configL7PlotZoom(chart, plot) + } + protected constructor(name: string, defaultData?: any[]) { + super(ChartLibraryType.L7_PLOT, name) + this.defaultData = defaultData + } + protected abstract setupOptions(chart: Chart, options: O, context?: Record): O +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/types/impl/s2.ts b/frontend/src/data-visualization/chart/components/js/panel/types/impl/s2.ts new file mode 100644 index 0000000..3bcc669 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/types/impl/s2.ts @@ -0,0 +1,169 @@ +import { + AntVAbstractChartView, + AntVDrawOptions, + ChartLibraryType +} from '@/data-visualization/chart/components/js/panel/types' +import { + S2Theme, + SpreadSheet, + Style, + S2Options, + Meta, + SERIES_NUMBER_FIELD, + setTooltipContainerStyle, + S2DataConfig, + S2Event +} from '@antv/s2' +import { + configHeaderInteraction, + configMergeCells, + configTooltip, + getConditions, + getCustomTheme, + getStyle, + handleTableEmptyStrategy +} from '@/data-visualization/chart/components/js/panel/common/common_table' +import '@antv/s2/dist/style.min.css' +import { find } from 'lodash-es' + +declare interface PageInfo { + currentPage: number + pageSize: number + total: number +} + +export interface S2DrawOptions extends AntVDrawOptions { + pageInfo?: PageInfo + resizeAction?: (...args: any) => void + touchAction?: (...args: any) => void +} +export abstract class S2ChartView

    extends AntVAbstractChartView { + public abstract drawChart(drawOption: S2DrawOptions

    ): P + protected constructor(name: string, defaultData: any[]) { + super(ChartLibraryType.S2, name, defaultData) + } + protected configTheme(chart: Chart): S2Theme { + return getCustomTheme(chart) + } + + protected configStyle(chart: Chart, s2DataConfig: S2DataConfig): Style { + return getStyle(chart, s2DataConfig) + } + + protected configEmptyDataStrategy(chart: Chart): Record[] { + return handleTableEmptyStrategy(chart) + } + + protected configTooltip(chart: Chart, option: S2Options) { + configTooltip(chart, option) + } + + protected configHeaderInteraction(chart: Chart, option: S2Options) { + configHeaderInteraction(chart, option) + } + + protected configConditions(chart: Chart) { + return getConditions(chart) + } + + protected configMergeCells(chart: Chart, option: S2Options, dataConfig: S2DataConfig) { + configMergeCells(chart, option, dataConfig) + } + + protected showTooltip(s2Instance: P, event, metaConfig: Meta[]) { + const cell = s2Instance.getCell(event.target) + const meta = cell.getMeta() + let content = '' + let field + switch (cell.cellType) { + case 'dataCell': + case 'mergedCell': + if (meta.valueField === SERIES_NUMBER_FIELD) { + content = meta.fieldValue.toString() + break + } + field = find(metaConfig, item => item.field === meta.valueField) + if (meta.fieldValue === 0) { + content = '0' + } + if (meta.fieldValue) { + content = field?.formatter?.(meta.fieldValue) + } + break + case 'rowCell': + case 'colCell': + content = meta.label + field = find(metaConfig, item => item.field === content) + if (field) { + content = field.name + } + break + } + if (!content) { + return + } + event.s2Instance = s2Instance + const style = s2Instance.options.tooltip.style + setTooltipContainerStyle(s2Instance.tooltip.container, { style }) + s2Instance.showTooltip({ + position: { + x: event.clientX, + y: event.clientY + }, + content, + meta, + event + }) + } + + protected configTouchEvent(s2Instance: P, option: S2DrawOptions

    , meta: Meta[]) { + const { touchAction } = option + // touch action + s2Instance.once(S2Event.LAYOUT_AFTER_RENDER, () => { + const touchActionInit = s2Instance.store.get('touchActionInit') + if (touchActionInit) { + return + } + s2Instance.store.set('touchActionInit', true) + const canvas = s2Instance.getCanvasElement() + let startTime = Date.now() + canvas.addEventListener('touchstart', () => { + startTime = Date.now() + }) + canvas.addEventListener('touchend', e => { + const duration = Date.now() - startTime + // 超过 300ms 触发复制 + if (duration > 300) { + return + } + const callback = () => { + const canvasPosition = canvas.getBoundingClientRect() + const touchPosition = [e.changedTouches[0].pageX, e.changedTouches[0].pageY] + const relativePosition = [ + touchPosition[0] - canvasPosition.x, + touchPosition[1] - canvasPosition.y + ] + const shape = s2Instance.container.getShape(relativePosition[0], relativePosition[1]) + // 图片单元格点击放大图片 + if (shape.cfg?.parent.constructor.name === 'ImageCell') { + return + } + e.preventDefault() + e.stopPropagation() + if (shape) { + const event = { + target: shape, + x: relativePosition[0], + y: relativePosition[1], + clientX: touchPosition[0], + clientY: touchPosition[1], + originEvent: e + } + this.showTooltip(s2Instance, event, meta) + } + } + touchAction(callback) + }) + }) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/panel/types/index.ts b/frontend/src/data-visualization/chart/components/js/panel/types/index.ts new file mode 100644 index 0000000..8c35bcc --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/panel/types/index.ts @@ -0,0 +1,128 @@ +import { useI18n } from '@/data-visualization/hooks/web/useI18n' + +const { t } = useI18n() + +export enum ChartRenderType { + ANT_V = 'antv', + ECHARTS = 'echarts', + CUSTOM = 'custom' +} + +export enum ChartLibraryType { + G2_PLOT = 'g2plot', + L7_PLOT = 'l7plot', + L7 = 'l7', + ECHARTS = 'echarts', + S2 = 's2', + RICH_TEXT = 'rich-text', + PICTURE_GROUP = 'picture-group', + INDICATOR = 'indicator' +} +export abstract class ChartWrapper { + chartInstance: O + abstract render: () => any + abstract destroy: () => any +} +export abstract class AbstractChartView { + render: ChartRenderType + library: ChartLibraryType + name: string + title: string + abstract properties: EditorProperty[] + abstract propertyInner: EditorPropertyInner + abstract axis: AxisType[] + abstract axisConfig: AxisConfig + abstract selectorSpec: EditorSelectorSpec + /** + * 在新建和切换图表的时候处理默认值 + * @param chart 数据库图表对象 + */ + setupDefaultOptions(chart: ChartObj): ChartObj { + return chart + } + + protected defaultData: any[] + + protected constructor( + render: ChartRenderType, + library: ChartLibraryType, + name: string, + defaultData?: any[] + ) { + this.render = render + this.library = library + this.name = name + this.defaultData = defaultData + } +} + +export interface AntVDrawOptions { + /** + * 生成的图表对象 + */ + chartObj: O + /** + * dom容器id + */ + container: string + /** + * 数据库中的图表配置对象 + */ + chart: Chart + /** + * 事件回调函数 + * @param args 事件参数 + */ + action?: (...args: any[]) => any +} + +export abstract class AntVAbstractChartView extends AbstractChartView { + axisConfig: AxisConfig = { + xAxis: { + name: `${t('chart.drag_block_type_axis')} / ${t('chart.dimension')}`, + type: 'd', + allowEmpty: true + }, + xAxisExt: { + name: `${t('chart.chart_group')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1, + allowEmpty: true + }, + extStack: { + name: `${t('chart.stack_item')} / ${t('chart.dimension')}`, + type: 'd', + limit: 1, + allowEmpty: true + }, + yAxis: { + name: `${t('chart.drag_block_value_axis')} / ${t('chart.quota')}`, + type: 'q', + limit: 1, + allowEmpty: true + } + } + selectorSpec: EditorSelectorSpec = { + 'misc-style-selector': { + title: `${t('chart.size')}` + }, + 'dual-y-axis-selector': { + title: `${t('chart.yAxis')}` + }, + 'x-axis-selector': { + title: `${t('chart.xAxis')}` + } + } + protected constructor(library: ChartLibraryType, name: string, defaultData?: any[]) { + super(ChartRenderType.ANT_V, library, name, defaultData) + } +} + +/** + * Echarts 图表的抽象类 + */ +export abstract class EchartsChartView extends AbstractChartView { + protected constructor(name: string, defaultData: any[]) { + super(ChartRenderType.ECHARTS, ChartLibraryType.ECHARTS, name, defaultData) + } +} diff --git a/frontend/src/data-visualization/chart/components/js/util.ts b/frontend/src/data-visualization/chart/components/js/util.ts new file mode 100644 index 0000000..33984be --- /dev/null +++ b/frontend/src/data-visualization/chart/components/js/util.ts @@ -0,0 +1,1209 @@ +import { isEmpty, isNumber } from 'lodash-es' +import { DEFAULT_TITLE_STYLE } from '../editor/util/chart' +import { equalsAny, includesAny } from '../editor/util/StringUtils' +import { FeatureCollection } from '@antv/l7plot/dist/esm/plots/choropleth/types' +import { useMapStoreWithOut } from '@/data-visualization/store/modules/map' +import { getGeoJson } from '@/api/data-visualization/map' +import { computed, toRaw } from 'vue' +import { Options } from '@antv/g2plot/esm' +import { PickOptions } from '@antv/g2plot/esm/core/plot' +import { innerExportDataSetDetails, innerExportDetails } from '@/api/data-visualization/chart' +import { ElMessage } from 'element-plus-secondary' +import { useI18n } from '@/data-visualization/hooks/web/useI18n' +import { useLinkStoreWithOut } from '@/data-visualization/store/modules/link' +import { useAppStoreWithOut } from '@/data-visualization/store/modules/app' +const appStore = useAppStoreWithOut() +const isDataEaseBi = computed(() => appStore.getIsDataEaseBi) + +const { t } = useI18n() +// 同时支持将hex和rgb,转换成rgba +export function hexColorToRGBA(hex, alpha) { + let rgb = [] // 定义rgb数组 + if (hex.indexOf('#') > -1) { + if (/^\#[0-9A-F]{3}$/i.test(hex)) { + // 判断传入是否为#三位十六进制数 + let sixHex = '#' + hex.replace(/[0-9A-F]/gi, function (kw) { + sixHex += kw + kw // 把三位16进制数转化为六位 + }) + hex = sixHex // 保存回hex + } + if (/^#[0-9A-F]{6}$/i.test(hex)) { + // 判断传入是否为#六位十六进制数 + hex.replace(/[0-9A-F]{2}/gi, function (kw) { + // eslint-disable-next-line no-eval + rgb.push(eval('0x' + kw)) // 十六进制转化为十进制并存如数组 + }) + return `rgba(${rgb.join(',')},${alpha / 100})` // 输出RGB格式颜色 + } else { + return 'rgb(0,0,0)' + } + } else { + rgb = hex.match(/\d+/g) + return `rgba(${rgb.join(',')},${alpha / 100})` + } +} + +export function digToHex(dig) { + let prefix = '' + const num = parseInt((dig * 2.55).toString()) + if (num < 16) { + prefix = '0' + } + return prefix.concat(num.toString(16).toUpperCase()) +} + +export function customSort(custom, data) { + const indexArr = [] + const joinArr = [] + for (let i = 0; i < custom.length; i++) { + const ele = custom[i] + for (let j = 0; j < data.length; j++) { + const d = data[j] + if (ele === d.field) { + joinArr.push(d) + indexArr.push(j) + } + } + } + // 取得 joinArr 就是两者的交集 + const indexArrData = [] + for (let i = 0; i < data.length; i++) { + indexArrData.push(i) + } + const indexResult = [] + for (let i = 0; i < indexArrData.length; i++) { + if (indexArr.indexOf(indexArrData[i]) === -1) { + indexResult.push(indexArrData[i]) + } + } + + const subArr = [] + for (let i = 0; i < indexResult.length; i++) { + subArr.push(data[indexResult[i]]) + } + + return joinArr.concat(subArr) +} + +export function customColor(custom, res) { + const result = [] + for (let i = 0; i < res.length; i++) { + const r = res[i] + let flag = false + for (let j = 0; j < custom.length; j++) { + const c = custom[j] + if (r.name === c.name) { + flag = true + result.push(c) + } + } + if (!flag) { + result.push(r) + } + } + return result +} + +export function getColors(chart, colors, reset) { + // 自定义颜色,先按照没有设定的情况,并排好序,当做最终结果 + let seriesColors = [] + let series + if (chart.type.includes('stack')) { + if (chart.data) { + const data = chart.data.data + const stackData = [] + for (let i = 0; i < data.length; i++) { + const s = data[i] + stackData.push(s.category) + } + const sArr = stackData.filter(function (item, index, stackData) { + return stackData.indexOf(item, 0) === index + }) + + for (let i = 0; i < sArr.length; i++) { + const s = sArr[i] + seriesColors.push({ + name: s, + color: colors[i % colors.length], + isCustom: false + }) + } + } + } else if ( + includesAny(chart.type, 'bar', 'scatter', 'radar', 'area') && + !chart.type.includes('group') + ) { + if (Object.prototype.toString.call(chart.yAxis) === '[object Array]') { + series = JSON.parse(JSON.stringify(chart.yAxis)) + } else { + series = JSON.parse(chart.yAxis) + } + if (series) { + for (let i = 0; i < series.length; i++) { + const s = series[i] + seriesColors.push({ + name: s.name, + color: colors[i % colors.length], + isCustom: false + }) + } + } + } else if (equalsAny(chart.type, 'bar-group', 'line')) { + // 拿到data中的category,并去重,然后构建seriesColor + if (chart.data) { + const data = chart.data.data + const s = [] + if (data) { + data.forEach(cur => { + if (s.indexOf(cur.category) < 0) { + s.push(cur.category) + } + }) + } + for (let i = 0; i < s.length; i++) { + seriesColors.push({ + name: s[i], + color: colors[i % colors.length], + isCustom: false + }) + } + } + } else { + if (chart.data) { + const data = chart.data.data + for (let i = 0; i < data.length; i++) { + const s = data[i] + seriesColors.push({ + name: s.field, + color: colors[i % colors.length], + isCustom: false + }) + } + } + } + // 如果有自定义,则与上述中的结果合并。 + // res,custom,以custom为准,去掉res中不存在的,并将custom中name一样的color赋值给res,不存在的name,即新增值,使用i % colors.length,从配色方案中选 + if (!reset) { + let sc = null + if (Object.prototype.toString.call(chart.customAttr) === '[object Object]') { + sc = JSON.parse(JSON.stringify(chart.customAttr)).color.seriesColors + } else { + sc = JSON.parse(chart.customAttr)['color'].seriesColors + } + if (sc && sc.length > 0) { + seriesColors = customColor(sc, seriesColors) + } + // 根据isCustom字段,修正color + for (let i = 0; i < seriesColors.length; i++) { + if (!seriesColors[i].isCustom) { + seriesColors[i].color = colors[i % colors.length] + } + } + } + return seriesColors +} + +export function antVCustomColor(chart) { + const colors = [] + if (chart.customAttr) { + const customAttr = JSON.parse(JSON.stringify(chart.customAttr)) + // color + if (customAttr.basicStyle) { + const basicStyle = JSON.parse(JSON.stringify(customAttr.basicStyle)) + + const customColors = getColors(chart, basicStyle.colors, false) + for (let i = 0; i < customColors.length; i++) { + colors.push(hexColorToRGBA(customColors[i].color, basicStyle.alpha)) + } + } + } + return colors +} + +export function getRemark(chart) { + const remark = {} as any + if (chart.customStyle) { + const customStyle = JSON.parse(JSON.stringify(chart.customStyle)) + if (customStyle.text) { + const title = JSON.parse(JSON.stringify(customStyle.text)) + remark.show = title.remarkShow ? title.remarkShow : DEFAULT_TITLE_STYLE.remarkShow + remark.content = title.remark ? title.remark : DEFAULT_TITLE_STYLE.remark + remark.bgFill = title.remarkBackgroundColor + ? title.remarkBackgroundColor + : DEFAULT_TITLE_STYLE.remarkBackgroundColor + } + } + return remark +} + +export const quotaViews = ['label', 'richTextView', 'indicator', 'gauge', 'liquid'] +// 地图 +const mapChartTypes = ['bubble-map', 'flow-map', 'heat-map', 'map', 'symbolic-map'] +// 分布图 +const distributionChartTypes = [ + 'pie', + 'pie-donut', + 'pie-rose', + 'pie-donut-rose', + 'radar', + 'treemap', + 'word-cloud' +] +// 关系图 +const relationChartTypes = ['scatter', 'quadrant', 'funnel', 'sankey', 'circle-packing'] +// 不支持指标累加的图表 +export const notSupportAccumulateViews = [ + ...quotaViews, + ...mapChartTypes, + ...distributionChartTypes, + ...relationChartTypes, + 'table-info', + 't-heatmap', + 'percentage-bar-stack', + 'percentage-bar-stack-horizontal', + 'progress-bar', + 'stock-line' +] + +export function handleEmptyDataStrategy(chart: Chart, options: O): O { + const { data } = options as unknown as Options + const isChartMix = chart.type.includes('chart-mix') + if (!data?.length) { + return options + } + const strategy = parseJson(chart.senior).functionCfg.emptyDataStrategy + if (strategy === 'ignoreData') { + if (isChartMix) { + for (let i = 0; i < data.length; i++) { + handleIgnoreData(data[i] as Record[]) + } + } else { + handleIgnoreData(data) + } + return options + } + const { yAxis, xAxisExt, extStack } = chart + const multiDimension = yAxis?.length >= 2 || xAxisExt?.length > 0 || extStack?.length > 0 + switch (strategy) { + case 'breakLine': { + if (multiDimension) { + // 多维度保持空 + if (isChartMix) { + for (let i = 0; i < data.length; i++) { + handleBreakLineMultiDimension(data[i] as Record[]) + } + } else { + handleBreakLineMultiDimension(data) + } + } + return { + ...options, + connectNulls: false + } + } + case 'setZero': { + if (multiDimension) { + // 多维度置0 + if (isChartMix) { + for (let i = 0; i < data.length; i++) { + handleSetZeroMultiDimension(data[i] as Record[]) + } + } else { + handleSetZeroMultiDimension(data) + } + } else { + // 单维度置0 + if (isChartMix) { + for (let i = 0; i < data.length; i++) { + handleSetZeroSingleDimension(data[i] as Record[]) + } + } else { + handleSetZeroSingleDimension(data) + } + } + break + } + } + return options +} + +function handleBreakLineMultiDimension(data) { + const dimensionInfoMap = new Map() + const subDimensionSet = new Set() + const quotaMap = new Map() + for (let i = 0; i < data.length; i++) { + const item = data[i] + const dimensionInfo = dimensionInfoMap.get(item.field) + if (dimensionInfo) { + dimensionInfo.set.add(item.category) + } else { + dimensionInfoMap.set(item.field, { set: new Set([item.category]), index: i }) + } + subDimensionSet.add(item.category) + quotaMap.set(item.category, item.quotaList) + } + // Map 是按照插入顺序排序的,所以插入索引往后推 + let insertCount = 0 + dimensionInfoMap.forEach((dimensionInfo, field) => { + if (dimensionInfo.set.size < subDimensionSet.size) { + let subInsertIndex = 0 + subDimensionSet.forEach(dimension => { + if (!dimensionInfo.set.has(dimension)) { + data.splice(dimensionInfo.index + insertCount + subInsertIndex, 0, { + field, + value: null, + category: dimension, + quotaList: quotaMap.get(dimension as string) + }) + } + subInsertIndex++ + }) + insertCount += subDimensionSet.size - dimensionInfo.set.size + } + }) +} + +function handleSetZeroMultiDimension(data: Record[]) { + const dimensionInfoMap = new Map() + const subDimensionSet = new Set() + const quotaMap = new Map() + for (let i = 0; i < data.length; i++) { + const item = data[i] + if (item.value === null) { + item.value = 0 + } + const dimensionInfo = dimensionInfoMap.get(item.field) + if (dimensionInfo) { + dimensionInfo.set.add(item.category) + } else { + dimensionInfoMap.set(item.field, { set: new Set([item.category]), index: i }) + } + subDimensionSet.add(item.category) + quotaMap.set(item.category, item.quotaList) + } + let insertCount = 0 + dimensionInfoMap.forEach((dimensionInfo, field) => { + if (dimensionInfo.set.size < subDimensionSet.size) { + let subInsertIndex = 0 + subDimensionSet.forEach(dimension => { + if (!dimensionInfo.set.has(dimension)) { + data.splice(dimensionInfo.index + insertCount + subInsertIndex, 0, { + field, + value: 0, + category: dimension, + quotaList: quotaMap.get(dimension as string) + }) + } + subInsertIndex++ + }) + insertCount += subDimensionSet.size - dimensionInfo.set.size + } + }) +} + +function handleSetZeroSingleDimension(data: Record[]) { + data.forEach(item => { + if (item.value === null) { + item.value = 0 + } + }) +} + +function handleIgnoreData(data: Record[]) { + for (let i = data.length - 1; i >= 0; i--) { + const item = data[i] + if (item.value === null) { + data.splice(i, 1) + } + } +} + +export function resetRgbOpacity(sourceColor: string, times: number): string { + if (sourceColor?.startsWith('rgb')) { + const numbers = sourceColor.match(/(\d(\.\d+)?)+/g) + if (numbers?.length === 4) { + const opacity = parseFloat(numbers[3]) + if (isNumber(opacity)) { + let resultOpacity = parseFloat((opacity * times).toFixed(2)) + if (resultOpacity > 1) { + resultOpacity = 1 + } + const colorArr = numbers.slice(0, 3).concat(resultOpacity.toString()) + return `rgba(${colorArr.join(',')})` + } + } + } + return sourceColor +} + +export function parseJson(str: T | JSONString): T { + if (typeof str !== 'string') { + return str as T + } + return JSON.parse(str) as T +} + +type FlowFunction = (param: P, result: R, context?: Record, thisArg?: any) => R + +export function flow(...flows: FlowFunction[]): FlowFunction { + return (param: P, result: R, context?: Record, thisArg?: any) => { + return flows.reduce((result: R, flow: FlowFunction) => { + if (thisArg) { + return flow.call(thisArg, param, result, context) + } else { + return flow(param, result, context) + } + }, result) + } +} + +export const isParent = (type: any, parentType: any) => { + let _type = type + while (_type) { + if (_type === parentType) { + return true + } + _type = _type.__proto__ + } + return false +} + +export const getGeoJsonFile = async (areaId: string): Promise => { + const mapStore = useMapStoreWithOut() + let geoJson = mapStore.mapCache[areaId] + if (!geoJson) { + const res = await getGeoJson(areaId) + geoJson = res.data + mapStore.setMap({ id: areaId, geoJson }) + } + return toRaw(geoJson) +} + +const getExcelDownloadRequest = (data, type?) => { + let fields = JSON.parse(JSON.stringify(data.fields)) + // liquid gauge 只需要导出一个字段 + if (['gauge', 'liquid'].includes(type) && fields.length > 1) { + fields = fields.slice(1) + } + const tableRow = JSON.parse(JSON.stringify(data.tableRow)) + const excelHeader = fields.map(item => item.chartShowName ?? item.name) + const excelTypes = fields.map(item => item.deType) + const excelHeaderKeys = fields.map(item => item.dataeaseName) + let excelData = tableRow.map(item => excelHeaderKeys.map(i => item[i])) + let detailFields = [] + if (data.detailFields?.length) { + detailFields = data.detailFields.map(item => { + return { + name: item.name, + deType: item.deType, + dataeaseName: item.dataeaseName + } + }) + excelData = tableRow.map(item => { + return excelHeaderKeys.map(i => { + if (i === 'detail' && !item[i] && Array.isArray(item['details'])) { + const arr = item['details'] + if (arr?.length) { + return arr.map(ele => detailFields.map(field => ele[field.dataeaseName])) + } + return null + } + return item[i] + }) + }) + } + return { + header: excelHeader, + details: excelData, + excelTypes: excelTypes, + excelHeaderKeys: excelHeaderKeys, + detailFields: detailFields + } +} + +export const exportExcelDownload = (chart, callBack?) => { + const excelName = chart.title + let request: any = { + proxy: null, + dvId: chart.sceneId, + viewId: chart.id, + viewInfo: chart, + viewName: excelName, + busiFlag: chart.busiFlag, + downloadType: chart.downloadType + } + if (chart.type.includes('chart-mix')) { + const req1 = getExcelDownloadRequest(chart.data.left) + const req2 = getExcelDownloadRequest(chart.data.right) + request = { + ...request, + multiInfo: [req1, req2] + } + if (chart.downloadType === 'dataset') { + delete request.multiInfo + } + } else { + const req = getExcelDownloadRequest(chart.data, chart.type) + request = { + ...request, + ...req + } + } + + if (chart.type.includes('symbolic-map')) { + request.detailFields = [] + } + + const linkStore = useLinkStoreWithOut() + + if (isDataEaseBi.value || appStore.getIsIframe) { + request.dataEaseBi = true + } + const method = request.downloadType === 'dataset' ? innerExportDataSetDetails : innerExportDetails + if (request.viewInfo?.customAttr?.basicStyle?.tablePageMode) { + request.viewInfo.customAttr.basicStyle.tablePageMode = 'page' + } + method(request) + .then(res => { + if (linkStore.getLinkToken || isDataEaseBi.value || appStore.getIsIframe) { + const blob = new Blob([res.data], { type: 'application/vnd.ms-excel' }) + const link = document.createElement('a') + link.style.display = 'none' + link.href = URL.createObjectURL(blob) + link.download = excelName + '.xlsx' // 下载的文件名 + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + } else { + callBack && callBack(res) + } + }) + .catch(() => { + console.error('Excel download error') + callBack('error') + }) +} + +export const copyString = (content: string, notify = false) => { + const clipboard = navigator.clipboard || { + writeText: data => { + return new Promise(resolve => { + const textareaDom = document.createElement('textarea') + textareaDom.setAttribute('style', 'z-index: -1;position: fixed;opacity: 0;') + textareaDom.value = data + document.body.appendChild(textareaDom) + textareaDom.select() + document.execCommand('copy') + textareaDom.remove() + resolve() + }) + } + } + clipboard.writeText(content).then(() => { + if (notify) { + ElMessage.success(t('commons.copy_success')) + } + }) +} + +/** + * 计算动态区间和颜色 + * @param minValue + * @param maxValue + * @param intervals + * @param colors + */ +export const getDynamicColorScale = ( + minValue: number, + maxValue: number, + intervals: number, + colors?: string[] +) => { + const step = (maxValue - minValue) / intervals + + const colorScale = [] + for (let i = 0; i < intervals; i++) { + colorScale.push({ + value: [minValue + i * step, minValue + (i + 1) * step], + color: colors?.[i], + label: `${(minValue + i * step).toFixed(0)} - ${(minValue + (i + 1) * step).toFixed(0)}` + }) + } + + return colorScale +} +/** + * 过滤掉不在区间的数据 + * @param data + * @param maxValue + * @param minValue + */ +export const filterChartDataByRange = (data: any[], maxValue: number, minValue: number) => { + return data.filter( + item => + item.value === null || + item.value === undefined || + (item.value >= minValue && item.value <= maxValue) + ) +} + +/** + * 获取数据最大最小值 + * @param data + * @param field 值字段 + * @param maxValue + * @param minValue + * @param callback + */ +export const getMaxAndMinValueByData = ( + data: any[], + field: string, + maxValue: number, + minValue: number, + callback: (max: number, min: number) => void +) => { + // 定义一个辅助函数来计算最大值或最小值 + const calculateExtreme = (isMax: boolean) => { + return data.reduce( + (extreme, current) => { + return isMax + ? current[field] > extreme + ? current[field] + : extreme + : current[field] < extreme + ? current[field] + : extreme + }, + isMax ? Number.MIN_SAFE_INTEGER : Number.MAX_SAFE_INTEGER + ) + } + if (minValue === null || maxValue === null) { + let maxResult = maxValue + let minResult = minValue + if (maxResult === null) { + maxResult = calculateExtreme(true) + } + if (minResult === null) { + minResult = calculateExtreme(false) + } + callback(maxResult, minResult) + } + if (minValue === 0 && maxValue === 0) { + const maxResult = calculateExtreme(true) + const minResult = calculateExtreme(false) + callback(maxResult, minResult) + } +} + +export const stepsColor = (start, end, steps, gamma) => { + let i + let j + let ms + let me + const output = [] + const so = [] + gamma = gamma || 1 + const normalize = function (channel) { + return Math.pow(channel / 255, gamma) + } + start = parseColor(start).map(normalize) + end = parseColor(end).map(normalize) + for (i = 0; i < steps; i++) { + ms = steps - 1 === 0 ? 0 : i / (steps - 1) + me = 1 - ms + for (j = 0; j < 3; j++) { + so[j] = pad(Math.round(Math.pow(start[j] * me + end[j] * ms, 1 / gamma) * 255).toString(16)) + } + output.push('#' + so.join('')) + } + function parseColor(hexStr) { + return hexStr.length === 4 + ? hexStr + .substr(1) + .split('') + .map(function (s) { + return 0x11 * parseInt(s, 16) + }) + : [hexStr.substr(1, 2), hexStr.substr(3, 2), hexStr.substr(5, 2)].map(function (s) { + return parseInt(s, 16) + }) + } + function pad(s) { + return s.length === 1 ? '0' + s : s + } + return output +} + +export const getMapColorCases = colorCases => { + const cloneColorCases = JSON.parse(JSON.stringify(colorCases)) + return cloneColorCases.map(colorItem => { + let curColors = colorItem.colors + if (['fresh', 'red', 'spiritual'].includes(colorItem.value)) { + curColors = colorItem.colors.reverse() + } + const len = curColors.length + const start = curColors[0] + const end = curColors[len - 1] + const itemResult = { + name: colorItem.name, + value: colorItem.value + '_split_gradient', + baseColors: [start, end], + colors: stepsColor(start, end, 9, 1) + } + return itemResult + }) +} + +export function getColor(chart: Chart) { + const basicStyle = parseJson(chart.customAttr).basicStyle + const { seriesColor } = basicStyle + if (seriesColor?.length) { + const { yAxis } = chart + const seriesMap = seriesColor.reduce((p, n) => { + p[n.id] = n + return p + }, {}) + yAxis?.forEach((axis, index) => { + const curAxisColor = seriesMap[axis.id] + if (curAxisColor) { + if (index + 1 > basicStyle.colors.length) { + basicStyle.colors.push(curAxisColor.color) + } else { + basicStyle.colors[index] = curAxisColor.color + } + } + }) + const color = basicStyle.colors.map(c => hexColorToRGBA(c, basicStyle.alpha)) + return color + } +} + +export function setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] { + const result: ChartBasicStyle['seriesColor'] = [] + const seriesSet = new Set() + const colors = chart.customAttr.basicStyle.colors + const yAxis = chart.yAxis + yAxis?.forEach(axis => { + if (seriesSet.has(axis.id)) { + return + } + seriesSet.add(axis.id) + result.push({ + id: axis.id, + name: axis.chartShowName ?? axis.name, + color: colors[(seriesSet.size - 1) % colors.length] + }) + }) + return result +} + +export function getGroupColor(chart: Chart, options: O) { + const { basicStyle } = parseJson(chart.customAttr) + const { seriesColor } = basicStyle + if (!seriesColor?.length) { + return + } + const seriesMap = seriesColor.reduce((p, n) => { + p[n.id] = n + return p + }, {}) + const { yAxis, xAxisExt } = chart + const { data } = options as unknown as Options + if (xAxisExt?.length) { + const seriesSet = new Set() + data?.forEach(d => d.category !== null && seriesSet.add(d.category)) + const tmp = [...seriesSet] + tmp.forEach((c, i) => { + const curAxisColor = seriesMap[c as string] + if (curAxisColor) { + if (i + 1 > basicStyle.colors.length) { + basicStyle.colors.push(curAxisColor.color) + } else { + basicStyle.colors[i] = curAxisColor.color + } + } + }) + } else { + yAxis?.forEach((axis, index) => { + const curAxisColor = seriesMap[axis.id] + if (curAxisColor) { + if (index + 1 > basicStyle.colors.length) { + basicStyle.colors.push(curAxisColor.color) + } else { + basicStyle.colors[index] = curAxisColor.color + } + } + }) + } + const color = basicStyle.colors.map(c => hexColorToRGBA(c, basicStyle.alpha)) + return color +} + +export function setUpGroupSeriesColor( + chart: ChartObj, + data?: any[] +): ChartBasicStyle['seriesColor'] { + const result: ChartBasicStyle['seriesColor'] = [] + const seriesSet = new Set() + const colors = chart.customAttr.basicStyle.colors + const { yAxis, xAxisExt } = chart + if (xAxisExt?.length) { + data?.forEach(d => { + if (d.value === null || d.category === null || seriesSet.has(d.category)) { + return + } + seriesSet.add(d.category) + result.push({ + id: d.category, + name: d.category, + color: colors[(seriesSet.size - 1) % colors.length] + }) + }) + } else { + yAxis?.forEach(axis => { + if (seriesSet.has(axis.id)) { + return + } + seriesSet.add(axis.id) + result.push({ + id: axis.id, + name: axis.chartShowName ?? axis.name, + color: colors[(seriesSet.size - 1) % colors.length] + }) + }) + } + return result +} + +export function getStackColor(chart: Chart, options: O) { + const { basicStyle } = parseJson(chart.customAttr) + const { seriesColor } = basicStyle + if (!seriesColor?.length) { + return + } + const seriesMap = seriesColor.reduce((p, n) => { + p[n.id] = n + return p + }, {}) + const { yAxis, extStack } = chart + const { data } = options as unknown as Options + if (extStack?.length) { + const seriesSet = new Set() + data?.forEach(d => d.category !== null && seriesSet.add(d.category)) + const tmp = [...seriesSet] + tmp.forEach((c, i) => { + const curAxisColor = seriesMap[c as string] + if (curAxisColor) { + if (i + 1 > basicStyle.colors.length) { + basicStyle.colors.push(curAxisColor.color) + } else { + basicStyle.colors[i] = curAxisColor.color + } + } + }) + } else { + yAxis?.forEach((axis, index) => { + const curAxisColor = seriesMap[axis.id] + if (curAxisColor) { + if (index + 1 > basicStyle.colors.length) { + basicStyle.colors.push(curAxisColor.color) + } else { + basicStyle.colors[index] = curAxisColor.color + } + } + }) + } + const color = basicStyle.colors.map(c => hexColorToRGBA(c, basicStyle.alpha)) + return color +} + +export function setUpStackSeriesColor( + chart: ChartObj, + data?: any[] +): ChartBasicStyle['seriesColor'] { + const result: ChartBasicStyle['seriesColor'] = [] + const seriesSet = new Set() + const colors = chart.customAttr.basicStyle.colors + const { yAxis, extStack } = chart + if (extStack?.length) { + data?.forEach(d => { + if (d.value === null || d.category === null || seriesSet.has(d.category)) { + return + } + seriesSet.add(d.category) + result.push({ + id: d.category, + name: d.category, + color: colors[(seriesSet.size - 1) % colors.length] + }) + }) + } else { + yAxis?.forEach(axis => { + if (seriesSet.has(axis.id)) { + return + } + seriesSet.add(axis.id) + result.push({ + id: axis.id, + name: axis.chartShowName ?? axis.name, + color: colors[(seriesSet.size - 1) % colors.length] + }) + }) + } + return result +} + +export function getSingleDimensionColor(chart: Chart, options: O) { + const { basicStyle } = parseJson(chart.customAttr) + const { seriesColor } = basicStyle + if (!seriesColor?.length) { + return + } + const seriesMap = seriesColor.reduce((p, n) => { + p[n.id] = n + return p + }, {}) + const { xAxis, yAxis } = chart + const { data } = options as unknown as Options + if (xAxis?.length && yAxis?.length) { + const seriesSet = new Set() + data?.forEach(d => d.field !== null && seriesSet.add(d.field)) + const tmp = [...seriesSet] + tmp.forEach((c, i) => { + const curAxisColor = seriesMap[c as string] + if (curAxisColor) { + if (i + 1 > basicStyle.colors.length) { + basicStyle.colors.push(curAxisColor.color) + } else { + basicStyle.colors[i] = curAxisColor.color + } + } + }) + } + const color = basicStyle.colors.map(c => hexColorToRGBA(c, basicStyle.alpha)) + return color +} + +export function setUpSingleDimensionSeriesColor( + chart: ChartObj, + data?: any[] +): ChartBasicStyle['seriesColor'] { + const result: ChartBasicStyle['seriesColor'] = [] + const seriesSet = new Set() + const colors = chart.customAttr.basicStyle.colors + const { xAxis, yAxis } = chart + if (!(xAxis?.length && yAxis?.length)) { + return result + } + data?.forEach(item => { + if (seriesSet.has(item.field)) { + return + } + seriesSet.add(item.field) + result.push({ + id: item.field, + name: item.field, + color: colors[(seriesSet.size - 1) % colors.length] + }) + }) + return result +} + +export function isAlphaColor(color: string): boolean { + if (!color?.trim()) { + return false + } + if (color.startsWith('#')) { + return color.length === 9 + } + if (color.startsWith('rgb')) { + return color.split(',').length === 4 + } + return false +} + +export function getColorFormAlphaColor(color: string): string { + if (isAlphaColor(color)) { + if (color.startsWith('#')) { + return color.slice(0, 7) + } + if (color.startsWith('rgb') || color.startsWith('RGB')) { + const list = color.split(',') + return list[0] + ',' + list[1] + ',' + list[2] + ')' + } + } + return color +} + +export function isTransparent(color: string): boolean { + if (!color?.trim()) { + return true + } + if (color.startsWith('#')) { + const tmp = color.substring(1, color.length) + if (tmp.length === 3 || tmp.length === 6) { + return false + } + if (tmp.length === 8) { + return tmp.substring(6, 8) === '00' + } + } + if (color.startsWith('rgb')) { + const tmp = color.split(',') + if (tmp.length !== 4) { + return false + } + const alpha = tmp[3].substring(0, tmp[3].length - 1) + return alpha.trim() === '0' + } + return false +} + +export function convertToAlphaColor(color: string, alpha: number): string { + if (!color?.trim()) { + return 'rgba(255,255,255,1)' + } + if (color.startsWith('#')) { + let colorStr = color.trim().substring(1) + if (colorStr.length === 3) { + const tmp = colorStr.split('') + colorStr = `${tmp[0]}${tmp[0]}${tmp[1]}${tmp[1]}${tmp[2]}${tmp[2]}` + } + if (colorStr.length !== 6) { + return '#FFFFFFFF' + } + const alphaHex = parseInt((alpha * 2.55).toFixed(0)) + .toString(16) + .toUpperCase() + return `#${colorStr}${alphaHex}` + } + if (color.startsWith('rgb')) { + const rgb = color.match(/\d+/g) + return `rgba(${rgb.join(',')},${alpha / 100})` + } + return 'rgba(255,255,255,1)' +} + +export function svgStrToUrl(svgStr: string): string { + let file = '' + try { + if (svgStr) { + const blob = new Blob([svgStr], { type: 'image/svg+xml' }) + file = URL.createObjectURL(blob) + } + } catch (e) {} + return file +} + +/** + * 获取非空数据的最小值 + * @param sourceData + * @param field + * @private + */ +export function filterEmptyMinValue(sourceData, field) { + let notEmptyMinValue = 0 + getMaxAndMinValueByData( + sourceData.filter(item => item[field]), + 'value', + 0, + 0, + (max, min) => { + notEmptyMinValue = min + } + ) + return notEmptyMinValue +} + +/** + * 获取折线条件样式 + * @param chart + */ +export function getLineConditions(chart) { + const { threshold } = parseJson(chart.senior) + const conditions = [] + if (threshold.enable) { + threshold.lineThreshold?.forEach(item => + item.conditions?.forEach(c => + conditions.push({ + fieldId: item.fieldId, + term: c.term, + value: c.value, + color: c.color, + min: c.min, + max: c.max + }) + ) + ) + } + return conditions +} + +/** + * 根据折线阈值条件获取新的标签颜色 + * @param conditions + * @param value + * @param fieldId + */ +export function getLineLabelColorByCondition(conditions, value, fieldId) { + const fieldConditions = conditions.filter(item => item.fieldId === fieldId) + let color = undefined + if (fieldConditions.length) { + fieldConditions.some(item => { + if ( + (item.term === 'lt' && value <= item.value) || + (item.term === 'gt' && value >= item.value) || + (item.term === 'between' && value >= item.min && value <= item.max) + ) { + color = item.color + return true + } + }) + } + return color +} + +/** + * 获取文本在画布中的测量信息 + * @param chart 图表内容 + * @param text 测量文本 + * @param font 文本样式 + * @param type 测量类型,高度宽度 + **/ +export const measureText = (chart, text, font, type) => { + const container = document.getElementById(chart.container) + const canvas = container.querySelector('canvas') + const ctx = canvas.getContext('2d') + const { fontWeight, fontSize, fontFamily } = font + ctx.font = [fontWeight, `${fontSize}px`, fontFamily].join(' ').trim() + const textMetrics = ctx.measureText(text) + if (type === 'height') { + return textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent + } + if (type === 'width') { + return textMetrics.actualBoundingBoxRight + textMetrics.actualBoundingBoxLeft + } + return 0 +} + +/** + * 获取十六进制颜色值 + * @param hex + * @param alpha + */ +export const hexToRgba = (hex, alpha = 1) => { + if (!hex.startsWith('#')) { + return hex + } + // 去掉 # 号 + hex = hex.replace('#', '') + // 转换为 RGB 分量 + const r = parseInt(hex.slice(0, 2), 16) + const g = parseInt(hex.slice(2, 4), 16) + const b = parseInt(hex.slice(4, 6), 16) + const hexAlpha = hex.slice(6, 8) + const a = hexAlpha ? parseInt(hex.slice(6, 8), 16) / 255 : alpha + // 返回 RGBA 格式 + return `rgba(${r}, ${g}, ${b}, ${a})` +} diff --git a/frontend/src/data-visualization/chart/components/views/components/ChartComponentG2Plot.vue b/frontend/src/data-visualization/chart/components/views/components/ChartComponentG2Plot.vue new file mode 100644 index 0000000..850343f --- /dev/null +++ b/frontend/src/data-visualization/chart/components/views/components/ChartComponentG2Plot.vue @@ -0,0 +1,737 @@ + + + + + diff --git a/frontend/src/data-visualization/chart/components/views/components/ChartComponentS2.vue b/frontend/src/data-visualization/chart/components/views/components/ChartComponentS2.vue new file mode 100644 index 0000000..b7fb246 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/views/components/ChartComponentS2.vue @@ -0,0 +1,831 @@ + + + + + + diff --git a/frontend/src/data-visualization/chart/components/views/components/ChartEmptyInfo.vue b/frontend/src/data-visualization/chart/components/views/components/ChartEmptyInfo.vue new file mode 100644 index 0000000..0468144 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/views/components/ChartEmptyInfo.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/frontend/src/data-visualization/chart/components/views/components/ChartError.vue b/frontend/src/data-visualization/chart/components/views/components/ChartError.vue new file mode 100644 index 0000000..ac3325a --- /dev/null +++ b/frontend/src/data-visualization/chart/components/views/components/ChartError.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/frontend/src/data-visualization/chart/components/views/components/DrillPath.vue b/frontend/src/data-visualization/chart/components/views/components/DrillPath.vue new file mode 100644 index 0000000..b40a787 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/views/components/DrillPath.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/frontend/src/data-visualization/chart/components/views/index.vue b/frontend/src/data-visualization/chart/components/views/index.vue new file mode 100644 index 0000000..add19a2 --- /dev/null +++ b/frontend/src/data-visualization/chart/components/views/index.vue @@ -0,0 +1,1283 @@ + + + + + diff --git a/frontend/src/data-visualization/chart/components/views/util/util.ts b/frontend/src/data-visualization/chart/components/views/util/util.ts new file mode 100644 index 0000000..c389d0a --- /dev/null +++ b/frontend/src/data-visualization/chart/components/views/util/util.ts @@ -0,0 +1,5 @@ +export const reverseColor = colorValue => { + colorValue = '0x' + colorValue.replace(/#/g, '') + const str = '000000' + (0xffffff - colorValue).toString(16) + return '#' + str.substring(str.length - 6, str.length) +} diff --git a/frontend/src/data-visualization/components/collapse-switch-item/index.ts b/frontend/src/data-visualization/components/collapse-switch-item/index.ts new file mode 100644 index 0000000..17ad731 --- /dev/null +++ b/frontend/src/data-visualization/components/collapse-switch-item/index.ts @@ -0,0 +1,2 @@ +import CollapseSwitchItem from './src/CollapseSwitchItem.vue' +export { CollapseSwitchItem } diff --git a/frontend/src/data-visualization/components/collapse-switch-item/src/CollapseSwitchItem.vue b/frontend/src/data-visualization/components/collapse-switch-item/src/CollapseSwitchItem.vue new file mode 100644 index 0000000..046ce69 --- /dev/null +++ b/frontend/src/data-visualization/components/collapse-switch-item/src/CollapseSwitchItem.vue @@ -0,0 +1,88 @@ + + + diff --git a/frontend/src/data-visualization/components/config-global/index.ts b/frontend/src/data-visualization/components/config-global/index.ts new file mode 100644 index 0000000..dda2462 --- /dev/null +++ b/frontend/src/data-visualization/components/config-global/index.ts @@ -0,0 +1,3 @@ +import ConfigGlobal from './src/ConfigGlobal.vue' + +export { ConfigGlobal } diff --git a/frontend/src/data-visualization/components/config-global/src/ConfigGlobal.vue b/frontend/src/data-visualization/components/config-global/src/ConfigGlobal.vue new file mode 100644 index 0000000..a567947 --- /dev/null +++ b/frontend/src/data-visualization/components/config-global/src/ConfigGlobal.vue @@ -0,0 +1,16 @@ + + + diff --git a/frontend/src/data-visualization/components/data-visualization/DeGrid.vue b/frontend/src/data-visualization/components/data-visualization/DeGrid.vue new file mode 100644 index 0000000..306e7b5 --- /dev/null +++ b/frontend/src/data-visualization/components/data-visualization/DeGrid.vue @@ -0,0 +1,92 @@ + + + + + diff --git a/frontend/src/data-visualization/components/data-visualization/DeGridScreen.vue b/frontend/src/data-visualization/components/data-visualization/DeGridScreen.vue new file mode 100644 index 0000000..a36f7f9 --- /dev/null +++ b/frontend/src/data-visualization/components/data-visualization/DeGridScreen.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/frontend/src/data-visualization/components/data-visualization/canvas/Area.vue b/frontend/src/data-visualization/components/data-visualization/canvas/Area.vue new file mode 100644 index 0000000..d425fdb --- /dev/null +++ b/frontend/src/data-visualization/components/data-visualization/canvas/Area.vue @@ -0,0 +1,44 @@ + + + + diff --git a/frontend/src/data-visualization/components/data-visualization/canvas/CanvasCore.vue b/frontend/src/data-visualization/components/data-visualization/canvas/CanvasCore.vue new file mode 100644 index 0000000..1865f98 --- /dev/null +++ b/frontend/src/data-visualization/components/data-visualization/canvas/CanvasCore.vue @@ -0,0 +1,1761 @@ + + + + + diff --git a/frontend/src/data-visualization/components/data-visualization/canvas/ComponentWrapper.vue b/frontend/src/data-visualization/components/data-visualization/canvas/ComponentWrapper.vue new file mode 100644 index 0000000..0f999e7 --- /dev/null +++ b/frontend/src/data-visualization/components/data-visualization/canvas/ComponentWrapper.vue @@ -0,0 +1,478 @@ + + + + + diff --git a/frontend/src/data-visualization/components/data-visualization/canvas/ComposeShow.vue b/frontend/src/data-visualization/components/data-visualization/canvas/ComposeShow.vue new file mode 100644 index 0000000..267a4e1 --- /dev/null +++ b/frontend/src/data-visualization/components/data-visualization/canvas/ComposeShow.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/frontend/src/data-visualization/components/data-visualization/canvas/ContextMenu.vue b/frontend/src/data-visualization/components/data-visualization/canvas/ContextMenu.vue new file mode 100644 index 0000000..0e42cdd --- /dev/null +++ b/frontend/src/data-visualization/components/data-visualization/canvas/ContextMenu.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/frontend/src/data-visualization/components/data-visualization/canvas/ContextMenuAsideDetails.vue b/frontend/src/data-visualization/components/data-visualization/canvas/ContextMenuAsideDetails.vue new file mode 100644 index 0000000..2e8501d --- /dev/null +++ b/frontend/src/data-visualization/components/data-visualization/canvas/ContextMenuAsideDetails.vue @@ -0,0 +1,21 @@ + + + diff --git a/frontend/src/data-visualization/components/data-visualization/canvas/ContextMenuDetails.vue b/frontend/src/data-visualization/components/data-visualization/canvas/ContextMenuDetails.vue new file mode 100644 index 0000000..1ad3dae --- /dev/null +++ b/frontend/src/data-visualization/components/data-visualization/canvas/ContextMenuDetails.vue @@ -0,0 +1,411 @@ + + + + + diff --git a/frontend/src/data-visualization/components/data-visualization/canvas/DePreview.vue b/frontend/src/data-visualization/components/data-visualization/canvas/DePreview.vue new file mode 100644 index 0000000..5f1d24b --- /dev/null +++ b/frontend/src/data-visualization/components/data-visualization/canvas/DePreview.vue @@ -0,0 +1,533 @@ + + + + + diff --git a/frontend/src/data-visualization/components/data-visualization/canvas/DragShadow.vue b/frontend/src/data-visualization/components/data-visualization/canvas/DragShadow.vue new file mode 100644 index 0000000..59ecbb2 --- /dev/null +++ b/frontend/src/data-visualization/components/data-visualization/canvas/DragShadow.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/frontend/src/data-visualization/components/data-visualization/canvas/MarkLine.vue b/frontend/src/data-visualization/components/data-visualization/canvas/MarkLine.vue new file mode 100644 index 0000000..b67239a --- /dev/null +++ b/frontend/src/data-visualization/components/data-visualization/canvas/MarkLine.vue @@ -0,0 +1,261 @@ + + + + diff --git a/frontend/src/data-visualization/components/data-visualization/canvas/PGrid.vue b/frontend/src/data-visualization/components/data-visualization/canvas/PGrid.vue new file mode 100644 index 0000000..86bcce4 --- /dev/null +++ b/frontend/src/data-visualization/components/data-visualization/canvas/PGrid.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/frontend/src/data-visualization/components/data-visualization/canvas/PointShadow.vue b/frontend/src/data-visualization/components/data-visualization/canvas/PointShadow.vue new file mode 100644 index 0000000..65a5e42 --- /dev/null +++ b/frontend/src/data-visualization/components/data-visualization/canvas/PointShadow.vue @@ -0,0 +1,72 @@ + + + + diff --git a/frontend/src/data-visualization/components/data-visualization/canvas/Shape.vue b/frontend/src/data-visualization/components/data-visualization/canvas/Shape.vue new file mode 100644 index 0000000..3f11e84 --- /dev/null +++ b/frontend/src/data-visualization/components/data-visualization/canvas/Shape.vue @@ -0,0 +1,1321 @@ + + + + + diff --git a/frontend/src/data-visualization/components/de-board/Board.vue b/frontend/src/data-visualization/components/de-board/Board.vue new file mode 100644 index 0000000..bf16732 --- /dev/null +++ b/frontend/src/data-visualization/components/de-board/Board.vue @@ -0,0 +1,65 @@ + + + + diff --git a/frontend/src/data-visualization/components/empty-background/index.ts b/frontend/src/data-visualization/components/empty-background/index.ts new file mode 100644 index 0000000..61ce840 --- /dev/null +++ b/frontend/src/data-visualization/components/empty-background/index.ts @@ -0,0 +1,3 @@ +import EmptyBackground from './src/EmptyBackground.vue' + +export { EmptyBackground } diff --git a/frontend/src/data-visualization/components/empty-background/src/EmptyBackground.vue b/frontend/src/data-visualization/components/empty-background/src/EmptyBackground.vue new file mode 100644 index 0000000..2e47e35 --- /dev/null +++ b/frontend/src/data-visualization/components/empty-background/src/EmptyBackground.vue @@ -0,0 +1,66 @@ + + + + + diff --git a/frontend/src/data-visualization/components/icon-custom/index.ts b/frontend/src/data-visualization/components/icon-custom/index.ts new file mode 100644 index 0000000..fb5a732 --- /dev/null +++ b/frontend/src/data-visualization/components/icon-custom/index.ts @@ -0,0 +1,9 @@ +import { h } from 'vue' +import { ElIcon } from 'element-plus-secondary' +import Icon from './src/Icon.vue' +const hIcon = (name: string) => { + return h(ElIcon, null, { + default: () => h(name) + }) +} +export { Icon, hIcon } diff --git a/frontend/src/data-visualization/components/icon-custom/src/Icon.vue b/frontend/src/data-visualization/components/icon-custom/src/Icon.vue new file mode 100644 index 0000000..6319d77 --- /dev/null +++ b/frontend/src/data-visualization/components/icon-custom/src/Icon.vue @@ -0,0 +1,45 @@ + + + + diff --git a/frontend/src/data-visualization/components/icon-group/board-list.ts b/frontend/src/data-visualization/components/icon-group/board-list.ts new file mode 100644 index 0000000..29a6032 --- /dev/null +++ b/frontend/src/data-visualization/components/icon-group/board-list.ts @@ -0,0 +1,22 @@ +import board_1 from '@/assets/svg/board_1.svg' +import board_2 from '@/assets/svg/board_2.svg' +import board_3 from '@/assets/svg/board_3.svg' +import board_4 from '@/assets/svg/board_4.svg' +import board_5 from '@/assets/svg/board_5.svg' +import board_6 from '@/assets/svg/board_6.svg' +import board_7 from '@/assets/svg/board_7.svg' +import board_8 from '@/assets/svg/board_8.svg' +import board_9 from '@/assets/svg/board_9.svg' + +const iconBoardMap = { + board_1: board_1, + board_2: board_2, + board_3: board_3, + board_4: board_4, + board_5: board_5, + board_6: board_6, + board_7: board_7, + board_8: board_8, + board_9: board_9 +} +export { iconBoardMap } diff --git a/frontend/src/data-visualization/components/icon-group/chart-dark-list.ts b/frontend/src/data-visualization/components/icon-group/chart-dark-list.ts new file mode 100644 index 0000000..600db62 --- /dev/null +++ b/frontend/src/data-visualization/components/icon-group/chart-dark-list.ts @@ -0,0 +1,95 @@ +import areaDark from '@/assets/svg/area-dark.svg' +import areaStackDark from '@/assets/svg/area-stack-dark.svg' +import barDark from '@/assets/svg/bar-dark.svg' +import barGroupDark from '@/assets/svg/bar-group-dark.svg' +import barGroupStackDark from '@/assets/svg/bar-group-stack-dark.svg' +import barHorizontalDark from '@/assets/svg/bar-horizontal-dark.svg' +import barRangeDark from '@/assets/svg/bar-range-dark.svg' +import barStackDark from '@/assets/svg/bar-stack-dark.svg' +import barStackHorizontalDark from '@/assets/svg/bar-stack-horizontal-dark.svg' +import bidirectionalBarDark from '@/assets/svg/bidirectional-bar-dark.svg' +import bubbleMapDark from '@/assets/svg/bubble-map-dark.svg' +import chartMixDark from '@/assets/svg/chart-mix-dark.svg' +import chartMixGroupDark from '@/assets/svg/chart-mix-group-dark.svg' +import chartMixStackDark from '@/assets/svg/chart-mix-stack-dark.svg' +import chartMixDualLineDark from '@/assets/svg/chart-mix-dual-line-dark.svg' +import flowMapDark from '@/assets/svg/flow-map-dark.svg' +import funnelDark from '@/assets/svg/funnel-dark.svg' +import gaugeDark from '@/assets/svg/gauge-dark.svg' +import heatMapDark from '@/assets/svg/heat-map-dark.svg' +import indicatorDark from '@/assets/svg/indicator-dark.svg' +import lineDark from '@/assets/svg/line-dark.svg' +import liquidDark from '@/assets/svg/liquid-dark.svg' +import mapDark from '@/assets/svg/map-dark.svg' +import percentageBarStackDark from '@/assets/svg/percentage-bar-stack-dark.svg' +import percentageBarStackHorizontalDark from '@/assets/svg/percentage-bar-stack-horizontal-dark.svg' +import pieDark from '@/assets/svg/pie-dark.svg' +import pieDonutDark from '@/assets/svg/pie-donut-dark.svg' +import pieDonutRoseDark from '@/assets/svg/pie-donut-rose-dark.svg' +import pieRoseDark from '@/assets/svg/pie-rose-dark.svg' +import progressBarDark from '@/assets/svg/progress-bar-dark.svg' +import quadrantDark from '@/assets/svg/quadrant-dark.svg' +import radarDark from '@/assets/svg/radar-dark.svg' +import richTextDark from '@/assets/svg/rich-text-dark.svg' +import sankeyDark from '@/assets/svg/sankey-dark.svg' +import scatterDark from '@/assets/svg/scatter-dark.svg' +import stockLineDark from '@/assets/svg/stock-line-dark.svg' +import symbolicMapDark from '@/assets/svg/symbolic-map-dark.svg' +import tableInfoDark from '@/assets/svg/table-info-dark.svg' +import tableNormalDark from '@/assets/svg/table-normal-dark.svg' +import tablePivotDark from '@/assets/svg/table-pivot-dark.svg' +import treemapDark from '@/assets/svg/treemap-dark.svg' +import waterfallDark from '@/assets/svg/waterfall-dark.svg' +import wordCloudDark from '@/assets/svg/word-cloud-dark.svg' +import tHeatmapDark from '@/assets/svg/t-heatmap-dark.svg' +import circlePackingDark from '@/assets/svg/circle-packing-dark.svg' + +const iconChartDarkMap = { + 'area-dark': areaDark, + 'area-stack-dark': areaStackDark, + 'bar-dark': barDark, + 'bar-group-dark': barGroupDark, + 'bar-group-stack-dark': barGroupStackDark, + 'bar-horizontal-dark': barHorizontalDark, + 'bar-range-dark': barRangeDark, + 'bar-stack-dark': barStackDark, + 'bar-stack-horizontal-dark': barStackHorizontalDark, + 'bidirectional-bar-dark': bidirectionalBarDark, + 'bubble-map-dark': bubbleMapDark, + 'chart-mix-dark': chartMixDark, + 'chart-mix-group-dark': chartMixGroupDark, + 'chart-mix-stack-dark': chartMixStackDark, + 'chart-mix-dual-line-dark': chartMixDualLineDark, + 'flow-map-dark': flowMapDark, + 'funnel-dark': funnelDark, + 'gauge-dark': gaugeDark, + 'heat-map-dark': heatMapDark, + 'indicator-dark': indicatorDark, + 'line-dark': lineDark, + 'liquid-dark': liquidDark, + 'map-dark': mapDark, + 'percentage-bar-stack-dark': percentageBarStackDark, + 'percentage-bar-stack-horizontal-dark': percentageBarStackHorizontalDark, + 'pie-dark': pieDark, + 'pie-donut-dark': pieDonutDark, + 'pie-donut-rose-dark': pieDonutRoseDark, + 'pie-rose-dark': pieRoseDark, + 'progress-bar-dark': progressBarDark, + 'quadrant-dark': quadrantDark, + 'radar-dark': radarDark, + 'rich-text-dark': richTextDark, + 'sankey-dark': sankeyDark, + 'scatter-dark': scatterDark, + 'stock-line-dark': stockLineDark, + 'symbolic-map-dark': symbolicMapDark, + 'table-info-dark': tableInfoDark, + 'table-normal-dark': tableNormalDark, + 'table-pivot-dark': tablePivotDark, + 'treemap-dark': treemapDark, + 'waterfall-dark': waterfallDark, + 'word-cloud-dark': wordCloudDark, + 't-heatmap-dark': tHeatmapDark, + 'circle-packing-dark': circlePackingDark +} + +export { iconChartDarkMap } diff --git a/frontend/src/data-visualization/components/icon-group/chart-list.ts b/frontend/src/data-visualization/components/icon-group/chart-list.ts new file mode 100644 index 0000000..02e6ba9 --- /dev/null +++ b/frontend/src/data-visualization/components/icon-group/chart-list.ts @@ -0,0 +1,101 @@ +import areaStack from '@/assets/svg/area-stack.svg' +import area from '@/assets/svg/area.svg' +import barGroupStack from '@/assets/svg/bar-group-stack.svg' +import barGroup from '@/assets/svg/bar-group.svg' +import barHorizontal from '@/assets/svg/bar-horizontal.svg' +import barRange from '@/assets/svg/bar-range.svg' +import barStackHorizontal from '@/assets/svg/bar-stack-horizontal.svg' +import barStack from '@/assets/svg/bar-stack.svg' +import bar from '@/assets/svg/bar.svg' +import bidirectionalBar from '@/assets/svg/bidirectional-bar.svg' +import bubbleMap from '@/assets/svg/bubble-map.svg' +import chartMixGroup from '@/assets/svg/chart-mix-group.svg' +import chartMixStack from '@/assets/svg/chart-mix-stack.svg' +import chartMixDualLine from '@/assets/svg/chart-mix-dual-line.svg' +import chartMix from '@/assets/svg/chart-mix.svg' +import flowMap from '@/assets/svg/flow-map.svg' +import funnel from '@/assets/svg/funnel.svg' +import gauge from '@/assets/svg/gauge.svg' +import heatMap from '@/assets/svg/heat-map.svg' +import indicator from '@/assets/svg/indicator.svg' +import line from '@/assets/svg/line.svg' +import liquid from '@/assets/svg/liquid.svg' +import map from '@/assets/svg/map.svg' +import percentageBarStackHorizontal from '@/assets/svg/percentage-bar-stack-horizontal.svg' +import percentageBarStack from '@/assets/svg/percentage-bar-stack.svg' +import pieDonutRose from '@/assets/svg/pie-donut-rose.svg' +import pieDonut from '@/assets/svg/pie-donut.svg' +import pieRose from '@/assets/svg/pie-rose.svg' +import pie from '@/assets/svg/pie.svg' +import progressBar from '@/assets/svg/progress-bar.svg' +import quadrant from '@/assets/svg/quadrant.svg' +import radar from '@/assets/svg/radar.svg' +import richText from '@/assets/svg/rich-text.svg' +import sankey from '@/assets/svg/sankey.svg' +import scatter from '@/assets/svg/scatter.svg' +import stockLine from '@/assets/svg/stock-line.svg' +import symbolicMap from '@/assets/svg/symbolic-map.svg' +import tableInfo from '@/assets/svg/table-info.svg' +import tableNormal from '@/assets/svg/table-normal.svg' +import tablePivot from '@/assets/svg/table-pivot.svg' +import treemap from '@/assets/svg/treemap.svg' +import waterfall from '@/assets/svg/waterfall.svg' +import wordCloud from '@/assets/svg/word-cloud.svg' +import tHeatmap from '@/assets/svg/t-heatmap.svg' +import pictureGroup from '@/assets/svg/picture-group.svg' +import filter from '@/assets/svg/filter.svg' +import outerParams from '@/assets/svg/icon_params_setting.svg' +import circlePacking from '@/assets/svg/circle-packing.svg' + +const iconChartMap = { + 'area-stack': areaStack, + area: area, + 'bar-group-stack': barGroupStack, + 'bar-group': barGroup, + 'bar-horizontal': barHorizontal, + 'bar-range': barRange, + 'bar-stack-horizontal': barStackHorizontal, + 'bar-stack': barStack, + bar: bar, + 'bidirectional-bar': bidirectionalBar, + 'bubble-map': bubbleMap, + 'chart-mix-group': chartMixGroup, + 'chart-mix-stack': chartMixStack, + 'chart-mix-dual-line': chartMixDualLine, + 'chart-mix': chartMix, + 'flow-map': flowMap, + funnel: funnel, + gauge: gauge, + 'heat-map': heatMap, + indicator: indicator, + line: line, + liquid: liquid, + map: map, + 'percentage-bar-stack-horizontal': percentageBarStackHorizontal, + 'percentage-bar-stack': percentageBarStack, + 'pie-donut-rose': pieDonutRose, + 'pie-donut': pieDonut, + 'pie-rose': pieRose, + pie: pie, + 'progress-bar': progressBar, + quadrant: quadrant, + radar: radar, + 'rich-text': richText, + sankey: sankey, + scatter: scatter, + 'stock-line': stockLine, + 'symbolic-map': symbolicMap, + 'table-info': tableInfo, + 'table-normal': tableNormal, + 'table-pivot': tablePivot, + treemap: treemap, + waterfall: waterfall, + 'word-cloud': wordCloud, + 't-heatmap': tHeatmap, + 'picture-group': pictureGroup, + filter: filter, + outerParams: outerParams, + 'circle-packing': circlePacking +} + +export { iconChartMap } diff --git a/frontend/src/data-visualization/components/icon-group/datasource-list.ts b/frontend/src/data-visualization/components/icon-group/datasource-list.ts new file mode 100644 index 0000000..2c86934 --- /dev/null +++ b/frontend/src/data-visualization/components/icon-group/datasource-list.ts @@ -0,0 +1,36 @@ +import mysqlDs from '@/assets/svg/mysql-ds.svg' +import oracleDs from '@/assets/svg/oracle-ds.svg' +import sqlServerDs from '@/assets/svg/sqlServer-ds.svg' +import TiDBDs from '@/assets/svg/TiDB-ds.svg' +import impalaDs from '@/assets/svg/impala-ds.svg' +import mariadbDs from '@/assets/svg/mariadb-ds.svg' +import StarRocksDs from '@/assets/svg/StarRocks-ds.svg' +import pgDs from '@/assets/svg/pg-ds.svg' +import mongoDs from '@/assets/svg/mongo-ds.svg' +import ckDs from '@/assets/svg/ck-ds.svg' +import db2Ds from '@/assets/svg/db2-ds.svg' +import redshiftDs from '@/assets/svg/redshift-ds.svg' +import APIDs from '@/assets/svg/API-ds.svg' +import ExcelDs from '@/assets/svg/Excel-ds.svg' +import dorisDs from '@/assets/svg/doris-ds.svg' +import esDs from '@/assets/svg/es-ds.svg' +const iconDatasourceMap = { + mysql: mysqlDs, + oracle: oracleDs, + sqlServer: sqlServerDs, + TiDB: TiDBDs, + impala: impalaDs, + mariadb: mariadbDs, + StarRocks: StarRocksDs, + pg: pgDs, + mongo: mongoDs, + ck: ckDs, + db2: db2Ds, + redshift: redshiftDs, + API: APIDs, + Excel: ExcelDs, + doris: dorisDs, + es: esDs +} + +export { iconDatasourceMap } diff --git a/frontend/src/data-visualization/components/icon-group/field-calculated-list.ts b/frontend/src/data-visualization/components/icon-group/field-calculated-list.ts new file mode 100644 index 0000000..583cd83 --- /dev/null +++ b/frontend/src/data-visualization/components/icon-group/field-calculated-list.ts @@ -0,0 +1,33 @@ +import icon_link_calculated_outlined from '@/assets/svg/icon_link-calculated_outlined.svg' +import icon_link_calculated_outlined_1 from '@/assets/svg/icon_link-calculated_outlined-1.svg' +import icon_text_calculated_outlined from '@/assets/svg/icon_text-calculated_outlined.svg' +import icon_text_calculated_outlined_1 from '@/assets/svg/icon_text-calculated_outlined-1.svg' +import icon_number_calculated_outlined from '@/assets/svg/icon_number-calculated_outlined.svg' +import icon_number_calculated_outlined_1 from '@/assets/svg/icon_number-calculated_outlined-1.svg' +import icon_local_calculated_outlined from '@/assets/svg/icon_local-calculated_outlined.svg' +import icon_local_calculated_outlined_1 from '@/assets/svg/icon_local-calculated_outlined-1.svg' +import icon_calendar_calculated_outlined from '@/assets/svg/icon_calendar-calculated_outlined.svg' +import icon_calendar_calculated_outlined_1 from '@/assets/svg/icon_calendar-calculated_outlined-1.svg' +const iconFieldCalculatedMap = [ + icon_text_calculated_outlined, + icon_calendar_calculated_outlined, + icon_number_calculated_outlined, + icon_number_calculated_outlined, + icon_number_calculated_outlined, + icon_local_calculated_outlined, + 'binary', + icon_link_calculated_outlined +] + +const iconFieldCalculatedQMap = [ + icon_text_calculated_outlined_1, + icon_calendar_calculated_outlined_1, + icon_number_calculated_outlined_1, + icon_number_calculated_outlined_1, + icon_number_calculated_outlined_1, + icon_local_calculated_outlined_1, + 'binary', + icon_link_calculated_outlined_1 +] + +export { iconFieldCalculatedMap, iconFieldCalculatedQMap } diff --git a/frontend/src/data-visualization/components/icon-group/field-list.ts b/frontend/src/data-visualization/components/icon-group/field-list.ts new file mode 100644 index 0000000..784ae35 --- /dev/null +++ b/frontend/src/data-visualization/components/icon-group/field-list.ts @@ -0,0 +1,14 @@ +import field_text from '@/assets/svg/field_text.svg' +import field_time from '@/assets/svg/field_time.svg' +import field_value from '@/assets/svg/field_value.svg' +import field_location from '@/assets/svg/field_location.svg' +import field_url from '@/assets/svg/field_url.svg' +const iconFieldMap = { + text: field_text, + value: field_value, + location: field_location, + time: field_time, + url: field_url +} + +export { iconFieldMap } diff --git a/frontend/src/data-visualization/components/plugin/index.ts b/frontend/src/data-visualization/components/plugin/index.ts new file mode 100644 index 0000000..6e790b2 --- /dev/null +++ b/frontend/src/data-visualization/components/plugin/index.ts @@ -0,0 +1,4 @@ +import XpackComponent from './src/index.vue' +import PluginComponent from './src/PluginComponent.vue' + +export { XpackComponent, PluginComponent } diff --git a/frontend/src/data-visualization/components/plugin/src/PluginComponent.vue b/frontend/src/data-visualization/components/plugin/src/PluginComponent.vue new file mode 100644 index 0000000..1286382 --- /dev/null +++ b/frontend/src/data-visualization/components/plugin/src/PluginComponent.vue @@ -0,0 +1,157 @@ + + + + + diff --git a/frontend/src/data-visualization/components/plugin/src/convert.js b/frontend/src/data-visualization/components/plugin/src/convert.js new file mode 100644 index 0000000..4dbf2b0 --- /dev/null +++ b/frontend/src/data-visualization/components/plugin/src/convert.js @@ -0,0 +1,88 @@ +export const execute = (text, pwd) => { + const te = window.atob(text) + const t = simpleEncode(te, pwd) + const pLen = pwd.length + + const proxyPathBtoa = proxyPath() + const btoaLen = proxyPathBtoa.length + + const prefix = t.substring(0, btoaLen + pLen) + const suffix = t.substring(btoaLen + pLen) + if (validatePrefix(prefix, pwd, proxyPathBtoa)) { + return formatSuffix(suffix, pwd) + } + console.error('please do not do that again!') + return null +} + +export const randomKey = () => { + const m = 8 + const n = 16 + return Math.ceil(Math.random() * (n - m + 1) + m - 1) +} + +const formatSuffix = (suffix, pwd) => { + const reversePwd = strReverse(pwd) + const t = suffix.replace(reversePwd, '') + const pLen = pwd.length + const r = t.substring(0, pLen) + t.substring(pLen + 2) + const encoder = new TextEncoder() + const ar = window.atob(r).split('/') + const bytes = [] + ar.forEach(a => { + const tBytes = encoder.encode(a) + bytes.push(tBytes) + }) + return bytes +} + +export const formatArray = bytesItem => { + if (!(bytesItem instanceof Uint8Array)) { + const bytes = new Uint8Array(bytesItem.length) + for (let index = 0; index < bytesItem.length; index++) { + bytes[index] = bytesItem[index] + } + bytesItem = bytes + } + const decoder = new TextDecoder() + const art = decoder.decode(bytesItem) + bytesItem = 'what is up man ?' + return art +} + +const validatePrefix = (prefix, pwd, proxyPathBtoa) => { + const t = prefix.replace(pwd, '') + return t === proxyPathBtoa +} + +const simpleEncode = (text, key) => { + const encoder = new TextEncoder() + const data = encoder.encode(text) + let tmpc = 0 + const klen = key.length + for (let i = 0; i < data.length; ++i) { + tmpc = data[i] ^ stringToChars(key.charAt(i % klen)) + if (tmpc != 0) { + data[i] = tmpc + } + } + const decoder = new TextDecoder() + return decoder.decode(data) +} + +export function stringToChars(_s) { + _s = _s.replace(/(^\s*)|(\s*$)/g, '') + let _r = '' + for (let i = 0; i < _s.length; i++) { + _r += i == 0 ? _s.charCodeAt(i) : '|' + _s.charCodeAt(i) + } + return _r +} + +const proxyPath = () => { + return 'Zml0MmNsb3VkIGlzIGEgcG90ZW50aWFsIGNvbXBhbnkh' +} + +const strReverse = text => { + return text.split('').reverse().join('') +} diff --git a/frontend/src/data-visualization/components/plugin/src/index.vue b/frontend/src/data-visualization/components/plugin/src/index.vue new file mode 100644 index 0000000..c333a29 --- /dev/null +++ b/frontend/src/data-visualization/components/plugin/src/index.vue @@ -0,0 +1,39 @@ + + + diff --git a/frontend/src/data-visualization/components/plugin/src/nolic.vue b/frontend/src/data-visualization/components/plugin/src/nolic.vue new file mode 100644 index 0000000..a4c3f7b --- /dev/null +++ b/frontend/src/data-visualization/components/plugin/src/nolic.vue @@ -0,0 +1,13 @@ + + + diff --git a/frontend/src/data-visualization/components/visualization/CanvasBaseSetting.vue b/frontend/src/data-visualization/components/visualization/CanvasBaseSetting.vue new file mode 100644 index 0000000..71cd63c --- /dev/null +++ b/frontend/src/data-visualization/components/visualization/CanvasBaseSetting.vue @@ -0,0 +1,145 @@ + + + + + diff --git a/frontend/src/data-visualization/components/visualization/CanvasCacheDialog.vue b/frontend/src/data-visualization/components/visualization/CanvasCacheDialog.vue new file mode 100644 index 0000000..5d9a886 --- /dev/null +++ b/frontend/src/data-visualization/components/visualization/CanvasCacheDialog.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/frontend/src/data-visualization/components/visualization/CanvasOptBar.vue b/frontend/src/data-visualization/components/visualization/CanvasOptBar.vue new file mode 100644 index 0000000..5679bd2 --- /dev/null +++ b/frontend/src/data-visualization/components/visualization/CanvasOptBar.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/frontend/src/data-visualization/components/visualization/ComponentButton.vue b/frontend/src/data-visualization/components/visualization/ComponentButton.vue new file mode 100644 index 0000000..437fa0f --- /dev/null +++ b/frontend/src/data-visualization/components/visualization/ComponentButton.vue @@ -0,0 +1,90 @@ + + + + diff --git a/frontend/src/data-visualization/components/visualization/ComponentButtonLabel.vue b/frontend/src/data-visualization/components/visualization/ComponentButtonLabel.vue new file mode 100644 index 0000000..a768a47 --- /dev/null +++ b/frontend/src/data-visualization/components/visualization/ComponentButtonLabel.vue @@ -0,0 +1,79 @@ + + + + diff --git a/frontend/src/data-visualization/components/visualization/ComponentEditBar.vue b/frontend/src/data-visualization/components/visualization/ComponentEditBar.vue new file mode 100644 index 0000000..7ecdf12 --- /dev/null +++ b/frontend/src/data-visualization/components/visualization/ComponentEditBar.vue @@ -0,0 +1,730 @@ + + + + + diff --git a/frontend/src/data-visualization/components/visualization/ComponentGroup.vue b/frontend/src/data-visualization/components/visualization/ComponentGroup.vue new file mode 100644 index 0000000..7b2d7c8 --- /dev/null +++ b/frontend/src/data-visualization/components/visualization/ComponentGroup.vue @@ -0,0 +1,49 @@ + + + + diff --git a/frontend/src/data-visualization/components/visualization/ComponentSelector.vue b/frontend/src/data-visualization/components/visualization/ComponentSelector.vue new file mode 100644 index 0000000..29b0ffc --- /dev/null +++ b/frontend/src/data-visualization/components/visualization/ComponentSelector.vue @@ -0,0 +1,43 @@ + + + + + diff --git a/frontend/src/data-visualization/components/visualization/DatasetParamsComponent.vue b/frontend/src/data-visualization/components/visualization/DatasetParamsComponent.vue new file mode 100644 index 0000000..92b6ffb --- /dev/null +++ b/frontend/src/data-visualization/components/visualization/DatasetParamsComponent.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/frontend/src/data-visualization/components/visualization/DatasetParamsSettingDialog.vue b/frontend/src/data-visualization/components/visualization/DatasetParamsSettingDialog.vue new file mode 100644 index 0000000..1d32ada --- /dev/null +++ b/frontend/src/data-visualization/components/visualization/DatasetParamsSettingDialog.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/frontend/src/data-visualization/components/visualization/DePreviewPopDialog.vue b/frontend/src/data-visualization/components/visualization/DePreviewPopDialog.vue new file mode 100644 index 0000000..c6389ab --- /dev/null +++ b/frontend/src/data-visualization/components/visualization/DePreviewPopDialog.vue @@ -0,0 +1,93 @@ +