@newtonedev/components 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AppShell/AppShell.d.ts +4 -0
- package/dist/AppShell/AppShell.d.ts.map +1 -0
- package/dist/AppShell/AppShell.styles.d.ts +16 -0
- package/dist/AppShell/AppShell.styles.d.ts.map +1 -0
- package/dist/AppShell/AppShell.types.d.ts +8 -0
- package/dist/AppShell/AppShell.types.d.ts.map +1 -0
- package/dist/AppShell/index.d.ts +3 -0
- package/dist/AppShell/index.d.ts.map +1 -0
- package/dist/Button/Button.d.ts +9 -4
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.styles.d.ts +33 -26
- package/dist/Button/Button.styles.d.ts.map +1 -1
- package/dist/Button/Button.types.d.ts +17 -2
- package/dist/Button/Button.types.d.ts.map +1 -1
- package/dist/ColorScaleSlider/ColorScaleSlider.d.ts +13 -0
- package/dist/ColorScaleSlider/ColorScaleSlider.d.ts.map +1 -0
- package/dist/ColorScaleSlider/ColorScaleSlider.styles.d.ts +54 -0
- package/dist/ColorScaleSlider/ColorScaleSlider.styles.d.ts.map +1 -0
- package/dist/ColorScaleSlider/ColorScaleSlider.types.d.ts +25 -0
- package/dist/ColorScaleSlider/ColorScaleSlider.types.d.ts.map +1 -0
- package/dist/ColorScaleSlider/index.d.ts +3 -0
- package/dist/ColorScaleSlider/index.d.ts.map +1 -0
- package/dist/Frame/Frame.d.ts +48 -0
- package/dist/Frame/Frame.d.ts.map +1 -0
- package/dist/Frame/Frame.styles.d.ts +39 -0
- package/dist/Frame/Frame.styles.d.ts.map +1 -0
- package/dist/Frame/Frame.types.d.ts +115 -0
- package/dist/Frame/Frame.types.d.ts.map +1 -0
- package/dist/Frame/Frame.utils.d.ts +39 -0
- package/dist/Frame/Frame.utils.d.ts.map +1 -0
- package/dist/Frame/index.d.ts +4 -0
- package/dist/Frame/index.d.ts.map +1 -0
- package/dist/HueSlider/HueSlider.d.ts +1 -1
- package/dist/HueSlider/HueSlider.d.ts.map +1 -1
- package/dist/HueSlider/HueSlider.styles.d.ts +47 -5
- package/dist/HueSlider/HueSlider.styles.d.ts.map +1 -1
- package/dist/HueSlider/HueSlider.types.d.ts +1 -0
- package/dist/HueSlider/HueSlider.types.d.ts.map +1 -1
- package/dist/Icon/Icon.d.ts +36 -0
- package/dist/Icon/Icon.d.ts.map +1 -0
- package/dist/Navbar/Navbar.d.ts +4 -0
- package/dist/Navbar/Navbar.d.ts.map +1 -0
- package/dist/Navbar/Navbar.styles.d.ts +31 -0
- package/dist/Navbar/Navbar.styles.d.ts.map +1 -0
- package/dist/Navbar/Navbar.types.d.ts +14 -0
- package/dist/Navbar/Navbar.types.d.ts.map +1 -0
- package/dist/Navbar/index.d.ts +3 -0
- package/dist/Navbar/index.d.ts.map +1 -0
- package/dist/Popover/Popover.d.ts +4 -0
- package/dist/Popover/Popover.d.ts.map +1 -0
- package/dist/Popover/Popover.styles.d.ts +9 -0
- package/dist/Popover/Popover.styles.d.ts.map +1 -0
- package/dist/Popover/Popover.types.d.ts +37 -0
- package/dist/Popover/Popover.types.d.ts.map +1 -0
- package/dist/Popover/index.d.ts +4 -0
- package/dist/Popover/index.d.ts.map +1 -0
- package/dist/Popover/usePopover.d.ts +3 -0
- package/dist/Popover/usePopover.d.ts.map +1 -0
- package/dist/Select/Select.d.ts +1 -8
- package/dist/Select/Select.d.ts.map +1 -1
- package/dist/Select/Select.styles.d.ts +32 -5
- package/dist/Select/Select.styles.d.ts.map +1 -1
- package/dist/Select/Select.types.d.ts +25 -1
- package/dist/Select/Select.types.d.ts.map +1 -1
- package/dist/Select/SelectOption.d.ts +13 -0
- package/dist/Select/SelectOption.d.ts.map +1 -0
- package/dist/Select/useSelect.d.ts +15 -0
- package/dist/Select/useSelect.d.ts.map +1 -0
- package/dist/Sidebar/Sidebar.d.ts +4 -0
- package/dist/Sidebar/Sidebar.d.ts.map +1 -0
- package/dist/Sidebar/Sidebar.styles.d.ts +31 -0
- package/dist/Sidebar/Sidebar.styles.d.ts.map +1 -0
- package/dist/Sidebar/Sidebar.types.d.ts +14 -0
- package/dist/Sidebar/Sidebar.types.d.ts.map +1 -0
- package/dist/Sidebar/index.d.ts +3 -0
- package/dist/Sidebar/index.d.ts.map +1 -0
- package/dist/Slider/Slider.d.ts +1 -1
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.styles.d.ts +48 -8
- package/dist/Slider/Slider.styles.d.ts.map +1 -1
- package/dist/Slider/Slider.types.d.ts +1 -0
- package/dist/Slider/Slider.types.d.ts.map +1 -1
- package/dist/TextInput/TextInput.styles.d.ts +3 -1
- package/dist/TextInput/TextInput.styles.d.ts.map +1 -1
- package/dist/Toggle/Toggle.styles.d.ts +2 -1
- package/dist/Toggle/Toggle.styles.d.ts.map +1 -1
- package/dist/fonts/GoogleFontLoader.d.ts +19 -0
- package/dist/fonts/GoogleFontLoader.d.ts.map +1 -0
- package/dist/fonts/IconFontLoader.d.ts +13 -0
- package/dist/fonts/IconFontLoader.d.ts.map +1 -0
- package/dist/fonts/buildGoogleFontsUrl.d.ts +17 -0
- package/dist/fonts/buildGoogleFontsUrl.d.ts.map +1 -0
- package/dist/fonts/googleFonts.d.ts +20 -0
- package/dist/fonts/googleFonts.d.ts.map +1 -0
- package/dist/index.cjs +2303 -205
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +27 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2279 -200
- package/dist/index.js.map +1 -1
- package/dist/registry/codegen.d.ts +11 -0
- package/dist/registry/codegen.d.ts.map +1 -0
- package/dist/registry/index.d.ts +4 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/registry.d.ts +7 -0
- package/dist/registry/registry.d.ts.map +1 -0
- package/dist/registry/types.d.ts +32 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/theme/FrameContext.d.ts +24 -0
- package/dist/theme/FrameContext.d.ts.map +1 -0
- package/dist/theme/NewtoneProvider.d.ts.map +1 -1
- package/dist/theme/defaults.d.ts.map +1 -1
- package/dist/theme/types.d.ts +64 -1
- package/dist/theme/types.d.ts.map +1 -1
- package/dist/tokens/computeTokens.d.ts +55 -3
- package/dist/tokens/computeTokens.d.ts.map +1 -1
- package/dist/tokens/types.d.ts +52 -0
- package/dist/tokens/types.d.ts.map +1 -1
- package/dist/tokens/useTokens.d.ts +12 -9
- package/dist/tokens/useTokens.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/AppShell/AppShell.styles.ts +20 -0
- package/src/AppShell/AppShell.tsx +17 -0
- package/src/AppShell/AppShell.types.ts +8 -0
- package/src/AppShell/index.ts +2 -0
- package/src/Button/Button.styles.ts +74 -41
- package/src/Button/Button.tsx +36 -17
- package/src/Button/Button.types.ts +20 -2
- package/src/Card/Card.styles.ts +2 -2
- package/src/ColorScaleSlider/ColorScaleSlider.styles.ts +60 -0
- package/src/ColorScaleSlider/ColorScaleSlider.tsx +156 -0
- package/src/ColorScaleSlider/ColorScaleSlider.types.ts +25 -0
- package/src/ColorScaleSlider/index.ts +2 -0
- package/src/Frame/Frame.styles.ts +213 -0
- package/src/Frame/Frame.tsx +242 -0
- package/src/Frame/Frame.types.ts +181 -0
- package/src/Frame/Frame.utils.ts +189 -0
- package/src/Frame/index.ts +21 -0
- package/src/HueSlider/HueSlider.styles.ts +58 -39
- package/src/HueSlider/HueSlider.tsx +97 -25
- package/src/HueSlider/HueSlider.types.ts +1 -0
- package/src/Icon/Icon.tsx +76 -0
- package/src/Navbar/Navbar.styles.ts +37 -0
- package/src/Navbar/Navbar.tsx +32 -0
- package/src/Navbar/Navbar.types.ts +14 -0
- package/src/Navbar/index.ts +2 -0
- package/src/Popover/Popover.styles.ts +39 -0
- package/src/Popover/Popover.tsx +103 -0
- package/src/Popover/Popover.types.ts +40 -0
- package/src/Popover/index.ts +3 -0
- package/src/Popover/usePopover.ts +26 -0
- package/src/Select/Select.styles.ts +49 -10
- package/src/Select/Select.tsx +127 -36
- package/src/Select/Select.types.ts +30 -1
- package/src/Select/SelectOption.tsx +104 -0
- package/src/Select/useSelect.ts +129 -0
- package/src/Sidebar/Sidebar.styles.ts +37 -0
- package/src/Sidebar/Sidebar.tsx +27 -0
- package/src/Sidebar/Sidebar.types.ts +14 -0
- package/src/Sidebar/index.ts +2 -0
- package/src/Slider/Slider.styles.ts +53 -25
- package/src/Slider/Slider.tsx +89 -24
- package/src/Slider/Slider.types.ts +1 -0
- package/src/TextInput/TextInput.styles.ts +9 -7
- package/src/Toggle/Toggle.styles.ts +4 -3
- package/src/fonts/GoogleFontLoader.tsx +63 -0
- package/src/fonts/IconFontLoader.tsx +49 -0
- package/src/fonts/buildGoogleFontsUrl.ts +31 -0
- package/src/fonts/googleFonts.ts +87 -0
- package/src/index.ts +70 -2
- package/src/registry/codegen.ts +132 -0
- package/src/registry/index.ts +17 -0
- package/src/registry/registry.ts +402 -0
- package/src/registry/types.ts +35 -0
- package/src/theme/FrameContext.tsx +29 -0
- package/src/theme/NewtoneProvider.tsx +9 -1
- package/src/theme/defaults.ts +51 -0
- package/src/theme/types.ts +66 -1
- package/src/tokens/computeTokens.ts +103 -46
- package/src/tokens/types.ts +52 -0
- package/src/tokens/useTokens.ts +30 -15
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { ComponentMeta } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handler props that should be included as placeholders in generated code.
|
|
5
|
+
* Maps component ID to the handler props it needs.
|
|
6
|
+
*/
|
|
7
|
+
const HANDLER_PROPS: Record<string, readonly string[]> = {
|
|
8
|
+
button: ['onPress={() => {}}'],
|
|
9
|
+
'text-input': ['value={value}', 'onChangeText={setValue}'],
|
|
10
|
+
select: ['value={value}', 'onValueChange={setValue}'],
|
|
11
|
+
toggle: ['value={value}', 'onValueChange={setValue}'],
|
|
12
|
+
slider: ['value={value}', 'onValueChange={setValue}'],
|
|
13
|
+
'hue-slider': ['value={value}', 'onValueChange={setValue}'],
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Default children content for components with children.
|
|
18
|
+
*/
|
|
19
|
+
const CHILDREN_CONTENT: Record<string, string> = {
|
|
20
|
+
button: 'Button',
|
|
21
|
+
card: '{/* content */}',
|
|
22
|
+
frame: '{/* content */}',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Generates a ready-to-paste JSX code snippet for a component with the given prop overrides.
|
|
27
|
+
*
|
|
28
|
+
* - Includes the import statement
|
|
29
|
+
* - Omits props that match their default values
|
|
30
|
+
* - Adds placeholder handlers for interactive props
|
|
31
|
+
* - Formats Select's options as a const declaration
|
|
32
|
+
*/
|
|
33
|
+
export function generateComponentCode(
|
|
34
|
+
component: ComponentMeta,
|
|
35
|
+
propOverrides: Record<string, unknown>,
|
|
36
|
+
): string {
|
|
37
|
+
const lines: string[] = [];
|
|
38
|
+
|
|
39
|
+
// Import line
|
|
40
|
+
lines.push(`import { ${component.importName} } from '@newtonedev/components';`);
|
|
41
|
+
|
|
42
|
+
// Select needs options declaration above JSX
|
|
43
|
+
if (component.id === 'select') {
|
|
44
|
+
lines.push('');
|
|
45
|
+
lines.push('const options = [');
|
|
46
|
+
lines.push(" { label: 'Option 1', value: 'option-1' },");
|
|
47
|
+
lines.push(" { label: 'Option 2', value: 'option-2' },");
|
|
48
|
+
lines.push(" { label: 'Option 3', value: 'option-3' },");
|
|
49
|
+
lines.push('];');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
lines.push('');
|
|
53
|
+
|
|
54
|
+
// Build props string
|
|
55
|
+
const propEntries: string[] = [];
|
|
56
|
+
|
|
57
|
+
// Add editable props (only non-default values)
|
|
58
|
+
for (const prop of component.editableProps) {
|
|
59
|
+
const value = propOverrides[prop.name] ?? prop.defaultValue;
|
|
60
|
+
if (value === prop.defaultValue) continue;
|
|
61
|
+
|
|
62
|
+
propEntries.push(formatProp(prop.name, value));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Add handler/runtime props
|
|
66
|
+
const handlers = HANDLER_PROPS[component.id];
|
|
67
|
+
if (handlers) {
|
|
68
|
+
for (const handler of handlers) {
|
|
69
|
+
propEntries.push(handler);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Special case: Select gets options={options} reference
|
|
74
|
+
if (component.id === 'select') {
|
|
75
|
+
propEntries.push('options={options}');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Build the JSX
|
|
79
|
+
const children = CHILDREN_CONTENT[component.id];
|
|
80
|
+
const hasProps = propEntries.length > 0;
|
|
81
|
+
const tag = component.importName;
|
|
82
|
+
|
|
83
|
+
if (component.hasChildren && children) {
|
|
84
|
+
if (hasProps && propEntries.length <= 3) {
|
|
85
|
+
// Single-line opening tag
|
|
86
|
+
lines.push(`<${tag} ${propEntries.join(' ')}>`);
|
|
87
|
+
lines.push(` ${children}`);
|
|
88
|
+
lines.push(`</${tag}>`);
|
|
89
|
+
} else if (hasProps) {
|
|
90
|
+
// Multi-line opening tag
|
|
91
|
+
lines.push(`<${tag}`);
|
|
92
|
+
for (const entry of propEntries) {
|
|
93
|
+
lines.push(` ${entry}`);
|
|
94
|
+
}
|
|
95
|
+
lines.push('>');
|
|
96
|
+
lines.push(` ${children}`);
|
|
97
|
+
lines.push(`</${tag}>`);
|
|
98
|
+
} else {
|
|
99
|
+
lines.push(`<${tag}>`);
|
|
100
|
+
lines.push(` ${children}`);
|
|
101
|
+
lines.push(`</${tag}>`);
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
// Self-closing tag
|
|
105
|
+
if (hasProps && propEntries.length <= 3) {
|
|
106
|
+
lines.push(`<${tag} ${propEntries.join(' ')} />`);
|
|
107
|
+
} else if (hasProps) {
|
|
108
|
+
lines.push(`<${tag}`);
|
|
109
|
+
for (const entry of propEntries) {
|
|
110
|
+
lines.push(` ${entry}`);
|
|
111
|
+
}
|
|
112
|
+
lines.push('/>');
|
|
113
|
+
} else {
|
|
114
|
+
lines.push(`<${tag} />`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return lines.join('\n');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function formatProp(name: string, value: unknown): string {
|
|
122
|
+
if (typeof value === 'string') {
|
|
123
|
+
return `${name}="${value}"`;
|
|
124
|
+
}
|
|
125
|
+
if (typeof value === 'boolean') {
|
|
126
|
+
return value ? name : `${name}={false}`;
|
|
127
|
+
}
|
|
128
|
+
if (typeof value === 'number') {
|
|
129
|
+
return `${name}={${value}}`;
|
|
130
|
+
}
|
|
131
|
+
return `${name}={${JSON.stringify(value)}}`;
|
|
132
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
CategoryMeta,
|
|
3
|
+
VariantMeta,
|
|
4
|
+
EditableProp,
|
|
5
|
+
EditablePropOption,
|
|
6
|
+
ComponentMeta,
|
|
7
|
+
} from './types';
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
CATEGORIES,
|
|
11
|
+
COMPONENTS,
|
|
12
|
+
getComponent,
|
|
13
|
+
getCategory,
|
|
14
|
+
getComponentsByCategory,
|
|
15
|
+
} from './registry';
|
|
16
|
+
|
|
17
|
+
export { generateComponentCode } from './codegen';
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import type { CategoryMeta, ComponentMeta } from './types';
|
|
2
|
+
|
|
3
|
+
export const CATEGORIES: readonly CategoryMeta[] = [
|
|
4
|
+
{ id: 'actions', name: 'Actions', description: 'Interactive elements that trigger actions' },
|
|
5
|
+
{ id: 'form-controls', name: 'Form Controls', description: 'Input elements for user data entry' },
|
|
6
|
+
{ id: 'range-inputs', name: 'Range Inputs', description: 'Slider controls for numeric values' },
|
|
7
|
+
{ id: 'layout', name: 'Layout', description: 'Structural and container components' },
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
export const COMPONENTS: readonly ComponentMeta[] = [
|
|
11
|
+
{
|
|
12
|
+
id: 'button',
|
|
13
|
+
name: 'Button',
|
|
14
|
+
importName: 'Button',
|
|
15
|
+
categoryId: 'actions',
|
|
16
|
+
description: 'Interactive button with multiple variants, sizes, and optional icon',
|
|
17
|
+
hasChildren: true,
|
|
18
|
+
variants: [
|
|
19
|
+
{ id: 'primary-md', label: 'Primary', props: { variant: 'primary', size: 'md' } },
|
|
20
|
+
{ id: 'secondary-md', label: 'Secondary', props: { variant: 'secondary', size: 'md' } },
|
|
21
|
+
{ id: 'ghost-md', label: 'Ghost', props: { variant: 'ghost', size: 'md' } },
|
|
22
|
+
{ id: 'outline-md', label: 'Outline', props: { variant: 'outline', size: 'md' } },
|
|
23
|
+
{ id: 'primary-sm', label: 'Primary Small', props: { variant: 'primary', size: 'sm' } },
|
|
24
|
+
{ id: 'primary-lg', label: 'Primary Large', props: { variant: 'primary', size: 'lg' } },
|
|
25
|
+
{ id: 'icon-left', label: 'Icon Left', props: { variant: 'primary', size: 'md', icon: 'add' } },
|
|
26
|
+
{ id: 'icon-right', label: 'Icon Right', props: { variant: 'primary', size: 'md', icon: 'arrow_forward', iconPosition: 'right' } },
|
|
27
|
+
{ id: 'icon-only', label: 'Icon Only', props: { variant: 'ghost', size: 'md', icon: 'settings' } },
|
|
28
|
+
],
|
|
29
|
+
editableProps: [
|
|
30
|
+
{
|
|
31
|
+
name: 'variant',
|
|
32
|
+
label: 'Variant',
|
|
33
|
+
control: 'select',
|
|
34
|
+
options: [
|
|
35
|
+
{ label: 'Primary', value: 'primary' },
|
|
36
|
+
{ label: 'Secondary', value: 'secondary' },
|
|
37
|
+
{ label: 'Ghost', value: 'ghost' },
|
|
38
|
+
{ label: 'Outline', value: 'outline' },
|
|
39
|
+
],
|
|
40
|
+
defaultValue: 'primary',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'size',
|
|
44
|
+
label: 'Size',
|
|
45
|
+
control: 'select',
|
|
46
|
+
options: [
|
|
47
|
+
{ label: 'Small', value: 'sm' },
|
|
48
|
+
{ label: 'Medium', value: 'md' },
|
|
49
|
+
{ label: 'Large', value: 'lg' },
|
|
50
|
+
],
|
|
51
|
+
defaultValue: 'md',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'icon',
|
|
55
|
+
label: 'Icon',
|
|
56
|
+
control: 'text',
|
|
57
|
+
defaultValue: '',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'iconPosition',
|
|
61
|
+
label: 'Icon Position',
|
|
62
|
+
control: 'select',
|
|
63
|
+
options: [
|
|
64
|
+
{ label: 'Left', value: 'left' },
|
|
65
|
+
{ label: 'Right', value: 'right' },
|
|
66
|
+
],
|
|
67
|
+
defaultValue: 'left',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'disabled',
|
|
71
|
+
label: 'Disabled',
|
|
72
|
+
control: 'toggle',
|
|
73
|
+
defaultValue: false,
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'text-input',
|
|
79
|
+
name: 'TextInput',
|
|
80
|
+
importName: 'TextInput',
|
|
81
|
+
categoryId: 'form-controls',
|
|
82
|
+
description: 'Text input field with label support',
|
|
83
|
+
hasChildren: false,
|
|
84
|
+
variants: [
|
|
85
|
+
{ id: 'default', label: 'Default', props: { label: 'Label', value: 'Sample text' } },
|
|
86
|
+
{ id: 'empty', label: 'Empty', props: { label: 'Email', value: '' } },
|
|
87
|
+
{ id: 'disabled', label: 'Disabled', props: { label: 'Disabled', value: 'Read only', disabled: true } },
|
|
88
|
+
],
|
|
89
|
+
editableProps: [
|
|
90
|
+
{
|
|
91
|
+
name: 'label',
|
|
92
|
+
label: 'Label',
|
|
93
|
+
control: 'text',
|
|
94
|
+
defaultValue: 'Label',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'disabled',
|
|
98
|
+
label: 'Disabled',
|
|
99
|
+
control: 'toggle',
|
|
100
|
+
defaultValue: false,
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: 'select',
|
|
106
|
+
name: 'Select',
|
|
107
|
+
importName: 'Select',
|
|
108
|
+
categoryId: 'form-controls',
|
|
109
|
+
description: 'Dropdown selector with options',
|
|
110
|
+
hasChildren: false,
|
|
111
|
+
variants: [
|
|
112
|
+
{
|
|
113
|
+
id: 'default',
|
|
114
|
+
label: 'Default',
|
|
115
|
+
props: {
|
|
116
|
+
label: 'Choose an option',
|
|
117
|
+
value: 'option-1',
|
|
118
|
+
options: [
|
|
119
|
+
{ label: 'Option 1', value: 'option-1' },
|
|
120
|
+
{ label: 'Option 2', value: 'option-2' },
|
|
121
|
+
{ label: 'Option 3', value: 'option-3' },
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: 'disabled',
|
|
127
|
+
label: 'Disabled',
|
|
128
|
+
props: {
|
|
129
|
+
label: 'Disabled select',
|
|
130
|
+
value: 'option-1',
|
|
131
|
+
disabled: true,
|
|
132
|
+
options: [{ label: 'Option 1', value: 'option-1' }],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
editableProps: [
|
|
137
|
+
{
|
|
138
|
+
name: 'label',
|
|
139
|
+
label: 'Label',
|
|
140
|
+
control: 'text',
|
|
141
|
+
defaultValue: 'Choose an option',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: 'disabled',
|
|
145
|
+
label: 'Disabled',
|
|
146
|
+
control: 'toggle',
|
|
147
|
+
defaultValue: false,
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: 'toggle',
|
|
153
|
+
name: 'Toggle',
|
|
154
|
+
importName: 'Toggle',
|
|
155
|
+
categoryId: 'form-controls',
|
|
156
|
+
description: 'Binary switch component',
|
|
157
|
+
hasChildren: false,
|
|
158
|
+
variants: [
|
|
159
|
+
{ id: 'on', label: 'On', props: { label: 'Enabled', value: true } },
|
|
160
|
+
{ id: 'off', label: 'Off', props: { label: 'Disabled', value: false } },
|
|
161
|
+
{ id: 'disabled', label: 'Disabled', props: { label: 'Locked', value: true, disabled: true } },
|
|
162
|
+
],
|
|
163
|
+
editableProps: [
|
|
164
|
+
{
|
|
165
|
+
name: 'label',
|
|
166
|
+
label: 'Label',
|
|
167
|
+
control: 'text',
|
|
168
|
+
defaultValue: 'Toggle',
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: 'disabled',
|
|
172
|
+
label: 'Disabled',
|
|
173
|
+
control: 'toggle',
|
|
174
|
+
defaultValue: false,
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id: 'slider',
|
|
180
|
+
name: 'Slider',
|
|
181
|
+
importName: 'Slider',
|
|
182
|
+
categoryId: 'range-inputs',
|
|
183
|
+
description: 'Numeric range slider',
|
|
184
|
+
hasChildren: false,
|
|
185
|
+
variants: [
|
|
186
|
+
{ id: 'default', label: 'Default', props: { label: 'Volume', value: 50, min: 0, max: 100 } },
|
|
187
|
+
{ id: 'with-step', label: 'With Step', props: { label: 'Steps', value: 3, min: 0, max: 10, step: 1, showValue: true } },
|
|
188
|
+
],
|
|
189
|
+
editableProps: [
|
|
190
|
+
{
|
|
191
|
+
name: 'label',
|
|
192
|
+
label: 'Label',
|
|
193
|
+
control: 'text',
|
|
194
|
+
defaultValue: 'Slider',
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
name: 'min',
|
|
198
|
+
label: 'Min',
|
|
199
|
+
control: 'number',
|
|
200
|
+
defaultValue: 0,
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: 'max',
|
|
204
|
+
label: 'Max',
|
|
205
|
+
control: 'number',
|
|
206
|
+
defaultValue: 100,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'showValue',
|
|
210
|
+
label: 'Show Value',
|
|
211
|
+
control: 'toggle',
|
|
212
|
+
defaultValue: false,
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
name: 'disabled',
|
|
216
|
+
label: 'Disabled',
|
|
217
|
+
control: 'toggle',
|
|
218
|
+
defaultValue: false,
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
id: 'hue-slider',
|
|
224
|
+
name: 'HueSlider',
|
|
225
|
+
importName: 'HueSlider',
|
|
226
|
+
categoryId: 'range-inputs',
|
|
227
|
+
description: 'Specialized slider for hue selection (0-360\u00b0)',
|
|
228
|
+
hasChildren: false,
|
|
229
|
+
variants: [
|
|
230
|
+
{ id: 'default', label: 'Default', props: { label: 'Hue', value: 180 } },
|
|
231
|
+
{ id: 'red', label: 'Red', props: { label: 'Warm Hue', value: 0 } },
|
|
232
|
+
{ id: 'blue', label: 'Blue', props: { label: 'Cool Hue', value: 240 } },
|
|
233
|
+
],
|
|
234
|
+
editableProps: [
|
|
235
|
+
{
|
|
236
|
+
name: 'label',
|
|
237
|
+
label: 'Label',
|
|
238
|
+
control: 'text',
|
|
239
|
+
defaultValue: 'Hue',
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
name: 'showValue',
|
|
243
|
+
label: 'Show Value',
|
|
244
|
+
control: 'toggle',
|
|
245
|
+
defaultValue: false,
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: 'disabled',
|
|
249
|
+
label: 'Disabled',
|
|
250
|
+
control: 'toggle',
|
|
251
|
+
defaultValue: false,
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
id: 'frame',
|
|
257
|
+
name: 'Frame',
|
|
258
|
+
importName: 'Frame',
|
|
259
|
+
categoryId: 'layout',
|
|
260
|
+
description: 'Foundational layout container with theme, elevation, spacing, and interactivity',
|
|
261
|
+
hasChildren: true,
|
|
262
|
+
variants: [
|
|
263
|
+
{ id: 'default', label: 'Default', props: {} },
|
|
264
|
+
{ id: 'primary', label: 'Primary', props: { theme: 'primary' } },
|
|
265
|
+
{ id: 'secondary', label: 'Secondary', props: { theme: 'secondary' } },
|
|
266
|
+
{ id: 'strong', label: 'Strong', props: { theme: 'strong' } },
|
|
267
|
+
{ id: 'deeply-sunken', label: 'Deeply Sunken', props: { elevation: -2, radius: 'md' } },
|
|
268
|
+
{ id: 'sunken', label: 'Sunken', props: { elevation: -1 } },
|
|
269
|
+
{ id: 'elevated', label: 'Elevated', props: { elevation: 1 } },
|
|
270
|
+
{ id: 'prominent', label: 'Prominent', props: { elevation: 2, radius: 'lg' } },
|
|
271
|
+
{ id: 'padded', label: 'Padded', props: { padding: 'lg', gap: 'md' } },
|
|
272
|
+
{ id: 'horizontal', label: 'Horizontal', props: { direction: 'horizontal', gap: 'md', align: 'center' } },
|
|
273
|
+
{ id: 'grid-3col', label: '3-Column Grid', props: { layout: 'grid', columns: 3, gap: 'md' } },
|
|
274
|
+
{ id: 'card-like', label: 'Card-like', props: { radius: 'lg', padding: 'lg', bordered: true, elevation: 1 } },
|
|
275
|
+
],
|
|
276
|
+
editableProps: [
|
|
277
|
+
{
|
|
278
|
+
name: 'theme',
|
|
279
|
+
label: 'Theme',
|
|
280
|
+
control: 'select',
|
|
281
|
+
options: [
|
|
282
|
+
{ label: 'Neutral', value: 'neutral' },
|
|
283
|
+
{ label: 'Primary', value: 'primary' },
|
|
284
|
+
{ label: 'Secondary', value: 'secondary' },
|
|
285
|
+
{ label: 'Strong', value: 'strong' },
|
|
286
|
+
],
|
|
287
|
+
defaultValue: 'neutral',
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
name: 'elevation',
|
|
291
|
+
label: 'Elevation',
|
|
292
|
+
control: 'select',
|
|
293
|
+
options: [
|
|
294
|
+
{ label: 'Deeply Sunken (-2)', value: -2 },
|
|
295
|
+
{ label: 'Sunken (-1)', value: -1 },
|
|
296
|
+
{ label: 'Default (0)', value: 0 },
|
|
297
|
+
{ label: 'Elevated (1)', value: 1 },
|
|
298
|
+
{ label: 'Prominent (2)', value: 2 },
|
|
299
|
+
],
|
|
300
|
+
defaultValue: 0,
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
name: 'direction',
|
|
304
|
+
label: 'Direction',
|
|
305
|
+
control: 'select',
|
|
306
|
+
options: [
|
|
307
|
+
{ label: 'Vertical', value: 'vertical' },
|
|
308
|
+
{ label: 'Horizontal', value: 'horizontal' },
|
|
309
|
+
],
|
|
310
|
+
defaultValue: 'vertical',
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
name: 'padding',
|
|
314
|
+
label: 'Padding',
|
|
315
|
+
control: 'select',
|
|
316
|
+
options: [
|
|
317
|
+
{ label: 'None', value: '' },
|
|
318
|
+
{ label: 'Small', value: 'sm' },
|
|
319
|
+
{ label: 'Medium', value: 'md' },
|
|
320
|
+
{ label: 'Large', value: 'lg' },
|
|
321
|
+
{ label: 'Extra Large', value: 'xl' },
|
|
322
|
+
],
|
|
323
|
+
defaultValue: '',
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
name: 'gap',
|
|
327
|
+
label: 'Gap',
|
|
328
|
+
control: 'select',
|
|
329
|
+
options: [
|
|
330
|
+
{ label: 'None', value: '' },
|
|
331
|
+
{ label: 'Small', value: 'sm' },
|
|
332
|
+
{ label: 'Medium', value: 'md' },
|
|
333
|
+
{ label: 'Large', value: 'lg' },
|
|
334
|
+
],
|
|
335
|
+
defaultValue: '',
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
name: 'radius',
|
|
339
|
+
label: 'Radius',
|
|
340
|
+
control: 'select',
|
|
341
|
+
options: [
|
|
342
|
+
{ label: 'None', value: 'none' },
|
|
343
|
+
{ label: 'Small', value: 'sm' },
|
|
344
|
+
{ label: 'Medium', value: 'md' },
|
|
345
|
+
{ label: 'Large', value: 'lg' },
|
|
346
|
+
{ label: 'Pill', value: 'pill' },
|
|
347
|
+
],
|
|
348
|
+
defaultValue: 'none',
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
name: 'bordered',
|
|
352
|
+
label: 'Bordered',
|
|
353
|
+
control: 'toggle',
|
|
354
|
+
defaultValue: false,
|
|
355
|
+
},
|
|
356
|
+
],
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
id: 'card',
|
|
360
|
+
name: 'Card',
|
|
361
|
+
importName: 'Card',
|
|
362
|
+
categoryId: 'layout',
|
|
363
|
+
description: 'Surface container with elevation levels',
|
|
364
|
+
hasChildren: true,
|
|
365
|
+
variants: [
|
|
366
|
+
{ id: 'elevation-0', label: 'Elevation 0', props: { elevation: 0 } },
|
|
367
|
+
{ id: 'elevation-1', label: 'Elevation 1', props: { elevation: 1 } },
|
|
368
|
+
{ id: 'elevation-2', label: 'Elevation 2', props: { elevation: 2 } },
|
|
369
|
+
],
|
|
370
|
+
editableProps: [
|
|
371
|
+
{
|
|
372
|
+
name: 'elevation',
|
|
373
|
+
label: 'Elevation',
|
|
374
|
+
control: 'select',
|
|
375
|
+
options: [
|
|
376
|
+
{ label: 'Level 0', value: 0 },
|
|
377
|
+
{ label: 'Level 1', value: 1 },
|
|
378
|
+
{ label: 'Level 2', value: 2 },
|
|
379
|
+
],
|
|
380
|
+
defaultValue: 0,
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
name: 'disabled',
|
|
384
|
+
label: 'Disabled',
|
|
385
|
+
control: 'toggle',
|
|
386
|
+
defaultValue: false,
|
|
387
|
+
},
|
|
388
|
+
],
|
|
389
|
+
},
|
|
390
|
+
];
|
|
391
|
+
|
|
392
|
+
export function getComponent(id: string): ComponentMeta | undefined {
|
|
393
|
+
return COMPONENTS.find((c) => c.id === id);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export function getCategory(id: string): CategoryMeta | undefined {
|
|
397
|
+
return CATEGORIES.find((c) => c.id === id);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export function getComponentsByCategory(categoryId: string): readonly ComponentMeta[] {
|
|
401
|
+
return COMPONENTS.filter((c) => c.categoryId === categoryId);
|
|
402
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface CategoryMeta {
|
|
2
|
+
readonly id: string;
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly description: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface VariantMeta {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
readonly label: string;
|
|
10
|
+
readonly props: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface EditablePropOption {
|
|
14
|
+
readonly label: string;
|
|
15
|
+
readonly value: string | number | boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface EditableProp {
|
|
19
|
+
readonly name: string;
|
|
20
|
+
readonly label: string;
|
|
21
|
+
readonly control: 'select' | 'text' | 'toggle' | 'number';
|
|
22
|
+
readonly options?: readonly EditablePropOption[];
|
|
23
|
+
readonly defaultValue: string | number | boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ComponentMeta {
|
|
27
|
+
readonly id: string;
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly importName: string;
|
|
30
|
+
readonly categoryId: string;
|
|
31
|
+
readonly description: string;
|
|
32
|
+
readonly hasChildren: boolean;
|
|
33
|
+
readonly variants: readonly VariantMeta[];
|
|
34
|
+
readonly editableProps: readonly EditableProp[];
|
|
35
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
import type { ThemeName, ElevationLevel } from './types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Context value provided by Frame to its descendants.
|
|
6
|
+
* Contains the resolved theme and elevation that children should use.
|
|
7
|
+
*/
|
|
8
|
+
export interface FrameContextValue {
|
|
9
|
+
readonly theme: ThemeName;
|
|
10
|
+
readonly elevation: ElevationLevel;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* FrameContext - Propagates theme and elevation overrides from Frame to descendants.
|
|
15
|
+
*
|
|
16
|
+
* When null, components fall back to NewtoneProvider's theme and default elevation (1).
|
|
17
|
+
* When present, useTokens() reads from this context instead.
|
|
18
|
+
*/
|
|
19
|
+
export const FrameContext = createContext<FrameContextValue | null>(null);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* useFrameContext - Read the nearest Frame's context, if any.
|
|
23
|
+
*
|
|
24
|
+
* Returns null when no parent Frame exists. This is intentionally not an error —
|
|
25
|
+
* components outside a Frame simply fall back to the NewtoneProvider defaults.
|
|
26
|
+
*/
|
|
27
|
+
export function useFrameContext(): FrameContextValue | null {
|
|
28
|
+
return useContext(FrameContext);
|
|
29
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React, { createContext, useState, useMemo, useContext } from 'react';
|
|
2
2
|
import type { NewtoneThemeConfig, NewtoneThemeContext, ColorMode, ThemeName } from './types';
|
|
3
3
|
import { DEFAULT_THEME_CONFIG } from './defaults';
|
|
4
|
+
import { GoogleFontLoader } from '../fonts/GoogleFontLoader';
|
|
5
|
+
import { IconFontLoader } from '../fonts/IconFontLoader';
|
|
4
6
|
|
|
5
7
|
const ThemeContext = createContext<NewtoneThemeContext | null>(null);
|
|
6
8
|
|
|
@@ -43,7 +45,13 @@ export function NewtoneProvider({
|
|
|
43
45
|
[config, mode, theme]
|
|
44
46
|
);
|
|
45
47
|
|
|
46
|
-
return
|
|
48
|
+
return (
|
|
49
|
+
<ThemeContext.Provider value={value}>
|
|
50
|
+
<GoogleFontLoader fonts={config.typography.fonts} />
|
|
51
|
+
<IconFontLoader icons={config.icons} />
|
|
52
|
+
{children}
|
|
53
|
+
</ThemeContext.Provider>
|
|
54
|
+
);
|
|
47
55
|
}
|
|
48
56
|
|
|
49
57
|
/**
|
package/src/theme/defaults.ts
CHANGED
|
@@ -39,4 +39,55 @@ export const DEFAULT_THEME_CONFIG: NewtoneThemeConfig = {
|
|
|
39
39
|
elevation: {
|
|
40
40
|
offsets: [-0.02, 0, 0.04], // [sunken, default, elevated]
|
|
41
41
|
},
|
|
42
|
+
spacing: {
|
|
43
|
+
xs: 4,
|
|
44
|
+
sm: 8,
|
|
45
|
+
md: 12,
|
|
46
|
+
lg: 16,
|
|
47
|
+
xl: 24,
|
|
48
|
+
xxl: 32,
|
|
49
|
+
},
|
|
50
|
+
radius: {
|
|
51
|
+
none: 0,
|
|
52
|
+
sm: 4,
|
|
53
|
+
md: 6,
|
|
54
|
+
lg: 8,
|
|
55
|
+
xl: 12,
|
|
56
|
+
pill: 999,
|
|
57
|
+
},
|
|
58
|
+
typography: {
|
|
59
|
+
fonts: {
|
|
60
|
+
mono: {
|
|
61
|
+
type: 'system',
|
|
62
|
+
family: 'ui-monospace',
|
|
63
|
+
fallback: 'SFMono-Regular, Menlo, Monaco, Consolas, monospace',
|
|
64
|
+
},
|
|
65
|
+
display: {
|
|
66
|
+
type: 'system',
|
|
67
|
+
family: 'system-ui',
|
|
68
|
+
fallback: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
69
|
+
},
|
|
70
|
+
default: {
|
|
71
|
+
type: 'system',
|
|
72
|
+
family: 'system-ui',
|
|
73
|
+
fallback: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
scale: {
|
|
77
|
+
xs: 10, // 16 / 1.25^2
|
|
78
|
+
sm: 13, // 16 / 1.25
|
|
79
|
+
base: 16,
|
|
80
|
+
md: 20, // 16 * 1.25
|
|
81
|
+
lg: 25, // 16 * 1.25^2
|
|
82
|
+
xl: 31, // 16 * 1.25^3
|
|
83
|
+
xxl: 39, // 16 * 1.25^4
|
|
84
|
+
},
|
|
85
|
+
lineHeight: { tight: 1.25, normal: 1.5, relaxed: 1.75 },
|
|
86
|
+
fontWeight: { regular: 400, medium: 500, semibold: 600, bold: 700 },
|
|
87
|
+
},
|
|
88
|
+
icons: {
|
|
89
|
+
variant: 'rounded', // Material Design 3 aesthetic
|
|
90
|
+
weight: 400, // Normal weight
|
|
91
|
+
autoGrade: true, // Enable mode-aware grade
|
|
92
|
+
},
|
|
42
93
|
};
|