@granularjs/ui 0.1.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 (220) hide show
  1. package/README.md +116 -0
  2. package/dist/fonts/Arimo-400.ttf +0 -0
  3. package/dist/fonts/Arimo-500.ttf +1449 -0
  4. package/dist/fonts/Arimo-600.ttf +1449 -0
  5. package/dist/fonts/Arimo-700.ttf +0 -0
  6. package/dist/fonts/Inter-400.woff2 +0 -0
  7. package/dist/fonts/Inter-500.woff2 +0 -0
  8. package/dist/fonts/Inter-600.woff2 +0 -0
  9. package/dist/fonts/Inter-700.woff2 +0 -0
  10. package/dist/fonts/Poppins-400.ttf +0 -0
  11. package/dist/fonts/Poppins-500.ttf +0 -0
  12. package/dist/fonts/Poppins-600.ttf +0 -0
  13. package/dist/fonts/Poppins-700.ttf +0 -0
  14. package/dist/granular-ui.min.js +3605 -0
  15. package/dist/granular-ui.min.js.map +7 -0
  16. package/package.json +55 -0
  17. package/src/components/Accordion.js +25 -0
  18. package/src/components/ActionIcon.js +20 -0
  19. package/src/components/Affix.js +11 -0
  20. package/src/components/Alert.js +33 -0
  21. package/src/components/Anchor.js +8 -0
  22. package/src/components/AppBar.js +14 -0
  23. package/src/components/Avatar.js +13 -0
  24. package/src/components/AvatarGroup.js +8 -0
  25. package/src/components/Badge.js +22 -0
  26. package/src/components/BadgeGroup.js +8 -0
  27. package/src/components/Blockquote.js +8 -0
  28. package/src/components/BottomBar.js +43 -0
  29. package/src/components/Breadcrumbs.js +19 -0
  30. package/src/components/Burger.js +13 -0
  31. package/src/components/Button.js +37 -0
  32. package/src/components/Calendar.js +109 -0
  33. package/src/components/Card.js +40 -0
  34. package/src/components/Center.js +8 -0
  35. package/src/components/Checkbox.js +46 -0
  36. package/src/components/CheckboxGroup.js +8 -0
  37. package/src/components/Chip.js +35 -0
  38. package/src/components/Code.js +8 -0
  39. package/src/components/Col.js +8 -0
  40. package/src/components/Collapse.js +8 -0
  41. package/src/components/Container.js +19 -0
  42. package/src/components/CopyButton.js +30 -0
  43. package/src/components/DateInput.js +123 -0
  44. package/src/components/DatePicker.js +7 -0
  45. package/src/components/Divider.js +22 -0
  46. package/src/components/Drawer.js +32 -0
  47. package/src/components/EventCalendar.js +972 -0
  48. package/src/components/Fieldset.js +12 -0
  49. package/src/components/Flex.js +25 -0
  50. package/src/components/Grid.js +8 -0
  51. package/src/components/GridTable.js +99 -0
  52. package/src/components/Group.js +29 -0
  53. package/src/components/HoverCard.js +24 -0
  54. package/src/components/Icon.js +19 -0
  55. package/src/components/Image.js +8 -0
  56. package/src/components/Indicator.js +21 -0
  57. package/src/components/Kbd.js +8 -0
  58. package/src/components/List.js +77 -0
  59. package/src/components/Loading.js +29 -0
  60. package/src/components/LoadingOverlay.js +9 -0
  61. package/src/components/Menu.js +129 -0
  62. package/src/components/Modal.js +61 -0
  63. package/src/components/MultiSelect.js +153 -0
  64. package/src/components/NavLink.js +72 -0
  65. package/src/components/Notification.js +42 -0
  66. package/src/components/Notifications.js +59 -0
  67. package/src/components/NumberField.js +389 -0
  68. package/src/components/NumberInput.js +5 -0
  69. package/src/components/Pagination.js +56 -0
  70. package/src/components/Paper.js +20 -0
  71. package/src/components/PasswordInput.js +29 -0
  72. package/src/components/PinInput.js +218 -0
  73. package/src/components/Popover.js +38 -0
  74. package/src/components/Popper.js +25 -0
  75. package/src/components/Progress.js +27 -0
  76. package/src/components/ProgressRing.js +11 -0
  77. package/src/components/Radio.js +22 -0
  78. package/src/components/RadioGroup.js +8 -0
  79. package/src/components/RangePicker.js +45 -0
  80. package/src/components/RangeSlider.js +143 -0
  81. package/src/components/Rating.js +42 -0
  82. package/src/components/ScrollArea.js +11 -0
  83. package/src/components/SearchInput.js +17 -0
  84. package/src/components/SegmentedControl.js +39 -0
  85. package/src/components/Select.js +71 -0
  86. package/src/components/SelectSearch.js +37 -0
  87. package/src/components/Sidebar.js +136 -0
  88. package/src/components/SimpleGrid.js +11 -0
  89. package/src/components/Skeleton.js +24 -0
  90. package/src/components/Slider.js +126 -0
  91. package/src/components/Space.js +8 -0
  92. package/src/components/Stack.js +27 -0
  93. package/src/components/Stepper.js +20 -0
  94. package/src/components/Switch.js +16 -0
  95. package/src/components/SwitchGroup.js +8 -0
  96. package/src/components/Table.js +42 -0
  97. package/src/components/Tabs.js +194 -0
  98. package/src/components/Tag.js +8 -0
  99. package/src/components/Text.js +42 -0
  100. package/src/components/TextInput.js +74 -0
  101. package/src/components/Textarea.js +15 -0
  102. package/src/components/Timeline.js +22 -0
  103. package/src/components/Title.js +18 -0
  104. package/src/components/Toast.js +16 -0
  105. package/src/components/ToastStack.js +21 -0
  106. package/src/components/Tooltip.js +12 -0
  107. package/src/hooks/useDisclosure.js +13 -0
  108. package/src/index.js +98 -0
  109. package/src/theme/fonts/Arimo-400.ttf +0 -0
  110. package/src/theme/fonts/Arimo-500.ttf +1449 -0
  111. package/src/theme/fonts/Arimo-600.ttf +1449 -0
  112. package/src/theme/fonts/Arimo-700.ttf +0 -0
  113. package/src/theme/fonts/Inter-400.woff2 +0 -0
  114. package/src/theme/fonts/Inter-500.woff2 +0 -0
  115. package/src/theme/fonts/Inter-600.woff2 +0 -0
  116. package/src/theme/fonts/Inter-700.woff2 +0 -0
  117. package/src/theme/fonts/Poppins-400.ttf +0 -0
  118. package/src/theme/fonts/Poppins-500.ttf +0 -0
  119. package/src/theme/fonts/Poppins-600.ttf +0 -0
  120. package/src/theme/fonts/Poppins-700.ttf +0 -0
  121. package/src/theme/icons.js +10 -0
  122. package/src/theme/styles.js +3630 -0
  123. package/src/theme/theme.js +71 -0
  124. package/src/utils.js +75 -0
  125. package/types/components/Accordion.d.ts +1 -0
  126. package/types/components/ActionIcon.d.ts +1 -0
  127. package/types/components/Affix.d.ts +1 -0
  128. package/types/components/Alert.d.ts +1 -0
  129. package/types/components/Anchor.d.ts +1 -0
  130. package/types/components/AppBar.d.ts +1 -0
  131. package/types/components/Avatar.d.ts +1 -0
  132. package/types/components/AvatarGroup.d.ts +1 -0
  133. package/types/components/Badge.d.ts +1 -0
  134. package/types/components/BadgeGroup.d.ts +1 -0
  135. package/types/components/Blockquote.d.ts +1 -0
  136. package/types/components/BottomBar.d.ts +4 -0
  137. package/types/components/Breadcrumbs.d.ts +1 -0
  138. package/types/components/Burger.d.ts +1 -0
  139. package/types/components/Button.d.ts +1 -0
  140. package/types/components/Calendar.d.ts +1 -0
  141. package/types/components/Card.d.ts +1 -0
  142. package/types/components/Center.d.ts +1 -0
  143. package/types/components/Checkbox.d.ts +1 -0
  144. package/types/components/CheckboxGroup.d.ts +1 -0
  145. package/types/components/Chip.d.ts +1 -0
  146. package/types/components/Code.d.ts +1 -0
  147. package/types/components/Col.d.ts +1 -0
  148. package/types/components/Collapse.d.ts +1 -0
  149. package/types/components/Container.d.ts +1 -0
  150. package/types/components/CopyButton.d.ts +1 -0
  151. package/types/components/DateInput.d.ts +1 -0
  152. package/types/components/DatePicker.d.ts +1 -0
  153. package/types/components/Divider.d.ts +1 -0
  154. package/types/components/Drawer.d.ts +1 -0
  155. package/types/components/EventCalendar.d.ts +1 -0
  156. package/types/components/Fieldset.d.ts +1 -0
  157. package/types/components/Flex.d.ts +1 -0
  158. package/types/components/Grid.d.ts +1 -0
  159. package/types/components/GridTable.d.ts +5 -0
  160. package/types/components/Group.d.ts +1 -0
  161. package/types/components/HoverCard.d.ts +1 -0
  162. package/types/components/Icon.d.ts +1 -0
  163. package/types/components/Image.d.ts +1 -0
  164. package/types/components/Indicator.d.ts +1 -0
  165. package/types/components/Kbd.d.ts +1 -0
  166. package/types/components/List.d.ts +5 -0
  167. package/types/components/Loading.d.ts +1 -0
  168. package/types/components/LoadingOverlay.d.ts +1 -0
  169. package/types/components/Menu.d.ts +2 -0
  170. package/types/components/Modal.d.ts +1 -0
  171. package/types/components/MultiSelect.d.ts +1 -0
  172. package/types/components/NavLink.d.ts +1 -0
  173. package/types/components/Notification.d.ts +1 -0
  174. package/types/components/Notifications.d.ts +1 -0
  175. package/types/components/NumberField.d.ts +1 -0
  176. package/types/components/NumberInput.d.ts +1 -0
  177. package/types/components/Pagination.d.ts +1 -0
  178. package/types/components/Paper.d.ts +1 -0
  179. package/types/components/PasswordInput.d.ts +1 -0
  180. package/types/components/PinInput.d.ts +1 -0
  181. package/types/components/Popover.d.ts +1 -0
  182. package/types/components/Popper.d.ts +1 -0
  183. package/types/components/Progress.d.ts +1 -0
  184. package/types/components/ProgressRing.d.ts +1 -0
  185. package/types/components/Radio.d.ts +1 -0
  186. package/types/components/RadioGroup.d.ts +1 -0
  187. package/types/components/RangePicker.d.ts +1 -0
  188. package/types/components/RangeSlider.d.ts +1 -0
  189. package/types/components/Rating.d.ts +1 -0
  190. package/types/components/ScrollArea.d.ts +1 -0
  191. package/types/components/SearchInput.d.ts +1 -0
  192. package/types/components/SegmentedControl.d.ts +1 -0
  193. package/types/components/Select.d.ts +1 -0
  194. package/types/components/SelectSearch.d.ts +1 -0
  195. package/types/components/Sidebar.d.ts +1 -0
  196. package/types/components/SimpleGrid.d.ts +1 -0
  197. package/types/components/Skeleton.d.ts +1 -0
  198. package/types/components/Slider.d.ts +5 -0
  199. package/types/components/Space.d.ts +1 -0
  200. package/types/components/Stack.d.ts +1 -0
  201. package/types/components/Stepper.d.ts +1 -0
  202. package/types/components/Switch.d.ts +1 -0
  203. package/types/components/SwitchGroup.d.ts +1 -0
  204. package/types/components/Table.d.ts +1 -0
  205. package/types/components/Tabs.d.ts +1 -0
  206. package/types/components/Tag.d.ts +1 -0
  207. package/types/components/Text.d.ts +1 -0
  208. package/types/components/TextInput.d.ts +1 -0
  209. package/types/components/Textarea.d.ts +1 -0
  210. package/types/components/Timeline.d.ts +1 -0
  211. package/types/components/Title.d.ts +1 -0
  212. package/types/components/Toast.d.ts +1 -0
  213. package/types/components/ToastStack.d.ts +1 -0
  214. package/types/components/Tooltip.d.ts +1 -0
  215. package/types/hooks/useDisclosure.d.ts +1 -0
  216. package/types/index.d.ts +93 -0
  217. package/types/theme/icons.d.ts +10 -0
  218. package/types/theme/styles.d.ts +1 -0
  219. package/types/theme/theme.d.ts +2 -0
  220. package/types/utils.d.ts +12 -0
