@colixsystems/widget-sdk 0.162.0 → 0.164.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 +34 -1
- package/dist/button-style.js +60 -0
- package/dist/button-view.js +197 -0
- package/dist/button.js +10 -0
- package/dist/button.native.js +10 -0
- package/dist/card-style.js +80 -0
- package/dist/card-view.js +174 -0
- package/dist/card.js +8 -0
- package/dist/card.native.js +8 -0
- package/dist/contract.cjs +72 -1
- package/dist/contract.js +72 -1
- package/dist/icon.js +7 -3
- package/dist/index.d.ts +112 -0
- package/dist/index.js +30 -0
- package/dist/index.native.js +30 -0
- package/dist/tabs-tokens.js +75 -0
- package/dist/tabs-view.js +169 -0
- package/dist/tabs.js +7 -0
- package/dist/tabs.native.js +7 -0
- package/dist/text-field-tokens.js +54 -0
- package/dist/text-field-view.js +79 -0
- package/dist/text-field.js +7 -0
- package/dist/text-field.native.js +7 -0
- package/dist/theme-components.cjs +5 -0
- package/dist/theme-components.js +5 -0
- package/package.json +1 -1
package/dist/contract.cjs
CHANGED
|
@@ -228,6 +228,9 @@ function isThemeColorTokenRef(value) {
|
|
|
228
228
|
// Single source for four consumers — the build runner's set_theme coercion, the
|
|
229
229
|
// shared host resolver, the planner prompt, and the SDK docs — so the vocabulary
|
|
230
230
|
// cannot drift between what Mason may emit and what a host actually applies.
|
|
231
|
+
// The looks a `tabs.style` token may name; `<Tabs>` draws each.
|
|
232
|
+
const THEME_TAB_STYLES = Object.freeze(["segmented", "pill", "underline"]);
|
|
233
|
+
|
|
231
234
|
const THEME_COMPONENT_SHADOWS = Object.freeze(["none", "sm", "md", "lg"]);
|
|
232
235
|
|
|
233
236
|
// sc-3857 — the `textTransform` token's closed enum, matching the per-instance
|
|
@@ -413,6 +416,7 @@ const MUTED_INPUT_FIELDS = Object.freeze({
|
|
|
413
416
|
textColor: "inputTextColor",
|
|
414
417
|
borderColor: "inputBorderColor",
|
|
415
418
|
radius: "inputRadius",
|
|
419
|
+
height: "inputHeight",
|
|
416
420
|
});
|
|
417
421
|
|
|
418
422
|
// The Form widgets' choice-field option list -- the closest thing this
|
|
@@ -445,6 +449,16 @@ const LIST_ROW_FIELDS = Object.freeze({
|
|
|
445
449
|
dividerWidth: "listDividerWidth",
|
|
446
450
|
});
|
|
447
451
|
|
|
452
|
+
// The tab-strip vocabulary. Bound by name as well as onto User Management,
|
|
453
|
+
// whose Users / Groups / Invites strip is drawn by the SDK <Tabs>.
|
|
454
|
+
const TABS_FIELDS = Object.freeze({
|
|
455
|
+
style: "tabsStyle",
|
|
456
|
+
activeColor: "tabsActiveColor",
|
|
457
|
+
activeTextColor: "tabsActiveTextColor",
|
|
458
|
+
textColor: "tabsTextColor",
|
|
459
|
+
radius: "tabsRadius",
|
|
460
|
+
});
|
|
461
|
+
|
|
448
462
|
const THEME_COMPONENTS = Object.freeze({
|
|
449
463
|
button: Object.freeze({
|
|
450
464
|
label: "Buttons",
|
|
@@ -563,6 +577,8 @@ const THEME_COMPONENTS = Object.freeze({
|
|
|
563
577
|
textColor: Object.freeze({ type: "color", uiDefault: "colors.onSurfaceMuted" }),
|
|
564
578
|
borderColor: Object.freeze({ type: "color", uiDefault: "colors.border" }),
|
|
565
579
|
radius: Object.freeze({ type: "size", min: 0, max: 48, uiDefault: "radii.sm" }),
|
|
580
|
+
// The field's minimum height; `<TextField>` reads it directly too.
|
|
581
|
+
height: Object.freeze({ type: "size", min: 32, max: 72 }),
|
|
566
582
|
}),
|
|
567
583
|
targets: Object.freeze({
|
|
568
584
|
"appstudio.form-input": MUTED_INPUT_FIELDS,
|
|
@@ -614,6 +630,22 @@ const THEME_COMPONENTS = Object.freeze({
|
|
|
614
630
|
"appstudio.data-list": LIST_ROW_FIELDS,
|
|
615
631
|
}),
|
|
616
632
|
}),
|
|
633
|
+
// Tab strips: `<Tabs>` and the page Tabs container read this scope directly;
|
|
634
|
+
// the fields bind it onto widgets. `style` is the first `enum` token.
|
|
635
|
+
tabs: Object.freeze({
|
|
636
|
+
label: "Tabs",
|
|
637
|
+
universalFields: TABS_FIELDS,
|
|
638
|
+
tokens: Object.freeze({
|
|
639
|
+
style: Object.freeze({ type: "enum", values: THEME_TAB_STYLES }),
|
|
640
|
+
activeColor: Object.freeze({ type: "color", uiDefault: "colors.primary" }),
|
|
641
|
+
activeTextColor: Object.freeze({ type: "color" }),
|
|
642
|
+
textColor: Object.freeze({ type: "color", uiDefault: "colors.onSurfaceMuted" }),
|
|
643
|
+
radius: Object.freeze({ type: "size", min: 0, max: 24 }),
|
|
644
|
+
}),
|
|
645
|
+
targets: Object.freeze({
|
|
646
|
+
"appstudio.user-management": TABS_FIELDS,
|
|
647
|
+
}),
|
|
648
|
+
}),
|
|
617
649
|
});
|
|
618
650
|
|
|
619
651
|
// sc-4505 — the value types a widget event payload field may declare. Narrower
|
|
@@ -2224,6 +2256,34 @@ const PRIMITIVES = [
|
|
|
2224
2256
|
rnComponent: null,
|
|
2225
2257
|
docsUrl: null,
|
|
2226
2258
|
},
|
|
2259
|
+
{
|
|
2260
|
+
name: "Tabs",
|
|
2261
|
+
description:
|
|
2262
|
+
'Themed tab strip. `<Tabs items={[{ key: "open", label: "Open" }, { key: "done", label: "Done", icon: "check" }]} value={tab} onChange={setTab} />`. THE way to switch between views inside a widget: its look (a segmented control, pills, or an underline — Theme Settings > Tabs) and its colours and corner radius come from the theme, so every tab strip in the app matches, including the page Tabs container. It draws the strip only; render the selected panel yourself below it. Never hand-roll tabs from `Pressable` chips. Props: `items` (`[{ key, label, icon?, disabled?, testID? }]`, icon is a lucide name), `value` (the selected key), `onChange(key)`, `accessibilityLabel`, `style`, `testID`, and the overrides `variant` ("segmented" | "pill" | "underline"), `activeColor`, `activeTextColor`, `textColor`, `radius`. The strip scrolls sideways when the tabs overflow. Declare `tabsStyle` / `tabsActiveColor` / `tabsActiveTextColor` / `tabsTextColor` / `tabsRadius` style fields and pass them through to let an author override one widget.',
|
|
2263
|
+
rnComponent: null,
|
|
2264
|
+
docsUrl: null,
|
|
2265
|
+
},
|
|
2266
|
+
{
|
|
2267
|
+
name: "Card",
|
|
2268
|
+
description:
|
|
2269
|
+
'Themed card surface. `<Card>…</Card>` — THE way to draw a card, panel or tile: with no props it takes the app\'s card look (Theme Settings > Cards: background, border, corner radius, padding, shadow, gradient) so it matches every built-in card. Override one card with `background`, `borderColor`, `radius`, `padding` (a number or `{ top, right, bottom, left }`), `shadow` ("none" | "sm" | "md" | "lg") or `gradient` (`{ from, to, angle }`), or pass `style={props.style}` when your widget declares the `cardBackground` / `cardBorderColor` / `cardRadius` / `cardPadding` / `cardGradient` / `shadow` / `margin` style fields, so an author can restyle it. A gradient paints on both hosts. `as` renders the card as another container (e.g. `Pressable`, `ScrollView`) and every other prop lands on it. Never hand-roll a card from `View` + border + radius.',
|
|
2270
|
+
rnComponent: null,
|
|
2271
|
+
docsUrl: null,
|
|
2272
|
+
},
|
|
2273
|
+
{
|
|
2274
|
+
name: "Button",
|
|
2275
|
+
description:
|
|
2276
|
+
'Themed button. `<Button title="Save" onPress={save} />` — THE way to draw a button: its fill, label colour, border, corner radius, padding, weight, shadow and gradient come from the app\'s button look (Theme Settings > Buttons, and Destructive / Focus ring for the danger variant and emphasis border), so it matches the built-in Button widget. Props: `title`, `onPress` (omit it for a purely visual button inside something else that is pressable), `variant` ("primary" default | "secondary" | "outlined" | "danger"), `icon` (a lucide name) with `iconPosition` ("leading" | "trailing"), `disabled`, `accessibilityLabel`, `testID`, `style` (extra container style, e.g. `alignSelf`) and `labelStyle`. Pass `fields` — `{ background, textColor, borderColor, borderWidth, paddingX, paddingY, radius, fontWeight, fontSize, shadow, gradient }` — to override one button, e.g. from style fields your widget declares. Never hand-roll a button from `Pressable` + background + radius.',
|
|
2277
|
+
rnComponent: null,
|
|
2278
|
+
docsUrl: null,
|
|
2279
|
+
},
|
|
2280
|
+
{
|
|
2281
|
+
name: "TextField",
|
|
2282
|
+
description:
|
|
2283
|
+
'Themed text-entry field. `<TextField value={name} onChangeText={setName} placeholder="Your name" accessibilityLabel="Name" />` — every `TextInput` prop works, and its background, text colour, border, corner radius and height come from the app\'s input look (Theme Settings > Muted), so a widget\'s fields match the built-in forms. `multiline` grows the field to a ceiling and then scrolls inside it. Override one field with `background`, `textColor`, `borderColor`, `radius` or `height`; `style` is merged last. Use it instead of styling a bare `TextInput`.',
|
|
2284
|
+
rnComponent: null,
|
|
2285
|
+
docsUrl: null,
|
|
2286
|
+
},
|
|
2227
2287
|
];
|
|
2228
2288
|
|
|
2229
2289
|
const CATEGORIES = [
|
|
@@ -4413,7 +4473,17 @@ const CONTRACT = deepFreeze({
|
|
|
4413
4473
|
// primitives that read it, plus `textRoleStyle` and its role helpers.
|
|
4414
4474
|
// Widgets each hand-rolled their rows, so two lists in one app disagreed.
|
|
4415
4475
|
// Additive: a theme without `list` paints the rows exactly as before.
|
|
4416
|
-
|
|
4476
|
+
// 1.126.0: additive — the `tabs` component scope and the `Tabs` primitive
|
|
4477
|
+
// that reads it, and the `enum` component-token type (`values`), first
|
|
4478
|
+
// used by `tabs.style`. The page Tabs container renders through `Tabs` on
|
|
4479
|
+
// both hosts. Additive: an unthemed strip is the segmented control the
|
|
4480
|
+
// export always drew.
|
|
4481
|
+
// 1.127.0: additive — the `Card`, `Button` and `TextField` primitives,
|
|
4482
|
+
// lifted from the built-in widgets' shared card surface, button surface
|
|
4483
|
+
// and form field so every widget draws them from one implementation, plus
|
|
4484
|
+
// a `height` token on the `muted` scope. Additive: the built-ins render
|
|
4485
|
+
// exactly as before.
|
|
4486
|
+
version: "1.127.0",
|
|
4417
4487
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
4418
4488
|
hooks: HOOKS,
|
|
4419
4489
|
primitives: PRIMITIVES,
|
|
@@ -4426,6 +4496,7 @@ const CONTRACT = deepFreeze({
|
|
|
4426
4496
|
themeTokens: DEFAULT_THEME_TOKENS,
|
|
4427
4497
|
themeColorTokens: THEME_COLOR_TOKENS,
|
|
4428
4498
|
themeComponents: THEME_COMPONENTS,
|
|
4499
|
+
themeComponentTabStyles: THEME_TAB_STYLES,
|
|
4429
4500
|
themeComponentShadows: THEME_COMPONENT_SHADOWS,
|
|
4430
4501
|
themeComponentTextTransforms: THEME_COMPONENT_TEXT_TRANSFORMS,
|
|
4431
4502
|
themeComponentGradient: THEME_COMPONENT_GRADIENT,
|
package/dist/contract.js
CHANGED
|
@@ -228,6 +228,9 @@ function isThemeColorTokenRef(value) {
|
|
|
228
228
|
// Single source for four consumers — the build runner's set_theme coercion, the
|
|
229
229
|
// shared host resolver, the planner prompt, and the SDK docs — so the vocabulary
|
|
230
230
|
// cannot drift between what Mason may emit and what a host actually applies.
|
|
231
|
+
// The looks a `tabs.style` token may name; `<Tabs>` draws each.
|
|
232
|
+
const THEME_TAB_STYLES = Object.freeze(["segmented", "pill", "underline"]);
|
|
233
|
+
|
|
231
234
|
const THEME_COMPONENT_SHADOWS = Object.freeze(["none", "sm", "md", "lg"]);
|
|
232
235
|
|
|
233
236
|
// sc-3857 — the `textTransform` token's closed enum, matching the per-instance
|
|
@@ -413,6 +416,7 @@ const MUTED_INPUT_FIELDS = Object.freeze({
|
|
|
413
416
|
textColor: "inputTextColor",
|
|
414
417
|
borderColor: "inputBorderColor",
|
|
415
418
|
radius: "inputRadius",
|
|
419
|
+
height: "inputHeight",
|
|
416
420
|
});
|
|
417
421
|
|
|
418
422
|
// The Form widgets' choice-field option list -- the closest thing this
|
|
@@ -445,6 +449,16 @@ const LIST_ROW_FIELDS = Object.freeze({
|
|
|
445
449
|
dividerWidth: "listDividerWidth",
|
|
446
450
|
});
|
|
447
451
|
|
|
452
|
+
// The tab-strip vocabulary. Bound by name as well as onto User Management,
|
|
453
|
+
// whose Users / Groups / Invites strip is drawn by the SDK <Tabs>.
|
|
454
|
+
const TABS_FIELDS = Object.freeze({
|
|
455
|
+
style: "tabsStyle",
|
|
456
|
+
activeColor: "tabsActiveColor",
|
|
457
|
+
activeTextColor: "tabsActiveTextColor",
|
|
458
|
+
textColor: "tabsTextColor",
|
|
459
|
+
radius: "tabsRadius",
|
|
460
|
+
});
|
|
461
|
+
|
|
448
462
|
const THEME_COMPONENTS = Object.freeze({
|
|
449
463
|
button: Object.freeze({
|
|
450
464
|
label: "Buttons",
|
|
@@ -563,6 +577,8 @@ const THEME_COMPONENTS = Object.freeze({
|
|
|
563
577
|
textColor: Object.freeze({ type: "color", uiDefault: "colors.onSurfaceMuted" }),
|
|
564
578
|
borderColor: Object.freeze({ type: "color", uiDefault: "colors.border" }),
|
|
565
579
|
radius: Object.freeze({ type: "size", min: 0, max: 48, uiDefault: "radii.sm" }),
|
|
580
|
+
// The field's minimum height; `<TextField>` reads it directly too.
|
|
581
|
+
height: Object.freeze({ type: "size", min: 32, max: 72 }),
|
|
566
582
|
}),
|
|
567
583
|
targets: Object.freeze({
|
|
568
584
|
"appstudio.form-input": MUTED_INPUT_FIELDS,
|
|
@@ -614,6 +630,22 @@ const THEME_COMPONENTS = Object.freeze({
|
|
|
614
630
|
"appstudio.data-list": LIST_ROW_FIELDS,
|
|
615
631
|
}),
|
|
616
632
|
}),
|
|
633
|
+
// Tab strips: `<Tabs>` and the page Tabs container read this scope directly;
|
|
634
|
+
// the fields bind it onto widgets. `style` is the first `enum` token.
|
|
635
|
+
tabs: Object.freeze({
|
|
636
|
+
label: "Tabs",
|
|
637
|
+
universalFields: TABS_FIELDS,
|
|
638
|
+
tokens: Object.freeze({
|
|
639
|
+
style: Object.freeze({ type: "enum", values: THEME_TAB_STYLES }),
|
|
640
|
+
activeColor: Object.freeze({ type: "color", uiDefault: "colors.primary" }),
|
|
641
|
+
activeTextColor: Object.freeze({ type: "color" }),
|
|
642
|
+
textColor: Object.freeze({ type: "color", uiDefault: "colors.onSurfaceMuted" }),
|
|
643
|
+
radius: Object.freeze({ type: "size", min: 0, max: 24 }),
|
|
644
|
+
}),
|
|
645
|
+
targets: Object.freeze({
|
|
646
|
+
"appstudio.user-management": TABS_FIELDS,
|
|
647
|
+
}),
|
|
648
|
+
}),
|
|
617
649
|
});
|
|
618
650
|
|
|
619
651
|
// sc-4505 — the value types a widget event payload field may declare. Narrower
|
|
@@ -2224,6 +2256,34 @@ const PRIMITIVES = [
|
|
|
2224
2256
|
rnComponent: null,
|
|
2225
2257
|
docsUrl: null,
|
|
2226
2258
|
},
|
|
2259
|
+
{
|
|
2260
|
+
name: "Tabs",
|
|
2261
|
+
description:
|
|
2262
|
+
'Themed tab strip. `<Tabs items={[{ key: "open", label: "Open" }, { key: "done", label: "Done", icon: "check" }]} value={tab} onChange={setTab} />`. THE way to switch between views inside a widget: its look (a segmented control, pills, or an underline — Theme Settings > Tabs) and its colours and corner radius come from the theme, so every tab strip in the app matches, including the page Tabs container. It draws the strip only; render the selected panel yourself below it. Never hand-roll tabs from `Pressable` chips. Props: `items` (`[{ key, label, icon?, disabled?, testID? }]`, icon is a lucide name), `value` (the selected key), `onChange(key)`, `accessibilityLabel`, `style`, `testID`, and the overrides `variant` ("segmented" | "pill" | "underline"), `activeColor`, `activeTextColor`, `textColor`, `radius`. The strip scrolls sideways when the tabs overflow. Declare `tabsStyle` / `tabsActiveColor` / `tabsActiveTextColor` / `tabsTextColor` / `tabsRadius` style fields and pass them through to let an author override one widget.',
|
|
2263
|
+
rnComponent: null,
|
|
2264
|
+
docsUrl: null,
|
|
2265
|
+
},
|
|
2266
|
+
{
|
|
2267
|
+
name: "Card",
|
|
2268
|
+
description:
|
|
2269
|
+
'Themed card surface. `<Card>…</Card>` — THE way to draw a card, panel or tile: with no props it takes the app\'s card look (Theme Settings > Cards: background, border, corner radius, padding, shadow, gradient) so it matches every built-in card. Override one card with `background`, `borderColor`, `radius`, `padding` (a number or `{ top, right, bottom, left }`), `shadow` ("none" | "sm" | "md" | "lg") or `gradient` (`{ from, to, angle }`), or pass `style={props.style}` when your widget declares the `cardBackground` / `cardBorderColor` / `cardRadius` / `cardPadding` / `cardGradient` / `shadow` / `margin` style fields, so an author can restyle it. A gradient paints on both hosts. `as` renders the card as another container (e.g. `Pressable`, `ScrollView`) and every other prop lands on it. Never hand-roll a card from `View` + border + radius.',
|
|
2270
|
+
rnComponent: null,
|
|
2271
|
+
docsUrl: null,
|
|
2272
|
+
},
|
|
2273
|
+
{
|
|
2274
|
+
name: "Button",
|
|
2275
|
+
description:
|
|
2276
|
+
'Themed button. `<Button title="Save" onPress={save} />` — THE way to draw a button: its fill, label colour, border, corner radius, padding, weight, shadow and gradient come from the app\'s button look (Theme Settings > Buttons, and Destructive / Focus ring for the danger variant and emphasis border), so it matches the built-in Button widget. Props: `title`, `onPress` (omit it for a purely visual button inside something else that is pressable), `variant` ("primary" default | "secondary" | "outlined" | "danger"), `icon` (a lucide name) with `iconPosition` ("leading" | "trailing"), `disabled`, `accessibilityLabel`, `testID`, `style` (extra container style, e.g. `alignSelf`) and `labelStyle`. Pass `fields` — `{ background, textColor, borderColor, borderWidth, paddingX, paddingY, radius, fontWeight, fontSize, shadow, gradient }` — to override one button, e.g. from style fields your widget declares. Never hand-roll a button from `Pressable` + background + radius.',
|
|
2277
|
+
rnComponent: null,
|
|
2278
|
+
docsUrl: null,
|
|
2279
|
+
},
|
|
2280
|
+
{
|
|
2281
|
+
name: "TextField",
|
|
2282
|
+
description:
|
|
2283
|
+
'Themed text-entry field. `<TextField value={name} onChangeText={setName} placeholder="Your name" accessibilityLabel="Name" />` — every `TextInput` prop works, and its background, text colour, border, corner radius and height come from the app\'s input look (Theme Settings > Muted), so a widget\'s fields match the built-in forms. `multiline` grows the field to a ceiling and then scrolls inside it. Override one field with `background`, `textColor`, `borderColor`, `radius` or `height`; `style` is merged last. Use it instead of styling a bare `TextInput`.',
|
|
2284
|
+
rnComponent: null,
|
|
2285
|
+
docsUrl: null,
|
|
2286
|
+
},
|
|
2227
2287
|
];
|
|
2228
2288
|
|
|
2229
2289
|
const CATEGORIES = [
|
|
@@ -4409,7 +4469,17 @@ const CONTRACT = deepFreeze({
|
|
|
4409
4469
|
// primitives that read it, plus `textRoleStyle` and its role helpers.
|
|
4410
4470
|
// Widgets each hand-rolled their rows, so two lists in one app disagreed.
|
|
4411
4471
|
// Additive: a theme without `list` paints the rows exactly as before.
|
|
4412
|
-
|
|
4472
|
+
// 1.126.0: additive — the `tabs` component scope and the `Tabs` primitive
|
|
4473
|
+
// that reads it, and the `enum` component-token type (`values`), first
|
|
4474
|
+
// used by `tabs.style`. The page Tabs container renders through `Tabs` on
|
|
4475
|
+
// both hosts. Additive: an unthemed strip is the segmented control the
|
|
4476
|
+
// export always drew.
|
|
4477
|
+
// 1.127.0: additive — the `Card`, `Button` and `TextField` primitives,
|
|
4478
|
+
// lifted from the built-in widgets' shared card surface, button surface
|
|
4479
|
+
// and form field so every widget draws them from one implementation, plus
|
|
4480
|
+
// a `height` token on the `muted` scope. Additive: the built-ins render
|
|
4481
|
+
// exactly as before.
|
|
4482
|
+
version: "1.127.0",
|
|
4413
4483
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
4414
4484
|
hooks: HOOKS,
|
|
4415
4485
|
primitives: PRIMITIVES,
|
|
@@ -4422,6 +4492,7 @@ const CONTRACT = deepFreeze({
|
|
|
4422
4492
|
themeTokens: DEFAULT_THEME_TOKENS,
|
|
4423
4493
|
themeColorTokens: THEME_COLOR_TOKENS,
|
|
4424
4494
|
themeComponents: THEME_COMPONENTS,
|
|
4495
|
+
themeComponentTabStyles: THEME_TAB_STYLES,
|
|
4425
4496
|
themeComponentShadows: THEME_COMPONENT_SHADOWS,
|
|
4426
4497
|
themeComponentTextTransforms: THEME_COMPONENT_TEXT_TRANSFORMS,
|
|
4427
4498
|
themeComponentGradient: THEME_COMPONENT_GRADIENT,
|
package/dist/icon.js
CHANGED
|
@@ -18,10 +18,14 @@ import React from "react";
|
|
|
18
18
|
import * as LucideIcons from "lucide-react-native";
|
|
19
19
|
import { normalizeLucideIconName } from "./lucideIconName.js";
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
/** The lucide component a name resolves to, or null for an unknown name. */
|
|
22
|
+
export function lucideGlyph(name) {
|
|
22
23
|
const resolved = normalizeLucideIconName(name);
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
return (resolved && LucideIcons[resolved]) || null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function Icon({ name, size, color }) {
|
|
28
|
+
const Component = lucideGlyph(name) || LucideIcons.Square;
|
|
25
29
|
const pixelSize = Number.isFinite(size) && size > 0 ? size : 24;
|
|
26
30
|
return React.createElement(Component, {
|
|
27
31
|
size: pixelSize,
|
package/dist/index.d.ts
CHANGED
|
@@ -2615,6 +2615,118 @@ export const EmptyState: (props: {
|
|
|
2615
2615
|
children?: ReactNode;
|
|
2616
2616
|
}) => any;
|
|
2617
2617
|
|
|
2618
|
+
/**
|
|
2619
|
+
* Themed card surface. With no props it takes the app's card look (Theme
|
|
2620
|
+
* Settings > Cards). Props override one card; `style` takes the `card*` style
|
|
2621
|
+
* fields a widget declares; `as` renders it as another container.
|
|
2622
|
+
*
|
|
2623
|
+
* @example
|
|
2624
|
+
* <Card><Text>…</Text></Card>
|
|
2625
|
+
* <Card as={Pressable} onPress={open} radius={12} shadow="md">…</Card>
|
|
2626
|
+
*/
|
|
2627
|
+
export const Card: (props: {
|
|
2628
|
+
background?: string;
|
|
2629
|
+
borderColor?: string;
|
|
2630
|
+
radius?: number | { topLeft?: number; topRight?: number; bottomRight?: number; bottomLeft?: number };
|
|
2631
|
+
padding?: number | { top?: number; right?: number; bottom?: number; left?: number };
|
|
2632
|
+
shadow?: "none" | "sm" | "md" | "lg";
|
|
2633
|
+
gradient?: { from: string; to: string; angle?: number };
|
|
2634
|
+
/** `card*` style fields, `shadow`, `margin` — a widget's per-instance style. */
|
|
2635
|
+
style?: Record<string, any>;
|
|
2636
|
+
/** The caller's own base style; a card given none starts from the theme. */
|
|
2637
|
+
baseStyle?: any;
|
|
2638
|
+
as?: any;
|
|
2639
|
+
fillInGradient?: boolean;
|
|
2640
|
+
containerStyle?: any;
|
|
2641
|
+
withoutMargin?: boolean;
|
|
2642
|
+
children?: ReactNode;
|
|
2643
|
+
[prop: string]: any;
|
|
2644
|
+
}) => any;
|
|
2645
|
+
|
|
2646
|
+
export type ButtonVariant = "primary" | "secondary" | "outlined" | "danger";
|
|
2647
|
+
export const BUTTON_VARIANTS: readonly ButtonVariant[];
|
|
2648
|
+
|
|
2649
|
+
/**
|
|
2650
|
+
* Themed button (Theme Settings > Buttons, Destructive, Focus ring). Omit
|
|
2651
|
+
* `onPress` for a purely visual button inside something pressable.
|
|
2652
|
+
*
|
|
2653
|
+
* @example
|
|
2654
|
+
* <Button title="Save" onPress={save} />
|
|
2655
|
+
* <Button title="Delete" variant="danger" icon="trash-2" onPress={remove} />
|
|
2656
|
+
*/
|
|
2657
|
+
export const Button: (props: {
|
|
2658
|
+
title: ReactNode;
|
|
2659
|
+
variant?: ButtonVariant;
|
|
2660
|
+
icon?: string;
|
|
2661
|
+
iconPosition?: "leading" | "trailing";
|
|
2662
|
+
onPress?: () => void;
|
|
2663
|
+
disabled?: boolean;
|
|
2664
|
+
/** Per-button surface fields; unset ones follow the theme. */
|
|
2665
|
+
fields?: {
|
|
2666
|
+
background?: string;
|
|
2667
|
+
textColor?: string;
|
|
2668
|
+
borderColor?: string;
|
|
2669
|
+
borderWidth?: number;
|
|
2670
|
+
paddingX?: number;
|
|
2671
|
+
paddingY?: number;
|
|
2672
|
+
radius?: number;
|
|
2673
|
+
fontWeight?: string;
|
|
2674
|
+
fontSize?: number;
|
|
2675
|
+
shadow?: "none" | "sm" | "md" | "lg";
|
|
2676
|
+
gradient?: { from: string; to: string; angle?: number };
|
|
2677
|
+
};
|
|
2678
|
+
style?: any;
|
|
2679
|
+
labelStyle?: any;
|
|
2680
|
+
accessibilityLabel?: string;
|
|
2681
|
+
testID?: string;
|
|
2682
|
+
}) => any;
|
|
2683
|
+
|
|
2684
|
+
/**
|
|
2685
|
+
* Themed text-entry field (Theme Settings > Muted). Every TextInput prop works.
|
|
2686
|
+
*
|
|
2687
|
+
* @example
|
|
2688
|
+
* <TextField value={name} onChangeText={setName} placeholder="Your name" accessibilityLabel="Name" />
|
|
2689
|
+
*/
|
|
2690
|
+
export const TextField: (props: {
|
|
2691
|
+
background?: string;
|
|
2692
|
+
textColor?: string;
|
|
2693
|
+
borderColor?: string;
|
|
2694
|
+
radius?: number;
|
|
2695
|
+
height?: number;
|
|
2696
|
+
multiline?: boolean;
|
|
2697
|
+
style?: any;
|
|
2698
|
+
[prop: string]: any;
|
|
2699
|
+
}) => any;
|
|
2700
|
+
|
|
2701
|
+
export type TabStyle = "segmented" | "pill" | "underline";
|
|
2702
|
+
|
|
2703
|
+
/**
|
|
2704
|
+
* Themed tab strip. Its look (segmented, pills or underline), colours and
|
|
2705
|
+
* corners come from the theme's `tabs` scope, so every strip in the app —
|
|
2706
|
+
* including the page Tabs container — matches. Draws the strip only; render
|
|
2707
|
+
* the selected panel yourself.
|
|
2708
|
+
*
|
|
2709
|
+
* @example
|
|
2710
|
+
* const [tab, setTab] = useState("open");
|
|
2711
|
+
* <Tabs items={[{ key: "open", label: "Open" }, { key: "done", label: "Done" }]} value={tab} onChange={setTab} />
|
|
2712
|
+
*/
|
|
2713
|
+
export const Tabs: (props: {
|
|
2714
|
+
items: Array<{ key: string; label: ReactNode; icon?: string; disabled?: boolean; testID?: string }>;
|
|
2715
|
+
value?: string;
|
|
2716
|
+
onChange?: (key: string) => void;
|
|
2717
|
+
/** Overrides the theme's tab style for this strip. */
|
|
2718
|
+
variant?: TabStyle;
|
|
2719
|
+
activeColor?: string;
|
|
2720
|
+
activeTextColor?: string;
|
|
2721
|
+
textColor?: string;
|
|
2722
|
+
radius?: number;
|
|
2723
|
+
/** For hosts rendering outside a widget context; widgets omit it. */
|
|
2724
|
+
theme?: any;
|
|
2725
|
+
accessibilityLabel?: string;
|
|
2726
|
+
style?: any;
|
|
2727
|
+
testID?: string;
|
|
2728
|
+
}) => any;
|
|
2729
|
+
|
|
2618
2730
|
export type TextRole = "display" | "title" | "subtitle" | "body" | "caption" | "overline";
|
|
2619
2731
|
export const TEXT_ROLES: readonly TextRole[];
|
|
2620
2732
|
export const TEXT_TRANSFORMS: readonly string[];
|
package/dist/index.js
CHANGED
|
@@ -191,6 +191,36 @@ export {
|
|
|
191
191
|
export { insertAtCaret, pushRecentEmoji } from "./emoji-picker-tokens.js";
|
|
192
192
|
// Themed list building blocks; rows follow the theme's `list` scope.
|
|
193
193
|
export { List, ListRow, EmptyState } from "./list.js";
|
|
194
|
+
// The one card surface: the theme's `card` scope, a gradient fill, margin.
|
|
195
|
+
export { Card } from "./card.js";
|
|
196
|
+
export {
|
|
197
|
+
SHADOW_PRESETS,
|
|
198
|
+
shadowStyle,
|
|
199
|
+
SPACING_MAX_PX,
|
|
200
|
+
CARD_RADIUS_MAX_PX,
|
|
201
|
+
FRAME_RADIUS_MAX_PX,
|
|
202
|
+
marginStyle,
|
|
203
|
+
spacingOverlay,
|
|
204
|
+
cornerRadiusOverlay,
|
|
205
|
+
cardSurfaceStyle,
|
|
206
|
+
cardOverlay,
|
|
207
|
+
gradientProps,
|
|
208
|
+
cardGradientProps,
|
|
209
|
+
} from "./card-style.js";
|
|
210
|
+
// The themed button; follows the theme's button, destructive and ring scopes.
|
|
211
|
+
export { Button, BUTTON_VARIANTS } from "./button.js";
|
|
212
|
+
export {
|
|
213
|
+
BUTTON_PADDING_MAX,
|
|
214
|
+
BUTTON_BORDER_WIDTH_MAX,
|
|
215
|
+
SELF_ALIGNMENTS,
|
|
216
|
+
buttonSurfaceOverlay,
|
|
217
|
+
buttonSurfaceTextOverlay,
|
|
218
|
+
selfAlignStyle,
|
|
219
|
+
} from "./button-style.js";
|
|
220
|
+
// The themed text-entry field; follows the theme's `muted` scope.
|
|
221
|
+
export { TextField } from "./text-field.js";
|
|
222
|
+
// A themed tab strip; follows the theme's `tabs` scope.
|
|
223
|
+
export { Tabs } from "./tabs.js";
|
|
194
224
|
// The semantic text tiers, shared with the built-in widgets.
|
|
195
225
|
export {
|
|
196
226
|
textRoleStyle,
|
package/dist/index.native.js
CHANGED
|
@@ -184,6 +184,36 @@ export {
|
|
|
184
184
|
export { insertAtCaret, pushRecentEmoji } from "./emoji-picker-tokens.js";
|
|
185
185
|
// Themed list building blocks; rows follow the theme's `list` scope.
|
|
186
186
|
export { List, ListRow, EmptyState } from "./list.native.js";
|
|
187
|
+
// The one card surface: the theme's `card` scope, a gradient fill, margin.
|
|
188
|
+
export { Card } from "./card.native.js";
|
|
189
|
+
export {
|
|
190
|
+
SHADOW_PRESETS,
|
|
191
|
+
shadowStyle,
|
|
192
|
+
SPACING_MAX_PX,
|
|
193
|
+
CARD_RADIUS_MAX_PX,
|
|
194
|
+
FRAME_RADIUS_MAX_PX,
|
|
195
|
+
marginStyle,
|
|
196
|
+
spacingOverlay,
|
|
197
|
+
cornerRadiusOverlay,
|
|
198
|
+
cardSurfaceStyle,
|
|
199
|
+
cardOverlay,
|
|
200
|
+
gradientProps,
|
|
201
|
+
cardGradientProps,
|
|
202
|
+
} from "./card-style.js";
|
|
203
|
+
// The themed button; follows the theme's button, destructive and ring scopes.
|
|
204
|
+
export { Button, BUTTON_VARIANTS } from "./button.native.js";
|
|
205
|
+
export {
|
|
206
|
+
BUTTON_PADDING_MAX,
|
|
207
|
+
BUTTON_BORDER_WIDTH_MAX,
|
|
208
|
+
SELF_ALIGNMENTS,
|
|
209
|
+
buttonSurfaceOverlay,
|
|
210
|
+
buttonSurfaceTextOverlay,
|
|
211
|
+
selfAlignStyle,
|
|
212
|
+
} from "./button-style.js";
|
|
213
|
+
// The themed text-entry field; follows the theme's `muted` scope.
|
|
214
|
+
export { TextField } from "./text-field.native.js";
|
|
215
|
+
// A themed tab strip; follows the theme's `tabs` scope.
|
|
216
|
+
export { Tabs } from "./tabs.native.js";
|
|
187
217
|
// The semantic text tiers, shared with the built-in widgets.
|
|
188
218
|
export {
|
|
189
219
|
textRoleStyle,
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// The values `<Tabs>` paints with, resolved from the theme's `tabs` component
|
|
2
|
+
// scope. Platform-free, like list-tokens.js, so both hosts draw one strip from
|
|
3
|
+
// the same numbers and tests can pin them without a renderer.
|
|
4
|
+
|
|
5
|
+
import { DEFAULT_THEME_TOKENS } from "./_theme-tokens.js";
|
|
6
|
+
import { CONTRACT, contrastRatio } from "./contract.js";
|
|
7
|
+
|
|
8
|
+
export const TAB_STYLES = CONTRACT.themeComponentTabStyles;
|
|
9
|
+
|
|
10
|
+
// Segmented is the strip the exported app's Tabs container always drew.
|
|
11
|
+
export const DEFAULT_TAB_STYLE = "segmented";
|
|
12
|
+
export const DEFAULT_TAB_RADIUS = 6;
|
|
13
|
+
|
|
14
|
+
// Per-strip overrides a widget may pass, named as the theme tokens are.
|
|
15
|
+
export const TAB_TOKEN_NAMES = Object.freeze([
|
|
16
|
+
"style",
|
|
17
|
+
"activeColor",
|
|
18
|
+
"activeTextColor",
|
|
19
|
+
"textColor",
|
|
20
|
+
"radius",
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
function isSet(value) {
|
|
24
|
+
return value !== undefined && value !== null && value !== "";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function pick(overrides, scope, name) {
|
|
28
|
+
if (overrides && isSet(overrides[name])) return overrides[name];
|
|
29
|
+
if (scope && isSet(scope[name])) return scope[name];
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Resolve one tab strip: explicit override, then the theme's `tabs` scope,
|
|
35
|
+
* then the widget's `defaultStyle`, then the tokens. The active label is the
|
|
36
|
+
* accent on the surface styles, and readable on the accent for a pill.
|
|
37
|
+
*
|
|
38
|
+
* @param {object} theme — the resolved workspace theme (`useTheme()` shape).
|
|
39
|
+
* @param {object} [overrides] — per-strip values keyed by TAB_TOKEN_NAMES.
|
|
40
|
+
*/
|
|
41
|
+
export function resolveTabsTokens(theme, overrides) {
|
|
42
|
+
const base = theme && typeof theme === "object" ? theme : DEFAULT_THEME_TOKENS;
|
|
43
|
+
const colors = base.colors || DEFAULT_THEME_TOKENS.colors;
|
|
44
|
+
const scope = base.components && base.components.tabs;
|
|
45
|
+
|
|
46
|
+
// The widget's own default sits BELOW the theme: a look still wins.
|
|
47
|
+
const requested =
|
|
48
|
+
pick(overrides, scope, "style") ?? (overrides && overrides.defaultStyle);
|
|
49
|
+
const style = TAB_STYLES.includes(requested) ? requested : DEFAULT_TAB_STYLE;
|
|
50
|
+
const ownAccent = pick(overrides, scope, "activeColor");
|
|
51
|
+
const activeColor = ownAccent ?? colors.primary;
|
|
52
|
+
return {
|
|
53
|
+
style,
|
|
54
|
+
activeColor,
|
|
55
|
+
activeTextColor:
|
|
56
|
+
pick(overrides, scope, "activeTextColor") ??
|
|
57
|
+
(style === "pill"
|
|
58
|
+
? ownAccent
|
|
59
|
+
? readableOn(ownAccent, colors)
|
|
60
|
+
: colors.onPrimary
|
|
61
|
+
: activeColor),
|
|
62
|
+
textColor: pick(overrides, scope, "textColor") ?? colors.onSurfaceMuted,
|
|
63
|
+
radius: pick(overrides, scope, "radius") ?? DEFAULT_TAB_RADIUS,
|
|
64
|
+
barBackground: colors.surfaceMuted,
|
|
65
|
+
activeSurface: colors.surface,
|
|
66
|
+
rule: colors.border,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// A chosen accent carries the label that reads best on it, dark or light.
|
|
71
|
+
function readableOn(fill, colors) {
|
|
72
|
+
const dark = colors.onSurface;
|
|
73
|
+
const light = "#ffffff";
|
|
74
|
+
return contrastRatio(fill, dark) >= contrastRatio(fill, light) ? dark : light;
|
|
75
|
+
}
|