@skewedaspect/sleekspace-ui 0.9.0 → 0.10.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 (210) hide show
  1. package/dist/components/Accordion/context.d.ts +4 -0
  2. package/dist/components/Autocomplete/SkAutocomplete.vue.d.ts +87 -0
  3. package/dist/components/Autocomplete/SkAutocompleteEmpty.vue.d.ts +17 -0
  4. package/dist/components/Autocomplete/SkAutocompleteGroup.vue.d.ts +17 -0
  5. package/dist/components/Autocomplete/SkAutocompleteGroupLabel.vue.d.ts +17 -0
  6. package/dist/components/Autocomplete/SkAutocompleteItem.vue.d.ts +39 -0
  7. package/dist/components/Autocomplete/SkAutocompleteSeparator.vue.d.ts +2 -0
  8. package/dist/components/Autocomplete/index.d.ts +7 -0
  9. package/dist/components/Autocomplete/types.d.ts +3 -0
  10. package/dist/components/Breadcrumbs/context.d.ts +4 -0
  11. package/dist/components/Button/SkButton.vue.d.ts +8 -1
  12. package/dist/components/Button/types.d.ts +2 -0
  13. package/dist/components/Card/SkCard.vue.d.ts +1 -1
  14. package/dist/components/ContextMenu/context.d.ts +3 -0
  15. package/dist/components/Dropdown/SkDropdown.vue.d.ts +1 -1
  16. package/dist/components/Dropdown/context.d.ts +3 -0
  17. package/dist/components/Field/SkField.vue.d.ts +7 -6
  18. package/dist/components/Input/SkInput.vue.d.ts +9 -2
  19. package/dist/components/Input/types.d.ts +2 -0
  20. package/dist/components/InputGroup/SkInputGroup.vue.d.ts +23 -0
  21. package/dist/components/InputGroup/SkInputGroupAddon.vue.d.ts +33 -0
  22. package/dist/components/InputGroup/types.d.ts +13 -0
  23. package/dist/components/NumberInput/SkNumberInput.vue.d.ts +15 -1
  24. package/dist/components/NumberInput/types.d.ts +2 -0
  25. package/dist/components/Pagination/context.d.ts +5 -0
  26. package/dist/components/Panel/SkPanel.vue.d.ts +1 -1
  27. package/dist/components/Panel/types.d.ts +2 -1
  28. package/dist/components/Radio/context.d.ts +4 -0
  29. package/dist/components/Select/SkSelect.vue.d.ts +7 -1
  30. package/dist/components/Select/types.d.ts +2 -0
  31. package/dist/components/Sidebar/SkSidebar.vue.d.ts +1 -1
  32. package/dist/components/Tabs/context.d.ts +6 -0
  33. package/dist/components/Textarea/SkTextarea.vue.d.ts +1 -1
  34. package/dist/components/Tooltip/SkTooltip.vue.d.ts +1 -1
  35. package/dist/composables/injectionKeys.d.ts +9 -0
  36. package/dist/global.d.ts +4 -0
  37. package/dist/index.d.ts +18 -0
  38. package/dist/sleekspace-ui.css +836 -280
  39. package/dist/sleekspace-ui.es.js +3759 -2545
  40. package/dist/sleekspace-ui.umd.js +3765 -2543
  41. package/dist/static/components/alert.d.ts +2 -1
  42. package/dist/static/components/avatar.d.ts +2 -1
  43. package/dist/static/components/breadcrumbs.d.ts +2 -1
  44. package/dist/static/components/button.d.ts +4 -2
  45. package/dist/static/components/card.d.ts +2 -1
  46. package/dist/static/components/checkbox.d.ts +2 -1
  47. package/dist/static/components/colorPicker.d.ts +2 -1
  48. package/dist/static/components/divider.d.ts +2 -1
  49. package/dist/static/components/dropdown.d.ts +2 -1
  50. package/dist/static/components/field.d.ts +2 -1
  51. package/dist/static/components/group.d.ts +2 -1
  52. package/dist/static/components/input.d.ts +4 -2
  53. package/dist/static/components/inputGroup.d.ts +8 -0
  54. package/dist/static/components/inputGroupAddon.d.ts +7 -0
  55. package/dist/static/components/navBar.d.ts +2 -1
  56. package/dist/static/components/numberInput.d.ts +4 -2
  57. package/dist/static/components/page.d.ts +2 -1
  58. package/dist/static/components/pagination.d.ts +2 -1
  59. package/dist/static/components/panel.d.ts +2 -1
  60. package/dist/static/components/progress.d.ts +2 -1
  61. package/dist/static/components/radio.d.ts +2 -1
  62. package/dist/static/components/select.d.ts +4 -2
  63. package/dist/static/components/sidebar.d.ts +2 -1
  64. package/dist/static/components/skeleton.d.ts +2 -1
  65. package/dist/static/components/slider.d.ts +2 -1
  66. package/dist/static/components/spinner.d.ts +2 -1
  67. package/dist/static/components/switchInput.d.ts +2 -1
  68. package/dist/static/components/table.d.ts +2 -1
  69. package/dist/static/components/tag.d.ts +2 -1
  70. package/dist/static/components/tagsInput.d.ts +2 -1
  71. package/dist/static/components/textarea.d.ts +2 -1
  72. package/dist/static/components/toolbar.d.ts +2 -1
  73. package/dist/static/components/tooltip.d.ts +2 -1
  74. package/dist/static/h.d.ts +2 -0
  75. package/dist/static/index.cjs.js +1 -1
  76. package/dist/static/index.d.ts +6 -0
  77. package/dist/static/index.es.js +366 -216
  78. package/dist/static/render.d.ts +2 -1
  79. package/dist/static/stringH.d.ts +2 -0
  80. package/dist/static/types.d.ts +5 -0
  81. package/dist/tailwind.css +222 -0
  82. package/dist/tokens.css +0 -223
  83. package/dist/types/corners.d.ts +1 -0
  84. package/dist/utils/slots.d.ts +6 -0
  85. package/llms-full.txt +17 -9
  86. package/package.json +9 -3
  87. package/src/components/Accordion/SkAccordion.vue +5 -2
  88. package/src/components/Accordion/SkAccordionItem.vue +7 -4
  89. package/src/components/Accordion/context.ts +23 -0
  90. package/src/components/Alert/SkAlert.vue +4 -2
  91. package/src/components/Autocomplete/SkAutocomplete.test.ts +83 -0
  92. package/src/components/Autocomplete/SkAutocomplete.vue +305 -0
  93. package/src/components/Autocomplete/SkAutocompleteEmpty.vue +39 -0
  94. package/src/components/Autocomplete/SkAutocompleteGroup.vue +46 -0
  95. package/src/components/Autocomplete/SkAutocompleteGroupLabel.vue +39 -0
  96. package/src/components/Autocomplete/SkAutocompleteItem.vue +85 -0
  97. package/src/components/Autocomplete/SkAutocompleteSeparator.vue +39 -0
  98. package/src/components/Autocomplete/index.ts +13 -0
  99. package/src/components/Autocomplete/types.ts +10 -0
  100. package/src/components/Breadcrumbs/SkBreadcrumbItem.vue +8 -3
  101. package/src/components/Breadcrumbs/SkBreadcrumbSeparator.vue +8 -2
  102. package/src/components/Breadcrumbs/SkBreadcrumbs.vue +11 -14
  103. package/src/components/Breadcrumbs/context.ts +20 -0
  104. package/src/components/Button/SkButton.vue +54 -11
  105. package/src/components/Button/types.ts +6 -0
  106. package/src/components/Card/SkCard.vue +12 -5
  107. package/src/components/Checkbox/SkCheckbox.vue +9 -2
  108. package/src/components/ColorPicker/SkColorPicker.vue +27 -5
  109. package/src/components/ContextMenu/SkContextMenu.vue +4 -1
  110. package/src/components/ContextMenu/SkContextMenuSubmenu.vue +5 -2
  111. package/src/components/ContextMenu/context.ts +17 -0
  112. package/src/components/Dropdown/SkDropdown.vue +2 -1
  113. package/src/components/Dropdown/SkDropdownSubmenu.vue +4 -3
  114. package/src/components/Dropdown/context.ts +16 -0
  115. package/src/components/Field/SkField.test.ts +88 -0
  116. package/src/components/Field/SkField.vue +15 -7
  117. package/src/components/Input/SkInput.test.ts +61 -0
  118. package/src/components/Input/SkInput.vue +42 -7
  119. package/src/components/Input/types.ts +2 -0
  120. package/src/components/InputGroup/SkInputGroup.test.ts +171 -0
  121. package/src/components/InputGroup/SkInputGroup.vue +131 -0
  122. package/src/components/InputGroup/SkInputGroupAddon.test.ts +104 -0
  123. package/src/components/InputGroup/SkInputGroupAddon.vue +107 -0
  124. package/src/components/InputGroup/types.ts +27 -0
  125. package/src/components/Listbox/SkListbox.vue +27 -6
  126. package/src/components/Modal/SkModal.vue +11 -4
  127. package/src/components/NavBar/SkNavBar.vue +5 -4
  128. package/src/components/NumberInput/SkNumberInput.vue +49 -8
  129. package/src/components/NumberInput/types.ts +2 -0
  130. package/src/components/Page/SkPage.vue +18 -15
  131. package/src/components/Pagination/SkPagination.vue +6 -3
  132. package/src/components/Pagination/SkPaginationItem.vue +8 -5
  133. package/src/components/Pagination/context.ts +19 -0
  134. package/src/components/Panel/types.ts +3 -2
  135. package/src/components/Popover/SkPopover.vue +11 -4
  136. package/src/components/Radio/SkRadio.vue +14 -4
  137. package/src/components/Radio/SkRadioGroup.vue +4 -2
  138. package/src/components/Radio/context.ts +17 -0
  139. package/src/components/Select/SkSelect.vue +39 -7
  140. package/src/components/Select/types.ts +2 -0
  141. package/src/components/Switch/SkSwitch.vue +14 -13
  142. package/src/components/Tabs/SkTab.vue +10 -3
  143. package/src/components/Tabs/SkTabList.vue +4 -2
  144. package/src/components/Tabs/SkTabs.vue +5 -3
  145. package/src/components/Tabs/context.ts +19 -0
  146. package/src/components/TagsInput/SkTagsInput.vue +28 -7
  147. package/src/components/Textarea/SkTextarea.vue +27 -6
  148. package/src/components/TreeView/SkTreeItem.vue +10 -2
  149. package/src/composables/injectionKeys.ts +52 -0
  150. package/src/index.ts +28 -0
  151. package/src/static/__tests__/parity.test.ts +2 -1
  152. package/src/static/__tests__/parityHarness.ts +5 -2
  153. package/src/static/components/__tests__/helpers.test.ts +191 -99
  154. package/src/static/components/alert.ts +12 -11
  155. package/src/static/components/avatar.ts +15 -16
  156. package/src/static/components/breadcrumbs.ts +3 -2
  157. package/src/static/components/button.ts +23 -27
  158. package/src/static/components/card.ts +3 -2
  159. package/src/static/components/checkbox.ts +11 -14
  160. package/src/static/components/colorPicker.ts +7 -9
  161. package/src/static/components/divider.ts +4 -3
  162. package/src/static/components/dropdown.ts +15 -6
  163. package/src/static/components/field.ts +32 -15
  164. package/src/static/components/group.ts +3 -2
  165. package/src/static/components/input.ts +20 -15
  166. package/src/static/components/inputGroup.ts +30 -0
  167. package/src/static/components/inputGroupAddon.ts +29 -0
  168. package/src/static/components/navBar.ts +30 -17
  169. package/src/static/components/numberInput.ts +17 -17
  170. package/src/static/components/page.ts +3 -2
  171. package/src/static/components/pagination.ts +3 -2
  172. package/src/static/components/panel.ts +3 -2
  173. package/src/static/components/progress.ts +3 -2
  174. package/src/static/components/radio.ts +14 -20
  175. package/src/static/components/select.ts +18 -15
  176. package/src/static/components/sidebar.ts +9 -13
  177. package/src/static/components/skeleton.ts +7 -10
  178. package/src/static/components/slider.ts +7 -9
  179. package/src/static/components/spinner.ts +22 -22
  180. package/src/static/components/switchInput.ts +12 -14
  181. package/src/static/components/table.ts +8 -10
  182. package/src/static/components/tag.ts +17 -11
  183. package/src/static/components/tagsInput.ts +3 -3
  184. package/src/static/components/textarea.ts +8 -13
  185. package/src/static/components/toolbar.ts +7 -10
  186. package/src/static/components/tooltip.ts +3 -2
  187. package/src/static/generated/defaults.ts +25 -9
  188. package/src/static/generated/propTypes.ts +19 -2
  189. package/src/static/h.ts +16 -0
  190. package/src/static/index.ts +8 -0
  191. package/src/static/render.test.ts +14 -10
  192. package/src/static/render.ts +33 -18
  193. package/src/static/specs.test.ts +1 -0
  194. package/src/static/specs.ts +22 -2
  195. package/src/static/stringH.ts +104 -0
  196. package/src/static/types.ts +25 -0
  197. package/src/styles/components/_autocomplete.scss +498 -0
  198. package/src/styles/components/_button.scss +55 -6
  199. package/src/styles/components/_index.scss +2 -0
  200. package/src/styles/components/_input-group.scss +292 -0
  201. package/src/styles/components/_input.scss +57 -9
  202. package/src/styles/components/_number-input.scss +88 -14
  203. package/src/styles/components/_select.scss +56 -9
  204. package/src/styles/mixins/_cut-border.scss +83 -0
  205. package/src/styles/tailwind.scss +262 -0
  206. package/src/styles/tokens.scss +8 -255
  207. package/src/types/corners.ts +10 -0
  208. package/src/utils/slots.test.ts +89 -0
  209. package/src/utils/slots.ts +80 -0
  210. package/web-types.json +392 -12
