@regardio/react 0.4.7 → 0.5.5

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 (172) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +6 -8
  3. package/dist/{components/background-slideshow.js → background-slideshow/index.js} +2 -11
  4. package/dist/{components/blurry-gradient.js → blurry-gradient/index.js} +15 -9
  5. package/dist/{components/carousel.d.ts → carousel/index.d.ts} +17 -9
  6. package/dist/{components/carousel.js → carousel/index.js} +34 -30
  7. package/dist/{components/countdown.js → countdown/index.js} +2 -11
  8. package/dist/{components/generic-error.js → generic-error/index.js} +1 -1
  9. package/dist/grid/index.d.ts +1196 -0
  10. package/dist/grid/index.js +239 -0
  11. package/dist/heading/index.d.ts +24 -0
  12. package/dist/{components/heading.js → heading/index.js} +15 -34
  13. package/dist/highlight/index.d.ts +13 -0
  14. package/dist/{components/highlight.js → highlight/index.js} +9 -17
  15. package/dist/hooks/use-nonce.d.ts +1 -6
  16. package/dist/hooks/use-nonce.js +1 -6
  17. package/dist/{components/icon-button.js → icon-button/index.js} +1 -1
  18. package/dist/{components/if.js → if/index.js} +1 -1
  19. package/dist/{components/iframe.js → iframe/index.js} +2 -11
  20. package/dist/{components/link.d.ts → link/index.d.ts} +19 -13
  21. package/dist/{components/link.js → link/index.js} +31 -36
  22. package/dist/list/index.d.ts +69 -0
  23. package/dist/list/index.js +65 -0
  24. package/dist/{components/markdown-container.js → markdown-container/index.js} +3 -67
  25. package/dist/{components/password-input.js → password-input/index.js} +2 -11
  26. package/dist/{components/picture.js → picture/index.js} +2 -11
  27. package/dist/{components/protected-email.d.ts → protected-email/index.d.ts} +1 -1
  28. package/dist/{components/protected-email.js → protected-email/index.js} +1 -1
  29. package/dist/text/index.d.ts +20 -0
  30. package/dist/text/index.js +38 -0
  31. package/dist/utils/author/index.d.ts +3 -0
  32. package/dist/utils/author/index.js +33 -0
  33. package/dist/utils/text/index.d.ts +15 -0
  34. package/dist/utils/text/index.js +73 -0
  35. package/package.json +170 -187
  36. package/src/background-slideshow/background-slideshow.stories.tsx +137 -0
  37. package/src/{components → background-slideshow}/background-slideshow.tsx +3 -1
  38. package/src/background-slideshow/index.ts +2 -0
  39. package/src/{stories/BlurryGradient.stories.tsx → blurry-gradient/blurry-gradient.stories.tsx} +1 -1
  40. package/src/{components → blurry-gradient}/blurry-gradient.tsx +14 -8
  41. package/src/blurry-gradient/index.ts +2 -0
  42. package/src/carousel/carousel-content.tsx +16 -0
  43. package/src/carousel/carousel-item.tsx +23 -0
  44. package/src/carousel/carousel-next.tsx +22 -0
  45. package/src/carousel/carousel-previous.tsx +22 -0
  46. package/src/{components/carousel.tsx → carousel/carousel-root.tsx} +8 -78
  47. package/src/carousel/carousel.stories.tsx +135 -0
  48. package/src/carousel/index.parts.ts +5 -0
  49. package/src/carousel/index.ts +4 -0
  50. package/src/{stories/Countdown.stories.tsx → countdown/countdown.stories.tsx} +1 -1
  51. package/src/{components → countdown}/countdown.tsx +3 -7
  52. package/src/countdown/index.ts +1 -0
  53. package/src/{stories/GenericError.stories.tsx → generic-error/generic-error.stories.tsx} +1 -1
  54. package/src/{components → generic-error}/generic-error.tsx +2 -0
  55. package/src/generic-error/index.ts +2 -0
  56. package/src/grid/grid-item.tsx +188 -0
  57. package/src/grid/grid-root.tsx +72 -0
  58. package/src/grid/grid.stories.tsx +236 -0
  59. package/src/grid/index.parts.ts +2 -0
  60. package/src/grid/index.ts +5 -0
  61. package/src/{stories/Heading.stories.tsx → heading/heading.stories.tsx} +1 -1
  62. package/src/{components → heading}/heading.tsx +17 -25
  63. package/src/heading/index.ts +2 -0
  64. package/src/{stories/Highlight.stories.tsx → highlight/highlight.stories.tsx} +1 -1
  65. package/src/{components → highlight}/highlight.tsx +13 -9
  66. package/src/highlight/index.ts +2 -0
  67. package/src/hooks/use-nonce.ts +0 -10
  68. package/src/{stories/IconButton.stories.tsx → icon-button/icon-button.stories.tsx} +1 -1
  69. package/src/icon-button/index.ts +2 -0
  70. package/src/{stories/If.stories.tsx → if/if.stories.tsx} +1 -1
  71. package/src/if/index.ts +1 -0
  72. package/src/{stories/Iframe.stories.tsx → iframe/iframe.stories.tsx} +1 -1
  73. package/src/{components → iframe}/iframe.tsx +1 -1
  74. package/src/iframe/index.ts +2 -0
  75. package/src/link/index.ts +2 -0
  76. package/src/{stories/Link.stories.tsx → link/link.stories.tsx} +52 -1
  77. package/src/{components → link}/link.tsx +39 -28
  78. package/src/list/index.parts.ts +2 -0
  79. package/src/list/index.ts +4 -0
  80. package/src/list/list-item.tsx +63 -0
  81. package/src/list/list-root-context.ts +21 -0
  82. package/src/list/list-root.tsx +81 -0
  83. package/src/list/list.css +32 -0
  84. package/src/list/list.stories.tsx +119 -0
  85. package/src/list/list.test.tsx +168 -0
  86. package/src/markdown-container/index.ts +2 -0
  87. package/src/{stories/MarkdownContainer.stories.tsx → markdown-container/markdown-container.stories.tsx} +56 -1
  88. package/src/{components → markdown-container}/markdown-container.tsx +3 -1
  89. package/src/password-input/index.ts +2 -0
  90. package/src/{stories/PasswordInput.stories.tsx → password-input/password-input.stories.tsx} +1 -1
  91. package/src/{components → password-input}/password-input.tsx +4 -4
  92. package/src/picture/index.ts +2 -0
  93. package/src/{stories/Picture.stories.tsx → picture/picture.stories.tsx} +1 -1
  94. package/src/{components → picture}/picture.tsx +2 -4
  95. package/src/protected-email/index.ts +2 -0
  96. package/src/{stories/ProtectedEmail.stories.tsx → protected-email/protected-email.stories.tsx} +1 -1
  97. package/src/{components → protected-email}/protected-email.tsx +3 -1
  98. package/src/tailwind.css +10 -0
  99. package/src/text/index.ts +2 -0
  100. package/src/{stories/Text.stories.tsx → text/text.stories.tsx} +1 -1
  101. package/src/text/text.tsx +46 -0
  102. package/src/utils/author/author.tsx +36 -0
  103. package/src/utils/author/index.ts +1 -0
  104. package/src/utils/text/index.ts +1 -0
  105. package/src/utils/text/text.tsx +103 -0
  106. package/dist/components/box.d.ts +0 -20
  107. package/dist/components/box.js +0 -50
  108. package/dist/components/definition-list.d.ts +0 -43
  109. package/dist/components/definition-list.js +0 -89
  110. package/dist/components/heading.d.ts +0 -27
  111. package/dist/components/highlight.d.ts +0 -19
  112. package/dist/components/item.d.ts +0 -70
  113. package/dist/components/item.js +0 -512
  114. package/dist/components/leaflet-map.d.ts +0 -34
  115. package/dist/components/leaflet-map.js +0 -201
  116. package/dist/components/list-item.d.ts +0 -19
  117. package/dist/components/list-item.js +0 -37
  118. package/dist/components/maptiler-map.d.ts +0 -27
  119. package/dist/components/maptiler-map.js +0 -129
  120. package/dist/components/text.d.ts +0 -20
  121. package/dist/components/text.js +0 -45
  122. package/dist/components/unordered-list.d.ts +0 -19
  123. package/dist/components/unordered-list.js +0 -39
  124. package/dist/utils/author.d.ts +0 -9
  125. package/dist/utils/author.js +0 -55
  126. package/dist/utils/cn.d.ts +0 -9
  127. package/dist/utils/cn.js +0 -14
  128. package/dist/utils/is-route-active.d.ts +0 -19
  129. package/dist/utils/is-route-active.js +0 -56
  130. package/dist/utils/text.d.ts +0 -24
  131. package/dist/utils/text.js +0 -127
  132. package/src/components/box.tsx +0 -45
  133. package/src/components/definition-list.tsx +0 -90
  134. package/src/components/item.tsx +0 -340
  135. package/src/components/leaflet-map.tsx +0 -294
  136. package/src/components/link.test.tsx +0 -387
  137. package/src/components/list-item.tsx +0 -30
  138. package/src/components/maptiler-map.tsx +0 -181
  139. package/src/components/text.tsx +0 -38
  140. package/src/components/unordered-list.tsx +0 -32
  141. package/src/hooks/use-nonce.test.ts +0 -35
  142. package/src/stories/BackgroundSlideshow.stories.tsx +0 -68
  143. package/src/stories/Box.stories.tsx +0 -83
  144. package/src/stories/Carousel.stories.tsx +0 -95
  145. package/src/stories/DefinitionList.stories.tsx +0 -51
  146. package/src/stories/Item.stories.tsx +0 -79
  147. package/src/stories/ListItem.stories.tsx +0 -38
  148. package/src/stories/UnorderedList.stories.tsx +0 -73
  149. package/src/styles/tailwind.css +0 -7
  150. package/src/test-setup.ts +0 -1
  151. package/src/utils/author.test.ts +0 -54
  152. package/src/utils/author.tsx +0 -73
  153. package/src/utils/cn.test.ts +0 -48
  154. package/src/utils/cn.ts +0 -14
  155. package/src/utils/is-route-active.test.ts +0 -80
  156. package/src/utils/is-route-active.ts +0 -100
  157. package/src/utils/text.test.ts +0 -152
  158. package/src/utils/text.tsx +0 -209
  159. package/src/vite-env.d.ts +0 -1
  160. /package/dist/{components/background-slideshow.d.ts → background-slideshow/index.d.ts} +0 -0
  161. /package/dist/{components/blurry-gradient.d.ts → blurry-gradient/index.d.ts} +0 -0
  162. /package/dist/{components/countdown.d.ts → countdown/index.d.ts} +0 -0
  163. /package/dist/{components/generic-error.d.ts → generic-error/index.d.ts} +0 -0
  164. /package/dist/{components/icon-button.d.ts → icon-button/index.d.ts} +0 -0
  165. /package/dist/{components/if.d.ts → if/index.d.ts} +0 -0
  166. /package/dist/{components/iframe.d.ts → iframe/index.d.ts} +0 -0
  167. /package/dist/{components/markdown-container.d.ts → markdown-container/index.d.ts} +0 -0
  168. /package/dist/{components/password-input.d.ts → password-input/index.d.ts} +0 -0
  169. /package/dist/{components/picture.d.ts → picture/index.d.ts} +0 -0
  170. /package/src/{components → icon-button}/icon-button.tsx +0 -0
  171. /package/src/{components → if}/if.tsx +0 -0
  172. /package/src/{styles/storybook.css → storybook.css} +0 -0
