aidos-ui 4.0.0 → 4.0.2
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/.claude/settings.local.json +14 -1
- package/CLAUDE.md +72 -0
- package/dist/BaseButton.js.map +1 -1
- package/dist/BaseLink.js.map +1 -1
- package/dist/BaseListItem.d.ts +1 -1
- package/dist/Calendar.d.ts +2 -2
- package/dist/Card.js +1 -1
- package/dist/Card.js.map +1 -1
- package/dist/DarkMode.js.map +1 -1
- package/dist/DatePicker.js.map +1 -1
- package/dist/Dialog.d.ts +3 -3
- package/dist/Dialog.js +7 -1
- package/dist/Dialog.js.map +1 -1
- package/dist/Dropdown.js.map +1 -1
- package/dist/Icon.d.ts +3 -3
- package/dist/Icon.js +2 -21
- package/dist/Icon.js.map +1 -1
- package/dist/Interactable.js.map +1 -1
- package/dist/NextJSRouterState.js.map +1 -1
- package/dist/OnVisible.d.ts +1 -1
- package/dist/OnVisible.js.map +1 -1
- package/dist/Popover.d.ts +3 -3
- package/dist/Popover.js +3 -1
- package/dist/Popover.js.map +1 -1
- package/dist/Providers.d.ts +2 -2
- package/dist/RangeInput.d.ts +1 -1
- package/dist/RootView.js.map +1 -1
- package/dist/Select.d.ts +0 -1
- package/dist/Storage.d.ts +2 -1
- package/dist/Storage.js.map +1 -1
- package/dist/Stream.js.map +1 -1
- package/dist/Text.js.map +1 -1
- package/dist/TextInput.d.ts +3 -2
- package/dist/TextInput.js +13 -4
- package/dist/TextInput.js.map +1 -1
- package/dist/TextPairing.js.map +1 -1
- package/dist/Tooltip.d.ts +2 -2
- package/dist/Tooltip.js +3 -2
- package/dist/Tooltip.js.map +1 -1
- package/dist/aria.js.map +1 -1
- package/dist/hash.js.map +1 -1
- package/dist/jss.d.ts +11 -6
- package/dist/jss.js +47 -2
- package/dist/jss.js.map +1 -1
- package/dist/numberToBase.js.map +1 -1
- package/dist/stateMachineContextFactory.js.map +1 -1
- package/dist/useCookie.js.map +1 -1
- package/dist/useKeyboard.js.map +1 -1
- package/dist/useNavigation.js.map +1 -1
- package/dist/useRefEffect.js.map +1 -1
- package/next-env.d.ts +2 -1
- package/next.config.js +2 -1
- package/package.json +27 -20
- package/src/BaseListItem.tsx +1 -1
- package/src/Calendar.tsx +2 -2
- package/src/Card.tsx +1 -1
- package/src/Dialog.tsx +10 -4
- package/src/Icon.tsx +4 -30
- package/src/Popover.tsx +6 -4
- package/src/Providers.tsx +1 -1
- package/src/RangeInput.tsx +1 -1
- package/src/Storage.tsx +1 -1
- package/src/TextInput.tsx +17 -5
- package/src/Tooltip.tsx +5 -4
- package/src/docs/mdx.tsx +40 -0
- package/src/docs/searchIndex.ts +1 -1
- package/src/jss.tsx +55 -3
- package/src/pages/Badge.mdx +136 -14
- package/src/pages/Button.mdx +121 -63
- package/src/pages/Calendar.mdx +196 -2
- package/src/pages/Card.mdx +123 -47
- package/src/pages/Checkbox.mdx +103 -28
- package/src/pages/Dialog.mdx +172 -7
- package/src/pages/FlexLayout.mdx +185 -51
- package/src/pages/Hashing.mdx +199 -8
- package/src/pages/Icon.mdx +150 -42
- package/src/pages/IconButton.mdx +127 -116
- package/src/pages/Link.mdx +118 -47
- package/src/pages/List.mdx +167 -283
- package/src/pages/ListHeader.mdx +157 -27
- package/src/pages/Popover.mdx +177 -55
- package/src/pages/ProgressBar.mdx +186 -6
- package/src/pages/ProgressCircle.mdx +196 -13
- package/src/pages/RangeInput.mdx +189 -61
- package/src/pages/Tabs.mdx +202 -346
- package/src/pages/Text.mdx +102 -81
- package/src/pages/TextInput.mdx +169 -44
- package/src/pages/TextPairing.mdx +164 -115
- package/src/pages/TimeInput.mdx +142 -37
- package/src/pages/Tooltip.mdx +99 -12
- package/src/pages/_app.tsx +13 -3
- package/src/pages/css-in-js.mdx +196 -83
- package/src/pages/navigation.mdx +122 -20
- package/src/pages/useCookie.mdx +168 -7
- package/src/pages/useKeyboard.mdx +242 -5
- package/src/pages/useNavigation.mdx +244 -16
- package/src/pages/usePromise.mdx +271 -6
- package/src/pages/useRefEffect.mdx +230 -28
- package/tsconfig.json +13 -4
package/src/pages/Dialog.mdx
CHANGED
|
@@ -1,15 +1,47 @@
|
|
|
1
1
|
import { DialogTrigger } from "../examples/ExampleDialog.tsx";
|
|
2
|
+
import { Button } from "../Button";
|
|
2
3
|
|
|
3
4
|
# Dialog
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
A modal dialog component that overlays the main UI. Dialogs trap focus and prevent interaction with the underlying content, following accessibility best practices.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Props
|
|
11
|
+
|
|
12
|
+
### Dialog component
|
|
13
|
+
|
|
14
|
+
| Prop | Type | Required | Description |
|
|
15
|
+
|------|------|----------|-------------|
|
|
16
|
+
| `label` | `string` | Yes | Dialog title displayed in the header |
|
|
17
|
+
| `children` | `ReactNode` | Yes | Dialog content |
|
|
18
|
+
| `close` | `() => void` | Yes | Function to close the dialog |
|
|
19
|
+
|
|
20
|
+
### useDialog hook
|
|
21
|
+
|
|
22
|
+
| Parameter | Type | Description |
|
|
23
|
+
|-----------|------|-------------|
|
|
24
|
+
| `DialogComponent` | `(props: { close: () => void } & Input) => JSX.Element` | Component to render inside the dialog |
|
|
25
|
+
| `options.closeOnOutsideClick` | `boolean` | Whether clicking outside closes the dialog |
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
- `open(input?: Input)` - Function to open the dialog with optional input data
|
|
29
|
+
- `close()` - Function to close the dialog programmatically
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Basic usage
|
|
34
|
+
|
|
35
|
+
Click the button to open a dialog:
|
|
6
36
|
|
|
7
37
|
<DialogTrigger />
|
|
8
38
|
|
|
9
39
|
```js
|
|
10
|
-
|
|
40
|
+
import { Dialog, useDialog } from "aidos-ui";
|
|
41
|
+
|
|
42
|
+
function MyDialog({ close }) {
|
|
11
43
|
return (
|
|
12
|
-
<Dialog close={close} label="
|
|
44
|
+
<Dialog close={close} label="Settings">
|
|
13
45
|
<Row padding="medium" align="center" justify="space-between">
|
|
14
46
|
<Span>Dark mode</Span>
|
|
15
47
|
<DarkModeToggle />
|
|
@@ -18,15 +50,148 @@ function ExampleDialog({ close }) {
|
|
|
18
50
|
);
|
|
19
51
|
}
|
|
20
52
|
|
|
21
|
-
|
|
22
|
-
const { open } = useDialog
|
|
23
|
-
({ close }) => <
|
|
53
|
+
function App() {
|
|
54
|
+
const { open } = useDialog(
|
|
55
|
+
({ close }) => <MyDialog close={close} />,
|
|
24
56
|
{ closeOnOutsideClick: true }
|
|
25
57
|
);
|
|
26
58
|
|
|
27
|
-
return
|
|
59
|
+
return (
|
|
60
|
+
<Button color="positive" onClick={() => open()}>
|
|
61
|
+
Open Settings
|
|
62
|
+
</Button>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Passing data to dialog
|
|
70
|
+
|
|
71
|
+
Use TypeScript generics to pass data when opening the dialog:
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
interface UserData {
|
|
75
|
+
name: string;
|
|
76
|
+
email: string;
|
|
28
77
|
}
|
|
29
78
|
|
|
79
|
+
function EditUserDialog({ close, name, email }: { close: () => void } & UserData) {
|
|
80
|
+
return (
|
|
81
|
+
<Dialog close={close} label="Edit User">
|
|
82
|
+
<Column padding="medium" gap="medium">
|
|
83
|
+
<TextInput value={name} onValueChange={() => {}} />
|
|
84
|
+
<TextInput value={email} onValueChange={() => {}} />
|
|
85
|
+
<Button color="positive" onClick={close}>
|
|
86
|
+
Save
|
|
87
|
+
</Button>
|
|
88
|
+
</Column>
|
|
89
|
+
</Dialog>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function App() {
|
|
94
|
+
const { open } = useDialog<UserData>(
|
|
95
|
+
({ close, name, email }) => (
|
|
96
|
+
<EditUserDialog close={close} name={name} email={email} />
|
|
97
|
+
),
|
|
98
|
+
{ closeOnOutsideClick: false }
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<Button
|
|
103
|
+
color="primary"
|
|
104
|
+
onClick={() => open({ name: "John", email: "john@example.com" })}
|
|
105
|
+
>
|
|
106
|
+
Edit User
|
|
107
|
+
</Button>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
30
110
|
```
|
|
31
111
|
|
|
32
112
|
---
|
|
113
|
+
|
|
114
|
+
## Dialog options
|
|
115
|
+
|
|
116
|
+
### Close on outside click
|
|
117
|
+
|
|
118
|
+
Control whether clicking the backdrop closes the dialog:
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
// Dialog closes when clicking outside
|
|
122
|
+
const { open } = useDialog(
|
|
123
|
+
({ close }) => <MyDialog close={close} />,
|
|
124
|
+
{ closeOnOutsideClick: true }
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// Dialog stays open until explicitly closed
|
|
128
|
+
const { open } = useDialog(
|
|
129
|
+
({ close }) => <MyDialog close={close} />,
|
|
130
|
+
{ closeOnOutsideClick: false }
|
|
131
|
+
);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Closing the dialog
|
|
137
|
+
|
|
138
|
+
Dialogs can be closed in several ways:
|
|
139
|
+
|
|
140
|
+
1. **Close button** - Built into the Dialog header
|
|
141
|
+
2. **Escape key** - Native dialog behavior
|
|
142
|
+
3. **Outside click** - When `closeOnOutsideClick: true`
|
|
143
|
+
4. **Programmatically** - Using the `close` function
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
function ConfirmDialog({ close, onConfirm }) {
|
|
147
|
+
return (
|
|
148
|
+
<Dialog close={close} label="Confirm">
|
|
149
|
+
<Column padding="medium" gap="medium">
|
|
150
|
+
<P>Are you sure you want to proceed?</P>
|
|
151
|
+
<Row gap="medium" justify="end">
|
|
152
|
+
<Button bare color="primary" onClick={close}>
|
|
153
|
+
Cancel
|
|
154
|
+
</Button>
|
|
155
|
+
<Button
|
|
156
|
+
color="positive"
|
|
157
|
+
onClick={() => {
|
|
158
|
+
onConfirm();
|
|
159
|
+
close();
|
|
160
|
+
}}
|
|
161
|
+
>
|
|
162
|
+
Confirm
|
|
163
|
+
</Button>
|
|
164
|
+
</Row>
|
|
165
|
+
</Column>
|
|
166
|
+
</Dialog>
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Setup
|
|
174
|
+
|
|
175
|
+
The `DialogProvider` must wrap your app to enable dialogs. This is typically included in the `Providers` component:
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
import { Providers } from "aidos-ui";
|
|
179
|
+
|
|
180
|
+
function App() {
|
|
181
|
+
return (
|
|
182
|
+
<Providers>
|
|
183
|
+
{/* Your app content */}
|
|
184
|
+
</Providers>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Accessibility
|
|
192
|
+
|
|
193
|
+
- Uses native `<dialog>` element with `showModal()` for proper modal behavior
|
|
194
|
+
- Focus is trapped within the dialog
|
|
195
|
+
- Escape key closes the dialog
|
|
196
|
+
- Focus returns to the triggering element when closed
|
|
197
|
+
- Backdrop prevents interaction with underlying content
|
package/src/pages/FlexLayout.mdx
CHANGED
|
@@ -1,77 +1,211 @@
|
|
|
1
1
|
import { Column } from "../Column";
|
|
2
2
|
import { Row } from "../Row";
|
|
3
3
|
import { Badge } from "../Badge";
|
|
4
|
+
import { BaseView } from "../BaseView";
|
|
5
|
+
import { Span } from "../Text";
|
|
4
6
|
import { cssVar } from "../jss";
|
|
5
7
|
|
|
6
8
|
# FlexLayout
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
`Row` and `Column` are the primary layout components, providing a simple API for flexbox layouts without writing custom styles.
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Row
|
|
15
|
+
|
|
16
|
+
Arranges children horizontally (flex-direction: row).
|
|
17
|
+
|
|
18
|
+
<Row gap="medium" padding="medium" jss={{ border: `1px solid ${cssVar("--divider")}`, borderRadius: 8 }}>
|
|
19
|
+
<Badge />
|
|
20
|
+
<Badge />
|
|
21
|
+
<Badge />
|
|
22
|
+
</Row>
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
<Row gap="medium" padding="medium">
|
|
26
|
+
<Badge />
|
|
27
|
+
<Badge />
|
|
28
|
+
<Badge />
|
|
29
|
+
</Row>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Column
|
|
35
|
+
|
|
36
|
+
Arranges children vertically (flex-direction: column).
|
|
37
|
+
|
|
38
|
+
<Column gap="medium" padding="medium" jss={{ border: `1px solid ${cssVar("--divider")}`, borderRadius: 8 }}>
|
|
39
|
+
<Badge />
|
|
40
|
+
<Badge />
|
|
41
|
+
<Badge />
|
|
42
|
+
</Column>
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
<Column gap="medium" padding="medium">
|
|
46
|
+
<Badge />
|
|
47
|
+
<Badge />
|
|
48
|
+
<Badge />
|
|
49
|
+
</Column>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Props
|
|
55
|
+
|
|
56
|
+
Both `Row` and `Column` accept the same props:
|
|
57
|
+
|
|
58
|
+
| Prop | Type | Default | Description |
|
|
59
|
+
|------|------|---------|-------------|
|
|
60
|
+
| `gap` | `Gap` | - | Space between children: `"none"`, `"xsmall"`, `"small"`, `"medium"`, `"large"`, `"xlarge"` |
|
|
61
|
+
| `padding` | `Padding` | - | Internal padding (same values as gap) |
|
|
62
|
+
| `justify` | `Justify` | - | Main axis alignment: `"start"`, `"end"`, `"center"`, `"space-between"`, `"space-around"`, `"space-evenly"` |
|
|
63
|
+
| `align` | `Align` | - | Cross axis alignment: `"start"`, `"end"`, `"center"`, `"stretch"`, `"baseline"` |
|
|
64
|
+
| `grow` | `boolean` | `false` | Allow element to grow (flex-grow: 1) |
|
|
65
|
+
| `shrink` | `boolean` | `false` | Allow element to shrink (flex-shrink: 1) |
|
|
66
|
+
| `wrap` | `boolean` | `false` | Allow items to wrap |
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Justify content
|
|
71
|
+
|
|
72
|
+
Control how items are distributed along the main axis:
|
|
14
73
|
|
|
15
74
|
<Column gap="medium">
|
|
16
|
-
<Row
|
|
17
|
-
|
|
18
|
-
justify="space-around"
|
|
19
|
-
align="center"
|
|
20
|
-
>
|
|
21
|
-
<Badge />
|
|
22
|
-
<Badge />
|
|
23
|
-
<Badge />
|
|
75
|
+
<Row justify="start" padding="small" jss={{ border: `1px solid ${cssVar("--highlight")}` }}>
|
|
76
|
+
<Badge /><Badge /><Badge />
|
|
24
77
|
</Row>
|
|
25
|
-
<Row
|
|
26
|
-
|
|
27
|
-
justify="space-evenly"
|
|
28
|
-
align="center"
|
|
29
|
-
>
|
|
30
|
-
<Badge />
|
|
31
|
-
<Badge />
|
|
32
|
-
<Badge />
|
|
78
|
+
<Row justify="center" padding="small" jss={{ border: `1px solid ${cssVar("--highlight")}` }}>
|
|
79
|
+
<Badge /><Badge /><Badge />
|
|
33
80
|
</Row>
|
|
34
|
-
<Row
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<Badge />
|
|
81
|
+
<Row justify="end" padding="small" jss={{ border: `1px solid ${cssVar("--highlight")}` }}>
|
|
82
|
+
<Badge /><Badge /><Badge />
|
|
83
|
+
</Row>
|
|
84
|
+
<Row justify="space-between" padding="small" jss={{ border: `1px solid ${cssVar("--highlight")}` }}>
|
|
85
|
+
<Badge /><Badge /><Badge />
|
|
86
|
+
</Row>
|
|
87
|
+
<Row justify="space-around" padding="small" jss={{ border: `1px solid ${cssVar("--highlight")}` }}>
|
|
88
|
+
<Badge /><Badge /><Badge />
|
|
89
|
+
</Row>
|
|
90
|
+
<Row justify="space-evenly" padding="small" jss={{ border: `1px solid ${cssVar("--highlight")}` }}>
|
|
91
|
+
<Badge /><Badge /><Badge />
|
|
42
92
|
</Row>
|
|
43
93
|
</Column>
|
|
44
94
|
|
|
45
95
|
```js
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
96
|
+
<Row justify="start">...</Row>
|
|
97
|
+
<Row justify="center">...</Row>
|
|
98
|
+
<Row justify="end">...</Row>
|
|
99
|
+
<Row justify="space-between">...</Row>
|
|
100
|
+
<Row justify="space-around">...</Row>
|
|
101
|
+
<Row justify="space-evenly">...</Row>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Align items
|
|
107
|
+
|
|
108
|
+
Control how items are aligned on the cross axis:
|
|
109
|
+
|
|
110
|
+
<Row gap="medium">
|
|
111
|
+
<Row align="start" padding="small" jss={{ border: `1px solid ${cssVar("--highlight")}`, height: 80 }}>
|
|
54
112
|
<Badge />
|
|
55
113
|
</Row>
|
|
56
|
-
<Row
|
|
57
|
-
jss={{ border: `1px solid ${cssVar("--highlight")}` }}
|
|
58
|
-
justify="space-evenly"
|
|
59
|
-
align="center"
|
|
60
|
-
>
|
|
61
|
-
<Badge />
|
|
62
|
-
<Badge />
|
|
114
|
+
<Row align="center" padding="small" jss={{ border: `1px solid ${cssVar("--highlight")}`, height: 80 }}>
|
|
63
115
|
<Badge />
|
|
64
116
|
</Row>
|
|
65
|
-
<Row
|
|
66
|
-
jss={{ border: `1px solid ${cssVar("--highlight")}` }}
|
|
67
|
-
justify="space-between"
|
|
68
|
-
align="center"
|
|
69
|
-
>
|
|
70
|
-
<Badge />
|
|
71
|
-
<Badge />
|
|
117
|
+
<Row align="end" padding="small" jss={{ border: `1px solid ${cssVar("--highlight")}`, height: 80 }}>
|
|
72
118
|
<Badge />
|
|
73
119
|
</Row>
|
|
120
|
+
<Row align="stretch" padding="small" jss={{ border: `1px solid ${cssVar("--highlight")}`, height: 80 }}>
|
|
121
|
+
<BaseView jss={{ backgroundColor: cssVar("--highlight"), width: 16 }} />
|
|
122
|
+
</Row>
|
|
123
|
+
</Row>
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
<Row align="start">...</Row>
|
|
127
|
+
<Row align="center">...</Row>
|
|
128
|
+
<Row align="end">...</Row>
|
|
129
|
+
<Row align="stretch">...</Row>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Gap sizes
|
|
135
|
+
|
|
136
|
+
<Column gap="medium">
|
|
137
|
+
<Row gap="none" jss={{ border: `1px solid ${cssVar("--divider")}` }}>
|
|
138
|
+
<Badge /><Badge /><Badge />
|
|
139
|
+
</Row>
|
|
140
|
+
<Row gap="small" jss={{ border: `1px solid ${cssVar("--divider")}` }}>
|
|
141
|
+
<Badge /><Badge /><Badge />
|
|
142
|
+
</Row>
|
|
143
|
+
<Row gap="medium" jss={{ border: `1px solid ${cssVar("--divider")}` }}>
|
|
144
|
+
<Badge /><Badge /><Badge />
|
|
145
|
+
</Row>
|
|
146
|
+
<Row gap="large" jss={{ border: `1px solid ${cssVar("--divider")}` }}>
|
|
147
|
+
<Badge /><Badge /><Badge />
|
|
148
|
+
</Row>
|
|
149
|
+
<Row gap="xlarge" jss={{ border: `1px solid ${cssVar("--divider")}` }}>
|
|
150
|
+
<Badge /><Badge /><Badge />
|
|
151
|
+
</Row>
|
|
74
152
|
</Column>
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
<Row gap="none">...</Row> // 0px
|
|
156
|
+
<Row gap="xsmall">...</Row> // 4px
|
|
157
|
+
<Row gap="small">...</Row> // 8px
|
|
158
|
+
<Row gap="medium">...</Row> // 12px
|
|
159
|
+
<Row gap="large">...</Row> // 16px
|
|
160
|
+
<Row gap="xlarge">...</Row> // 24px
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Grow and shrink
|
|
166
|
+
|
|
167
|
+
Control how items expand or contract:
|
|
168
|
+
|
|
169
|
+
<Row gap="medium" jss={{ border: `1px solid ${cssVar("--divider")}` }}>
|
|
170
|
+
<BaseView padding="medium" jss={{ backgroundColor: cssVar("--highlight") }}>
|
|
171
|
+
<Span color="light">Fixed</Span>
|
|
172
|
+
</BaseView>
|
|
173
|
+
<BaseView grow padding="medium" jss={{ backgroundColor: cssVar("--secondary-background") }}>
|
|
174
|
+
<Span>Grows to fill space</Span>
|
|
175
|
+
</BaseView>
|
|
176
|
+
<BaseView padding="medium" jss={{ backgroundColor: cssVar("--highlight") }}>
|
|
177
|
+
<Span color="light">Fixed</Span>
|
|
178
|
+
</BaseView>
|
|
179
|
+
</Row>
|
|
180
|
+
|
|
181
|
+
```js
|
|
182
|
+
<Row>
|
|
183
|
+
<BaseView>Fixed width</BaseView>
|
|
184
|
+
<BaseView grow>Fills remaining space</BaseView>
|
|
185
|
+
<BaseView>Fixed width</BaseView>
|
|
186
|
+
</Row>
|
|
75
187
|
```
|
|
76
188
|
|
|
77
189
|
---
|
|
190
|
+
|
|
191
|
+
## Nesting layouts
|
|
192
|
+
|
|
193
|
+
Combine Row and Column for complex layouts:
|
|
194
|
+
|
|
195
|
+
```js
|
|
196
|
+
<Column gap="medium" padding="medium">
|
|
197
|
+
<Row justify="space-between" align="center">
|
|
198
|
+
<Span>Title</Span>
|
|
199
|
+
<Button color="primary" onClick={() => {}}>Action</Button>
|
|
200
|
+
</Row>
|
|
201
|
+
<Row gap="medium">
|
|
202
|
+
<Column grow gap="small">
|
|
203
|
+
<Span>Main content</Span>
|
|
204
|
+
<P>Description text here...</P>
|
|
205
|
+
</Column>
|
|
206
|
+
<Column gap="small">
|
|
207
|
+
<Span>Sidebar</Span>
|
|
208
|
+
</Column>
|
|
209
|
+
</Row>
|
|
210
|
+
</Column>
|
|
211
|
+
```
|
package/src/pages/Hashing.mdx
CHANGED
|
@@ -1,20 +1,211 @@
|
|
|
1
|
-
# Hashing and
|
|
1
|
+
# Hashing and Unique IDs
|
|
2
|
+
|
|
3
|
+
Utility functions for generating consistent hashes and converting numbers to different bases. Useful for creating unique identifiers, keys, and compact representations.
|
|
4
|
+
|
|
5
|
+
---
|
|
2
6
|
|
|
3
7
|
## hash
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
Generates a numeric hash from a string. The hash is deterministic - the same input always produces the same output.
|
|
10
|
+
|
|
11
|
+
### Signature
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
function hash(source: string, modulus?: number): number
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Parameters
|
|
18
|
+
|
|
19
|
+
| Parameter | Type | Default | Description |
|
|
20
|
+
|-----------|------|---------|-------------|
|
|
21
|
+
| `source` | `string` | Required | The string to hash |
|
|
22
|
+
| `modulus` | `number` | - | Optional modulus to constrain the hash range |
|
|
23
|
+
|
|
24
|
+
### Returns
|
|
25
|
+
|
|
26
|
+
A non-negative integer hash value.
|
|
27
|
+
|
|
28
|
+
### Basic usage
|
|
6
29
|
|
|
7
30
|
```js
|
|
8
|
-
|
|
9
|
-
|
|
31
|
+
import { hash } from "aidos-ui";
|
|
32
|
+
|
|
33
|
+
const id = hash("ABC"); // 64578
|
|
34
|
+
const id2 = hash("ABC"); // 64578 (same input = same output)
|
|
35
|
+
const id3 = hash("ABCD"); // Different value
|
|
10
36
|
```
|
|
11
37
|
|
|
38
|
+
### With modulus
|
|
39
|
+
|
|
40
|
+
Constrain the hash to a specific range:
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
// Hash constrained to 0-9
|
|
44
|
+
const small_id = hash("ABC", 10); // 8
|
|
45
|
+
|
|
46
|
+
// Hash constrained to 0-99
|
|
47
|
+
const medium_id = hash("ABC", 100); // 78
|
|
48
|
+
|
|
49
|
+
// Useful for array indices
|
|
50
|
+
const colors = ["red", "blue", "green", "yellow"];
|
|
51
|
+
const colorIndex = hash("user123", colors.length);
|
|
52
|
+
const userColor = colors[colorIndex];
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Use cases
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
// Consistent avatar colors
|
|
59
|
+
function getUserColor(userId) {
|
|
60
|
+
const colors = ["#FF5733", "#33FF57", "#3357FF", "#FF33F5"];
|
|
61
|
+
return colors[hash(userId, colors.length)];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Cache keys
|
|
65
|
+
const cacheKey = hash(JSON.stringify(params));
|
|
66
|
+
|
|
67
|
+
// Partitioning
|
|
68
|
+
const partition = hash(userId, 10); // 0-9
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
12
73
|
## numberToBase
|
|
13
74
|
|
|
14
|
-
|
|
75
|
+
Converts a number to a string representation in an arbitrary base using custom characters.
|
|
76
|
+
|
|
77
|
+
### Signature
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
function numberToBase(
|
|
81
|
+
value: number,
|
|
82
|
+
alphabet?: string[]
|
|
83
|
+
): string
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Parameters
|
|
87
|
+
|
|
88
|
+
| Parameter | Type | Default | Description |
|
|
89
|
+
|-----------|------|---------|-------------|
|
|
90
|
+
| `value` | `number` | Required | The number to convert |
|
|
91
|
+
| `alphabet` | `string[]` | `["a"-"z"]` | Characters to use for each digit |
|
|
92
|
+
|
|
93
|
+
### Returns
|
|
94
|
+
|
|
95
|
+
A string representation of the number in the given base.
|
|
96
|
+
|
|
97
|
+
### Basic usage
|
|
15
98
|
|
|
16
99
|
```js
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
100
|
+
import { numberToBase } from "aidos-ui";
|
|
101
|
+
|
|
102
|
+
// Default: base-26 using lowercase letters
|
|
103
|
+
const id = numberToBase(hash("ABC")); // "cqmv"
|
|
104
|
+
const id2 = numberToBase(12345); // "wmt"
|
|
20
105
|
```
|
|
106
|
+
|
|
107
|
+
### Custom bases
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
// Binary (base-2)
|
|
111
|
+
numberToBase(8, ["0", "1"]); // "1000"
|
|
112
|
+
|
|
113
|
+
// With custom symbols
|
|
114
|
+
numberToBase(hash("ABC"), ["?", "!"]); // "!!!!!???!???!?!"
|
|
115
|
+
|
|
116
|
+
// Hexadecimal-like
|
|
117
|
+
const hex = ["0", "1", "2", "3", "4", "5", "6", "7",
|
|
118
|
+
"8", "9", "a", "b", "c", "d", "e", "f"];
|
|
119
|
+
numberToBase(255, hex); // "ff"
|
|
120
|
+
|
|
121
|
+
// URL-safe characters
|
|
122
|
+
const urlSafe = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".split("");
|
|
123
|
+
numberToBase(123456789, urlSafe); // Compact URL-safe ID
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Combining with hash
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
// Generate compact, unique IDs
|
|
130
|
+
function generateId(input) {
|
|
131
|
+
return numberToBase(hash(input));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const userId = generateId("user@example.com"); // e.g., "xkcd"
|
|
135
|
+
const postId = generateId("blog-post-title"); // e.g., "qwer"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Common patterns
|
|
141
|
+
|
|
142
|
+
### Unique class names
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
function uniqueClassName(styles) {
|
|
146
|
+
const styleString = JSON.stringify(styles);
|
|
147
|
+
return `_${numberToBase(hash(styleString))}`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const className = uniqueClassName({ color: "red" }); // e.g., "_abc"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Consistent randomization
|
|
154
|
+
|
|
155
|
+
```js
|
|
156
|
+
// Same input always gives same "random" result
|
|
157
|
+
function pseudoRandom(seed, max) {
|
|
158
|
+
return hash(seed, max);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Consistent shuffle based on seed
|
|
162
|
+
function seededShuffle(array, seed) {
|
|
163
|
+
return array.slice().sort((a, b) => {
|
|
164
|
+
return hash(`${seed}-${a}`) - hash(`${seed}-${b}`);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Short URLs
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
// Convert database IDs to short codes
|
|
173
|
+
function toShortCode(numericId) {
|
|
174
|
+
const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
|
|
175
|
+
return numberToBase(numericId, chars);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
toShortCode(123456); // e.g., "w7e"
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Color from string
|
|
182
|
+
|
|
183
|
+
```js
|
|
184
|
+
function stringToColor(str) {
|
|
185
|
+
const h = hash(str, 360);
|
|
186
|
+
return `hsl(${h}, 70%, 50%)`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
stringToColor("John"); // Consistent color for "John"
|
|
190
|
+
stringToColor("Jane"); // Different color for "Jane"
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Algorithm details
|
|
196
|
+
|
|
197
|
+
The `hash` function uses a simple but effective string hashing algorithm:
|
|
198
|
+
- Iterates through each character
|
|
199
|
+
- Combines character codes using bit shifting
|
|
200
|
+
- Produces a 32-bit integer hash
|
|
201
|
+
- Applies modulus if specified
|
|
202
|
+
|
|
203
|
+
This is suitable for:
|
|
204
|
+
- Generating consistent keys
|
|
205
|
+
- Distributing values evenly
|
|
206
|
+
- Creating pseudo-random but reproducible results
|
|
207
|
+
|
|
208
|
+
Not suitable for:
|
|
209
|
+
- Cryptographic purposes
|
|
210
|
+
- Security-sensitive hashing
|
|
211
|
+
- Password storage
|