@@ -9,8 +9,8 @@
9
9
 
10
10
  import type { AlertKind, StaticCustomColors } from '../types';
11
11
 
12
+ import type { H } from '../h';
12
13
  import { composeClasses } from '../classes';
13
- import { escapeAttr } from '../escape';
14
14
 
15
15
  //----------------------------------------------------------------------------------------------------------------------
16
16
 
@@ -62,7 +62,7 @@ export interface AlertStaticProps extends StaticCustomColors
62
62
 
63
63
  //----------------------------------------------------------------------------------------------------------------------
64
64
 
65
- export function alert(props : AlertStaticProps = {}, children = '') : string
65
+ export function alert<T>(h : H<T>, props : AlertStaticProps = {}, children ?: T | T[]) : T
66
66
  {
67
67
  const kind = props.kind ?? 'info';
68
68
 
@@ -78,25 +78,25 @@ export function alert(props : AlertStaticProps = {}, children = '') : string
78
78
  propsWithDefaults as Record<string, unknown>
79
79
  );
80
80
 
81
- const attrs : string[] = [ `class="${ escapeAttr(classes) }"`, 'role="alert"' ];
81
+ const attrs : Record<string, unknown> = { class: classes, role: 'alert' };
82
82
 
83
83
  // Custom color vars — match the Vue component's useCustomColors('alert', ...) output.
