@ankhorage/zora 4.3.0 → 4.4.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/components/radio/RadioGroup.d.ts.map +1 -1
  3. package/dist/components/radio/RadioGroup.js +27 -7
  4. package/dist/components/radio/RadioGroup.js.map +1 -1
  5. package/dist/components/radio/meta.d.ts +203 -4
  6. package/dist/components/radio/meta.d.ts.map +1 -1
  7. package/dist/components/radio/meta.js +168 -4
  8. package/dist/components/radio/meta.js.map +1 -1
  9. package/dist/components/radio/resolveRadioGroupThemeRecipe.d.ts +20 -0
  10. package/dist/components/radio/resolveRadioGroupThemeRecipe.d.ts.map +1 -0
  11. package/dist/components/radio/resolveRadioGroupThemeRecipe.js +34 -0
  12. package/dist/components/radio/resolveRadioGroupThemeRecipe.js.map +1 -0
  13. package/dist/components/radio/themeRecipeMeta.d.ts +26 -0
  14. package/dist/components/radio/themeRecipeMeta.d.ts.map +1 -0
  15. package/dist/components/radio/themeRecipeMeta.js +27 -0
  16. package/dist/components/radio/themeRecipeMeta.js.map +1 -0
  17. package/dist/components/radio/types.d.ts +1 -0
  18. package/dist/components/radio/types.d.ts.map +1 -1
  19. package/dist/components/radio/types.js.map +1 -1
  20. package/dist/metadata/themeRecipeMeta.d.ts.map +1 -1
  21. package/dist/metadata/themeRecipeMeta.js +2 -0
  22. package/dist/metadata/themeRecipeMeta.js.map +1 -1
  23. package/package.json +1 -1
  24. package/src/components/radio/RadioGroup.test.ts +31 -0
  25. package/src/components/radio/RadioGroup.tsx +45 -8
  26. package/src/components/radio/meta.ts +170 -4
  27. package/src/components/radio/resolveRadioGroupThemeRecipe.ts +45 -0
  28. package/src/components/radio/themeRecipeMeta.ts +28 -0
  29. package/src/components/radio/types.ts +1 -0
  30. package/src/metadata/componentMeta.test.ts +21 -0
  31. package/src/metadata/themeRecipeMeta.test.ts +4 -0
  32. package/src/metadata/themeRecipeMeta.ts +2 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 037b931: Make RadioGroup manifest-authorable with normalized value bindings and events, and add a card-style presentation that preserves single-choice radio semantics.
8
+
3
9
  ## 4.3.0
4
10
 
