@alveole/storybook 0.26.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/README.md +255 -0
- package/dist/components/FilterBadge.d.ts +7 -0
- package/dist/components/FilterBadge.d.ts.map +1 -0
- package/dist/components/FilterBadge.js +4 -0
- package/dist/components/JsonBlock.d.ts +5 -0
- package/dist/components/JsonBlock.d.ts.map +1 -0
- package/dist/components/JsonBlock.js +13 -0
- package/dist/components/SearchField.d.ts +8 -0
- package/dist/components/SearchField.d.ts.map +1 -0
- package/dist/components/SearchField.js +16 -0
- package/dist/components/StoryCard.d.ts +7 -0
- package/dist/components/StoryCard.d.ts.map +1 -0
- package/dist/components/StoryCard.js +11 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/screens/StoriesScreen.d.ts +12 -0
- package/dist/screens/StoriesScreen.d.ts.map +1 -0
- package/dist/screens/StoriesScreen.js +33 -0
- package/dist/screens/StoryDetailScreen.d.ts +7 -0
- package/dist/screens/StoryDetailScreen.d.ts.map +1 -0
- package/dist/screens/StoryDetailScreen.js +41 -0
- package/dist/screens/ThemeConstantDetailScreen.d.ts +6 -0
- package/dist/screens/ThemeConstantDetailScreen.d.ts.map +1 -0
- package/dist/screens/ThemeConstantDetailScreen.js +9 -0
- package/dist/screens/ThemeConstantsScreen.d.ts +11 -0
- package/dist/screens/ThemeConstantsScreen.d.ts.map +1 -0
- package/dist/screens/ThemeConstantsScreen.js +14 -0
- package/dist/screens/ThemePaletteScreen.d.ts +12 -0
- package/dist/screens/ThemePaletteScreen.d.ts.map +1 -0
- package/dist/screens/ThemePaletteScreen.js +17 -0
- package/dist/screens/ThemeTypographyScreen.d.ts +7 -0
- package/dist/screens/ThemeTypographyScreen.d.ts.map +1 -0
- package/dist/screens/ThemeTypographyScreen.js +11 -0
- package/dist/types.d.ts +25 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +14 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +45 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# @alveole/storybook
|
|
2
|
+
|
|
3
|
+
Composants réutilisables pour exposer un catalogue de stories et de tokens de thème.
|
|
4
|
+
|
|
5
|
+
Le package reprend les grandes parties du `ui-kit` de `groove-application/app/ui-kit`, mais sous une forme réutilisable et indépendante d'Expo Router.
|
|
6
|
+
|
|
7
|
+
## Prérequis
|
|
8
|
+
|
|
9
|
+
Le package est prévu pour être utilisé avec :
|
|
10
|
+
|
|
11
|
+
- `@alveole/components`
|
|
12
|
+
- `@alveole/theme`
|
|
13
|
+
- `react`
|
|
14
|
+
- `react-native`
|
|
15
|
+
|
|
16
|
+
Il doit être rendu dans une app déjà enveloppée par `ThemeProvider`.
|
|
17
|
+
|
|
18
|
+
## Exports disponibles
|
|
19
|
+
|
|
20
|
+
Le package exporte :
|
|
21
|
+
|
|
22
|
+
- `StoriesScreen`
|
|
23
|
+
- `StoryDetailScreen`
|
|
24
|
+
- `ThemeConstantsScreen`
|
|
25
|
+
- `ThemeConstantDetailScreen`
|
|
26
|
+
- `ThemePaletteScreen`
|
|
27
|
+
- `ThemeTypographyScreen`
|
|
28
|
+
- `JsonBlock`
|
|
29
|
+
- les types `StorybookMeta`, `StorybookModule`, `StorybookFlag`, `StorybookFlagKey`
|
|
30
|
+
- les helpers de `utils`
|
|
31
|
+
|
|
32
|
+
## Ecriture recommandee des stories
|
|
33
|
+
|
|
34
|
+
Le package `@alveole/storybook` est pense pour consommer directement les stories ecrites comme dans `packages/components`.
|
|
35
|
+
|
|
36
|
+
Le pattern recommande est :
|
|
37
|
+
|
|
38
|
+
- un `export default` qui satisfait le type `Story`
|
|
39
|
+
- des `export const` pour chaque exemple rendu
|
|
40
|
+
|
|
41
|
+
Exemple proche de l'ecriture actuelle de [Box.stories.tsx](/Users/corentincouq/Documents/alveole/packages/components/src/core/Box/Box.stories.tsx) :
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import { Story } from '../../type';
|
|
45
|
+
import { Typography } from '../Typography';
|
|
46
|
+
import { Box } from './Box';
|
|
47
|
+
|
|
48
|
+
export default {
|
|
49
|
+
title: 'Box',
|
|
50
|
+
tags: ['Kit'],
|
|
51
|
+
experimental: false,
|
|
52
|
+
description: 'Description du composant',
|
|
53
|
+
component: Box,
|
|
54
|
+
styleFn: () => 'Aucun style applique',
|
|
55
|
+
} satisfies Story;
|
|
56
|
+
|
|
57
|
+
export const BoxDefault = () => (
|
|
58
|
+
<Box tag="box">
|
|
59
|
+
<Typography>Contenu par defaut</Typography>
|
|
60
|
+
</Box>
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
export const BoxPadded = () => (
|
|
64
|
+
<Box p={16}>
|
|
65
|
+
<Typography>Box avec padding</Typography>
|
|
66
|
+
</Box>
|
|
67
|
+
);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Le `default` porte les metadonnees de la story. Les autres exports sont les variantes affichees par `StoryDetailScreen`.
|
|
71
|
+
|
|
72
|
+
Concretement, `@alveole/storybook` n'introduit pas un nouveau format de story. Il reutilise celui que tu ecris deja dans `packages/components/src/**/*.stories.tsx`.
|
|
73
|
+
|
|
74
|
+
## Structure attendue par le package
|
|
75
|
+
|
|
76
|
+
Quand tu fais :
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import * as Stories from '@alveole/components/stories';
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
chaque module exporte se retrouve sous une forme equivalente a :
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
import type { StorybookModule } from '@alveole/storybook';
|
|
86
|
+
|
|
87
|
+
const ButtonStory = {
|
|
88
|
+
default: {
|
|
89
|
+
title: 'Button',
|
|
90
|
+
tags: ['Kit'],
|
|
91
|
+
experimental: false,
|
|
92
|
+
description: 'Boutons de l application',
|
|
93
|
+
figmaURL: 'https://figma.com/...',
|
|
94
|
+
styleFn: () => ({}),
|
|
95
|
+
},
|
|
96
|
+
Primary: () => <MyButton label="Primary" />,
|
|
97
|
+
Secondary: () => <MyButton label="Secondary" />,
|
|
98
|
+
} satisfies StorybookModule;
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Autrement dit :
|
|
102
|
+
|
|
103
|
+
- `default` doit contenir les metadonnees
|
|
104
|
+
- les autres exports doivent etre des composants React affichables
|
|
105
|
+
- il ne faut pas rendre `StoriesScreen` ou `StoryDetailScreen` a l'interieur d'une story unitaire
|
|
106
|
+
- le bon point d'entree est un ecran catalogue qui consomme `@alveole/components/stories`
|
|
107
|
+
|
|
108
|
+
## Exemple minimal pour les stories
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
import * as Stories from '@alveole/components/stories';
|
|
112
|
+
import { StoriesScreen, StoryDetailScreen, type StorybookModule } from '@alveole/storybook';
|
|
113
|
+
import React from 'react';
|
|
114
|
+
|
|
115
|
+
const storyList = Object.values(Stories) as StorybookModule[];
|
|
116
|
+
|
|
117
|
+
export const StoryCatalogPage = () => {
|
|
118
|
+
const [selectedStory, setSelectedStory] = React.useState<StorybookModule | null>(null);
|
|
119
|
+
|
|
120
|
+
if (selectedStory) {
|
|
121
|
+
return <StoryDetailScreen story={selectedStory} />;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<StoriesScreen
|
|
126
|
+
stories={storyList}
|
|
127
|
+
title="UI Kit - Components"
|
|
128
|
+
description="Catalogue des composants"
|
|
129
|
+
onSelectStory={setSelectedStory}
|
|
130
|
+
/>
|
|
131
|
+
);
|
|
132
|
+
};
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Exemple avec Expo Router
|
|
136
|
+
|
|
137
|
+
Le package ne crée pas les routes. Il fournit seulement les écrans. À toi de brancher la navigation.
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
import * as Stories from '@alveole/components/stories';
|
|
141
|
+
import { StoriesScreen, type StorybookModule } from '@alveole/storybook';
|
|
142
|
+
import { router } from 'expo-router';
|
|
143
|
+
|
|
144
|
+
const storyList = Object.values(Stories) as StorybookModule[];
|
|
145
|
+
|
|
146
|
+
export default function UIKitStoriesRoute() {
|
|
147
|
+
return (
|
|
148
|
+
<StoriesScreen
|
|
149
|
+
stories={storyList}
|
|
150
|
+
onSelectStory={story => {
|
|
151
|
+
router.push(`/ui-kit/components/${encodeURIComponent(story.default.title)}`);
|
|
152
|
+
}}
|
|
153
|
+
/>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Puis dans la route de détail :
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
import * as Stories from '@alveole/components/stories';
|
|
162
|
+
import { StoryDetailScreen, type StorybookModule } from '@alveole/storybook';
|
|
163
|
+
import { useLocalSearchParams } from 'expo-router';
|
|
164
|
+
|
|
165
|
+
export default function UIKitStoryDetailRoute() {
|
|
166
|
+
const { component } = useLocalSearchParams<{ component: string }>();
|
|
167
|
+
|
|
168
|
+
const story = (Object.values(Stories) as StorybookModule[]).find(entry => entry.default.title === component) ?? null;
|
|
169
|
+
|
|
170
|
+
return <StoryDetailScreen story={story} />;
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Exemple pour les constantes du thème
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
import * as ThemeConstants from '@alveole/theme';
|
|
178
|
+
import { ThemeConstantsScreen } from '@alveole/storybook';
|
|
179
|
+
|
|
180
|
+
export default function ThemeConstantsPage() {
|
|
181
|
+
return (
|
|
182
|
+
<ThemeConstantsScreen constants={ThemeConstants} title="UI Kit - Constants" description="Constantes du thème" />
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Exemple pour la palette
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
import { CustomPalette } from '@alveole/theme';
|
|
191
|
+
import { ThemePaletteScreen } from '@alveole/storybook';
|
|
192
|
+
|
|
193
|
+
export default function ThemePalettePage() {
|
|
194
|
+
return <ThemePaletteScreen palette={CustomPalette} />;
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Exemple pour la typographie
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
import { CustomTypography } from '@alveole/theme';
|
|
202
|
+
import { ThemeTypographyScreen } from '@alveole/storybook';
|
|
203
|
+
|
|
204
|
+
export default function ThemeTypographyPage() {
|
|
205
|
+
return <ThemeTypographyScreen typography={CustomTypography} />;
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Props principales
|
|
210
|
+
|
|
211
|
+
### `StoriesScreen`
|
|
212
|
+
|
|
213
|
+
- `stories`: liste des stories
|
|
214
|
+
- `title`: titre de page
|
|
215
|
+
- `description`: description de page
|
|
216
|
+
- `emptyMessage`: message affiché si aucun résultat
|
|
217
|
+
- `createLabel`: libellé du bouton d’action
|
|
218
|
+
- `onCreatePress`: callback du bouton d’action
|
|
219
|
+
- `onSelectStory`: callback au clic sur une story
|
|
220
|
+
|
|
221
|
+
### `StoryDetailScreen`
|
|
222
|
+
|
|
223
|
+
- `story`: story sélectionnée
|
|
224
|
+
- `notFoundMessage`: message si la story est absente
|
|
225
|
+
|
|
226
|
+
### `ThemeConstantsScreen`
|
|
227
|
+
|
|
228
|
+
- `constants`: objet de constantes
|
|
229
|
+
- `title`: titre de page
|
|
230
|
+
- `description`: description de page
|
|
231
|
+
- `onSelectConstant`: callback au clic sur une constante
|
|
232
|
+
|
|
233
|
+
### `ThemeConstantDetailScreen`
|
|
234
|
+
|
|
235
|
+
- `name`: nom de la constante
|
|
236
|
+
- `value`: valeur de la constante
|
|
237
|
+
|
|
238
|
+
### `ThemePaletteScreen`
|
|
239
|
+
|
|
240
|
+
- `palette`: objet de palette
|
|
241
|
+
- `title`: titre de page
|
|
242
|
+
- `description`: description de page
|
|
243
|
+
|
|
244
|
+
### `ThemeTypographyScreen`
|
|
245
|
+
|
|
246
|
+
- `typography`: objet de tokens typo
|
|
247
|
+
- `title`: titre de page
|
|
248
|
+
- `description`: description de page
|
|
249
|
+
|
|
250
|
+
## Notes
|
|
251
|
+
|
|
252
|
+
- Le package n’embarque pas de moteur Storybook officiel.
|
|
253
|
+
- Il ne dépend pas d’Expo Router, mais il est pensé pour s’y brancher facilement.
|
|
254
|
+
- `StoryDetailScreen` ouvre le lien Figma via `Linking.openURL`.
|
|
255
|
+
- Les vues de détail affichent les objets sous forme JSON via `JsonBlock`.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FilterBadge.d.ts","sourceRoot":"","sources":["../../src/components/FilterBadge.tsx"],"names":[],"mappings":"AAGA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,4BAA4B,gBAAgB,4CAQvE,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Badge, Box } from '@alveole/components';
|
|
3
|
+
import { Pressable } from 'react-native';
|
|
4
|
+
export const FilterBadge = ({ active, label, onPress }) => (_jsx(Pressable, { onPress: onPress, children: _jsx(Box, { children: _jsx(Badge, { variant: active ? 'new' : 'default', size: "sm", children: label }) }) }));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JsonBlock.d.ts","sourceRoot":"","sources":["../../src/components/JsonBlock.tsx"],"names":[],"mappings":"AAGA,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,WAAW,cAAc,4CAyBlD,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Typography } from '@alveole/components';
|
|
3
|
+
import { useTheme } from '@alveole/theme';
|
|
4
|
+
export const JsonBlock = ({ value }) => {
|
|
5
|
+
const { text, color, radius } = useTheme();
|
|
6
|
+
return (_jsx(Box, { backgroundColor: color.light.background['alt-grey'], borderColor: color.light.border['plain-grey'], borderRadius: radius('md'), borderWidth: 1, p: '100', width: '100%', children: _jsx(Typography, { style: [
|
|
7
|
+
text['Corps de texte'].SM.Regular,
|
|
8
|
+
{
|
|
9
|
+
fontFamily: 'monospace',
|
|
10
|
+
whiteSpace: 'pre-wrap',
|
|
11
|
+
},
|
|
12
|
+
], children: typeof value === 'string' ? value : JSON.stringify(value, null, 2) }) }));
|
|
13
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type SearchFieldProps = {
|
|
2
|
+
label: string;
|
|
3
|
+
placeholder?: string;
|
|
4
|
+
value: string;
|
|
5
|
+
onChangeText: (value: string) => void;
|
|
6
|
+
};
|
|
7
|
+
export declare const SearchField: ({ label, placeholder, value, onChangeText }: SearchFieldProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=SearchField.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SearchField.d.ts","sourceRoot":"","sources":["../../src/components/SearchField.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,6CAA6C,gBAAgB,4CAuBxF,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Typography } from '@alveole/components';
|
|
3
|
+
import { useTheme } from '@alveole/theme';
|
|
4
|
+
import { TextInput } from 'react-native';
|
|
5
|
+
export const SearchField = ({ label, placeholder, value, onChangeText }) => {
|
|
6
|
+
const { text, color, radius, spacing } = useTheme();
|
|
7
|
+
return (_jsxs(Box, { display: "flex", gap: 8, children: [_jsx(Typography, { style: text['Corps de texte'].SM.SemiBold, children: label }), _jsx(TextInput, { placeholder: placeholder, value: value, onChangeText: onChangeText, placeholderTextColor: color.light.text['mention-grey'], style: {
|
|
8
|
+
borderWidth: 1,
|
|
9
|
+
borderColor: color.light.border['plain-grey'],
|
|
10
|
+
borderRadius: radius('md'),
|
|
11
|
+
paddingVertical: spacing('075'),
|
|
12
|
+
paddingHorizontal: spacing('100'),
|
|
13
|
+
backgroundColor: color.light.background['default-grey'],
|
|
14
|
+
color: color.light.text['default-grey'],
|
|
15
|
+
} })] }));
|
|
16
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { StorybookModule } from '../types';
|
|
2
|
+
export type StoryCardProps = {
|
|
3
|
+
story: StorybookModule;
|
|
4
|
+
onPress?: (story: StorybookModule) => void;
|
|
5
|
+
};
|
|
6
|
+
export declare const StoryCard: ({ story, onPress }: StoryCardProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
//# sourceMappingURL=StoryCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StoryCard.d.ts","sourceRoot":"","sources":["../../src/components/StoryCard.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAG3C,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;CAC5C,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,oBAAoB,cAAc,4CA2B3D,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Badge, Box, Card, Typography } from '@alveole/components';
|
|
3
|
+
import { useTheme } from '@alveole/theme';
|
|
4
|
+
import { Pressable } from 'react-native';
|
|
5
|
+
import { getStoryFlags } from '../utils';
|
|
6
|
+
export const StoryCard = ({ story, onPress }) => {
|
|
7
|
+
const { text } = useTheme();
|
|
8
|
+
const meta = story.default;
|
|
9
|
+
const flags = getStoryFlags(meta);
|
|
10
|
+
return (_jsx(Pressable, { onPress: onPress ? () => onPress(story) : undefined, children: _jsx(Card, { height: '100%', children: _jsxs(Card.Section, { display: "flex", gap: 12, p: '100', children: [_jsxs(Box, { display: "flex", flexDirection: "row", flexWrap: "wrap", gap: 8, children: [meta.tags.map(tag => (_jsx(Badge, { variant: "info", size: "sm", children: tag }, tag))), flags.map(flag => (_jsx(Badge, { variant: "default", size: "sm", children: flag.label }, flag.key)))] }), _jsx(Typography, { style: text.Titres['H5 - XS'], children: meta.title }), _jsx(Typography, { style: text['Corps de texte'].SM.Regular, children: meta.description })] }) }) }));
|
|
11
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './components/JsonBlock';
|
|
2
|
+
export * from './screens/StoriesScreen';
|
|
3
|
+
export * from './screens/StoryDetailScreen';
|
|
4
|
+
export * from './screens/ThemeConstantDetailScreen';
|
|
5
|
+
export * from './screens/ThemeConstantsScreen';
|
|
6
|
+
export * from './screens/ThemePaletteScreen';
|
|
7
|
+
export * from './screens/ThemeTypographyScreen';
|
|
8
|
+
export * from './types';
|
|
9
|
+
export * from './utils';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qCAAqC,CAAC;AACpD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './components/JsonBlock';
|
|
2
|
+
export * from './screens/StoriesScreen';
|
|
3
|
+
export * from './screens/StoryDetailScreen';
|
|
4
|
+
export * from './screens/ThemeConstantDetailScreen';
|
|
5
|
+
export * from './screens/ThemeConstantsScreen';
|
|
6
|
+
export * from './screens/ThemePaletteScreen';
|
|
7
|
+
export * from './screens/ThemeTypographyScreen';
|
|
8
|
+
export * from './types';
|
|
9
|
+
export * from './utils';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { StorybookModule } from '../types';
|
|
2
|
+
export type StoriesScreenProps = {
|
|
3
|
+
stories: StorybookModule[];
|
|
4
|
+
title?: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
emptyMessage?: string;
|
|
7
|
+
createLabel?: string;
|
|
8
|
+
onCreatePress?: () => void;
|
|
9
|
+
onSelectStory?: (story: StorybookModule) => void;
|
|
10
|
+
};
|
|
11
|
+
export declare const StoriesScreen: ({ stories, title, description, emptyMessage, createLabel, onCreatePress, onSelectStory, }: StoriesScreenProps) => import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
//# sourceMappingURL=StoriesScreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StoriesScreen.d.ts","sourceRoot":"","sources":["../../src/screens/StoriesScreen.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAiB,eAAe,EAAE,MAAM,UAAU,CAAC;AAW1D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;CAClD,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,2FAQ3B,kBAAkB,4CAmGpB,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Button, Page, Section, Typography } from '@alveole/components';
|
|
3
|
+
import { useTheme } from '@alveole/theme';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { useWindowDimensions } from 'react-native';
|
|
6
|
+
import { FilterBadge } from '../components/FilterBadge';
|
|
7
|
+
import { SearchField } from '../components/SearchField';
|
|
8
|
+
import { StoryCard } from '../components/StoryCard';
|
|
9
|
+
import { filterStories, getAllStoryTags, groupStoriesByTag } from '../utils';
|
|
10
|
+
const AVAILABLE_FLAGS = [
|
|
11
|
+
{ key: 'figma', label: 'Figma' },
|
|
12
|
+
{ key: 'experimental', label: 'Experimental' },
|
|
13
|
+
{ key: 'props', label: 'Props' },
|
|
14
|
+
{ key: 'webOnly', label: 'Web only' },
|
|
15
|
+
{ key: 'mobileOnly', label: 'Mobile only' },
|
|
16
|
+
];
|
|
17
|
+
export const StoriesScreen = ({ stories, title = 'UI Kit - Components', description = 'Shared component catalog', emptyMessage = 'No story found.', createLabel, onCreatePress, onSelectStory, }) => {
|
|
18
|
+
const { text } = useTheme();
|
|
19
|
+
const { width } = useWindowDimensions();
|
|
20
|
+
const columns = width >= 1200 ? 3 : width >= 768 ? 2 : 1;
|
|
21
|
+
const [query, setQuery] = React.useState('');
|
|
22
|
+
const [selectedTag, setSelectedTag] = React.useState(null);
|
|
23
|
+
const [selectedFlag, setSelectedFlag] = React.useState(null);
|
|
24
|
+
const allTags = React.useMemo(() => getAllStoryTags(stories), [stories]);
|
|
25
|
+
const filteredStories = React.useMemo(() => filterStories({
|
|
26
|
+
stories,
|
|
27
|
+
query,
|
|
28
|
+
selectedTag,
|
|
29
|
+
selectedFlag,
|
|
30
|
+
}), [stories, query, selectedTag, selectedFlag]);
|
|
31
|
+
const groupedStories = React.useMemo(() => groupStoriesByTag(filteredStories, allTags), [filteredStories, allTags]);
|
|
32
|
+
return (_jsxs(Page, { scrollable: true, title: title, description: description, beforeContent: createLabel && onCreatePress ? (_jsx(Section, { withPaddingY: true, children: _jsx(Box, { style: { alignItems: 'flex-end' }, children: _jsx(Button, { title: createLabel, variant: "primary", onPress: onCreatePress }) }) })) : undefined, children: [_jsx(Section, { withPaddingY: true, children: _jsxs(Box, { display: "flex", gap: 16, children: [_jsx(SearchField, { label: "Search", placeholder: "Button, Tabs, Card...", value: query, onChangeText: setQuery }), _jsxs(Box, { display: "flex", gap: 8, children: [_jsx(Typography, { style: text['Corps de texte'].SM.SemiBold, children: "Tags" }), _jsxs(Box, { display: "flex", flexDirection: "row", flexWrap: "wrap", gap: 8, children: [_jsx(FilterBadge, { active: selectedTag === null, label: "All", onPress: () => setSelectedTag(null) }), allTags.map(tag => (_jsx(FilterBadge, { active: selectedTag === tag, label: tag, onPress: () => setSelectedTag(current => (current === tag ? null : tag)) }, tag)))] })] }), _jsxs(Box, { display: "flex", gap: 8, children: [_jsx(Typography, { style: text['Corps de texte'].SM.SemiBold, children: "Flags" }), _jsxs(Box, { display: "flex", flexDirection: "row", flexWrap: "wrap", gap: 8, children: [_jsx(FilterBadge, { active: selectedFlag === null, label: "All", onPress: () => setSelectedFlag(null) }), AVAILABLE_FLAGS.map(flag => (_jsx(FilterBadge, { active: selectedFlag === flag.key, label: flag.label, onPress: () => setSelectedFlag(current => (current === flag.key ? null : flag.key)) }, flag.key)))] })] }), _jsxs(Typography, { style: text['Corps de texte'].SM.Regular, children: [filteredStories.length, " result", filteredStories.length > 1 ? 's' : ''] })] }) }), groupedStories.length === 0 ? (_jsx(Section, { withPaddingY: true, children: _jsx(Typography, { style: text['Corps de texte'].MD.Regular, children: emptyMessage }) })) : (groupedStories.map(([tag, taggedStories]) => (_jsx(Section, { withPaddingY: true, children: _jsxs(Box, { display: "flex", gap: 16, children: [_jsx(Typography, { style: text.Titres['H4 - SM'], children: tag }), _jsx(Box, { display: "flex", flexDirection: "row", flexWrap: "wrap", gap: 16, children: taggedStories.map(story => (_jsx(Box, { width: columns === 1 ? '100%' : columns === 2 ? '48%' : '31%', children: _jsx(StoryCard, { story: story, onPress: onSelectStory }) }, story.default.title))) })] }) }, tag))))] }));
|
|
33
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { StorybookModule } from '../types';
|
|
2
|
+
export type StoryDetailScreenProps = {
|
|
3
|
+
story?: StorybookModule | null;
|
|
4
|
+
notFoundMessage?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const StoryDetailScreen: ({ story, notFoundMessage }: StoryDetailScreenProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
//# sourceMappingURL=StoryDetailScreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StoryDetailScreen.d.ts","sourceRoot":"","sources":["../../src/screens/StoryDetailScreen.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAG3C,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,4BAAiD,sBAAsB,4CA4FxG,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Button, Divider, Page, Section, Tabs, Typography } from '@alveole/components';
|
|
3
|
+
import { useTheme } from '@alveole/theme';
|
|
4
|
+
import { Linking } from 'react-native';
|
|
5
|
+
import { JsonBlock } from '../components/JsonBlock';
|
|
6
|
+
import { getStoryExamples, getStoryFlags } from '../utils';
|
|
7
|
+
export const StoryDetailScreen = ({ story, notFoundMessage = 'Story not found.' }) => {
|
|
8
|
+
const { text } = useTheme();
|
|
9
|
+
if (!story) {
|
|
10
|
+
return (_jsx(Page, { title: "Story not found", description: notFoundMessage, children: _jsx(Section, { withPaddingY: true, children: _jsx(Typography, { style: text['Corps de texte'].MD.Regular, children: notFoundMessage }) }) }));
|
|
11
|
+
}
|
|
12
|
+
const meta = story.default;
|
|
13
|
+
const flags = getStoryFlags(meta);
|
|
14
|
+
const examples = getStoryExamples(story);
|
|
15
|
+
const isTemplate = meta.tags.includes('Template');
|
|
16
|
+
const examplesContent = (_jsx(Box, { display: "flex", gap: 16, children: examples.map(([key, Example]) => (_jsxs(Box, { display: "flex", gap: 12, children: [!isTemplate ? _jsx(Typography, { style: text['Corps de texte'].SM.SemiBold, children: key }) : null, _jsx(Box, { borderColor: '#e8e8e8', borderWidth: 1, borderRadius: 8, p: '100', style: isTemplate ? { minHeight: 400 } : undefined, children: _jsx(Example, {}) }), _jsx(Divider, {})] }, key))) }));
|
|
17
|
+
return (_jsx(Page, { scrollable: true, title: meta.title, description: meta.description, children: _jsx(Section, { withPaddingY: true, children: _jsxs(Box, { display: "flex", gap: 16, children: [_jsxs(Box, { display: "flex", flexDirection: "row", flexWrap: "wrap", gap: 8, children: [meta.tags.map(tag => (_jsx(Typography, { style: text['Corps de texte'].XS.SemiBold, children: tag }, tag))), flags.map(flag => (_jsx(Typography, { style: text['Corps de texte'].XS.Regular, children: flag.label }, flag.key)))] }), meta.figmaURL ? (_jsx(Box, { style: { alignItems: 'flex-start' }, children: _jsx(Button, { title: "Open Figma", variant: "primary", onPress: () => Linking.openURL(meta.figmaURL) }) })) : null, _jsx(Tabs, { defaultValue: "examples", tabs: [
|
|
18
|
+
{
|
|
19
|
+
value: 'examples',
|
|
20
|
+
label: 'Examples',
|
|
21
|
+
content: examplesContent,
|
|
22
|
+
scrollable: true,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
value: 'styles',
|
|
26
|
+
label: 'Styles',
|
|
27
|
+
content: _jsx(JsonBlock, { value: meta.styleFn() }),
|
|
28
|
+
scrollable: true,
|
|
29
|
+
},
|
|
30
|
+
...(meta.props != null
|
|
31
|
+
? [
|
|
32
|
+
{
|
|
33
|
+
value: 'props',
|
|
34
|
+
label: 'Props',
|
|
35
|
+
content: _jsx(JsonBlock, { value: meta.props }),
|
|
36
|
+
scrollable: true,
|
|
37
|
+
},
|
|
38
|
+
]
|
|
39
|
+
: []),
|
|
40
|
+
] })] }) }) }));
|
|
41
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type ThemeConstantDetailScreenProps = {
|
|
2
|
+
name: string;
|
|
3
|
+
value: unknown;
|
|
4
|
+
};
|
|
5
|
+
export declare const ThemeConstantDetailScreen: ({ name, value }: ThemeConstantDetailScreenProps) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
//# sourceMappingURL=ThemeConstantDetailScreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ThemeConstantDetailScreen.d.ts","sourceRoot":"","sources":["../../src/screens/ThemeConstantDetailScreen.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,8BAA8B,GAAG;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,iBAAiB,8BAA8B,4CAsBxF,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Page, Section, Typography } from '@alveole/components';
|
|
3
|
+
import { useTheme } from '@alveole/theme';
|
|
4
|
+
import { JsonBlock } from '../components/JsonBlock';
|
|
5
|
+
export const ThemeConstantDetailScreen = ({ name, value }) => {
|
|
6
|
+
const { text } = useTheme();
|
|
7
|
+
const entries = typeof value === 'object' && value != null ? Object.entries(value) : [];
|
|
8
|
+
return (_jsx(Page, { scrollable: true, title: name, description: name, children: _jsx(Section, { withPaddingY: true, children: _jsx(Box, { display: "flex", gap: 16, children: entries.length === 0 ? (_jsx(JsonBlock, { value: value })) : (entries.map(([entryName, entryValue]) => (_jsxs(Box, { display: "flex", gap: 8, children: [_jsx(Typography, { style: text.Titres['H6 - XXS'], children: entryName }), _jsx(JsonBlock, { value: entryValue })] }, entryName)))) }) }) }));
|
|
9
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type ThemeConstantsScreenProps = {
|
|
2
|
+
constants: Record<string, unknown>;
|
|
3
|
+
title?: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
onSelectConstant?: (entry: {
|
|
6
|
+
name: string;
|
|
7
|
+
value: unknown;
|
|
8
|
+
}) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare const ThemeConstantsScreen: ({ constants, title, description, onSelectConstant, }: ThemeConstantsScreenProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=ThemeConstantsScreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ThemeConstantsScreen.d.ts","sourceRoot":"","sources":["../../src/screens/ThemeConstantsScreen.tsx"],"names":[],"mappings":"AAKA,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;CACtE,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,sDAKlC,yBAAyB,4CAgC3B,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Card, Page, Section, Typography } from '@alveole/components';
|
|
3
|
+
import { useTheme } from '@alveole/theme';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { Pressable, useWindowDimensions } from 'react-native';
|
|
6
|
+
export const ThemeConstantsScreen = ({ constants, title = 'UI Kit - Constants', description = 'Theme constants', onSelectConstant, }) => {
|
|
7
|
+
const { text } = useTheme();
|
|
8
|
+
const { width } = useWindowDimensions();
|
|
9
|
+
const columns = width >= 1200 ? 3 : width >= 768 ? 2 : 1;
|
|
10
|
+
const entries = React.useMemo(() => Object.entries(constants)
|
|
11
|
+
.filter(([, value]) => typeof value === 'object' && value != null)
|
|
12
|
+
.sort((left, right) => left[0].localeCompare(right[0])), [constants]);
|
|
13
|
+
return (_jsx(Page, { scrollable: true, title: title, description: description, children: _jsx(Section, { withPaddingY: true, children: _jsx(Box, { display: "flex", flexDirection: "row", flexWrap: "wrap", gap: 16, children: entries.map(([name, value]) => (_jsx(Box, { width: columns === 1 ? '100%' : columns === 2 ? '48%' : '31%', children: _jsx(Pressable, { onPress: onSelectConstant ? () => onSelectConstant({ name, value }) : undefined, children: _jsx(Card, { children: _jsx(Card.Section, { p: '100', children: _jsx(Typography, { style: text.Titres['H5 - XS'], children: name }) }) }) }) }, name))) }) }) }));
|
|
14
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface PaletteScale {
|
|
2
|
+
[key: string]: PaletteValue;
|
|
3
|
+
}
|
|
4
|
+
type PaletteValue = string | PaletteScale;
|
|
5
|
+
export type ThemePaletteScreenProps = {
|
|
6
|
+
palette: Record<string, PaletteValue>;
|
|
7
|
+
title?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare const ThemePaletteScreen: ({ palette, title, description, }: ThemePaletteScreenProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=ThemePaletteScreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ThemePaletteScreen.d.ts","sourceRoot":"","sources":["../../src/screens/ThemePaletteScreen.tsx"],"names":[],"mappings":"AAKA,UAAU,YAAY;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAAC;CAC7B;AAED,KAAK,YAAY,GAAG,MAAM,GAAG,YAAY,CAAC;AAmB1C,MAAM,MAAM,uBAAuB,GAAG;IACpC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,kCAIhC,uBAAuB,4CAuBzB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Card, Page, Section, Typography } from '@alveole/components';
|
|
3
|
+
import { useTheme } from '@alveole/theme';
|
|
4
|
+
import { useWindowDimensions } from 'react-native';
|
|
5
|
+
import { JsonBlock } from '../components/JsonBlock';
|
|
6
|
+
const PaletteValueView = ({ label, value }) => {
|
|
7
|
+
const { text } = useTheme();
|
|
8
|
+
return (_jsxs(Box, { display: "flex", gap: 8, children: [label ? _jsx(Typography, { style: text['Corps de texte'].XS.SemiBold, children: label }) : null, typeof value === 'string' ? _jsx(Box, { height: 48, width: 48, borderRadius: 8, backgroundColor: value }) : null, _jsx(JsonBlock, { value: value }), typeof value === 'object'
|
|
9
|
+
? Object.entries(value).map(([childLabel, childValue]) => (_jsx(PaletteValueView, { label: childLabel, value: childValue }, childLabel)))
|
|
10
|
+
: null] }));
|
|
11
|
+
};
|
|
12
|
+
export const ThemePaletteScreen = ({ palette, title = 'UI Kit - Theme colors', description = 'Theme palette', }) => {
|
|
13
|
+
const { text } = useTheme();
|
|
14
|
+
const { width } = useWindowDimensions();
|
|
15
|
+
const columns = width >= 1200 ? 3 : width >= 768 ? 2 : 1;
|
|
16
|
+
return (_jsx(Page, { scrollable: true, title: title, description: description, children: _jsx(Section, { withPaddingY: true, children: _jsx(Box, { display: "flex", flexDirection: "row", flexWrap: "wrap", gap: 16, children: Object.entries(palette).map(([key, value]) => (_jsx(Box, { width: columns === 1 ? '100%' : columns === 2 ? '48%' : '31%', children: _jsx(Card, { children: _jsxs(Card.Section, { display: "flex", gap: 16, p: '100', children: [_jsx(Typography, { style: text.Titres['H5 - XS'], children: key }), _jsx(PaletteValueView, { value: value })] }) }) }, key))) }) }) }));
|
|
17
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type ThemeTypographyScreenProps = {
|
|
2
|
+
typography: Record<string, unknown>;
|
|
3
|
+
title?: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const ThemeTypographyScreen: ({ typography, title, description, }: ThemeTypographyScreenProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
//# sourceMappingURL=ThemeTypographyScreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ThemeTypographyScreen.d.ts","sourceRoot":"","sources":["../../src/screens/ThemeTypographyScreen.tsx"],"names":[],"mappings":"AAKA,MAAM,MAAM,0BAA0B,GAAG;IACvC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,qCAInC,0BAA0B,4CAgC5B,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Card, Page, Section, Typography } from '@alveole/components';
|
|
3
|
+
import { useTheme } from '@alveole/theme';
|
|
4
|
+
import { useWindowDimensions } from 'react-native';
|
|
5
|
+
import { JsonBlock } from '../components/JsonBlock';
|
|
6
|
+
export const ThemeTypographyScreen = ({ typography, title = 'UI Kit - Theme typography', description = 'Theme text styles', }) => {
|
|
7
|
+
const { text } = useTheme();
|
|
8
|
+
const { width } = useWindowDimensions();
|
|
9
|
+
const columns = width >= 1200 ? 3 : width >= 768 ? 2 : 1;
|
|
10
|
+
return (_jsx(Page, { scrollable: true, title: title, description: description, children: _jsx(Section, { withPaddingY: true, children: _jsx(Box, { display: "flex", flexDirection: "row", flexWrap: "wrap", gap: 16, children: Object.entries(typography).map(([key, value]) => (_jsx(Box, { width: columns === 1 ? '100%' : columns === 2 ? '48%' : '31%', children: _jsx(Card, { children: _jsxs(Card.Section, { display: "flex", gap: 16, p: '100', children: [_jsx(Typography, { style: text.Titres['H5 - XS'], children: key }), typeof value === 'object' && value != null ? (Object.entries(value).map(([subKey, subValue]) => (_jsxs(Box, { display: "flex", gap: 8, children: [_jsx(Typography, { style: text['Corps de texte'].XS.SemiBold, children: subKey }), _jsx(JsonBlock, { value: subValue })] }, subKey)))) : (_jsx(JsonBlock, { value: value }))] }) }) }, key))) }) }) }));
|
|
11
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type StorybookTag = 'Kit' | 'Composant' | 'Template' | (string & {});
|
|
3
|
+
export type StorybookMeta = {
|
|
4
|
+
title: string;
|
|
5
|
+
tags: readonly StorybookTag[];
|
|
6
|
+
experimental: boolean;
|
|
7
|
+
figmaURL?: string;
|
|
8
|
+
mobileOnly?: boolean;
|
|
9
|
+
webOnly?: boolean;
|
|
10
|
+
description: string;
|
|
11
|
+
component?: React.ComponentType<any>;
|
|
12
|
+
config?: object;
|
|
13
|
+
props?: unknown;
|
|
14
|
+
styleFn: () => string | object;
|
|
15
|
+
};
|
|
16
|
+
export type StorybookExample = React.ComponentType<any>;
|
|
17
|
+
export type StorybookModule<TMeta extends StorybookMeta = StorybookMeta> = {
|
|
18
|
+
default: TMeta;
|
|
19
|
+
} & Record<string, StorybookExample | TMeta>;
|
|
20
|
+
export type StorybookFlagKey = 'figma' | 'experimental' | 'props' | 'webOnly' | 'mobileOnly';
|
|
21
|
+
export type StorybookFlag = {
|
|
22
|
+
key: StorybookFlagKey;
|
|
23
|
+
label: string;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,WAAW,GAAG,UAAU,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAE5E,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,YAAY,EAAE,CAAC;IAC9B,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,MAAM,GAAG,MAAM,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;AAExD,MAAM,MAAM,eAAe,CAAC,KAAK,SAAS,aAAa,GAAG,aAAa,IAAI;IACzE,OAAO,EAAE,KAAK,CAAC;CAChB,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,KAAK,CAAC,CAAC;AAE7C,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,cAAc,GAAG,OAAO,GAAG,SAAS,GAAG,YAAY,CAAC;AAE7F,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,gBAAgB,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { StorybookFlag, StorybookMeta, StorybookModule } from './types';
|
|
2
|
+
export declare const normalizeText: (value: string) => string;
|
|
3
|
+
export declare const getStoryFlags: (meta: StorybookMeta) => StorybookFlag[];
|
|
4
|
+
export declare const getAllStoryTags: (stories: StorybookModule[]) => string[];
|
|
5
|
+
export declare const filterStories: (params: {
|
|
6
|
+
stories: StorybookModule[];
|
|
7
|
+
query: string;
|
|
8
|
+
selectedTag: string | null;
|
|
9
|
+
selectedFlag: StorybookFlag["key"] | null;
|
|
10
|
+
}) => StorybookModule[];
|
|
11
|
+
export declare const sortStoriesByTitle: (stories: StorybookModule[]) => StorybookModule[];
|
|
12
|
+
export declare const groupStoriesByTag: (stories: StorybookModule[], tags: string[]) => (readonly [string, StorybookModule[]])[];
|
|
13
|
+
export declare const getStoryExamples: (story: StorybookModule) => [string, Exclude<StorybookModule[keyof StorybookModule], StorybookMeta>][];
|
|
14
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAUxE,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,WAIP,CAAC;AAErC,eAAO,MAAM,aAAa,GAAI,MAAM,aAAa,KAAG,aAAa,EAC+B,CAAC;AAEjG,eAAO,MAAM,eAAe,GAAI,SAAS,eAAe,EAAE,KAAG,MAAM,EAOlE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,QAAQ;IACpC,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;CAC3C,sBAoBA,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,SAAS,eAAe,EAAE,sBAC8B,CAAC;AAE5F,eAAO,MAAM,iBAAiB,GAAI,SAAS,eAAe,EAAE,EAAE,MAAM,MAAM,EAAE,6CAGd,CAAC;AAE/D,eAAO,MAAM,gBAAgB,GAAI,OAAO,eAAe,KAC+C,CAClG,MAAM,EACN,OAAO,CAAC,eAAe,CAAC,MAAM,eAAe,CAAC,EAAE,aAAa,CAAC,CAC/D,EAAE,CAAC"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const FLAG_DEFINITIONS = [
|
|
2
|
+
{ key: 'figma', label: 'Figma', isActive: (meta) => Boolean(meta.figmaURL) },
|
|
3
|
+
{ key: 'experimental', label: 'Experimental', isActive: (meta) => Boolean(meta.experimental) },
|
|
4
|
+
{ key: 'props', label: 'Props', isActive: (meta) => meta.props != null },
|
|
5
|
+
{ key: 'webOnly', label: 'Web only', isActive: (meta) => Boolean(meta.webOnly) },
|
|
6
|
+
{ key: 'mobileOnly', label: 'Mobile only', isActive: (meta) => Boolean(meta.mobileOnly) },
|
|
7
|
+
];
|
|
8
|
+
export const normalizeText = (value) => value
|
|
9
|
+
.toLowerCase()
|
|
10
|
+
.normalize('NFD')
|
|
11
|
+
.replace(/[\u0300-\u036f]/g, '');
|
|
12
|
+
export const getStoryFlags = (meta) => FLAG_DEFINITIONS.filter(flag => flag.isActive(meta)).map(({ key, label }) => ({ key, label }));
|
|
13
|
+
export const getAllStoryTags = (stories) => {
|
|
14
|
+
const tags = new Set();
|
|
15
|
+
for (const story of stories) {
|
|
16
|
+
for (const tag of story.default.tags)
|
|
17
|
+
tags.add(tag);
|
|
18
|
+
}
|
|
19
|
+
return Array.from(tags).sort((left, right) => left.localeCompare(right));
|
|
20
|
+
};
|
|
21
|
+
export const filterStories = (params) => {
|
|
22
|
+
const { stories, query, selectedTag, selectedFlag } = params;
|
|
23
|
+
const normalizedQuery = normalizeText(query.trim());
|
|
24
|
+
return stories.filter(story => {
|
|
25
|
+
const meta = story.default;
|
|
26
|
+
const matchesQuery = normalizedQuery.length === 0 ||
|
|
27
|
+
normalizeText(meta.title).includes(normalizedQuery) ||
|
|
28
|
+
normalizeText(meta.description).includes(normalizedQuery);
|
|
29
|
+
if (!matchesQuery)
|
|
30
|
+
return false;
|
|
31
|
+
if (selectedTag && !meta.tags.includes(selectedTag))
|
|
32
|
+
return false;
|
|
33
|
+
if (selectedFlag) {
|
|
34
|
+
const definition = FLAG_DEFINITIONS.find(flag => flag.key === selectedFlag);
|
|
35
|
+
if (definition && !definition.isActive(meta))
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
export const sortStoriesByTitle = (stories) => [...stories].sort((left, right) => left.default.title.localeCompare(right.default.title));
|
|
42
|
+
export const groupStoriesByTag = (stories, tags) => tags
|
|
43
|
+
.map(tag => [tag, stories.filter(story => story.default.tags.includes(tag))])
|
|
44
|
+
.filter(([, groupedStories]) => groupedStories.length > 0);
|
|
45
|
+
export const getStoryExamples = (story) => Object.entries(story).filter(([key, value]) => key !== 'default' && typeof value === 'function');
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alveole/storybook",
|
|
3
|
+
"version": "0.26.0",
|
|
4
|
+
"description": "Catalog screens and helpers for Alveole stories and theme tokens.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./package.json": "./package.json"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -p tsconfig.build.json",
|
|
21
|
+
"lint": "eslint .",
|
|
22
|
+
"typecheck": "tsc -p tsconfig.typecheck.json --noEmit"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@alveole/eslint-config": "file:../eslint-config",
|
|
26
|
+
"eslint": "^9.39.2"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"@alveole/components": "*",
|
|
30
|
+
"@alveole/theme": "*",
|
|
31
|
+
"react": "*",
|
|
32
|
+
"react-native": "*"
|
|
33
|
+
}
|
|
34
|
+
}
|