@ankhorage/zora 0.3.6 → 0.3.7

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 (117) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +0 -90
  3. package/package.json +5 -1
  4. package/src/components/badge/Badge.tsx +19 -0
  5. package/src/components/badge/index.ts +2 -0
  6. package/src/components/badge/types.ts +14 -0
  7. package/src/components/button/Button.tsx +13 -0
  8. package/src/components/button/index.ts +2 -0
  9. package/src/components/button/types.ts +16 -0
  10. package/src/components/card/Card.tsx +65 -0
  11. package/src/components/card/index.ts +2 -0
  12. package/src/components/card/types.ts +15 -0
  13. package/src/components/drawer/Drawer.tsx +27 -0
  14. package/src/components/drawer/index.ts +2 -0
  15. package/src/components/drawer/types.ts +12 -0
  16. package/src/components/icon/Icon.tsx +8 -0
  17. package/src/components/icon/index.ts +2 -0
  18. package/src/components/icon-button/IconButton.tsx +27 -0
  19. package/src/components/icon-button/index.ts +2 -0
  20. package/src/components/icon-button/types.ts +15 -0
  21. package/src/components/input/Input.tsx +38 -0
  22. package/src/components/input/index.ts +2 -0
  23. package/src/components/input/types.ts +12 -0
  24. package/src/components/modal/Modal.tsx +37 -0
  25. package/src/components/modal/index.ts +2 -0
  26. package/src/components/modal/types.ts +15 -0
  27. package/src/components/select/Select.tsx +49 -0
  28. package/src/components/select/index.ts +2 -0
  29. package/src/components/select/types.ts +14 -0
  30. package/src/components/tabs/Tabs.tsx +103 -0
  31. package/src/components/tabs/index.ts +2 -0
  32. package/src/components/tabs/types.ts +25 -0
  33. package/src/components/textarea/Textarea.tsx +38 -0
  34. package/src/components/textarea/index.ts +2 -0
  35. package/src/components/textarea/types.ts +12 -0
  36. package/src/components/toolbar/Toolbar.tsx +38 -0
  37. package/src/components/toolbar/ToolbarAction.tsx +15 -0
  38. package/src/components/toolbar/index.ts +3 -0
  39. package/src/components/toolbar/types.ts +21 -0
  40. package/src/index.ts +72 -0
  41. package/src/internal/deepMerge.ts +23 -0
  42. package/src/internal/recipes.test.ts +46 -0
  43. package/src/internal/recipes.ts +92 -0
  44. package/src/layout/app-shell/AppShell.tsx +15 -0
  45. package/src/layout/app-shell/index.ts +2 -0
  46. package/src/layout/app-shell/types.ts +7 -0
  47. package/src/layout/auth-layout/AuthLayout.tsx +29 -0
  48. package/src/layout/auth-layout/index.ts +2 -0
  49. package/src/layout/auth-layout/types.ts +10 -0
  50. package/src/layout/page/Page.tsx +17 -0
  51. package/src/layout/page/index.ts +2 -0
  52. package/src/layout/page/types.ts +11 -0
  53. package/src/layout/page-header/PageHeader.tsx +41 -0
  54. package/src/layout/page-header/index.ts +2 -0
  55. package/src/layout/page-header/types.ts +10 -0
  56. package/src/layout/page-section/PageSection.tsx +14 -0
  57. package/src/layout/page-section/index.ts +2 -0
  58. package/src/layout/page-section/types.ts +9 -0
  59. package/src/layout/settings-layout/SettingsLayout.tsx +26 -0
  60. package/src/layout/settings-layout/index.ts +2 -0
  61. package/src/layout/settings-layout/types.ts +10 -0
  62. package/src/layout/sidebar-layout/SidebarLayout.tsx +23 -0
  63. package/src/layout/sidebar-layout/index.ts +2 -0
  64. package/src/layout/sidebar-layout/types.ts +10 -0
  65. package/src/layout/topbar-layout/TopbarLayout.tsx +14 -0
  66. package/src/layout/topbar-layout/index.ts +2 -0
  67. package/src/layout/topbar-layout/types.ts +8 -0
  68. package/src/patterns/collection-editor/CollectionEditor.tsx +100 -0
  69. package/src/patterns/collection-editor/index.ts +2 -0
  70. package/src/patterns/collection-editor/types.ts +25 -0
  71. package/src/patterns/confirm-dialog/ConfirmDialog.tsx +46 -0
  72. package/src/patterns/confirm-dialog/index.ts +2 -0
  73. package/src/patterns/confirm-dialog/types.ts +19 -0
  74. package/src/patterns/disclosure-section/DisclosureSection.tsx +61 -0
  75. package/src/patterns/disclosure-section/index.ts +2 -0
  76. package/src/patterns/disclosure-section/types.ts +15 -0
  77. package/src/patterns/empty-state/EmptyState.tsx +53 -0
  78. package/src/patterns/empty-state/index.ts +2 -0
  79. package/src/patterns/empty-state/types.ts +20 -0
  80. package/src/patterns/form-field/FormField.tsx +27 -0
  81. package/src/patterns/form-field/index.ts +2 -0
  82. package/src/patterns/form-field/types.ts +11 -0
  83. package/src/patterns/inspector-field/InspectorField.tsx +16 -0
  84. package/src/patterns/inspector-field/index.ts +2 -0
  85. package/src/patterns/inspector-field/types.ts +15 -0
  86. package/src/patterns/notice/Notice.tsx +30 -0
  87. package/src/patterns/notice/index.ts +2 -0
  88. package/src/patterns/notice/types.ts +12 -0
  89. package/src/patterns/panel/Panel.tsx +8 -0
  90. package/src/patterns/panel/index.ts +2 -0
  91. package/src/patterns/panel/types.ts +15 -0
  92. package/src/patterns/responsive-panel/ResponsivePanel.tsx +70 -0
  93. package/src/patterns/responsive-panel/index.ts +2 -0
  94. package/src/patterns/responsive-panel/types.ts +20 -0
  95. package/src/patterns/section-header/SectionHeader.tsx +39 -0
  96. package/src/patterns/section-header/index.ts +2 -0
  97. package/src/patterns/section-header/types.ts +9 -0
  98. package/src/patterns/settings-row/SettingsRow.tsx +40 -0
  99. package/src/patterns/settings-row/index.ts +2 -0
  100. package/src/patterns/settings-row/types.ts +27 -0
  101. package/src/patterns/switch-field/SwitchField.tsx +24 -0
  102. package/src/patterns/switch-field/index.ts +2 -0
  103. package/src/patterns/switch-field/types.ts +10 -0
  104. package/src/patterns/tile-grid/PaletteItem.tsx +49 -0
  105. package/src/patterns/tile-grid/TileGrid.tsx +44 -0
  106. package/src/patterns/tile-grid/index.ts +3 -0
  107. package/src/patterns/tile-grid/types.ts +20 -0
  108. package/src/patterns/tree-view/TreeItem.tsx +86 -0
  109. package/src/patterns/tree-view/TreeView.tsx +50 -0
  110. package/src/patterns/tree-view/index.ts +3 -0
  111. package/src/patterns/tree-view/types.ts +31 -0
  112. package/src/theme/ZoraProvider.tsx +22 -0
  113. package/src/theme/createZoraTheme.test.ts +25 -0
  114. package/src/theme/createZoraTheme.ts +10 -0
  115. package/src/theme/index.ts +6 -0
  116. package/src/theme/useZoraTheme.ts +5 -0
  117. package/src/theme/zoraTheme.ts +16 -0