5
11
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"RadioGroup.d.ts","sourceRoot":"","sources":["../../../src/components/radio/RadioGroup.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B,OAAO,KAAK,EAAoB,eAAe,EAAE,MAAM,SAAS,CAAC;AAqDjE;;GAEG;AACH,eAAO,MAAM,UAAU,GAtDE,MAAM,SAAS,MAAM,8DAsDe,CAAC"}
1
+ {"version":3,"file":"RadioGroup.d.ts","sourceRoot":"","sources":["../../../src/components/radio/RadioGroup.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,OAAO,KAAK,EAAoB,eAAe,EAAE,MAAM,SAAS,CAAC;AA6DjE;;GAEG;AACH,eAAO,MAAM,UAAU,GA3DE,MAAM,SAAS,MAAM,8DA2De,CAAC"}
@@ -1,17 +1,24 @@
1
- import { Radio } from '@ankhorage/surface';
1
+ import { Radio, useTheme } from '@ankhorage/surface';
2
2
  import React from 'react';
3
3
  import { View } from 'react-native';
4
4
  import { Stack } from '../../foundation';
5
+ import { useZoraThemeRecipe } from '../../theme/useZoraThemeRecipe';
5
6
  import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
6
7
  import { Text } from '../text';
7
- function RadioGroupInner({ themeId: _themeId, mode: _mode, value, onValueChange, options, orientation = 'vertical', gap = 's', color = 'primary', size = 'm', invalid = false, readOnly = false, disabled = false, testID, interactionPolicy, }) {
8
+ import { resolveRadioGroupThemeRecipe } from './resolveRadioGroupThemeRecipe';
9
+ /***
10
+ * Resolves the themed radio-group presentation within the active ZORA scope.
11
+ */
12
+ function RadioGroupInner({ themeId: _themeId, mode: _mode, value, onValueChange, options, orientation = 'vertical', gap, presentation = 'inline', color, size, invalid = false, readOnly = false, disabled = false, testID, interactionPolicy, }) {
8
13
  const isHorizontal = orientation === 'horizontal';
14
+ const themeFields = useZoraThemeRecipe('RadioGroup');
15
+ const recipe = resolveRadioGroupThemeRecipe({ gap, color, size, themeFields });
9
16
  return (<View testID={testID} accessibilityRole="radiogroup" style={{
10
17
  flexDirection: isHorizontal ? 'row' : 'column',
11
18
  flexWrap: isHorizontal ? 'wrap' : 'nowrap',
12
19
  }}>
13
- <Stack direction={isHorizontal ? 'row' : 'column'} gap={gap} wrap={isHorizontal ? 'wrap' : 'nowrap'}>
14
- {options.map((option) => (<RadioGroupItem key={option.value} option={option} checked={value === option.value} disabled={disabled || option.disabled === true} invalid={invalid} readOnly={readOnly} size={size} color={color} onSelect={onValueChange} interactionPolicy={interactionPolicy}/>))}
20
+ <Stack direction={isHorizontal ? 'row' : 'column'} gap={recipe.gap} wrap={isHorizontal ? 'wrap' : 'nowrap'}>
21
+ {options.map((option) => (<RadioGroupItem key={option.value} option={option} checked={value === option.value} disabled={disabled || option.disabled === true} invalid={invalid} readOnly={readOnly} size={recipe.size} color={recipe.color} orientation={orientation} presentation={presentation} onSelect={onValueChange} interactionPolicy={interactionPolicy}/>))}
15
22
  </Stack>
16
23
  </View>);
17
24
  }
@@ -19,16 +26,29 @@ function RadioGroupInner({ themeId: _themeId, mode: _mode, value, onValueChange,
19
26
  * Renders a group of radio options for selecting a single value.
20
27
  */
21
28
  export const RadioGroup = withZoraThemeScope(RadioGroupInner);
22
- function RadioGroupItem({ option, checked, disabled, invalid, readOnly, size, color, onSelect, interactionPolicy, }) {
29
+ /***
30
+ * Renders one option through the single Surface Radio interaction boundary.
31
+ */
32
+ function RadioGroupItem({ option, checked, disabled, invalid, readOnly, size, color, orientation, presentation, onSelect, interactionPolicy, }) {
33
+ const { theme } = useTheme();
23
34
  const passive = interactionPolicy === 'passive';
24
- return (<Radio interactionPolicy={interactionPolicy} checked={checked} disabled={disabled} invalid={invalid} readOnly={readOnly} size={size} color={color} testID={option.testID} onCheckedChange={(nextChecked) => {
35
+ const isCard = presentation === 'card';
36
+ return (<Radio interactionPolicy={interactionPolicy} checked={checked} disabled={disabled} invalid={invalid} readOnly={readOnly} size={size} color={color} testID={option.testID} bg={isCard
37
+ ? checked
38
+ ? theme.semantics.selection.background
39
+ : theme.semantics.surface.default
40
+ : undefined} borderColor={isCard
41
+ ? checked
42
+ ? theme.semantics.selection.border
43
+ : theme.semantics.border.default
44
+ : undefined} borderWidth={isCard ? 1 : undefined} p={isCard ? 'm' : undefined} radius={isCard ? 'l' : undefined} width={isCard && orientation === 'vertical' ? '100%' : undefined} onCheckedChange={(nextChecked) => {
25
45
  if (passive)
26
46
  return;
27
47
  if (nextChecked)
28
48
  onSelect(option.value);
29
49
  }}>
30
50
  <Stack gap="xs">
31
- <Text>{option.label}</Text>
51
+ <Text weight={isCard ? 'semiBold' : undefined}>{option.label}</Text>
32
52
  {option.description ? (<Text emphasis="muted" variant="caption">
33
53
  {option.description}
34
54
  </Text>) : null}
@@ -1 +1 @@
1
- {"version":3,"file":"RadioGroup.js","sourceRoot":"","sources":["../../../src/components/radio/RadioGroup.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEpC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG/B,SAAS,eAAe,CAAwB,EAC9C,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,KAAK,EACX,KAAK,EACL,aAAa,EACb,OAAO,EACP,WAAW,GAAG,UAAU,EACxB,GAAG,GAAG,GAAG,EACT,KAAK,GAAG,SAAS,EACjB,IAAI,GAAG,GAAG,EACV,OAAO,GAAG,KAAK,EACf,QAAQ,GAAG,KAAK,EAChB,QAAQ,GAAG,KAAK,EAChB,MAAM,EACN,iBAAiB,GACO;IACxB,MAAM,YAAY,GAAG,WAAW,KAAK,YAAY,CAAC;IAElD,OAAO,CACL,CAAC,IAAI,CACH,MAAM,CAAC,CAAC,MAAM,CAAC,CACf,iBAAiB,CAAC,YAAY,CAC9B,KAAK,CAAC,CAAC;YACL,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ;YAC9C,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;SAC3C,CAAC,CAEF;MAAA,CAAC,KAAK,CACJ,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAC3C,GAAG,CAAC,CAAC,GAAG,CAAC,CACT,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAEvC;QAAA,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CACvB,CAAC,cAAc,CACb,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAClB,MAAM,CAAC,CAAC,MAAM,CAAC,CACf,OAAO,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,CAChC,QAAQ,CAAC,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI,CAAC,CAC/C,OAAO,CAAC,CAAC,OAAO,CAAC,CACjB,QAAQ,CAAC,CAAC,QAAQ,CAAC,CACnB,IAAI,CAAC,CAAC,IAAI,CAAC,CACX,KAAK,CAAC,CAAC,KAAK,CAAC,CACb,QAAQ,CAAC,CAAC,aAAa,CAAC,CACxB,iBAAiB,CAAC,CAAC,iBAAiB,CAAC,EACrC,CACH,CAAC,CACJ;MAAA,EAAE,KAAK,CACT;IAAA,EAAE,IAAI,CAAC,CACR,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,kBAAkB,CAAC,eAAe,CAAC,CAAC;AAE9D,SAAS,cAAc,CAAwB,EAC7C,MAAM,EACN,OAAO,EACP,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,iBAAiB,GAWlB;IACC,MAAM,OAAO,GAAG,iBAAiB,KAAK,SAAS,CAAC;IAEhD,OAAO,CACL,CAAC,KAAK,CACJ,iBAAiB,CAAC,CAAC,iBAAiB,CAAC,CACrC,OAAO,CAAC,CAAC,OAAO,CAAC,CACjB,QAAQ,CAAC,CAAC,QAAQ,CAAC,CACnB,OAAO,CAAC,CAAC,OAAO,CAAC,CACjB,QAAQ,CAAC,CAAC,QAAQ,CAAC,CACnB,IAAI,CAAC,CAAC,IAAI,CAAC,CACX,KAAK,CAAC,CAAC,KAAK,CAAC,CACb,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CACtB,eAAe,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE;YAC/B,IAAI,OAAO;gBAAE,OAAO;YACpB,IAAI,WAAW;gBAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1C,CAAC,CAAC,CAEF;MAAA,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CACb;QAAA,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,CAC1B;QAAA,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CACpB,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CACtC;YAAA,CAAC,MAAM,CAAC,WAAW,CACrB;UAAA,EAAE,IAAI,CAAC,CACR,CAAC,CAAC,CAAC,IAAI,CACV;MAAA,EAAE,KAAK,CACT;IAAA,EAAE,KAAK,CAAC,CACT,CAAC;AACJ,CAAC","sourcesContent":["import { Radio } from '@ankhorage/surface';\nimport React from 'react';\nimport { View } from 'react-native';\n\nimport { Stack } from '../../foundation';\nimport { withZoraThemeScope } from '../../theme/withZoraThemeScope';\nimport { Text } from '../text';\nimport type { RadioGroupOption, RadioGroupProps } from './types';\n\nfunction RadioGroupInner<TValue extends string>({\n themeId: _themeId,\n mode: _mode,\n value,\n onValueChange,\n options,\n orientation = 'vertical',\n gap = 's',\n color = 'primary',\n size = 'm',\n invalid = false,\n readOnly = false,\n disabled = false,\n testID,\n interactionPolicy,\n}: RadioGroupProps<TValue>) {\n const isHorizontal = orientation === 'horizontal';\n\n return (\n <View\n testID={testID}\n accessibilityRole=\"radiogroup\"\n style={{\n flexDirection: isHorizontal ? 'row' : 'column',\n flexWrap: isHorizontal ? 'wrap' : 'nowrap',\n }}\n >\n <Stack\n direction={isHorizontal ? 'row' : 'column'}\n gap={gap}\n wrap={isHorizontal ? 'wrap' : 'nowrap'}\n >\n {options.map((option) => (\n <RadioGroupItem\n key={option.value}\n option={option}\n checked={value === option.value}\n disabled={disabled || option.disabled === true}\n invalid={invalid}\n readOnly={readOnly}\n size={size}\n color={color}\n onSelect={onValueChange}\n interactionPolicy={interactionPolicy}\n />\n ))}\n </Stack>\n </View>\n );\n}\n\n/***\n * Renders a group of radio options for selecting a single value.\n */\nexport const RadioGroup = withZoraThemeScope(RadioGroupInner);\n\nfunction RadioGroupItem<TValue extends string>({\n option,\n checked,\n disabled,\n invalid,\n readOnly,\n size,\n color,\n onSelect,\n interactionPolicy,\n}: {\n option: RadioGroupOption<TValue>;\n checked: boolean;\n disabled: boolean;\n invalid: boolean;\n readOnly: boolean;\n size: NonNullable<RadioGroupProps<TValue>['size']>;\n color: NonNullable<RadioGroupProps<TValue>['color']>;\n onSelect: (value: TValue) => void;\n interactionPolicy: RadioGroupProps<TValue>['interactionPolicy'];\n}) {\n const passive = interactionPolicy === 'passive';\n\n return (\n <Radio\n interactionPolicy={interactionPolicy}\n checked={checked}\n disabled={disabled}\n invalid={invalid}\n readOnly={readOnly}\n size={size}\n color={color}\n testID={option.testID}\n onCheckedChange={(nextChecked) => {\n if (passive) return;\n if (nextChecked) onSelect(option.value);\n }}\n >\n <Stack gap=\"xs\">\n <Text>{option.label}</Text>\n {option.description ? (\n <Text emphasis=\"muted\" variant=\"caption\">\n {option.description}\n </Text>\n ) : null}\n </Stack>\n </Radio>\n );\n}\n"]}
1
+ {"version":3,"file":"RadioGroup.js","sourceRoot":"","sources":["../../../src/components/radio/RadioGroup.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Radio, useTheme } from '@ankhorage/surface';\nimport React from 'react';\nimport { View } from 'react-native';\n\nimport { Stack } from '../../foundation';\nimport { useZoraThemeRecipe } from '../../theme/useZoraThemeRecipe';\nimport { withZoraThemeScope } from '../../theme/withZoraThemeScope';\nimport { Text } from '../text';\nimport { resolveRadioGroupThemeRecipe } from './resolveRadioGroupThemeRecipe';\nimport type { RadioGroupOption, RadioGroupProps } from './types';\n\n/***\n * Resolves the themed radio-group presentation within the active ZORA scope.\n */\nfunction RadioGroupInner<TValue extends string>({\n themeId: _themeId,\n mode: _mode,\n value,\n onValueChange,\n options,\n orientation = 'vertical',\n gap,\n presentation = 'inline',\n color,\n size,\n invalid = false,\n readOnly = false,\n disabled = false,\n testID,\n interactionPolicy,\n}: RadioGroupProps<TValue>) {\n const isHorizontal = orientation === 'horizontal';\n const themeFields = useZoraThemeRecipe('RadioGroup');\n const recipe = resolveRadioGroupThemeRecipe({ gap, color, size, themeFields });\n\n return (\n <View\n testID={testID}\n accessibilityRole=\"radiogroup\"\n style={{\n flexDirection: isHorizontal ? 'row' : 'column',\n flexWrap: isHorizontal ? 'wrap' : 'nowrap',\n }}\n >\n <Stack\n direction={isHorizontal ? 'row' : 'column'}\n gap={recipe.gap}\n wrap={isHorizontal ? 'wrap' : 'nowrap'}\n >\n {options.map((option) => (\n <RadioGroupItem\n key={option.value}\n option={option}\n checked={value === option.value}\n disabled={disabled || option.disabled === true}\n invalid={invalid}\n readOnly={readOnly}\n size={recipe.size}\n color={recipe.color}\n orientation={orientation}\n presentation={presentation}\n onSelect={onValueChange}\n interactionPolicy={interactionPolicy}\n />\n ))}\n </Stack>\n </View>\n );\n}\n\n/***\n * Renders a group of radio options for selecting a single value.\n */\nexport const RadioGroup = withZoraThemeScope(RadioGroupInner);\n\n/***\n * Renders one option through the single Surface Radio interaction boundary.\n */\nfunction RadioGroupItem<TValue extends string>({\n option,\n checked,\n disabled,\n invalid,\n readOnly,\n size,\n color,\n orientation,\n presentation,\n onSelect,\n interactionPolicy,\n}: {\n option: RadioGroupOption<TValue>;\n checked: boolean;\n disabled: boolean;\n invalid: boolean;\n readOnly: boolean;\n size: NonNullable<RadioGroupProps<TValue>['size']>;\n color: NonNullable<RadioGroupProps<TValue>['color']>;\n orientation: NonNullable<RadioGroupProps<TValue>['orientation']>;\n presentation: NonNullable<RadioGroupProps<TValue>['presentation']>;\n onSelect: (value: TValue) => void;\n interactionPolicy: RadioGroupProps<TValue>['interactionPolicy'];\n}) {\n const { theme } = useTheme();\n const passive = interactionPolicy === 'passive';\n const isCard = presentation === 'card';\n\n return (\n <Radio\n interactionPolicy={interactionPolicy}\n checked={checked}\n disabled={disabled}\n invalid={invalid}\n readOnly={readOnly}\n size={size}\n color={color}\n testID={option.testID}\n bg={\n isCard\n ? checked\n ? theme.semantics.selection.background\n : theme.semantics.surface.default\n : undefined\n }\n borderColor={\n isCard\n ? checked\n ? theme.semantics.selection.border\n : theme.semantics.border.default\n : undefined\n }\n borderWidth={isCard ? 1 : undefined}\n p={isCard ? 'm' : undefined}\n radius={isCard ? 'l' : undefined}\n width={isCard && orientation === 'vertical' ? '100%' : undefined}\n onCheckedChange={(nextChecked) => {\n if (passive) return;\n if (nextChecked) onSelect(option.value);\n }}\n >\n <Stack gap=\"xs\">\n <Text weight={isCard ? 'semiBold' : undefined}>{option.label}</Text>\n {option.description ? (\n <Text emphasis=\"muted\" variant=\"caption\">\n {option.description}\n </Text>\n ) : null}\n </Stack>\n </Radio>\n );\n}\n"]}
@@ -3,15 +3,214 @@ export declare const radioMeta: {
3
3
  readonly category: "component";
4
4
  readonly directManifestNode: false;
5
5
  readonly allowedChildren: readonly [];
6
- readonly note: "Form control component; not represented as a manifest node in v1.";
6
+ readonly note: "Composition-level radio control; author repeated single-choice UI through RadioGroup.";
7
7
  readonly props: {};
8
8
  };
9
9
  export declare const radioGroupMeta: {
10
10
  readonly name: "RadioGroup";
11
11
  readonly category: "component";
12
- readonly directManifestNode: false;
12
+ readonly description: "Single-choice group with inline or card presentation while preserving radio semantics.";
13
+ readonly directManifestNode: true;
13
14
  readonly allowedChildren: readonly [];
14
- readonly note: "Form control component; not represented as a manifest node in v1.";
15
- readonly props: {};
15
+ readonly blueprint: {
16
+ readonly label: "Radio group";
17
+ readonly defaultProps: {
18
+ readonly value: "option-b";
19
+ readonly options: readonly [{
20
+ readonly value: "option-a";
21
+ readonly label: "First option";
22
+ readonly description: "Supporting detail";
23
+ }, {
24
+ readonly value: "option-b";
25
+ readonly label: "Second option";
26
+ readonly description: "Supporting detail";
27
+ }, {
28
+ readonly value: "option-c";
29
+ readonly label: "Third option";
30
+ readonly description: "Supporting detail";
31
+ }];
32
+ readonly orientation: "vertical";
33
+ readonly presentation: "inline";
34
+ readonly invalid: false;
35
+ readonly readOnly: false;
36
+ readonly disabled: false;
37
+ };
38
+ };
39
+ readonly bindings: {
40
+ readonly props: {
41
+ readonly value: {
42
+ readonly label: "Value";
43
+ readonly description: "Currently selected option value.";
44
+ readonly value: {
45
+ readonly type: "string";
46
+ };
47
+ readonly acceptsFallback: true;
48
+ readonly acceptsTransforms: true;
49
+ };
50
+ readonly disabled: {
51
+ readonly label: "Disabled";
52
+ readonly description: "Whether the complete radio group is disabled.";
53
+ readonly value: {
54
+ readonly type: "boolean";
55
+ };
56
+ readonly acceptsFallback: true;
57
+ readonly acceptsTransforms: true;
58
+ };
59
+ };
60
+ readonly events: {
61
+ readonly valueChange: {
62
+ readonly label: "Value change";
63
+ readonly description: "Runs when the selected radio value changes.";
64
+ readonly payload: {
65
+ readonly eventType: "radioGroup.valueChange";
66
+ readonly fields: readonly [{
67
+ readonly path: "value";
68
+ readonly type: "string";
69
+ readonly label: "Value";
70
+ }];
71
+ };
72
+ };
73
+ };
74
+ };
75
+ readonly events: {
76
+ readonly valueChange: {
77
+ readonly label: "Value change";
78
+ readonly eventType: "radioGroup.valueChange";
79
+ readonly description: "Emitted when the selected radio value changes.";
80
+ readonly payloadFields: readonly [{
81
+ readonly path: "value";
82
+ readonly type: "string";
83
+ readonly label: "Value";
84
+ }];
85
+ };
86
+ };
87
+ readonly props: {
88
+ readonly value: {
89
+ readonly type: "string";
90
+ readonly category: "State";
91
+ readonly label: "Value";
92
+ readonly authoring: {
93
+ readonly authority: "instance";
94
+ };
95
+ };
96
+ readonly options: {
97
+ readonly type: "array";
98
+ readonly category: "Content";
99
+ readonly label: "Options";
100
+ readonly itemSchema: readonly [{
101
+ readonly key: "value";
102
+ readonly schema: {
103
+ readonly type: "string";
104
+ readonly category: "Content";
105
+ readonly label: "Value";
106
+ };
107
+ }, {
108
+ readonly key: "label";
109
+ readonly schema: {
110
+ readonly type: "string";
111
+ readonly category: "Content";
112
+ readonly label: "Label";
113
+ };
114
+ }, {
115
+ readonly key: "description";
116
+ readonly schema: {
117
+ readonly type: "string";
118
+ readonly category: "Content";
119
+ readonly label: "Description";
120
+ };
121
+ }, {
122
+ readonly key: "disabled";
123
+ readonly schema: {
124
+ readonly type: "boolean";
125
+ readonly category: "State";
126
+ readonly label: "Disabled";
127
+ readonly default: false;
128
+ };
129
+ }];
130
+ readonly authoring: {
131
+ readonly authority: "instance";
132
+ };
133
+ };
134
+ readonly orientation: {
135
+ readonly type: "enum";
136
+ readonly category: "Layout";
137
+ readonly label: "Orientation";
138
+ readonly enum: readonly ["horizontal", "vertical"];
139
+ readonly default: "vertical";
140
+ readonly authoring: {
141
+ readonly authority: "instance";
142
+ };
143
+ };
144
+ readonly gap: {
145
+ readonly type: "enum";
146
+ readonly category: "Layout";
147
+ readonly label: "Gap";
148
+ readonly enum: readonly ["xs", "s", "m", "l"];
149
+ readonly authoring: {
150
+ readonly authority: "theme";
151
+ readonly scope: "component";
152
+ readonly allowInstanceOverride: true;
153
+ };
154
+ };
155
+ readonly presentation: {
156
+ readonly type: "enum";
157
+ readonly category: "Style";
158
+ readonly label: "Presentation";
159
+ readonly enum: readonly ["inline", "card"];
160
+ readonly default: "inline";
161
+ readonly authoring: {
162
+ readonly authority: "instance";
163
+ };
164
+ };
165
+ readonly color: {
166
+ readonly type: "enum";
167
+ readonly category: "Style";
168
+ readonly label: "Color";
169
+ readonly enum: readonly ["primary", "secondary", "tertiary", "quaternary", "neutral", "success", "warning", "error", "info", "danger"];
170
+ readonly authoring: {
171
+ readonly authority: "theme";
172
+ readonly scope: "component";
173
+ readonly allowInstanceOverride: true;
174
+ };
175
+ };
176
+ readonly size: {
177
+ readonly type: "enum";
178
+ readonly category: "Style";
179
+ readonly label: "Size";
180
+ readonly enum: readonly ["s", "m", "l"];
181
+ readonly authoring: {
182
+ readonly authority: "theme";
183
+ readonly scope: "component";
184
+ readonly allowInstanceOverride: true;
185
+ };
186
+ };
187
+ readonly invalid: {
188
+ readonly type: "boolean";
189
+ readonly category: "State";
190
+ readonly label: "Invalid";
191
+ readonly default: false;
192
+ readonly authoring: {
193
+ readonly authority: "instance";
194
+ };
195
+ };
196
+ readonly readOnly: {
197
+ readonly type: "boolean";
198
+ readonly category: "State";
199
+ readonly label: "Read only";
200
+ readonly default: false;
201
+ readonly authoring: {
202
+ readonly authority: "instance";
203
+ };
204
+ };
205
+ readonly disabled: {
206
+ readonly type: "boolean";
207
+ readonly category: "State";
208
+ readonly label: "Disabled";
209
+ readonly default: false;
210
+ readonly authoring: {
211
+ readonly authority: "instance";
212
+ };
213
+ };
214
+ };
16
215
  };
17
216
  //# sourceMappingURL=meta.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"meta.d.ts","sourceRoot":"","sources":["../../../src/components/radio/meta.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,SAAS;;;;;;;CAOgB,CAAC;AAEvC,eAAO,MAAM,cAAc;;;;;;;CAOW,CAAC"}
1
+ {"version":3,"file":"meta.d.ts","sourceRoot":"","sources":["../../../src/components/radio/meta.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,SAAS;;;;;;;CAOgB,CAAC;AAEvC,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsKW,CAAC"}
@@ -1,17 +1,181 @@
1
+ import { ZORA_COLORS } from '../../internal/colorModel';
2
+ const themeAuthoring = {
3
+ authority: 'theme',
4
+ scope: 'component',
5
+ allowInstanceOverride: true,
6
+ };
1
7
  export const radioMeta = {
2
8
  name: 'Radio',
3
9
  category: 'component',
4
10
  directManifestNode: false,
5
11
  allowedChildren: [],
6
- note: 'Form control component; not represented as a manifest node in v1.',
12
+ note: 'Composition-level radio control; author repeated single-choice UI through RadioGroup.',
7
13
  props: {},
8
14
  };
9
15
  export const radioGroupMeta = {
10
16
  name: 'RadioGroup',
11
17
  category: 'component',
12
- directManifestNode: false,
18
+ description: 'Single-choice group with inline or card presentation while preserving radio semantics.',
19
+ directManifestNode: true,
13
20
  allowedChildren: [],
14
- note: 'Form control component; not represented as a manifest node in v1.',
15
- props: {},
21
+ blueprint: {
22
+ label: 'Radio group',
23
+ defaultProps: {
24
+ value: 'option-b',
25
+ options: [
26
+ { value: 'option-a', label: 'First option', description: 'Supporting detail' },
27
+ { value: 'option-b', label: 'Second option', description: 'Supporting detail' },
28
+ { value: 'option-c', label: 'Third option', description: 'Supporting detail' },
29
+ ],
30
+ orientation: 'vertical',
31
+ presentation: 'inline',
32
+ invalid: false,
33
+ readOnly: false,
34
+ disabled: false,
35
+ },
36
+ },
37
+ bindings: {
38
+ props: {
39
+ value: {
40
+ label: 'Value',
41
+ description: 'Currently selected option value.',
42
+ value: { type: 'string' },
43
+ acceptsFallback: true,
44
+ acceptsTransforms: true,
45
+ },
46
+ disabled: {
47
+ label: 'Disabled',
48
+ description: 'Whether the complete radio group is disabled.',
49
+ value: { type: 'boolean' },
50
+ acceptsFallback: true,
51
+ acceptsTransforms: true,
52
+ },
53
+ },
54
+ events: {
55
+ valueChange: {
56
+ label: 'Value change',
57
+ description: 'Runs when the selected radio value changes.',
58
+ payload: {
59
+ eventType: 'radioGroup.valueChange',
60
+ fields: [{ path: 'value', type: 'string', label: 'Value' }],
61
+ },
62
+ },
63
+ },
64
+ },
65
+ events: {
66
+ valueChange: {
67
+ label: 'Value change',
68
+ eventType: 'radioGroup.valueChange',
69
+ description: 'Emitted when the selected radio value changes.',
70
+ payloadFields: [{ path: 'value', type: 'string', label: 'Value' }],
71
+ },
72
+ },
73
+ props: {
74
+ value: {
75
+ type: 'string',
76
+ category: 'State',
77
+ label: 'Value',
78
+ authoring: { authority: 'instance' },
79
+ },
80
+ options: {
81
+ type: 'array',
82
+ category: 'Content',
83
+ label: 'Options',
84
+ itemSchema: [
85
+ {
86
+ key: 'value',
87
+ schema: {
88
+ type: 'string',
89
+ category: 'Content',
90
+ label: 'Value',
91
+ },
92
+ },
93
+ {
94
+ key: 'label',
95
+ schema: {
96
+ type: 'string',
97
+ category: 'Content',
98
+ label: 'Label',
99
+ },
100
+ },
101
+ {
102
+ key: 'description',
103
+ schema: {
104
+ type: 'string',
105
+ category: 'Content',
106
+ label: 'Description',
107
+ },
108
+ },
109
+ {
110
+ key: 'disabled',
111
+ schema: {
112
+ type: 'boolean',
113
+ category: 'State',
114
+ label: 'Disabled',
115
+ default: false,
116
+ },
117
+ },
118
+ ],
119
+ authoring: { authority: 'instance' },
120
+ },
121
+ orientation: {
122
+ type: 'enum',
123
+ category: 'Layout',
124
+ label: 'Orientation',
125
+ enum: ['horizontal', 'vertical'],
126
+ default: 'vertical',
127
+ authoring: { authority: 'instance' },
128
+ },
129
+ gap: {
130
+ type: 'enum',
131
+ category: 'Layout',
132
+ label: 'Gap',
133
+ enum: ['xs', 's', 'm', 'l'],
134
+ authoring: themeAuthoring,
135
+ },
136
+ presentation: {
137
+ type: 'enum',
138
+ category: 'Style',
139
+ label: 'Presentation',
140
+ enum: ['inline', 'card'],
141
+ default: 'inline',
142
+ authoring: { authority: 'instance' },
143
+ },
144
+ color: {
145
+ type: 'enum',
146
+ category: 'Style',
147
+ label: 'Color',
148
+ enum: [...ZORA_COLORS],
149
+ authoring: themeAuthoring,
150
+ },
151
+ size: {
152
+ type: 'enum',
153
+ category: 'Style',
154
+ label: 'Size',
155
+ enum: ['s', 'm', 'l'],
156
+ authoring: themeAuthoring,
157
+ },
158
+ invalid: {
159
+ type: 'boolean',
160
+ category: 'State',
161
+ label: 'Invalid',
162
+ default: false,
163
+ authoring: { authority: 'instance' },
164
+ },
165
+ readOnly: {
166
+ type: 'boolean',
167
+ category: 'State',
168
+ label: 'Read only',
169
+ default: false,
170
+ authoring: { authority: 'instance' },
171
+ },
172
+ disabled: {
173
+ type: 'boolean',
174
+ category: 'State',
175
+ label: 'Disabled',
176
+ default: false,
177
+ authoring: { authority: 'instance' },
178
+ },
179
+ },
16
180
  };
17
181
  //# sourceMappingURL=meta.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"meta.js","sourceRoot":"","sources":["../../../src/components/radio/meta.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,WAAW;IACrB,kBAAkB,EAAE,KAAK;IACzB,eAAe,EAAE,EAAE;IACnB,IAAI,EAAE,mEAAmE;IACzE,KAAK,EAAE,EAAE;CAC2B,CAAC;AAEvC,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,IAAI,EAAE,YAAY;IAClB,QAAQ,EAAE,WAAW;IACrB,kBAAkB,EAAE,KAAK;IACzB,eAAe,EAAE,EAAE;IACnB,IAAI,EAAE,mEAAmE;IACzE,KAAK,EAAE,EAAE;CAC2B,CAAC","sourcesContent":["import type { ZoraComponentMeta } from '../../metadata';\n\nexport const radioMeta = {\n name: 'Radio',\n category: 'component',\n directManifestNode: false,\n allowedChildren: [],\n note: 'Form control component; not represented as a manifest node in v1.',\n props: {},\n} as const satisfies ZoraComponentMeta;\n\nexport const radioGroupMeta = {\n name: 'RadioGroup',\n category: 'component',\n directManifestNode: false,\n allowedChildren: [],\n note: 'Form control component; not represented as a manifest node in v1.',\n props: {},\n} as const satisfies ZoraComponentMeta;\n"]}
1
+ {"version":3,"file":"meta.js","sourceRoot":"","sources":["../../../src/components/radio/meta.ts"],"names":[],"mappings":"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","sourcesContent":["import { ZORA_COLORS } from '../../internal/colorModel';\nimport type { ZoraComponentMeta } from '../../metadata';\n\nconst themeAuthoring = {\n authority: 'theme',\n scope: 'component',\n allowInstanceOverride: true,\n} as const;\n\nexport const radioMeta = {\n name: 'Radio',\n category: 'component',\n directManifestNode: false,\n allowedChildren: [],\n note: 'Composition-level radio control; author repeated single-choice UI through RadioGroup.',\n props: {},\n} as const satisfies ZoraComponentMeta;\n\nexport const radioGroupMeta = {\n name: 'RadioGroup',\n category: 'component',\n description:\n 'Single-choice group with inline or card presentation while preserving radio semantics.',\n directManifestNode: true,\n allowedChildren: [],\n blueprint: {\n label: 'Radio group',\n defaultProps: {\n value: 'option-b',\n options: [\n { value: 'option-a', label: 'First option', description: 'Supporting detail' },\n { value: 'option-b', label: 'Second option', description: 'Supporting detail' },\n { value: 'option-c', label: 'Third option', description: 'Supporting detail' },\n ],\n orientation: 'vertical',\n presentation: 'inline',\n invalid: false,\n readOnly: false,\n disabled: false,\n },\n },\n bindings: {\n props: {\n value: {\n label: 'Value',\n description: 'Currently selected option value.',\n value: { type: 'string' },\n acceptsFallback: true,\n acceptsTransforms: true,\n },\n disabled: {\n label: 'Disabled',\n description: 'Whether the complete radio group is disabled.',\n value: { type: 'boolean' },\n acceptsFallback: true,\n acceptsTransforms: true,\n },\n },\n events: {\n valueChange: {\n label: 'Value change',\n description: 'Runs when the selected radio value changes.',\n payload: {\n eventType: 'radioGroup.valueChange',\n fields: [{ path: 'value', type: 'string', label: 'Value' }],\n },\n },\n },\n },\n events: {\n valueChange: {\n label: 'Value change',\n eventType: 'radioGroup.valueChange',\n description: 'Emitted when the selected radio value changes.',\n payloadFields: [{ path: 'value', type: 'string', label: 'Value' }],\n },\n },\n props: {\n value: {\n type: 'string',\n category: 'State',\n label: 'Value',\n authoring: { authority: 'instance' },\n },\n options: {\n type: 'array',\n category: 'Content',\n label: 'Options',\n itemSchema: [\n {\n key: 'value',\n schema: {\n type: 'string',\n category: 'Content',\n label: 'Value',\n },\n },\n {\n key: 'label',\n schema: {\n type: 'string',\n category: 'Content',\n label: 'Label',\n },\n },\n {\n key: 'description',\n schema: {\n type: 'string',\n category: 'Content',\n label: 'Description',\n },\n },\n {\n key: 'disabled',\n schema: {\n type: 'boolean',\n category: 'State',\n label: 'Disabled',\n default: false,\n },\n },\n ],\n authoring: { authority: 'instance' },\n },\n orientation: {\n type: 'enum',\n category: 'Layout',\n label: 'Orientation',\n enum: ['horizontal', 'vertical'],\n default: 'vertical',\n authoring: { authority: 'instance' },\n },\n gap: {\n type: 'enum',\n category: 'Layout',\n label: 'Gap',\n enum: ['xs', 's', 'm', 'l'],\n authoring: themeAuthoring,\n },\n presentation: {\n type: 'enum',\n category: 'Style',\n label: 'Presentation',\n enum: ['inline', 'card'],\n default: 'inline',\n authoring: { authority: 'instance' },\n },\n color: {\n type: 'enum',\n category: 'Style',\n label: 'Color',\n enum: [...ZORA_COLORS],\n authoring: themeAuthoring,\n },\n size: {\n type: 'enum',\n category: 'Style',\n label: 'Size',\n enum: ['s', 'm', 'l'],\n authoring: themeAuthoring,\n },\n invalid: {\n type: 'boolean',\n category: 'State',\n label: 'Invalid',\n default: false,\n authoring: { authority: 'instance' },\n },\n readOnly: {\n type: 'boolean',\n category: 'State',\n label: 'Read only',\n default: false,\n authoring: { authority: 'instance' },\n },\n disabled: {\n type: 'boolean',\n category: 'State',\n label: 'Disabled',\n default: false,\n authoring: { authority: 'instance' },\n },\n },\n} as const satisfies ZoraComponentMeta;\n"]}
@@ -0,0 +1,20 @@
1
+ import type { ThemeRecipeOverrideValue } from '@ankhorage/contracts';
2
+ import type { ZoraColor } from '../../internal/colorModel';
3
+ import type { RadioGroupProps } from './types';
4
+ type RadioGroupGap = NonNullable<RadioGroupProps<string>['gap']>;
5
+ type RadioGroupSize = NonNullable<RadioGroupProps<string>['size']>;
6
+ /***
7
+ * Resolves RadioGroup presentation props from instance overrides and the active theme recipe.
8
+ */
9
+ export declare function resolveRadioGroupThemeRecipe(input: {
10
+ readonly gap?: RadioGroupGap;
11
+ readonly color?: ZoraColor;
12
+ readonly size?: RadioGroupSize;
13
+ readonly themeFields: Readonly<Record<string, ThemeRecipeOverrideValue>>;
14
+ }): {
15
+ readonly gap: RadioGroupGap;
16
+ readonly color: "primary" | "secondary" | "tertiary" | "quaternary" | "neutral" | "error" | "success" | "warning" | "info" | "danger";
17
+ readonly size: RadioGroupSize;
18
+ };
19
+ export {};
20
+ //# sourceMappingURL=resolveRadioGroupThemeRecipe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveRadioGroupThemeRecipe.d.ts","sourceRoot":"","sources":["../../../src/components/radio/resolveRadioGroupThemeRecipe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAErE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,KAAK,aAAa,GAAG,WAAW,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AACjE,KAAK,cAAc,GAAG,WAAW,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAEnE;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IAClD,QAAQ,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC,CAAC;CAC1E;;;;EAMA"}
@@ -0,0 +1,34 @@
1
+ /***
2
+ * Resolves RadioGroup presentation props from instance overrides and the active theme recipe.
3
+ */
4
+ export function resolveRadioGroupThemeRecipe(input) {
5
+ return {
6
+ gap: input.gap ?? readGap(input.themeFields.gap) ?? 's',
7
+ color: input.color ?? readColor(input.themeFields.color) ?? 'primary',
8
+ size: input.size ?? readSize(input.themeFields.size) ?? 'm',
9
+ };
10
+ }
11
+ /*** Reads a valid RadioGroup gap from a theme recipe value. */
12
+ function readGap(value) {
13
+ if (value === 'xs' || value === 's' || value === 'm' || value === 'l')
14
+ return value;
15
+ return undefined;
16
+ }
17
+ /*** Reads a valid ZORA color from a theme recipe value. */
18
+ function readColor(value) {
19
+ if (value === 'primary' || value === 'secondary' || value === 'tertiary')
20
+ return value;
21
+ if (value === 'quaternary' || value === 'neutral' || value === 'danger')
22
+ return value;
23
+ if (value === 'success' || value === 'warning' || value === 'error' || value === 'info') {
24
+ return value;
25
+ }
26
+ return undefined;
27
+ }
28
+ /*** Reads a valid RadioGroup control size from a theme recipe value. */
29
+ function readSize(value) {
30
+ if (value === 's' || value === 'm' || value === 'l')
31
+ return value;
32
+ return undefined;
33
+ }
34
+ //# sourceMappingURL=resolveRadioGroupThemeRecipe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveRadioGroupThemeRecipe.js","sourceRoot":"","sources":["../../../src/components/radio/resolveRadioGroupThemeRecipe.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,UAAU,4BAA4B,CAAC,KAK5C;IACC,OAAO;QACL,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,GAAG;QACvD,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,SAAS;QACrE,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,GAAG;KACnD,CAAC;AACb,CAAC;AAED,+DAA+D;AAC/D,SAAS,OAAO,CAAC,KAA2C;IAC1D,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IACpF,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,2DAA2D;AAC3D,SAAS,SAAS,CAAC,KAA2C;IAC5D,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK,UAAU;QAAE,OAAO,KAAK,CAAC;IACvF,IAAI,KAAK,KAAK,YAAY,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtF,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QACxF,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,wEAAwE;AACxE,SAAS,QAAQ,CAAC,KAA2C;IAC3D,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IAClE,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["import type { ThemeRecipeOverrideValue } from '@ankhorage/contracts';\n\nimport type { ZoraColor } from '../../internal/colorModel';\nimport type { RadioGroupProps } from './types';\n\ntype RadioGroupGap = NonNullable<RadioGroupProps<string>['gap']>;\ntype RadioGroupSize = NonNullable<RadioGroupProps<string>['size']>;\n\n/***\n * Resolves RadioGroup presentation props from instance overrides and the active theme recipe.\n */\nexport function resolveRadioGroupThemeRecipe(input: {\n readonly gap?: RadioGroupGap;\n readonly color?: ZoraColor;\n readonly size?: RadioGroupSize;\n readonly themeFields: Readonly<Record<string, ThemeRecipeOverrideValue>>;\n}) {\n return {\n gap: input.gap ?? readGap(input.themeFields.gap) ?? 's',\n color: input.color ?? readColor(input.themeFields.color) ?? 'primary',\n size: input.size ?? readSize(input.themeFields.size) ?? 'm',\n } as const;\n}\n\n/*** Reads a valid RadioGroup gap from a theme recipe value. */\nfunction readGap(value: ThemeRecipeOverrideValue | undefined): RadioGroupGap | undefined {\n if (value === 'xs' || value === 's' || value === 'm' || value === 'l') return value;\n return undefined;\n}\n\n/*** Reads a valid ZORA color from a theme recipe value. */\nfunction readColor(value: ThemeRecipeOverrideValue | undefined): ZoraColor | undefined {\n if (value === 'primary' || value === 'secondary' || value === 'tertiary') return value;\n if (value === 'quaternary' || value === 'neutral' || value === 'danger') return value;\n if (value === 'success' || value === 'warning' || value === 'error' || value === 'info') {\n return value;\n }\n return undefined;\n}\n\n/*** Reads a valid RadioGroup control size from a theme recipe value. */\nfunction readSize(value: ThemeRecipeOverrideValue | undefined): RadioGroupSize | undefined {\n if (value === 's' || value === 'm' || value === 'l') return value;\n return undefined;\n}\n"]}
@@ -0,0 +1,26 @@
1
+ export declare const radioGroupThemeRecipeMeta: {
2
+ readonly name: "RadioGroup";
3
+ readonly kind: "component";
4
+ readonly description: "Maps shared selection-control presentation choices into RadioGroup defaults.";
5
+ readonly fields: {
6
+ readonly gap: {
7
+ readonly type: "choice";
8
+ readonly label: "Gap";
9
+ readonly options: readonly ["xs", "s", "m", "l"];
10
+ readonly default: "s";
11
+ };
12
+ readonly color: {
13
+ readonly type: "choice";
14
+ readonly label: "Color";
15
+ readonly options: readonly ["primary", "secondary", "tertiary", "quaternary", "neutral", "success", "warning", "error", "info", "danger"];
16
+ readonly default: "primary";
17
+ };
18
+ readonly size: {
19
+ readonly type: "choice";
20
+ readonly label: "Size";
21
+ readonly options: readonly ["s", "m", "l"];
22
+ readonly default: "m";
23
+ };
24
+ };
25
+ };
26
+ //# sourceMappingURL=themeRecipeMeta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"themeRecipeMeta.d.ts","sourceRoot":"","sources":["../../../src/components/radio/themeRecipeMeta.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;CAwBE,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { ZORA_COLORS } from '../../internal/colorModel';
2
+ export const radioGroupThemeRecipeMeta = {
3
+ name: 'RadioGroup',
4
+ kind: 'component',
5
+ description: 'Maps shared selection-control presentation choices into RadioGroup defaults.',
6
+ fields: {
7
+ gap: {
8
+ type: 'choice',
9
+ label: 'Gap',
10
+ options: ['xs', 's', 'm', 'l'],
11
+ default: 's',
12
+ },
13
+ color: {
14
+ type: 'choice',
15
+ label: 'Color',
16
+ options: ZORA_COLORS,
17
+ default: 'primary',
18
+ },
19
+ size: {
20
+ type: 'choice',
21
+ label: 'Size',
22
+ options: ['s', 'm', 'l'],
23
+ default: 'm',
24
+ },
25
+ },
26
+ };
27
+ //# sourceMappingURL=themeRecipeMeta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"themeRecipeMeta.js","sourceRoot":"","sources":["../../../src/components/radio/themeRecipeMeta.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAGxD,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,IAAI,EAAE,YAAY;IAClB,IAAI,EAAE,WAAW;IACjB,WAAW,EAAE,8EAA8E;IAC3F,MAAM,EAAE;QACN,GAAG,EAAE;YACH,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;YAC9B,OAAO,EAAE,GAAG;SACb;QACD,KAAK,EAAE;YACL,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,WAAW;YACpB,OAAO,EAAE,SAAS;SACnB;QACD,IAAI,EAAE;YACJ,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,MAAM;YACb,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;YACxB,OAAO,EAAE,GAAG;SACb;KACF;CACqC,CAAC","sourcesContent":["import { ZORA_COLORS } from '../../internal/colorModel';\nimport type { ZoraThemeRecipeMeta } from '../../metadata/themeRecipeTypes';\n\nexport const radioGroupThemeRecipeMeta = {\n name: 'RadioGroup',\n kind: 'component',\n description: 'Maps shared selection-control presentation choices into RadioGroup defaults.',\n fields: {\n gap: {\n type: 'choice',\n label: 'Gap',\n options: ['xs', 's', 'm', 'l'],\n default: 's',\n },\n color: {\n type: 'choice',\n label: 'Color',\n options: ZORA_COLORS,\n default: 'primary',\n },\n size: {\n type: 'choice',\n label: 'Size',\n options: ['s', 'm', 'l'],\n default: 'm',\n },\n },\n} as const satisfies ZoraThemeRecipeMeta;\n"]}
@@ -16,5 +16,6 @@ export interface RadioGroupProps<TValue extends string> extends ZoraBaseProps, P
16
16
  options: readonly RadioGroupOption<TValue>[];
17
17
  orientation?: 'horizontal' | 'vertical';
18
18
  gap?: 'xs' | 's' | 'm' | 'l';
19
+ presentation?: 'inline' | 'card';
19
20
  }
20
21
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/radio/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,WAAW,UAAW,SAAQ,aAAa,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;CAAG;AAEjG,MAAM,WAAW,gBAAgB,CAAC,MAAM,SAAS,MAAM;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe,CAAC,MAAM,SAAS,MAAM,CACpD,SACE,aAAa,EACb,IAAI,CACF,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC,EAC3C,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CACvD;IACH,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,EAAE,SAAS,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;IAC7C,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACxC,GAAG,CAAC,EAAE,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;CAC9B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/radio/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,WAAW,UAAW,SAAQ,aAAa,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;CAAG;AAEjG,MAAM,WAAW,gBAAgB,CAAC,MAAM,SAAS,MAAM;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe,CAAC,MAAM,SAAS,MAAM,CACpD,SACE,aAAa,EACb,IAAI,CACF,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC,EAC3C,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CACvD;IACH,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,EAAE,SAAS,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC;IAC7C,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACxC,GAAG,CAAC,EAAE,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAC7B,YAAY,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CAClC"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/components/radio/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { RadioProps as SurfaceRadioProps } from '@ankhorage/surface';\nimport type React from 'react';\n\nimport type { ZoraBaseProps } from '../../theme/ZoraBaseProps';\n\nexport interface RadioProps extends ZoraBaseProps, Omit<SurfaceRadioProps, 'mode' | 'themeId'> {}\n\nexport interface RadioGroupOption<TValue extends string> {\n value: TValue;\n label: React.ReactNode;\n description?: React.ReactNode;\n disabled?: boolean;\n testID?: string;\n}\n\nexport interface RadioGroupProps<TValue extends string>\n extends\n ZoraBaseProps,\n Pick<\n Omit<SurfaceRadioProps, 'mode' | 'themeId'>,\n 'color' | 'size' | 'invalid' | 'readOnly' | 'disabled'\n > {\n value: TValue;\n onValueChange: (value: TValue) => void;\n options: readonly RadioGroupOption<TValue>[];\n orientation?: 'horizontal' | 'vertical';\n gap?: 'xs' | 's' | 'm' | 'l';\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/components/radio/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { RadioProps as SurfaceRadioProps } from '@ankhorage/surface';\nimport type React from 'react';\n\nimport type { ZoraBaseProps } from '../../theme/ZoraBaseProps';\n\nexport interface RadioProps extends ZoraBaseProps, Omit<SurfaceRadioProps, 'mode' | 'themeId'> {}\n\nexport interface RadioGroupOption<TValue extends string> {\n value: TValue;\n label: React.ReactNode;\n description?: React.ReactNode;\n disabled?: boolean;\n testID?: string;\n}\n\nexport interface RadioGroupProps<TValue extends string>\n extends\n ZoraBaseProps,\n Pick<\n Omit<SurfaceRadioProps, 'mode' | 'themeId'>,\n 'color' | 'size' | 'invalid' | 'readOnly' | 'disabled'\n > {\n value: TValue;\n onValueChange: (value: TValue) => void;\n options: readonly RadioGroupOption<TValue>[];\n orientation?: 'horizontal' | 'vertical';\n gap?: 'xs' | 's' | 'm' | 'l';\n presentation?: 'inline' | 'card';\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"themeRecipeMeta.d.ts","sourceRoot":"","sources":["../../src/metadata/themeRecipeMeta.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AAEtE,eAAO,MAAM,sBAAsB,EAAE,2BAMpC,CAAC"}
1
+ {"version":3,"file":"themeRecipeMeta.d.ts","sourceRoot":"","sources":["../../src/metadata/themeRecipeMeta.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AAEtE,eAAO,MAAM,sBAAsB,EAAE,2BAOpC,CAAC"}
@@ -1,12 +1,14 @@
1
1
  import { buttonThemeRecipeMeta } from '../components/button/themeRecipeMeta';
2
2
  import { cardThemeRecipeMeta } from '../components/card/themeRecipeMeta';
3
3
  import { headingThemeRecipeMeta } from '../components/heading/themeRecipeMeta';
4
+ import { radioGroupThemeRecipeMeta } from '../components/radio/themeRecipeMeta';
4
5
  import { textThemeRecipeMeta } from '../components/text/themeRecipeMeta';
5
6
  import { panelThemeRecipeMeta } from '../patterns/panel/themeRecipeMeta';
6
7
  export const ZORA_THEME_RECIPE_META = {
7
8
  Button: buttonThemeRecipeMeta,
8
9
  Card: cardThemeRecipeMeta,
9
10
  Heading: headingThemeRecipeMeta,
11
+ RadioGroup: radioGroupThemeRecipeMeta,
10
12
  Text: textThemeRecipeMeta,
11
13
  Panel: panelThemeRecipeMeta,
12
14
  };
@@ -1 +1 @@
1
- {"version":3,"file":"themeRecipeMeta.js","sourceRoot":"","sources":["../../src/metadata/themeRecipeMeta.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAGzE,MAAM,CAAC,MAAM,sBAAsB,GAAgC;IACjE,MAAM,EAAE,qBAAqB;IAC7B,IAAI,EAAE,mBAAmB;IACzB,OAAO,EAAE,sBAAsB;IAC/B,IAAI,EAAE,mBAAmB;IACzB,KAAK,EAAE,oBAAoB;CAC5B,CAAC","sourcesContent":["import { buttonThemeRecipeMeta } from '../components/button/themeRecipeMeta';\nimport { cardThemeRecipeMeta } from '../components/card/themeRecipeMeta';\nimport { headingThemeRecipeMeta } from '../components/heading/themeRecipeMeta';\nimport { textThemeRecipeMeta } from '../components/text/themeRecipeMeta';\nimport { panelThemeRecipeMeta } from '../patterns/panel/themeRecipeMeta';\nimport type { ZoraThemeRecipeMetaRegistry } from './themeRecipeTypes';\n\nexport const ZORA_THEME_RECIPE_META: ZoraThemeRecipeMetaRegistry = {\n Button: buttonThemeRecipeMeta,\n Card: cardThemeRecipeMeta,\n Heading: headingThemeRecipeMeta,\n Text: textThemeRecipeMeta,\n Panel: panelThemeRecipeMeta,\n};\n"]}
1
+ {"version":3,"file":"themeRecipeMeta.js","sourceRoot":"","sources":["../../src/metadata/themeRecipeMeta.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAGzE,MAAM,CAAC,MAAM,sBAAsB,GAAgC;IACjE,MAAM,EAAE,qBAAqB;IAC7B,IAAI,EAAE,mBAAmB;IACzB,OAAO,EAAE,sBAAsB;IAC/B,UAAU,EAAE,yBAAyB;IACrC,IAAI,EAAE,mBAAmB;IACzB,KAAK,EAAE,oBAAoB;CAC5B,CAAC","sourcesContent":["import { buttonThemeRecipeMeta } from '../components/button/themeRecipeMeta';\nimport { cardThemeRecipeMeta } from '../components/card/themeRecipeMeta';\nimport { headingThemeRecipeMeta } from '../components/heading/themeRecipeMeta';\nimport { radioGroupThemeRecipeMeta } from '../components/radio/themeRecipeMeta';\nimport { textThemeRecipeMeta } from '../components/text/themeRecipeMeta';\nimport { panelThemeRecipeMeta } from '../patterns/panel/themeRecipeMeta';\nimport type { ZoraThemeRecipeMetaRegistry } from './themeRecipeTypes';\n\nexport const ZORA_THEME_RECIPE_META: ZoraThemeRecipeMetaRegistry = {\n Button: buttonThemeRecipeMeta,\n Card: cardThemeRecipeMeta,\n Heading: headingThemeRecipeMeta,\n RadioGroup: radioGroupThemeRecipeMeta,\n Text: textThemeRecipeMeta,\n Panel: panelThemeRecipeMeta,\n};\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ankhorage/zora",
3
3
  "type": "module",
4
- "version": "4.3.0",
4
+ "version": "4.4.0",
5
5
  "description": "Opinionated React Native and React Native Web UI kit built on @ankhorage/surface.",
6
6
  "homepage": "https://github.com/ankhorage/zora#readme",
7
7
  "bugs": {
@@ -0,0 +1,31 @@
1
+ import { expect, test } from 'bun:test';
2
+
3
+ import { resolveRadioGroupThemeRecipe } from './resolveRadioGroupThemeRecipe';
4
+
5
+ test('resolves RadioGroup theme defaults while preserving instance overrides', () => {
6
+ expect(resolveRadioGroupThemeRecipe({ themeFields: {} })).toEqual({
7
+ gap: 's',
8
+ color: 'primary',
9
+ size: 'm',
10
+ });
11
+
12
+ expect(
13
+ resolveRadioGroupThemeRecipe({
14
+ gap: 'l',
15
+ color: 'danger',
16
+ size: 's',
17
+ themeFields: { gap: 'xs', color: 'secondary', size: 'l' },
18
+ }),
19
+ ).toEqual({ gap: 'l', color: 'danger', size: 's' });
20
+ });
21
+
22
+ test('card presentation stays on the RadioGroup and Surface Radio semantic path', async () => {
23
+ const source = await Bun.file('src/components/radio/RadioGroup.tsx').text();
24
+
25
+ expect(source).toContain('accessibilityRole="radiogroup"');
26
+ expect(source).toContain('checked={value === option.value}');
27
+ expect(source).toContain('<Radio');
28
+ expect(source).toContain("const isCard = presentation === 'card';");
29
+ expect(source).not.toContain('<Button');
30
+ expect(source).not.toContain('SelectableItem');
31
+ });
@@ -1,12 +1,17 @@
1
- import { Radio } from '@ankhorage/surface';
1
+ import { Radio, useTheme } from '@ankhorage/surface';
2
2
  import React from 'react';
3
3
  import { View } from 'react-native';
4
4
 
5
5
  import { Stack } from '../../foundation';
6
+ import { useZoraThemeRecipe } from '../../theme/useZoraThemeRecipe';
6
7
  import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
7
8
  import { Text } from '../text';
9
+ import { resolveRadioGroupThemeRecipe } from './resolveRadioGroupThemeRecipe';
8
10
  import type { RadioGroupOption, RadioGroupProps } from './types';
9
11
 
12
+ /***
13
+ * Resolves the themed radio-group presentation within the active ZORA scope.
14
+ */
10
15
  function RadioGroupInner<TValue extends string>({
11
16
  themeId: _themeId,
12
17
  mode: _mode,
@@ -14,9 +19,10 @@ function RadioGroupInner<TValue extends string>({
14
19
  onValueChange,
15
20
  options,
16
21
  orientation = 'vertical',
17
- gap = 's',
18
- color = 'primary',
19
- size = 'm',
22
+ gap,
23
+ presentation = 'inline',
24
+ color,
25
+ size,
20
26
  invalid = false,
21
27
  readOnly = false,
22
28
  disabled = false,
@@ -24,6 +30,8 @@ function RadioGroupInner<TValue extends string>({
24
30
  interactionPolicy,
25
31
  }: RadioGroupProps<TValue>) {
26
32
  const isHorizontal = orientation === 'horizontal';
33
+ const themeFields = useZoraThemeRecipe('RadioGroup');
34
+ const recipe = resolveRadioGroupThemeRecipe({ gap, color, size, themeFields });
27
35
 
28
36
  return (
29
37
  <View
@@ -36,7 +44,7 @@ function RadioGroupInner<TValue extends string>({
36
44
  >
37
45
  <Stack
38
46
  direction={isHorizontal ? 'row' : 'column'}
39
- gap={gap}
47
+ gap={recipe.gap}
40
48
  wrap={isHorizontal ? 'wrap' : 'nowrap'}
41
49
  >
42
50
  {options.map((option) => (
@@ -47,8 +55,10 @@ function RadioGroupInner<TValue extends string>({
47
55
  disabled={disabled || option.disabled === true}
48
56
  invalid={invalid}
49
57
  readOnly={readOnly}
50
- size={size}
51
- color={color}
58
+ size={recipe.size}
59
+ color={recipe.color}
60
+ orientation={orientation}
61
+ presentation={presentation}
52
62
  onSelect={onValueChange}
53
63
  interactionPolicy={interactionPolicy}
54
64
  />
@@ -63,6 +73,9 @@ function RadioGroupInner<TValue extends string>({
63
73
  */
64
74
  export const RadioGroup = withZoraThemeScope(RadioGroupInner);
65
75
 
76
+ /***
77
+ * Renders one option through the single Surface Radio interaction boundary.
78
+ */
66
79
  function RadioGroupItem<TValue extends string>({
67
80
  option,
68
81
  checked,
@@ -71,6 +84,8 @@ function RadioGroupItem<TValue extends string>({
71
84
  readOnly,
72
85
  size,
73
86
  color,
87
+ orientation,
88
+ presentation,
74
89
  onSelect,
75
90
  interactionPolicy,
76
91
  }: {
@@ -81,10 +96,14 @@ function RadioGroupItem<TValue extends string>({
81
96
  readOnly: boolean;
82
97
  size: NonNullable<RadioGroupProps<TValue>['size']>;
83
98
  color: NonNullable<RadioGroupProps<TValue>['color']>;
99
+ orientation: NonNullable<RadioGroupProps<TValue>['orientation']>;
100
+ presentation: NonNullable<RadioGroupProps<TValue>['presentation']>;
84
101
  onSelect: (value: TValue) => void;
85
102
  interactionPolicy: RadioGroupProps<TValue>['interactionPolicy'];
86
103
  }) {
104
+ const { theme } = useTheme();
87
105
  const passive = interactionPolicy === 'passive';
106
+ const isCard = presentation === 'card';
88
107
 
89
108
  return (
90
109
  <Radio
@@ -96,13 +115,31 @@ function RadioGroupItem<TValue extends string>({
96
115
  size={size}
97
116
  color={color}
98
117
  testID={option.testID}
118
+ bg={
119
+ isCard
120
+ ? checked
121
+ ? theme.semantics.selection.background
122
+ : theme.semantics.surface.default
123
+ : undefined
124
+ }
125
+ borderColor={
126
+ isCard
127
+ ? checked
128
+ ? theme.semantics.selection.border
129
+ : theme.semantics.border.default
130
+ : undefined
131
+ }
132
+ borderWidth={isCard ? 1 : undefined}
133
+ p={isCard ? 'm' : undefined}
134
+ radius={isCard ? 'l' : undefined}
135
+ width={isCard && orientation === 'vertical' ? '100%' : undefined}
99
136
  onCheckedChange={(nextChecked) => {
100
137
  if (passive) return;
101
138
  if (nextChecked) onSelect(option.value);
102
139
  }}
103
140
  >
104
141
  <Stack gap="xs">
105
- <Text>{option.label}</Text>
142
+ <Text weight={isCard ? 'semiBold' : undefined}>{option.label}</Text>
106
143
  {option.description ? (
107
144
  <Text emphasis="muted" variant="caption">
108
145
  {option.description}
@@ -1,19 +1,185 @@
1
+ import { ZORA_COLORS } from '../../internal/colorModel';
1
2
  import type { ZoraComponentMeta } from '../../metadata';
2
3
 
4
+ const themeAuthoring = {
5
+ authority: 'theme',
6
+ scope: 'component',
7
+ allowInstanceOverride: true,
8
+ } as const;
9
+
3
10
  export const radioMeta = {
4
11
  name: 'Radio',
5
12
  category: 'component',
6
13
  directManifestNode: false,
7
14
  allowedChildren: [],
8
- note: 'Form control component; not represented as a manifest node in v1.',
15
+ note: 'Composition-level radio control; author repeated single-choice UI through RadioGroup.',
9
16
  props: {},
10
17
  } as const satisfies ZoraComponentMeta;
11
18
 
12
19
  export const radioGroupMeta = {
13
20
  name: 'RadioGroup',
14
21
  category: 'component',
15
- directManifestNode: false,
22
+ description:
23
+ 'Single-choice group with inline or card presentation while preserving radio semantics.',
24
+ directManifestNode: true,
16
25
  allowedChildren: [],
17
- note: 'Form control component; not represented as a manifest node in v1.',
18
- props: {},
26
+ blueprint: {
27
+ label: 'Radio group',
28
+ defaultProps: {
29
+ value: 'option-b',
30
+ options: [
31
+ { value: 'option-a', label: 'First option', description: 'Supporting detail' },
32
+ { value: 'option-b', label: 'Second option', description: 'Supporting detail' },
33
+ { value: 'option-c', label: 'Third option', description: 'Supporting detail' },
34
+ ],
35
+ orientation: 'vertical',
36
+ presentation: 'inline',
37
+ invalid: false,
38
+ readOnly: false,
39
+ disabled: false,
40
+ },
41
+ },
42
+ bindings: {
43
+ props: {
44
+ value: {
45
+ label: 'Value',
46
+ description: 'Currently selected option value.',
47
+ value: { type: 'string' },
48
+ acceptsFallback: true,
49
+ acceptsTransforms: true,
50
+ },
51
+ disabled: {
52
+ label: 'Disabled',
53
+ description: 'Whether the complete radio group is disabled.',
54
+ value: { type: 'boolean' },
55
+ acceptsFallback: true,
56
+ acceptsTransforms: true,
57
+ },
58
+ },
59
+ events: {
60
+ valueChange: {
61
+ label: 'Value change',
62
+ description: 'Runs when the selected radio value changes.',
63
+ payload: {
64
+ eventType: 'radioGroup.valueChange',
65
+ fields: [{ path: 'value', type: 'string', label: 'Value' }],
66
+ },
67
+ },
68
+ },
69
+ },
70
+ events: {
71
+ valueChange: {
72
+ label: 'Value change',
73
+ eventType: 'radioGroup.valueChange',
74
+ description: 'Emitted when the selected radio value changes.',
75
+ payloadFields: [{ path: 'value', type: 'string', label: 'Value' }],
76
+ },
77
+ },
78
+ props: {
79
+ value: {
80
+ type: 'string',
81
+ category: 'State',
82
+ label: 'Value',
83
+ authoring: { authority: 'instance' },
84
+ },
85
+ options: {
86
+ type: 'array',
87
+ category: 'Content',
88
+ label: 'Options',
89
+ itemSchema: [
90
+ {
91
+ key: 'value',
92
+ schema: {
93
+ type: 'string',
94
+ category: 'Content',
95
+ label: 'Value',
96
+ },
97
+ },
98
+ {
99
+ key: 'label',
100
+ schema: {
101
+ type: 'string',
102
+ category: 'Content',
103
+ label: 'Label',
104
+ },
105
+ },
106
+ {
107
+ key: 'description',
108
+ schema: {
109
+ type: 'string',
110
+ category: 'Content',
111
+ label: 'Description',
112
+ },
113
+ },
114
+ {
115
+ key: 'disabled',
116
+ schema: {
117
+ type: 'boolean',
118
+ category: 'State',
119
+ label: 'Disabled',
120
+ default: false,
121
+ },
122
+ },
123
+ ],
124
+ authoring: { authority: 'instance' },
125
+ },
126
+ orientation: {
127
+ type: 'enum',
128
+ category: 'Layout',
129
+ label: 'Orientation',
130
+ enum: ['horizontal', 'vertical'],
131
+ default: 'vertical',
132
+ authoring: { authority: 'instance' },
133
+ },
134
+ gap: {
135
+ type: 'enum',
136
+ category: 'Layout',
137
+ label: 'Gap',
138
+ enum: ['xs', 's', 'm', 'l'],
139
+ authoring: themeAuthoring,
140
+ },
141
+ presentation: {
142
+ type: 'enum',
143
+ category: 'Style',
144
+ label: 'Presentation',
145
+ enum: ['inline', 'card'],
146
+ default: 'inline',
147
+ authoring: { authority: 'instance' },
148
+ },
149
+ color: {
150
+ type: 'enum',
151
+ category: 'Style',
152
+ label: 'Color',
153
+ enum: [...ZORA_COLORS],
154
+ authoring: themeAuthoring,
155
+ },
156
+ size: {
157
+ type: 'enum',
158
+ category: 'Style',
159
+ label: 'Size',
160
+ enum: ['s', 'm', 'l'],
161
+ authoring: themeAuthoring,
162
+ },
163
+ invalid: {
164
+ type: 'boolean',
165
+ category: 'State',
166
+ label: 'Invalid',
167
+ default: false,
168
+ authoring: { authority: 'instance' },
169
+ },
170
+ readOnly: {
171
+ type: 'boolean',
172
+ category: 'State',
173
+ label: 'Read only',
174
+ default: false,
175
+ authoring: { authority: 'instance' },
176
+ },
177
+ disabled: {
178
+ type: 'boolean',
179
+ category: 'State',
180
+ label: 'Disabled',
181
+ default: false,
182
+ authoring: { authority: 'instance' },
183
+ },
184
+ },
19
185
  } as const satisfies ZoraComponentMeta;
@@ -0,0 +1,45 @@
1
+ import type { ThemeRecipeOverrideValue } from '@ankhorage/contracts';
2
+
3
+ import type { ZoraColor } from '../../internal/colorModel';
4
+ import type { RadioGroupProps } from './types';
5
+
6
+ type RadioGroupGap = NonNullable<RadioGroupProps<string>['gap']>;
7
+ type RadioGroupSize = NonNullable<RadioGroupProps<string>['size']>;
8
+
9
+ /***
10
+ * Resolves RadioGroup presentation props from instance overrides and the active theme recipe.
11
+ */
12
+ export function resolveRadioGroupThemeRecipe(input: {
13
+ readonly gap?: RadioGroupGap;
14
+ readonly color?: ZoraColor;
15
+ readonly size?: RadioGroupSize;
16
+ readonly themeFields: Readonly<Record<string, ThemeRecipeOverrideValue>>;
17
+ }) {
18
+ return {
19
+ gap: input.gap ?? readGap(input.themeFields.gap) ?? 's',
20
+ color: input.color ?? readColor(input.themeFields.color) ?? 'primary',
21
+ size: input.size ?? readSize(input.themeFields.size) ?? 'm',
22
+ } as const;
23
+ }
24
+
25
+ /*** Reads a valid RadioGroup gap from a theme recipe value. */
26
+ function readGap(value: ThemeRecipeOverrideValue | undefined): RadioGroupGap | undefined {
27
+ if (value === 'xs' || value === 's' || value === 'm' || value === 'l') return value;
28
+ return undefined;
29
+ }
30
+
31
+ /*** Reads a valid ZORA color from a theme recipe value. */
32
+ function readColor(value: ThemeRecipeOverrideValue | undefined): ZoraColor | undefined {
33
+ if (value === 'primary' || value === 'secondary' || value === 'tertiary') return value;
34
+ if (value === 'quaternary' || value === 'neutral' || value === 'danger') return value;
35
+ if (value === 'success' || value === 'warning' || value === 'error' || value === 'info') {
36
+ return value;
37
+ }
38
+ return undefined;
39
+ }
40
+
41
+ /*** Reads a valid RadioGroup control size from a theme recipe value. */
42
+ function readSize(value: ThemeRecipeOverrideValue | undefined): RadioGroupSize | undefined {
43
+ if (value === 's' || value === 'm' || value === 'l') return value;
44
+ return undefined;
45
+ }
@@ -0,0 +1,28 @@
1
+ import { ZORA_COLORS } from '../../internal/colorModel';
2
+ import type { ZoraThemeRecipeMeta } from '../../metadata/themeRecipeTypes';
3
+
4
+ export const radioGroupThemeRecipeMeta = {
5
+ name: 'RadioGroup',
6
+ kind: 'component',
7
+ description: 'Maps shared selection-control presentation choices into RadioGroup defaults.',
8
+ fields: {
9
+ gap: {
10
+ type: 'choice',
11
+ label: 'Gap',
12
+ options: ['xs', 's', 'm', 'l'],
13
+ default: 's',
14
+ },
15
+ color: {
16
+ type: 'choice',
17
+ label: 'Color',
18
+ options: ZORA_COLORS,
19
+ default: 'primary',
20
+ },
21
+ size: {
22
+ type: 'choice',
23
+ label: 'Size',
24
+ options: ['s', 'm', 'l'],
25
+ default: 'm',
26
+ },
27
+ },
28
+ } as const satisfies ZoraThemeRecipeMeta;
@@ -25,4 +25,5 @@ export interface RadioGroupProps<TValue extends string>
25
25
  options: readonly RadioGroupOption<TValue>[];
26
26
  orientation?: 'horizontal' | 'vertical';
27
27
  gap?: 'xs' | 's' | 'm' | 'l';
28
+ presentation?: 'inline' | 'card';
28
29
  }
@@ -126,6 +126,7 @@ describe('ZORA_COMPONENT_META invariants', () => {
126
126
  'EmptyState',
127
127
  'Hero',
128
128
  'Button',
129
+ 'RadioGroup',
129
130
  'ThemeModeToggle',
130
131
  'Input',
131
132
  'Textarea',
@@ -198,6 +199,26 @@ describe('ZORA_COMPONENT_META invariants', () => {
198
199
  expect(ZORA_COMPONENT_META.Image.props.alt?.authoring).toEqual({ authority: 'instance' });
199
200
  });
200
201
 
202
+ test('RadioGroup is a direct manifest leaf with card presentation and normalized value events', () => {
203
+ const radioGroup = ZORA_COMPONENT_META.RadioGroup;
204
+
205
+ expect(radioGroup.directManifestNode).toBe(true);
206
+ expect(radioGroup.allowedChildren).toEqual([]);
207
+ expect(radioGroup.props.presentation?.enum).toEqual(['inline', 'card']);
208
+ expect(radioGroup.props.options?.type).toBe('array');
209
+ expect(radioGroup.props.options?.itemSchema?.map((item) => item.key)).toEqual([
210
+ 'value',
211
+ 'label',
212
+ 'description',
213
+ 'disabled',
214
+ ]);
215
+ expect(radioGroup.bindings?.props?.value?.value).toEqual({ type: 'string' });
216
+ expect(radioGroup.events?.valueChange?.eventType).toBe('radioGroup.valueChange');
217
+ expect(radioGroup.events?.valueChange?.payloadFields).toEqual([
218
+ { path: 'value', type: 'string', label: 'Value' },
219
+ ]);
220
+ });
221
+
201
222
  test('ThemeModeToggle is a direct manifest leaf with serializable defaults', () => {
202
223
  expect(ZORA_COMPONENT_META.ThemeModeToggle.directManifestNode).toBe(true);
203
224
  expect(ZORA_COMPONENT_META.ThemeModeToggle.allowedChildren).toEqual([]);
@@ -11,6 +11,7 @@ describe('ZORA_THEME_RECIPE_META', () => {
11
11
  'Button',
12
12
  'Card',
13
13
  'Heading',
14
+ 'RadioGroup',
14
15
  'Text',
15
16
  'Panel',
16
17
  ]);
@@ -50,6 +51,9 @@ describe('ZORA_THEME_RECIPE_META', () => {
50
51
  test('keeps recipe defaults aligned with existing runtime behavior', () => {
51
52
  expect(ZORA_THEME_RECIPE_META.Button?.fields.size?.default).toBe('l');
52
53
  expect(ZORA_THEME_RECIPE_META.Card?.fields.padding?.default).toBeUndefined();
54
+ expect(ZORA_THEME_RECIPE_META.RadioGroup?.fields.gap?.default).toBe('s');
55
+ expect(ZORA_THEME_RECIPE_META.RadioGroup?.fields.color?.default).toBe('primary');
56
+ expect(ZORA_THEME_RECIPE_META.RadioGroup?.fields.size?.default).toBe('m');
53
57
  expect(ZORA_THEME_RECIPE_META.Text?.fields.weight?.default).toBeUndefined();
54
58
  expect(ZORA_THEME_RECIPE_META.Heading?.fields.size?.default).toBeUndefined();
55
59
  });
@@ -1,6 +1,7 @@
1
1
  import { buttonThemeRecipeMeta } from '../components/button/themeRecipeMeta';
2
2
  import { cardThemeRecipeMeta } from '../components/card/themeRecipeMeta';
3
3
  import { headingThemeRecipeMeta } from '../components/heading/themeRecipeMeta';
4
+ import { radioGroupThemeRecipeMeta } from '../components/radio/themeRecipeMeta';
4
5
  import { textThemeRecipeMeta } from '../components/text/themeRecipeMeta';
5
6
  import { panelThemeRecipeMeta } from '../patterns/panel/themeRecipeMeta';
6
7
  import type { ZoraThemeRecipeMetaRegistry } from './themeRecipeTypes';
@@ -9,6 +10,7 @@ export const ZORA_THEME_RECIPE_META: ZoraThemeRecipeMetaRegistry = {
9
10
  Button: buttonThemeRecipeMeta,
10
11
  Card: cardThemeRecipeMeta,
11
12
  Heading: headingThemeRecipeMeta,
13
+ RadioGroup: radioGroupThemeRecipeMeta,
12
14
  Text: textThemeRecipeMeta,
13
15
  Panel: panelThemeRecipeMeta,
14
16
  };