aeico-components 0.1.3 → 0.1.5

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 (218) hide show
  1. package/dist/alert.cjs +6 -0
  2. package/dist/alert.cjs.map +1 -0
  3. package/dist/alert.js +6 -0
  4. package/dist/alert.js.map +1 -0
  5. package/dist/badge.cjs +6 -0
  6. package/dist/badge.cjs.map +1 -0
  7. package/dist/badge.js +6 -0
  8. package/dist/badge.js.map +1 -0
  9. package/dist/breadcrumb.cjs +7 -0
  10. package/dist/breadcrumb.cjs.map +1 -0
  11. package/dist/breadcrumb.js +7 -0
  12. package/dist/breadcrumb.js.map +1 -0
  13. package/dist/button-group.cjs +6 -0
  14. package/dist/button-group.cjs.map +1 -0
  15. package/dist/button-group.js +6 -0
  16. package/dist/button-group.js.map +1 -0
  17. package/dist/button.cjs +6 -0
  18. package/dist/button.cjs.map +1 -0
  19. package/dist/button.js +6 -0
  20. package/dist/button.js.map +1 -0
  21. package/dist/card.cjs +6 -0
  22. package/dist/card.cjs.map +1 -0
  23. package/dist/card.js +6 -0
  24. package/dist/card.js.map +1 -0
  25. package/dist/checkbox.cjs +6 -0
  26. package/dist/checkbox.cjs.map +1 -0
  27. package/dist/checkbox.js +6 -0
  28. package/dist/checkbox.js.map +1 -0
  29. package/dist/chunks/aeico-component.cjs +17 -0
  30. package/dist/chunks/aeico-component.cjs.map +1 -0
  31. package/dist/chunks/aeico-component.js +18 -0
  32. package/dist/chunks/aeico-component.js.map +1 -0
  33. package/dist/chunks/aeico-field.cjs +179 -0
  34. package/dist/chunks/aeico-field.cjs.map +1 -0
  35. package/dist/chunks/aeico-field.js +180 -0
  36. package/dist/chunks/aeico-field.js.map +1 -0
  37. package/dist/chunks/alert.cjs +170 -0
  38. package/dist/chunks/alert.cjs.map +1 -0
  39. package/dist/chunks/alert.js +171 -0
  40. package/dist/chunks/alert.js.map +1 -0
  41. package/dist/chunks/badge.cjs +85 -0
  42. package/dist/chunks/badge.cjs.map +1 -0
  43. package/dist/chunks/badge.js +86 -0
  44. package/dist/chunks/badge.js.map +1 -0
  45. package/dist/chunks/breadcrumb-item.cjs +261 -0
  46. package/dist/chunks/breadcrumb-item.cjs.map +1 -0
  47. package/dist/chunks/breadcrumb-item.js +262 -0
  48. package/dist/chunks/breadcrumb-item.js.map +1 -0
  49. package/dist/chunks/button-group.cjs +79 -0
  50. package/dist/chunks/button-group.cjs.map +1 -0
  51. package/dist/chunks/button-group.js +80 -0
  52. package/dist/chunks/button-group.js.map +1 -0
  53. package/dist/chunks/button.cjs +348 -0
  54. package/dist/chunks/button.cjs.map +1 -0
  55. package/dist/chunks/button.js +349 -0
  56. package/dist/chunks/button.js.map +1 -0
  57. package/dist/chunks/card.cjs +93 -0
  58. package/dist/chunks/card.cjs.map +1 -0
  59. package/dist/chunks/card.js +94 -0
  60. package/dist/chunks/card.js.map +1 -0
  61. package/dist/chunks/checkbox.cjs +73 -0
  62. package/dist/chunks/checkbox.cjs.map +1 -0
  63. package/dist/chunks/checkbox.js +74 -0
  64. package/dist/chunks/checkbox.js.map +1 -0
  65. package/dist/chunks/color.cjs +4 -0
  66. package/dist/chunks/color.cjs.map +1 -0
  67. package/dist/chunks/color.js +5 -0
  68. package/dist/chunks/color.js.map +1 -0
  69. package/dist/chunks/detail.cjs +143 -0
  70. package/dist/chunks/detail.cjs.map +1 -0
  71. package/dist/chunks/detail.js +144 -0
  72. package/dist/chunks/detail.js.map +1 -0
  73. package/dist/chunks/dialog.cjs +117 -0
  74. package/dist/chunks/dialog.cjs.map +1 -0
  75. package/dist/chunks/dialog.js +118 -0
  76. package/dist/chunks/dialog.js.map +1 -0
  77. package/dist/chunks/divider.cjs +80 -0
  78. package/dist/chunks/divider.cjs.map +1 -0
  79. package/dist/chunks/divider.js +81 -0
  80. package/dist/chunks/divider.js.map +1 -0
  81. package/dist/chunks/dropdown-button.cjs +534 -0
  82. package/dist/chunks/dropdown-button.cjs.map +1 -0
  83. package/dist/chunks/dropdown-button.js +535 -0
  84. package/dist/chunks/dropdown-button.js.map +1 -0
  85. package/dist/chunks/icon-button.cjs +35 -0
  86. package/dist/chunks/icon-button.cjs.map +1 -0
  87. package/dist/chunks/icon-button.js +36 -0
  88. package/dist/chunks/icon-button.js.map +1 -0
  89. package/dist/chunks/icon.cjs +78 -0
  90. package/dist/chunks/icon.cjs.map +1 -0
  91. package/dist/chunks/icon.js +79 -0
  92. package/dist/chunks/icon.js.map +1 -0
  93. package/dist/chunks/navbar.cjs +142 -0
  94. package/dist/chunks/navbar.cjs.map +1 -0
  95. package/dist/chunks/navbar.js +143 -0
  96. package/dist/chunks/navbar.js.map +1 -0
  97. package/dist/chunks/radio.cjs +181 -0
  98. package/dist/chunks/radio.cjs.map +1 -0
  99. package/dist/chunks/radio.js +182 -0
  100. package/dist/chunks/radio.js.map +1 -0
  101. package/dist/chunks/select.cjs +350 -0
  102. package/dist/chunks/select.cjs.map +1 -0
  103. package/dist/chunks/select.js +351 -0
  104. package/dist/chunks/select.js.map +1 -0
  105. package/dist/chunks/size.cjs +4 -0
  106. package/dist/chunks/size.cjs.map +1 -0
  107. package/dist/chunks/size.js +5 -0
  108. package/dist/chunks/size.js.map +1 -0
  109. package/dist/chunks/slider.cjs +648 -0
  110. package/dist/chunks/slider.cjs.map +1 -0
  111. package/dist/chunks/slider.js +649 -0
  112. package/dist/chunks/slider.js.map +1 -0
  113. package/dist/chunks/switch.cjs +73 -0
  114. package/dist/chunks/switch.cjs.map +1 -0
  115. package/dist/chunks/switch.js +74 -0
  116. package/dist/chunks/switch.js.map +1 -0
  117. package/dist/chunks/tab-panel.cjs +165 -0
  118. package/dist/chunks/tab-panel.cjs.map +1 -0
  119. package/dist/chunks/tab-panel.js +166 -0
  120. package/dist/chunks/tab-panel.js.map +1 -0
  121. package/dist/chunks/tag.cjs +108 -0
  122. package/dist/chunks/tag.cjs.map +1 -0
  123. package/dist/chunks/tag.js +109 -0
  124. package/dist/chunks/tag.js.map +1 -0
  125. package/dist/chunks/text-input.cjs +59 -0
  126. package/dist/chunks/text-input.cjs.map +1 -0
  127. package/dist/chunks/text-input.js +60 -0
  128. package/dist/chunks/text-input.js.map +1 -0
  129. package/dist/chunks/variables.cjs +372 -0
  130. package/dist/chunks/variables.cjs.map +1 -0
  131. package/dist/chunks/variables.js +373 -0
  132. package/dist/chunks/variables.js.map +1 -0
  133. package/dist/detail.cjs +6 -0
  134. package/dist/detail.cjs.map +1 -0
  135. package/dist/detail.js +6 -0
  136. package/dist/detail.js.map +1 -0
  137. package/dist/dialog.cjs +6 -0
  138. package/dist/dialog.cjs.map +1 -0
  139. package/dist/dialog.js +6 -0
  140. package/dist/dialog.js.map +1 -0
  141. package/dist/divider.cjs +6 -0
  142. package/dist/divider.cjs.map +1 -0
  143. package/dist/divider.js +6 -0
  144. package/dist/divider.js.map +1 -0
  145. package/dist/dropdown.cjs +7 -0
  146. package/dist/dropdown.cjs.map +1 -0
  147. package/dist/dropdown.js +7 -0
  148. package/dist/dropdown.js.map +1 -0
  149. package/dist/icon-button.cjs +6 -0
  150. package/dist/icon-button.cjs.map +1 -0
  151. package/dist/icon-button.js +6 -0
  152. package/dist/icon-button.js.map +1 -0
  153. package/dist/icon.cjs +6 -0
  154. package/dist/icon.cjs.map +1 -0
  155. package/dist/icon.js +6 -0
  156. package/dist/icon.js.map +1 -0
  157. package/dist/index.cjs +93 -4168
  158. package/dist/index.cjs.map +1 -1
  159. package/dist/index.js +92 -4167
  160. package/dist/index.js.map +1 -1
  161. package/dist/navbar.cjs +6 -0
  162. package/dist/navbar.cjs.map +1 -0
  163. package/dist/navbar.js +6 -0
  164. package/dist/navbar.js.map +1 -0
  165. package/dist/radio-group.cjs +7 -0
  166. package/dist/radio-group.cjs.map +1 -0
  167. package/dist/radio-group.js +7 -0
  168. package/dist/radio-group.js.map +1 -0
  169. package/dist/select.cjs +99 -0
  170. package/dist/select.cjs.map +1 -0
  171. package/dist/select.js +99 -0
  172. package/dist/select.js.map +1 -0
  173. package/dist/slider.cjs +6 -0
  174. package/dist/slider.cjs.map +1 -0
  175. package/dist/slider.js +6 -0
  176. package/dist/slider.js.map +1 -0
  177. package/dist/switch.cjs +6 -0
  178. package/dist/switch.cjs.map +1 -0
  179. package/dist/switch.js +6 -0
  180. package/dist/switch.js.map +1 -0
  181. package/dist/tabs.cjs +8 -0
  182. package/dist/tabs.cjs.map +1 -0
  183. package/dist/tabs.js +8 -0
  184. package/dist/tabs.js.map +1 -0
  185. package/dist/tag.cjs +5 -0
  186. package/dist/tag.cjs.map +1 -0
  187. package/dist/tag.js +5 -0
  188. package/dist/tag.js.map +1 -0
  189. package/dist/text-input.cjs +6 -0
  190. package/dist/text-input.cjs.map +1 -0
  191. package/dist/text-input.js +6 -0
  192. package/dist/text-input.js.map +1 -0
  193. package/dist/types/aeico-field.d.ts +5 -5
  194. package/dist/types/button/button.d.ts +2 -1
  195. package/dist/types/checkbox/checkbox.d.ts +5 -5
  196. package/dist/types/index.d.ts +2 -0
  197. package/dist/types/progress-bar/defines.d.ts +1 -0
  198. package/dist/types/progress-bar/index.d.ts +3 -0
  199. package/dist/types/progress-bar/progress-bar.d.ts +37 -0
  200. package/dist/types/radio-group/radio-group.d.ts +1 -1
  201. package/dist/types/select/select.d.ts +1 -1
  202. package/dist/types/switch/switch.d.ts +5 -5
  203. package/package.json +16 -4
  204. package/src/aeico-field.ts +12 -8
  205. package/src/button/button.ts +11 -13
  206. package/src/checkbox/checkbox.ts +4 -4
  207. package/src/index.ts +2 -0
  208. package/src/navbar/navbar.ts +1 -3
  209. package/src/progress-bar/defines.ts +8 -0
  210. package/src/progress-bar/index.ts +3 -0
  211. package/src/progress-bar/progress-bar.ts +80 -0
  212. package/src/radio-group/radio-group.ts +1 -1
  213. package/src/select/select.ts +1 -1
  214. package/src/styles/components/progress-bar.css +44 -0
  215. package/src/switch/switch.ts +4 -4
  216. package/src/tabs/tab.ts +1 -1
  217. package/src/tabs/tabs.ts +1 -2
  218. package/src/utils.ts +1 -0
