@baron1996/klinecharts-runtime 0.1.0

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 (37) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +22 -0
  3. package/README.md +40 -0
  4. package/dist/events.d.ts +9 -0
  5. package/dist/events.d.ts.map +1 -0
  6. package/dist/events.js +18 -0
  7. package/dist/index.d.ts +7 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +6 -0
  10. package/dist/lifecycle.d.ts +5 -0
  11. package/dist/lifecycle.d.ts.map +1 -0
  12. package/dist/lifecycle.js +23 -0
  13. package/dist/runtime.d.ts +25 -0
  14. package/dist/runtime.d.ts.map +1 -0
  15. package/dist/runtime.js +166 -0
  16. package/dist/toolbar/standard-toolbar-styles.d.ts +2 -0
  17. package/dist/toolbar/standard-toolbar-styles.d.ts.map +1 -0
  18. package/dist/toolbar/standard-toolbar-styles.js +219 -0
  19. package/dist/toolbar/standard-toolbar.d.ts +5 -0
  20. package/dist/toolbar/standard-toolbar.d.ts.map +1 -0
  21. package/dist/toolbar/standard-toolbar.js +219 -0
  22. package/dist/toolbar/toolbar-icons.d.ts +441 -0
  23. package/dist/toolbar/toolbar-icons.d.ts.map +1 -0
  24. package/dist/toolbar/toolbar-icons.js +187 -0
  25. package/dist/toolbar/toolbar-tools.d.ts +56 -0
  26. package/dist/toolbar/toolbar-tools.d.ts.map +1 -0
  27. package/dist/toolbar/toolbar-tools.js +138 -0
  28. package/dist/types.d.ts +46 -0
  29. package/dist/types.d.ts.map +1 -0
  30. package/dist/types.js +1 -0
  31. package/licenses/KLineCharts-LICENSE +201 -0
  32. package/licenses/KLineCharts-NOTICE +5 -0
  33. package/licenses/Noto-Sans-SC-OFL-1.1 +93 -0
  34. package/licenses/Tabler-Icons-LICENSE +21 -0
  35. package/licenses/TradingView-Lightweight-Charts-LICENSE +201 -0
  36. package/licenses/fflate-LICENSE +21 -0
  37. package/package.json +45 -0