@@ -0,0 +1,71 @@
1
+ import { Div, Span, when, state, after } from '@granularjs/core';
2
+ import { cx, splitPropsChildren, classVar, resolveValue } from '../utils.js';
3
+
4
+ export function Select(...args) {
5
+ const { props, rawProps } = splitPropsChildren(args, { data: [], size: 'md' });
6
+ const { data, value, size, leftSection, rightSection, placeholder, className, ...rest } = props;
7
+ const { onChange } = rawProps;
8
+ const open = state(false);
9
+ const currentState = state(resolveValue(value) ?? '');
10
+
11
+ after(value).change((next) => {
12
+ const resolved = resolveValue(next);
13
+ if (resolved === undefined) return;
14
+ currentState.set(resolved ?? '');
15
+ });
16
+
17
+ const selectValue = (next) => {
18
+ currentState.set(next);
19
+ onChange?.(next);
20
+ open.set(false);
21
+ };
22
+
23
+ const valueClass = after(currentState).compute((current) => {
24
+ if (!current) return 'g-ui-select-placeholder';
25
+ return '';
26
+ });
27
+ const displayLabel = after(currentState).compute((current) => {
28
+ const items = resolveValue(data) ?? [];
29
+ const match = items.find((item) => item.value === current);
30
+ if (current) return match?.label ?? '';
31
+ return placeholder ?? '';
32
+ });
33
+
34
+ return Div(
35
+ { ...rest, className: cx('g-ui-select-root', className) },
36
+ Div(
37
+ { className: cx('g-ui-input-wrapper', classVar('g-ui-input-size-', size, 'md')) },
38
+ when(leftSection, () => Div({ className: 'g-ui-input-section' }, leftSection)),
39
+ Div(
40
+ {
41
+ className: cx('g-ui-select', valueClass),
42
+ onClick: () => open.set(!open.get()),
43
+ },
44
+ Span({ className: 'g-ui-select-value' }, displayLabel)
45
+ ),
46
+ when(rightSection, () => Div({ className: 'g-ui-input-section' }, rightSection), () =>
47
+ Span({ className: 'g-ui-select-caret' }, '▾')
48
+ )
49
+ ),
50
+ when(open, () =>
51
+ Div(
52
+ { className: 'g-ui-select-dropdown' },
53
+ (resolveValue(data) ?? []).map((item) =>
54
+ Div(
55
+ {
56
+ className: cx(
57
+ 'g-ui-select-item',
58
+ after(currentState).compute((current) => {
59
+ if (current === item.value) return 'g-ui-select-item-active';
60
+ return '';
61
+ })
62
+ ),
63
+ onClick: () => selectValue(item.value),
64
+ },
65
+ item.label
66
+ )
67
+ )
68
+ )
69
+ )
70
+ );
71
+ }
@@ -0,0 +1,37 @@
1
+ import { Div, Span } from '@granularjs/core';
2
+ import { cx, splitPropsChildren } from '../utils.js';
3
+ import { state } from '@granularjs/core';
4
+ import { TextInput } from './TextInput.js';
5
+
6
+ export function SelectSearch(...args) {
7
+ const { props, rawProps } = splitPropsChildren(args, { data: [] });
8
+ const { data, className, ...rest } = props;
9
+ const { onChange } = rawProps;
10
+ const query = state('');
11
+
12
+ const items = data.filter((item) =>
13
+ String(item.label || '').toLowerCase().includes(String(query.get()).toLowerCase())
14
+ );
15
+
16
+ return Div(
17
+ { ...rest, className: cx('g-ui-select-search', className) },
18
+ TextInput({
19
+ inputClassName: 'g-ui-select-search-input',
20
+ placeholder: 'Search...',
21
+ value: query,
22
+ onInput: (ev) => query.set(ev?.target?.value ?? ''),
23
+ }),
24
+ Div(
25
+ { className: 'g-ui-select-search-list' },
26
+ items.map((item) =>
27
+ Div(
28
+ {
29
+ className: 'g-ui-select-search-item',
30
+ onClick: () => onChange?.(item.value),
31
+ },
32
+ item.label
33
+ )
34
+ )
35
+ )
36
+ );
37
+ }
@@ -0,0 +1,136 @@
1
+ import { Div, portal, after, isSignal, isState, isStatePath, state, when } from '@granularjs/core';
2
+ import { cx, splitPropsChildren, resolveValue, resolveBool } from '../utils.js';
3
+
4
+ export function Sidebar(...args) {
5
+ const { props, children } = splitPropsChildren(args, {
6
+ position: 'left',
7
+ size: 'md',
8
+ blur: true,
9
+ fixed: false,
10
+ minWidth: 'button',
11
+ });
12
+
13
+ return when(props.fixed,
14
+ () => FixedSidebar(props, children),
15
+ () => DrawerSidebar(props, children)
16
+ );
17
+ }
18
+
19
+ const DrawerSidebar = (props, children)=>{
20
+ const {
21
+ open,
22
+ onClose,
23
+ position,
24
+ size,
25
+ blur,
26
+ fixed,
27
+ minWidth,
28
+ className,
29
+ ...rest
30
+ } = props;
31
+ const mounted = state(false);
32
+ const openState = state(false);
33
+ const transitionMs = 220;
34
+
35
+ const applyOpen = (next) => {
36
+ if (next) {
37
+ mounted.set(true);
38
+ if (typeof requestAnimationFrame === 'function') {
39
+ requestAnimationFrame(() => openState.set(true));
40
+ } else {
41
+ setTimeout(() => openState.set(true), 0);
42
+ }
43
+ return;
44
+ }
45
+ openState.set(false);
46
+ setTimeout(() => mounted.set(false), transitionMs);
47
+ };
48
+
49
+ applyOpen(!!open.get());
50
+ after(open).change((next) => applyOpen(!!next));
51
+
52
+ const overlayClass = after(openState, blur).compute((values) => {
53
+ const [open, blur] = values;
54
+
55
+ return cx(
56
+ 'g-ui-sidebar-overlay',
57
+ open && 'g-ui-sidebar-overlay-open',
58
+ !blur && 'g-ui-sidebar-overlay-noblur'
59
+ )
60
+ });
61
+
62
+ const drawerClass = after(
63
+ size,
64
+ position,
65
+ openState,
66
+ className
67
+ ).compute((values) => {
68
+ const [size, position, open, className] = values;
69
+ const positionClass = position === 'right'
70
+ ? 'g-ui-sidebar-drawer-right g-ui-sidebar-position-right'
71
+ : 'g-ui-sidebar-drawer-left';
72
+ return cx(
73
+ 'g-ui-sidebar',
74
+ 'g-ui-sidebar-drawer',
75
+ `g-ui-sidebar-size-${size}`,
76
+ positionClass,
77
+ open && 'g-ui-sidebar-drawer-open',
78
+ resolveValue(className)
79
+ )
80
+ });
81
+
82
+ return when(mounted, () =>
83
+ portal(
84
+ Div({ className: overlayClass, onClick: onClose },
85
+ Div({ className: drawerClass, onClick: (ev) => ev.stopPropagation() },
86
+ children
87
+ )
88
+ )
89
+ )
90
+ )
91
+ }
92
+
93
+ const FixedSidebar = (props, children)=>{
94
+ const {
95
+ open,
96
+ position,
97
+ size,
98
+ minWidth,
99
+ className,
100
+ ...rest
101
+ } = props;
102
+
103
+ const classNameComposition = after(
104
+ size,
105
+ position,
106
+ open,
107
+ minWidth,
108
+ className,
109
+ ).compute((values)=>{
110
+ const [size, position, open, minWidth, className] = values;
111
+
112
+ const minWidthValue = minWidth || 'button';
113
+ let minWidthClass = null;
114
+ if (minWidthValue === 'button') minWidthClass = 'g-ui-sidebar-min-button';
115
+ else if (minWidthValue === 'xs') minWidthClass = 'g-ui-sidebar-min-xs';
116
+ else if (minWidthValue === 'sm') minWidthClass = 'g-ui-sidebar-min-sm';
117
+ else if (minWidthValue === 'md') minWidthClass = 'g-ui-sidebar-min-md';
118
+
119
+ const classNameString = cx(
120
+ 'g-ui-sidebar g-ui-sidebar-fixed',
121
+ `g-ui-sidebar-size-${size||'md'}`,
122
+ minWidthClass,
123
+ open && 'g-ui-sidebar-fixed-open',
124
+ className
125
+ );
126
+ return classNameString;
127
+ });
128
+
129
+ return Div(
130
+ {
131
+ ...rest,
132
+ className: classNameComposition,
133
+ },
134
+ children
135
+ );
136
+ }
@@ -0,0 +1,11 @@
1
+ import { Div } from '@granularjs/core';
2
+ import { cx, splitPropsChildren } from '../utils.js';
3
+
4
+ export function SimpleGrid(...args) {
5
+ const { props, children } = splitPropsChildren(args, { cols: 3, gap: 'md' });
6
+ const { cols, gap, className, ...rest } = props;
7
+ return Div(
8
+ { ...rest, className: cx('g-ui-grid', [gap, (value) => `g-ui-gap-${value}`], [cols, (value) => `g-ui-grid-cols-${value}`], className) },
9
+ children
10
+ );
11
+ }
@@ -0,0 +1,24 @@
1
+ import { Div, after } from '@granularjs/core';
2
+ import { cx, splitPropsChildren } from '../utils.js';
3
+
4
+ export function Skeleton(...args) {
5
+ const { props } = splitPropsChildren(args, { height: 'md', width: 'full' });
6
+ const { height, width, className, ...rest } = props;
7
+ const customStyle = after(height, width).compute(([h, w]) => {
8
+ const style = {};
9
+ if (!isNaN(h)) style.height = `${h}px`;
10
+ if (!isNaN(w)) style.width = `${w}px`;
11
+ return style;
12
+ });
13
+
14
+ return Div({
15
+ ...rest,
16
+ style: { width: customStyle.width, height: customStyle.height, ...(rest.style || {}) },
17
+ className: cx(
18
+ 'g-ui-skeleton',
19
+ [height, (value) => `g-ui-skeleton-h-${value}`],
20
+ [width, (value) => `g-ui-skeleton-w-${value}`],
21
+ className
22
+ ),
23
+ });
24
+ }
@@ -0,0 +1,126 @@
1
+ import { Div, after, state, when } from '@granularjs/core';
2
+ import { cx, splitPropsChildren, classVar, classFlag, resolveBool, resolveValue } from '../utils.js';
3
+
4
+ export function Slider(...args) {
5
+ const { props, rawProps } = splitPropsChildren(args, {
6
+ size: 'md',
7
+ min: 0,
8
+ max: 100,
9
+ step: 1,
10
+ });
11
+ const { onChange } = rawProps;
12
+ const {
13
+ value,
14
+ marks,
15
+ size,
16
+ min,
17
+ max,
18
+ step,
19
+ disabled,
20
+ className,
21
+ ...rest
22
+ } = props;
23
+ const currentState = state(resolveValue(value ?? min));
24
+ after(value).change((next) => {
25
+ if (next == null) return;
26
+ currentState.set(resolveValue(next));
27
+ });
28
+ const getBounds = () => {
29
+ const minValue = Number(resolveValue(min));
30
+ const maxValue = Number(resolveValue(max));
31
+ if (!Number.isFinite(minValue) || !Number.isFinite(maxValue)) {
32
+ return { minValue: 0, maxValue: 100 };
33
+ }
34
+ return { minValue: Math.min(minValue, maxValue), maxValue: Math.max(minValue, maxValue) };
35
+ };
36
+ const getStep = () => {
37
+ const stepValue = Number(resolveValue(step));
38
+ if (Number.isFinite(stepValue) && stepValue > 0) return stepValue;
39
+ return 1;
40
+ };
41
+ const setValue = (next) => {
42
+ const { minValue, maxValue } = getBounds();
43
+ const stepValue = getStep();
44
+ const clamped = Math.max(minValue, Math.min(maxValue, Number(next)));
45
+ const stepped = Math.round((clamped - minValue) / stepValue) * stepValue + minValue;
46
+ if (stepped === currentState.get()) return;
47
+ currentState.set(stepped);
48
+ onChange?.(stepped);
49
+ };
50
+ const percent = after(currentState).compute((v) => {
51
+ const { minValue, maxValue } = getBounds();
52
+ const range = maxValue - minValue;
53
+ if (range <= 0) return 0;
54
+ const pct = ((Number(v ?? minValue) - minValue) / range) * 100;
55
+ return Math.max(0, Math.min(100, pct));
56
+ });
57
+ const updateFromEvent = (ev, getRect) => {
58
+ const rect = getRect?.();
59
+ if (!rect || rect.width === 0) return;
60
+ const x = Math.min(Math.max(ev.clientX - rect.left, 0), rect.width);
61
+ const ratio = x / rect.width;
62
+ const { minValue, maxValue } = getBounds();
63
+ setValue(minValue + ratio * (maxValue - minValue));
64
+ };
65
+ const startDrag = (ev) => {
66
+ if (resolveBool(disabled)) return;
67
+ ev.preventDefault?.();
68
+ const trackEl = ev.currentTarget;
69
+ const getRect = () => trackEl.getBoundingClientRect();
70
+ trackEl.setPointerCapture?.(ev.pointerId);
71
+ updateFromEvent(ev, getRect);
72
+ const handleMove = (moveEv) => updateFromEvent(moveEv, getRect);
73
+ const handleUp = () => {
74
+ trackEl.releasePointerCapture?.(ev.pointerId);
75
+ window.removeEventListener('pointermove', handleMove);
76
+ window.removeEventListener('pointerup', handleUp);
77
+ };
78
+ window.addEventListener('pointermove', handleMove);
79
+ window.addEventListener('pointerup', handleUp);
80
+ };
81
+ return Div(
82
+ {
83
+ ...rest,
84
+ className: cx(
85
+ 'g-ui-slider',
86
+ classVar('g-ui-slider-size-', size, 'md'),
87
+ classFlag('g-ui-slider-disabled', disabled),
88
+ className
89
+ ),
90
+ },
91
+ Div(
92
+ { className: 'g-ui-slider-track', onPointerDown: startDrag },
93
+ Div({
94
+ className: 'g-ui-slider-bar',
95
+ style: after(percent).compute((p) => ({ width: `${p}%` })),
96
+ }),
97
+ Div({
98
+ className: 'g-ui-slider-thumb',
99
+ style: after(percent).compute((p) => ({ left: `${p}%` })),
100
+ })
101
+ ),
102
+ when(marks, () => Div(
103
+ { className: 'g-ui-slider-marks' },
104
+ marks.map((mark) => SliderMark({ mark, getBounds }))
105
+ ))
106
+ );
107
+ }
108
+
109
+ export const SliderMark = ({ mark, getBounds }) => {
110
+ const { minValue, maxValue } = getBounds();
111
+ const range = maxValue - minValue;
112
+
113
+ const value = mark.value ?? mark;
114
+ const label = mark.label ?? String(mark.value) ?? value;
115
+
116
+ const markValue = Number(value);
117
+ let pct = 0;
118
+ if (range > 0) pct = ((markValue - minValue) / range) * 100;
119
+ return Div(
120
+ {
121
+ className: 'g-ui-slider-mark',
122
+ style: { left: `${Math.max(0, Math.min(100, pct))}%` },
123
+ },
124
+ Div({ className: 'g-ui-slider-mark-label' }, label)
125
+ );
126
+ }
@@ -0,0 +1,8 @@
1
+ import { Div } from '@granularjs/core';
2
+ import { cx, splitPropsChildren } from '../utils.js';
3
+
4
+ export function Space(...args) {
5
+ const { props } = splitPropsChildren(args, { size: 'md' });
6
+ const { size, className, ...rest } = props;
7
+ return Div({ ...rest, className: cx('g-ui-space', [size, (value) => `g-ui-space-${value}`], className) });
8
+ }
@@ -0,0 +1,27 @@
1
+ import { Div } from '@granularjs/core';
2
+ import { cx, splitPropsChildren, classVar, classMap } from '../utils.js';
3
+
4
+ export function Stack(...args) {
5
+ const { props, children } = splitPropsChildren(args, { gap: 'md' });
6
+ const { gap, align, justify, className, style, ...rest } = props;
7
+ const justifyClass = classMap(justify, {
8
+ between: 'g-ui-justify-between',
9
+ center: 'g-ui-justify-center',
10
+ end: 'g-ui-justify-end',
11
+ start: 'g-ui-justify-start',
12
+ });
13
+ return Div(
14
+ {
15
+ ...rest,
16
+ style,
17
+ className: cx(
18
+ 'g-ui-stack',
19
+ classVar('g-ui-gap-', gap, 'md'),
20
+ classVar('g-ui-align-', align),
21
+ justifyClass,
22
+ className
23
+ ),
24
+ },
25
+ children
26
+ );
27
+ }
@@ -0,0 +1,20 @@
1
+ import { Div } from '@granularjs/core';
2
+ import { cx, splitPropsChildren } from '../utils.js';
3
+
4
+ export function Stepper(...args) {
5
+ const { props } = splitPropsChildren(args, { active: 0, items: [] });
6
+ const { active, items, className, ...rest } = props;
7
+ return Div(
8
+ { ...rest, className: cx('g-ui-stepper', className) },
9
+ items.map((item, idx) =>
10
+ Div(
11
+ { className: cx('g-ui-stepper-item', [active, (value) => {
12
+ if (idx === value) return 'g-ui-stepper-active';
13
+ return '';
14
+ }]) },
15
+ Div({ className: 'g-ui-stepper-index' }, String(idx + 1)),
16
+ Div({ className: 'g-ui-stepper-label' }, item.label)
17
+ )
18
+ )
19
+ );
20
+ }
@@ -0,0 +1,16 @@
1
+ import { Input, Label, Span, when } from '@granularjs/core';
2
+ import { cx, splitPropsChildren, classVar } from '../utils.js';
3
+
4
+ export function Switch(...args) {
5
+ const { props } = splitPropsChildren(args, { size: 'md' });
6
+ const { label, size, className, style, inputProps, ...rest } = props;
7
+ return Label(
8
+ { className: cx('g-ui-switch', classVar('g-ui-switch-size-', size, 'md'), className) },
9
+ Input({
10
+ type: 'checkbox',
11
+ className: cx('g-ui-switch-input', classVar('g-ui-switch-size-', size, 'md'), inputProps?.className),
12
+ ...rest,
13
+ }),
14
+ when(label, () => Span({ className: 'g-ui-switch-label' }, label))
15
+ );
16
+ }
@@ -0,0 +1,8 @@
1
+ import { Div } from '@granularjs/core';
2
+ import { cx, splitPropsChildren } from '../utils.js';
3
+
4
+ export function SwitchGroup(...args) {
5
+ const { props, children } = splitPropsChildren(args);
6
+ const { className, ...rest } = props;
7
+ return Div({ ...rest, className: cx('g-ui-switch-group', className) }, children);
8
+ }
@@ -0,0 +1,42 @@
1
+ import { Table as HtmlTable, Thead, Tbody, Tr, Th, Td } from '@granularjs/core';
2
+ import { cx, splitPropsChildren, classFlag } from '../utils.js';
3
+
4
+ export function Table(...args) {
5
+ const { props } = splitPropsChildren(args, { headers: [], rows: [] });
6
+ const {
7
+ headers,
8
+ rows,
9
+ striped,
10
+ highlightOnHover,
11
+ withBorder,
12
+ withColumnBorders,
13
+ className,
14
+ style,
15
+ ...rest
16
+ } = props;
17
+ return HtmlTable(
18
+ {
19
+ ...rest,
20
+ className: cx(
21
+ 'g-ui-table',
22
+ classFlag('g-ui-table-striped', striped),
23
+ classFlag('g-ui-table-hover', highlightOnHover),
24
+ classFlag('g-ui-table-with-border', withBorder),
25
+ classFlag('g-ui-table-column-borders', withColumnBorders),
26
+ className
27
+ ),
28
+ },
29
+ headers.length
30
+ ? Thead(Tr(headers.map((header) => Th(header))))
31
+ : null,
32
+ Tbody(
33
+ rows.map((row) =>
34
+ Tr(
35
+ Array.isArray(row)
36
+ ? row.map((cell) => Td(cell))
37
+ : Object.values(row).map((cell) => Td(cell))
38
+ )
39
+ )
40
+ )
41
+ );
42
+ }