@@ -0,0 +1,236 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { Grid } from './index';
3
+
4
+ const meta: Meta<typeof Grid.Root> = {
5
+ component: Grid.Root,
6
+ decorators: [
7
+ (Story) => (
8
+ <div
9
+ style={
10
+ {
11
+ '--spacing-grid-gutter': '1rem',
12
+ '--spacing-grid-max': '80rem',
13
+ } as React.CSSProperties
14
+ }
15
+ >
16
+ <Story />
17
+ </div>
18
+ ),
19
+ ],
20
+ parameters: {
21
+ layout: 'padded',
22
+ },
23
+ title: 'Components/Grid',
24
+ };
25
+
26
+ export default meta;
27
+ type Story = StoryObj<typeof Grid.Root>;
28
+
29
+ const ItemBox = ({ children, className }: { children: React.ReactNode; className?: string }) => (
30
+ <div className={`bg-blue-100 border border-blue-300 rounded p-4 text-center ${className ?? ''}`}>
31
+ {children}
32
+ </div>
33
+ );
34
+
35
+ export const Default: Story = {
36
+ render: () => (
37
+ <Grid.Root>
38
+ <Grid.Item span={12}>
39
+ <ItemBox>Full width (12 cols)</ItemBox>
40
+ </Grid.Item>
41
+ <Grid.Item span={6}>
42
+ <ItemBox>Half (6 cols)</ItemBox>
43
+ </Grid.Item>
44
+ <Grid.Item span={6}>
45
+ <ItemBox>Half (6 cols)</ItemBox>
46
+ </Grid.Item>
47
+ <Grid.Item span={4}>
48
+ <ItemBox>Third (4 cols)</ItemBox>
49
+ </Grid.Item>
50
+ <Grid.Item span={4}>
51
+ <ItemBox>Third (4 cols)</ItemBox>
52
+ </Grid.Item>
53
+ <Grid.Item span={4}>
54
+ <ItemBox>Third (4 cols)</ItemBox>
55
+ </Grid.Item>
56
+ </Grid.Root>
57
+ ),
58
+ };
59
+
60
+ export const ResponsiveSpan: Story = {
61
+ name: 'Responsive Span (Container Queries)',
62
+ render: () => (
63
+ <Grid.Root>
64
+ <Grid.Item
65
+ span={12}
66
+ spanLg={3}
67
+ spanMd={4}
68
+ spanSm={6}
69
+ >
70
+ <ItemBox>12 → 6 → 4 → 3</ItemBox>
71
+ </Grid.Item>
72
+ <Grid.Item
73
+ span={12}
74
+ spanLg={3}
75
+ spanMd={4}
76
+ spanSm={6}
77
+ >
78
+ <ItemBox>12 → 6 → 4 → 3</ItemBox>
79
+ </Grid.Item>
80
+ <Grid.Item
81
+ span={12}
82
+ spanLg={3}
83
+ spanMd={4}
84
+ spanSm={6}
85
+ >
86
+ <ItemBox>12 → 6 → 4 → 3</ItemBox>
87
+ </Grid.Item>
88
+ <Grid.Item
89
+ span={12}
90
+ spanLg={3}
91
+ spanMd={4}
92
+ spanSm={6}
93
+ >
94
+ <ItemBox>12 → 6 → 4 → 3</ItemBox>
95
+ </Grid.Item>
96
+ </Grid.Root>
97
+ ),
98
+ };
99
+
100
+ export const ColumnPositioning: Story = {
101
+ name: 'Column Start/End Positioning',
102
+ render: () => (
103
+ <Grid.Root>
104
+ <Grid.Item
105
+ end={5}
106
+ start={1}
107
+ >
108
+ <ItemBox>Cols 1-4</ItemBox>
109
+ </Grid.Item>
110
+ <Grid.Item
111
+ end={13}
112
+ start={5}
113
+ >
114
+ <ItemBox>Cols 5-12</ItemBox>
115
+ </Grid.Item>
116
+ <Grid.Item
117
+ span={8}
118
+ start={3}
119
+ >
120
+ <ItemBox>Start at 3, span 8</ItemBox>
121
+ </Grid.Item>
122
+ </Grid.Root>
123
+ ),
124
+ };
125
+
126
+ export const RowSpan: Story = {
127
+ name: 'Row Spanning',
128
+ render: () => (
129
+ <Grid.Root>
130
+ <Grid.Item
131
+ rowSpan={2}
132
+ span={4}
133
+ >
134
+ <ItemBox className="h-full">Spans 2 rows</ItemBox>
135
+ </Grid.Item>
136
+ <Grid.Item span={8}>
137
+ <ItemBox>Row 1</ItemBox>
138
+ </Grid.Item>
139
+ <Grid.Item span={8}>
140
+ <ItemBox>Row 2</ItemBox>
141
+ </Grid.Item>
142
+ </Grid.Root>
143
+ ),
144
+ };
145
+
146
+ export const FlowVariants: Story = {
147
+ name: 'Grid Flow Variants',
148
+ render: () => (
149
+ <div className="space-y-8">
150
+ <div>
151
+ <h3 className="mb-2 font-semibold">Dense (default)</h3>
152
+ <Grid.Root flow="dense">
153
+ <Grid.Item span={8}>
154
+ <ItemBox>8 cols</ItemBox>
155
+ </Grid.Item>
156
+ <Grid.Item span={6}>
157
+ <ItemBox>6 cols</ItemBox>
158
+ </Grid.Item>
159
+ <Grid.Item span={4}>
160
+ <ItemBox>4 cols (fills gap)</ItemBox>
161
+ </Grid.Item>
162
+ </Grid.Root>
163
+ </div>
164
+ <div>
165
+ <h3 className="mb-2 font-semibold">Row</h3>
166
+ <Grid.Root flow="row">
167
+ <Grid.Item span={8}>
168
+ <ItemBox>8 cols</ItemBox>
169
+ </Grid.Item>
170
+ <Grid.Item span={6}>
171
+ <ItemBox>6 cols</ItemBox>
172
+ </Grid.Item>
173
+ <Grid.Item span={4}>
174
+ <ItemBox>4 cols (new row)</ItemBox>
175
+ </Grid.Item>
176
+ </Grid.Root>
177
+ </div>
178
+ </div>
179
+ ),
180
+ };
181
+
182
+ export const AlignVariants: Story = {
183
+ name: 'Content Alignment',
184
+ render: () => (
185
+ <div className="space-y-8">
186
+ <div>
187
+ <h3 className="mb-2 font-semibold">Start</h3>
188
+ <Grid.Root
189
+ align="start"
190
+ className="min-h-[200px] bg-gray-50"
191
+ >
192
+ <Grid.Item span={4}>
193
+ <ItemBox>Item</ItemBox>
194
+ </Grid.Item>
195
+ <Grid.Item span={4}>
196
+ <ItemBox>Item</ItemBox>
197
+ </Grid.Item>
198
+ </Grid.Root>
199
+ </div>
200
+ <div>
201
+ <h3 className="mb-2 font-semibold">Center</h3>
202
+ <Grid.Root
203
+ align="center"
204
+ className="min-h-[200px] bg-gray-50"
205
+ >
206
+ <Grid.Item span={4}>
207
+ <ItemBox>Item</ItemBox>
208
+ </Grid.Item>
209
+ <Grid.Item span={4}>
210
+ <ItemBox>Item</ItemBox>
211
+ </Grid.Item>
212
+ </Grid.Root>
213
+ </div>
214
+ </div>
215
+ ),
216
+ };
217
+
218
+ export const StyleOverrides: Story = {
219
+ name: 'Style Overrides (tailwind-variants)',
220
+ render: () => (
221
+ <Grid.Root className="bg-linear-to-r from-purple-50 to-pink-50 p-4 rounded-lg">
222
+ <Grid.Item
223
+ className="bg-purple-200 rounded-lg p-6"
224
+ span={6}
225
+ >
226
+ Custom styled item
227
+ </Grid.Item>
228
+ <Grid.Item
229
+ className="bg-pink-200 rounded-lg p-6"
230
+ span={6}
231
+ >
232
+ Custom styled item
233
+ </Grid.Item>
234
+ </Grid.Root>
235
+ ),
236
+ };
@@ -0,0 +1,2 @@
1
+ export { GridItem as Item } from './grid-item';
2
+ export { GridRoot as Root } from './grid-root';
@@ -0,0 +1,5 @@
1
+ export type { GridItemProps, GridItemVariants } from './grid-item';
2
+ export { gridItem } from './grid-item';
3
+ export type { GridRootProps } from './grid-root';
4
+ export { useGrid } from './grid-root';
5
+ export * as Grid from './index.parts';
@@ -1,5 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { Heading } from '../components/heading';
2
+ import { Heading } from './heading';
3
3
 
