@simple-base/solid 0.1.0 → 0.1.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/README.md +247 -0
- package/package.json +7 -2
package/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# @simple-base/solid
|
|
2
|
+
|
|
3
|
+
Accessible [SolidJS](https://www.solidjs.com/) components for Simple Base — native element behavior, a small props surface, and styling that lives in plain CSS.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add @simple-base/solid
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Install the stylesheet alongside it, since the components only emit class names and attributes:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
pnpm add @simple-base/css
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`solid-js` is a peer dependency and is not bundled.
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
Import the stylesheet once in your entry point:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import "@simple-base/css";
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Then use the components. Every component accepts `class` plus the native attributes for the element it renders:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { Badge, Button, Input } from "@simple-base/solid";
|
|
31
|
+
|
|
32
|
+
export function Toolbar() {
|
|
33
|
+
return (
|
|
34
|
+
<form>
|
|
35
|
+
<Input name="email" type="email" placeholder="you@example.com" required />
|
|
36
|
+
<Button type="submit">Save</Button>
|
|
37
|
+
<Button variant="ghost" size="small">
|
|
38
|
+
Cancel
|
|
39
|
+
</Button>
|
|
40
|
+
<Badge variant="success">Active</Badge>
|
|
41
|
+
</form>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Prefer importing only the stylesheets you use:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import "@simple-base/tokens/css";
|
|
50
|
+
import "@simple-base/css/button";
|
|
51
|
+
import "@simple-base/css/badge";
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Components
|
|
55
|
+
|
|
56
|
+
**Simple components** render one element and take native props:
|
|
57
|
+
|
|
58
|
+
| Component | Renders | Options |
|
|
59
|
+
| ---------- | ---------------------- | ---------------------------------------------------------- |
|
|
60
|
+
| `Button` | `button` | `variant`, `size` |
|
|
61
|
+
| `Badge` | `span` | `variant`, `size` |
|
|
62
|
+
| `Input` | `input` | Native input props; `type` excludes `radio` and `checkbox` |
|
|
63
|
+
| `TextArea` | `textarea` | Native props |
|
|
64
|
+
| `Checkbox` | `input[type=checkbox]` | Native props |
|
|
65
|
+
| `Radio` | `input[type=radio]` | Native props |
|
|
66
|
+
| `Switch` | `input[role=switch]` | Native props; requires `aria-label` or `aria-labelledby` |
|
|
67
|
+
|
|
68
|
+
**Compound components** expose parts as properties on the root (`Select.Label`, `Table.Cell`, and so on):
|
|
69
|
+
|
|
70
|
+
| Component | Parts |
|
|
71
|
+
| ------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
72
|
+
| `AlertDialog` | `Root`, `Icon`, `Content`, `Kicker`, `Title`, `Description`, `Actions` |
|
|
73
|
+
| `Combobox` | `Root`, `Label`, `Control`, `Input`, `Trigger`, `Portal`, `Positioner`, `Content`, `List`, `Empty`, `Item` |
|
|
74
|
+
| `Select` | `Root`, `Label`, `Control`, `Trigger`, `ValueText`, `Indicator`, `Portal`, `Positioner`, `Content`, `List`, `Empty`, `Item` |
|
|
75
|
+
| `Table` | `Root`, `Wrap`, `Caption`, `Header`, `Body`, `Footer`, `Row`, `ColumnHeader`, `RowHeader`, `Cell` |
|
|
76
|
+
|
|
77
|
+
The CSS package ships more components than this adapter currently covers. Breadcrumb, card, dialog, disclosure, empty state, field, keyboard shortcut, menu, pagination, progress, range, segmented control, status, tabs, and typography are available as styles with selector-level APIs.
|
|
78
|
+
|
|
79
|
+
## Select
|
|
80
|
+
|
|
81
|
+
`id`, `label`, `options`, and `onValueChange` are required. `onValueChange` receives the selected option's value, not its label.
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
import { createSignal } from "solid-js";
|
|
85
|
+
import { Select } from "@simple-base/solid";
|
|
86
|
+
|
|
87
|
+
const timezones = [
|
|
88
|
+
{ label: "Central European Time (UTC+01:00)", value: "cet" },
|
|
89
|
+
{ label: "Coordinated Universal Time", value: "utc" },
|
|
90
|
+
{ label: "Japan Standard Time", value: "jst", disabled: true },
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
export function TimezonePicker() {
|
|
94
|
+
const [timezone, setTimezone] = createSignal("");
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<Select
|
|
98
|
+
id="timezone"
|
|
99
|
+
label="Timezone"
|
|
100
|
+
placeholder="Select a timezone"
|
|
101
|
+
options={timezones}
|
|
102
|
+
onValueChange={setTimezone}
|
|
103
|
+
>
|
|
104
|
+
<Select.Label />
|
|
105
|
+
<Select.Control>
|
|
106
|
+
<Select.Trigger>
|
|
107
|
+
<Select.ValueText />
|
|
108
|
+
<Select.Indicator>
|
|
109
|
+
<svg
|
|
110
|
+
aria-hidden="true"
|
|
111
|
+
viewBox="0 0 24 24"
|
|
112
|
+
fill="none"
|
|
113
|
+
stroke="currentColor"
|
|
114
|
+
stroke-width="2"
|
|
115
|
+
>
|
|
116
|
+
<path d="m6 9 6 6 6-6" />
|
|
117
|
+
</svg>
|
|
118
|
+
</Select.Indicator>
|
|
119
|
+
</Select.Trigger>
|
|
120
|
+
</Select.Control>
|
|
121
|
+
<Select.Portal>
|
|
122
|
+
<Select.Positioner>
|
|
123
|
+
<Select.Content>
|
|
124
|
+
<Select.List>{(option) => <Select.Item option={option} />}</Select.List>
|
|
125
|
+
</Select.Content>
|
|
126
|
+
</Select.Positioner>
|
|
127
|
+
</Select.Portal>
|
|
128
|
+
</Select>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Additional root props: `value` makes selection controlled (`""` means cleared), `name` adds a hidden native select so the value submits with the form, `disabled`, `invalid`, and `required` drive state styling and labeling, `placement` picks the popup side, and `onOpenChange` reports visibility.
|
|
134
|
+
|
|
135
|
+
`Select.Empty` renders beside `Select.List` and appears only while the popup is open with no options. `Select.Portal` accepts `mount` — pass a dialog element's node to keep the popup interactive inside a native modal.
|
|
136
|
+
|
|
137
|
+
## Combobox
|
|
138
|
+
|
|
139
|
+
Same root props as `Select`, with a text input that filters options by label.
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
<Combobox
|
|
143
|
+
id="country"
|
|
144
|
+
label="Country"
|
|
145
|
+
placeholder="Search countries"
|
|
146
|
+
options={countries}
|
|
147
|
+
onValueChange={setCountry}
|
|
148
|
+
>
|
|
149
|
+
<Combobox.Label />
|
|
150
|
+
<Combobox.Control>
|
|
151
|
+
<Combobox.Input />
|
|
152
|
+
<Combobox.Trigger>
|
|
153
|
+
<svg
|
|
154
|
+
aria-hidden="true"
|
|
155
|
+
viewBox="0 0 24 24"
|
|
156
|
+
fill="none"
|
|
157
|
+
stroke="currentColor"
|
|
158
|
+
stroke-width="2"
|
|
159
|
+
>
|
|
160
|
+
<path d="m6 9 6 6 6-6" />
|
|
161
|
+
</svg>
|
|
162
|
+
</Combobox.Trigger>
|
|
163
|
+
</Combobox.Control>
|
|
164
|
+
<Combobox.Portal>
|
|
165
|
+
<Combobox.Positioner>
|
|
166
|
+
<Combobox.Content>
|
|
167
|
+
<Combobox.List>{(option) => <Combobox.Item option={option} />}</Combobox.List>
|
|
168
|
+
<Combobox.Empty>No countries found. Try another search.</Combobox.Empty>
|
|
169
|
+
</Combobox.Content>
|
|
170
|
+
</Combobox.Positioner>
|
|
171
|
+
</Combobox.Portal>
|
|
172
|
+
</Combobox>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Table
|
|
176
|
+
|
|
177
|
+
`ColumnHeader` defaults `scope` to `col` and `RowHeader` to `row`. `Cell` accepts `variant`: `code` or `number`.
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
import { Table } from "@simple-base/solid";
|
|
181
|
+
|
|
182
|
+
<Table.Wrap>
|
|
183
|
+
<Table>
|
|
184
|
+
<Table.Caption>Recent invoices</Table.Caption>
|
|
185
|
+
<Table.Header>
|
|
186
|
+
<Table.Row>
|
|
187
|
+
<Table.ColumnHeader>Invoice</Table.ColumnHeader>
|
|
188
|
+
<Table.ColumnHeader>Status</Table.ColumnHeader>
|
|
189
|
+
<Table.ColumnHeader>Amount</Table.ColumnHeader>
|
|
190
|
+
</Table.Row>
|
|
191
|
+
</Table.Header>
|
|
192
|
+
<Table.Body>
|
|
193
|
+
<Table.Row>
|
|
194
|
+
<Table.RowHeader>INV-001</Table.RowHeader>
|
|
195
|
+
<Table.Cell>Paid</Table.Cell>
|
|
196
|
+
<Table.Cell variant="number">$250.00</Table.Cell>
|
|
197
|
+
</Table.Row>
|
|
198
|
+
</Table.Body>
|
|
199
|
+
</Table>
|
|
200
|
+
</Table.Wrap>;
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`Table.Wrap` provides the horizontal scroll container the table styles expect.
|
|
204
|
+
|
|
205
|
+
## AlertDialog
|
|
206
|
+
|
|
207
|
+
Renders a native `<dialog>` with `role="alertdialog"`, wiring `aria-labelledby` and `aria-describedby` to the title and description automatically.
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
import { AlertDialog, Button } from "@simple-base/solid";
|
|
211
|
+
|
|
212
|
+
<AlertDialog open>
|
|
213
|
+
<AlertDialog.Content>
|
|
214
|
+
<AlertDialog.Title>Delete project?</AlertDialog.Title>
|
|
215
|
+
<AlertDialog.Description>This action cannot be undone.</AlertDialog.Description>
|
|
216
|
+
</AlertDialog.Content>
|
|
217
|
+
<AlertDialog.Actions>
|
|
218
|
+
<Button variant="ghost">Cancel</Button>
|
|
219
|
+
<Button variant="danger">Delete</Button>
|
|
220
|
+
</AlertDialog.Actions>
|
|
221
|
+
</AlertDialog>;
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Props conventions
|
|
225
|
+
|
|
226
|
+
- `class` is reactive and merged with the component's own classes; it never replaces them.
|
|
227
|
+
- Native attributes pass through, except the ones the component owns (such as `id`, `role`, and `aria-*` that describe the widget's own structure).
|
|
228
|
+
- Event handlers compose: your `onClick` runs alongside the component's internal handling, not instead of it.
|
|
229
|
+
- Shared option names and defaults come from [@simple-base/contracts](https://www.npmjs.com/package/@simple-base/contracts), and the unmodified option types are re-exported from this package.
|
|
230
|
+
|
|
231
|
+
## Themes
|
|
232
|
+
|
|
233
|
+
Themes come from the token layer. Set `data-theme` on the root or any nested region:
|
|
234
|
+
|
|
235
|
+
```html
|
|
236
|
+
<html data-theme="nord"></html>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Links
|
|
240
|
+
|
|
241
|
+
- [Repository](https://github.com/redasalmi/simple-base)
|
|
242
|
+
- [Styles and selector API](https://www.npmjs.com/package/@simple-base/css)
|
|
243
|
+
- [Design tokens](https://www.npmjs.com/package/@simple-base/tokens)
|
|
244
|
+
|
|
245
|
+
## License
|
|
246
|
+
|
|
247
|
+
[MIT](https://github.com/redasalmi/simple-base/blob/main/LICENSE)
|
package/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simple-base/solid",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/redasalmi/simple-base.git",
|
|
8
|
+
"directory": "packages/solid"
|
|
9
|
+
},
|
|
5
10
|
"files": [
|
|
6
11
|
"dist"
|
|
7
12
|
],
|
|
@@ -21,7 +26,7 @@
|
|
|
21
26
|
"registry": "https://registry.npmjs.org/"
|
|
22
27
|
},
|
|
23
28
|
"dependencies": {
|
|
24
|
-
"@simple-base/contracts": "0.1.
|
|
29
|
+
"@simple-base/contracts": "0.1.1",
|
|
25
30
|
"@zag-js/combobox": "^1.43.3",
|
|
26
31
|
"@zag-js/select": "^1.43.3",
|
|
27
32
|
"@zag-js/solid": "^1.43.3",
|