84
84
  const styleParts : string[] = [];
85
85
  if(typeof props.baseColor === 'string')
86
86
  {
87
- styleParts.push(`--sk-alert-color-base: ${ escapeAttr(props.baseColor) };`);
87
+ styleParts.push(`--sk-alert-color-base: ${ props.baseColor };`);
88
88
  }
89
89
  if(typeof props.textColor === 'string')
90
90
  {
91
- styleParts.push(`--sk-alert-fg: ${ escapeAttr(props.textColor) };`);
91
+ styleParts.push(`--sk-alert-fg: ${ props.textColor };`);
92
92
  }
93
93
  if(styleParts.length > 0)
94
94
  {
95
- attrs.push(`style="${ styleParts.join(' ') }"`);
95
+ attrs.style = styleParts.join(' ');
96
96
  }
97
97
 
98
- // Determine icon HTML: explicit false = no icon; explicit string = custom; undefined = auto
99
- let iconHtml = '';
98
+ // Determine icon: explicit false = no icon; explicit string = custom; undefined = auto
99
+ let iconEl : T | null = null;
100
100
  if(props.icon !== false)
101
101
  {
102
102
  const iconContent = typeof props.icon === 'string'
@@ -105,13 +105,14 @@ export function alert(props : AlertStaticProps = {}, children = '') : string
105
105
 
106
106
  if(iconContent !== null)
107
107
  {
108
- iconHtml = `<div class="sk-alert-icon">${ iconContent }</div>`;
108
+ iconEl = h('div', { class: 'sk-alert-icon', innerHTML: iconContent });
109
109
  }
110
110
  }
