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.
Files changed (99) hide show
  1. package/.claude/settings.local.json +14 -1
  2. package/CLAUDE.md +72 -0
  3. package/dist/BaseButton.js.map +1 -1
  4. package/dist/BaseLink.js.map +1 -1
  5. package/dist/BaseListItem.d.ts +1 -1
  6. package/dist/Calendar.d.ts +2 -2
  7. package/dist/Card.js +1 -1
  8. package/dist/Card.js.map +1 -1
  9. package/dist/DarkMode.js.map +1 -1
  10. package/dist/DatePicker.js.map +1 -1
  11. package/dist/Dialog.d.ts +3 -3
  12. package/dist/Dialog.js +7 -1
  13. package/dist/Dialog.js.map +1 -1
  14. package/dist/Dropdown.js.map +1 -1
  15. package/dist/Icon.d.ts +3 -3
  16. package/dist/Icon.js +2 -21
  17. package/dist/Icon.js.map +1 -1
  18. package/dist/Interactable.js.map +1 -1
  19. package/dist/NextJSRouterState.js.map +1 -1
  20. package/dist/OnVisible.d.ts +1 -1
  21. package/dist/OnVisible.js.map +1 -1
  22. package/dist/Popover.d.ts +3 -3
  23. package/dist/Popover.js +3 -1
  24. package/dist/Popover.js.map +1 -1
  25. package/dist/Providers.d.ts +2 -2
  26. package/dist/RangeInput.d.ts +1 -1
  27. package/dist/RootView.js.map +1 -1
  28. package/dist/Select.d.ts +0 -1
  29. package/dist/Storage.d.ts +2 -1
  30. package/dist/Storage.js.map +1 -1
  31. package/dist/Stream.js.map +1 -1
  32. package/dist/Text.js.map +1 -1
  33. package/dist/TextInput.d.ts +3 -2
  34. package/dist/TextInput.js +13 -4
  35. package/dist/TextInput.js.map +1 -1
  36. package/dist/TextPairing.js.map +1 -1
  37. package/dist/Tooltip.d.ts +2 -2
  38. package/dist/Tooltip.js +3 -2
  39. package/dist/Tooltip.js.map +1 -1
  40. package/dist/aria.js.map +1 -1
  41. package/dist/hash.js.map +1 -1
  42. package/dist/jss.d.ts +11 -6
  43. package/dist/jss.js +47 -2
  44. package/dist/jss.js.map +1 -1
  45. package/dist/numberToBase.js.map +1 -1
  46. package/dist/stateMachineContextFactory.js.map +1 -1
  47. package/dist/useCookie.js.map +1 -1
  48. package/dist/useKeyboard.js.map +1 -1
  49. package/dist/useNavigation.js.map +1 -1
  50. package/dist/useRefEffect.js.map +1 -1
  51. package/next-env.d.ts +2 -1
  52. package/next.config.js +2 -1
  53. package/package.json +27 -20
  54. package/src/BaseListItem.tsx +1 -1
  55. package/src/Calendar.tsx +2 -2
  56. package/src/Card.tsx +1 -1
  57. package/src/Dialog.tsx +10 -4
  58. package/src/Icon.tsx +4 -30
  59. package/src/Popover.tsx +6 -4
  60. package/src/Providers.tsx +1 -1
  61. package/src/RangeInput.tsx +1 -1
  62. package/src/Storage.tsx +1 -1
  63. package/src/TextInput.tsx +17 -5
  64. package/src/Tooltip.tsx +5 -4
  65. package/src/docs/mdx.tsx +40 -0
  66. package/src/docs/searchIndex.ts +1 -1
  67. package/src/jss.tsx +55 -3
  68. package/src/pages/Badge.mdx +136 -14
  69. package/src/pages/Button.mdx +121 -63
  70. package/src/pages/Calendar.mdx +196 -2
  71. package/src/pages/Card.mdx +123 -47
  72. package/src/pages/Checkbox.mdx +103 -28
  73. package/src/pages/Dialog.mdx +172 -7
  74. package/src/pages/FlexLayout.mdx +185 -51
  75. package/src/pages/Hashing.mdx +199 -8
  76. package/src/pages/Icon.mdx +150 -42
  77. package/src/pages/IconButton.mdx +127 -116
  78. package/src/pages/Link.mdx +118 -47
  79. package/src/pages/List.mdx +167 -283
  80. package/src/pages/ListHeader.mdx +157 -27
  81. package/src/pages/Popover.mdx +177 -55
  82. package/src/pages/ProgressBar.mdx +186 -6
  83. package/src/pages/ProgressCircle.mdx +196 -13
  84. package/src/pages/RangeInput.mdx +189 -61
  85. package/src/pages/Tabs.mdx +202 -346
  86. package/src/pages/Text.mdx +102 -81
  87. package/src/pages/TextInput.mdx +169 -44
  88. package/src/pages/TextPairing.mdx +164 -115
  89. package/src/pages/TimeInput.mdx +142 -37
  90. package/src/pages/Tooltip.mdx +99 -12
  91. package/src/pages/_app.tsx +13 -3
  92. package/src/pages/css-in-js.mdx +196 -83
  93. package/src/pages/navigation.mdx +122 -20
  94. package/src/pages/useCookie.mdx +168 -7
  95. package/src/pages/useKeyboard.mdx +242 -5
  96. package/src/pages/useNavigation.mdx +244 -16
  97. package/src/pages/usePromise.mdx +271 -6
  98. package/src/pages/useRefEffect.mdx +230 -28
  99. package/tsconfig.json +13 -4
