@simple-base/solid 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.
- package/LICENSE +21 -0
- package/dist/index.d.ts +200 -0
- package/dist/index.js +941 -0
- package/dist/index.jsx +563 -0
- package/package.json +44 -0
package/dist/index.jsx
ADDED
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
import { For, Show, createContext, createEffect, createMemo, createSignal, createUniqueId, on, onCleanup, onMount, splitProps, useContext } from "solid-js";
|
|
2
|
+
import { cn } from "cn";
|
|
3
|
+
import { badgeDefaults, buttonDefaults } from "@simple-base/contracts";
|
|
4
|
+
import { Portal } from "solid-js/web";
|
|
5
|
+
import * as combobox from "@zag-js/combobox";
|
|
6
|
+
import { mergeProps, normalizeProps, useMachine } from "@zag-js/solid";
|
|
7
|
+
import * as select from "@zag-js/select";
|
|
8
|
+
//#region src/components/AlertDialog.tsx
|
|
9
|
+
const AlertDialogContext = createContext(null);
|
|
10
|
+
function useAlertDialog() {
|
|
11
|
+
const context = useContext(AlertDialogContext);
|
|
12
|
+
if (!context) throw new Error("useAlertDialog must be used within an AlertDialogRoot");
|
|
13
|
+
return context;
|
|
14
|
+
}
|
|
15
|
+
function AlertDialogRoot(props) {
|
|
16
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
17
|
+
const id = createUniqueId();
|
|
18
|
+
const titleId = `${id}-title`;
|
|
19
|
+
const descriptionId = `${id}-description`;
|
|
20
|
+
return <AlertDialogContext.Provider value={{
|
|
21
|
+
titleId,
|
|
22
|
+
descriptionId
|
|
23
|
+
}}>
|
|
24
|
+
<dialog {...rest} role="alertdialog" aria-labelledby={titleId} aria-describedby={descriptionId} class={cn("sb-alert-dialog", local.class)} />
|
|
25
|
+
</AlertDialogContext.Provider>;
|
|
26
|
+
}
|
|
27
|
+
function AlertDialogIcon(props) {
|
|
28
|
+
const [local, rest] = splitProps(props, ["class", "aria-hidden"]);
|
|
29
|
+
return <div {...rest} aria-hidden={local["aria-hidden"] ?? true} class={cn("sb-alert-dialog-icon", local.class)} />;
|
|
30
|
+
}
|
|
31
|
+
function AlertDialogContent(props) {
|
|
32
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
33
|
+
return <div {...rest} class={cn("sb-alert-dialog-content", local.class)} />;
|
|
34
|
+
}
|
|
35
|
+
function AlertDialogKicker(props) {
|
|
36
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
37
|
+
return <p {...rest} class={cn("sb-alert-dialog-kicker", local.class)} />;
|
|
38
|
+
}
|
|
39
|
+
function AlertDialogTitle(props) {
|
|
40
|
+
const { titleId } = useAlertDialog();
|
|
41
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
42
|
+
return <h3 {...rest} id={titleId} class={cn("sb-alert-dialog-title", local.class)} />;
|
|
43
|
+
}
|
|
44
|
+
function AlertDialogDescription(props) {
|
|
45
|
+
const { descriptionId } = useAlertDialog();
|
|
46
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
47
|
+
return <p {...rest} id={descriptionId} class={cn("sb-alert-dialog-description", local.class)} />;
|
|
48
|
+
}
|
|
49
|
+
function AlertDialogActions(props) {
|
|
50
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
51
|
+
return <div {...rest} class={cn("sb-alert-dialog-actions", local.class)} />;
|
|
52
|
+
}
|
|
53
|
+
const AlertDialog = Object.assign(AlertDialogRoot, {
|
|
54
|
+
Icon: AlertDialogIcon,
|
|
55
|
+
Content: AlertDialogContent,
|
|
56
|
+
Kicker: AlertDialogKicker,
|
|
57
|
+
Title: AlertDialogTitle,
|
|
58
|
+
Description: AlertDialogDescription,
|
|
59
|
+
Actions: AlertDialogActions
|
|
60
|
+
});
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/components/Badge.tsx
|
|
63
|
+
function Badge(props) {
|
|
64
|
+
const [local, rest] = splitProps(props, [
|
|
65
|
+
"class",
|
|
66
|
+
"variant",
|
|
67
|
+
"size"
|
|
68
|
+
]);
|
|
69
|
+
return <span {...rest} class={cn("sb-badge", local.class)} data-variant={local.variant ?? badgeDefaults.variant} data-size={local.size ?? badgeDefaults.size} />;
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/components/Button.tsx
|
|
73
|
+
function Button(props) {
|
|
74
|
+
const [local, rest] = splitProps(props, [
|
|
75
|
+
"class",
|
|
76
|
+
"variant",
|
|
77
|
+
"size"
|
|
78
|
+
]);
|
|
79
|
+
return <button {...rest} class={cn("sb-button", local.class)} data-variant={local.variant ?? buttonDefaults.variant} data-size={local.size ?? buttonDefaults.size} />;
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/components/Switch.tsx
|
|
83
|
+
function Switch(props) {
|
|
84
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
85
|
+
return <input {...rest} type="checkbox" role="switch" class={cn("sb-switch", local.class)} />;
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/components/Checkbox.tsx
|
|
89
|
+
function Checkbox(props) {
|
|
90
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
91
|
+
return <input {...rest} type="checkbox" class={cn("sb-checkbox", local.class)} />;
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/components/Input.tsx
|
|
95
|
+
function Input(props) {
|
|
96
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
97
|
+
return <input {...rest} class={cn("sb-input", local.class)} />;
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/components/Radio.tsx
|
|
101
|
+
function Radio(props) {
|
|
102
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
103
|
+
return <input {...rest} type="radio" class={cn("sb-radio", local.class)} />;
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/components/TextArea.tsx
|
|
107
|
+
function TextArea(props) {
|
|
108
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
109
|
+
return <textarea {...rest} class={cn("sb-textarea", local.class)} />;
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/mergeWidgetProps.ts
|
|
113
|
+
const hyphenate = (property) => property.startsWith("--") ? property : property.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
|
|
114
|
+
const serializeStyle = (style) => {
|
|
115
|
+
if (typeof style === "string") return style;
|
|
116
|
+
let css = "";
|
|
117
|
+
for (const property in style) {
|
|
118
|
+
const value = style[property];
|
|
119
|
+
if (value == null || value === "") continue;
|
|
120
|
+
const declaration = `${hyphenate(property)}: ${value}`;
|
|
121
|
+
css = css ? `${css};${declaration}` : declaration;
|
|
122
|
+
}
|
|
123
|
+
return css;
|
|
124
|
+
};
|
|
125
|
+
const composeStyles = (widgetStyle, consumerStyle) => {
|
|
126
|
+
if (consumerStyle == null) return widgetStyle;
|
|
127
|
+
if (widgetStyle == null) return consumerStyle;
|
|
128
|
+
const widgetCss = serializeStyle(widgetStyle);
|
|
129
|
+
const consumerCss = serializeStyle(consumerStyle);
|
|
130
|
+
if (!widgetCss) return consumerStyle;
|
|
131
|
+
if (!consumerCss) return widgetStyle;
|
|
132
|
+
return `${widgetCss};${consumerCss}`;
|
|
133
|
+
};
|
|
134
|
+
function mergeWidgetProps(behavior, props) {
|
|
135
|
+
const normalized = new Proxy(props, { get(target, key, receiver) {
|
|
136
|
+
const value = Reflect.get(target, key, receiver);
|
|
137
|
+
if (typeof key === "string" && key.startsWith("on") && Array.isArray(value) && typeof value[0] === "function") return (event) => value[0](value[1], event);
|
|
138
|
+
return value;
|
|
139
|
+
} });
|
|
140
|
+
const { style: widgetStyle, ...behaviorProps } = behavior;
|
|
141
|
+
const { style: consumerStyle, ...consumerProps } = normalized;
|
|
142
|
+
const merged = mergeProps(behaviorProps, consumerProps);
|
|
143
|
+
const style = composeStyles(widgetStyle, consumerStyle);
|
|
144
|
+
return style === void 0 ? merged : {
|
|
145
|
+
...merged,
|
|
146
|
+
style
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
150
|
+
//#region src/components/Combobox.tsx
|
|
151
|
+
const ComboboxContext = createContext(null);
|
|
152
|
+
function useCombobox() {
|
|
153
|
+
const context = useContext(ComboboxContext);
|
|
154
|
+
if (!context) throw new Error("useCombobox must be used within a Combobox");
|
|
155
|
+
return context;
|
|
156
|
+
}
|
|
157
|
+
function ComboboxRoot(props) {
|
|
158
|
+
const [local, rest] = splitProps(props, [
|
|
159
|
+
"class",
|
|
160
|
+
"children",
|
|
161
|
+
"id",
|
|
162
|
+
"label",
|
|
163
|
+
"name",
|
|
164
|
+
"placeholder",
|
|
165
|
+
"options",
|
|
166
|
+
"value",
|
|
167
|
+
"disabled",
|
|
168
|
+
"placement",
|
|
169
|
+
"invalid",
|
|
170
|
+
"required",
|
|
171
|
+
"onValueChange",
|
|
172
|
+
"onOpenChange"
|
|
173
|
+
]);
|
|
174
|
+
const [query, setQuery] = createSignal("");
|
|
175
|
+
const options = createMemo(() => {
|
|
176
|
+
const search = query().toLowerCase();
|
|
177
|
+
return local.options.filter((option) => option.label.toLowerCase().includes(search));
|
|
178
|
+
});
|
|
179
|
+
const collection = createMemo(() => combobox.collection({
|
|
180
|
+
items: options(),
|
|
181
|
+
itemToValue: (item) => item.value,
|
|
182
|
+
itemToString: (item) => item.label,
|
|
183
|
+
isItemDisabled: (item) => item.disabled ?? false
|
|
184
|
+
}));
|
|
185
|
+
const positioning = createMemo(() => local.placement ? { placement: local.placement } : void 0);
|
|
186
|
+
const service = useMachine(combobox.machine, {
|
|
187
|
+
get id() {
|
|
188
|
+
return local.id;
|
|
189
|
+
},
|
|
190
|
+
get ids() {
|
|
191
|
+
return { root: local.id };
|
|
192
|
+
},
|
|
193
|
+
get name() {
|
|
194
|
+
return local.name;
|
|
195
|
+
},
|
|
196
|
+
get placeholder() {
|
|
197
|
+
return local.placeholder;
|
|
198
|
+
},
|
|
199
|
+
get disabled() {
|
|
200
|
+
return local.disabled;
|
|
201
|
+
},
|
|
202
|
+
get invalid() {
|
|
203
|
+
return local.invalid;
|
|
204
|
+
},
|
|
205
|
+
get required() {
|
|
206
|
+
return local.required;
|
|
207
|
+
},
|
|
208
|
+
get value() {
|
|
209
|
+
if (local.value === void 0) return void 0;
|
|
210
|
+
return local.value === "" ? [] : [local.value];
|
|
211
|
+
},
|
|
212
|
+
get positioning() {
|
|
213
|
+
return positioning();
|
|
214
|
+
},
|
|
215
|
+
get collection() {
|
|
216
|
+
return collection();
|
|
217
|
+
},
|
|
218
|
+
onOpenChange({ open, reason }) {
|
|
219
|
+
if (open && reason !== "input-change") setQuery("");
|
|
220
|
+
local.onOpenChange?.(open);
|
|
221
|
+
},
|
|
222
|
+
onInputValueChange({ inputValue, reason }) {
|
|
223
|
+
setQuery(reason === "input-change" ? inputValue : "");
|
|
224
|
+
},
|
|
225
|
+
onValueChange({ value }) {
|
|
226
|
+
local.onValueChange(value[0] ?? "");
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
const api = createMemo(() => combobox.connect(service, normalizeProps));
|
|
230
|
+
return <ComboboxContext.Provider value={{
|
|
231
|
+
label: () => local.label,
|
|
232
|
+
options,
|
|
233
|
+
api
|
|
234
|
+
}}>
|
|
235
|
+
<div {...mergeWidgetProps(api().getRootProps(), rest)} class={cn("sb-combobox", local.class)}>
|
|
236
|
+
{local.children}
|
|
237
|
+
</div>
|
|
238
|
+
</ComboboxContext.Provider>;
|
|
239
|
+
}
|
|
240
|
+
function ComboboxLabel(props) {
|
|
241
|
+
const { api, label } = useCombobox();
|
|
242
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
243
|
+
return <label {...mergeWidgetProps(api().getLabelProps(), rest)} class={cn("sb-combobox-label", local.class)}>
|
|
244
|
+
{local.children ?? label()}
|
|
245
|
+
</label>;
|
|
246
|
+
}
|
|
247
|
+
function ComboboxControl(props) {
|
|
248
|
+
const { api } = useCombobox();
|
|
249
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
250
|
+
return <div {...mergeWidgetProps(api().getControlProps(), rest)} class={cn("sb-combobox-control", local.class)}>
|
|
251
|
+
{local.children}
|
|
252
|
+
</div>;
|
|
253
|
+
}
|
|
254
|
+
function ComboboxInput(props) {
|
|
255
|
+
const { api, label } = useCombobox();
|
|
256
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
257
|
+
return <input aria-label={label()} {...mergeWidgetProps(api().getInputProps(), rest)} class={cn("sb-combobox-input", local.class)} />;
|
|
258
|
+
}
|
|
259
|
+
function ComboboxTrigger(props) {
|
|
260
|
+
const { api } = useCombobox();
|
|
261
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
262
|
+
return <button {...mergeWidgetProps(api().getTriggerProps(), rest)} class={cn("sb-combobox-trigger", local.class)}>
|
|
263
|
+
{local.children}
|
|
264
|
+
</button>;
|
|
265
|
+
}
|
|
266
|
+
function ComboboxPortal(props) {
|
|
267
|
+
return <Portal mount={props.mount}>{props.children}</Portal>;
|
|
268
|
+
}
|
|
269
|
+
function ComboboxPositioner(props) {
|
|
270
|
+
const { api } = useCombobox();
|
|
271
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
272
|
+
return <div {...mergeWidgetProps(api().getPositionerProps(), rest)} class={cn("sb-combobox-positioner", local.class)}>
|
|
273
|
+
{local.children}
|
|
274
|
+
</div>;
|
|
275
|
+
}
|
|
276
|
+
function ComboboxContent(props) {
|
|
277
|
+
const { api } = useCombobox();
|
|
278
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
279
|
+
return <div hidden={!api().open} {...rest} class={cn("sb-combobox-content", local.class)}>
|
|
280
|
+
{local.children}
|
|
281
|
+
</div>;
|
|
282
|
+
}
|
|
283
|
+
function ComboboxList(props) {
|
|
284
|
+
const { api, options, label } = useCombobox();
|
|
285
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
286
|
+
return <ul aria-label={label()} {...mergeWidgetProps(api().getContentProps(), rest)} class={cn("sb-combobox-list", local.class)}>
|
|
287
|
+
<For each={options()}>{(option) => local.children(option)}</For>
|
|
288
|
+
</ul>;
|
|
289
|
+
}
|
|
290
|
+
function ComboboxEmpty(props) {
|
|
291
|
+
const { api, options } = useCombobox();
|
|
292
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
293
|
+
return <Show when={api().open && options().length === 0}>
|
|
294
|
+
<div {...rest} role="status" class={cn("sb-combobox-empty", local.class)}>
|
|
295
|
+
{local.children}
|
|
296
|
+
</div>
|
|
297
|
+
</Show>;
|
|
298
|
+
}
|
|
299
|
+
function ComboboxItem(props) {
|
|
300
|
+
const { api } = useCombobox();
|
|
301
|
+
const [local, rest] = splitProps(props, ["class", "option"]);
|
|
302
|
+
return <li {...mergeWidgetProps(api().getItemProps({ item: local.option }), rest)} class={cn("sb-combobox-item", local.class)}>
|
|
303
|
+
{local.option.label}
|
|
304
|
+
</li>;
|
|
305
|
+
}
|
|
306
|
+
const Combobox = Object.assign(ComboboxRoot, {
|
|
307
|
+
Root: ComboboxRoot,
|
|
308
|
+
Label: ComboboxLabel,
|
|
309
|
+
Control: ComboboxControl,
|
|
310
|
+
Input: ComboboxInput,
|
|
311
|
+
Trigger: ComboboxTrigger,
|
|
312
|
+
Portal: ComboboxPortal,
|
|
313
|
+
Positioner: ComboboxPositioner,
|
|
314
|
+
Content: ComboboxContent,
|
|
315
|
+
List: ComboboxList,
|
|
316
|
+
Empty: ComboboxEmpty,
|
|
317
|
+
Item: ComboboxItem
|
|
318
|
+
});
|
|
319
|
+
//#endregion
|
|
320
|
+
//#region src/components/Select.tsx
|
|
321
|
+
const SelectContext = createContext(null);
|
|
322
|
+
function useSelect() {
|
|
323
|
+
const context = useContext(SelectContext);
|
|
324
|
+
if (!context) throw new Error("useSelect must be used within a Select");
|
|
325
|
+
return context;
|
|
326
|
+
}
|
|
327
|
+
function SelectRoot(props) {
|
|
328
|
+
const [local, rest] = splitProps(props, [
|
|
329
|
+
"class",
|
|
330
|
+
"children",
|
|
331
|
+
"id",
|
|
332
|
+
"label",
|
|
333
|
+
"name",
|
|
334
|
+
"placeholder",
|
|
335
|
+
"options",
|
|
336
|
+
"value",
|
|
337
|
+
"disabled",
|
|
338
|
+
"placement",
|
|
339
|
+
"invalid",
|
|
340
|
+
"required",
|
|
341
|
+
"onValueChange",
|
|
342
|
+
"onOpenChange"
|
|
343
|
+
]);
|
|
344
|
+
const collection = createMemo(() => select.collection({
|
|
345
|
+
items: local.options,
|
|
346
|
+
itemToValue: (item) => item.value,
|
|
347
|
+
itemToString: (item) => item.label,
|
|
348
|
+
isItemDisabled: (item) => item.disabled ?? false
|
|
349
|
+
}));
|
|
350
|
+
const positioning = createMemo(() => local.placement ? { placement: local.placement } : void 0);
|
|
351
|
+
const service = useMachine(select.machine, {
|
|
352
|
+
get id() {
|
|
353
|
+
return local.id;
|
|
354
|
+
},
|
|
355
|
+
get ids() {
|
|
356
|
+
return { root: local.id };
|
|
357
|
+
},
|
|
358
|
+
get name() {
|
|
359
|
+
return local.name;
|
|
360
|
+
},
|
|
361
|
+
get disabled() {
|
|
362
|
+
return local.disabled;
|
|
363
|
+
},
|
|
364
|
+
get invalid() {
|
|
365
|
+
return local.invalid;
|
|
366
|
+
},
|
|
367
|
+
get required() {
|
|
368
|
+
return local.required;
|
|
369
|
+
},
|
|
370
|
+
get value() {
|
|
371
|
+
if (local.value === void 0) return void 0;
|
|
372
|
+
return local.value === "" ? [] : [local.value];
|
|
373
|
+
},
|
|
374
|
+
get positioning() {
|
|
375
|
+
return positioning();
|
|
376
|
+
},
|
|
377
|
+
get collection() {
|
|
378
|
+
return collection();
|
|
379
|
+
},
|
|
380
|
+
onOpenChange({ open }) {
|
|
381
|
+
local.onOpenChange?.(open);
|
|
382
|
+
},
|
|
383
|
+
onValueChange({ value }) {
|
|
384
|
+
local.onValueChange(value[0] ?? "");
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
const api = createMemo(() => select.connect(service, normalizeProps));
|
|
388
|
+
let hiddenSelect;
|
|
389
|
+
const syncHiddenSelect = () => {
|
|
390
|
+
hiddenSelect.value = api().value[0] ?? "";
|
|
391
|
+
};
|
|
392
|
+
createEffect(on([collection, () => api().value], syncHiddenSelect));
|
|
393
|
+
onMount(() => {
|
|
394
|
+
const form = hiddenSelect.form;
|
|
395
|
+
let resetFrame = 0;
|
|
396
|
+
const handleReset = () => {
|
|
397
|
+
cancelAnimationFrame(resetFrame);
|
|
398
|
+
resetFrame = requestAnimationFrame(syncHiddenSelect);
|
|
399
|
+
};
|
|
400
|
+
form?.addEventListener("reset", handleReset);
|
|
401
|
+
onCleanup(() => {
|
|
402
|
+
form?.removeEventListener("reset", handleReset);
|
|
403
|
+
cancelAnimationFrame(resetFrame);
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
return <SelectContext.Provider value={{
|
|
407
|
+
label: () => local.label,
|
|
408
|
+
placeholder: () => local.placeholder,
|
|
409
|
+
options: () => local.options,
|
|
410
|
+
api
|
|
411
|
+
}}>
|
|
412
|
+
<div {...mergeWidgetProps(api().getRootProps(), rest)} class={cn("sb-select-root", local.class)}>
|
|
413
|
+
<select ref={(element) => {
|
|
414
|
+
hiddenSelect = element;
|
|
415
|
+
}} {...api().getHiddenSelectProps()}>
|
|
416
|
+
<For each={local.options}>
|
|
417
|
+
{(option) => <option value={option.value}>{option.label}</option>}
|
|
418
|
+
</For>
|
|
419
|
+
</select>
|
|
420
|
+
{local.children}
|
|
421
|
+
</div>
|
|
422
|
+
</SelectContext.Provider>;
|
|
423
|
+
}
|
|
424
|
+
function SelectLabel(props) {
|
|
425
|
+
const { api, label } = useSelect();
|
|
426
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
427
|
+
return <label {...mergeWidgetProps(api().getLabelProps(), rest)} class={cn("sb-select-label", local.class)}>
|
|
428
|
+
{local.children ?? label()}
|
|
429
|
+
</label>;
|
|
430
|
+
}
|
|
431
|
+
function SelectControl(props) {
|
|
432
|
+
const { api } = useSelect();
|
|
433
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
434
|
+
return <div {...mergeWidgetProps(api().getControlProps(), rest)} class={cn("sb-select-control", local.class)}>
|
|
435
|
+
{local.children}
|
|
436
|
+
</div>;
|
|
437
|
+
}
|
|
438
|
+
function SelectTrigger(props) {
|
|
439
|
+
const { api, label } = useSelect();
|
|
440
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
441
|
+
return <button aria-label={label()} {...mergeWidgetProps(api().getTriggerProps(), rest)} class={cn("sb-select-trigger", local.class)}>
|
|
442
|
+
{local.children}
|
|
443
|
+
</button>;
|
|
444
|
+
}
|
|
445
|
+
function SelectValueText(props) {
|
|
446
|
+
const { api, placeholder } = useSelect();
|
|
447
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
448
|
+
return <span {...mergeWidgetProps(api().getValueTextProps(), rest)} class={cn("sb-select-value-text", local.class)}>
|
|
449
|
+
{local.children ?? (api().valueAsString || placeholder())}
|
|
450
|
+
</span>;
|
|
451
|
+
}
|
|
452
|
+
function SelectIndicator(props) {
|
|
453
|
+
const { api } = useSelect();
|
|
454
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
455
|
+
return <span {...mergeWidgetProps(api().getIndicatorProps(), rest)} aria-hidden="true" class={cn("sb-select-indicator", local.class)}>
|
|
456
|
+
{local.children}
|
|
457
|
+
</span>;
|
|
458
|
+
}
|
|
459
|
+
function SelectPortal(props) {
|
|
460
|
+
return <Portal mount={props.mount}>{props.children}</Portal>;
|
|
461
|
+
}
|
|
462
|
+
function SelectPositioner(props) {
|
|
463
|
+
const { api } = useSelect();
|
|
464
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
465
|
+
return <div {...mergeWidgetProps(api().getPositionerProps(), rest)} class={cn("sb-select-positioner", local.class)}>
|
|
466
|
+
{local.children}
|
|
467
|
+
</div>;
|
|
468
|
+
}
|
|
469
|
+
function SelectContent(props) {
|
|
470
|
+
const { api } = useSelect();
|
|
471
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
472
|
+
return <div hidden={!api().open} {...rest} class={cn("sb-select-content", local.class)}>
|
|
473
|
+
{local.children}
|
|
474
|
+
</div>;
|
|
475
|
+
}
|
|
476
|
+
function SelectList(props) {
|
|
477
|
+
const { api, options, label } = useSelect();
|
|
478
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
479
|
+
return <ul aria-label={label()} {...mergeWidgetProps(api().getContentProps(), rest)} class={cn("sb-select-list", local.class)}>
|
|
480
|
+
<For each={options()}>{(option) => local.children(option)}</For>
|
|
481
|
+
</ul>;
|
|
482
|
+
}
|
|
483
|
+
function SelectEmpty(props) {
|
|
484
|
+
const { api, options } = useSelect();
|
|
485
|
+
const [local, rest] = splitProps(props, ["class", "children"]);
|
|
486
|
+
return <Show when={api().open && options().length === 0}>
|
|
487
|
+
<div {...rest} role="status" class={cn("sb-select-empty", local.class)}>
|
|
488
|
+
{local.children}
|
|
489
|
+
</div>
|
|
490
|
+
</Show>;
|
|
491
|
+
}
|
|
492
|
+
function SelectItem(props) {
|
|
493
|
+
const { api } = useSelect();
|
|
494
|
+
const [local, rest] = splitProps(props, ["class", "option"]);
|
|
495
|
+
return <li {...mergeWidgetProps(api().getItemProps({ item: local.option }), rest)} class={cn("sb-select-item", local.class)}>
|
|
496
|
+
{local.option.label}
|
|
497
|
+
</li>;
|
|
498
|
+
}
|
|
499
|
+
const Select = Object.assign(SelectRoot, {
|
|
500
|
+
Root: SelectRoot,
|
|
501
|
+
Label: SelectLabel,
|
|
502
|
+
Control: SelectControl,
|
|
503
|
+
Trigger: SelectTrigger,
|
|
504
|
+
ValueText: SelectValueText,
|
|
505
|
+
Indicator: SelectIndicator,
|
|
506
|
+
Portal: SelectPortal,
|
|
507
|
+
Positioner: SelectPositioner,
|
|
508
|
+
Content: SelectContent,
|
|
509
|
+
List: SelectList,
|
|
510
|
+
Empty: SelectEmpty,
|
|
511
|
+
Item: SelectItem
|
|
512
|
+
});
|
|
513
|
+
//#endregion
|
|
514
|
+
//#region src/components/Table.tsx
|
|
515
|
+
function TableRoot(props) {
|
|
516
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
517
|
+
return <table {...rest} class={cn("sb-table", local.class)} />;
|
|
518
|
+
}
|
|
519
|
+
function TableWrap(props) {
|
|
520
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
521
|
+
return <div {...rest} class={cn("sb-table-wrap", local.class)} />;
|
|
522
|
+
}
|
|
523
|
+
function TableCaption(props) {
|
|
524
|
+
return <caption {...props} />;
|
|
525
|
+
}
|
|
526
|
+
function TableHeader(props) {
|
|
527
|
+
return <thead {...props} />;
|
|
528
|
+
}
|
|
529
|
+
function TableBody(props) {
|
|
530
|
+
return <tbody {...props} />;
|
|
531
|
+
}
|
|
532
|
+
function TableFooter(props) {
|
|
533
|
+
return <tfoot {...props} />;
|
|
534
|
+
}
|
|
535
|
+
function TableRow(props) {
|
|
536
|
+
return <tr {...props} />;
|
|
537
|
+
}
|
|
538
|
+
function TableColumnHeader(props) {
|
|
539
|
+
const [local, rest] = splitProps(props, ["scope"]);
|
|
540
|
+
return <th {...rest} scope={local.scope ?? "col"} />;
|
|
541
|
+
}
|
|
542
|
+
function TableRowHeader(props) {
|
|
543
|
+
const [local, rest] = splitProps(props, ["scope"]);
|
|
544
|
+
return <th {...rest} scope={local.scope ?? "row"} />;
|
|
545
|
+
}
|
|
546
|
+
function TableCell(props) {
|
|
547
|
+
const [local, rest] = splitProps(props, ["variant"]);
|
|
548
|
+
return <td {...rest} data-variant={local.variant} />;
|
|
549
|
+
}
|
|
550
|
+
const Table = Object.assign(TableRoot, {
|
|
551
|
+
Root: TableRoot,
|
|
552
|
+
Wrap: TableWrap,
|
|
553
|
+
Caption: TableCaption,
|
|
554
|
+
Header: TableHeader,
|
|
555
|
+
Body: TableBody,
|
|
556
|
+
Footer: TableFooter,
|
|
557
|
+
Row: TableRow,
|
|
558
|
+
ColumnHeader: TableColumnHeader,
|
|
559
|
+
RowHeader: TableRowHeader,
|
|
560
|
+
Cell: TableCell
|
|
561
|
+
});
|
|
562
|
+
//#endregion
|
|
563
|
+
export { AlertDialog, Badge, Button, Checkbox, Combobox, Input, Radio, Select, Switch, Table, TextArea };
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@simple-base/solid",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist"
|
|
7
|
+
],
|
|
8
|
+
"type": "module",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"solid": "./dist/index.jsx",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public",
|
|
21
|
+
"registry": "https://registry.npmjs.org/"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@simple-base/contracts": "0.1.0",
|
|
25
|
+
"@zag-js/combobox": "^1.43.3",
|
|
26
|
+
"@zag-js/select": "^1.43.3",
|
|
27
|
+
"@zag-js/solid": "^1.43.3",
|
|
28
|
+
"cn": "^0.2.5"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^25.8.0",
|
|
32
|
+
"rolldown-plugin-solid": "^0.2.1",
|
|
33
|
+
"solid-js": "^1.9.13",
|
|
34
|
+
"tsdown": "^0.23.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"solid-js": "^1.9.12"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsdown",
|
|
41
|
+
"dev": "tsdown --watch --no-clean",
|
|
42
|
+
"typecheck": "tsc --noEmit"
|
|
43
|
+
}
|
|
44
|
+
}
|