111
111
 
112
- const contentHtml = `<div class="sk-alert-content">${ children }</div>`;
112
+ const contentEl = h('div', { class: 'sk-alert-content' }, children);
113
113
 
114
- return `<div ${ attrs.join(' ') }>${ iconHtml }${ contentHtml }</div>`;
114
+ const innerChildren : T[] = iconEl !== null ? [ iconEl, contentEl ] : [ contentEl ];
115
+ return h('div', attrs, innerChildren);
115
116
  }
116
117
 
117
118
  //----------------------------------------------------------------------------------------------------------------------
@@ -15,7 +15,8 @@
15
15
 
16
16
  import type { AvatarSize, ComponentKind, StaticCustomColors } from '../types';
17
17
 
18
- import { escapeAttr } from '../escape';
18
+ import type { H } from '../h';
19
+ import { text } from '../escape';
19
20
 
20
21
  //----------------------------------------------------------------------------------------------------------------------
21
22
 
@@ -39,48 +40,46 @@ const DEFAULT_ICON_SVG = '<svg class="sk-avatar-icon" viewBox="0 0 24 24" fill="
39
40
 
40
41
  //----------------------------------------------------------------------------------------------------------------------
41
42
 
42
- export function avatar(props : AvatarStaticProps = {}) : string
43
+ export function avatar<T>(h : H<T>, props : AvatarStaticProps = {}) : T
43
44
  {
44
45
  const kind = props.kind ?? 'neutral';
45
46
  const size = props.size ?? 'md';
46
47
 
47
48
  const classes = [ 'sk-avatar', `sk-${ kind }`, `sk-${ size }` ];
48
- const attrs : string[] = [ `class="${ escapeAttr(classes.join(' ')) }"` ];
49
+ const wrapperAttrs : Record<string, unknown> = { class: classes.join(' ') };
49
50
 
50
51
  // Custom color vars — match Vue's customStyles computed property
51
52
  const styleParts : string[] = [];
52
53
  if(typeof props.baseColor === 'string')
53
54
  {
54
- styleParts.push(`--sk-avatar-color-base: ${ escapeAttr(props.baseColor) };`);
55
+ styleParts.push(`--sk-avatar-color-base: ${ props.baseColor };`);
55
56
  }
56
57
  if(typeof props.textColor === 'string')
57
58
  {
58
- styleParts.push(`--sk-avatar-fg: ${ escapeAttr(props.textColor) };`);
59
+ styleParts.push(`--sk-avatar-fg: ${ props.textColor };`);
59
60
  }
60
61
  if(styleParts.length > 0)
61
62
  {
62
- attrs.push(`style="${ styleParts.join(' ') }"`);
63
+ wrapperAttrs.style = styleParts.join(' ');
63
64
  }
64
65
 
65
66
  // Inner content: image > initials > default icon (matches Vue's v-if / v-else-if / v-else)
66
- let inner : string;
67
67
  if(props.src)
68
68
  {
69
69
  const alt = props.alt ?? '';
70
- inner = `<img src="${ escapeAttr(props.src) }" alt="${ escapeAttr(alt) }" class="sk-avatar-image">`;
70
+ // img is void no children
71
+ return h('div', wrapperAttrs, h('img', { src: props.src, alt, class: 'sk-avatar-image' }));
71
72
  }
72
- else if(props.initials)
73
+
74
+ if(props.initials)
73
75
  {
74
76
  // Truncate to 2 chars, uppercase — matches Vue's displayInitials computed
75
- const display = props.initials.slice(0, 2).toUpperCase();
76
- inner = `<span class="sk-avatar-initials">${ escapeAttr(display) }</span>`;
77
- }
78
- else
79
- {
80
- inner = DEFAULT_ICON_SVG;
77
+ const display = text(props.initials.slice(0, 2).toUpperCase());
78
+ return h('div', wrapperAttrs, h('span', { class: 'sk-avatar-initials', innerHTML: display }));
81
79
  }
82
80
 
83
- return `<div ${ attrs.join(' ') }>${ inner }</div>`;
81
+ // Default fallback: inject the SVG via innerHTML so Vue/React treat it as markup, not text.
82
+ return h('div', { ...wrapperAttrs, innerHTML: DEFAULT_ICON_SVG });
84
83
  }
85
84
 
86
85
  //----------------------------------------------------------------------------------------------------------------------
@@ -7,6 +7,7 @@
7
7
 
8
8
  import type { BreadcrumbsKind, StaticCustomColors } from '../types';
9
9
 
10
+ import type { H } from '../h';
10
11
  import { render } from '../render';
11
12
  import { SPECS } from '../specs';
12
13
 
@@ -20,9 +21,9 @@ export interface BreadcrumbsStaticProps extends StaticCustomColors
20
21
 
21
22
  //----------------------------------------------------------------------------------------------------------------------
22
23
 