@@ -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
- The `Dialog` component can be used to show a modal window on top of the main UI. When visible, it will trap focus and clicks to the underlying UI, which is the required behaviour for acceessibility.
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
- function ExampleDialog({ close }) {
40
+ import { Dialog, useDialog } from "aidos-ui";
41
+
42
+ function MyDialog({ close }) {
11
43
  return (
12
- <Dialog close={close} label="Example">
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
- export function DialogTrigger() {
22
- const { open } = useDialog<void>(
23
- ({ close }) => <ExampleDialog close={close} />,
53
+ function App() {
54
+ const { open } = useDialog(
55
+ ({ close }) => <MyDialog close={close} />,
24
56
  { closeOnOutsideClick: true }
25
57
  );
26
58
 
27
- return <Button label="Show dialog" color={"positive"} onClick={() => open()} />;
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
@@ -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
- Because flex is so common there are a `Row` and a `Column` components, with a lot of utility that mean it is very easy to create complex layouts without having to create extra styles.
10
+ `Row` and `Column` are the primary layout components, providing a simple API for flexbox layouts without writing custom styles.
9
11
 
10
- - justify: like the `justify-content` flex property.
11
- - gap: like the `gap` property, it accepts any `Size`.
12
- - align: like the `align-items` flex property.
13
- - padding: like the `padding` property, it accepts any `Padding`.
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
- jss={{ border: `1px solid ${cssVar("--highlight")}` }}
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
- jss={{ border: `1px solid ${cssVar("--highlight")}` }}
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
- jss={{ border: `1px solid ${cssVar("--highlight")}` }}
36
- justify="space-between"
37
- align="center"
38
- >
39
- <Badge />
40
- <Badge />
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
- <Column gap="medium">
47
- <Row
48
- jss={{ border: `1px solid ${cssVar("--highlight")}` }}
49
- justify="space-around"
50
- align="center"
51
- >
52
- <Badge />
53
- <Badge />
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
+ ```
@@ -1,20 +1,211 @@
1
- # Hashing and unique IDs
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
- The `hash` function can be used to create a numeric hash from a given string. If you want to constraint the hashing space you can pass an optional modulus.
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
- const id = hash("ABC"); // 64578
9
- const small_id = hash("ABC", 10); // 8
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
- The `numberToBase` util will transform any number to an arbitrary base represented as a string of characters (e.g. the base two could be ["0", "1"]). This can be useful to convert between numeric representations or to compress big numbers. It defaults to a base of 26 elements formed of the English alphabet.
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
- const id_base_26 = numberToBase(hash("ABC")); // cqmv
18
- const id_base_2 = numberToBase(hash("ABC"), ["?", "!"]); //!!!!!???!???!?!
19
- const id_base_2 = numberToBase(8, ["1", "0"]); // 100
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