@castui/cast-ui 2.0.0 → 3.1.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 +20 -570
- package/dist/components/Button/Button.d.ts +36 -14
- package/dist/components/Button/Button.d.ts.map +1 -1
- package/dist/components/Button/Button.js +87 -66
- package/dist/components/Button/Button.js.map +1 -1
- package/dist/components/Button/index.d.ts +2 -0
- package/dist/components/Button/index.d.ts.map +1 -0
- package/dist/components/Button/index.js +2 -0
- package/dist/components/Button/index.js.map +1 -0
- package/dist/components/Icon/Icon.d.ts +22 -8
- package/dist/components/Icon/Icon.d.ts.map +1 -1
- package/dist/components/Icon/Icon.js +25 -26
- package/dist/components/Icon/Icon.js.map +1 -1
- package/dist/components/Icon/index.d.ts +2 -0
- package/dist/components/Icon/index.d.ts.map +1 -0
- package/dist/components/Icon/index.js +2 -0
- package/dist/components/Icon/index.js.map +1 -0
- package/dist/index.d.ts +4 -51
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -31
- package/dist/index.js.map +1 -1
- package/dist/theme/ThemeContext.d.ts +59 -0
- package/dist/theme/ThemeContext.d.ts.map +1 -0
- package/dist/theme/ThemeContext.js +92 -0
- package/dist/theme/ThemeContext.js.map +1 -0
- package/dist/theme/index.d.ts +3 -6
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +2 -3
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/themes.d.ts +14 -0
- package/dist/theme/themes.d.ts.map +1 -0
- package/dist/theme/themes.js +98 -0
- package/dist/theme/themes.js.map +1 -0
- package/dist/theme/types.d.ts +30 -467
- package/dist/theme/types.d.ts.map +1 -1
- package/dist/theme/types.js +3 -7
- package/dist/theme/types.js.map +1 -1
- package/dist/tokens/colors.d.ts +35 -0
- package/dist/tokens/colors.d.ts.map +1 -0
- package/dist/tokens/colors.js +74 -0
- package/dist/tokens/colors.js.map +1 -0
- package/dist/tokens/index.d.ts +3 -0
- package/dist/tokens/index.d.ts.map +1 -0
- package/dist/tokens/index.js +3 -0
- package/dist/tokens/index.js.map +1 -0
- package/dist/tokens/typography.d.ts +27 -0
- package/dist/tokens/typography.d.ts.map +1 -0
- package/dist/tokens/typography.js +30 -0
- package/dist/tokens/typography.js.map +1 -0
- package/package.json +2 -7
- package/dist/components/Alert/Alert.d.ts +0 -14
- package/dist/components/Alert/Alert.d.ts.map +0 -1
- package/dist/components/Alert/Alert.js +0 -57
- package/dist/components/Alert/Alert.js.map +0 -1
- package/dist/components/AppBar/AppBar.d.ts +0 -11
- package/dist/components/AppBar/AppBar.d.ts.map +0 -1
- package/dist/components/AppBar/AppBar.js +0 -32
- package/dist/components/AppBar/AppBar.js.map +0 -1
- package/dist/components/Autocomplete/Autocomplete.d.ts +0 -18
- package/dist/components/Autocomplete/Autocomplete.d.ts.map +0 -1
- package/dist/components/Autocomplete/Autocomplete.js +0 -72
- package/dist/components/Autocomplete/Autocomplete.js.map +0 -1
- package/dist/components/Backdrop/Backdrop.d.ts +0 -8
- package/dist/components/Backdrop/Backdrop.d.ts.map +0 -1
- package/dist/components/Backdrop/Backdrop.js +0 -25
- package/dist/components/Backdrop/Backdrop.js.map +0 -1
- package/dist/components/Badge/Badge.d.ts +0 -11
- package/dist/components/Badge/Badge.d.ts.map +0 -1
- package/dist/components/Badge/Badge.js +0 -42
- package/dist/components/Badge/Badge.js.map +0 -1
- package/dist/components/Card/Card.d.ts +0 -20
- package/dist/components/Card/Card.d.ts.map +0 -1
- package/dist/components/Card/Card.js +0 -58
- package/dist/components/Card/Card.js.map +0 -1
- package/dist/components/Checkbox/Checkbox.d.ts +0 -12
- package/dist/components/Checkbox/Checkbox.d.ts.map +0 -1
- package/dist/components/Checkbox/Checkbox.js +0 -39
- package/dist/components/Checkbox/Checkbox.js.map +0 -1
- package/dist/components/Chip/Chip.d.ts +0 -13
- package/dist/components/Chip/Chip.d.ts.map +0 -1
- package/dist/components/Chip/Chip.js +0 -29
- package/dist/components/Chip/Chip.js.map +0 -1
- package/dist/components/Dialog/Dialog.d.ts +0 -15
- package/dist/components/Dialog/Dialog.d.ts.map +0 -1
- package/dist/components/Dialog/Dialog.js +0 -41
- package/dist/components/Dialog/Dialog.js.map +0 -1
- package/dist/components/Divider/Divider.d.ts +0 -6
- package/dist/components/Divider/Divider.d.ts.map +0 -1
- package/dist/components/Divider/Divider.js +0 -25
- package/dist/components/Divider/Divider.js.map +0 -1
- package/dist/components/FAB/FAB.d.ts +0 -14
- package/dist/components/FAB/FAB.d.ts.map +0 -1
- package/dist/components/FAB/FAB.js +0 -55
- package/dist/components/FAB/FAB.js.map +0 -1
- package/dist/components/Link/Link.d.ts +0 -11
- package/dist/components/Link/Link.d.ts.map +0 -1
- package/dist/components/Link/Link.js +0 -26
- package/dist/components/Link/Link.js.map +0 -1
- package/dist/components/Select/Select.d.ts +0 -18
- package/dist/components/Select/Select.d.ts.map +0 -1
- package/dist/components/Select/Select.js +0 -72
- package/dist/components/Select/Select.js.map +0 -1
- package/dist/components/Skeleton/Skeleton.d.ts +0 -11
- package/dist/components/Skeleton/Skeleton.d.ts.map +0 -1
- package/dist/components/Skeleton/Skeleton.js +0 -27
- package/dist/components/Skeleton/Skeleton.js.map +0 -1
- package/dist/components/Snackbar/Snackbar.d.ts +0 -14
- package/dist/components/Snackbar/Snackbar.d.ts.map +0 -1
- package/dist/components/Snackbar/Snackbar.js +0 -40
- package/dist/components/Snackbar/Snackbar.js.map +0 -1
- package/dist/components/SpeedDial/SpeedDial.d.ts +0 -18
- package/dist/components/SpeedDial/SpeedDial.d.ts.map +0 -1
- package/dist/components/SpeedDial/SpeedDial.js +0 -55
- package/dist/components/SpeedDial/SpeedDial.js.map +0 -1
- package/dist/components/Switch/Switch.d.ts +0 -12
- package/dist/components/Switch/Switch.d.ts.map +0 -1
- package/dist/components/Switch/Switch.js +0 -39
- package/dist/components/Switch/Switch.js.map +0 -1
- package/dist/components/Table/Table.d.ts +0 -15
- package/dist/components/Table/Table.d.ts.map +0 -1
- package/dist/components/Table/Table.js +0 -62
- package/dist/components/Table/Table.js.map +0 -1
- package/dist/components/TextField/TextField.d.ts +0 -19
- package/dist/components/TextField/TextField.d.ts.map +0 -1
- package/dist/components/TextField/TextField.js +0 -60
- package/dist/components/TextField/TextField.js.map +0 -1
- package/dist/components/Typography/Typography.d.ts +0 -21
- package/dist/components/Typography/Typography.d.ts.map +0 -1
- package/dist/components/Typography/Typography.js +0 -41
- package/dist/components/Typography/Typography.js.map +0 -1
- package/dist/default.reference.json +0 -421
- package/dist/theme/ThemeProvider.d.ts +0 -34
- package/dist/theme/ThemeProvider.d.ts.map +0 -1
- package/dist/theme/ThemeProvider.js +0 -41
- package/dist/theme/ThemeProvider.js.map +0 -1
- package/dist/theme/createTheme.d.ts +0 -49
- package/dist/theme/createTheme.d.ts.map +0 -1
- package/dist/theme/createTheme.js +0 -85
- package/dist/theme/createTheme.js.map +0 -1
- package/dist/theme/fonts.d.ts +0 -96
- package/dist/theme/fonts.d.ts.map +0 -1
- package/dist/theme/fonts.js +0 -148
- package/dist/theme/fonts.js.map +0 -1
- package/dist/tokens/generated/default.d.ts +0 -3
- package/dist/tokens/generated/default.d.ts.map +0 -1
- package/dist/tokens/generated/default.js +0 -422
- package/dist/tokens/generated/default.js.map +0 -1
- package/dist/tokens/generated/index.d.ts +0 -2
- package/dist/tokens/generated/index.d.ts.map +0 -1
- package/dist/tokens/generated/index.js +0 -3
- package/dist/tokens/generated/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,89 +1,18 @@
|
|
|
1
1
|
# Cast UI
|
|
2
2
|
|
|
3
|
-
A cross-platform
|
|
3
|
+
A cross-platform component library for React Native (iOS, Android, Web).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> Components are currently being built. Check back soon.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Installation
|
|
8
8
|
|
|
9
|
+
```bash
|
|
10
|
+
npm install @castui/cast-ui
|
|
9
11
|
```
|
|
10
|
-
Figma Variables (JSON)
|
|
11
|
-
|
|
|
12
|
-
Token Build Script src/tokens/build.ts
|
|
13
|
-
|
|
|
14
|
-
TypeScript Theme Objects src/tokens/generated/*.ts
|
|
15
|
-
|
|
|
16
|
-
ThemeProvider React Context (src/theme/)
|
|
17
|
-
|
|
|
18
|
-
RN Components Pressable, View, Text (src/components/)
|
|
19
|
-
|
|
|
20
|
-
+-----------+-----------+
|
|
21
|
-
| | |
|
|
22
|
-
iOS Android Web (via React Native Web)
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
**Token tiers** follow a three-layer alias chain. Changing a value at any tier cascades automatically:
|
|
26
|
-
|
|
27
|
-
```
|
|
28
|
-
Primitive --> Semantic --> Component
|
|
29
|
-
|
|
30
|
-
Example:
|
|
31
|
-
Primitive.Colour.Blue-600
|
|
32
|
-
--> Semantic.Colour.Primary
|
|
33
|
-
--> Component.Button.Filled.Background
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
See [`design-tokens/DESIGN-TOKENS-SUMMARY.md`](design-tokens/DESIGN-TOKENS-SUMMARY.md) for the full token reference.
|
|
37
|
-
|
|
38
|
-
## Examples
|
|
39
|
-
|
|
40
|
-
For live examples showing Cast UI components with custom themes (Consumer, Corporate, Luxury), see the companion repo:
|
|
41
12
|
|
|
42
|
-
**
|
|
13
|
+
**Peer dependencies:** `react` (>=18), `react-native` (>=0.72)
|
|
43
14
|
|
|
44
|
-
##
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
cast-ui/
|
|
48
|
-
src/
|
|
49
|
-
components/
|
|
50
|
-
Button/
|
|
51
|
-
Button.tsx RN Pressable + Text, theme-aware
|
|
52
|
-
Button.stories.tsx Storybook stories
|
|
53
|
-
Card/
|
|
54
|
-
Card.tsx RN View + Text, theme-aware
|
|
55
|
-
Card.stories.tsx Storybook stories
|
|
56
|
-
theme/
|
|
57
|
-
types.ts CastTheme TypeScript interface
|
|
58
|
-
createTheme.ts Deep-merge utility for custom themes
|
|
59
|
-
ThemeProvider.tsx React Context provider + useTheme hook
|
|
60
|
-
fonts.ts Dynamic font family helpers
|
|
61
|
-
index.ts Barrel export
|
|
62
|
-
tokens/
|
|
63
|
-
build.ts Reads Figma JSON, generates TS theme objects
|
|
64
|
-
generated/ Auto-generated (gitignored)
|
|
65
|
-
default.ts
|
|
66
|
-
default.reference.json Copy-paste starting point for custom themes
|
|
67
|
-
index.ts
|
|
68
|
-
index.ts Public API
|
|
69
|
-
design-tokens/ Source of truth (Figma export)
|
|
70
|
-
Default.tokens.json Default theme (system-ui, slate neutrals)
|
|
71
|
-
DESIGN-TOKENS-SUMMARY.md Complete token reference
|
|
72
|
-
.storybook/
|
|
73
|
-
main.ts Webpack config with react-native-web alias
|
|
74
|
-
preview.ts Decorator wrapping stories in default theme
|
|
75
|
-
preview-head.html (empty — default theme uses system fonts)
|
|
76
|
-
.github/workflows/
|
|
77
|
-
chromatic.yml Visual regression testing on push
|
|
78
|
-
adoption.yml Zeroheight adoption tracking on push to main
|
|
79
|
-
publish.yml Publish to npm on push to main
|
|
80
|
-
dist/ Build output (gitignored)
|
|
81
|
-
default.reference.json Shipped in package for consumers
|
|
82
|
-
tsconfig.json Development TypeScript config
|
|
83
|
-
tsconfig.build.json Library build config (excludes stories)
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## Getting Started
|
|
15
|
+
## Development
|
|
87
16
|
|
|
88
17
|
### Prerequisites
|
|
89
18
|
|
|
@@ -96,515 +25,36 @@ cast-ui/
|
|
|
96
25
|
npm install
|
|
97
26
|
```
|
|
98
27
|
|
|
99
|
-
### Build Tokens
|
|
100
|
-
|
|
101
|
-
Generates the TypeScript theme object and reference JSON from the Figma token file in `design-tokens/`:
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
npm run build:tokens
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
This writes to `src/tokens/generated/` (gitignored). Runs automatically before Storybook and library builds.
|
|
108
|
-
|
|
109
28
|
### Start Storybook
|
|
110
29
|
|
|
111
30
|
```bash
|
|
112
31
|
npm run storybook
|
|
113
32
|
```
|
|
114
33
|
|
|
115
|
-
Opens at `http://localhost:6006
|
|
116
|
-
|
|
117
|
-
## Scripts
|
|
118
|
-
|
|
119
|
-
| Script | Description |
|
|
120
|
-
|--------|-------------|
|
|
121
|
-
| `npm run build:tokens` | Generate TypeScript theme files from Figma token JSON |
|
|
122
|
-
| `npm run storybook` | Start Storybook dev server (auto-runs `build:tokens`) |
|
|
123
|
-
| `npm run build-storybook` | Build static Storybook (auto-runs `build:tokens`) |
|
|
124
|
-
| `npm run build` | Full library build: tokens + TypeScript compilation + reference JSON to `dist/` |
|
|
125
|
-
| `npm publish` | Publish to npm (auto-runs `build` via `prepublishOnly`) |
|
|
126
|
-
| `npm run zh:track-package` | Register/update package info with Zeroheight (runs automatically via CI) |
|
|
127
|
-
|
|
128
|
-
## Creating a Custom Theme
|
|
129
|
-
|
|
130
|
-
Cast UI ships a **Default base theme** that uses system fonts, slate neutrals, and moderate border radii. Create your own branded theme in three steps:
|
|
131
|
-
|
|
132
|
-
### 1. Copy the reference JSON
|
|
133
|
-
|
|
134
|
-
The package includes `default.reference.json` — a complete snapshot of all theme values. Copy it into your project as a starting point:
|
|
135
|
-
|
|
136
|
-
```bash
|
|
137
|
-
cp node_modules/@castui/cast-ui/dist/default.reference.json ./my-brand-theme.json
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### 2. Modify desired values
|
|
141
|
-
|
|
142
|
-
Edit `my-brand-theme.json` to change only the values you want. You only need to include the properties you're overriding — everything else inherits from the Default theme:
|
|
143
|
-
|
|
144
|
-
```json
|
|
145
|
-
{
|
|
146
|
-
"name": "my-brand",
|
|
147
|
-
"semantic": {
|
|
148
|
-
"colour": {
|
|
149
|
-
"primary": "#553C9A",
|
|
150
|
-
"onPrimary": "#FFFFFF",
|
|
151
|
-
"primaryHover": "#6B46C1",
|
|
152
|
-
"primaryPressed": "#44337A"
|
|
153
|
-
},
|
|
154
|
-
"fontFamily": {
|
|
155
|
-
"brand": "Poppins",
|
|
156
|
-
"interface": "Poppins"
|
|
157
|
-
},
|
|
158
|
-
"borderRadius": {
|
|
159
|
-
"small": 12,
|
|
160
|
-
"medium": 16,
|
|
161
|
-
"large": 24
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### 3. Create the theme and provide it
|
|
168
|
-
|
|
169
|
-
```tsx
|
|
170
|
-
import { CastThemeProvider, createTheme, googleFontsUrl } from '@castui/cast-ui';
|
|
171
|
-
import overrides from './my-brand-theme.json';
|
|
172
|
-
|
|
173
|
-
const myTheme = createTheme(overrides);
|
|
174
|
-
|
|
175
|
-
// Load any custom fonts (web)
|
|
176
|
-
const fontUrl = googleFontsUrl(myTheme);
|
|
177
|
-
if (fontUrl) {
|
|
178
|
-
const link = document.createElement('link');
|
|
179
|
-
link.href = fontUrl;
|
|
180
|
-
link.rel = 'stylesheet';
|
|
181
|
-
document.head.appendChild(link);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export default function App() {
|
|
185
|
-
return (
|
|
186
|
-
<CastThemeProvider theme={myTheme}>
|
|
187
|
-
{/* your app */}
|
|
188
|
-
</CastThemeProvider>
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
`createTheme()` deep-merges your partial overrides with the Default theme, producing a complete `CastTheme` object. You can also pass a second argument to merge against a different base: `createTheme(overrides, otherBaseTheme)`.
|
|
194
|
-
|
|
195
|
-
## Theme System
|
|
196
|
-
|
|
197
|
-
### ThemeProvider
|
|
198
|
-
|
|
199
|
-
Wrap your app in `CastThemeProvider`. Defaults to the Default base theme:
|
|
200
|
-
|
|
201
|
-
```tsx
|
|
202
|
-
import { CastThemeProvider, defaultTheme, createTheme } from '@castui/cast-ui';
|
|
203
|
-
|
|
204
|
-
// Use the default theme as-is...
|
|
205
|
-
<CastThemeProvider theme={defaultTheme}>
|
|
206
|
-
<App />
|
|
207
|
-
</CastThemeProvider>
|
|
208
|
-
|
|
209
|
-
// ...or create a custom theme
|
|
210
|
-
const myTheme = createTheme({ name: 'my-brand', semantic: { colour: { primary: '#FF0000' } } });
|
|
211
|
-
<CastThemeProvider theme={myTheme}>
|
|
212
|
-
<App />
|
|
213
|
-
</CastThemeProvider>
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### useTheme
|
|
217
|
-
|
|
218
|
-
Access the current theme inside any component:
|
|
219
|
-
|
|
220
|
-
```tsx
|
|
221
|
-
import { useTheme } from '@castui/cast-ui';
|
|
222
|
-
|
|
223
|
-
function MyComponent() {
|
|
224
|
-
const theme = useTheme();
|
|
225
|
-
|
|
226
|
-
return (
|
|
227
|
-
<View style={{ backgroundColor: theme.semantic.colour.surface }}>
|
|
228
|
-
<Text style={{
|
|
229
|
-
color: theme.semantic.colour.onSurface,
|
|
230
|
-
fontSize: theme.semantic.fontSize.body,
|
|
231
|
-
}}>
|
|
232
|
-
Themed text
|
|
233
|
-
</Text>
|
|
234
|
-
</View>
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
### Theme Object Shape
|
|
240
|
-
|
|
241
|
-
Every theme conforms to the `CastTheme` interface (see `src/theme/types.ts`):
|
|
242
|
-
|
|
243
|
-
```
|
|
244
|
-
theme.name string (e.g. 'default', 'my-brand')
|
|
245
|
-
theme.semantic.colour.* 30 semantic colours (surface, primary, error, etc.)
|
|
246
|
-
theme.semantic.fontFamily.* brand, interface, data
|
|
247
|
-
theme.semantic.fontSize.* display, h1, h2, h3, body, small, button
|
|
248
|
-
theme.semantic.fontWeight.* heading, body, button
|
|
249
|
-
theme.semantic.lineHeight.* display, h1, h2, h3, body, small, button (px)
|
|
250
|
-
theme.semantic.letterSpacing.* heading, body, label
|
|
251
|
-
theme.semantic.paragraphSpacing.* body, editorial
|
|
252
|
-
theme.semantic.paragraphIndent.* editorial
|
|
253
|
-
theme.semantic.borderRadius.* small, medium, large
|
|
254
|
-
theme.component.button.* All button tokens (padding, colours, variants, states)
|
|
255
|
-
theme.component.card.* All card tokens (padding, colours, typography, elevation)
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
## Components
|
|
259
|
-
|
|
260
|
-
### Button
|
|
261
|
-
|
|
262
|
-
React Native `Pressable` + `Text`. Consumes all tokens from `theme.component.button`.
|
|
263
|
-
|
|
264
|
-
```tsx
|
|
265
|
-
import { Button } from '@castui/cast-ui';
|
|
266
|
-
|
|
267
|
-
<Button label="Get started" variant="filled" />
|
|
268
|
-
<Button label="Learn more" variant="outline" />
|
|
269
|
-
<Button label="Cancel" variant="text" disabled />
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
| Prop | Type | Default | Description |
|
|
273
|
-
|------|------|---------|-------------|
|
|
274
|
-
| `label` | `string` | required | Button text |
|
|
275
|
-
| `variant` | `'filled' \| 'outline' \| 'text'` | `'filled'` | Visual variant |
|
|
276
|
-
| `disabled` | `boolean` | `false` | Disabled state |
|
|
277
|
-
| `backgroundColor` | `string` | - | Override background colour |
|
|
278
|
-
|
|
279
|
-
### Card
|
|
280
|
-
|
|
281
|
-
React Native `View` + `Text`. Consumes tokens from `theme.component.card` and `theme.semantic`.
|
|
282
|
-
|
|
283
|
-
```tsx
|
|
284
|
-
import { Card, Button } from '@castui/cast-ui';
|
|
285
|
-
|
|
286
|
-
<Card
|
|
287
|
-
title="Title"
|
|
288
|
-
subtitle="Subtitle"
|
|
289
|
-
body="Body"
|
|
290
|
-
actions={
|
|
291
|
-
<>
|
|
292
|
-
<Button label="Action 1" variant="filled" />
|
|
293
|
-
<Button label="Action 2" variant="outline" />
|
|
294
|
-
</>
|
|
295
|
-
}
|
|
296
|
-
/>
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
| Prop | Type | Default | Description |
|
|
300
|
-
|------|------|---------|-------------|
|
|
301
|
-
| `title` | `string` | required | Card heading text |
|
|
302
|
-
| `subtitle` | `string` | - | Optional subtitle below the title |
|
|
303
|
-
| `body` | `string` | - | Optional body text |
|
|
304
|
-
| `actions` | `React.ReactNode` | - | Optional actions row (e.g. Button components) |
|
|
305
|
-
|
|
306
|
-
## Font Handling
|
|
307
|
-
|
|
308
|
-
Components reference font families from the theme tokens. Font **loading** is the consumer's responsibility.
|
|
309
|
-
|
|
310
|
-
### Web
|
|
311
|
-
|
|
312
|
-
Use `googleFontsUrl()` to generate a Google Fonts `<link>` href for any theme:
|
|
313
|
-
|
|
314
|
-
```ts
|
|
315
|
-
import { googleFontsUrl } from '@castui/cast-ui';
|
|
316
|
-
|
|
317
|
-
const url = googleFontsUrl(myTheme);
|
|
318
|
-
// Returns null for themes using only system-ui, or a Google Fonts URL for custom fonts
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
### Discovering theme fonts
|
|
322
|
-
|
|
323
|
-
Use `getThemeFontFamilies()` to dynamically extract all custom font families from any theme:
|
|
324
|
-
|
|
325
|
-
```ts
|
|
326
|
-
import { getThemeFontFamilies } from '@castui/cast-ui';
|
|
327
|
-
|
|
328
|
-
const families = getThemeFontFamilies(myTheme);
|
|
329
|
-
// → ['Poppins'] or ['Inter', 'Merriweather'] etc.
|
|
330
|
-
// → [] for the Default theme (system-ui only)
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
### React Native / Expo
|
|
334
|
-
|
|
335
|
-
Load fonts with `expo-font` before rendering. On **Android**, each weight must
|
|
336
|
-
be registered under a distinct name (the Expo Google Fonts convention) because
|
|
337
|
-
Android cannot combine a generic `fontFamily` with a numeric `fontWeight` for
|
|
338
|
-
custom fonts.
|
|
339
|
-
|
|
340
|
-
```ts
|
|
341
|
-
import { useFonts } from 'expo-font';
|
|
342
|
-
import {
|
|
343
|
-
Poppins_400Regular,
|
|
344
|
-
Poppins_500Medium,
|
|
345
|
-
Poppins_700Bold,
|
|
346
|
-
} from '@expo-google-fonts/poppins';
|
|
347
|
-
|
|
348
|
-
const [loaded] = useFonts({
|
|
349
|
-
Poppins: Poppins_400Regular, // weight 400 → bare name
|
|
350
|
-
Poppins_500Medium: Poppins_500Medium, // weight 500
|
|
351
|
-
Poppins_700Bold: Poppins_700Bold, // weight 700
|
|
352
|
-
});
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
The naming convention is:
|
|
356
|
-
|
|
357
|
-
| Weight | Registration Key |
|
|
358
|
-
|--------|-----------------|
|
|
359
|
-
| 400 | `"FontName"` |
|
|
360
|
-
| 500 | `"FontName_500Medium"` |
|
|
361
|
-
| 700 | `"FontName_700Bold"` |
|
|
362
|
-
|
|
363
|
-
Components use `resolveFont()` internally so the correct font name is selected
|
|
364
|
-
automatically on each platform:
|
|
365
|
-
|
|
366
|
-
- **iOS / Web** — `{ fontFamily, fontWeight }` passed through unchanged.
|
|
367
|
-
- **Android** — maps to the weight-specific registered name (e.g. `"Poppins_700Bold"`) and sets `fontWeight: 'normal'`.
|
|
368
|
-
- **system-ui** — omits `fontFamily` (platform default) on all platforms.
|
|
34
|
+
Opens at `http://localhost:6006`.
|
|
369
35
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
> **Custom Themes:** The convention works for any font — register each weight
|
|
373
|
-
> under `FontName`, `FontName_500Medium`, and `FontName_700Bold` and
|
|
374
|
-
> `resolveFont()` will handle the rest. The `ANDROID_WEIGHT_SUFFIX` map is
|
|
375
|
-
> exported if you need to build registration keys programmatically.
|
|
376
|
-
|
|
377
|
-
## Consumer Installation
|
|
36
|
+
### Build
|
|
378
37
|
|
|
379
38
|
```bash
|
|
380
|
-
npm
|
|
39
|
+
npm run build
|
|
381
40
|
```
|
|
382
41
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
```tsx
|
|
386
|
-
import {
|
|
387
|
-
CastThemeProvider,
|
|
388
|
-
useTheme,
|
|
389
|
-
Button,
|
|
390
|
-
Card,
|
|
391
|
-
defaultTheme,
|
|
392
|
-
createTheme,
|
|
393
|
-
googleFontsUrl,
|
|
394
|
-
getThemeFontFamilies,
|
|
395
|
-
resolveFont,
|
|
396
|
-
ANDROID_WEIGHT_SUFFIX,
|
|
397
|
-
} from '@castui/cast-ui';
|
|
398
|
-
|
|
399
|
-
import type { CastTheme, DeepPartial } from '@castui/cast-ui';
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
## Storybook
|
|
403
|
-
|
|
404
|
-
Storybook runs React Native components in the browser via React Native Web. The webpack config in `.storybook/main.ts` aliases `react-native` to `react-native-web`.
|
|
405
|
-
|
|
406
|
-
The built-in Storybook shows components in the **Default theme**. For a cross-platform demo with custom themes, see the [Expo Snack](https://github.com/Connagh/cast-ui-examples/tree/main/expo-snack) in the examples repo.
|
|
407
|
-
|
|
408
|
-
### Chromatic
|
|
409
|
-
|
|
410
|
-
Visual regression testing runs on every push via the GitHub Actions workflow at `.github/workflows/chromatic.yml`. Requires a `CHROMATIC_PROJECT_TOKEN` repository secret.
|
|
411
|
-
|
|
412
|
-
## Token Build Pipeline
|
|
413
|
-
|
|
414
|
-
The build script (`src/tokens/build.ts`) reads the Figma-exported JSON from `design-tokens/` and generates a fully-resolved TypeScript theme object plus a reference JSON.
|
|
415
|
-
|
|
416
|
-
**What it does:**
|
|
417
|
-
|
|
418
|
-
1. Reads `Default.tokens.json`
|
|
419
|
-
2. Recursively resolves all `{Primitive.Colour.Slate-900}` style alias references through the three-tier chain
|
|
420
|
-
3. Extracts hex values from Figma's colour object format (`{ colorSpace, components, hex }`)
|
|
421
|
-
4. Outputs `default.ts` (typed theme object) and `default.reference.json` (copy-paste starting point) to `src/tokens/generated/`
|
|
422
|
-
|
|
423
|
-
**When to re-run:** After updating `design-tokens/Default.tokens.json`. The script runs automatically before Storybook and library builds.
|
|
424
|
-
|
|
425
|
-
**Creating a custom theme:** Copy `default.reference.json` from the published package, modify the values you want, and pass the overrides to `createTheme()`. See [Creating a Custom Theme](#creating-a-custom-theme) above.
|
|
426
|
-
|
|
427
|
-
## Adding a New Component
|
|
428
|
-
|
|
429
|
-
1. Create `src/components/ComponentName/ComponentName.tsx`
|
|
430
|
-
2. Import `useTheme` and consume tokens from the theme object
|
|
431
|
-
3. Use only React Native primitives (`View`, `Text`, `Pressable`, `ScrollView`, etc.)
|
|
432
|
-
4. Create `ComponentName.stories.tsx` alongside the component
|
|
433
|
-
5. Export the component and its types from `src/index.ts`
|
|
434
|
-
6. If the component needs new design tokens, add them to `Default.tokens.json` and update `src/theme/types.ts`
|
|
435
|
-
|
|
436
|
-
## Adoption Tracking
|
|
437
|
-
|
|
438
|
-
Design system adoption is measured via [Zeroheight](https://zeroheight.com/). There are two sides to the setup: **this design system repo** and the **consumer app repos** that install it.
|
|
439
|
-
|
|
440
|
-
### This repo (design system)
|
|
441
|
-
|
|
442
|
-
The only command relevant here is `track-package`. It registers `@castui/cast-ui` and its current version with Zeroheight so the dashboard knows what the latest release is.
|
|
443
|
-
|
|
444
|
-
This runs automatically on every push to `main` via the GitHub Actions workflow at `.github/workflows/adoption.yml`. You can also run it locally:
|
|
445
|
-
|
|
446
|
-
```bash
|
|
447
|
-
npm run zh:track-package
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
### Consumer app repos
|
|
451
|
-
|
|
452
|
-
The adoption CLI is designed to be run **in the repositories that consume this design system**. These are the commands consumers (or their CI pipelines) should run:
|
|
453
|
-
|
|
454
|
-
| Command | What it measures |
|
|
455
|
-
|---------|-----------------|
|
|
456
|
-
| `npx @zeroheight/adoption-cli analyze --component-usage` | Which Cast UI components are imported and how their props are used |
|
|
457
|
-
| `npx @zeroheight/adoption-cli analyze --color-usage` | Hardcoded color values (`#hex`, `rgb()`, etc.) that should be replaced with theme tokens |
|
|
458
|
-
| `npx @zeroheight/adoption-cli monitor-repo` | Which version of `@castui/cast-ui` the consumer app is on |
|
|
459
|
-
|
|
460
|
-
**How color tracking works:** The `--color-usage` flag scans for *non-token* color values — raw hex, rgb, or hsl strings that a developer hardcoded instead of using `theme.semantic.colour.*`. A high count means developers are bypassing the design system; a count trending to zero means strong token adoption. Running this in the design system repo itself correctly returns zero results because components only reference theme tokens, never hardcoded colours.
|
|
461
|
-
|
|
462
|
-
### Consumer CI example
|
|
463
|
-
|
|
464
|
-
Consumer teams can automate adoption reporting by adding a workflow to their repos:
|
|
465
|
-
|
|
466
|
-
```yaml
|
|
467
|
-
# .github/workflows/design-system-adoption.yml
|
|
468
|
-
name: "Design System Adoption"
|
|
469
|
-
|
|
470
|
-
on:
|
|
471
|
-
push:
|
|
472
|
-
branches: [main]
|
|
473
|
-
|
|
474
|
-
jobs:
|
|
475
|
-
adoption:
|
|
476
|
-
runs-on: ubuntu-latest
|
|
477
|
-
steps:
|
|
478
|
-
- uses: actions/checkout@v5
|
|
479
|
-
- uses: actions/setup-node@v6
|
|
480
|
-
with:
|
|
481
|
-
node-version: 20
|
|
482
|
-
- run: npm ci
|
|
483
|
-
- name: Report adoption to Zeroheight
|
|
484
|
-
env:
|
|
485
|
-
ZEROHEIGHT_CLIENT_ID: ${{ secrets.ZEROHEIGHT_CLIENT_ID }}
|
|
486
|
-
ZEROHEIGHT_ACCESS_TOKEN: ${{ secrets.ZEROHEIGHT_ACCESS_TOKEN }}
|
|
487
|
-
run: |
|
|
488
|
-
npx @zeroheight/adoption-cli analyze --component-usage --interactive false -r "${{ github.repository }}"
|
|
489
|
-
npx @zeroheight/adoption-cli analyze --color-usage --interactive false -r "${{ github.repository }}"
|
|
490
|
-
npx @zeroheight/adoption-cli monitor-repo --interactive false
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
### Required secrets
|
|
494
|
-
|
|
495
|
-
All Zeroheight CLI commands in CI require two repository secrets:
|
|
42
|
+
## Scripts
|
|
496
43
|
|
|
497
|
-
|
|
|
44
|
+
| Script | Description |
|
|
498
45
|
|--------|-------------|
|
|
499
|
-
| `
|
|
500
|
-
| `
|
|
501
|
-
|
|
502
|
-
Generate these at **Zeroheight > Account Settings > Access Tokens**.
|
|
503
|
-
|
|
504
|
-
## npm Publishing
|
|
505
|
-
|
|
506
|
-
The publish workflow (`.github/workflows/publish.yml`) runs on every push to `main`. It **automatically checks** whether the `version` in `package.json` has changed compared to what's already on npm. If the version is the same, the workflow skips silently — no build, no publish. If the version is new, it builds and publishes.
|
|
507
|
-
|
|
508
|
-
This means merging a PR to `main` does **not** automatically publish to npm. Only PRs that include a version bump in `package.json` will trigger a release.
|
|
509
|
-
|
|
510
|
-
**To publish a new version:**
|
|
511
|
-
|
|
512
|
-
1. Create a feature branch and make your changes
|
|
513
|
-
2. Update the `version` field in `package.json` (e.g. `0.3.0` → `0.4.0`)
|
|
514
|
-
3. Push the branch and open a PR to `main`
|
|
515
|
-
4. Chromatic runs and creates status checks on the PR
|
|
516
|
-
5. Review and accept any visual changes in the Chromatic UI
|
|
517
|
-
6. Once Chromatic checks pass, merge the PR
|
|
518
|
-
7. The publish workflow detects the new version, builds tokens + TypeScript, and publishes to npm
|
|
519
|
-
|
|
520
|
-
**If you don't bump the version:** The PR merges normally, Chromatic still runs, but the publish step is skipped. Your code is on `main` but no new npm version is created. This is useful for documentation changes, CI updates, or batching multiple changes before a release.
|
|
521
|
-
|
|
522
|
-
**If Chromatic changes are rejected:** Fix the code on your feature branch, push again. Chromatic re-runs with the new changes. Repeat until the visuals are right, then accept and merge.
|
|
523
|
-
|
|
524
|
-
**Version numbering guide:**
|
|
525
|
-
|
|
526
|
-
| Change type | Bump | Example |
|
|
527
|
-
|-------------|------|---------|
|
|
528
|
-
| Bug fix or minor tweak | Patch | `0.3.0` → `0.3.1` |
|
|
529
|
-
| New component or feature | Minor | `0.3.0` → `0.4.0` |
|
|
530
|
-
| Breaking API change | Major | `0.9.0` → `1.0.0` |
|
|
531
|
-
|
|
532
|
-
**Package details:**
|
|
533
|
-
|
|
534
|
-
- **Entry point:** `dist/index.js` (CJS) with `dist/index.d.ts` type declarations
|
|
535
|
-
- **Included files:** `dist/`, `README.md`, `LICENSE`
|
|
536
|
-
- **Peer dependencies:** `react` (>=18), `react-native` (>=0.72)
|
|
537
|
-
- **License:** MIT
|
|
538
|
-
|
|
539
|
-
You can still publish manually if needed:
|
|
540
|
-
|
|
541
|
-
```bash
|
|
542
|
-
npm login
|
|
543
|
-
npm publish
|
|
544
|
-
```
|
|
545
|
-
|
|
546
|
-
The `prepublishOnly` hook runs the full build automatically.
|
|
547
|
-
|
|
548
|
-
## Security
|
|
549
|
-
|
|
550
|
-
This repository uses a **whitelist-based `.gitignore`**. Everything is ignored by default (`*`), and only explicitly allowed paths are tracked. This prevents accidental exposure of secrets, logs, or environment files.
|
|
551
|
-
|
|
552
|
-
**Never tracked:** `.env`, `.env.*`, `*.log`, `*.local`, `.DS_Store`, `dist/`, `src/tokens/generated/`, `storybook-static/`.
|
|
553
|
-
|
|
554
|
-
When adding new top-level files or directories, you must add a `!path` entry to `.gitignore` for them to be tracked by git.
|
|
46
|
+
| `npm run storybook` | Start Storybook dev server |
|
|
47
|
+
| `npm run build-storybook` | Build static Storybook |
|
|
48
|
+
| `npm run build` | TypeScript compilation to `dist/` |
|
|
555
49
|
|
|
556
50
|
## CI/CD
|
|
557
51
|
|
|
558
52
|
| Workflow | Trigger | Purpose |
|
|
559
53
|
|----------|---------|---------|
|
|
560
|
-
| Chromatic
|
|
561
|
-
| Adoption Tracking
|
|
562
|
-
| Publish to npm
|
|
563
|
-
|
|
564
|
-
**Required secrets:**
|
|
565
|
-
|
|
566
|
-
| Secret | Used by |
|
|
567
|
-
|--------|---------|
|
|
568
|
-
| `CHROMATIC_PROJECT_TOKEN` | Chromatic workflow |
|
|
569
|
-
| `ZEROHEIGHT_CLIENT_ID` | Adoption Tracking workflow |
|
|
570
|
-
| `ZEROHEIGHT_ACCESS_TOKEN` | Adoption Tracking workflow |
|
|
571
|
-
|
|
572
|
-
The Publish workflow authenticates to npm via **OpenID Connect (OIDC)** using npm's Trusted Publishers feature — no `NPM_TOKEN` secret is needed. The trust relationship is configured at [npmjs.com](https://www.npmjs.com/) under **Package Settings → Trusted Publishers**. The `--provenance` flag attaches a verified build attestation to each published version.
|
|
573
|
-
|
|
574
|
-
### Branch Protection
|
|
575
|
-
|
|
576
|
-
Branch protection on `main` ensures that **no code is merged without passing Chromatic visual checks**. This is what prevents unreviewed visual changes from being published to npm.
|
|
577
|
-
|
|
578
|
-
**To set up (one-time, on GitHub):**
|
|
579
|
-
|
|
580
|
-
1. Go to **Settings → Branches** in your repository
|
|
581
|
-
2. Click **Add branch protection rule**
|
|
582
|
-
3. Set **Branch name pattern** to `main`
|
|
583
|
-
4. Enable **Require a pull request before merging**
|
|
584
|
-
5. Enable **Require status checks to pass before merging**
|
|
585
|
-
6. Search for and add **Run Chromatic** (GitHub Actions) and **UI Tests** (any source) as required checks
|
|
586
|
-
7. Click **Save changes**
|
|
587
|
-
|
|
588
|
-
Once enabled, you can no longer push directly to `main`. All changes go through feature branches and PRs, with Chromatic as a mandatory gate.
|
|
589
|
-
|
|
590
|
-
## Dependencies
|
|
591
|
-
|
|
592
|
-
### Runtime (peer)
|
|
593
|
-
|
|
594
|
-
| Package | Version | Purpose |
|
|
595
|
-
|---------|---------|---------|
|
|
596
|
-
| `react` | >=18 | React core |
|
|
597
|
-
| `react-native` | >=0.72 | Cross-platform UI primitives |
|
|
598
|
-
|
|
599
|
-
### Development only
|
|
54
|
+
| Chromatic | Every push | Visual regression testing via Storybook snapshots |
|
|
55
|
+
| Adoption Tracking | Push to `main` | Registers package version with Zeroheight |
|
|
56
|
+
| Publish to npm | Push to `main` | Builds and publishes package to npm (only if version changed) |
|
|
600
57
|
|
|
601
|
-
|
|
602
|
-
|---------|---------|
|
|
603
|
-
| `react-native-web` | Renders RN components in browser for Storybook |
|
|
604
|
-
| `@storybook/react-webpack5` | Storybook framework |
|
|
605
|
-
| `chromatic` | Visual regression testing |
|
|
606
|
-
| `style-dictionary` | Installed for future token transforms (current build uses custom script) |
|
|
607
|
-
| `ts-node` | Runs the TypeScript token build script |
|
|
608
|
-
| `typescript` | Type checking and compilation |
|
|
58
|
+
## License
|
|
609
59
|
|
|
610
|
-
|
|
60
|
+
MIT
|
|
@@ -1,18 +1,40 @@
|
|
|
1
|
-
import { type PressableProps } from 'react-native';
|
|
2
|
-
export type ButtonVariant = 'filled' | 'outline' | 'text';
|
|
3
|
-
export interface ButtonProps extends Omit<PressableProps, 'style'> {
|
|
4
|
-
/** Button label text. */
|
|
5
|
-
label: string;
|
|
6
|
-
/** Visual variant. @default 'filled' */
|
|
7
|
-
variant?: ButtonVariant;
|
|
8
|
-
/** Override background color. */
|
|
9
|
-
backgroundColor?: string;
|
|
10
|
-
}
|
|
11
1
|
/**
|
|
12
|
-
*
|
|
2
|
+
* Button — the foundational interactive component of Cast UI.
|
|
13
3
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
4
|
+
* Maps 1:1 to the Figma <Button> component:
|
|
5
|
+
* intent → neutral | brand | danger
|
|
6
|
+
* prominence → default | bold | subtle
|
|
7
|
+
* size → small | default | large
|
|
8
|
+
*
|
|
9
|
+
* Spacing tokens come from the density theme (compact/default/comfortable).
|
|
10
|
+
* Colours come from the semantic intent system (constant across densities).
|
|
11
|
+
* Typography uses the label scale (sm/md/lg) matched to the button size.
|
|
16
12
|
*/
|
|
17
|
-
|
|
13
|
+
import React from 'react';
|
|
14
|
+
import { type ViewStyle, type StyleProp, type GestureResponderEvent } from 'react-native';
|
|
15
|
+
import type { IntentName, ProminenceName } from '../../tokens';
|
|
16
|
+
export type ButtonSize = 'small' | 'default' | 'large';
|
|
17
|
+
export type ButtonProps = {
|
|
18
|
+
/** The button label text. */
|
|
19
|
+
children: string;
|
|
20
|
+
/** Semantic intent — drives the colour scheme. */
|
|
21
|
+
intent?: IntentName;
|
|
22
|
+
/** Visual weight — default (outlined), bold (filled), or subtle (ghost). */
|
|
23
|
+
prominence?: ProminenceName;
|
|
24
|
+
/** Size variant — controls padding, gap, and typography scale. */
|
|
25
|
+
size?: ButtonSize;
|
|
26
|
+
/** Disables interaction and applies muted styling. */
|
|
27
|
+
disabled?: boolean;
|
|
28
|
+
/** Icon before the label — Material Symbols name string or a ReactNode. */
|
|
29
|
+
leadingIcon?: string | React.ReactNode;
|
|
30
|
+
/** Icon after the label — Material Symbols name string or a ReactNode. */
|
|
31
|
+
trailingIcon?: string | React.ReactNode;
|
|
32
|
+
/** Press handler. */
|
|
33
|
+
onPress?: (e: GestureResponderEvent) => void;
|
|
34
|
+
/** Outer style — use for positioning (margin, flex, alignSelf). */
|
|
35
|
+
style?: StyleProp<ViewStyle>;
|
|
36
|
+
/** Accessibility label — falls back to children text if not provided. */
|
|
37
|
+
accessibilityLabel?: string;
|
|
38
|
+
};
|
|
39
|
+
export declare function Button({ children, intent, prominence, size, disabled, leadingIcon, trailingIcon, onPress, style, accessibilityLabel, }: ButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
18
40
|
//# sourceMappingURL=Button.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../../src/components/Button/Button.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../../src/components/Button/Button.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAgC,MAAM,OAAO,CAAC;AACrD,OAAO,EAKL,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,qBAAqB,EAC3B,MAAM,cAAc,CAAC;AAGtB,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAO/D,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAEvD,MAAM,MAAM,WAAW,GAAG;IACxB,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,kEAAkE;IAClE,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IACvC,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IACxC,qBAAqB;IACrB,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC7C,mEAAmE;IACnE,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,yEAAyE;IACzE,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAoBF,wBAAgB,MAAM,CAAC,EACrB,QAAQ,EACR,MAAkB,EAClB,UAAsB,EACtB,IAAgB,EAChB,QAAgB,EAChB,WAAW,EACX,YAAY,EACZ,OAAO,EACP,KAAK,EACL,kBAAkB,GACnB,EAAE,WAAW,2CAwGb"}
|