23
- export function breadcrumbs(props : BreadcrumbsStaticProps = {}, children = '') : string
24
+ export function breadcrumbs<T>(h : H<T>, props : BreadcrumbsStaticProps = {}, children ?: T | T[]) : T
24
25
  {
25
- return render(SPECS.breadcrumbs, props as Record<string, unknown>, children);
26
+ return render(h, SPECS.breadcrumbs, props as Record<string, unknown>, children);
26
27
  }
27
28
 
28
29
  //----------------------------------------------------------------------------------------------------------------------
@@ -6,10 +6,10 @@
6
6
  // their own <a> with class="sk-button" — this helper emits plain HTML only.
7
7
  //----------------------------------------------------------------------------------------------------------------------
8
8
 
9
- import type { ButtonKind, ButtonSize, ButtonType, ButtonVariant, StaticCustomColors } from '../types';
9
+ import type { ButtonCorner, ButtonKind, ButtonSize, ButtonType, ButtonVariant, StaticCustomColors } from '../types';
10
10
 
11
+ import type { H } from '../h';
11
12
  import { composeClasses } from '../classes';
12
- import { escapeAttr } from '../escape';
13
13
 
14
14
  //----------------------------------------------------------------------------------------------------------------------
15
15
 
@@ -23,53 +23,49 @@ export interface ButtonStaticProps extends StaticCustomColors
23
23
  loading ?: boolean;
24
24
  pressed ?: boolean;
25
25
  dense ?: boolean;
26
+ corners ?: ButtonCorner[];
26
27
  href ?: string;
27
28
  }
28
29
 
29
30
  //----------------------------------------------------------------------------------------------------------------------
30
31
 
31
- export function button(props : ButtonStaticProps = {}, children = '') : string
32
+ export function button<T>(h : H<T>, props : ButtonStaticProps = {}, children ?: T | T[]) : T
32
33
  {
33
- const classes = composeClasses({
34
- base: 'sk-button',
35
- kind: true,
36
- size: true,
37
- variant: true,
38
- booleanFlags: [ 'loading', 'pressed', 'dense' ],
39
- }, props as Record<string, unknown>);
34
+ const classes = composeClasses(
35
+ {
36
+ base: 'sk-button',
37
+ kind: true,
38
+ size: true,
39
+ variant: true,
40
+ booleanFlags: [ 'loading', 'pressed', 'dense' ],
41
+ listFlags: [ { prop: 'corners', family: 'cut' } ],
42
+ },
43
+ props as Record<string, unknown>
44
+ );
40
45
 
41
- const attrs : string[] = [ `class="${ escapeAttr(classes) }"` ];
42
46
  const isAnchor = typeof props.href === 'string';
47
+ const attrs : Record<string, unknown> = { class: classes };
43
48
 
44
49
  if(isAnchor)
45
50
  {
46
- attrs.push(`href="${ escapeAttr(props.href ?? '') }"`);
51
+ attrs.href = props.href ?? '';
47
52
  }
48
53
  else
49
54
  {
50
- const buttonType = props.type ?? 'button';
51
- attrs.push(`type="${ escapeAttr(buttonType) }"`);
55
+ attrs.type = props.type ?? 'button';
52
56
  }
53
57
 
54
58
  if(props.disabled)
55
59
  {
56
- attrs.push(isAnchor ? 'aria-disabled="true"' : 'disabled');
60
+ if(isAnchor) { attrs['aria-disabled'] = 'true'; }
61
+ else { attrs.disabled = true; }
57
62
  }
58
63
 
59
- if(props.loading)
60
- {
61
- attrs.push('aria-busy="true"');
62
- }
63
-
64
- if(props.pressed)
65
- {
66
- attrs.push('aria-pressed="true"');
67
- }
64
+ if(props.loading) { attrs['aria-busy'] = 'true'; }
65
+ if(props.pressed) { attrs['aria-pressed'] = 'true'; }
68
66
 
69
67
  const tag = isAnchor ? 'a' : 'button';
70
- const inner = `<span class="sk-button-chrome">${ children }</span>`;
71
-
72
- return `<${ tag } ${ attrs.join(' ') }>${ inner }</${ tag }>`;
68
+ return h(tag, attrs, h('span', { class: 'sk-button-chrome' }, children));
73
69
  }
74
70
 
75
71
  //----------------------------------------------------------------------------------------------------------------------
@@ -7,6 +7,7 @@
7
7
 
8
8
  import type { CardKind, StaticCustomColors } from '../types';
9
9
 
10
+ import type { H } from '../h';
10
11
  import { render } from '../render';
11
12
  import { SPECS } from '../specs';
12
13
 
@@ -19,9 +20,9 @@ export interface CardStaticProps extends StaticCustomColors
19
20
 
20
21
  //----------------------------------------------------------------------------------------------------------------------
21
22
 
22
- export function card(props : CardStaticProps = {}, children = '') : string
23
+ export function card<T>(h : H<T>, props : CardStaticProps = {}, children ?: T | T[]) : T
23
24
  {
24
- return render(SPECS.card, props as Record<string, unknown>, children);
25
+ return render(h, SPECS.card, props as Record<string, unknown>, children);
25
26
  }
26
27
 
27
28
  //----------------------------------------------------------------------------------------------------------------------
@@ -8,8 +8,8 @@
8
8
 
9
9
  import type { ComponentKind, ComponentSize } from '../types';
10
10
 
11
+ import type { H } from '../h';
11
12
  import { composeClasses } from '../classes';
12
- import { escapeAttr } from '../escape';
13
13
 
14
14
  //----------------------------------------------------------------------------------------------------------------------