@@ -0,0 +1,219 @@
1
+ export const STANDARD_TOOLBAR_STYLES = String.raw `
2
+ .baron-kline-toolbar,
3
+ .baron-kline-toolbar *,
4
+ .baron-kline-toolbar-tooltip,
5
+ .baron-kline-toolbar-tooltip * {
6
+ box-sizing: border-box;
7
+ }
8
+
9
+ .baron-kline-toolbar {
10
+ position: relative;
11
+ display: flex;
12
+ width: 100%;
13
+ min-width: 0;
14
+ color: rgba(32, 35, 42, 1);
15
+ background: rgba(255, 255, 255, 1);
16
+ border-bottom: 1px solid rgba(218, 220, 226, 1);
17
+ font-family: "Baron Sans", "Noto Sans SC", sans-serif;
18
+ }
19
+
20
+ .baron-kline-toolbar__viewport {
21
+ width: 100%;
22
+ min-width: 0;
23
+ overflow-x: auto;
24
+ overflow-y: hidden;
25
+ contain: layout paint;
26
+ scrollbar-width: none;
27
+ }
28
+
29
+ .baron-kline-toolbar__viewport::-webkit-scrollbar {
30
+ display: none;
31
+ }
32
+
33
+ .baron-kline-toolbar__content {
34
+ display: flex;
35
+ gap: 0;
36
+ align-items: center;
37
+ width: max-content;
38
+ min-width: 100%;
39
+ min-height: 50px;
40
+ padding: 8px;
41
+ }
42
+
43
+ .baron-kline-toolbar__group {
44
+ display: flex;
45
+ flex: 0 0 auto;
46
+ gap: 2px;
47
+ align-items: center;
48
+ }
49
+
50
+ .baron-kline-toolbar__group + .baron-kline-toolbar__group,
51
+ .baron-kline-toolbar__text-field {
52
+ padding-left: 5px;
53
+ margin-left: 3px;
54
+ border-left: 1px solid rgba(217, 220, 227, 1);
55
+ }
56
+
57
+ .baron-kline-toolbar__button {
58
+ position: relative;
59
+ display: grid;
60
+ flex: 0 0 auto;
61
+ width: 34px;
62
+ height: 34px;
63
+ padding: 0;
64
+ place-items: center;
65
+ color: rgba(32, 35, 42, 1);
66
+ background: transparent;
67
+ border: 0;
68
+ border-radius: 8px;
69
+ cursor: pointer;
70
+ transition:
71
+ color 140ms ease,
72
+ background-color 140ms ease,
73
+ transform 140ms ease;
74
+ }
75
+
76
+ .baron-kline-toolbar__button:hover {
77
+ color: rgba(41, 98, 255, 1);
78
+ background: rgba(41, 98, 255, 0.1);
79
+ transform: translateY(-1px);
80
+ }
81
+
82
+ .baron-kline-toolbar__button:focus-visible {
83
+ outline: 2px solid rgba(41, 98, 255, 1);
84
+ outline-offset: 2px;
85
+ }
86
+
87
+ .baron-kline-toolbar__button[aria-pressed="true"] {
88
+ color: rgba(255, 255, 255, 1);
89
+ background: rgba(41, 98, 255, 1);
90
+ }
91
+
92
+ .baron-kline-toolbar__button svg {
93
+ width: 19px;
94
+ height: 19px;
95
+ }
96
+
97
+ .baron-kline-toolbar__text-field {
98
+ display: flex;
99
+ flex: 0 0 auto;
100
+ align-items: center;
101
+ }
102
+
103
+ .baron-kline-toolbar__text-input {
104
+ width: 152px;
105
+ height: 34px;
106
+ padding: 0 10px;
107
+ color: rgba(32, 35, 42, 1);
108
+ background: rgba(248, 249, 251, 1);
109
+ border: 1px solid rgba(217, 220, 227, 1);
110
+ border-radius: 7px;
111
+ font: inherit;
112
+ font-size: 12px;
113
+ outline: none;
114
+ transition:
115
+ background-color 140ms ease,
116
+ border-color 140ms ease,
117
+ box-shadow 140ms ease;
118
+ }
119
+
120
+ .baron-kline-toolbar__text-input::placeholder {
121
+ color: rgba(116, 122, 136, 1);
122
+ }
123
+
124
+ .baron-kline-toolbar__text-input:hover {
125
+ background: rgba(255, 255, 255, 1);
126
+ border-color: rgba(184, 188, 198, 1);
127
+ }
128
+
129
+ .baron-kline-toolbar__text-input:focus {
130
+ background: rgba(255, 255, 255, 1);
131
+ border-color: rgba(41, 98, 255, 1);
132
+ box-shadow: 0 0 0 2px rgba(41, 98, 255, 0.12);
133
+ }
134
+
135
+ .baron-kline-toolbar__visually-hidden {
136
+ position: absolute;
137
+ width: 1px;
138
+ height: 1px;
139
+ padding: 0;
140
+ margin: -1px;
141
+ overflow: hidden;
142
+ clip: rect(0, 0, 0, 0);
143
+ white-space: nowrap;
144
+ border: 0;
145
+ }
146
+
147
+ .baron-kline-toolbar-tooltip {
148
+ position: fixed;
149
+ z-index: 10000;
150
+ display: grid;
151
+ gap: 3px;
152
+ min-width: max-content;
153
+ padding: 7px 9px;
154
+ color: rgba(245, 247, 251, 1);
155
+ background: rgba(24, 27, 34, 1);
156
+ border: 1px solid rgba(255, 255, 255, 0.12);
157
+ border-radius: 6px;
158
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.24);
159
+ opacity: 0;
160
+ pointer-events: none;
161
+ transform: translateY(-3px);
162
+ transition:
163
+ opacity 120ms ease,
164
+ transform 120ms ease;
165
+ }
166
+
167
+ .baron-kline-toolbar-tooltip[hidden] {
168
+ display: none;
169
+ }
170
+
171
+ .baron-kline-toolbar-tooltip--visible {
172
+ opacity: 1;
173
+ transform: translateY(0);
174
+ }
175
+
176
+ .baron-kline-toolbar-tooltip strong {
177
+ font-size: 11px;
178
+ font-weight: 650;
179
+ line-height: 1.2;
180
+ }
181
+
182
+ .baron-kline-toolbar-tooltip code {
183
+ color: rgba(158, 173, 209, 1);
184
+ font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
185
+ font-size: 8px;
186
+ line-height: 1.2;
187
+ }
188
+
189
+ @media (max-width: 480px) {
190
+ .baron-kline-toolbar::after {
191
+ position: absolute;
192
+ z-index: 2;
193
+ top: 0;
194
+ right: 0;
195
+ bottom: 1px;
196
+ width: 34px;
197
+ background: linear-gradient(
198
+ 90deg,
199
+ rgba(255, 255, 255, 0),
200
+ rgba(255, 255, 255, 0.98)
201
+ );
202
+ content: "";
203
+ pointer-events: none;
204
+ }
205
+
206
+ .baron-kline-toolbar__content {
207
+ min-height: 48px;
208
+ padding: 7px;
209
+ }
210
+ }
211
+
212
+ @media (prefers-reduced-motion: reduce) {
213
+ .baron-kline-toolbar__button,
214
+ .baron-kline-toolbar__text-input,
215
+ .baron-kline-toolbar-tooltip {
216
+ transition: none;
217
+ }
218
+ }
219
+ `;
@@ -0,0 +1,5 @@
1
+ import type { KLineSceneRuntime } from '../runtime.js';
2
+ import type { StandardToolbar, StandardToolbarOptions } from '../types.js';
3
+ /** 创建不含撤销/重做的标准离线编辑工具栏。 */
4
+ export declare function createStandardToolbar(container: HTMLElement, runtime: KLineSceneRuntime, options?: StandardToolbarOptions): StandardToolbar;
5
+ //# sourceMappingURL=standard-toolbar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standard-toolbar.d.ts","sourceRoot":"","sources":["../../src/toolbar/standard-toolbar.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EACX,eAAe,EACf,sBAAsB,EAEtB,MAAM,aAAa,CAAC;AA2KrB,2BAA2B;AAC3B,wBAAgB,qBAAqB,CACpC,SAAS,EAAE,WAAW,EACtB,OAAO,EAAE,iBAAiB,EAC1B,OAAO,GAAE,sBAA2B,GAClC,eAAe,CAoIjB"}
@@ -0,0 +1,219 @@
1
+ import { serializeCanonicalScene } from '@baron1996/kline-scene-schema';
2
+ import { SUPPORTED_OVERLAYS } from '@baron1996/klinecharts-adapter';
3
+ import { registerRuntimeTeardown } from '../lifecycle.js';
4
+ import { createToolbarIcon } from './toolbar-icons.js';
5
+ import { STANDARD_TOOLBAR_STYLES } from './standard-toolbar-styles.js';
6
+ import { OVERLAY_TOOL_PRESENTATIONS, TEXT_OVERLAY_TYPES, TOOLBAR_ACTIONS, TOOLBAR_GROUPS, } from './toolbar-tools.js';
7
+ let nextToolbarId = 1;
8
+ function createIconButton(presentation, action) {
9
+ const element = document.createElement('button');
10
+ element.type = 'button';
11
+ element.className = 'baron-kline-toolbar__button';
12
+ element.setAttribute('aria-label', presentation.label);
13
+ element.append(createToolbarIcon(presentation.icon));
14
+ element.addEventListener('click', action);
15
+ return {
16
+ element,
17
+ cleanup: () => element.removeEventListener('click', action),
18
+ };
19
+ }
20
+ function createTooltip(viewport, tooltipId) {
21
+ const element = document.createElement('div');
22
+ element.id = tooltipId;
23
+ element.className = 'baron-kline-toolbar-tooltip';
24
+ element.setAttribute('role', 'tooltip');
25
+ element.hidden = true;
26
+ const name = document.createElement('strong');
27
+ const type = document.createElement('code');
28
+ element.append(name, type);
29
+ document.body.append(element);
30
+ let anchor;
31
+ const position = (button) => {
32
+ const viewportPadding = 8;
33
+ const tooltipGap = 8;
34
+ const buttonRect = button.getBoundingClientRect();
35
+ const viewportRect = viewport.getBoundingClientRect();
36
+ const tooltipRect = element.getBoundingClientRect();
37
+ const centeredLeft = buttonRect.left + (buttonRect.width - tooltipRect.width) / 2;
38
+ const minimumLeft = Math.max(viewportRect.left + viewportPadding, viewportPadding);
39
+ const maximumLeft = Math.min(viewportRect.right - tooltipRect.width - viewportPadding, window.innerWidth - tooltipRect.width - viewportPadding);
40
+ const left = Math.max(minimumLeft, Math.min(centeredLeft, maximumLeft));
41
+ element.style.left = `${Math.round(left)}px`;
42
+ element.style.top = `${Math.round(buttonRect.bottom + tooltipGap)}px`;
43
+ };
44
+ const hide = () => {
45
+ anchor?.removeAttribute('aria-describedby');
46
+ element.classList.remove('baron-kline-toolbar-tooltip--visible');
47
+ element.hidden = true;
48
+ anchor = undefined;
49
+ };
50
+ const show = (button, label, tooltipType) => {
51
+ anchor?.removeAttribute('aria-describedby');
52
+ anchor = button;
53
+ name.textContent = label;
54
+ type.textContent = tooltipType;
55
+ element.hidden = false;
56
+ button.setAttribute('aria-describedby', tooltipId);
57
+ position(button);
58
+ element.classList.add('baron-kline-toolbar-tooltip--visible');
59
+ };
60
+ const bind = (button, label, tooltipType) => {
61
+ const handleMouseEnter = () => show(button, label, tooltipType);
62
+ const handleMouseLeave = () => {
63
+ if (document.activeElement !== button) {
64
+ hide();
65
+ }
66
+ };
67
+ const handleFocus = () => show(button, label, tooltipType);
68
+ const handleBlur = () => hide();
69
+ button.addEventListener('mouseenter', handleMouseEnter);
70
+ button.addEventListener('mouseleave', handleMouseLeave);
71
+ button.addEventListener('focus', handleFocus);
72
+ button.addEventListener('blur', handleBlur);
73
+ return () => {
74
+ button.removeEventListener('mouseenter', handleMouseEnter);
75
+ button.removeEventListener('mouseleave', handleMouseLeave);
76
+ button.removeEventListener('focus', handleFocus);
77
+ button.removeEventListener('blur', handleBlur);
78
+ if (anchor === button) {
79
+ hide();
80
+ }
81
+ };
82
+ };
83
+ const handleViewportChange = () => hide();
84
+ viewport.addEventListener('scroll', handleViewportChange);
85
+ window.addEventListener('resize', handleViewportChange);
86
+ window.addEventListener('scroll', handleViewportChange, true);
87
+ return {
88
+ bind,
89
+ hide,
90
+ destroy() {
91
+ hide();
92
+ viewport.removeEventListener('scroll', handleViewportChange);
93
+ window.removeEventListener('resize', handleViewportChange);
94
+ window.removeEventListener('scroll', handleViewportChange, true);
95
+ element.remove();
96
+ },
97
+ };
98
+ }
99
+ function downloadScene(runtime, fileName) {
100
+ const bytes = serializeCanonicalScene(runtime.exportScene());
101
+ const blob = new Blob([bytes], { type: 'application/json' });
102
+ const url = URL.createObjectURL(blob);
103
+ const anchor = document.createElement('a');
104
+ anchor.href = url;
105
+ anchor.download = fileName;
106
+ anchor.click();
107
+ URL.revokeObjectURL(url);
108
+ }
109
+ function setActiveOverlayButton(buttons, activeButton) {
110
+ for (const button of buttons) {
111
+ button.setAttribute('aria-pressed', String(button === activeButton));
112
+ }
113
+ }
114
+ /** 创建不含撤销/重做的标准离线编辑工具栏。 */
115
+ export function createStandardToolbar(container, runtime, options = {}) {
116
+ const toolbarId = nextToolbarId++;
117
+ const root = document.createElement('div');
118
+ root.className = 'baron-kline-toolbar';
119
+ root.setAttribute('role', 'toolbar');
120
+ root.setAttribute('aria-label', 'K 线标注工具');
121
+ const style = document.createElement('style');
122
+ style.dataset.baronToolbarStyles = '';
123
+ style.textContent = STANDARD_TOOLBAR_STYLES;
124
+ const viewport = document.createElement('div');
125
+ viewport.className = 'baron-kline-toolbar__viewport';
126
+ const content = document.createElement('div');
127
+ content.className = 'baron-kline-toolbar__content';
128
+ viewport.append(content);
129
+ root.append(style, viewport);
130
+ const tooltip = createTooltip(viewport, `baron-kline-toolbar-tooltip-${toolbarId}`);
131
+ const cleanupCallbacks = [];
132
+ const overlayButtons = [];
133
+ const textInput = document.createElement('input');
134
+ textInput.type = 'text';
135
+ textInput.className = 'baron-kline-toolbar__text-input';
136
+ textInput.dataset.action = 'overlay-text';
137
+ textInput.setAttribute('aria-label', '标注文本');
138
+ textInput.placeholder = '输入标注文本';
139
+ for (const group of TOOLBAR_GROUPS) {
140
+ const groupElement = document.createElement('div');
141
+ groupElement.className = 'baron-kline-toolbar__group';
142
+ groupElement.dataset.toolbarGroup = group.id;
143
+ groupElement.setAttribute('role', 'group');
144
+ groupElement.setAttribute('aria-label', group.label);
145
+ if (group.id === 'action') {
146
+ for (const presentation of TOOLBAR_ACTIONS) {
147
+ const action = presentation.action === 'delete'
148
+ ? () => {
149
+ const id = runtime.getSelectedOverlayId();
150
+ if (id === undefined) {
151
+ return;
152
+ }
153
+ const overlay = runtime.getOverlay(id);
154
+ if (overlay !== undefined && !overlay.locked) {
155
+ runtime.removeOverlay(id);
156
+ }
157
+ }
158
+ : () => {
159
+ downloadScene(runtime, options.downloadFileName ?? 'kline-scene.json');
160
+ };
161
+ const button = createIconButton(presentation, action);
162
+ button.element.dataset.action = presentation.action;
163
+ groupElement.append(button.element);
164
+ cleanupCallbacks.push(button.cleanup, tooltip.bind(button.element, presentation.label, presentation.action));
165
+ }
166
+ }
167
+ else {
168
+ for (const overlayType of SUPPORTED_OVERLAYS) {
169
+ const presentation = OVERLAY_TOOL_PRESENTATIONS[overlayType];
170
+ if (presentation.group !== group.id) {
171
+ continue;
172
+ }
173
+ let buttonElement;
174
+ const action = () => {
175
+ runtime.startOverlayDrawing(overlayType, TEXT_OVERLAY_TYPES.has(overlayType)
176
+ ? { text: textInput.value }
177
+ : {});
178
+ setActiveOverlayButton(overlayButtons, buttonElement);
179
+ tooltip.hide();
180
+ };
181
+ const button = createIconButton(presentation, action);
182
+ buttonElement = button.element;
183
+ buttonElement.dataset.overlayType = overlayType;
184
+ buttonElement.setAttribute('aria-pressed', 'false');
185
+ overlayButtons.push(buttonElement);
186
+ groupElement.append(buttonElement);
187
+ cleanupCallbacks.push(button.cleanup, tooltip.bind(buttonElement, presentation.label, overlayType));
188
+ }
189
+ }
190
+ content.append(groupElement);
191
+ }
192
+ const textField = document.createElement('label');
193
+ textField.className = 'baron-kline-toolbar__text-field';
194
+ const textLabel = document.createElement('span');
195
+ textLabel.className = 'baron-kline-toolbar__visually-hidden';
196
+ textLabel.textContent = '标注文本';
197
+ textField.append(textLabel, textInput);
198
+ content.append(textField);
199
+ container.append(root);
200
+ let destroyed = false;
201
+ let unregisterRuntime = () => { };
202
+ const toolbar = {
203
+ element: root,
204
+ destroy() {
205
+ if (destroyed) {
206
+ return;
207
+ }
208
+ destroyed = true;
209
+ unregisterRuntime();
210
+ for (const cleanup of cleanupCallbacks) {
211
+ cleanup();
212
+ }
213
+ tooltip.destroy();
214
+ root.remove();
215
+ },
216
+ };
217
+ unregisterRuntime = registerRuntimeTeardown(runtime, () => toolbar.destroy());
218
+ return toolbar;
219
+ }