@basic-ui/material 1.0.0-alpha.16 → 1.0.0-alpha.17

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 (114) hide show
  1. package/build/cjs/index.js +294 -51
  2. package/build/cjs/index.js.map +1 -1
  3. package/build/esm/Alert/Alert.d.ts +1 -1
  4. package/build/esm/AppBar/AppBarButton.d.ts +1 -1
  5. package/build/esm/BottomSheet/BottomSheet.d.ts +1 -1
  6. package/build/esm/BottomSheet/BottomSheetSurface.d.ts +1 -1
  7. package/build/esm/Button/Button.d.ts +1 -1
  8. package/build/esm/Button/ButtonGroup.d.ts +1 -1
  9. package/build/esm/Button/FilledButton.d.ts +1 -1
  10. package/build/esm/Button/OutlinedButton.d.ts +1 -1
  11. package/build/esm/Button/TransparentButton.d.ts +1 -1
  12. package/build/esm/CheckBox/CheckBox.d.ts +1 -1
  13. package/build/esm/Chip/ButtonChip.d.ts +1 -1
  14. package/build/esm/Chip/ChipBase.d.ts +1 -1
  15. package/build/esm/Chip/ChoiceChip.d.ts +1 -1
  16. package/build/esm/Combobox/Combobox.d.ts +7 -7
  17. package/build/esm/Combobox/Combobox.js +4 -3
  18. package/build/esm/Combobox/Combobox.js.map +1 -1
  19. package/build/esm/Dialog/Dialog.d.ts +1 -1
  20. package/build/esm/Dialog/DialogBackdrop.d.ts +1 -1
  21. package/build/esm/Dialog/DialogSurface.d.ts +1 -1
  22. package/build/esm/Dialog/Scrim.d.ts +1 -1
  23. package/build/esm/Divider/Divider.d.ts +1 -1
  24. package/build/esm/Link/Link.d.ts +1 -1
  25. package/build/esm/Link/Link.js +12 -0
  26. package/build/esm/Link/Link.js.map +1 -1
  27. package/build/esm/ListItem/ListItem.d.ts +3 -1
  28. package/build/esm/ListItem/ListItem.js +46 -42
  29. package/build/esm/ListItem/ListItem.js.map +1 -1
  30. package/build/esm/Menu/Menu.d.ts +4 -4
  31. package/build/esm/NavRail/NavRailItem.d.ts +3 -3
  32. package/build/esm/Paper/Paper.d.ts +1 -1
  33. package/build/esm/RadioButton/RadioGroup.d.ts +1 -1
  34. package/build/esm/Ripple/useRippleSurface.d.ts +1 -0
  35. package/build/esm/Ripple/useRippleSurface.js +17 -16
  36. package/build/esm/Ripple/useRippleSurface.js.map +1 -1
  37. package/build/esm/Select/Select.d.ts +1 -1
  38. package/build/esm/Select/Select.js +4 -0
  39. package/build/esm/Select/Select.js.map +1 -1
  40. package/build/esm/Select/SelectIcon.d.ts +1 -1
  41. package/build/esm/SelectItem/SelectItem.d.ts +5 -2
  42. package/build/esm/SelectItem/SelectItem.js +17 -4
  43. package/build/esm/SelectItem/SelectItem.js.map +1 -1
  44. package/build/esm/SelectionControl/SelectionControlText.d.ts +1 -1
  45. package/build/esm/Skeleton/Skeleton.d.ts +1 -1
  46. package/build/esm/Slider/Slider.d.ts +17 -0
  47. package/build/esm/Slider/Slider.js +224 -0
  48. package/build/esm/Slider/Slider.js.map +1 -0
  49. package/build/esm/Slider/index.d.ts +1 -0
  50. package/build/esm/Slider/index.js +2 -0
  51. package/build/esm/Slider/index.js.map +1 -0
  52. package/build/esm/Snackbar/Snackbar.d.ts +1 -1
  53. package/build/esm/Switch/Switch.d.ts +1 -1
  54. package/build/esm/Tab/Tab.d.ts +1 -1
  55. package/build/esm/Tab/TabList.d.ts +1 -1
  56. package/build/esm/Tab/TabPanel.d.ts +1 -1
  57. package/build/esm/TabIndicator/TabIndicator.d.ts +1 -1
  58. package/build/esm/Table/TableHead.d.ts +1 -1
  59. package/build/esm/Table/TableRow.d.ts +1 -1
  60. package/build/esm/TextField/FilledContainer.d.ts +1 -1
  61. package/build/esm/TextField/HelperText.d.ts +1 -1
  62. package/build/esm/TextField/Input.d.ts +1 -1
  63. package/build/esm/TextField/OutlinedContainer.d.ts +1 -1
  64. package/build/esm/TextField/OutlinedContainer.js +13 -5
  65. package/build/esm/TextField/OutlinedContainer.js.map +1 -1
  66. package/build/esm/TextField/TextField.d.ts +1 -1
  67. package/build/esm/ThemeExplorer/BorderSlider.d.ts +7 -0
  68. package/build/esm/ThemeExplorer/BorderSlider.js +78 -0
  69. package/build/esm/ThemeExplorer/BorderSlider.js.map +1 -0
  70. package/build/esm/ThemeExplorer/ColorSchemePicker.d.ts +10 -0
  71. package/build/esm/ThemeExplorer/ColorSchemePicker.js +54 -0
  72. package/build/esm/ThemeExplorer/ColorSchemePicker.js.map +1 -0
  73. package/build/esm/ThemeExplorer/FontAutoComplete.d.ts +9 -0
  74. package/build/esm/ThemeExplorer/FontAutoComplete.js +128 -0
  75. package/build/esm/ThemeExplorer/FontAutoComplete.js.map +1 -0
  76. package/build/esm/ThemeExplorer/ThemeBuilder.d.ts +2 -0
  77. package/build/esm/ThemeExplorer/ThemeBuilder.js +231 -93
  78. package/build/esm/ThemeExplorer/ThemeBuilder.js.map +1 -1
  79. package/build/esm/ThemeExplorer/components.js +4 -4
  80. package/build/esm/ThemeExplorer/components.js.map +1 -1
  81. package/build/esm/ThemeExplorer/googleFonts.d.ts +1 -0
  82. package/build/esm/ThemeExplorer/googleFonts.js +7 -0
  83. package/build/esm/ThemeExplorer/googleFonts.js.map +1 -0
  84. package/build/esm/ThemeExplorer/updateGoogleFonts.js +70 -0
  85. package/build/esm/ThemeExplorer/updateGoogleFonts.js.map +1 -0
  86. package/build/esm/ThemeExplorer/useDeferredColor.js +3 -4
  87. package/build/esm/ThemeExplorer/useDeferredColor.js.map +1 -1
  88. package/build/esm/Tooltip/Tooltip.d.ts +1 -1
  89. package/build/esm/index.d.ts +1 -0
  90. package/build/esm/index.js +1 -0
  91. package/build/esm/index.js.map +1 -1
  92. package/build/tsconfig-build.tsbuildinfo +1 -1
  93. package/package.json +6 -6
  94. package/src/Combobox/Combobox.tsx +5 -2
  95. package/src/Link/Link.tsx +12 -0
  96. package/src/ListItem/ListItem.tsx +48 -41
  97. package/src/Ripple/useRippleSurface.ts +8 -2
  98. package/src/Select/PaymentMethodSelect.story.tsx +17 -24
  99. package/src/Select/Select.tsx +6 -0
  100. package/src/SelectItem/SelectItem.tsx +13 -3
  101. package/src/Slider/Slider.story.tsx +36 -0
  102. package/src/Slider/Slider.tsx +275 -0
  103. package/src/Slider/index.ts +1 -0
  104. package/src/TextField/OutlinedContainer.tsx +8 -3
  105. package/src/ThemeExplorer/BorderSlider.tsx +73 -0
  106. package/src/ThemeExplorer/ColorSchemePicker.tsx +55 -0
  107. package/src/ThemeExplorer/FontAutoComplete.tsx +139 -0
  108. package/src/ThemeExplorer/ThemeBuilder.story.tsx +2 -1
  109. package/src/ThemeExplorer/ThemeBuilder.tsx +218 -82
  110. package/src/ThemeExplorer/components.tsx +4 -4
  111. package/src/ThemeExplorer/googleFonts.ts +1436 -0
  112. package/src/ThemeExplorer/updateGoogleFonts.js +33 -0
  113. package/src/ThemeExplorer/useDeferredColor.tsx +3 -6
  114. package/src/index.ts +1 -0
