@opendata-ai/openchart-vue 6.3.0 → 6.5.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendata-ai/openchart-vue",
3
- "version": "6.3.0",
3
+ "version": "6.5.0",
4
4
  "description": "Vue 3 components for openchart: <Chart />, <DataTable />, <Graph />, <VizThemeProvider />",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Riley Hilliard",
@@ -49,9 +49,9 @@
49
49
  "typecheck": "tsc --noEmit"
50
50
  },
51
51
  "dependencies": {
52
- "@opendata-ai/openchart-core": "6.3.0",
53
- "@opendata-ai/openchart-engine": "6.3.0",
54
- "@opendata-ai/openchart-vanilla": "6.3.0"
52
+ "@opendata-ai/openchart-core": "6.5.0",
53
+ "@opendata-ai/openchart-engine": "6.5.0",
54
+ "@opendata-ai/openchart-vanilla": "6.5.0"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "vue": ">=3.3.0"
package/src/Chart.ts CHANGED
@@ -12,6 +12,7 @@ import type {
12
12
  ChartSpec,
13
13
  DarkMode,
14
14
  ElementEdit,
15
+ ElementRef,
15
16
  GraphSpec,
16
17
  LayerSpec,
17
18
  MarkEvent,
@@ -64,6 +65,10 @@ export const Chart = defineComponent({
64
65
  type: [String, Object] as PropType<string | CSSProperties>,
65
66
  default: undefined,
66
67
  },
68
+ selectedElement: {
69
+ type: Object as PropType<ElementRef>,
70
+ default: undefined,
71
+ },
67
72
  },
68
73
  emits: {
69
74
  'mark-click': (_event: MarkEvent) => true,
@@ -73,9 +78,12 @@ export const Chart = defineComponent({
73
78
  'annotation-click': (_annotation: Annotation, _event: MouseEvent) => true,
74
79
  'annotation-edit': (_annotation: TextAnnotation, _updatedOffset: AnnotationOffset) => true,
75
80
  edit: (_edit: ElementEdit) => true,
81
+ select: (_element: ElementRef) => true,
82
+ deselect: (_element: ElementRef) => true,
83
+ 'text-edit': (_element: ElementRef, _oldText: string, _newText: string) => true,
76
84
  'data-point-click': (_data: Record<string, unknown>) => true,
77
85
  },
78
- setup(props, { emit }) {
86
+ setup(props, { emit, expose }) {
79
87
  const containerRef = ref<HTMLDivElement | null>(null);
80
88
  let instance: ChartInstance | null = null;
81
89
  let prevSpec = '';
@@ -92,6 +100,9 @@ export const Chart = defineComponent({
92
100
  const hasAnnotationEditListener =
93
101
  'onAnnotation-edit' in vnodeProps || 'onAnnotationEdit' in vnodeProps;
94
102
  const hasEditListener = 'onEdit' in vnodeProps;
103
+ const hasSelectListener = 'onSelect' in vnodeProps;
104
+ const hasDeselectListener = 'onDeselect' in vnodeProps;
105
+ const hasTextEditListener = 'onText-edit' in vnodeProps || 'onTextEdit' in vnodeProps;
95
106
 
96
107
  function resolveTheme(): ThemeConfig | undefined {
97
108
  return props.theme ?? contextTheme?.value;
@@ -125,6 +136,19 @@ export const Chart = defineComponent({
125
136
  }
126
137
  : {}),
127
138
  ...(hasEditListener ? { onEdit: (edit: ElementEdit) => emit('edit', edit) } : {}),
139
+ ...(hasSelectListener
140
+ ? { onSelect: (element: ElementRef) => emit('select', element) }
141
+ : {}),
142
+ ...(hasDeselectListener
143
+ ? { onDeselect: (element: ElementRef) => emit('deselect', element) }
144
+ : {}),
145
+ ...(hasTextEditListener
146
+ ? {
147
+ onTextEdit: (element: ElementRef, oldText: string, newText: string) =>
148
+ emit('text-edit', element, oldText, newText),
149
+ }
150
+ : {}),
151
+ ...(props.selectedElement ? { selectedElement: props.selectedElement } : {}),
128
152
  responsive: true,
129
153
  };
130
154
 
@@ -138,6 +162,22 @@ export const Chart = defineComponent({
138
162
  prevSpec = '';
139
163
  }
140
164
 
165
+ // Expose imperative methods for parent ref access
166
+ expose({
167
+ getSelectedElement(): ElementRef | null {
168
+ return instance?.getSelectedElement() ?? null;
169
+ },
170
+ select(elementRef: ElementRef): void {
171
+ instance?.select(elementRef);
172
+ },
173
+ deselect(): void {
174
+ instance?.deselect();
175
+ },
176
+ get instance() {
177
+ return instance;
178
+ },
179
+ });
180
+
141
181
  onMounted(() => {
142
182
  mountChart();
143
183
  });
@@ -153,7 +193,20 @@ export const Chart = defineComponent({
153
193
  if (!instance) return;
154
194
  if (newVal !== prevSpec) {
155
195
  prevSpec = newVal;
156
- instance.update(props.spec);
196
+ instance.update(props.spec, { selectedElement: props.selectedElement });
197
+ }
198
+ },
199
+ );
200
+
201
+ // Watch selectedElement prop changes
202
+ watch(
203
+ () => props.selectedElement,
204
+ (newVal) => {
205
+ if (!instance) return;
206
+ if (newVal) {
207
+ instance.select(newVal);
208
+ } else {
209
+ instance.deselect();
157
210
  }
158
211
  },
159
212
  );
@@ -174,7 +227,7 @@ export const Chart = defineComponent({
174
227
  );
175
228
 
176
229
  const rootClass = () => {
177
- const base = 'viz-chart-root';
230
+ const base = 'oc-chart-root';
178
231
  return props.class ? `${base} ${props.class}` : base;
179
232
  };
180
233
 
package/src/DataTable.ts CHANGED
@@ -180,7 +180,7 @@ export const DataTable = defineComponent({
180
180
  });
181
181
 
182
182
  const rootClass = () => {
183
- const base = 'viz-table-root';
183
+ const base = 'oc-table-root';
184
184
  return props.class ? `${base} ${props.class}` : base;
185
185
  };
186
186
 
package/src/Graph.ts CHANGED
@@ -165,7 +165,7 @@ export const Graph = defineComponent({
165
165
  );
166
166
 
167
167
  const rootClass = () => {
168
- const base = 'viz-graph-root';
168
+ const base = 'oc-graph-root';
169
169
  return props.class ? `${base} ${props.class}` : base;
170
170
  };
171
171
 
@@ -67,21 +67,21 @@ describe('Chart', () => {
67
67
  const wrapper = await mountChart({ spec: lineSpec });
68
68
  const svg = wrapper.find('svg');
69
69
  expect(svg.exists()).toBe(true);
70
- expect(svg.attributes('class')).toBe('viz-chart');
70
+ expect(svg.attributes('class')).toBe('oc-chart');
71
71
  wrapper.unmount();
72
72
  });
73
73
 
74
74
  it('renders chrome text elements', async () => {
75
75
  const wrapper = await mountChart({ spec: lineSpec });
76
76
 
77
- const title = wrapper.find('.viz-title');
77
+ const title = wrapper.find('.oc-title');
78
78
  expect(title.exists()).toBe(true);
79
79
  expect(title.text()).toBe('GDP Growth');
80
80
 
81
- const subtitle = wrapper.find('.viz-subtitle');
81
+ const subtitle = wrapper.find('.oc-subtitle');
82
82
  expect(subtitle.text()).toBe('US vs UK over time');
83
83
 
84
- const source = wrapper.find('.viz-source');
84
+ const source = wrapper.find('.oc-source');
85
85
  expect(source.text()).toBe('World Bank');
86
86
  wrapper.unmount();
87
87
  });
@@ -89,13 +89,13 @@ describe('Chart', () => {
89
89
  it('spec changes trigger re-render', async () => {
90
90
  const wrapper = await mountChart({ spec: lineSpec });
91
91
 
92
- const titleBefore = wrapper.find('.viz-title');
92
+ const titleBefore = wrapper.find('.oc-title');
93
93
  expect(titleBefore.text()).toBe('GDP Growth');
94
94
 
95
95
  await wrapper.setProps({ spec: barSpec });
96
96
  await flushPromises();
97
97
 
98
- const titleAfter = wrapper.find('.viz-title');
98
+ const titleAfter = wrapper.find('.oc-title');
99
99
  expect(titleAfter.text()).toBe('Updated Title');
100
100
  wrapper.unmount();
101
101
  });
@@ -79,13 +79,13 @@ describe('DataTable', () => {
79
79
  it('spec changes trigger re-render', async () => {
80
80
  const wrapper = await mountTable({ spec: tableSpec });
81
81
 
82
- const titleBefore = wrapper.find('.viz-table-title');
82
+ const titleBefore = wrapper.find('.oc-table-title');
83
83
  expect(titleBefore.text()).toBe('People Table');
84
84
 
85
85
  await wrapper.setProps({ spec: updatedSpec });
86
86
  await flushPromises();
87
87
 
88
- const titleAfter = wrapper.find('.viz-table-title');
88
+ const titleAfter = wrapper.find('.oc-table-title');
89
89
  expect(titleAfter.text()).toBe('Updated Table');
90
90
 
91
91
  const headersAfter = wrapper.findAll('thead th');
@@ -72,11 +72,11 @@ describe('Graph', () => {
72
72
  it('renders chrome text elements', async () => {
73
73
  const wrapper = await mountGraph({ spec: basicSpec });
74
74
 
75
- const title = wrapper.find('.viz-title');
75
+ const title = wrapper.find('.oc-title');
76
76
  expect(title.exists()).toBe(true);
77
77
  expect(title.text()).toBe('Test Graph');
78
78
 
79
- const subtitle = wrapper.find('.viz-subtitle');
79
+ const subtitle = wrapper.find('.oc-subtitle');
80
80
  expect(subtitle.text()).toBe('A simple test graph');
81
81
  wrapper.unmount();
82
82
  });
@@ -84,13 +84,13 @@ describe('Graph', () => {
84
84
  it('spec changes trigger re-render', async () => {
85
85
  const wrapper = await mountGraph({ spec: basicSpec });
86
86
 
87
- const titleBefore = wrapper.find('.viz-title');
87
+ const titleBefore = wrapper.find('.oc-title');
88
88
  expect(titleBefore.text()).toBe('Test Graph');
89
89
 
90
90
  await wrapper.setProps({ spec: updatedSpec });
91
91
  await flushPromises();
92
92
 
93
- const titleAfter = wrapper.find('.viz-title');
93
+ const titleAfter = wrapper.find('.oc-title');
94
94
  expect(titleAfter.text()).toBe('Updated Graph');
95
95
  wrapper.unmount();
96
96
  });