@kanaries/graphic-walker 0.2.9 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/App.d.ts +0 -2
  2. package/dist/components/callout.d.ts +7 -0
  3. package/dist/components/sizeSetting.d.ts +1 -0
  4. package/dist/components/toolbar/components.d.ts +11 -0
  5. package/dist/components/toolbar/index.d.ts +15 -0
  6. package/dist/components/toolbar/toolbar-button.d.ts +7 -0
  7. package/dist/components/toolbar/toolbar-item.d.ts +40 -0
  8. package/dist/components/toolbar/toolbar-select-button.d.ts +18 -0
  9. package/dist/components/toolbar/toolbar-toggle-button.d.ts +8 -0
  10. package/dist/components/tooltip.d.ts +13 -0
  11. package/dist/fields/datasetFields/dimFields.d.ts +1 -1
  12. package/dist/fields/datasetFields/meaFields.d.ts +1 -1
  13. package/dist/fields/filterField/filterPill.d.ts +1 -1
  14. package/dist/fields/obComponents/obFContainer.d.ts +1 -1
  15. package/dist/fields/obComponents/obPill.d.ts +1 -1
  16. package/dist/graphic-walker.es.js +56487 -55304
  17. package/dist/graphic-walker.es.js.map +1 -1
  18. package/dist/graphic-walker.umd.js +501 -288
  19. package/dist/graphic-walker.umd.js.map +1 -1
  20. package/dist/index.d.ts +4 -0
  21. package/dist/style.css +0 -1
  22. package/dist/utils/throttle.d.ts +5 -0
  23. package/dist/visualSettings/menubar.d.ts +8 -0
  24. package/package.json +4 -2
  25. package/src/App.tsx +0 -4
  26. package/src/components/callout.tsx +58 -0
  27. package/src/components/sizeSetting.tsx +61 -49
  28. package/src/components/tabs/pureTab.tsx +7 -1
  29. package/src/components/toolbar/components.tsx +110 -0
  30. package/src/components/toolbar/index.tsx +57 -0
  31. package/src/components/toolbar/toolbar-button.tsx +28 -0
  32. package/src/components/toolbar/toolbar-item.tsx +218 -0
  33. package/src/components/toolbar/toolbar-select-button.tsx +196 -0
  34. package/src/components/toolbar/toolbar-toggle-button.tsx +70 -0
  35. package/src/components/tooltip.tsx +135 -0
  36. package/src/empty_sheet.css +9 -0
  37. package/src/fields/aestheticFields.tsx +1 -1
  38. package/src/fields/datasetFields/dimFields.tsx +3 -3
  39. package/src/fields/datasetFields/index.tsx +2 -2
  40. package/src/fields/datasetFields/meaFields.tsx +3 -3
  41. package/src/fields/fieldsContext.tsx +1 -1
  42. package/src/fields/filterField/filterPill.tsx +1 -1
  43. package/src/fields/filterField/index.tsx +1 -1
  44. package/src/fields/obComponents/obFContainer.tsx +1 -1
  45. package/src/fields/obComponents/obPill.tsx +1 -1
  46. package/src/fields/posFields/index.tsx +1 -1
  47. package/src/global.d.ts +7 -0
  48. package/src/index.tsx +47 -8
  49. package/src/store/visualSpecStore.ts +1 -1
  50. package/src/utils/throttle.ts +28 -0
  51. package/src/visualSettings/index.tsx +316 -321
  52. package/src/visualSettings/menubar.tsx +1 -1
package/dist/index.d.ts CHANGED
@@ -1,3 +1,7 @@
1
1
  import React from 'react';
2
2
  import { EditorProps } from './App';
3
+ import './empty_sheet.css';
4
+ export declare const ShadowDomContext: React.Context<{
5
+ root: ShadowRoot | null;
6
+ }>;
3
7
  export declare const GraphicWalker: React.FC<EditorProps>;
