@object-ui/plugin-view 3.3.0 → 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.
- package/CHANGELOG.md +12 -0
- package/README.md +21 -1
- package/dist/index.js +2679 -2936
- package/dist/index.umd.cjs +1 -5
- package/dist/packages/plugin-view/src/config/view-config-schema.d.ts +20 -0
- package/dist/packages/plugin-view/src/config/view-config-utils.d.ts +58 -0
- package/dist/packages/plugin-view/src/index.d.ts +4 -0
- package/package.json +41 -9
- package/.turbo/turbo-build.log +0 -39
- package/src/FilterUI.tsx +0 -350
- package/src/ObjectView.tsx +0 -1133
- package/src/SharedViewLink.tsx +0 -199
- package/src/SortUI.tsx +0 -210
- package/src/ViewSwitcher.tsx +0 -379
- package/src/ViewTabBar.tsx +0 -656
- package/src/__tests__/FilterUI.test.tsx +0 -641
- package/src/__tests__/ObjectView.test.tsx +0 -705
- package/src/__tests__/SharedViewLinkPassword.test.tsx +0 -172
- package/src/__tests__/SortUI.test.tsx +0 -380
- package/src/__tests__/ViewTabBar.test.tsx +0 -710
- package/src/__tests__/config-sync-integration.test.tsx +0 -588
- package/src/__tests__/toolbar-consistency.test.tsx +0 -755
- package/src/index.tsx +0 -197
- package/tsconfig.json +0 -8
- package/vite.config.ts +0 -48
- package/vitest.config.ts +0 -12
- package/vitest.setup.ts +0 -1
package/.turbo/turbo-build.log
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @object-ui/plugin-view@3.3.0 build /home/runner/work/objectui/objectui/packages/plugin-view
|
|
3
|
-
> vite build
|
|
4
|
-
|
|
5
|
-
[36mvite v8.0.8 [32mbuilding client environment for production...[36m[39m
|
|
6
|
-
[2K
|
|
7
|
-
rendering chunks...
|
|
8
|
-
[96msrc/ObjectView.tsx[0m:[93m662[0m:[93m9[0m - [91merror[0m[90m TS2591: [0mCannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
|
|
9
|
-
[32m
|
|
10
|
-
[36m[vite:dts][32m Start generate declaration files...[39m
|
|
11
|
-
|
|
12
|
-
[7m662[0m if (process.env.NODE_ENV === 'development') {
|
|
13
|
-
[7m [0m [91m ~~~~~~~[0m
|
|
14
|
-
|
|
15
|
-
[32m[36m[vite:dts][32m Declaration files built in 9034ms.
|
|
16
|
-
[39m
|
|
17
|
-
computing gzip size...
|
|
18
|
-
dist/index.js 119.40 kB │ gzip: 32.03 kB
|
|
19
|
-
|
|
20
|
-
[33m[33m[PLUGIN_TIMINGS] Warning:[0m Your build spent significant time in plugin `vite:dts`. See https://rolldown.rs/options/checks#plugintimings for more details.
|
|
21
|
-
[39m
|
|
22
|
-
[2K
|
|
23
|
-
rendering chunks...
|
|
24
|
-
computing gzip size...
|
|
25
|
-
dist/index.umd.cjs 90.77 kB │ gzip: 27.65 kB
|
|
26
|
-
|
|
27
|
-
[33m[33m[MISSING_GLOBAL_NAME] Warning:[0m No name was provided for external module "@object-ui/core" in "output.globals" – guessing "_object_ui_core".
|
|
28
|
-
[39m
|
|
29
|
-
[33m[33m[MISSING_GLOBAL_NAME] Warning:[0m No name was provided for external module "@object-ui/react" in "output.globals" – guessing "_object_ui_react".
|
|
30
|
-
[39m
|
|
31
|
-
[33m[33m[MISSING_GLOBAL_NAME] Warning:[0m No name was provided for external module "@object-ui/plugin-grid" in "output.globals" – guessing "_object_ui_plugin_grid".
|
|
32
|
-
[39m
|
|
33
|
-
[33m[33m[MISSING_GLOBAL_NAME] Warning:[0m No name was provided for external module "@object-ui/plugin-form" in "output.globals" – guessing "_object_ui_plugin_form".
|
|
34
|
-
[39m
|
|
35
|
-
[33m[33m[MISSING_GLOBAL_NAME] Warning:[0m No name was provided for external module "@object-ui/components" in "output.globals" – guessing "_object_ui_components".
|
|
36
|
-
[39m
|
|
37
|
-
[33m[33m[MISSING_GLOBAL_NAME] Warning:[0m No name was provided for external module "lucide-react" in "output.globals" – guessing "lucide_react".
|
|
38
|
-
[39m
|
|
39
|
-
[32m✓ built in 9.53s[39m
|
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
|
-
};
|