@object-ui/plugin-view 3.1.5 → 3.3.1

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 (34) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +21 -1
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js +2716 -3140
  5. package/dist/index.umd.cjs +1 -10
  6. package/dist/{plugin-view → packages/plugin-view}/src/ObjectView.d.ts +2 -0
  7. package/dist/packages/plugin-view/src/config/view-config-schema.d.ts +20 -0
  8. package/dist/packages/plugin-view/src/config/view-config-utils.d.ts +58 -0
  9. package/dist/{plugin-view → packages/plugin-view}/src/index.d.ts +4 -0
  10. package/package.json +42 -10
  11. package/.turbo/turbo-build.log +0 -34
  12. package/src/FilterUI.tsx +0 -350
  13. package/src/ObjectView.tsx +0 -1109
  14. package/src/SharedViewLink.tsx +0 -199
  15. package/src/SortUI.tsx +0 -210
  16. package/src/ViewSwitcher.tsx +0 -379
  17. package/src/ViewTabBar.tsx +0 -656
  18. package/src/__tests__/FilterUI.test.tsx +0 -641
  19. package/src/__tests__/ObjectView.test.tsx +0 -705
  20. package/src/__tests__/SharedViewLinkPassword.test.tsx +0 -172
  21. package/src/__tests__/SortUI.test.tsx +0 -380
  22. package/src/__tests__/ViewTabBar.test.tsx +0 -710
  23. package/src/__tests__/config-sync-integration.test.tsx +0 -588
  24. package/src/__tests__/toolbar-consistency.test.tsx +0 -755
  25. package/src/index.tsx +0 -197
  26. package/tsconfig.json +0 -8
  27. package/vite.config.ts +0 -44
  28. package/vitest.config.ts +0 -12
  29. package/vitest.setup.ts +0 -1
  30. /package/dist/{plugin-view → packages/plugin-view}/src/FilterUI.d.ts +0 -0
  31. /package/dist/{plugin-view → packages/plugin-view}/src/SharedViewLink.d.ts +0 -0
  32. /package/dist/{plugin-view → packages/plugin-view}/src/SortUI.d.ts +0 -0
  33. /package/dist/{plugin-view → packages/plugin-view}/src/ViewSwitcher.d.ts +0 -0
  34. /package/dist/{plugin-view → packages/plugin-view}/src/ViewTabBar.d.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@object-ui/plugin-view",
3
- "version": "3.1.5",
3
+ "version": "3.3.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Object View plugin for Object UI",
@@ -19,13 +19,13 @@
19
19
  "@dnd-kit/sortable": "^10.0.0",
20
20
  "@dnd-kit/utilities": "^3.2.2",
21
21
  "class-variance-authority": "^0.7.1",
22
- "lucide-react": "^0.577.0",
23
- "@object-ui/components": "3.1.5",
24
- "@object-ui/core": "3.1.5",
25
- "@object-ui/plugin-form": "3.1.5",
26
- "@object-ui/plugin-grid": "3.1.5",
27
- "@object-ui/react": "3.1.5",
28
- "@object-ui/types": "3.1.5"
22
+ "lucide-react": "^1.8.0",
23
+ "@object-ui/components": "3.3.1",
24
+ "@object-ui/core": "3.3.1",
25
+ "@object-ui/plugin-form": "3.3.1",
26
+ "@object-ui/plugin-grid": "3.3.1",
27
+ "@object-ui/react": "3.3.1",
28
+ "@object-ui/types": "3.3.1"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "react": "^18.0.0 || ^19.0.0",
@@ -33,10 +33,42 @@
33
33
  },
34
34
  "devDependencies": {
35
35
  "@vitejs/plugin-react": "^6.0.1",
36
- "typescript": "^5.9.3",
37
- "vite": "^8.0.1",
36
+ "typescript": "^6.0.3",
37
+ "vite": "^8.0.9",
38
38
  "vite-plugin-dts": "^4.5.4"
39
39
  },