@@ -0,0 +1 @@
1
+ export * from './Slider';
@@ -58,12 +58,17 @@ export const OutlinedContainer = forwardRef<
58
58
 
59
59
  if (element) {
60
60
  setLabelWidth(element.offsetWidth);
61
- (document as any)?.fonts?.ready?.then(() => {
61
+ function handleFontsLoaded() {
62
62
  // set the label width again once the fonts have been loaded
63
63
  requestAnimationFrame(() => {
64
- setLabelWidth(element.offsetWidth);
64
+ if (element) setLabelWidth(element.offsetWidth);
65
65
  });
66
- });
66
+ }
67
+ document.fonts?.addEventListener?.('loadingdone', handleFontsLoaded);
68
+ document.fonts?.ready?.then(handleFontsLoaded);
69
+
70
+ return () =>
71
+ document.fonts?.removeEventListener?.('loadingdone', handleFontsLoaded);
67
72
  }
68
73
  }, [label]);
69
74
 
@@ -0,0 +1,73 @@
1
+ import type { ButtonHTMLAttributes, FC } from 'react';
2
+ import { startTransition, useEffect, useRef, useState } from 'react';
3
+
4
+ import type { BoxProps } from '../Box';
5
+ import { Box } from '../Box';
6
+ import { Link as BaseLink } from '../Link';
7
+ import { Slider, SliderMarker } from '../Slider';
8
+ import { Text } from '../Text';
9
+
10
+ const Link: FC<
11
+ BoxProps<HTMLButtonElement, ButtonHTMLAttributes<HTMLButtonElement>>
12
+ > = BaseLink as any;
13
+
14
+ export function BorderSlider(props: {
15
+ label: string;
16
+ defaultValue: number;
17
+ onChange: (value: number) => void;
18
+ onReset: () => void;
19
+ resetDisabled: boolean;
20
+ }) {
21
+ const [value, setValue] = useState(props.defaultValue);
22
+
23
+ const resetting = useRef(false);
24
+ useEffect(() => {
25
+ if (resetting.current) {
26
+ setValue(props.defaultValue);
27
+ resetting.current = false;
28
+ }
29
+ }, [props.defaultValue]);
30
+
31
+ return (
32
+ <Box width="100%" display="flex" flexDirection="column">
33
+ <Text display="flex" justifyContent="space-between" width="100%" pb={2}>
34
+ <span>
35
+ {props.label}{' '}
36
+ <Link
37
+ as="button"
38
+ disabled={props.resetDisabled ? true : undefined}
39
+ onClick={() => {
40
+ resetting.current = true;
41
+ props.onReset();
42
+ }}
43
+ >
44
+ Reset
45
+ </Link>
46
+ </span>
47
+ <span>{value}px</span>
48
+ </Text>
49
+ <Slider
50
+ value={value}
51
+ onChange={(e, v) => {
52
+ setValue(v);
53
+ startTransition(() => {
54
+ props.onChange(v);
55
+ });
56
+ }}
57
+ min={0}
58
+ max={64}
59
+ step={2}
60
+ >
61
+ <SliderMarker value={2} />
62
+ <SliderMarker value={4} />
63
+ <SliderMarker value={8} />
64
+ <SliderMarker value={12} />
65
+ <SliderMarker value={16} />
66
+ <SliderMarker value={24} />
67
+ <SliderMarker value={32} />
68
+ <SliderMarker value={40} />
69
+ <SliderMarker value={48} />
70
+ </Slider>
71
+ </Box>
72
+ );
73
+ }
@@ -0,0 +1,55 @@
1
+ import type { Color } from '@basic-ui/color-picker';
2
+ import { startTransition, useEffect, useRef, useState } from 'react';
3
+
4
+ import { Box } from '../Box';
5
+ import { Button } from '../Button';
6
+ import { TextFieldColorPicker } from './TextFieldColorPicker';
7
+
8
+ interface ColorSchemePickerProps {
9
+ label: string;
10
+ initialValue: Color;
11
+ onChange: (c: Color) => void;
12
+ onReset: () => void;
13
+ resetDisabled: boolean;
14
+ }
15
+
16
+ export function ColorSchemePicker(props: ColorSchemePickerProps) {
17
+ const { label, initialValue, onChange, onReset, resetDisabled } = props;
18
+ const [value, setValue] = useState(initialValue);
19
+
20
+ const remountKey = useRef(0);
21
+ const resetting = useRef(false);
22
+ useEffect(() => {
23
+ if (resetting.current) {
24
+ remountKey.current++;
25
+ resetting.current = false;
26
+ setValue(props.initialValue);
27
+ }
28
+ }, [props.initialValue]);
29
+
30
+ return (
31
+ <Box display="flex" minWidth="300px" flex="1" alignItems="center">
32
+ <TextFieldColorPicker
33
+ key={remountKey.current}
34
+ label={label}
35
+ value={value}
36
+ onChange={(c) => {
37
+ setValue(c);
38
+ startTransition(() => {
39
+ onChange(c);
40
+ });
41
+ }}
42
+ />
43
+ <Button
44
+ variant="text"
45
+ disabled={resetDisabled}
46
+ onClick={() => {
47
+ resetting.current = true;
48
+ onReset();
49
+ }}
50
+ >
51
+ Reset
52
+ </Button>
53
+ </Box>
54
+ );
55
+ }
@@ -0,0 +1,139 @@
1
+ import {
2
+ useState,
3
+ useMemo,
4
+ useEffect,
5
+ useRef,
6
+ useDeferredValue,
7
+ memo,
8
+ } from 'react';
9
+
10
+ import { Box } from '../Box';
11
+ import { Button } from '../Button';
12
+ import {
13
+ Combobox,
14
+ ComboboxOption,
15
+ ComboboxList,
16
+ ComboboxPopover,
17
+ ComboboxInput,
18
+ ComboboxButton,
19
+ } from '../Combobox';
20
+ import { Text } from '../Text';
21
+ import { googleFonts } from './googleFonts';
22
+
23
+ export interface FontAutoCompleteProps {
24
+ label?: string;
25
+ initialValue?: string;
26
+ onSelect?: (font: string) => void;
27
+ onReset: () => void;
28
+ resetDisabled: boolean;
29
+ }
30
+
31
+ function useFontMatch(searchTerm: string) {
32
+ return useMemo(() => {
33
+ const term = searchTerm.trim().toLowerCase();
34
+ return term === ''
35
+ ? googleFonts
36
+ : googleFonts.filter((font) => font.toLowerCase().indexOf(term) !== -1);
37
+ }, [searchTerm]);
38
+ }
39
+
40
+ export function parseMainFont(fontStr: string): string | undefined {
41
+ const fonts = fontStr.split(/, */).map((f) => f.replace(/"([^"]+)"/, '$1'));
42
+ return fonts.length > 0 ? fonts[0] : undefined;
43
+ }
44
+
45
+ export function FontAutoComplete({
46
+ initialValue = '',
47
+ onSelect,
48
+ label,
49
+ onReset,
50
+ resetDisabled,
51
+ }: FontAutoCompleteProps) {
52
+ const [term, setTerm] = useState<undefined | string>(undefined);
53
+ const filterTerm = useDeferredValue(term);
54
+ const [selected, setSelected] = useState(initialValue);
55
+
56
+ const resetting = useRef(false);
57
+ useEffect(() => {
58
+ if (resetting.current) {
59
+ setSelected(initialValue);
60
+ setTerm(undefined);
61
+ resetting.current = false;
62
+ }
63
+ }, [initialValue]);
64
+
65
+ const results = useFontMatch(filterTerm || '');
66
+ return (
67
+ <Box display="flex" minWidth="300px" flex="1" alignItems="center">
68
+ <Combobox
69
+ onSelect={(text) => {
70
+ setSelected(text);
71
+ setTerm(text);
72
+ onSelect?.(text);
73
+ }}
74
+ selectOnBlur
75
+ width="100%"
76
+ >
77
+ <Box position="relative" width="100%">
78
+ <ComboboxInput
79
+ onChange={(e) => {
80
+ setTerm(e.target.value);
81
+ }}
82
+ value={term === undefined ? selected : term}
83
+ label={label}
84
+ />
85
+ <Box
86
+ height={56}
87
+ width={56}
88
+ position="absolute"
89
+ justifyContent="center"
90
+ alignItems="center"
91
+ display="flex"
92
+ top={0}
93
+ right={0}
94
+ bottom={0}
95
+ >
96
+ <ComboboxButton />
97
+ </Box>
98
+ </Box>
99
+ <SearchResults results={results} />
100
+ </Combobox>
101
+ <Button
102
+ variant="text"
103
+ onClick={() => {
104
+ resetting.current = true;
105
+ onReset();
106
+ }}
107
+ disabled={resetDisabled}
108
+ >
109
+ Reset
110
+ </Button>
111
+ </Box>
112
+ );
113
+ }
114
+
115
+ const SearchResults = memo(function SearchResults(props: {
116
+ results: string[];
117
+ }) {
118
+ if (!props.results) {
119
+ return null;
120
+ }
121
+
122
+ return (
123
+ <ComboboxPopover zIndex={100}>
124
+ <ComboboxList persistSelection={true}>
125
+ <Text variant="label-medium" opacity={0.6} px={3} py={2}>
126
+ Sorted by popularity
127
+ </Text>
128
+ {props.results.map((result) => (
129
+ <ComboboxOption
130
+ key={result}
131
+ id={result}
132
+ text={result}
133
+ value={result}
134
+ />
135
+ ))}
136
+ </ComboboxList>
137
+ </ComboboxPopover>
138
+ );
139
+ });
@@ -1,7 +1,8 @@
1
+ import { theme } from '../theme/theme';
1
2
  import { ThemeBuilder } from './ThemeBuilder';