package/dist/style.css CHANGED
@@ -1 +0,0 @@
1
- /*! tailwindcss v2.2.19 | MIT License | https://tailwindcss.com *//*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */*,:before,:after{box-sizing:border-box}html{tab-size:4}html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"}hr{height:0;color:inherit}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}button,[type=button],[type=submit]{-webkit-appearance:button}::-moz-focus-inner{border-style:none;padding:0}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}button{background-color:transparent;background-image:none}fieldset{margin:0;padding:0}ol,ul{list-style:none;margin:0;padding:0}html{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";line-height:1.5}body{font-family:inherit;line-height:inherit}*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:currentColor}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{padding:0;line-height:inherit;color:inherit}pre,code,kbd,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-opacity: 1;border-color:rgba(229,231,235,var(--tw-border-opacity))}.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.top-2{top:.5rem}.right-2{right:.5rem}.z-auto{z-index:auto}.col-span-1{grid-column:span 1 / span 1}.col-span-5{grid-column:span 5 / span 5}.float-right{float:right}.m-1{margin:.25rem}.m-2{margin:.5rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mr-0{margin-right:0}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-0\.5{margin-right:.125rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.-mb-px{margin-bottom:-1px}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-2{height:.5rem}.h-3{height:.75rem}.h-4{height:1rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-16{height:4rem}.h-48{height:12rem}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-16{width:4rem}.w-60{width:15rem}.w-full{width:100%}.min-w-96{min-width:96px}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.flex-shrink{flex-shrink:1}.flex-grow-0{flex-grow:0}.flex-grow{flex-grow:1}.border-collapse{border-collapse:collapse}.transform{--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;transform:translate(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}@keyframes ping{75%,to{transform:scale(2);opacity:0}}@keyframes pulse{50%{opacity:.5}}@keyframes bounce{0%,to{transform:translateY(-25%);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;animation-timing-function:cubic-bezier(0,0,.2,1)}}.animate-spin{animation:spin 1s linear infinite}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.cursor-not-allowed{cursor:not-allowed}.select-none{-webkit-user-select:none;user-select:none}.resize{resize:both}.appearance-none{-webkit-appearance:none;appearance:none}.grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-stretch{align-items:stretch}.justify-center{justify-content:center}.self-center{align-self:center}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-hidden{overflow-y:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.rounded-sm{border-radius:.125rem}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.border-2{border-width:2px}.border{border-width:1px}.border-t-2{border-top-width:2px}.border-t{border-top-width:1px}.border-r{border-right-width:1px}.border-b{border-bottom-width:1px}.border-l-2{border-left-width:2px}.border-l{border-left-width:1px}.border-transparent{border-color:transparent}.border-gray-200{--tw-border-opacity: 1;border-color:rgba(229,231,235,var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity: 1;border-color:rgba(209,213,219,var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity: 1;border-color:rgba(156,163,175,var(--tw-border-opacity))}.border-gray-500{--tw-border-opacity: 1;border-color:rgba(107,114,128,var(--tw-border-opacity))}.border-blue-400{--tw-border-opacity: 1;border-color:rgba(96,165,250,var(--tw-border-opacity))}.border-blue-500{--tw-border-opacity: 1;border-color:rgba(59,130,246,var(--tw-border-opacity))}.hover\:border-gray-300:hover{--tw-border-opacity: 1;border-color:rgba(209,213,219,var(--tw-border-opacity))}.hover\:border-gray-600:hover{--tw-border-opacity: 1;border-color:rgba(75,85,99,var(--tw-border-opacity))}.focus\:border-gray-500:focus{--tw-border-opacity: 1;border-color:rgba(107,114,128,var(--tw-border-opacity))}.focus\:border-blue-500:focus{--tw-border-opacity: 1;border-color:rgba(59,130,246,var(--tw-border-opacity))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgba(255,255,255,var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.bg-gray-400{--tw-bg-opacity: 1;background-color:rgba(156,163,175,var(--tw-bg-opacity))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgba(239,68,68,var(--tw-bg-opacity))}.bg-yellow-600{--tw-bg-opacity: 1;background-color:rgba(217,119,6,var(--tw-bg-opacity))}.bg-blue-100{--tw-bg-opacity: 1;background-color:rgba(219,234,254,var(--tw-bg-opacity))}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgba(37,99,235,var(--tw-bg-opacity))}.bg-indigo-50{--tw-bg-opacity: 1;background-color:rgba(238,242,255,var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgba(249,250,251,var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgba(243,244,246,var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgba(229,231,235,var(--tw-bg-opacity))}.hover\:bg-red-100:hover{--tw-bg-opacity: 1;background-color:rgba(254,226,226,var(--tw-bg-opacity))}.hover\:bg-yellow-100:hover{--tw-bg-opacity: 1;background-color:rgba(254,243,199,var(--tw-bg-opacity))}.hover\:bg-yellow-500:hover{--tw-bg-opacity: 1;background-color:rgba(245,158,11,var(--tw-bg-opacity))}.hover\:bg-green-50:hover{--tw-bg-opacity: 1;background-color:rgba(236,253,245,var(--tw-bg-opacity))}.hover\:bg-blue-100:hover{--tw-bg-opacity: 1;background-color:rgba(219,234,254,var(--tw-bg-opacity))}.disabled\:bg-gray-300:disabled{--tw-bg-opacity: 1;background-color:rgba(209,213,219,var(--tw-bg-opacity))}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-4{padding:1rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.pt-0{padding-top:0}.pt-1{padding-top:.25rem}.pt-0\.5{padding-top:.125rem}.pr-2{padding-right:.5rem}.pr-6{padding-right:1.5rem}.pb-0{padding-bottom:0}.pb-1{padding-bottom:.25rem}.pb-2{padding-bottom:.5rem}.pb-0\.5{padding-bottom:.125rem}.pl-2{padding-left:.5rem}.pl-6{padding-left:1.5rem}.text-xs{font-size:.75rem;line-height:1rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.font-semibold{font-weight:600}.font-bold{font-weight:700}.ordinal{--tw-ordinal: var(--tw-empty, );--tw-slashed-zero: var(--tw-empty, );--tw-numeric-figure: var(--tw-empty, );--tw-numeric-spacing: var(--tw-empty, );--tw-numeric-fraction: var(--tw-empty, );font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.ordinal{--tw-ordinal: ordinal}.leading-none{line-height:1}.text-black{--tw-text-opacity: 1;color:rgba(0,0,0,var(--tw-text-opacity))}.text-white{--tw-text-opacity: 1;color:rgba(255,255,255,var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity: 1;color:rgba(209,213,219,var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgba(156,163,175,var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgba(107,114,128,var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity: 1;color:rgba(75,85,99,var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity: 1;color:rgba(55,65,81,var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity: 1;color:rgba(31,41,55,var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity: 1;color:rgba(17,24,39,var(--tw-text-opacity))}.text-red-600{--tw-text-opacity: 1;color:rgba(220,38,38,var(--tw-text-opacity))}.text-yellow-500{--tw-text-opacity: 1;color:rgba(245,158,11,var(--tw-text-opacity))}.text-green-500{--tw-text-opacity: 1;color:rgba(16,185,129,var(--tw-text-opacity))}.text-blue-500{--tw-text-opacity: 1;color:rgba(59,130,246,var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgba(55,65,81,var(--tw-text-opacity))}.hover\:text-purple-600:hover{--tw-text-opacity: 1;color:rgba(124,58,237,var(--tw-text-opacity))}.underline{text-decoration:underline}*,:before,:after{--tw-shadow: 0 0 #0000}.shadow{--tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px 0 rgba(0, 0, 0, .06);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}*,:before,:after{--tw-ring-inset: var(--tw-empty, );--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgba(59, 130, 246, .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000}.filter{--tw-blur: var(--tw-empty, );--tw-brightness: var(--tw-empty, );--tw-contrast: var(--tw-empty, );--tw-grayscale: var(--tw-empty, );--tw-hue-rotate: var(--tw-empty, );--tw-invert: var(--tw-empty, );--tw-saturate: var(--tw-empty, );--tw-sepia: var(--tw-empty, );--tw-drop-shadow: var(--tw-empty, );filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur{--tw-blur: blur(8px)}@media (min-width: 768px){.md\:col-span-2{grid-column:span 2 / span 2}.md\:col-span-3{grid-column:span 3 / span 3}.md\:col-span-7{grid-column:span 7 / span 7}.md\:grid{display:grid}.md\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.md\:flex-col{flex-direction:column}}@media (min-width: 1280px){.xl\:col-span-1{grid-column:span 1 / span 1}.xl\:col-span-4{grid-column:span 4 / span 4}.xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}
@@ -0,0 +1,5 @@
1
+ declare const throttle: (fn: () => void, time: number, options?: Partial<{
2
+ leading: boolean;
3
+ trailing: boolean;
4
+ }>) => () => void;
5
+ export default throttle;
@@ -1,4 +1,12 @@
1
1
  import React from 'react';
2
2
  export declare const MenubarContainer: import("styled-components").StyledComponent<"div", any, {}, never>;
3
+ interface ButtonWithShortcutProps {
4
+ label: string;
5
+ shortcut: string;
6
+ disabled: boolean;
7
+ handler: () => void;
8
+ icon?: JSX.Element;
9
+ }
10
+ export declare const ButtonWithShortcut: React.FC<ButtonWithShortcutProps>;
3
11
  declare const _default: React.FunctionComponent<{}>;
4
12
  export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kanaries/graphic-walker",
3
- "version": "0.2.9",
3
+ "version": "0.2.10",
4
4
  "scripts": {
5
5
  "dev:front_end": "vite --host",
6
6
  "dev": "npm run dev:front_end",
@@ -33,6 +33,7 @@
33
33
  "types": "./dist/index.d.ts",
34
34
  "dependencies": {
35
35
  "@heroicons/react": "^2.0.8",
36
+ "@kanaries/react-beautiful-dnd": "0.0.1",
36
37
  "@kanaries/web-data-loader": "0.1.5",
37
38
  "autoprefixer": "^10.3.5",
38
39
  "i18next": "^21.9.1",
@@ -41,10 +42,11 @@
41
42
  "mobx": "^6.3.3",
42
43
  "mobx-react-lite": "^3.2.1",
43
44
  "postcss": "^8.3.7",
45
+ "postinstall-postinstall": "^2.1.0",
44
46
  "re-resizable": "^6.9.8",
45
- "react-beautiful-dnd": "^13.1.1",
46
47
  "react-i18next": "^11.18.6",
47
48
  "react-json-view": "^1.21.3",
49
+ "react-shadow": "^20.0.0",
48
50
  "rxjs": "^7.3.0",
49
51
  "tailwindcss": "^2.2.15",
50
52
  "uuid": "^8.3.2",
package/src/App.tsx CHANGED
@@ -19,10 +19,7 @@ import { useGlobalStore } from './store';
19
19
  import { preAnalysis, destroyWorker } from './services'
20
20
  import VisNav from './segments/visNav';
21
21
  import { mergeLocaleRes, setLocaleLanguage } from './locales/i18n';
22
- import Menubar from './visualSettings/menubar';
23
22
  import FilterField from './fields/filterField';
24
- import "tailwindcss/tailwind.css"
25
- import './index.css'
26
23
 
27
24
 
28
25
  export interface EditorProps {
@@ -99,7 +96,6 @@ const App: React.FC<EditorProps> = props => {
99
96
  {/* <PureTabs tabs={[{label: 'a', key: 'a'}, {label: 'b', key: 'b'}]} selectedKey='a' onSelected={() => {}} /> */}
100
97
  </div>
101
98
  <Container style={{ marginTop: '0em', borderTop: 'none' }}>
102
- <Menubar />
103
99
  <VisualSettings rendererHandler={rendererRef} />
104
100
  <div className="md:grid md:grid-cols-12 xl:grid-cols-6">
105
101
  <div className="md:col-span-3 xl:col-span-1">
@@ -0,0 +1,58 @@
1
+ import React, { memo, ReactNode, useContext, useEffect, useState } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import styled from 'styled-components';
4
+ import { ShadowDomContext } from '..';
5
+
6
+
7
+ export interface CalloutProps {
8
+ target: string;
9
+ children: ReactNode;
10
+ }
11
+
12
+ const Bubble = styled.div`
13
+ border-radius: 1px;
14
+ transform: translate(-50%, 0);
15
+ filter: drop-shadow(0 1.6px 1px rgba(0, 0, 0, 0.24)) drop-shadow(0 -1px 0.8px rgba(0, 0, 0, 0.19));
16
+ user-select: none;
17
+ display: block;
18
+ width: max-content;
19
+ height: max-content;
20
+ ::before {
21
+ content: "";
22
+ display: block;
23
+ position: absolute;
24
+ top: 0;
25
+ left: 50%;
26
+ width: 8px;
27
+ height: 8px;
28
+ transform: translate(-50%, -50%) rotate(45deg);
29
+ background-color: #fff;
30
+ border-radius: 1px;
31
+ }
32
+ `;
33
+
34
+ const Callout = memo<CalloutProps>(function Callout ({ target, children }) {
35
+ const shadowDomMeta = useContext(ShadowDomContext);
36
+ const { root } = shadowDomMeta;
37
+ const [pos, setPos] = useState<[number, number] | null>(null);
38
+
39
+ useEffect(() => {
40
+ const el = (
41
+ target.startsWith('#') ? root?.getElementById(target.slice(1)) : root?.querySelector(target)
42
+ ) as HTMLElement | null;
43
+ if (el) {
44
+ const rect = el.getBoundingClientRect();
45
+ setPos([rect.x + rect.width / 2, rect.y + rect.height]);
46
+ }
47
+ }, [target, root]);
48
+
49
+ return root && pos && createPortal(
50
+ <Bubble role="dialog" className="fixed bg-white z-50" style={{ left: pos[0], top: pos[1] + 4 }}>
51
+ {children}
52
+ </Bubble>,
53
+ root
54
+ );
55
+ });
56
+
57
+
58
+ export default Callout;
@@ -10,6 +10,45 @@ interface SizeSettingProps {
10
10
  height: number;
11
11
  }
12
12
 
13
+ export const ResizeDialog: React.FC<SizeSettingProps> = props => {
14
+ const { onWidthChange, onHeightChange, width, height, children } = props
15
+ const { t } = useTranslation('translation', { keyPrefix: 'main.tabpanel.settings.size_setting' });
16
+
17
+ return <>
18
+ {children}
19
+ <div className="mt-4 w-60">
20
+ <input className="w-full h-2 bg-blue-100 appearance-none"
21
+ style={{ cursor: 'ew-resize' }}
22
+ type="range"
23
+ name="width"
24
+ value={Math.sqrt(width / 1000)}
25
+ min="0" max="1" step="0.01"
26
+ onChange={(e) => {
27
+ onWidthChange(Math.round(Number(e.target.value) ** 2 * 1000))
28
+ }}
29
+ />
30
+ <output className="text-sm ml-1" htmlFor="width">
31
+ {`${t('width')}: ${width}`}
32
+ </output>
33
+ </div>
34
+ <div className=" mt-2">
35
+ <input className="w-full h-2 bg-blue-100 appearance-none"
36
+ style={{ cursor: 'ew-resize' }}
37
+ type="range"
38
+ name="height"
39
+ value={Math.sqrt(height / 1000)}
40
+ min="0" max="1" step="0.01"
41
+ onChange={(e) => {
42
+ onHeightChange(Math.round(Number(e.target.value) ** 2 * 1000))
43
+ }}
44
+ />
45
+ <output className="text-sm ml-1" htmlFor="height">
46
+ {`${t('height')}: ${height}`}
47
+ </output>
48
+ </div>
49
+ </>
50
+ };
51
+
13
52
  const SizeSetting: React.FC<SizeSettingProps> = props => {
14
53
  const { onWidthChange, onHeightChange, width, height } = props
15
54
  const [show, setShow] = useState<boolean>(false);
@@ -21,10 +60,17 @@ const SizeSetting: React.FC<SizeSettingProps> = props => {
21
60
  setShow(false);
22
61
  };
23
62
 
24
- document.body.addEventListener('click', closeDialog);
63
+ let subscribed = false;
64
+ const timer = setTimeout(() => {
65
+ subscribed = true;
66
+ document.body.addEventListener('click', closeDialog);
67
+ }, 200);
25
68
 
26
69
  return () => {
27
- document.body.removeEventListener('click', closeDialog);
70
+ clearTimeout(timer);
71
+ if (subscribed) {
72
+ document.body.removeEventListener('click', closeDialog);
73
+ }
28
74
  };
29
75
  }
30
76
  }, [show]);
@@ -41,54 +87,20 @@ const SizeSetting: React.FC<SizeSettingProps> = props => {
41
87
  }}
42
88
  className="w-4 h-4 inline-block mr-0.5 text-gray-900"
43
89
  />
44
- {
45
- show && <div role="dialog" className="absolute z-auto bg-white p-4 border border-gray-200 shadow cursor-default" onClick={e => e.stopPropagation()} style={{ zIndex: 25535 }}>
46
- <div>
47
- <XMarkIcon
48
- className="text-gray-900 absolute right-2 top-2 w-4 cursor-pointer hover:bg-red-100"
49
- role="button"
50
- tabIndex={0}
51
- aria-label="close"
52
- onClick={(e) => {
53
- setShow(false);
54
- e.stopPropagation();
55
- }}
56
- />
57
- </div>
58
-
59
- <div className="mt-4 w-60">
60
- <input className="w-full h-2 bg-blue-100 appearance-none"
61
- style={{ cursor: 'ew-resize' }}
62
- type="range"
63
- name="width"
64
- value={Math.sqrt(width / 1000)}
65
- min="0" max="1" step="0.01"
66
- onChange={(e) => {
67
- onWidthChange(Math.round(Number(e.target.value) ** 2 * 1000))
68
- }}
69
- />
70
- <output className="text-sm ml-1" htmlFor="width">
71
- {`${t('width')}: ${width}`}
72
- </output>
73
- </div>
74
- <div className=" mt-2">
75
- <input className="w-full h-2 bg-blue-100 appearance-none"
76
- style={{ cursor: 'ew-resize' }}
77
- type="range"
78
- name="height"
79
- value={Math.sqrt(height / 1000)}
80
- min="0" max="1" step="0.01"
81
- onChange={(e) => {
82
- onHeightChange(Math.round(Number(e.target.value) ** 2 * 1000))
83
- }}
84
- />
85
- <output className="text-sm ml-1" htmlFor="height">
86
- {`${t('height')}: ${height}`}
87
- </output>
88
- </div>
89
-
90
+ {show && <ResizeDialog {...props}>
91
+ <div>
92
+ <XMarkIcon
93
+ className="text-gray-900 absolute right-2 top-2 w-4 cursor-pointer hover:bg-red-100"
94
+ role="button"
95
+ tabIndex={0}
96
+ aria-label="close"
97
+ onClick={(e) => {
98
+ setShow(false);
99
+ e.stopPropagation();
100
+ }}
101
+ />
90
102
  </div>
91
- }
103
+ </ResizeDialog>}
92
104
  </div>
93
105
  }
94
106
 
@@ -28,7 +28,7 @@ export default function PureTabs(props: PureTabsProps) {
28
28
  }, [tabs.length]);
29
29
 
30
30
  useEffect(() => {
31
- clearEditStatus
31
+ clearEditStatus();
32
32
  }, [clearEditStatus]);
33
33
 
34
34
  return (
@@ -55,6 +55,12 @@ export default function PureTabs(props: PureTabsProps) {
55
55
  onInput={(e) => {
56
56
  onEditLabel && onEditLabel(`${e.currentTarget.textContent}`, tabIndex)
57
57
  }}
58
+ onKeyDown={(e) => {
59
+ if (e.key === 'Enter') {
60
+ clearEditStatus();
61
+ e.preventDefault();
62
+ }
63
+ }}
58
64
  key={tab.key}
59
65
  className={classNames(
60
66
  tab.key === selectedKey
@@ -0,0 +1,110 @@
1
+ import { KeyboardEvent, MouseEvent, useMemo, useRef } from "react";
2
+ import styled from "styled-components";
3
+
4
+
5
+ export const useHandlers = (action: () => void, disabled: boolean, triggerKeys: string[] = ['Enter'], allowPropagation = true) => {
6
+ const actionRef = useRef(action);
7
+ actionRef.current = () => {
8
+ if (disabled) {
9
+ return;
10
+ }
11
+ action();
12
+ };
13
+ const triggerKeysRef = useRef(triggerKeys);
14
+ triggerKeysRef.current = triggerKeys;
15
+
16
+ return useMemo(() => ({
17
+ onClick: (ev: MouseEvent) => {
18
+ if (!allowPropagation) {
19
+ ev.stopPropagation();
20
+ }
21
+ actionRef.current();
22
+ },
23
+ onKeyDown: (ev: KeyboardEvent) => {
24
+ if (!allowPropagation) {
25
+ ev.stopPropagation();
26
+ }
27
+ if (triggerKeysRef.current.includes(ev.key)) {
28
+ ev.stopPropagation();
29
+ ev.preventDefault();
30
+ actionRef.current();
31
+ }
32
+ },
33
+ onMouseOut: (ev: MouseEvent) => {
34
+ if (!allowPropagation) {
35
+ ev.stopPropagation();
36
+ }
37
+ (document.querySelector('*:focus') as null | HTMLElement)?.blur();
38
+ },
39
+ }), [allowPropagation]);
40
+ };
41
+
42
+ export const ToolbarContainer = styled.div`
43
+ --height: 36px;
44
+ --icon-size: 18px;
45
+ width: 100%;
46
+ height: var(--height);
47
+ background-color: var(--background-color);
48
+ color: var(--color);
49
+ /* box-shadow: 0px 1px 3px 1px rgba(136, 136, 136, 0.1); */
50
+ border-radius: 2px;
51
+ overflow: hidden;
52
+ display: flex;
53
+ flex-direction: row;
54
+ > * {
55
+ flex-grow: 0;
56
+ flex-shrink: 0;
57
+ }
58
+ `;
59
+
60
+ export const ToolbarSplitter = styled.div`
61
+ display: inline-block;
62
+ margin: calc(var(--height) / 6) calc(var(--icon-size) / 4);
63
+ height: calc(var(--height) * 2 / 3);
64
+ width: 1px;
65
+ background: #bbbbbb50;
66
+ `;
67
+
68
+ export const ToolbarItemContainerElement = styled.div<{ split: boolean }>`
69
+ display: inline-flex;
70
+ flex-direction: row;
71
+ user-select: none;
72
+ outline: none;
73
+ width: ${({ split }) => split ? 'calc(var(--height) + 10px)' : 'var(--height)'};
74
+ height: var(--height);
75
+ overflow: hidden;
76
+ color: var(--color);
77
+ position: relative;
78
+ > svg {
79
+ flex-grow: 0;
80
+ flex-shrink: 0;
81
+ width: var(--icon-size);
82
+ height: var(--icon-size);
83
+ margin: calc((var(--height) - var(--icon-size)) / 2);
84
+ margin-right: ${({ split }) => split ? 'calc((var(--height) - var(--icon-size)) / 4)' : ''};
85
+ transition: text-shadow 100ms;
86
+ }
87
+ --shadow-color: #0F172A55;
88
+ &[aria-disabled=true] {
89
+ cursor: default;
90
+ > * {
91
+ opacity: 0.33;
92
+ }
93
+ }
94
+ &[aria-disabled=false] {
95
+ cursor: pointer;
96
+ :hover, :focus, &.open {
97
+ background-image: linear-gradient(#FFFFFFCC, #FEFEFECC);
98
+ --background-color: #FEFEFE;
99
+ color: var(--color-hover);
100
+ &.split * svg {
101
+ pointer-events: none;
102
+ transform: translate(-50%, -20%);
103
+ }
104
+ & svg {
105
+ text-shadow: 0 0 1.5px var(--shadow-color);
106
+ }
107
+ }
108
+ }
109
+ transition: color 100ms, background-image 100ms;
110
+ `;
@@ -0,0 +1,57 @@
1
+ import React, { CSSProperties, memo, ReactNode, useState } from "react";
2
+ import styled from "styled-components";
3
+ import { ToolbarContainer, ToolbarSplitter } from "./components";
4
+ import ToolbarItem, { ToolbarItemProps, ToolbarItemSplitter } from "./toolbar-item";
5
+
6
+
7
+ const Root = styled.div`
8
+ width: 100%;
9
+ --background-color: #f7f7f7;
10
+ --color: #777;
11
+ --color-hover: #555;
12
+ --blue: #282958;
13
+ --blue-dark: #1d1e38;
14
+ `;
15
+
16
+ export interface ToolbarProps {
17
+ items: ToolbarItemProps[];
18
+ styles?: Partial<{
19
+ root: CSSProperties & Record<string, string>;
20
+ container: CSSProperties & Record<string, string>;
21
+ item: CSSProperties & Record<string, string>;
22
+ icon: CSSProperties & Record<string, string>;
23
+ splitIcon: CSSProperties & Record<string, string>;
24
+ }>;
25
+ }
26
+
27
+ const Toolbar = memo<ToolbarProps>(function Toolbar ({ items, styles }) {
28
+ const [openedKey, setOpenedKey] = useState<string | null>(null);
29
+ const [slot, setSlot] = useState<ReactNode>(null);
30
+
31
+ return (
32
+ <Root style={styles?.root}>
33
+ <ToolbarContainer style={styles?.container}>
34
+ {items.map((item, i) => {
35
+ if (item === ToolbarItemSplitter) {
36
+ return <ToolbarSplitter key={i} />;
37
+ }
38
+ return (
39
+ <ToolbarItem
40
+ key={item.key}
41
+ item={item}
42
+ styles={styles}
43
+ openedKey={openedKey}
44
+ setOpenedKey={setOpenedKey}
45
+ renderSlot={node => setSlot(node)}
46
+ />
47
+ );
48
+ })}
49
+ </ToolbarContainer>
50
+ {slot}
51
+ </Root>
52
+ );
53
+ });
54
+
55
+
56
+ export default Toolbar;
57
+ export type { ToolbarItemProps };
@@ -0,0 +1,28 @@
1
+ import React, { memo } from "react";
2
+ import { IToolbarItem, IToolbarProps, ToolbarItemContainer } from "./toolbar-item";
3
+ import { useHandlers } from "./components";
4
+
5
+
6
+ export interface ToolbarButtonItem extends IToolbarItem {
7
+ onClick?: () => void;
8
+ }
9
+
10
+ const ToolbarButton = memo<IToolbarProps<ToolbarButtonItem>>(function ToolbarButton(props) {
11
+ const { item, styles } = props;
12
+ const { icon: Icon, label, disabled, onClick } = item;
13
+ const handlers = useHandlers(() => onClick?.(), disabled ?? false);
14
+
15
+ return (
16
+ <>
17
+ <ToolbarItemContainer
18
+ props={props}
19
+ handlers={onClick ? handlers : null}
20
+ >
21
+ <Icon style={styles?.icon} />
22
+ </ToolbarItemContainer>
23
+ </>
24
+ );
25
+ });
26
+
27
+
28
+ export default ToolbarButton;