15
15
 
@@ -25,24 +25,21 @@ export interface CheckboxStaticProps
25
25
 
26
26
  //----------------------------------------------------------------------------------------------------------------------
27
27
 
28
- export function checkbox(props : CheckboxStaticProps = {}, children = '') : string
28
+ export function checkbox<T>(h : H<T>, props : CheckboxStaticProps = {}, children ?: T | T[]) : T
29
29
  {
30
30
  const classes = composeClasses({ base: 'sk-checkbox', kind: true, size: true }, props as Record<string, unknown>);
31
31
 
32
- const inputAttrs : string[] = [ 'type="checkbox"' ];
33
- if(props.name)
34
- {
35
- inputAttrs.push(`name="${ escapeAttr(props.name) }"`);
36
- }
37
- if(props.checked === true) { inputAttrs.push('checked'); }
38
- if(props.disabled === true) { inputAttrs.push('disabled'); }
39
- if(props.required === true) { inputAttrs.push('required'); }
32
+ const inputAttrs : Record<string, unknown> = { type: 'checkbox' };
33
+ if(props.name) { inputAttrs.name = props.name; }
34
+ if(props.checked === true) { inputAttrs.checked = true; }
35
+ if(props.disabled === true) { inputAttrs.disabled = true; }
36
+ if(props.required === true) { inputAttrs.required = true; }
40
37
 
41
- const inputEl = `<input ${ inputAttrs.join(' ') } />`;
42
- const boxEl = `<span class="sk-checkbox-box"></span>`;
43
- const labelEl = `<span class="sk-checkbox-label">${ children }</span>`;
38
+ const inputEl = h('input', inputAttrs);
39
+ const boxEl = h('span', { class: 'sk-checkbox-box' });
40
+ const labelEl = h('span', { class: 'sk-checkbox-label' }, children);
44
41
 
45
- return `<label class="${ escapeAttr(classes) }">${ inputEl }${ boxEl }${ labelEl }</label>`;
42
+ return h('label', { class: classes }, [ inputEl, boxEl, labelEl ]);
46
43
  }
47
44
 
48
45
  //----------------------------------------------------------------------------------------------------------------------
@@ -7,8 +7,8 @@
7
7
 
8
8
  import type { ComponentSize } from '../types';
9
9
 
10
+ import type { H } from '../h';
10
11
  import { composeClasses } from '../classes';
11
- import { escapeAttr } from '../escape';
12
12
 
13
13
  //----------------------------------------------------------------------------------------------------------------------
14
14
 
@@ -22,24 +22,22 @@ export interface ColorPickerStaticProps
22
22
 
23
23
  //----------------------------------------------------------------------------------------------------------------------
24
24
 
25
- export function colorPicker(props : ColorPickerStaticProps = {}) : string
25
+ export function colorPicker<T>(h : H<T>, props : ColorPickerStaticProps = {}) : T
26
26
  {
27
27
  const classes = composeClasses({ base: 'sk-color-picker', size: true }, props as Record<string, unknown>);
28
- const attrs : string[] = [ `class="${ escapeAttr(classes) }"`, 'type="color"' ];
28
+ const attrs : Record<string, unknown> = { class: classes, type: 'color' };
29
29
 
30
30
  const passthroughString = [ 'value', 'name' ] as const;
31
31
  for(const key of passthroughString)
32
32
  {
33
33
  const val = props[key];
34
- if(typeof val === 'string')
35
- {
36
- attrs.push(`${ key }="${ escapeAttr(val) }"`);
37
- }
34
+ if(typeof val === 'string') { attrs[key] = val; }
38
35
  }
39
36
 
40
- if(props.disabled === true) { attrs.push('disabled'); }
37
+ if(props.disabled === true) { attrs.disabled = true; }
41
38
 
42
- return `<input ${ attrs.join(' ') } />`;
39
+ // void element no children
40
+ return h('input', attrs);
43
41
  }
44
42
 
45
43
  //----------------------------------------------------------------------------------------------------------------------
@@ -10,7 +10,7 @@
10
10
 
11
11
  import type { ComponentKind, ComponentSize, DividerOrientation, DividerVariant } from '../types';
12
12
 
13
- import { escapeAttr } from '../escape';
13
+ import type { H } from '../h';
14
14
 
15
15
  //----------------------------------------------------------------------------------------------------------------------
16
16
 
@@ -24,7 +24,7 @@ export interface DividerStaticProps
24
24
 
25
25
  //----------------------------------------------------------------------------------------------------------------------
26
26
 
27
- export function divider(props : DividerStaticProps = {}) : string
27
+ export function divider<T>(h : H<T>, props : DividerStaticProps = {}) : T
28
28
  {
29
29
  const orientation = props.orientation ?? 'horizontal';
30
30
  const kind = props.kind ?? 'neutral';
@@ -33,7 +33,8 @@ export function divider(props : DividerStaticProps = {}) : string
33
33
  const classes : string[] = [ 'sk-divider', `sk-${ orientation }`, `sk-${ kind }`, `sk-${ size }` ];
34
34
  if(props.variant === 'subtle') { classes.push('sk-subtle'); }
35
35
 
36
- return `<hr class="${ escapeAttr(classes.join(' ')) }" role="separator">`;
36
+ // hr is void no children
37
+ return h('hr', { class: classes.join(' '), role: 'separator' });
37
38
  }
38
39
 
39
40
  //----------------------------------------------------------------------------------------------------------------------
@@ -8,8 +8,9 @@
8
8
 