@@ -0,0 +1,14 @@
1
+ import { Stack } from '@ankhorage/surface';
2
+ import React from 'react';
3
+
4
+ import { SectionHeader } from '../../patterns/section-header';
5
+ import type { PageSectionProps } from './types';
6
+
7
+ export function PageSection({ title, description, actions, children, testID }: PageSectionProps) {
8
+ return (
9
+ <Stack gap="m" testID={testID}>
10
+ {title ? <SectionHeader actions={actions} description={description} title={title} /> : null}
11
+ {children}
12
+ </Stack>
13
+ );
14
+ }
@@ -0,0 +1,2 @@
1
+ export { PageSection } from './PageSection';
2
+ export type { PageSectionProps } from './types';
@@ -0,0 +1,9 @@
1
+ import type React from 'react';
2
+
3
+ export interface PageSectionProps {
4
+ title?: React.ReactNode;
5
+ description?: React.ReactNode;
6
+ actions?: React.ReactNode;
7
+ children?: React.ReactNode;
8
+ testID?: string;
9
+ }
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+
3
+ import { Page } from '../page';
4
+ import { PageHeader } from '../page-header';
5
+ import { SidebarLayout } from '../sidebar-layout';
6
+ import type { SettingsLayoutProps } from './types';
7
+
8
+ export function SettingsLayout({
9
+ title,
10
+ description,
11
+ sidebar,
12
+ children,
13
+ actions,
14
+ testID,
15
+ }: SettingsLayoutProps) {
16
+ return (
17
+ <Page
18
+ header={
19
+ title ? <PageHeader actions={actions} description={description} title={title} /> : null
20
+ }
21
+ testID={testID}
22
+ >
23
+ <SidebarLayout sidebar={sidebar}>{children}</SidebarLayout>
24
+ </Page>
25
+ );
26
+ }
@@ -0,0 +1,2 @@
1
+ export { SettingsLayout } from './SettingsLayout';
2
+ export type { SettingsLayoutProps } from './types';
@@ -0,0 +1,10 @@
1
+ import type React from 'react';
2
+
3
+ export interface SettingsLayoutProps {
4
+ title?: React.ReactNode;
5
+ description?: React.ReactNode;
6
+ sidebar: React.ReactNode;
7
+ children?: React.ReactNode;
8
+ actions?: React.ReactNode;
9
+ testID?: string;
10
+ }
@@ -0,0 +1,23 @@
1
+ import { Box, Stack } from '@ankhorage/surface';
2
+ import React from 'react';
3
+
4
+ import type { SidebarLayoutProps } from './types';
5
+
6
+ export function SidebarLayout({
7
+ sidebar,
8
+ children,
9
+ aside,
10
+ sidebarWidth = 280,
11
+ asideWidth = 280,
12
+ testID,
13
+ }: SidebarLayoutProps) {
14
+ return (
15
+ <Stack direction={{ base: 'column', lg: 'row' }} gap="l" testID={testID} align="flex-start">
16
+ <Box width={{ base: '100%', lg: sidebarWidth }}>{sidebar}</Box>
17
+ <Box flex={1} width="100%">
18
+ {children}
19
+ </Box>
20
+ {aside ? <Box width={{ base: '100%', lg: asideWidth }}>{aside}</Box> : null}
21
+ </Stack>
22
+ );
23
+ }
@@ -0,0 +1,2 @@
1
+ export { SidebarLayout } from './SidebarLayout';
2
+ export type { SidebarLayoutProps } from './types';
@@ -0,0 +1,10 @@
1
+ import type React from 'react';
2
+
3
+ export interface SidebarLayoutProps {
4
+ sidebar: React.ReactNode;
5
+ children?: React.ReactNode;
6
+ aside?: React.ReactNode;
7
+ sidebarWidth?: number;
8
+ asideWidth?: number;
9
+ testID?: string;
10
+ }
@@ -0,0 +1,14 @@
1
+ import { Box, Stack } from '@ankhorage/surface';
2
+ import React from 'react';
3
+
4
+ import { SidebarLayout } from '../sidebar-layout';
5
+ import type { TopbarLayoutProps } from './types';
6
+
7
+ export function TopbarLayout({ topbar, children, sidebar, testID }: TopbarLayoutProps) {
8
+ return (
9
+ <Stack gap="l" testID={testID}>
10
+ <Box>{topbar}</Box>
11
+ {sidebar ? <SidebarLayout sidebar={sidebar}>{children}</SidebarLayout> : children}
12
+ </Stack>
13
+ );
14
+ }
@@ -0,0 +1,2 @@
1
+ export { TopbarLayout } from './TopbarLayout';
2
+ export type { TopbarLayoutProps } from './types';
@@ -0,0 +1,8 @@
1
+ import type React from 'react';
2
+
3
+ export interface TopbarLayoutProps {
4
+ topbar: React.ReactNode;
5
+ children?: React.ReactNode;
6
+ sidebar?: React.ReactNode;
7
+ testID?: string;
8
+ }
@@ -0,0 +1,100 @@
1
+ import { Box, Stack, Text } from '@ankhorage/surface';
2
+ import React from 'react';
3
+
4
+ import { Button } from '../../components/button';
5
+ import { IconButton } from '../../components/icon-button';
6
+ import { Panel } from '../panel';
7
+ import type { CollectionEditorProps } from './types';
8
+
9
+ export function CollectionEditor<TItem>({
10
+ title,
11
+ description,
12
+ items,
13
+ renderItem,
14
+ onAdd,
15
+ onRemove,
16
+ onMove,
17
+ addLabel = 'Add Item',
18
+ emptyLabel = 'No items yet.',
19
+ disabled,
20
+ testID,
21
+ }: CollectionEditorProps<TItem>) {
22
+ const isEmpty = items.length === 0;
23
+
24
+ return (
25
+ <Panel
26
+ compact
27
+ description={description}
28
+ testID={testID}
29
+ title={title}
30
+ actions={
31
+ onAdd ? (
32
+ <Button emphasis="soft" size="s" disabled={disabled} onPress={onAdd}>
33
+ {addLabel}
34
+ </Button>
35
+ ) : null
36
+ }
37
+ >
38
+ <Stack gap="s">
39
+ {isEmpty ? (
40
+ <Box py="m">
41
+ <Text align="center" tone="muted">
42
+ {emptyLabel}
43
+ </Text>
44
+ </Box>
45
+ ) : (
46
+ items.map((item, index) => (
47
+ <Box key={index} bg="subtle" p="s" radius="m" borderColor="border" borderWidth={1}>
48
+ <Stack direction="row" gap="m" align="center">
49
+ <Box flex={1}>
50
+ {renderItem({
51
+ item,
52
+ index,
53
+ remove: () => onRemove?.(index),
54
+ moveUp: () => onMove?.(index, index - 1),
55
+ moveDown: () => onMove?.(index, index + 1),
56
+ canMoveUp: index > 0,
57
+ canMoveDown: index < items.length - 1,
58
+ })}
59
+ </Box>
60
+ <Stack direction="row" gap="xs">
61
+ {onMove ? (
62
+ <>
63
+ <IconButton
64
+ icon={{ name: 'arrow-up-outline' }}
65
+ label="Move Up"
66
+ disabled={disabled ?? index === 0}
67
+ onPress={() => onMove(index, index - 1)}
68
+ size="s"
69
+ emphasis="ghost"
70
+ />
71
+ <IconButton
72
+ icon={{ name: 'arrow-down-outline' }}
73
+ label="Move Down"
74
+ disabled={disabled ?? index === items.length - 1}
75
+ onPress={() => onMove(index, index + 1)}
76
+ size="s"
77
+ emphasis="ghost"
78
+ />
79
+ </>
80
+ ) : null}
81
+ {onRemove ? (
82
+ <IconButton
83
+ icon={{ name: 'trash-outline' }}
84
+ label="Remove"
85
+ tone="danger"
86
+ disabled={disabled}
87
+ onPress={() => onRemove(index)}
88
+ size="s"
89
+ emphasis="ghost"
90
+ />
91
+ ) : null}
92
+ </Stack>
93
+ </Stack>
94
+ </Box>
95
+ ))
96
+ )}
97
+ </Stack>
98
+ </Panel>
99
+ );
100
+ }
@@ -0,0 +1,2 @@
1
+ export * from './CollectionEditor';
2
+ export * from './types';
@@ -0,0 +1,25 @@
1
+ import type React from 'react';
2
+
3
+ export interface CollectionEditorRenderItemProps<TItem> {
4
+ item: TItem;
5
+ index: number;
6
+ remove: () => void;
7
+ moveUp: () => void;
8
+ moveDown: () => void;
9
+ canMoveUp: boolean;
10
+ canMoveDown: boolean;
11
+ }
12
+
13
+ export interface CollectionEditorProps<TItem> {
14
+ title?: React.ReactNode;
15
+ description?: React.ReactNode;
16
+ items: readonly TItem[];
17
+ renderItem: (props: CollectionEditorRenderItemProps<TItem>) => React.ReactNode;
18
+ onAdd?: () => void;
19
+ onRemove?: (index: number) => void;
20
+ onMove?: (from: number, to: number) => void;
21
+ addLabel?: React.ReactNode;
22
+ emptyLabel?: React.ReactNode;
23
+ disabled?: boolean;
24
+ testID?: string;
25
+ }
@@ -0,0 +1,46 @@
1
+ import { Stack } from '@ankhorage/surface';
2
+ import React from 'react';
3
+
4
+ import { Button } from '../../components/button';
5
+ import { Modal } from '../../components/modal';
6
+ import type { ConfirmDialogProps } from './types';
7
+
8
+ export function ConfirmDialog({
9
+ visible,
10
+ title,
11
+ description,
12
+ children,
13
+ confirmLabel = 'Confirm',
14
+ cancelLabel = 'Cancel',
15
+ confirmTone = 'danger',
16
+ confirmEmphasis = 'solid',
17
+ busy = false,
18
+ closeOnBackdrop = true,
19
+ onConfirm,
20
+ onCancel,
21
+ testID,
22
+ }: ConfirmDialogProps) {
23
+ return (
24
+ <Modal
25
+ closeOnBackdrop={closeOnBackdrop}
26
+ description={description}
27
+ footer={
28
+ <Stack direction={{ base: 'column', md: 'row' }} gap="s" justify="flex-end">
29
+ <Button emphasis="soft" onPress={onCancel} tone="neutral">
30
+ {cancelLabel}
31
+ </Button>
32
+ <Button emphasis={confirmEmphasis} loading={busy} onPress={onConfirm} tone={confirmTone}>
33
+ {confirmLabel}
34
+ </Button>
35
+ </Stack>
36
+ }
37
+ onDismiss={onCancel}
38
+ testID={testID}
39
+ title={title}
40
+ visible={visible}
41
+ width="narrow"
42
+ >
43
+ {children}
44
+ </Modal>
45
+ );
46
+ }
@@ -0,0 +1,2 @@
1
+ export { ConfirmDialog } from './ConfirmDialog';
2
+ export type { ConfirmDialogProps } from './types';
@@ -0,0 +1,19 @@
1
+ import type React from 'react';
2
+
3
+ import type { ZoraEmphasis, ZoraTone } from '../../internal/recipes';
4
+
5
+ export interface ConfirmDialogProps {
6
+ visible: boolean;
7
+ title: React.ReactNode;
8
+ description?: React.ReactNode;
9
+ children?: React.ReactNode;
10
+ confirmLabel?: React.ReactNode;
11
+ cancelLabel?: React.ReactNode;
12
+ confirmTone?: ZoraTone;
13
+ confirmEmphasis?: ZoraEmphasis;
14
+ busy?: boolean;
15
+ closeOnBackdrop?: boolean;
16
+ onConfirm?: () => void;
17
+ onCancel?: () => void;
18
+ testID?: string;
19
+ }
@@ -0,0 +1,61 @@
1
+ import { Box, Stack } from '@ankhorage/surface';
2
+ import React from 'react';
3
+
4
+ import { IconButton } from '../../components/icon-button';
5
+ import { Panel } from '../panel';
6
+ import type { DisclosureSectionProps } from './types';
7
+
8
+ export function DisclosureSection({
9
+ title,
10
+ description,
11
+ icon,
12
+ defaultOpen = true,
13
+ open: controlledOpen,
14
+ onOpenChange,
15
+ actions,
16
+ children,
17
+ disabled,
18
+ testID,
19
+ }: DisclosureSectionProps) {
20
+ const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
21
+
22
+ const isControlled = controlledOpen !== undefined;
23
+ const isOpen = isControlled ? controlledOpen : internalOpen;
24
+
25
+ const toggleOpen = () => {
26
+ if (disabled) return;
27
+
28
+ if (isControlled) {
29
+ onOpenChange?.(!controlledOpen);
30
+ } else {
31
+ setInternalOpen(!internalOpen);
32
+ onOpenChange?.(!internalOpen);
33
+ }
34
+ };
35
+
36
+ return (
37
+ <Panel
38
+ compact
39
+ description={description}
40
+ testID={testID}
41
+ title={title}
42
+ eyebrow={icon ? <Box pb="xs">{/* Surface icon spec would go here */}</Box> : null}
43
+ actions={
44
+ <Stack direction="row" gap="xs" align="center">
45
+ {actions}
46
+ <IconButton
47
+ icon={{ name: isOpen ? 'chevron-up-outline' : 'chevron-down-outline' }}
48
+ label={isOpen ? 'Collapse' : 'Expand'}
49
+ emphasis="ghost"
50
+ tone="neutral"
51
+ size="s"
52
+ disabled={disabled}
53
+ onPress={toggleOpen}
54
+ />
55
+ </Stack>
56
+ }
57
+ >
58
+ {isOpen ? <Box pt="m">{children}</Box> : null}
59
+ </Panel>
60
+ );
61
+ }
@@ -0,0 +1,2 @@
1
+ export * from './DisclosureSection';
2
+ export * from './types';
@@ -0,0 +1,15 @@
1
+ import type { ButtonIconSpec } from '@ankhorage/surface';
2
+ import type { ReactNode } from 'react';
3
+
4
+ export interface DisclosureSectionProps {
5
+ title: ReactNode;
6
+ description?: ReactNode;
7
+ icon?: ButtonIconSpec;
8
+ defaultOpen?: boolean;
9
+ open?: boolean;
10
+ onOpenChange?: (open: boolean) => void;
11
+ actions?: ReactNode;
12
+ children?: ReactNode;
13
+ disabled?: boolean;
14
+ testID?: string;
15
+ }
@@ -0,0 +1,53 @@
1
+ import { Stack } from '@ankhorage/surface';
2
+ import React from 'react';
3
+
4
+ import { Button } from '../../components/button';
5
+ import { Card } from '../../components/card';
6
+ import type { EmptyStateProps } from './types';
7
+
8
+ export function EmptyState({
9
+ title,
10
+ description,
11
+ eyebrow,
12
+ primaryAction,
13
+ secondaryAction,
14
+ footer,
15
+ testID,
16
+ }: EmptyStateProps) {
17
+ return (
18
+ <Card
19
+ compact
20
+ description={description}
21
+ eyebrow={eyebrow}
22
+ testID={testID}
23
+ title={title}
24
+ tone="subtle"
25
+ >
26
+ <Stack gap="m">
27
+ {primaryAction || secondaryAction ? (
28
+ <Stack direction={{ base: 'column', md: 'row' }} gap="s">
29
+ {primaryAction ? (
30
+ <Button
31
+ emphasis={primaryAction.emphasis}
32
+ onPress={primaryAction.onPress}
33
+ tone={primaryAction.tone}
34
+ >
35
+ {primaryAction.label}
36
+ </Button>
37
+ ) : null}
38
+ {secondaryAction ? (
39
+ <Button
40
+ emphasis={secondaryAction.emphasis ?? 'soft'}
41
+ onPress={secondaryAction.onPress}
42
+ tone={secondaryAction.tone ?? 'neutral'}
43
+ >
44
+ {secondaryAction.label}
45
+ </Button>
46
+ ) : null}
47
+ </Stack>
48
+ ) : null}
49
+ {footer}
50
+ </Stack>
51
+ </Card>
52
+ );
53
+ }
@@ -0,0 +1,2 @@
1
+ export { EmptyState } from './EmptyState';
2
+ export type { EmptyStateAction, EmptyStateProps } from './types';
@@ -0,0 +1,20 @@
1
+ import type React from 'react';
2
+
3
+ import type { ZoraEmphasis, ZoraTone } from '../../internal/recipes';
4
+
5
+ export interface EmptyStateAction {
6
+ label: React.ReactNode;
7
+ onPress: () => void;
8
+ tone?: ZoraTone;
9
+ emphasis?: ZoraEmphasis;
10
+ }
11
+
12
+ export interface EmptyStateProps {
13
+ title: React.ReactNode;
14
+ description?: React.ReactNode;
15
+ eyebrow?: React.ReactNode;
16
+ primaryAction?: EmptyStateAction;
17
+ secondaryAction?: EmptyStateAction;
18
+ footer?: React.ReactNode;
19
+ testID?: string;
20
+ }
@@ -0,0 +1,27 @@
1
+ import { Field, Stack, Text } from '@ankhorage/surface';
2
+ import React from 'react';
3
+
4
+ import type { FormFieldProps } from './types';
5
+
6
+ export function FormField({ label, description, helperText, children, ...props }: FormFieldProps) {
7
+ return (
8
+ <Field
9
+ {...props}
10
+ helperText={helperText}
11
+ label={
12
+ <Stack gap="xs">
13
+ <Text variant="label" weight="semiBold">
14
+ {label}
15
+ </Text>
16
+ {description ? (
17
+ <Text tone="muted" variant="bodySmall">
18
+ {description}
19
+ </Text>
20
+ ) : null}
21
+ </Stack>
22
+ }
23
+ >
24
+ {children}
25
+ </Field>
26
+ );
27
+ }
@@ -0,0 +1,2 @@
1
+ export { FormField } from './FormField';
2
+ export type { FormFieldProps } from './types';
@@ -0,0 +1,11 @@
1
+ import type { FieldProps as SurfaceFieldProps } from '@ankhorage/surface';
2
+ import type React from 'react';
3
+
4
+ export interface FormFieldProps extends Pick<
5
+ SurfaceFieldProps,
6
+ 'children' | 'disabled' | 'errorText' | 'invalid' | 'readOnly' | 'required' | 'testID'
7
+ > {
8
+ label: React.ReactNode;
9
+ description?: React.ReactNode;
10
+ helperText?: React.ReactNode;
11
+ }
@@ -0,0 +1,16 @@
1
+ import { Box, Stack } from '@ankhorage/surface';
2
+ import React from 'react';
3
+
4
+ import { FormField } from '../form-field';
5
+ import type { InspectorFieldProps } from './types';
6
+
7
+ export function InspectorField({ label, control, children, ...props }: InspectorFieldProps) {
8
+ return (
9
+ <FormField {...props} label={label}>
10
+ <Stack direction="row" gap="s" align="center">
11
+ <Box flex={1}>{children}</Box>
12
+ {control ? <Box>{control}</Box> : null}
13
+ </Stack>
14
+ </FormField>
15
+ );
16
+ }
@@ -0,0 +1,2 @@
1
+ export * from './InspectorField';
2
+ export * from './types';
@@ -0,0 +1,15 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface InspectorFieldProps {
4
+ label: ReactNode;
5
+ description?: ReactNode;
6
+ helperText?: ReactNode;
7
+ errorText?: ReactNode;
8
+ required?: boolean;
9
+ invalid?: boolean;
10
+ disabled?: boolean;
11
+ readOnly?: boolean;
12
+ control?: ReactNode;
13
+ children?: ReactNode;
14
+ testID?: string;
15
+ }
@@ -0,0 +1,30 @@
1
+ import { Box, Stack } from '@ankhorage/surface';
2
+ import React from 'react';
3
+
4
+ import { Badge } from '../../components/badge';
5
+ import { Card } from '../../components/card';
6
+ import type { NoticeProps } from './types';
7
+
8
+ export function Notice({
9
+ title,
10
+ description,
11
+ children,
12
+ actions,
13
+ tone = 'primary',
14
+ testID,
15
+ }: NoticeProps) {
16
+ return (
17
+ <Card
18
+ description={description}
19
+ eyebrow={<Badge tone={tone}>{String(tone).toUpperCase()}</Badge>}
20
+ testID={testID}
21
+ title={title}
22
+ tone="subtle"
23
+ >
24
+ <Stack gap="m">
25
+ {children ? <Box>{children}</Box> : null}
26
+ {actions ? <Box>{actions}</Box> : null}
27
+ </Stack>
28
+ </Card>
29
+ );
30
+ }
@@ -0,0 +1,2 @@
1
+ export { Notice } from './Notice';
2
+ export type { NoticeProps } from './types';
@@ -0,0 +1,12 @@
1
+ import type React from 'react';
2
+
3
+ import type { ZoraTone } from '../../internal/recipes';
4
+
5
+ export interface NoticeProps {
6
+ title: React.ReactNode;
7
+ description?: React.ReactNode;
8
+ children?: React.ReactNode;
9
+ actions?: React.ReactNode;
10
+ tone?: ZoraTone;
11
+ testID?: string;
12
+ }
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+
3
+ import { Card } from '../../components/card';
4
+ import type { PanelProps } from './types';
5
+
6
+ export function Panel(props: PanelProps) {
7
+ return <Card {...props} />;
8
+ }
@@ -0,0 +1,2 @@
1
+ export { Panel } from './Panel';
2
+ export type { PanelProps } from './types';
@@ -0,0 +1,15 @@
1
+ import type React from 'react';
2
+
3
+ import type { ZoraCardTone } from '../../internal/recipes';
4
+
5
+ export interface PanelProps {
6
+ title?: React.ReactNode;
7
+ description?: React.ReactNode;
8
+ eyebrow?: React.ReactNode;
9
+ actions?: React.ReactNode;
10
+ footer?: React.ReactNode;
11
+ children?: React.ReactNode;
12
+ tone?: ZoraCardTone;
13
+ compact?: boolean;
14
+ testID?: string;
15
+ }