40
+ "keywords": [
41
+ "objectui",
42
+ "sdui",
43
+ "schema-driven-ui",
44
+ "react",
45
+ "tailwind",
46
+ "shadcn",
47
+ "objectstack",
48
+ "plugin",
49
+ "view",
50
+ "objectql",
51
+ "record-view"
52
+ ],
53
+ "author": "ObjectStack Team <team@objectstack.ai>",
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "git+https://github.com/objectstack-ai/objectui.git",
57
+ "directory": "packages/plugin-view"
58
+ },
59
+ "bugs": {
60
+ "url": "https://github.com/objectstack-ai/objectui/issues"
61
+ },
62
+ "homepage": "https://www.objectui.org/docs/plugins/plugin-view",
63
+ "publishConfig": {
64
+ "access": "public"
65
+ },
66
+ "files": [
67
+ "dist",
68
+ "README.md",
69
+ "CHANGELOG.md",
70
+ "LICENSE"
71
+ ],
40
72
  "scripts": {
41
73
  "build": "vite build",
42
74
  "test": "vitest run",
@@ -1,34 +0,0 @@
1
-
2
- > @object-ui/plugin-view@3.1.5 build /home/runner/work/objectui/objectui/packages/plugin-view
3
- > vite build
4
-
5
- vite v8.0.1 building client environment for production...
6
- 
7
- rendering chunks...
8
- 
9
- [vite:dts] Start generate declaration files...
10
- [vite:dts] Declaration files built in 6270ms.
11
- 
12
- computing gzip size...
13
- dist/index.js 126.91 kB │ gzip: 34.50 kB
14
-
15
- [PLUGIN_TIMINGS] Warning: Your build spent significant time in plugin `vite:dts`. See https://rolldown.rs/options/checks#plugintimings for more details.
16
- 
17
- 
18
- rendering chunks...
19
- computing gzip size...
20
- dist/index.umd.cjs 96.40 kB │ gzip: 29.72 kB
21
-
22
- [MISSING_GLOBAL_NAME] Warning: No name was provided for external module "@object-ui/core" in "output.globals" – guessing "_object_ui_core".
23
- 
24
- [MISSING_GLOBAL_NAME] Warning: No name was provided for external module "@object-ui/react" in "output.globals" – guessing "_object_ui_react".
25
- 
26
- [MISSING_GLOBAL_NAME] Warning: No name was provided for external module "@object-ui/plugin-grid" in "output.globals" – guessing "_object_ui_plugin_grid".
27
- 
28
- [MISSING_GLOBAL_NAME] Warning: No name was provided for external module "@object-ui/plugin-form" in "output.globals" – guessing "_object_ui_plugin_form".
29
- 
30
- [MISSING_GLOBAL_NAME] Warning: No name was provided for external module "@object-ui/components" in "output.globals" – guessing "_object_ui_components".
31
- 
32
- [MISSING_GLOBAL_NAME] Warning: No name was provided for external module "lucide-react" in "output.globals" – guessing "lucide_react".
33
- 
34
- ✓ built in 6.58s
package/src/FilterUI.tsx DELETED
@@ -1,350 +0,0 @@
1
- /**
2
- * ObjectUI
3
- * Copyright (c) 2024-present ObjectStack Inc.
4
- *
5
- * This source code is licensed under the MIT license found in the
6
- * LICENSE file in the root directory of this source tree.
7
- */
8
-
9
- import * as React from 'react';
10
- import {
11
- cn,
12
- Button,
13
- Checkbox,
14
- Drawer,
15
- DrawerContent,
16
- DrawerDescription,
17
- DrawerHeader,
18
- DrawerTitle,
19
- Input,
20
- Label,
21
- Popover,
22
- PopoverContent,
23
- PopoverTrigger,
24
- Select,
25
- SelectContent,
26
- SelectItem,
27
- SelectTrigger,
28
- SelectValue,
29
- } from '@object-ui/components';
30
- import { cva } from 'class-variance-authority';
31
- import { SlidersHorizontal, X } from 'lucide-react';
32
- import type { FilterUISchema } from '@object-ui/types';
33
-
34
- export type FilterUIProps = {
35
- schema: FilterUISchema;
36
- className?: string;
37
- onChange?: (values: Record<string, any>) => void;
38
- [key: string]: any;
39
- };
40
-
41
- type FilterValue = Record<string, any>;
42
-
43
- type FilterConfig = FilterUISchema['filters'][number];
44
-
45
- type DateRangeValue = {
46
- start?: string;
47
- end?: string;
48
- };
49
-
50
- const filterContainerVariants = cva('flex', {
51
- variants: {
52
- layout: {
53
- inline: 'flex-col space-y-4',
54
- popover: 'items-center',
55
- drawer: 'items-center',
56
- },
57
- },
58
- defaultVariants: {
59
- layout: 'inline',
60
- },
61
- });
62
-
63
- const isEmptyValue = (value: any): boolean => {
64
- if (value === null || value === undefined || value === '') return true;
65
- if (Array.isArray(value)) return value.length === 0;
66
- if (typeof value === 'object') {
67
- return Object.values(value).every(v => v === null || v === undefined || v === '');
68
- }
69
- return false;
70
- };
71
-
72
- const getDateRangeValue = (value: any): DateRangeValue => {
73
- if (Array.isArray(value)) {
74
- return { start: value[0] || '', end: value[1] || '' };
75
- }
76
- if (value && typeof value === 'object') {
77
- return { start: value.start || '', end: value.end || '' };
78
- }
79
- return { start: '', end: '' };
80
- };
81
-
82
- export const FilterUI: React.FC<FilterUIProps> = ({
83
- schema,
84
- className,
85
- onChange,
86
- }) => {
87
- const [values, setValues] = React.useState<FilterValue>(schema.values || {});
88
- const [open, setOpen] = React.useState(false);
89
-
90
- React.useEffect(() => {
91
- if (schema.values) {
92
- setValues(schema.values);
93
- }
94
- }, [schema.values]);
95
-
96
- const notifyChange = React.useCallback((nextValues: FilterValue) => {
97
- onChange?.(nextValues);
98
-
99
- if (schema.onChange && typeof window !== 'undefined') {
100
- window.dispatchEvent(
101
- new CustomEvent(schema.onChange, {
102
- detail: { values: nextValues },
103
- })
104
- );
105
- }
106
- }, [onChange, schema.onChange]);
107
-
108
- const updateValue = React.useCallback((field: string, value: any) => {
109
- const nextValues = { ...values, [field]: value };
110
- setValues(nextValues);
111
-
112
- if (!schema.showApply) {
113
- notifyChange(nextValues);
114
- }
115
- }, [notifyChange, schema.showApply, values]);
116
-
117
- const clearValues = React.useCallback(() => {
118
- const nextValues: FilterValue = {};
119
- setValues(nextValues);
120
-
121
- if (schema.showApply) {
122
- notifyChange(nextValues);
123
- return;
124
- }
125
-
126
- notifyChange(nextValues);
127
- }, [notifyChange, schema.showApply]);
128
-
129
- const applyValues = React.useCallback(() => {
130
- notifyChange(values);
131
- setOpen(false);
132
- }, [notifyChange, values]);
133
-
134
- const activeCount = React.useMemo(() => {
135
- return Object.values(values).filter(value => !isEmptyValue(value)).length;
136
- }, [values]);
137
-
138
- const renderInput = (filter: FilterConfig) => {
139
- const label = filter.label || filter.field;
140
- const placeholder = filter.placeholder || `Filter by ${label}`;
141
-
142
- switch (filter.type) {
143
- case 'number':
144
- return (
145
- <Input
146
- type="number"
147
- value={values[filter.field] ?? ''}
148
- placeholder={placeholder}
149
- onChange={(event) => {
150
- const raw = event.target.value;
151
- const parsed = raw === '' ? '' : Number(raw);
152
- updateValue(filter.field, parsed);
153
- }}
154
- />
155
- );
156
- case 'select':
157
- return (
158
- <Select
159
- value={values[filter.field] !== undefined ? String(values[filter.field]) : ''}
160
- onValueChange={(value) => {
161
- const option = filter.options?.find(opt => String(opt.value) === value);
162
- updateValue(filter.field, option ? option.value : value);
163
- }}
164
- >
165
- <SelectTrigger>
166
- <SelectValue placeholder={placeholder} />
167
- </SelectTrigger>
168
- <SelectContent>
169
- {filter.options?.map(option => (
170
- <SelectItem key={String(option.value)} value={String(option.value)}>
171
- {option.label}
172
- </SelectItem>
173
- ))}
174
- </SelectContent>
175
- </Select>
176
- );
177
- case 'multi-select': {
178
- const currentVal = values[filter.field];
179
- const selectedValues: (string | number | boolean)[] = Array.isArray(currentVal)
180
- ? currentVal
181
- : currentVal ? [currentVal] : [];
182
- return (
183
- <div className="max-h-40 overflow-y-auto space-y-0.5 border rounded-md p-2">
184
- {filter.options?.map(option => {
185
- const isChecked = selectedValues.map(String).includes(String(option.value));
186
- return (
187
- <label
188
- key={String(option.value)}
189
- className={cn(
190
- 'flex items-center gap-2 text-sm py-1 px-1.5 rounded cursor-pointer',
191
- isChecked ? 'bg-primary/5 text-primary' : 'hover:bg-muted',
192
- )}
193
- >
194
- <Checkbox
195
- checked={isChecked}
196
- onCheckedChange={(checked) => {
197
- const next = checked
198
- ? [...selectedValues, option.value]
199
- : selectedValues.filter(v => String(v) !== String(option.value));
200
- updateValue(filter.field, next);
201
- }}
202
- />
203
- <span className="truncate">{option.label}</span>
204
- </label>
205
- );
206
- })}
207
- </div>
208
- );
209
- }
210
- case 'date':
211
- return (
212
- <Input
213
- type="date"
214
- value={values[filter.field] ?? ''}
215
- onChange={(event) => updateValue(filter.field, event.target.value)}
216
- />
217
- );
218
- case 'date-range': {
219
- const range = getDateRangeValue(values[filter.field]);
220
- return (
221
- <div className="flex items-center gap-2">
222
- <Input
223
- type="date"
224
- value={range.start ?? ''}
225
- onChange={(event) => {
226
- updateValue(filter.field, { ...range, start: event.target.value });
227
- }}
228
- />
229
- <Input
230
- type="date"
231
- value={range.end ?? ''}
232
- onChange={(event) => {
233
- updateValue(filter.field, { ...range, end: event.target.value });
234
- }}
235
- />
236
- </div>
237
- );
238
- }
239
- case 'boolean':
240
- return (
241
- <div className="flex items-center gap-2">
242
- <Checkbox
243
- checked={Boolean(values[filter.field])}
244
- onCheckedChange={(checked) => updateValue(filter.field, Boolean(checked))}
245
- />
246
- <span className="text-sm text-muted-foreground">Enabled</span>
247
- </div>
248
- );
249
- case 'text':
250
- default:
251
- return (
252
- <Input
253
- value={values[filter.field] ?? ''}
254
- placeholder={placeholder}
255
- onChange={(event) => updateValue(filter.field, event.target.value)}
256
- />
257
- );
258
- }
259
- };
260
-
261
- const form = (
262
- <div className="space-y-4">
263
- <div className="grid gap-4 sm:grid-cols-2">
264
- {schema.filters.map(filter => (
265
- <div key={filter.field} className="space-y-2">
266
- <Label className="text-xs text-muted-foreground">{filter.label || filter.field}</Label>
267
- {renderInput(filter)}
268
- </div>
269
- ))}
270
- </div>
271
-
272
- {(schema.showApply || schema.showClear) && (
273
- <div className="flex items-center justify-end gap-2 border-t pt-3">
274
- {schema.showClear && (
275
- <Button type="button" variant="ghost" size="sm" onClick={clearValues}>
276
- Clear
277
- </Button>
278
- )}
279
- {schema.showApply && (
280
- <Button type="button" size="sm" onClick={applyValues}>
281
- Apply
282
- </Button>
283
- )}
284
- </div>
285
- )}
286
- </div>
287
- );
288
-
289
- const layout = schema.layout || 'inline';
290
-
291
- if (layout === 'popover') {
292
- return (
293
- <div className={cn(filterContainerVariants({ layout }), className)}>
294
- <Popover open={open} onOpenChange={setOpen}>
295
- <PopoverTrigger asChild>
296
- <Button type="button" variant={activeCount > 0 ? 'secondary' : 'outline'} size="sm" className="gap-2">
297
- <SlidersHorizontal className="h-4 w-4" />
298
- Filters
299
- {activeCount > 0 && (
300
- <span className="inline-flex h-5 min-w-[20px] items-center justify-center rounded-full bg-primary/10 px-1 text-xs font-medium text-primary">
301
- {activeCount}
302
- </span>
303
- )}
304
- </Button>
305
- </PopoverTrigger>
306
- <PopoverContent align="start" className="w-[520px] p-4">
307
- {form}
308
- </PopoverContent>
309
- </Popover>
310
- </div>
311
- );
312
- }
313
-
314
- if (layout === 'drawer') {
315
- return (
316
- <div className={cn(filterContainerVariants({ layout }), className)}>
317
- <Button type="button" variant={activeCount > 0 ? 'secondary' : 'outline'} size="sm" className="gap-2" onClick={() => setOpen(true)}>
318
- <SlidersHorizontal className="h-4 w-4" />
319
- Filters
320
- {activeCount > 0 && (
321
- <span className="inline-flex h-5 min-w-[20px] items-center justify-center rounded-full bg-primary/10 px-1 text-xs font-medium text-primary">
322
- {activeCount}
323
- </span>
324
- )}
325
- </Button>
326
- <Drawer open={open} onOpenChange={setOpen}>
327
- <DrawerContent>
328
- <DrawerHeader>
329
- <DrawerTitle>Filters</DrawerTitle>
330
- <DrawerDescription>Refine the data with advanced filters.</DrawerDescription>
331
- </DrawerHeader>
332
- <div className="px-4 pb-6">{form}</div>
333
- </DrawerContent>
334
- </Drawer>
335
- </div>
336
- );
337
- }
338
-
339
- return (
340
- <div className={cn(filterContainerVariants({ layout }), className)}>
341
- {form}
342
- {!schema.showApply && schema.showClear && (
343
- <Button type="button" variant="ghost" size="sm" className="gap-2" onClick={clearValues}>
344
- <X className="h-3.5 w-3.5" />
345
- Clear filters
346
- </Button>
347
- )}
348
- </div>
349
- );
350
- };