4
4
  const meta: Meta<typeof Heading> = {
5
5
  component: Heading,
@@ -1,43 +1,36 @@
1
+ import { cn, tv } from '@regardio/tailwind/utils';
1
2
  import type { ElementType, HTMLAttributes } from 'react';
2
- import { cn, cva, type VariantProps } from '../utils/cn';
3
3
  import { lowerCaseSzett, shy } from '../utils/text';
4
4
 
5
- const heading = cva({
5
+ const levelVariants = {
6
+ 1: ['text-2xl'],
7
+ 2: ['text-xl'],
8
+ 3: ['text-lg'],
9
+ 4: ['text-md'],
10
+ 5: ['text-sm'],
11
+ 6: ['text-xs'],
12
+ } as const;
13
+
14
+ const heading = tv({
6
15
  base: [],
7
16
  defaultVariants: {
8
17
  level: 3,
9
18
  },
10
19
  variants: {
11
- level: {
12
- 1: ['text-2xl'],
13
- 2: ['text-xl'],
14
- 3: ['text-lg'],
15
- 4: ['text-md'],
16
- 5: ['text-sm'],
17
- 6: ['text-xs'],
18
- },
19
- variant: {},
20
+ level: levelVariants,
20
21
  },
21
22
  });
22
23
 
23
- export interface HeadingProps
24
- extends HTMLAttributes<HTMLHeadingElement>,
25
- VariantProps<typeof heading> {
24
+ export type HeadingLevel = keyof typeof levelVariants;
25
+
26
+ export interface HeadingProps extends HTMLAttributes<HTMLHeadingElement> {
26
27
  as?: ElementType;
27
28
  className?: string;
29
+ level?: HeadingLevel;
28
30
  locale?: string;
29
31
  }
30
32
 
31
- export type HeadingLevel = HeadingProps['level'];
32
-
33
- export const Heading = ({
34
- as,
35
- children,
36
- className,
37
- level = 2,
38
- variant,
39
- ...props
40
- }: HeadingProps) => {
33
+ export const Heading = ({ as, children, className, level = 2, ...props }: HeadingProps) => {
41
34
  const word = lowerCaseSzett(shy(children));
42
35
  const Component: ElementType = as || `h${level}`;
43
36
 
@@ -46,7 +39,6 @@ export const Heading = ({
46
39
  className={cn(
47
40
  heading({
48
41
  level,
49
- variant,
50
42
  }),
51
43
  className,
52
44
  )}
@@ -0,0 +1,2 @@
1
+ export type { HeadingLevel, HeadingProps } from './heading';
2
+ export { Heading } from './heading';
@@ -1,5 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { Highlight } from '../components/highlight';
2
+ import { Highlight } from './highlight';
3
3
 
4
4
  const meta: Meta<typeof Highlight> = {
5
5
  component: Highlight,
@@ -1,25 +1,29 @@
1
+ import { tv } from '@regardio/tailwind/utils';
1
2
  import type { ComponentProps } from 'react';
2
- import { cva, type VariantProps } from '../utils/cn';
3
3
 
4
- const li = cva({
4
+ const highlightVariants = {
5
+ primary: ['highlight'],
6
+ } as const;
7
+
8
+ const highlight = tv({
5
9
  defaultVariants: {
6
10
  variant: 'primary',
7
11
  },
8
12
  variants: {
9
- variant: {
10
- primary: ['highlight'],
11
- },
13
+ variant: highlightVariants,
12
14
  },
13
15
  });
14
16
 
15
- export interface HighlightProps extends ComponentProps<'mark'>, VariantProps<typeof li> {}
17
+ export type HighlightVariant = keyof typeof highlightVariants;
16
18
 
17
- export const Highlight = (props: HighlightProps) => {
18
- const { children, className, variant } = props;
19
+ export interface HighlightProps extends ComponentProps<'mark'> {
20
+ variant?: HighlightVariant;
21
+ }
19
22
 
23
+ export const Highlight = ({ children, className, variant }: HighlightProps) => {
20
24
  return (
21
25
  <mark
22
- className={li({
26
+ className={highlight({
23
27
  className,
24
28
  variant,
25
29
  })}
@@ -0,0 +1,2 @@
1
+ export type { HighlightProps } from './highlight';
2
+ export { Highlight } from './highlight';
@@ -8,13 +8,3 @@ export const NonceContext = createContext<string>('');
8
8
  export const NonceProvider = NonceContext.Provider;
9
9
 
10
10
  export const useNonce = () => useContext(NonceContext);
11
-
12
- /**
13
- * Generate a cryptographically secure nonce for CSP.
14
- * @returns A base64-encoded random nonce
15
- */
16
- export function generateNonce(): string {
17
- const array = new Uint8Array(16);
18
- crypto.getRandomValues(array);
19
- return btoa(String.fromCharCode(...array));
20
- }
@@ -1,5 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { IconButton } from '../components/icon-button';
2
+ import { IconButton } from './icon-button';
3
3
 
4
4
  const meta: Meta<typeof IconButton> = {
5
5
  component: IconButton,
@@ -0,0 +1,2 @@
1
+ export type { IconButtonProps } from './icon-button';
2
+ export { IconButton } from './icon-button';
@@ -1,5 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { If } from '../components/if';
2
+ import { If } from './if';
3
3
 
4
4
  const meta: Meta<typeof If> = {
5
5
  component: If,
@@ -0,0 +1 @@
1
+ export { If } from './if';
@@ -1,5 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { Iframe } from '../components/iframe';
2
+ import { Iframe } from './iframe';
3
3
 
4
4
  const meta: Meta<typeof Iframe> = {
5
5
  component: Iframe,
@@ -1,5 +1,5 @@
1
+ import { cn } from '@regardio/tailwind/utils';
1
2
  import type React from 'react';
2
- import { cn } from '../utils/cn';
3
3
 
4
4
  export type IframeProps = {
5
5
  src: string;
@@ -0,0 +1,2 @@
1
+ export type { IframeProps } from './iframe';
2
+ export { Iframe } from './iframe';
@@ -0,0 +1,2 @@
1
+ export type { LinkBaseProps, LinkProps, MarkdownLinkProps, PathResolver } from './link';
2
+ export { Link, LinkBase, MarkdownLink, PathResolverProvider, usePathResolver } from './link';
@@ -1,6 +1,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import { MemoryRouter } from 'react-router';
3
- import { Link } from '../components/link';
3
+ import { Link } from './link';
4
4
 
5
5
  const meta: Meta<typeof Link> = {
6
6
  component: Link,
@@ -56,3 +56,54 @@ export const AllVariants: Story = {
56
56
  </div>
57
57
  ),
58
58
  };
59
+
60
+ export const TelephoneLink: Story = {
61
+ args: {
62
+ children: 'Call Us',
63
+ to: 'tel:+1234567890',
64
+ },
65
+ };
66
+
67
+ export const MailtoLink: Story = {
68
+ args: {
69
+ children: 'Email Us',
70
+ to: 'mailto:hello@example.com',
71
+ },
72
+ };
73
+
74
+ export const HashLink: Story = {
75
+ args: {
76
+ children: 'Jump to Section',
77
+ to: '#section-id',
78
+ },
79
+ };
80
+
81
+ export const WithSearchAndHash: Story = {
82
+ args: {
83
+ children: 'Link with Query',
84
+ to: { hash: '#results', pathname: '/search', search: '?q=test' },
85
+ },
86
+ };
87
+
88
+ export const EmptyPath: Story = {
89
+ args: {
90
+ children: 'No destination',
91
+ to: '',
92
+ },
93
+ };
94
+
95
+ export const WithArrow: Story = {
96
+ args: {
97
+ arrow: 'rarr',
98
+ children: 'Link with Arrow',
99
+ to: '/arrow',
100
+ },
101
+ };
102
+
103
+ export const ButtonVariant: Story = {
104
+ args: {
105
+ children: 'Button Style Link',
106
+ to: '/button',
107
+ variant: 'button',
108
+ },
109
+ };
@@ -1,7 +1,9 @@
1
+ 'use client';
2
+
3
+ import { tv } from '@regardio/tailwind/utils';
1
4
  import { createContext, useCallback, useContext } from 'react';
2
5
  import type { NavLinkProps, NavLinkRenderProps } from 'react-router';
3
6
  import { NavLink } from 'react-router';
4
- import { cva, type VariantProps } from '../utils/cn';
5
7
  import { lowerCaseSzett } from '../utils/text';
6
8
 
7
9
  /**
@@ -120,42 +122,51 @@ export const LinkBase = ({
120
122
  );
121
123
  };
122
124
 
123
- const link = cva({
125
+ const arrowVariants = {
126
+ darr: 'darr',
127
+ larr: 'larr',
128
+ rarr: 'rarr',
129
+ uarr: 'uarr',
130
+ } as const;
131
+
132
+ const linkVariants = {
133
+ button: [
134
+ 'block',
135
+ 'button',
136
+ 'mt-s',
137
+ 'relative',
138
+ 'rarr',
139
+ 'text-right',
140
+ 'text-sm',
141
+ 'tracking-wider',
142
+ 'uppercase',
143
+ ],
144
+ code: ['font-monospace'],
145
+ link: ['rarr', '!bg-transparent', 'uppercase', '!tracking-wider'],
146
+ navtitle: ['block', 'uppercase', 'tracking-wider'],
147
+ primary: [],
148
+ subtitle: ['text-lg'],
149
+ } as const;
150
+
151
+ const link = tv({
124
152
  base: [],
125
153
  defaultVariants: {
126
154
  variant: 'primary',
127
155
  },
128
156
  variants: {
129
- arrow: {
130
- darr: 'darr',
131
- larr: 'larr',
132
- rarr: 'rarr',
133
- uarr: 'uarr',
134
- },
135
- variant: {
136
- button: [
137
- 'block',
138
- 'button',
139
- 'mt-s',
140
- 'relative',
141
- 'rarr',
142
- 'text-right',
143
- 'text-sm',
144
- 'tracking-wider',
145
- 'uppercase',
146
- ],
147
- code: ['font-monospace'],
148
- link: ['rarr', '!bg-transparent', 'uppercase', '!tracking-wider'],
149
- navtitle: ['block', 'uppercase', 'tracking-wider'],
150
- primary: [],
151
- subtitle: ['text-lg'],
152
- },
157
+ arrow: arrowVariants,
158
+ variant: linkVariants,
153
159
  },
154
160
  });
155
161
 
156
- export interface LinkProps extends Omit<NavLinkProps, 'to'>, VariantProps<typeof link> {
162
+ export type LinkArrow = keyof typeof arrowVariants;
163
+ export type LinkVariant = keyof typeof linkVariants;
164
+
165
+ export interface LinkProps extends Omit<NavLinkProps, 'to'> {
166
+ arrow?: LinkArrow;
157
167
  to?: string | Partial<{ pathname?: string; search?: string; hash?: string }>;
158
168
  routeKey?: string;
169
+ variant?: LinkVariant;
159
170
  viewTransition?: boolean;
160
171
  }
161
172
 
@@ -174,7 +185,7 @@ export const Link = ({
174
185
  {...props}
175
186
  className={link({
176
187
  arrow,
177
- className,
188
+ className: typeof className === 'string' ? className : undefined,
178
189
  variant,
179
190
  })}
180
191
  routeKey={routeKey}
@@ -0,0 +1,2 @@
1
+ export { ListItem as Item } from './list-item';
2
+ export { ListRoot as Root } from './list-root';
@@ -0,0 +1,4 @@
1
+ export * as List from './index.parts';
2
+ export type { ListItemProps } from './list-item';
3
+ export type { ListRootProps } from './list-root';
4
+ export type { ListRootContextValue } from './list-root-context';
@@ -0,0 +1,63 @@
1
+ 'use client';
2
+
3
+ import {
4
+ type ComponentPropsWithoutRef,
5
+ type ElementType,
6
+ type ForwardedRef,
7
+ forwardRef,
8
+ type ReactNode,
9
+ } from 'react';
10
+ import { useListRootContext } from './list-root-context';
11
+
12
+ type ListItemElement = 'li' | 'dd' | 'dt' | 'div' | 'span';
13
+
14
+ export type ListItemProps<T extends ListItemElement = 'li'> = Omit<
15
+ ComponentPropsWithoutRef<T>,
16
+ 'children'
17
+ > & {
18
+ /**
19
+ * The element type to render.
20
+ * Falls back to the defaultItemElement from ListRoot context, or 'li'.
21
+ */
22
+ render?: T;
23
+ /**
24
+ * The content of the list item.
25
+ */
26
+ children?: ReactNode;
27
+ };
28
+
29
+ function ListItemImpl<T extends ListItemElement = 'li'>(
30
+ props: ListItemProps<T>,
31
+ ref: ForwardedRef<HTMLElement>,
32
+ ) {
33
+ const context = useListRootContext();
34
+
35
+ const { render, children, className, ...elementProps } = props;
36
+
37
+ const resolvedElement = render ?? context?.defaultItemElement ?? 'li';
38
+ const resolvedClassName = context?.defaultItemClassName
39
+ ? className
40
+ ? `${context.defaultItemClassName} ${className}`
41
+ : context.defaultItemClassName
42
+ : className;
43
+
44
+ const Component = resolvedElement as ElementType;
45
+
46
+ return (
47
+ <Component
48
+ className={resolvedClassName}
49
+ ref={ref}
50
+ {...elementProps}
51
+ >
52
+ {children}
53
+ </Component>
54
+ );
55
+ }
56
+
57
+ export const ListItem = forwardRef(ListItemImpl) as <T extends ListItemElement = 'li'>(
58
+ props: ListItemProps<T> & { ref?: ForwardedRef<HTMLElement> },
59
+ ) => ReturnType<typeof ListItemImpl>;
60
+
61
+ export namespace ListItem {
62
+ export type Props<T extends ListItemElement = 'li'> = ListItemProps<T>;
63
+ }