9
9
  import type { SemanticKind } from '../types';
10
10
 
11
+ import type { H } from '../h';
11
12
  import { composeClasses } from '../classes';
12
- import { escapeAttr } from '../escape';
13
+ import { text } from '../escape';
13
14
 
14
15
  //----------------------------------------------------------------------------------------------------------------------
15
16
 
@@ -23,14 +24,22 @@ export interface DropdownStaticProps
23
24
 
24
25
  //----------------------------------------------------------------------------------------------------------------------
25
26
 
26
- export function dropdown(props : DropdownStaticProps, children = '') : string
27
+ export function dropdown<T>(h : H<T>, props : DropdownStaticProps, children ?: T | T[]) : T
27
28
  {
28
29
  const spec = { base: 'sk-dropdown', kind: true, size: true } as const;
29
30
  const classes = composeClasses(spec, props as unknown as Record<string, unknown>);
30
- const attrs : string[] = [ `class="${ escapeAttr(classes) }"` ];
31
- if(props.open) { attrs.push('open'); }
32
-
33
- return `<details ${ attrs.join(' ') }><summary>${ escapeAttr(props.summary) }</summary>${ children }</details>`;
31
+ const attrs : Record<string, unknown> = { class: classes };
32
+ if(props.open) { attrs.open = true; }
33
+
34
+ const summaryEl = h('summary', { innerHTML: text(props.summary) });
35
+ const inner : T[] = [ summaryEl ];
36
+ if(children !== undefined)
37
+ {
38
+ if(Array.isArray(children)) { inner.push(...children); }
39
+ else { inner.push(children); }
40
+ }
41
+
42
+ return h('details', attrs, inner);
34
43
  }
35
44
 
36
45
  //----------------------------------------------------------------------------------------------------------------------
@@ -21,7 +21,8 @@
21
21
 
22
22
  import type { FieldLabelPosition } from '../types';
23
23
 
24
- import { escapeAttr } from '../escape';
24
+ import type { H } from '../h';
25
+ import { text } from '../escape';
25
26
 
26
27
  //----------------------------------------------------------------------------------------------------------------------
27
28
 
@@ -43,44 +44,60 @@ export interface FieldStaticProps
43
44
 
44
45
  //----------------------------------------------------------------------------------------------------------------------
45
46
 
46
- export function field(props : FieldStaticProps = {}, children = '') : string
47
+ export function field<T>(h : H<T>, props : FieldStaticProps = {}, children ?: T | T[]) : T
47
48
  {
48
49
  const labelPosition = props.labelPosition ?? 'top';
49
50
 
50
51
  const classes : string[] = [ 'sk-field', `sk-label-${ labelPosition }` ];
51
52
  if(props.error) { classes.push('sk-has-error'); }
52
53
 
53
- const attrs = `class="${ escapeAttr(classes.join(' ')) }"`;
54
+ const rootAttrs : Record<string, unknown> = { class: classes.join(' ') };
55
+
56
+ const inner : T[] = [];
54
57
 
55
58
  // Label element (only when label prop is set)
56
- let labelHtml = '';
57
59
  if(props.label)
58
60
  {
59
- const forAttr = props.id ? ` for="${ escapeAttr(props.id) }"` : '';
60
- const requiredSpan = props.required ? '<span class="sk-field-required">*</span>' : '';
61
- labelHtml = `<label${ forAttr } class="sk-field-label">${ escapeAttr(props.label) }${ requiredSpan }</label>`;
61
+ const labelAttrs : Record<string, unknown> = { class: 'sk-field-label' };
62
+ if(props.id) { labelAttrs.for = props.id; }
63
+
64
+ if(props.required)
65
+ {
66
+ // Combine label text + required asterisk as innerHTML so both are real DOM nodes in Vue/React.
67
+ const requiredSpan = `<span class="sk-field-required">*</span>`;
68
+ labelAttrs.innerHTML = `${ text(props.label) }${ requiredSpan }`;
69
+ inner.push(h('label', labelAttrs));
70
+ }
71
+ else
72
+ {
73
+ labelAttrs.innerHTML = text(props.label);
74
+ inner.push(h('label', labelAttrs));
75
+ }
62
76
  }
63
77
 
64
78
  // Input wrapper always emitted
65
- const wrapperHtml = `<div class="sk-field-input-wrapper">${ children }</div>`;
79
+ inner.push(h('div', { class: 'sk-field-input-wrapper' }, children));
66
80
 
67
81
  // Description (only when set and no error)
68
- let descriptionHtml = '';
69
82
  if(props.description && !props.error)
70
83
  {
71
- const idAttr = props.id ? ` id="${ escapeAttr(props.id) }-description"` : '';
72
- descriptionHtml = `<p${ idAttr } class="sk-field-description">${ escapeAttr(props.description) }</p>`;
84
+ const descAttrs : Record<string, unknown> = {
85
+ class: 'sk-field-description',
86
+ innerHTML: text(props.description),
87
+ };
88
+ if(props.id) { descAttrs.id = `${ props.id }-description`; }
89
+ inner.push(h('p', descAttrs));
73
90
  }
74
91
 
75
92
  // Error (only when set)
76
- let errorHtml = '';
77
93
  if(props.error)
78
94
  {
79
- const idAttr = props.id ? ` id="${ escapeAttr(props.id) }-error"` : '';
80
- errorHtml = `<p${ idAttr } class="sk-field-error">${ escapeAttr(props.error) }</p>`;
95
+ const errAttrs : Record<string, unknown> = { class: 'sk-field-error', innerHTML: text(props.error) };
96
+ if(props.id) { errAttrs.id = `${ props.id }-error`; }
97
+ inner.push(h('p', errAttrs));
81
98
  }
82
99
 
83
- return `<div ${ attrs }>${ labelHtml }${ wrapperHtml }${ descriptionHtml }${ errorHtml }</div>`;
100
+ return h('div', rootAttrs, inner);
84
101
  }