@@ -1,4 +1,4 @@
1
- import AeicoField from '../aeico-field';
1
+ import AeicoField, { type FieldAction } from '../aeico-field';
2
2
  import type { InferProps, Props } from 'aeico';
3
3
  import { html } from 'aeico';
4
4
  import styleVariables from '../styles/variables.css?inline';
@@ -7,7 +7,7 @@ import colorCSS from '../styles/color.css?inline';
7
7
  import styles from '../styles/components/checkbox.css?inline';
8
8
  import { CheckboxVariant } from './defines';
9
9
 
10
- class Checkbox extends AeicoField {
10
+ class Checkbox extends AeicoField<boolean> {
11
11
  protected fieldElement: HTMLInputElement | null = null;
12
12
 
13
13
  static tagName = 'checkbox';
@@ -34,11 +34,11 @@ class Checkbox extends AeicoField {
34
34
  }
35
35
  }
36
36
 
37
- protected getEventPayload(checked: boolean, oldChecked: boolean, action: any) {
37
+ protected getEventPayload(checked: boolean, oldChecked: boolean, action: FieldAction) {
38
38
  return { checked, oldChecked, action };
39
39
  }
40
40
 
41
- protected setValue(checked: boolean, options?: { silent?: boolean; action?: any }): void {
41
+ protected setValue(checked: boolean, options?: { silent?: boolean; action?: FieldAction }): void {
42
42
  const oldChecked = this.getValue();
43
43
  this.checked = checked;
44
44
  this.writeValue(checked);
package/src/index.ts CHANGED
@@ -57,6 +57,7 @@ export { default as Divider } from './divider';
57
57
  export { default as Card } from './card';
58
58
  export { default as Navbar } from './navbar';
59
59
  export { default as Detail } from './detail';
60
+ export { default as ProgressBar } from './progress-bar';
60
61
 
61
62
  // Component types
62
63
  export type { SelectProps, SelectOption, SelectOptions, SelectOptionValue } from './select';
@@ -78,6 +79,7 @@ export type { BadgeProps, BadgeColor, BadgeSize, BadgeVariant } from './badge';
78
79
  export type { TagProps, TagColor, TagSize, TagVariant } from './tag';
79
80
  export type { DialogProps } from './dialog';
80
81
  export type { IconProps, IconSize, IconColor, IconDefinition, IconRegistryData } from './icon';
82
+ export type { ProgressBarProps, ProgressBarColor } from './progress-bar';
81
83
  export type { IconButtonProps, IconButtonVariant, IconButtonSize } from './icon-button';
82
84
  export type { DividerProps } from './divider';
83
85
  export type { CardProps, CardVariant, CardColor } from './card';
@@ -74,8 +74,6 @@ class Navbar extends AeicoComponent {
74
74
 
75
75
  connectedCallback() {
76
76
  super.connectedCallback();
77
- // Close menu when a nav link is clicked on mobile
78
- this.listen('click', this._handleInnerClick);
79
77
  // Close menu when clicking outside the navbar
80
78
  this._outsideClickHandler = (e: MouseEvent) => {
81
79
  // Event retargeting in shadow DOM means e.target is the host element
@@ -119,7 +117,7 @@ class Navbar extends AeicoComponent {
119
117
 
120
118
  protected render() {
121
119
  return html(({ div, nav, button, span, slot }) => {
122
- div({ class: 'inner' }, () => {
120
+ div({ class: 'inner', '@click': this._handleInnerClick }, () => {
123
121
  div({ part: 'brand' }, () => {
124
122
  slot({ name: 'brand' });
125
123
  });
@@ -0,0 +1,8 @@
1
+ export type ProgressBarColor =
2
+ | 'default'
3
+ | 'primary'
4
+ | 'secondary'
5
+ | 'success'
6
+ | 'danger'
7
+ | 'warning'
8
+ | 'info';
@@ -0,0 +1,3 @@
1
+ export { default, default as ProgressBar } from './progress-bar';
2
+ export type { ProgressBarProps } from './progress-bar';
3
+ export type { ProgressBarColor } from './defines';
@@ -0,0 +1,80 @@
1
+ import type { InferProps } from 'aeico';
2
+ import { html, prop } from 'aeico';
3
+ import AeicoComponent from '../aeico-component';
4
+ import styleVariables from '../styles/variables.css?inline';
5
+ import colorCSS from '../styles/color.css?inline';
6
+ import style from '../styles/components/progress-bar.css?inline';
7
+ import type { ProgressBarColor } from './defines';
8
+
9
+ /**
10
+ * Progress bars show how far along an ongoing operation is as a horizontal fill.
11
+ * Use them for file uploads, multi-step flows, or any task with measurable progress.
12
+ *
13
+ * @prop {number} value - Completion percentage, automatically clamped to 0–100.
14
+ * @prop {string} label - Accessible label applied as `aria-label` on the track.
15
+ * @prop {'default'|'primary'|'secondary'|'success'|'danger'|'warning'|'info'} color
16
+ * - Preset color variant driven by the shared color system.
17
+ * @prop {boolean} animated - When set, overlays a shimmer sweep animation on the bar.
18
+ *
19
+ * @csspart base - The outermost wrapper `<div>`.
20
+ * @csspart track - The background track `<div>`.
21
+ * @csspart bar - The filled progress `<span>`.
22
+ *
23
+ * @cssproperty [--progress-height=8px] - Height of both the track and the bar.
24
+ * @cssproperty [--progress-bar-color=var(--color-solid)] - Fill color of the bar.
25
+ * When set, takes precedence over the `color` prop entirely.
26
+ */
27
+ class ProgressBar extends AeicoComponent {
28
+ static tagName = 'progress-bar';
29
+ protected static styles = [styleVariables, colorCSS, style];
30
+
31
+ @prop({ type: Number })
32
+ accessor value: number = 0;
33
+
34
+ @prop({ type: String })
35
+ accessor label: string = '';
36
+
37
+ @prop({ type: String })
38
+ accessor color: ProgressBarColor = 'primary';
39
+
40
+ @prop({ type: Boolean })
41
+ accessor animated: boolean = false;
42
+
43
+ protected render() {
44
+ const clamped = Math.min(100, Math.max(0, this.value));
45
+
46
+ return html(({ div, span }) => {
47
+ div({ part: 'base' }, () => {
48
+ div(
49
+ {
50
+ part: 'track',
51
+ className: 'progress-track',
52
+ role: 'progressbar',
53
+ 'aria-valuenow': String(clamped),
54
+ 'aria-valuemin': '0',
55
+ 'aria-valuemax': '100',
56
+ ...(this.label ? { 'aria-label': this.label } : {}),
57
+ },
58
+ () => {
59
+ span({
60
+ part: 'bar',
61
+ className: 'progress-bar',
62
+ style: { width: `${clamped}%` },
63
+ });
64
+ },
65
+ );
66
+ });
67
+ });
68
+ }
69
+ }
70
+
71
+ ProgressBar.register();
72
+
73
+ declare global {
74
+ interface HTMLElementTagNameMap {
75
+ 'ae-progress-bar': ProgressBar;
76
+ }
77
+ }
78
+
79
+ export default ProgressBar;
80
+ export type ProgressBarProps = InferProps<typeof ProgressBar>;
@@ -117,7 +117,7 @@ class RadioGroup extends AeicoField {
117
117
  return this.value ?? '';
118
118
  }
119
119
 
120
- protected writeValue(_value: any): void {
120
+ protected writeValue(_value: string): void {
121
121
  // All visual state is driven by builder diff on next render;
122
122
  // for native radio inputs we need to sync checked immediately.
123
123
  // The render() reads this.value, so update handles the rest.
@@ -107,7 +107,7 @@ class Select extends AeicoField<SelectOptionValue | SelectMultiValue> {
107
107
  // Reactive re-render via this.value prop change handles the display update
108
108
  }
109
109
 
110
- protected getValue(): any {
110
+ protected getValue(): SelectOptionValue | SelectMultiValue {
111
111
  if (this.multiple) return this._getMultiValues();
112
112
 
113
113
  return this.value || '';
@@ -0,0 +1,44 @@
1
+ :host {
2
+ display: block;
3
+ --progress-height: 8px;
4
+ --color-solid: var(--color-primary);
5
+ }
6
+
7
+ .progress-track {
8
+ width: 100%;
9
+ height: var(--progress-height);
10
+ background: var(--border-subtle);
11
+ border-radius: calc(var(--progress-height) / 2);
12
+ overflow: hidden;
13
+ }
14
+
15
+ .progress-bar {
16
+ display: block;
17
+ height: 100%;
18
+ border-radius: inherit;
19
+ background: var(--progress-bar-color, var(--color-solid));
20
+ width: 0%;
21
+ transition: width 0.35s ease;
22
+ position: relative;
23
+ }
24
+
25
+ /* animated: shimmer sweep on top of the solid fill */
26
+ :host([animated]) .progress-bar::after {
27
+ content: '';
28
+ position: absolute;
29
+ inset: 0;
30
+ border-radius: inherit;
31
+ background: linear-gradient(
32
+ 90deg,
33
+ transparent 0%,
34
+ rgba(255, 255, 255, 0.35) 50%,
35
+ transparent 100%
36
+ );
37
+ background-size: 200% 100%;
38
+ animation: progress-shimmer 1.4s linear infinite;
39
+ }
40
+
41
+ @keyframes progress-shimmer {
42
+ 0% { background-position: 200% 0; }
43
+ 100% { background-position: -200% 0; }
44
+ }
@@ -1,4 +1,4 @@
1
- import AeicoField from '../aeico-field';
1
+ import AeicoField, { type FieldAction } from '../aeico-field';
2
2
  import type { InferProps, Props } from 'aeico';
3
3
  import { html } from 'aeico';
4
4
  import styleVariables from '../styles/variables.css?inline';
@@ -6,7 +6,7 @@ import sizeCSS from '../styles/size.css?inline';
6
6
  import colorCSS from '../styles/color.css?inline';
7
7
  import styles from '../styles/components/switch.css?inline';
8
8
 
9
- class Switch extends AeicoField {
9
+ class Switch extends AeicoField<boolean> {
10
10
  protected fieldElement: HTMLInputElement | null = null;
11
11
 
12
12
  static tagName = 'switch';
@@ -31,11 +31,11 @@ class Switch extends AeicoField {
31
31
  }
32
32
  }
33
33
 
34
- protected getEventPayload(checked: boolean, oldChecked: boolean, action: any) {
34
+ protected getEventPayload(checked: boolean, oldChecked: boolean, action: FieldAction) {
35
35
  return { checked, oldChecked, action };
36
36
  }
37
37
 
38
- protected setValue(checked: boolean, options?: { silent?: boolean; action?: any }): void {
38
+ protected setValue(checked: boolean, options?: { silent?: boolean; action?: FieldAction }): void {
39
39
  const oldChecked = this.getValue();
40
40
  this.checked = checked;
41
41
  this.writeValue(checked);
package/src/tabs/tab.ts CHANGED
@@ -19,7 +19,6 @@ class Tab extends AeicoComponent {
19
19
  connectedCallback() {
20
20
  this.setAttribute('slot', 'tab');
21
21
  super.connectedCallback();
22
- this.listen('click', this._handleClick);
23
22
  }
24
23
 
25
24
  private _handleClick = () => {
@@ -42,6 +41,7 @@ class Tab extends AeicoComponent {
42
41
  'aria-selected': this.active,
43
42
  'aria-disabled': this.disabled,
44
43
  disabled: this.disabled,
44
+ '@click': this._handleClick,
45
45
  },
46
46
  () => {
47
47
  slot();
package/src/tabs/tabs.ts CHANGED
@@ -66,7 +66,6 @@ class Tabs extends AeicoComponent {
66
66
 
67
67
  connectedCallback() {
68
68
  super.connectedCallback();
69
- this.listen('_tab-click', this._handleTabClick);
70
69
  this._observer = new MutationObserver(() => this.update());
71
70
  this._observer.observe(this, { childList: true });
72
71
  }
@@ -113,7 +112,7 @@ class Tabs extends AeicoComponent {
113
112
 
114
113
  protected render() {
115
114
  return html(({ nav, div, slot }) => {
116
- nav({ part: 'tab-nav', role: 'tablist' }, () => {
115
+ nav({ part: 'tab-nav', role: 'tablist', '@_tab-click': this._handleTabClick }, () => {
117
116
  slot({ name: 'tab' });
118
117
  });
119
118
  div({ part: 'panels' }, () => {
package/src/utils.ts CHANGED
@@ -2,5 +2,6 @@ export const SVG_NS = 'http://www.w3.org/2000/svg';
2
2
 
3
3
  export function toKebab(str: string): string {
4
4
  const cleaned = str.replace(/^[_\d]+/, '');
5
+
5
6
  return cleaned.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
6
7
  }