2
3
 
3
4
  export default {
4
5
  title: 'components/ThemeExplorer',
5
6
  };
6
7
 
7
- export const Builder = () => <ThemeBuilder />;
8
+ export const Builder = () => <ThemeBuilder baseTheme={theme} />;
@@ -1,5 +1,5 @@
1
1
  import type { ComponentType } from 'react';
2
- import { useState, useMemo, useDeferredValue, useEffect } from 'react';
2
+ import { useState, useMemo, useEffect } from 'react';
3
3
  import {
4
4
  CorePalette,
5
5
  argbFromHex,
@@ -11,18 +11,22 @@ import {
11
11
  THEME_OVERRIDE_STORAGE_KEY,
12
12
  injectThemeOverride,
13
13
  transformTheme,
14
+ loadGoogleFont,
14
15
  } from '@basic-ui/dynamic-theme';
16
+ import { rem } from 'polished';
17
+ import remToPx from 'polished/lib/helpers/remToPx';
15
18
 
16
19
  import { Box } from '../Box';
17
20
  import { TonalColors } from './components';
18
- import { TextFieldColorPicker } from './TextFieldColorPicker';
19
21
  import { ThemeColors } from './ThemeColors';
20
22
  import { makeColorScheme } from './makeColorScheme';
21
- import { theme } from '../theme';
22
23
  import { Text } from '../Text';
23
- import { Button } from '../Button';
24
24
  import { useLocalStorageCachedState } from './useLocalStorageCachedState';
25
25
  import { useDeferredColor } from './useDeferredColor';
26
+ import { FontAutoComplete, parseMainFont } from './FontAutoComplete';
27
+ import type { Theme } from '../theme/theme';
28
+ import { BorderSlider } from './BorderSlider';
29
+ import { ColorSchemePicker } from './ColorSchemePicker';
26
30
 
27
31
  export interface ThemeBuilderProps {
28
32
  CodeHighlightingComponent?: ComponentType<{
@@ -30,6 +34,7 @@ export interface ThemeBuilderProps {
30
34
  className: string;
31
35
  showButtons?: boolean;
32
36
  }>;
37
+ baseTheme: Theme;
33
38
  }
34
39
 
35
40
  function DefaultHighlighter(props: { children: string }) {
@@ -50,16 +55,52 @@ export const ThemeBuilder = (props: ThemeBuilderProps) => {
50
55
  };
51
56
 
52
57
  const ThemeBuilderImpl = (props: ThemeBuilderProps) => {
53
- const { CodeHighlightingComponent = DefaultHighlighter } = props;
58
+ const { baseTheme: theme, CodeHighlightingComponent = DefaultHighlighter } =
59
+ props;
60
+ const [plainFont, setPlainFont] = useLocalStorageCachedState(
61
+ parseMainFont(theme.fonts.plain),
62
+ 'fontFamily.plain'
63
+ );
64
+ const [brandFont, setBrandFont] = useLocalStorageCachedState(
65
+ parseMainFont(theme.fonts.brand),
66
+ 'fontFamily.brand'
67
+ );
68
+
69
+ const [radiusExtraSmall, setRadiusExtraSmall] =
70
+ useLocalStorageCachedState<number>(
71
+ parseInt(remToPx(theme.radii['extra-small'])),
72
+ 'radii.extra-small'
73
+ );
74
+
75
+ const [radiusSmall, setRadiusSmall] = useLocalStorageCachedState<number>(
76
+ parseInt(remToPx(theme.radii['small'])),
77
+ 'radii.small'
78
+ );
79
+
80
+ const [radiusMedium, setRadiusMedium] = useLocalStorageCachedState<number>(
81
+ parseInt(remToPx(theme.radii['medium'])),
82
+ 'radii.medium'
83
+ );
84
+
85
+ const [radiusLarge, setRadiusLarge] = useLocalStorageCachedState<number>(
86
+ parseInt(remToPx(theme.radii['large'])),
87
+ 'radii.large'
88
+ );
89
+
90
+ const [radiusExtraLarge, setRadiusExtraLarge] =
91
+ useLocalStorageCachedState<number>(
92
+ parseInt(remToPx(theme.radii['extra-large'])),
93
+ 'radii.extra-large'
94
+ );
95
+
54
96
  const [primaryColor, setPrimaryColor] = useLocalStorageCachedState<Color>(
55
97
  toColor('hex', theme.colors.primary),
56
98
  'primary'
57
99
  );
58
- const deferredPrimaryColor = useDeferredValue(primaryColor);
59
100
 
60
101
  const { a1, a2, a3, error, n1, n2 } = useMemo(
61
- () => CorePalette.of(argbFromHex(deferredPrimaryColor.hex)),
62
- [deferredPrimaryColor.hex]
102
+ () => CorePalette.of(argbFromHex(primaryColor.hex)),
103
+ [primaryColor.hex]
63
104
  );
64
105
 
65
106
  const [secondaryColor, secondaryColorTonal, setSecondaryColor] =
@@ -84,10 +125,43 @@ const ThemeBuilderImpl = (props: ThemeBuilderProps) => {
84
125
  [a1, secondaryColorTonal, tertiaryColorTonal, error, neutralColorTonal, n2]
85
126
  );
86
127
 
128
+ const newTheme = useMemo(
129
+ () => ({
130
+ colors: scheme,
131
+ fonts: { plain: `"${plainFont}"`, brand: `"${brandFont}"` },
132
+ radii: {
133
+ 'extra-small': rem(radiusExtraSmall),
134
+ 'extra-small_top': `${rem(radiusExtraSmall)} ${rem(
135
+ radiusExtraSmall
136
+ )} 0 0`,
137
+ small: rem(radiusSmall),
138
+ medium: rem(radiusMedium),
139
+ large: rem(radiusLarge),
140
+ large_end: `0 ${rem(radiusLarge)} ${rem(radiusLarge)} 0`,
141
+ large_top: `${rem(radiusLarge)} ${rem(radiusLarge)} 0 0`,
142
+ 'extra-large': rem(radiusExtraLarge),
143
+ 'extra-large_top': `${rem(radiusExtraLarge)} ${rem(
144
+ radiusExtraLarge
145
+ )} 0 0`,
146
+ },
147
+ }),
148
+ [
149
+ scheme,
150
+ plainFont,
151
+ brandFont,
152
+ radiusExtraSmall,
153
+ radiusSmall,
154
+ radiusMedium,
155
+ radiusLarge,
156
+ radiusExtraLarge,
157
+ ]
158
+ );
159
+
87
160
  const colorModes = useMemo(
88
- () => transformTheme({ colors: scheme }, []).colorModes,
89
- [scheme]
161
+ () => transformTheme(newTheme, ['fonts', 'radii']).colorModes,
162
+ [newTheme]
90
163
  );
164
+
91
165
  useEffect(() => {
92
166
  localStorage.setItem(
93
167
  THEME_OVERRIDE_STORAGE_KEY,
@@ -98,93 +172,153 @@ const ThemeBuilderImpl = (props: ThemeBuilderProps) => {
98
172
 
99
173
  return (
100
174
  <>
175
+ {/* Shape */}
176
+ <Text variant="headline-small">Shape</Text>
101
177
  <Box display="flex" my={3} sx={{ gap: 3, flexWrap: 'wrap' }}>
102
- <Box display="flex" minWidth="300px" flex="1">
103
- <TextFieldColorPicker
104
- label="Primary color"
105
- value={primaryColor}
106
- onChange={(c) => setPrimaryColor(c)}
107
- />
108
- <Button
109
- variant="text"
110
- disabled={
111
- primaryColor.hex === toColor('hex', theme.colors.primary).hex
112
- }
113
- onClick={() =>
114
- setPrimaryColor(toColor('hex', theme.colors.primary))
115
- }
116
- >
117
- Reset
118
- </Button>
119
- </Box>
120
- <Box display="flex" minWidth="300px" flex="1">
121
- <TextFieldColorPicker
122
- label="Secondary color"
123
- value={
124
- secondaryColor ||
125
- toColor('hex', hexFromArgb(secondaryColorTonal.tone(40)))
126
- }
127
- onChange={(c) => setSecondaryColor(c)}
128
- />
129
- <Button
130
- variant="text"
131
- onClick={() => setSecondaryColor(null)}
132
- disabled={secondaryColor === null}
133
- >
134
- Reset
135
- </Button>
136
- </Box>
137
- <Box display="flex" minWidth="300px" flex="1">
138
- <TextFieldColorPicker
139
- label="Tertiary color"
140
- value={
141
- tertiaryColor ||
142
- toColor('hex', hexFromArgb(tertiaryColorTonal.tone(40)))
178
+ <BorderSlider
179
+ label="Extra small"
180
+ defaultValue={radiusExtraSmall}
181
+ onChange={(v) => setRadiusExtraSmall(v)}
182
+ onReset={() => {
183
+ setRadiusExtraSmall(parseInt(remToPx(theme.radii['extra-small'])));
184
+ }}
185
+ resetDisabled={rem(radiusExtraSmall) === theme.radii['extra-small']}
186
+ />
187
+ <BorderSlider
188
+ label="Small"
189
+ defaultValue={radiusSmall}
190
+ onChange={(v) => setRadiusSmall(v)}
191
+ onReset={() => {
192
+ setRadiusSmall(parseInt(remToPx(theme.radii['small'])));
193
+ }}
194
+ resetDisabled={rem(radiusSmall) === theme.radii.small}
195
+ />
196
+ <BorderSlider
197
+ label="Medium"
198
+ defaultValue={radiusMedium}
199
+ onChange={(v) => setRadiusMedium(v)}
200
+ onReset={() => {
201
+ setRadiusMedium(parseInt(remToPx(theme.radii['medium'])));
202
+ }}
203
+ resetDisabled={rem(radiusMedium) === theme.radii['medium']}
204
+ />
205
+ <BorderSlider
206
+ label="Large"
207
+ defaultValue={radiusLarge}
208
+ onChange={(v) => setRadiusLarge(v)}
209
+ onReset={() => {
210
+ setRadiusLarge(parseInt(remToPx(theme.radii['large'])));
211
+ }}
212
+ resetDisabled={rem(radiusLarge) === theme.radii['large']}
213
+ />
214
+ <BorderSlider
215
+ label="Extra large"
216
+ defaultValue={radiusExtraLarge}
217
+ onChange={(v) => setRadiusExtraLarge(v)}
218
+ onReset={() => {
219
+ setRadiusExtraLarge(parseInt(remToPx(theme.radii['extra-large'])));
220
+ }}
221
+ resetDisabled={rem(radiusExtraLarge) === theme.radii['extra-large']}
222
+ />
223
+ </Box>
224
+ {/* Font pickers */}
225
+ <Text variant="headline-small">Typography</Text>
226
+ <Box display="flex" my={3} sx={{ gap: 3, flexWrap: 'wrap' }}>
227
+ <FontAutoComplete
228
+ label="Brand"
229
+ initialValue={brandFont}
230
+ onSelect={(f) => {
231
+ if (f) {
232
+ setBrandFont(f);
233
+ loadGoogleFont([
234
+ { family: f, weights: [300, 400, 500, 600, 700] },
235
+ ]);
143
236
  }
144
- onChange={(c) => setTertiaryColor(c)}
145
- />
146
- <Button
147
- variant="text"
148
- onClick={() => setTertiaryColor(null)}
149
- disabled={tertiaryColor === null}
150
- >
151
- Reset
152
- </Button>
153
- </Box>
154
- <Box display="flex" minWidth="300px" flex="1">
155
- <TextFieldColorPicker
156
- label="Neutral color"
157
- value={
158
- neutralColor ||
159
- toColor('hex', hexFromArgb(neutralColorTonal.tone(40)))
237
+ }}
238
+ onReset={() => {
239
+ setBrandFont(parseMainFont(theme.fonts.brand));
240
+ }}
241
+ resetDisabled={brandFont === parseMainFont(theme.fonts.brand)}
242
+ />
243
+ <FontAutoComplete
244
+ label="Plain"
245
+ initialValue={plainFont}
246
+ onSelect={(f) => {
247
+ if (f) {
248
+ setPlainFont(f);
249
+ loadGoogleFont([
250
+ { family: f, weights: [300, 400, 500, 600, 700] },
251
+ ]);
160
252
  }
161
- onChange={(c) => setNeutralColor(c)}
162
- />
163
- <Button
164
- variant="text"
165
- onClick={() => setNeutralColor(null)}
166
- disabled={neutralColor === null}
167
- >
168
- Reset
169
- </Button>
170
- </Box>
253
+ }}
254
+ onReset={() => {
255
+ setPlainFont(parseMainFont(theme.fonts.plain));
256
+ }}
257
+ resetDisabled={plainFont === parseMainFont(theme.fonts.plain)}
258
+ />
259
+ </Box>
260
+ {/* Color pickers */}
261
+ <Text variant="headline-small">Color schemes</Text>
262
+ <Box display="flex" my={3} sx={{ gap: 3, flexWrap: 'wrap' }}>
263
+ <ColorSchemePicker
264
+ label="Primary color"
265
+ initialValue={primaryColor}
266
+ onChange={(c) => setPrimaryColor(c)}
267
+ onReset={() => setPrimaryColor(toColor('hex', theme.colors.primary))}
268
+ resetDisabled={
269
+ primaryColor.hex === toColor('hex', theme.colors.primary).hex
270
+ }
271
+ />
272
+ <ColorSchemePicker
273
+ label="Secondary color"
274
+ initialValue={
275
+ secondaryColor ||
276
+ toColor('hex', hexFromArgb(secondaryColorTonal.tone(40)))
277
+ }
278
+ onChange={(c) => setSecondaryColor(c)}
279
+ onReset={() => setSecondaryColor(null)}
280
+ resetDisabled={secondaryColor === null}
281
+ />
282
+ <ColorSchemePicker
283
+ label="Tertiary color"
284
+ initialValue={
285
+ tertiaryColor ||
286
+ toColor('hex', hexFromArgb(tertiaryColorTonal.tone(40)))
287
+ }
288
+ onChange={(c) => setTertiaryColor(c)}
289
+ onReset={() => setTertiaryColor(null)}
290
+ resetDisabled={tertiaryColor === null}
291
+ />
292
+ <ColorSchemePicker
293
+ label="Neutral color"
294
+ initialValue={
295
+ neutralColor ||
296
+ toColor('hex', hexFromArgb(neutralColorTonal.tone(40)))
297
+ }
298
+ onChange={(c) => setNeutralColor(c)}
299
+ onReset={() => setNeutralColor(null)}
300
+ resetDisabled={neutralColor === null}
301
+ />
171
302
  </Box>
172
303
  <Box width="100%">
173
304
  <Box display="flex" flexDirection="column" sx={{ gap: 4 }}>
174
305
  <Box flex="1">
175
- <Text variant="title-medium" as="h1" pb={'0.3em'}>
306
+ <Text variant="title-medium" as="h1" pb={'0.3em'} pt={3}>
176
307
  Light theme
177
308
  </Text>
178
309
  <ThemeColors scheme={scheme} />
179
310
  </Box>
180
311
  <Box flex="1">
181
- <Text variant="title-medium" as="h1" pb={'0.3em'}>
312
+ <Text variant="title-medium" as="h1" pb={'0.3em'} pt={3}>
182
313
  Dark theme
183
314
  </Text>
184
315
  <ThemeColors scheme={scheme.modes.dark} />
185
316
  </Box>
186
317
  </Box>
187
318
  <Box my={4} display="flex" sx={{ gap: 3 }} flexDirection="column">
319
+ <Text variant="title-medium" as="h1" pb={'0.3em'} pt={3}>
320
+ Palettes
321
+ </Text>
188
322
  <TonalColors palette={a1} />
189
323
  <TonalColors palette={secondaryColorTonal} />
190
324
  <TonalColors palette={tertiaryColorTonal} />
@@ -192,10 +326,12 @@ const ThemeBuilderImpl = (props: ThemeBuilderProps) => {
192
326
  <TonalColors palette={neutralColorTonal} />
193
327
  <TonalColors palette={n2} />
194
328
  </Box>
195
- <CodeHighlightingComponent className={`language-json`}>
196
- {JSON.stringify({ colors: scheme }, null, 2)}
197
- </CodeHighlightingComponent>
198
329
  </Box>
330
+ {/* Color pickers */}
331
+ <Text variant="headline-small">Theme</Text>
332
+ <CodeHighlightingComponent className={`language-json`}>
333
+ {JSON.stringify(newTheme, null, 2)}
334
+ </CodeHighlightingComponent>
199
335
  </>
200
336
  );
201
337
  };