85
102
 
86
103
  //----------------------------------------------------------------------------------------------------------------------
@@ -7,6 +7,7 @@
7
7
 
8
8
  import type { GroupOrientation } from '../types';
9
9
 
10
+ import type { H } from '../h';
10
11
  import { render } from '../render';
11
12
  import { SPECS } from '../specs';
12
13
 
@@ -19,9 +20,9 @@ export interface GroupStaticProps
19
20
 
20
21
  //----------------------------------------------------------------------------------------------------------------------
21
22
 
22
- export function group(props : GroupStaticProps = {}, children = '') : string
23
+ export function group<T>(h : H<T>, props : GroupStaticProps = {}, children ?: T | T[]) : T
23
24
  {
24
- return render(SPECS.group, props as Record<string, unknown>, children);
25
+ return render(h, SPECS.group, props as Record<string, unknown>, children);
25
26
  }
26
27
 
27
28
  //----------------------------------------------------------------------------------------------------------------------
@@ -5,10 +5,10 @@
5
5
  // name, id, and boolean attrs (disabled, readonly, required).
6
6
  //----------------------------------------------------------------------------------------------------------------------
7
7
 
8
- import type { ComponentKind, ComponentSize } from '../types';
8
+ import type { ComponentKind, ComponentSize, InputCorner } from '../types';
9
9
 
10
+ import type { H } from '../h';
10
11
  import { composeClasses } from '../classes';
11
- import { escapeAttr } from '../escape';
12
12
 
13
13
  //----------------------------------------------------------------------------------------------------------------------
14
14
 
@@ -16,6 +16,7 @@ export interface InputStaticProps
16
16
  {
17
17
  kind ?: ComponentKind;
18
18
  size ?: ComponentSize;
19
+ corners ?: InputCorner[];
19
20
  type ?: string;
20
21
  value ?: string;
21
22
  placeholder ?: string;
@@ -28,28 +29,32 @@ export interface InputStaticProps
28
29
 
29
30
  //----------------------------------------------------------------------------------------------------------------------
30
31
 
31
- export function input(props : InputStaticProps = {}) : string
32
+ export function input<T>(h : H<T>, props : InputStaticProps = {}) : T
32
33
  {
33
- const classes = composeClasses({ base: 'sk-input', kind: true, size: true }, props as Record<string, unknown>);
34
- const attrs : string[] = [ `class="${ escapeAttr(classes) }"` ];
34
+ const classes = composeClasses(
35
+ {
36
+ base: 'sk-input',
37
+ kind: true,
38
+ size: true,
39
+ listFlags: [ { prop: 'corners', family: 'cut' } ],
40
+ },
41
+ props as Record<string, unknown>
42
+ );
43
+ const attrs : Record<string, unknown> = { class: classes };
35
44
 
36
45
  const passthroughString = [ 'type', 'value', 'placeholder', 'name', 'id' ] as const;
37
46
  for(const key of passthroughString)
38
47
  {
39
48
  const val = props[key];
40
- if(typeof val === 'string')
41
- {
42
- attrs.push(`${ key }="${ escapeAttr(val) }"`);
43
- }
49
+ if(typeof val === 'string') { attrs[key] = val; }
44
50
  }
45
51
 
46
- const passthroughBool = [ 'disabled', 'readonly', 'required' ] as const;
47
- for(const key of passthroughBool)
48
- {
49
- if(props[key] === true) { attrs.push(key); }
50
- }
52
+ if(props.disabled === true) { attrs.disabled = true; }
53
+ if(props.readonly === true) { attrs.readonly = true; }
54
+ if(props.required === true) { attrs.required = true; }
51
55
 
52
- return `<input ${ attrs.join(' ') } />`;
56
+ // void element no children
57
+ return h('input', attrs);
53
58
  }
54
59
 
55
60
  //----------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,30 @@
1
+ //----------------------------------------------------------------------------------------------------------------------
2
+ // InputGroup Static Helper
3
+ //
4
+ // Thin typed wrapper over the generic render() core. Emits a class-API <div> with sk-input-group
5
+ // modifier classes derived from props.
6
+ //----------------------------------------------------------------------------------------------------------------------
7
+
8
+ import type { ComponentKind, ComponentSize, InputGroupCorner, StaticCustomColors } from '../types';
9
+
10
+ import type { H } from '../h';
11
+ import { render } from '../render';
12
+ import { SPECS } from '../specs';
13
+
14
+ //----------------------------------------------------------------------------------------------------------------------
15
+
16
+ export interface InputGroupStaticProps extends StaticCustomColors
17
+ {
18
+ kind ?: ComponentKind;
19
+ size ?: ComponentSize;
20
+ corners ?: InputGroupCorner[];
21
+ }
22
+
23
+ //----------------------------------------------------------------------------------------------------------------------
24
+
25
+ export function inputGroup<T>(h : H<T>, props : InputGroupStaticProps = {}, children ?: T | T[]) : T
26
+ {
27
+ return render(h, SPECS.inputGroup, props as Record<string, unknown>, children);
28
+ }
29
+
30
+ //----------------------------------------------------------------------------------------------------------------------