bitwrench 2.0.14 → 2.0.16

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 (61) hide show
  1. package/README.md +57 -21
  2. package/dist/bitwrench-bccl.cjs.js +3746 -0
  3. package/dist/bitwrench-bccl.cjs.min.js +40 -0
  4. package/dist/bitwrench-bccl.esm.js +3741 -0
  5. package/dist/bitwrench-bccl.esm.min.js +40 -0
  6. package/dist/bitwrench-bccl.umd.js +3752 -0
  7. package/dist/bitwrench-bccl.umd.min.js +40 -0
  8. package/dist/bitwrench-code-edit.cjs.js +99 -49
  9. package/dist/bitwrench-code-edit.cjs.min.js +23 -0
  10. package/dist/bitwrench-code-edit.es5.js +79 -16
  11. package/dist/bitwrench-code-edit.es5.min.js +9 -2
  12. package/dist/bitwrench-code-edit.esm.js +99 -49
  13. package/dist/bitwrench-code-edit.esm.min.js +9 -2
  14. package/dist/bitwrench-code-edit.umd.js +99 -49
  15. package/dist/bitwrench-code-edit.umd.min.js +9 -2
  16. package/dist/bitwrench-lean.cjs.js +4923 -3248
  17. package/dist/bitwrench-lean.cjs.min.js +35 -6
  18. package/dist/bitwrench-lean.es5.js +6325 -4580
  19. package/dist/bitwrench-lean.es5.min.js +32 -3
  20. package/dist/bitwrench-lean.esm.js +4923 -3248
  21. package/dist/bitwrench-lean.esm.min.js +35 -6
  22. package/dist/bitwrench-lean.umd.js +4923 -3248
  23. package/dist/bitwrench-lean.umd.min.js +35 -6
  24. package/dist/bitwrench.cjs.js +5082 -3667
  25. package/dist/bitwrench.cjs.min.js +38 -8
  26. package/dist/bitwrench.css +2289 -6034
  27. package/dist/bitwrench.es5.js +6862 -5346
  28. package/dist/bitwrench.es5.min.js +34 -5
  29. package/dist/bitwrench.esm.js +5082 -3667
  30. package/dist/bitwrench.esm.min.js +38 -8
  31. package/dist/bitwrench.min.css +1 -0
  32. package/dist/bitwrench.umd.js +5082 -3667
  33. package/dist/bitwrench.umd.min.js +38 -8
  34. package/dist/builds.json +184 -74
  35. package/dist/bwserve.cjs.js +646 -0
  36. package/dist/bwserve.esm.js +638 -0
  37. package/dist/sri.json +36 -26
  38. package/package.json +23 -6
  39. package/readme.html +71 -32
  40. package/src/bitwrench-bccl-entry.js +72 -0
  41. package/src/{bitwrench-components-v2.js → bitwrench-bccl.js} +396 -647
  42. package/src/bitwrench-code-edit.js +98 -48
  43. package/src/bitwrench-color-utils.js +24 -18
  44. package/src/bitwrench-components-stub.js +4 -1
  45. package/src/bitwrench-file-ops.js +180 -0
  46. package/src/bitwrench-lean.js +2 -2
  47. package/src/bitwrench-styles.js +1287 -4029
  48. package/src/bitwrench-utils.js +458 -0
  49. package/src/bitwrench.js +2070 -1292
  50. package/src/bwserve/client.js +182 -0
  51. package/src/bwserve/index.js +352 -0
  52. package/src/bwserve/shell.js +103 -0
  53. package/src/cli/index.js +36 -15
  54. package/src/cli/layout-default.js +18 -18
  55. package/src/cli/serve.js +325 -0
  56. package/src/generate-css.js +73 -53
  57. package/src/version.js +3 -3
  58. package/src/bitwrench-component-base.js +0 -736
  59. package/src/bitwrench-components-inline.js +0 -374
  60. package/src/bitwrench-components.js +0 -610
  61. /package/bin/{bitwrench.js → bwcli.js} +0 -0
@@ -12,11 +12,43 @@
12
12
  * Handle classes (CardHandle, TableHandle, NavbarHandle, TabsHandle)
13
13
  * provide imperative DOM manipulation for rendered components.
14
14
  *
15
- * @module bitwrench-components-v2
15
+ * @module bitwrench-bccl
16
16
  * @license BSD-2-Clause
17
17
  * @author M A Chatterjee <deftio [at] deftio [dot] com>
18
18
  */
19
19
 
20
+ // =========================================================================
21
+ // Variant → Utility Class Mapping
22
+ //
23
+ // Components compose these shared utility classes instead of owning
24
+ // their own variant selectors. The CSS is generated once by
25
+ // generatePaletteUtilities() + generateInteractionRules().
26
+ // =========================================================================
27
+
28
+ /**
29
+ * Maps component type to a function that returns utility classes for a variant.
30
+ * Each function takes a variant name (e.g. 'primary') and returns a class string.
31
+ * @type {Object.<string, function(string): string>}
32
+ */
33
+ /**
34
+ * Convert a variant name to a single palette class.
35
+ * All BCCL components use this: variant='primary' → class includes 'bw_primary'.
36
+ * The CSS palette class (.bw-primary) sets bg/color/border; component-specific
37
+ * overrides in generatePaletteClasses() adjust per component type.
38
+ *
39
+ * @param {string} v - Variant name (e.g. 'primary', 'danger', 'outline_primary')
40
+ * @returns {string} CSS class string
41
+ */
42
+ export function variantClass(v) {
43
+ if (!v) return '';
44
+ // Handle outline variants: 'outline_primary' or 'outline-primary'
45
+ if (v.indexOf('outline') === 0) {
46
+ var base = v.replace(/^outline[_-]/, '');
47
+ return 'bw_btn_outline bw_' + base;
48
+ }
49
+ return 'bw_' + v;
50
+ }
51
+
20
52
  /**
21
53
  * Create a card component with optional header, body, footer, and image support
22
54
  *
@@ -76,54 +108,54 @@ export function makeCard(props = {}) {
76
108
 
77
109
  const shadowClasses = {
78
110
  none: '',
79
- sm: 'bw-shadow-sm',
80
- md: 'bw-shadow',
81
- lg: 'bw-shadow-lg'
111
+ sm: 'bw_shadow_sm',
112
+ md: 'bw_shadow',
113
+ lg: 'bw_shadow_lg'
82
114
  };
83
115
 
84
116
  const cardClasses = [
85
- 'bw-card',
86
- variant ? `bw-card-${variant}` : '',
117
+ 'bw_card',
118
+ variantClass(variant),
87
119
  shadow ? (shadowClasses[shadow] || '') : '',
88
- !bordered ? 'bw-border-0' : '',
89
- hoverable ? 'bw-card-hoverable' : '',
120
+ !bordered ? 'bw_border_0' : '',
121
+ hoverable ? 'bw_card_hoverable' : '',
90
122
  className
91
123
  ].filter(Boolean).join(' ').trim();
92
124
 
93
125
  const cardContent = [
94
126
  header && {
95
127
  t: 'div',
96
- a: { class: `bw-card-header ${headerClass}`.trim() },
128
+ a: { class: `bw_card_header ${headerClass}`.trim() },
97
129
  c: header
98
130
  },
99
131
  image && (imagePosition === 'top' || imagePosition === 'left') && {
100
132
  t: 'img',
101
133
  a: {
102
- class: `bw-card-img-${imagePosition}`,
134
+ class: `bw_card_img_${imagePosition}`,
103
135
  src: image.src,
104
136
  alt: image.alt || ''
105
137
  }
106
138
  },
107
139
  {
108
140
  t: 'div',
109
- a: { class: `bw-card-body ${bodyClass}`.trim() },
141
+ a: { class: `bw_card_body ${bodyClass}`.trim() },
110
142
  c: [
111
- title && { t: 'h5', a: { class: 'bw-card-title' }, c: title },
112
- subtitle && { t: 'h6', a: { class: 'bw-card-subtitle bw-mb-2 bw-text-muted' }, c: subtitle },
143
+ title && { t: 'h5', a: { class: 'bw_card_title' }, c: title },
144
+ subtitle && { t: 'h6', a: { class: 'bw_card_subtitle bw_mb_2 bw_text_muted' }, c: subtitle },
113
145
  content && (Array.isArray(content) ? content : [content])
114
146
  ].flat().filter(Boolean)
115
147
  },
116
148
  image && (imagePosition === 'bottom' || imagePosition === 'right') && {
117
149
  t: 'img',
118
150
  a: {
119
- class: `bw-card-img-${imagePosition}`,
151
+ class: `bw_card_img_${imagePosition}`,
120
152
  src: image.src,
121
153
  alt: image.alt || ''
122
154
  }
123
155
  },
124
156
  footer && {
125
157
  t: 'div',
126
- a: { class: `bw-card-footer ${footerClass}`.trim() },
158
+ a: { class: `bw_card_footer ${footerClass}`.trim() },
127
159
  c: footer
128
160
  }
129
161
  ].filter(Boolean);
@@ -135,7 +167,7 @@ export function makeCard(props = {}) {
135
167
  a: { class: cardClasses, style },
136
168
  c: {
137
169
  t: 'div',
138
- a: { class: 'bw-row bw-g-0' },
170
+ a: { class: 'bw_row bw_g_0' },
139
171
  c: cardContent
140
172
  },
141
173
  o: {
@@ -197,9 +229,9 @@ export function makeButton(props = {}) {
197
229
  a: {
198
230
  type,
199
231
  class: [
200
- 'bw-btn',
201
- `bw-btn-${variant}`,
202
- size && `bw-btn-${size}`,
232
+ 'bw_btn',
233
+ variantClass(variant),
234
+ size && `bw_btn_${size}`,
203
235
  className
204
236
  ].filter(Boolean).join(' '),
205
237
  disabled,
@@ -233,7 +265,7 @@ export function makeContainer(props = {}) {
233
265
 
234
266
  return {
235
267
  t: 'div',
236
- a: { class: `bw-container${fluid ? '-fluid' : ''} ${className}`.trim() },
268
+ a: { class: `bw_container${fluid ? '-fluid' : ''} ${className}`.trim() },
237
269
  c: children
238
270
  };
239
271
  }
@@ -244,7 +276,7 @@ export function makeContainer(props = {}) {
244
276
  * @param {Object} [props] - Row configuration
245
277
  * @param {Array|Object|string} [props.children] - Child columns
246
278
  * @param {string} [props.className] - Additional CSS classes
247
- * @param {number} [props.gap] - Gap size (1-5) applied via bw-g-{gap} class
279
+ * @param {number} [props.gap] - Gap size (1-5) applied via bw_g_{gap} class
248
280
  * @returns {Object} TACO object representing a grid row
249
281
  * @category Component Builders
250
282
  * @example
@@ -259,7 +291,7 @@ export function makeRow(props = {}) {
259
291
  return {
260
292
  t: 'div',
261
293
  a: {
262
- class: `bw-row ${gap ? `bw-g-${gap}` : ''} ${className}`.trim()
294
+ class: `bw_row ${gap ? `bw_g_${gap}` : ''} ${className}`.trim()
263
295
  },
264
296
  c: children
265
297
  };
@@ -293,20 +325,20 @@ export function makeCol(props = {}) {
293
325
  // Responsive sizes
294
326
  Object.entries(size).forEach(([breakpoint, value]) => {
295
327
  if (breakpoint === 'xs') {
296
- classes.push(`bw-col-${value}`);
328
+ classes.push(`bw_col_${value}`);
297
329
  } else {
298
- classes.push(`bw-col-${breakpoint}-${value}`);
330
+ classes.push(`bw_col_${breakpoint}-${value}`);
299
331
  }
300
332
  });
301
333
  } else if (size) {
302
- classes.push(`bw-col-${size}`);
334
+ classes.push(`bw_col_${size}`);
303
335
  } else {
304
- classes.push('bw-col');
336
+ classes.push('bw_col');
305
337
  }
306
338
 
307
- if (offset) classes.push(`bw-offset-${offset}`);
308
- if (push) classes.push(`bw-push-${push}`);
309
- if (pull) classes.push(`bw-pull-${pull}`);
339
+ if (offset) classes.push(`bw_offset_${offset}`);
340
+ if (push) classes.push(`bw_push_${push}`);
341
+ if (pull) classes.push(`bw_pull_${pull}`);
310
342
 
311
343
  return {
312
344
  t: 'div',
@@ -349,16 +381,16 @@ export function makeNav(props = {}) {
349
381
  return {
350
382
  t: 'ul',
351
383
  a: {
352
- class: `bw-nav ${pills ? 'bw-nav-pills' : 'bw-nav-tabs'} ${vertical ? 'bw-nav-vertical' : ''} ${className}`.trim()
384
+ class: `bw_nav ${pills ? 'bw_nav_pills' : 'bw_nav_tabs'} ${vertical ? 'bw_nav_vertical' : ''} ${className}`.trim()
353
385
  },
354
386
  c: items.map(item => ({
355
387
  t: 'li',
356
- a: { class: 'bw-nav-item' },
388
+ a: { class: 'bw_nav_item' },
357
389
  c: {
358
390
  t: 'a',
359
391
  a: {
360
392
  href: item.href || '#',
361
- class: `bw-nav-link ${item.active ? 'active' : ''} ${item.disabled ? 'disabled' : ''}`.trim()
393
+ class: `bw_nav_link ${item.active ? 'active' : ''} ${item.disabled ? 'disabled' : ''}`.trim()
362
394
  },
363
395
  c: item.text
364
396
  }
@@ -402,25 +434,25 @@ export function makeNavbar(props = {}) {
402
434
  return {
403
435
  t: 'nav',
404
436
  a: {
405
- class: `bw-navbar ${dark ? 'bw-navbar-dark' : 'bw-navbar-light'} ${className}`.trim()
437
+ class: `bw_navbar ${dark ? 'bw_navbar_dark' : 'bw_navbar_light'} ${className}`.trim()
406
438
  },
407
439
  c: {
408
440
  t: 'div',
409
- a: { class: 'bw-container' },
441
+ a: { class: 'bw_container' },
410
442
  c: [
411
443
  brand && {
412
444
  t: 'a',
413
- a: { href: brandHref, class: 'bw-navbar-brand' },
445
+ a: { href: brandHref, class: 'bw_navbar_brand' },
414
446
  c: brand
415
447
  },
416
448
  items.length > 0 && {
417
449
  t: 'div',
418
- a: { class: 'bw-navbar-nav' },
450
+ a: { class: 'bw_navbar_nav' },
419
451
  c: items.map(item => ({
420
452
  t: 'a',
421
453
  a: {
422
454
  href: item.href || '#',
423
- class: `bw-nav-link ${item.active ? 'active' : ''}`
455
+ class: `bw_nav_link ${item.active ? 'active' : ''}`
424
456
  },
425
457
  c: item.text
426
458
  }))
@@ -471,27 +503,27 @@ export function makeTabs(props = {}) {
471
503
 
472
504
  return {
473
505
  t: 'div',
474
- a: { class: 'bw-tabs' },
506
+ a: { class: 'bw_tabs' },
475
507
  c: [
476
508
  {
477
509
  t: 'ul',
478
- a: { class: 'bw-nav bw-nav-tabs', role: 'tablist' },
510
+ a: { class: 'bw_nav bw_nav_tabs', role: 'tablist' },
479
511
  c: tabs.map((tab, index) => ({
480
512
  t: 'li',
481
- a: { class: 'bw-nav-item', role: 'presentation' },
513
+ a: { class: 'bw_nav_item', role: 'presentation' },
482
514
  c: {
483
515
  t: 'button',
484
516
  a: {
485
- class: `bw-nav-link ${index === actualActiveIndex ? 'active' : ''}`,
517
+ class: `bw_nav_link ${index === actualActiveIndex ? 'active' : ''}`,
486
518
  type: 'button',
487
519
  role: 'tab',
488
520
  tabindex: index === actualActiveIndex ? '0' : '-1',
489
521
  'aria-selected': index === actualActiveIndex ? 'true' : 'false',
490
522
  'data-tab-index': index,
491
523
  onclick: (e) => {
492
- const tabsContainer = e.target.closest('.bw-tabs');
493
- const allTabs = tabsContainer.querySelectorAll('.bw-nav-link');
494
- const allPanes = tabsContainer.querySelectorAll('.bw-tab-pane');
524
+ const tabsContainer = e.target.closest('.bw_tabs');
525
+ const allTabs = tabsContainer.querySelectorAll('.bw_nav_link');
526
+ const allPanes = tabsContainer.querySelectorAll('.bw_tab_pane');
495
527
 
496
528
  allTabs.forEach(t => {
497
529
  t.classList.remove('active');
@@ -513,11 +545,11 @@ export function makeTabs(props = {}) {
513
545
  },
514
546
  {
515
547
  t: 'div',
516
- a: { class: 'bw-tab-content' },
548
+ a: { class: 'bw_tab_content' },
517
549
  c: tabs.map((tab, index) => ({
518
550
  t: 'div',
519
551
  a: {
520
- class: `bw-tab-pane ${index === actualActiveIndex ? 'active' : ''}`,
552
+ class: `bw_tab_pane ${index === actualActiveIndex ? 'active' : ''}`,
521
553
  role: 'tabpanel'
522
554
  },
523
555
  c: tab.content
@@ -594,7 +626,7 @@ export function makeAlert(props = {}) {
594
626
  return {
595
627
  t: 'div',
596
628
  a: {
597
- class: `bw-alert bw-alert-${variant} ${dismissible ? 'bw-alert-dismissible' : ''} ${className}`.trim(),
629
+ class: `bw_alert ${variantClass(variant)} ${dismissible ? 'bw_alert_dismissible' : ''} ${className}`.trim(),
598
630
  role: 'alert'
599
631
  },
600
632
  c: [
@@ -603,10 +635,10 @@ export function makeAlert(props = {}) {
603
635
  t: 'button',
604
636
  a: {
605
637
  type: 'button',
606
- class: 'bw-close',
638
+ class: 'bw_close',
607
639
  'aria-label': 'Close',
608
640
  onclick: function(e) {
609
- var alert = e.target.closest('.bw-alert');
641
+ var alert = e.target.closest('.bw_alert');
610
642
  if (alert) { alert.remove(); }
611
643
  }
612
644
  },
@@ -643,12 +675,12 @@ export function makeBadge(props = {}) {
643
675
  className = ''
644
676
  } = props;
645
677
 
646
- const sizeClass = size === 'sm' ? ' bw-badge-sm' : size === 'lg' ? ' bw-badge-lg' : '';
678
+ const sizeClass = size === 'sm' ? ' bw_badge_sm' : size === 'lg' ? ' bw_badge_lg' : '';
647
679
 
648
680
  return {
649
681
  t: 'span',
650
682
  a: {
651
- class: `bw-badge bw-badge-${variant}${sizeClass} ${pill ? 'bw-badge-pill' : ''} ${className}`.trim()
683
+ class: `bw_badge ${variantClass(variant)}${sizeClass} ${pill ? 'bw_badge_pill' : ''} ${className}`.trim()
652
684
  },
653
685
  c: text
654
686
  };
@@ -691,17 +723,17 @@ export function makeProgress(props = {}) {
691
723
  return {
692
724
  t: 'div',
693
725
  a: {
694
- class: 'bw-progress',
726
+ class: 'bw_progress',
695
727
  style: height ? { height: `${height}px` } : undefined
696
728
  },
697
729
  c: {
698
730
  t: 'div',
699
731
  a: {
700
732
  class: [
701
- 'bw-progress-bar',
702
- `bw-progress-bar-${variant}`,
703
- striped && 'bw-progress-bar-striped',
704
- animated && 'bw-progress-bar-animated'
733
+ 'bw_progress_bar',
734
+ variantClass(variant),
735
+ striped && 'bw_progress_bar_striped',
736
+ animated && 'bw_progress_bar_animated'
705
737
  ].filter(Boolean).join(' '),
706
738
  role: 'progressbar',
707
739
  style: { width: `${percentage}%` },
@@ -747,7 +779,7 @@ export function makeListGroup(props = {}) {
747
779
 
748
780
  return {
749
781
  t: 'div',
750
- a: { class: `bw-list-group ${flush ? 'bw-list-group-flush' : ''}`.trim() },
782
+ a: { class: `bw_list_group ${flush ? 'bw_list_group_flush' : ''}`.trim() },
751
783
  c: items.map(item => {
752
784
  const isObject = typeof item === 'object';
753
785
  const text = isObject ? item.text : item;
@@ -762,7 +794,7 @@ export function makeListGroup(props = {}) {
762
794
  t: 'a',
763
795
  a: {
764
796
  class: [
765
- 'bw-list-group-item',
797
+ 'bw_list_group_item',
766
798
  active && 'active',
767
799
  disabled && 'disabled'
768
800
  ].filter(Boolean).join(' '),
@@ -781,7 +813,7 @@ export function makeListGroup(props = {}) {
781
813
  t: 'div',
782
814
  a: {
783
815
  class: [
784
- 'bw-list-group-item',
816
+ 'bw_list_group_item',
785
817
  active && 'active',
786
818
  disabled && 'disabled'
787
819
  ].filter(Boolean).join(' ')
@@ -822,11 +854,11 @@ export function makeBreadcrumb(props = {}) {
822
854
  a: { 'aria-label': 'breadcrumb' },
823
855
  c: {
824
856
  t: 'ol',
825
- a: { class: 'bw-breadcrumb' },
857
+ a: { class: 'bw_breadcrumb' },
826
858
  c: items.map((item, index) => ({
827
859
  t: 'li',
828
860
  a: {
829
- class: `bw-breadcrumb-item ${item.active ? 'active' : ''}`,
861
+ class: `bw_breadcrumb_item ${item.active ? 'active' : ''}`,
830
862
  'aria-current': item.active ? 'page' : undefined
831
863
  },
832
864
  c: item.active ? item.text : {
@@ -899,7 +931,7 @@ export function makeFormGroup(props = {}) {
899
931
  var styledInput = input;
900
932
  if (validation && input && input.a) {
901
933
  styledInput = { t: input.t, a: Object.assign({}, input.a), c: input.c, o: input.o };
902
- var validClass = validation === 'valid' ? 'bw-is-valid' : validation === 'invalid' ? 'bw-is-invalid' : '';
934
+ var validClass = validation === 'valid' ? 'bw_is_valid' : validation === 'invalid' ? 'bw_is_invalid' : '';
903
935
  if (validClass) {
904
936
  styledInput.a.class = ((styledInput.a.class || '') + ' ' + validClass).trim();
905
937
  }
@@ -907,22 +939,22 @@ export function makeFormGroup(props = {}) {
907
939
 
908
940
  return {
909
941
  t: 'div',
910
- a: { class: 'bw-form-group' },
942
+ a: { class: 'bw_form_group' },
911
943
  c: [
912
944
  label && {
913
945
  t: 'label',
914
- a: { for: id, class: 'bw-form-label' },
915
- c: required ? [label, { t: 'span', a: { class: 'bw-text-danger', style: 'margin-left: 0.25rem' }, c: '*' }] : label
946
+ a: { for: id, class: 'bw_form_label' },
947
+ c: required ? [label, { t: 'span', a: { class: 'bw_text_danger bw_ms_1' }, c: '*' }] : label
916
948
  },
917
949
  styledInput,
918
950
  feedback && validation && {
919
951
  t: 'div',
920
- a: { class: validation === 'valid' ? 'bw-valid-feedback' : 'bw-invalid-feedback' },
952
+ a: { class: validation === 'valid' ? 'bw_valid_feedback' : 'bw_invalid_feedback' },
921
953
  c: feedback
922
954
  },
923
955
  help && {
924
956
  t: 'small',
925
- a: { class: 'bw-form-text bw-text-muted' },
957
+ a: { class: 'bw_form_text bw_text_muted' },
926
958
  c: help
927
959
  }
928
960
  ].filter(Boolean)
@@ -975,7 +1007,7 @@ export function makeInput(props = {}) {
975
1007
  t: 'input',
976
1008
  a: {
977
1009
  type,
978
- class: `bw-form-control ${className}`.trim(),
1010
+ class: `bw_form_control ${className}`.trim(),
979
1011
  placeholder,
980
1012
  value,
981
1013
  id,
@@ -1028,7 +1060,7 @@ export function makeTextarea(props = {}) {
1028
1060
  return {
1029
1061
  t: 'textarea',
1030
1062
  a: {
1031
- class: `bw-form-control ${className}`.trim(),
1063
+ class: `bw_form_control ${className}`.trim(),
1032
1064
  placeholder,
1033
1065
  rows,
1034
1066
  id,
@@ -1082,7 +1114,7 @@ export function makeSelect(props = {}) {
1082
1114
  return {
1083
1115
  t: 'select',
1084
1116
  a: {
1085
- class: `bw-form-control ${className}`.trim(),
1117
+ class: `bw_form_control ${className}`.trim(),
1086
1118
  id,
1087
1119
  name,
1088
1120
  disabled,
@@ -1133,13 +1165,13 @@ export function makeCheckbox(props = {}) {
1133
1165
 
1134
1166
  return {
1135
1167
  t: 'div',
1136
- a: { class: `bw-form-check ${className}`.trim() },
1168
+ a: { class: `bw_form_check ${className}`.trim() },
1137
1169
  c: [
1138
1170
  {
1139
1171
  t: 'input',
1140
1172
  a: {
1141
1173
  type: 'checkbox',
1142
- class: 'bw-form-check-input',
1174
+ class: 'bw_form_check_input',
1143
1175
  checked,
1144
1176
  id,
1145
1177
  name,
@@ -1150,7 +1182,7 @@ export function makeCheckbox(props = {}) {
1150
1182
  },
1151
1183
  label && {
1152
1184
  t: 'label',
1153
- a: { class: 'bw-form-check-label', for: id },
1185
+ a: { class: 'bw_form_check_label', for: id },
1154
1186
  c: label
1155
1187
  }
1156
1188
  ].filter(Boolean)
@@ -1188,7 +1220,7 @@ export function makeStack(props = {}) {
1188
1220
  return {
1189
1221
  t: 'div',
1190
1222
  a: {
1191
- class: `bw-${direction === 'vertical' ? 'vstack' : 'hstack'} bw-gap-${gap} ${className}`.trim()
1223
+ class: `bw_${direction === 'vertical' ? 'vstack' : 'hstack'} bw_gap_${gap} ${className}`.trim()
1192
1224
  },
1193
1225
  c: children
1194
1226
  };
@@ -1216,12 +1248,12 @@ export function makeSpinner(props = {}) {
1216
1248
  return {
1217
1249
  t: 'div',
1218
1250
  a: {
1219
- class: `bw-spinner-${type} bw-spinner-${type}-${size} bw-text-${variant}`,
1251
+ class: `bw_spinner_${type} bw_spinner_${type}-${size} ${variantClass(variant)}`,
1220
1252
  role: 'status'
1221
1253
  },
1222
1254
  c: {
1223
1255
  t: 'span',
1224
- a: { class: 'bw-visually-hidden' },
1256
+ a: { class: 'bw_visually_hidden' },
1225
1257
  c: 'Loading...'
1226
1258
  }
1227
1259
  };
@@ -1272,44 +1304,44 @@ export function makeHero(props = {}) {
1272
1304
  } = props;
1273
1305
 
1274
1306
  const sizeClasses = {
1275
- sm: 'bw-py-3',
1276
- md: 'bw-py-4',
1277
- lg: 'bw-py-5',
1278
- xl: 'bw-py-6'
1307
+ sm: 'bw_py_3',
1308
+ md: 'bw_py_4',
1309
+ lg: 'bw_py_5',
1310
+ xl: 'bw_py_6'
1279
1311
  };
1280
1312
 
1281
1313
  return {
1282
1314
  t: 'section',
1283
1315
  a: {
1284
- class: `bw-hero bw-hero-${variant} ${sizeClasses[size] || sizeClasses.lg} ${centered ? 'bw-text-center' : ''} ${className}`.trim(),
1316
+ class: `bw_hero ${variantClass(variant)} ${sizeClasses[size] || sizeClasses.lg} ${centered ? 'bw_text_center' : ''} ${className}`.trim(),
1285
1317
  style: backgroundImage ? `background-image: url('${backgroundImage}'); background-size: cover; background-position: center;` : undefined
1286
1318
  },
1287
1319
  c: [
1288
1320
  overlay && {
1289
1321
  t: 'div',
1290
- a: { class: 'bw-hero-overlay' }
1322
+ a: { class: 'bw_hero_overlay' }
1291
1323
  },
1292
1324
  {
1293
1325
  t: 'div',
1294
- a: { class: 'bw-container' },
1326
+ a: { class: 'bw_container' },
1295
1327
  c: {
1296
1328
  t: 'div',
1297
- a: { class: 'bw-hero-content' },
1329
+ a: { class: 'bw_hero_content' },
1298
1330
  c: [
1299
1331
  title && {
1300
1332
  t: 'h1',
1301
- a: { class: 'bw-hero-title bw-display-4 bw-mb-3' },
1333
+ a: { class: 'bw_hero_title bw_display_4 bw_mb_3' },
1302
1334
  c: title
1303
1335
  },
1304
1336
  subtitle && {
1305
1337
  t: 'p',
1306
- a: { class: 'bw-hero-subtitle bw-lead bw-mb-4' },
1338
+ a: { class: 'bw_hero_subtitle bw_lead bw_mb_4' },
1307
1339
  c: subtitle
1308
1340
  },
1309
1341
  content,
1310
1342
  actions && {
1311
1343
  t: 'div',
1312
- a: { class: 'bw-hero-actions bw-mt-4' },
1344
+ a: { class: 'bw_hero_actions bw_mt_4' },
1313
1345
  c: actions
1314
1346
  }
1315
1347
  ].filter(Boolean)
@@ -1355,37 +1387,37 @@ export function makeFeatureGrid(props = {}) {
1355
1387
  className = ''
1356
1388
  } = props;
1357
1389
 
1358
- const colClass = `bw-col-md-${12/columns}`;
1390
+ const colClass = `bw_col_md_${12/columns}`;
1359
1391
 
1360
1392
  return {
1361
1393
  t: 'div',
1362
- a: { class: `bw-feature-grid ${className}`.trim() },
1394
+ a: { class: `bw_feature_grid ${className}`.trim() },
1363
1395
  c: {
1364
1396
  t: 'div',
1365
- a: { class: 'bw-row bw-g-4' },
1397
+ a: { class: 'bw_row bw_g_4' },
1366
1398
  c: features.map(feature => ({
1367
1399
  t: 'div',
1368
1400
  a: { class: colClass },
1369
1401
  c: {
1370
1402
  t: 'div',
1371
- a: { class: `bw-feature ${centered ? 'bw-text-center' : ''}` },
1403
+ a: { class: `bw_feature ${centered ? 'bw_text_center' : ''}` },
1372
1404
  c: [
1373
1405
  feature.icon && {
1374
1406
  t: 'div',
1375
1407
  a: {
1376
- class: 'bw-feature-icon bw-mb-3 bw-text-primary',
1408
+ class: 'bw_feature_icon bw_mb_3 bw_text_primary',
1377
1409
  style: `font-size: ${iconSize};`
1378
1410
  },
1379
1411
  c: feature.icon
1380
1412
  },
1381
1413
  feature.title && {
1382
1414
  t: 'h3',
1383
- a: { class: 'bw-feature-title bw-h5 bw-mb-2' },
1415
+ a: { class: 'bw_feature_title bw_h5 bw_mb_2' },
1384
1416
  c: feature.title
1385
1417
  },
1386
1418
  feature.description && {
1387
1419
  t: 'p',
1388
- a: { class: 'bw-feature-description bw-text-muted' },
1420
+ a: { class: 'bw_feature_description bw_text_muted' },
1389
1421
  c: feature.description
1390
1422
  }
1391
1423
  ].filter(Boolean)
@@ -1429,19 +1461,19 @@ export function makeCTA(props = {}) {
1429
1461
 
1430
1462
  return {
1431
1463
  t: 'section',
1432
- a: { class: `bw-cta bw-bg-${variant} bw-py-5 ${className}`.trim() },
1464
+ a: { class: `bw_cta bw_bg_${variant} bw_py_5 ${className}`.trim() },
1433
1465
  c: {
1434
1466
  t: 'div',
1435
- a: { class: 'bw-container' },
1467
+ a: { class: 'bw_container' },
1436
1468
  c: {
1437
1469
  t: 'div',
1438
- a: { class: `bw-cta-content ${centered ? 'bw-text-center' : ''}` },
1470
+ a: { class: `bw_cta_content ${centered ? 'bw_text_center' : ''}` },
1439
1471
  c: [
1440
- title && { t: 'h2', a: { class: 'bw-cta-title bw-mb-3' }, c: title },
1441
- description && { t: 'p', a: { class: 'bw-cta-description bw-lead bw-mb-4' }, c: description },
1472
+ title && { t: 'h2', a: { class: 'bw_cta_title bw_mb_3' }, c: title },
1473
+ description && { t: 'p', a: { class: 'bw_cta_description bw_lead bw_mb_4' }, c: description },
1442
1474
  actions && {
1443
1475
  t: 'div',
1444
- a: { class: 'bw-cta-actions' },
1476
+ a: { class: 'bw_cta_actions' },
1445
1477
  c: actions
1446
1478
  }
1447
1479
  ].filter(Boolean)
@@ -1481,27 +1513,27 @@ export function makeSection(props = {}) {
1481
1513
  } = props;
1482
1514
 
1483
1515
  const spacingClasses = {
1484
- sm: 'bw-py-3',
1485
- md: 'bw-py-4',
1486
- lg: 'bw-py-5',
1487
- xl: 'bw-py-6'
1516
+ sm: 'bw_py_3',
1517
+ md: 'bw_py_4',
1518
+ lg: 'bw_py_5',
1519
+ xl: 'bw_py_6'
1488
1520
  };
1489
1521
 
1490
1522
  return {
1491
1523
  t: 'section',
1492
1524
  a: {
1493
- class: `bw-section ${spacingClasses[spacing] || spacingClasses.md} ${variant !== 'default' ? `bw-bg-${variant}` : ''} ${className}`.trim()
1525
+ class: `bw_section ${spacingClasses[spacing] || spacingClasses.md} ${variant !== 'default' ? `bw_bg_${variant}` : ''} ${className}`.trim()
1494
1526
  },
1495
1527
  c: {
1496
1528
  t: 'div',
1497
- a: { class: 'bw-container' },
1529
+ a: { class: 'bw_container' },
1498
1530
  c: [
1499
1531
  (title || subtitle) && {
1500
1532
  t: 'div',
1501
- a: { class: 'bw-section-header bw-text-center bw-mb-5' },
1533
+ a: { class: 'bw_section_header bw_text_center bw_mb_5' },
1502
1534
  c: [
1503
- title && { t: 'h2', a: { class: 'bw-section-title' }, c: title },
1504
- subtitle && { t: 'p', a: { class: 'bw-section-subtitle bw-text-muted' }, c: subtitle }
1535
+ title && { t: 'h2', a: { class: 'bw_section_title' }, c: title },
1536
+ subtitle && { t: 'p', a: { class: 'bw_section_subtitle bw_text_muted' }, c: subtitle }
1505
1537
  ].filter(Boolean)
1506
1538
  },
1507
1539
  content
@@ -1518,317 +1550,9 @@ export function makeSection(props = {}) {
1518
1550
  // full re-renders. Used by bw.createCard(), bw.createTable(), etc.
1519
1551
  // =========================================================================
1520
1552
 
1521
- /**
1522
- * Imperative handle for a rendered card component
1523
- *
1524
- * Provides methods to update card title, content, and CSS classes
1525
- * without re-rendering the entire component. Created automatically
1526
- * when using bw.createCard().
1527
- *
1528
- * @category Component Handles
1529
- */
1530
- export class CardHandle {
1531
- /**
1532
- * @param {Element} element - The card's root DOM element
1533
- * @param {Object} taco - The original TACO object used to create the card
1534
- */
1535
- constructor(element, taco) {
1536
- this.element = element;
1537
- this._taco = taco;
1538
- this.state = taco.o?.state || {};
1539
-
1540
- // Cache child elements
1541
- this.children = {
1542
- header: element.querySelector('.bw-card-header'),
1543
- title: element.querySelector('.bw-card-title'),
1544
- body: element.querySelector('.bw-card-body'),
1545
- footer: element.querySelector('.bw-card-footer')
1546
- };
1547
- }
1548
-
1549
- /**
1550
- * Update the card title text
1551
- *
1552
- * @param {string} title - New title text
1553
- * @returns {CardHandle} this (for chaining)
1554
- */
1555
- setTitle(title) {
1556
- if (this.children.title) {
1557
- this.children.title.textContent = title;
1558
- }
1559
- return this;
1560
- }
1561
-
1562
- /**
1563
- * Replace the card body content
1564
- *
1565
- * @param {string|Object} content - New content (string or TACO object)
1566
- * @returns {CardHandle} this (for chaining)
1567
- */
1568
- setContent(content) {
1569
- if (this.children.body) {
1570
- if (typeof content === 'string') {
1571
- this.children.body.textContent = content;
1572
- } else {
1573
- // Re-render content
1574
- this.children.body.innerHTML = '';
1575
- const newContent = window.bw.taco.toDOM(content);
1576
- this.children.body.appendChild(newContent);
1577
- }
1578
- }
1579
- return this;
1580
- }
1581
-
1582
- /**
1583
- * Add a CSS class to the card root element
1584
- *
1585
- * @param {string} className - Class to add
1586
- * @returns {CardHandle} this (for chaining)
1587
- */
1588
- addClass(className) {
1589
- this.element.classList.add(className);
1590
- return this;
1591
- }
1592
-
1593
- /**
1594
- * Remove a CSS class from the card root element
1595
- *
1596
- * @param {string} className - Class to remove
1597
- * @returns {CardHandle} this (for chaining)
1598
- */
1599
- removeClass(className) {
1600
- this.element.classList.remove(className);
1601
- return this;
1602
- }
1603
-
1604
- /**
1605
- * Query a child element within the card
1606
- *
1607
- * @param {string} selector - CSS selector
1608
- * @returns {Element|null} Matching element or null
1609
- */
1610
- select(selector) {
1611
- return this.element.querySelector(selector);
1612
- }
1613
- }
1614
-
1615
- /**
1616
- * Imperative handle for a rendered table component
1617
- *
1618
- * Provides methods for data updates and column sorting. Caches
1619
- * thead/tbody/header references for efficient DOM updates.
1620
- * Created automatically when using bw.createTable().
1621
- *
1622
- * @category Component Handles
1623
- */
1624
- export class TableHandle {
1625
- /**
1626
- * @param {Element} element - The table's root DOM element
1627
- * @param {Object} taco - The original TACO object used to create the table
1628
- */
1629
- constructor(element, taco) {
1630
- this.element = element;
1631
- this._taco = taco;
1632
- this.state = taco.o?.state || {};
1633
- this._data = this.state.data || [];
1634
- this._sortColumn = null;
1635
- this._sortDirection = 'asc';
1636
-
1637
- // Cache elements
1638
- this.children = {
1639
- thead: element.querySelector('thead'),
1640
- tbody: element.querySelector('tbody'),
1641
- headers: element.querySelectorAll('th')
1642
- };
1643
-
1644
- // Set up sorting if enabled
1645
- if (this.state.sortable) {
1646
- this._setupSorting();
1647
- }
1648
- }
1649
-
1650
- /**
1651
- * Attach click-to-sort handlers on all column headers
1652
- * @private
1653
- */
1654
- _setupSorting() {
1655
- this.children.headers.forEach((th, index) => {
1656
- th.style.cursor = 'pointer';
1657
- th.onclick = () => this.sortBy(th.textContent);
1658
- });
1659
- }
1660
-
1661
- /**
1662
- * Replace the table data and re-render the body
1663
- *
1664
- * @param {Array<Object>} data - Array of row objects
1665
- * @returns {TableHandle} this (for chaining)
1666
- */
1667
- setData(data) {
1668
- this._data = data;
1669
- this._renderBody();
1670
- return this;
1671
- }
1672
-
1673
- /**
1674
- * Sort the table by a column name
1675
- *
1676
- * Toggles direction if the same column is sorted again.
1677
- *
1678
- * @param {string} column - Column header text to sort by
1679
- * @param {string} [direction] - Sort direction ("asc" or "desc"); toggles if omitted
1680
- * @returns {TableHandle} this (for chaining)
1681
- */
1682
- sortBy(column, direction) {
1683
- if (column === this._sortColumn && !direction) {
1684
- this._sortDirection = this._sortDirection === 'asc' ? 'desc' : 'asc';
1685
- } else {
1686
- this._sortColumn = column;
1687
- this._sortDirection = direction || 'asc';
1688
- }
1689
-
1690
- const columnKey = Object.keys(this._data[0])[
1691
- Array.from(this.children.headers).findIndex(th => th.textContent === column)
1692
- ];
1693
-
1694
- this._data.sort((a, b) => {
1695
- const aVal = a[columnKey];
1696
- const bVal = b[columnKey];
1697
- const result = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
1698
- return this._sortDirection === 'asc' ? result : -result;
1699
- });
1700
-
1701
- this._renderBody();
1702
- return this;
1703
- }
1704
-
1705
- /**
1706
- * Re-render the tbody from current _data
1707
- * @private
1708
- */
1709
- _renderBody() {
1710
- this.children.tbody.innerHTML = '';
1711
- this._data.forEach(row => {
1712
- const tr = document.createElement('tr');
1713
- Object.values(row).forEach(value => {
1714
- const td = document.createElement('td');
1715
- td.textContent = value;
1716
- tr.appendChild(td);
1717
- });
1718
- this.children.tbody.appendChild(tr);
1719
- });
1720
- }
1721
- }
1722
-
1723
- /**
1724
- * Imperative handle for a rendered navbar component
1725
- *
1726
- * Provides methods to update the active navigation link.
1727
- * Created automatically when using bw.createNavbar().
1728
- *
1729
- * @category Component Handles
1730
- */
1731
- export class NavbarHandle {
1732
- /**
1733
- * @param {Element} element - The navbar's root DOM element
1734
- * @param {Object} taco - The original TACO object used to create the navbar
1735
- */
1736
- constructor(element, taco) {
1737
- this.element = element;
1738
- this._taco = taco;
1739
- this.state = taco.o?.state || {};
1740
-
1741
- this.children = {
1742
- brand: element.querySelector('.bw-navbar-brand'),
1743
- links: element.querySelectorAll('.bw-nav-link')
1744
- };
1745
- }
1746
-
1747
- /**
1748
- * Set the active navigation link by href
1749
- *
1750
- * @param {string} href - The href value of the link to activate
1751
- * @returns {NavbarHandle} this (for chaining)
1752
- */
1753
- setActive(href) {
1754
- this.children.links.forEach(link => {
1755
- if (link.getAttribute('href') === href) {
1756
- link.classList.add('active');
1757
- } else {
1758
- link.classList.remove('active');
1759
- }
1760
- });
1761
- return this;
1762
- }
1763
- }
1764
-
1765
- /**
1766
- * Imperative handle for a rendered tabs component
1767
- *
1768
- * Provides programmatic tab switching. Sets up click handlers
1769
- * on tab buttons and manages active states on both buttons and panes.
1770
- * Created automatically when using bw.createTabs().
1771
- *
1772
- * @category Component Handles
1773
- */
1774
- export class TabsHandle {
1775
- /**
1776
- * @param {Element} element - The tabs container DOM element
1777
- * @param {Object} taco - The original TACO object used to create the tabs
1778
- */
1779
- constructor(element, taco) {
1780
- this.element = element;
1781
- this._taco = taco;
1782
- this.state = taco.o?.state || {};
1783
-
1784
- this.children = {
1785
- navItems: element.querySelectorAll('.bw-nav-link'),
1786
- tabPanes: element.querySelectorAll('.bw-tab-pane')
1787
- };
1788
-
1789
- this._setupTabs();
1790
- }
1791
-
1792
- /**
1793
- * Attach click handlers to tab navigation buttons
1794
- * @private
1795
- */
1796
- _setupTabs() {
1797
- this.children.navItems.forEach((navItem, index) => {
1798
- navItem.onclick = (e) => {
1799
- e.preventDefault();
1800
- this.switchTo(index);
1801
- };
1802
- });
1803
- }
1804
-
1805
- /**
1806
- * Programmatically switch to a tab by index
1807
- *
1808
- * @param {number} index - Zero-based tab index to activate
1809
- * @returns {TabsHandle} this (for chaining)
1810
- */
1811
- switchTo(index) {
1812
- this.children.navItems.forEach((item, i) => {
1813
- if (i === index) {
1814
- item.classList.add('active');
1815
- } else {
1816
- item.classList.remove('active');
1817
- }
1818
- });
1819
-
1820
- this.children.tabPanes.forEach((pane, i) => {
1821
- if (i === index) {
1822
- pane.classList.add('active');
1823
- } else {
1824
- pane.classList.remove('active');
1825
- }
1826
- });
1827
-
1828
- this.state.activeIndex = index;
1829
- return this;
1830
- }
1831
- }
1553
+ // Handle classes (CardHandle, TableHandle, NavbarHandle, TabsHandle)
1554
+ // removed in v2.0.15 superseded by ComponentHandle.
1555
+ // See dev/dead-code-elimination-v2.0.15.md for recovery.
1832
1556
 
1833
1557
  /**
1834
1558
  * Create a code demo component for documentation pages
@@ -1883,16 +1607,16 @@ export function makeCodeDemo(props = {}) {
1883
1607
  {
1884
1608
  t: 'button',
1885
1609
  a: {
1886
- class: 'bw-copy-btn bw-code-copy-btn',
1610
+ class: 'bw_copy_btn bw_code_copy_btn',
1887
1611
  onclick: function(e) {
1888
1612
  navigator.clipboard.writeText(code).then(function() {
1889
1613
  var btn = e.target;
1890
1614
  var originalText = btn.textContent;
1891
1615
  btn.textContent = 'Copied!';
1892
- btn.classList.add('bw-code-copy-btn-copied');
1616
+ btn.classList.add('bw_code_copy_btn_copied');
1893
1617
  setTimeout(function() {
1894
1618
  btn.textContent = originalText;
1895
- btn.classList.remove('bw-code-copy-btn-copied');
1619
+ btn.classList.remove('bw_code_copy_btn_copied');
1896
1620
  }, 2000);
1897
1621
  });
1898
1622
  }
@@ -1903,10 +1627,10 @@ export function makeCodeDemo(props = {}) {
1903
1627
  ? globalThis.bw.codeEditor({ code: code, lang: language === 'javascript' ? 'js' : language, readOnly: true, height: 'auto' })
1904
1628
  : {
1905
1629
  t: 'pre',
1906
- a: { class: 'bw-code-pre' },
1630
+ a: { class: 'bw_code_pre' },
1907
1631
  c: {
1908
1632
  t: 'code',
1909
- a: { class: `bw-code-block language-${language}` },
1633
+ a: { class: `bw_code_block language-${language}` },
1910
1634
  c: code
1911
1635
  }
1912
1636
  }
@@ -1919,7 +1643,7 @@ export function makeCodeDemo(props = {}) {
1919
1643
  title && { t: 'h3', c: title },
1920
1644
  description && {
1921
1645
  t: 'p',
1922
- a: { class: 'bw-text-muted', style: 'margin-bottom: 1rem;' },
1646
+ a: { class: 'bw_text_muted bw_mb_3' },
1923
1647
  c: description
1924
1648
  },
1925
1649
  makeTabs({ tabs, id: demoId })
@@ -1927,7 +1651,7 @@ export function makeCodeDemo(props = {}) {
1927
1651
 
1928
1652
  return {
1929
1653
  t: 'div',
1930
- a: { class: 'bw-code-demo' },
1654
+ a: { class: 'bw_code_demo' },
1931
1655
  c: content
1932
1656
  };
1933
1657
  }
@@ -1984,10 +1708,10 @@ export function makePagination(props = {}) {
1984
1708
  // Previous arrow
1985
1709
  items.push({
1986
1710
  t: 'li',
1987
- a: { class: `bw-page-item ${currentPage <= 1 ? 'bw-disabled' : ''}`.trim() },
1711
+ a: { class: `bw_page_item ${currentPage <= 1 ? 'bw_disabled' : ''}`.trim() },
1988
1712
  c: {
1989
1713
  t: 'a',
1990
- a: { class: 'bw-page-link', href: '#', onclick: handleClick(currentPage - 1), 'aria-label': 'Previous' },
1714
+ a: { class: 'bw_page_link', href: '#', onclick: handleClick(currentPage - 1), 'aria-label': 'Previous' },
1991
1715
  c: '\u2039'
1992
1716
  }
1993
1717
  });
@@ -1997,10 +1721,10 @@ export function makePagination(props = {}) {
1997
1721
  (function(pageNum) {
1998
1722
  items.push({
1999
1723
  t: 'li',
2000
- a: { class: `bw-page-item ${pageNum === currentPage ? 'bw-active' : ''}`.trim() },
1724
+ a: { class: `bw_page_item ${pageNum === currentPage ? 'bw_active' : ''}`.trim() },
2001
1725
  c: {
2002
1726
  t: 'a',
2003
- a: { class: 'bw-page-link', href: '#', onclick: handleClick(pageNum) },
1727
+ a: { class: 'bw_page_link', href: '#', onclick: handleClick(pageNum) },
2004
1728
  c: '' + pageNum
2005
1729
  }
2006
1730
  });
@@ -2010,10 +1734,10 @@ export function makePagination(props = {}) {
2010
1734
  // Next arrow
2011
1735
  items.push({
2012
1736
  t: 'li',
2013
- a: { class: `bw-page-item ${currentPage >= pages ? 'bw-disabled' : ''}`.trim() },
1737
+ a: { class: `bw_page_item ${currentPage >= pages ? 'bw_disabled' : ''}`.trim() },
2014
1738
  c: {
2015
1739
  t: 'a',
2016
- a: { class: 'bw-page-link', href: '#', onclick: handleClick(currentPage + 1), 'aria-label': 'Next' },
1740
+ a: { class: 'bw_page_link', href: '#', onclick: handleClick(currentPage + 1), 'aria-label': 'Next' },
2017
1741
  c: '\u203A'
2018
1742
  }
2019
1743
  });
@@ -2024,7 +1748,7 @@ export function makePagination(props = {}) {
2024
1748
  c: {
2025
1749
  t: 'ul',
2026
1750
  a: {
2027
- class: `bw-pagination ${size ? 'bw-pagination-' + size : ''} ${className}`.trim()
1751
+ class: `bw_pagination ${size ? 'bw_pagination_' + size : ''} ${className}`.trim()
2028
1752
  },
2029
1753
  c: items
2030
1754
  }
@@ -2066,13 +1790,13 @@ export function makeRadio(props = {}) {
2066
1790
 
2067
1791
  return {
2068
1792
  t: 'div',
2069
- a: { class: `bw-form-check ${className}`.trim() },
1793
+ a: { class: `bw_form_check ${className}`.trim() },
2070
1794
  c: [
2071
1795
  {
2072
1796
  t: 'input',
2073
1797
  a: {
2074
1798
  type: 'radio',
2075
- class: 'bw-form-check-input',
1799
+ class: 'bw_form_check_input',
2076
1800
  name,
2077
1801
  value,
2078
1802
  checked,
@@ -2083,7 +1807,7 @@ export function makeRadio(props = {}) {
2083
1807
  },
2084
1808
  label && {
2085
1809
  t: 'label',
2086
- a: { class: 'bw-form-check-label', for: id },
1810
+ a: { class: 'bw_form_check_label', for: id },
2087
1811
  c: label
2088
1812
  }
2089
1813
  ].filter(Boolean)
@@ -2120,7 +1844,7 @@ export function makeButtonGroup(props = {}) {
2120
1844
  return {
2121
1845
  t: 'div',
2122
1846
  a: {
2123
- class: `${vertical ? 'bw-btn-group-vertical' : 'bw-btn-group'} ${size ? 'bw-btn-group-' + size : ''} ${className}`.trim(),
1847
+ class: `${vertical ? 'bw_btn_group_vertical' : 'bw_btn_group'} ${size ? 'bw_btn_group_' + size : ''} ${className}`.trim(),
2124
1848
  role: 'group'
2125
1849
  },
2126
1850
  c: children
@@ -2160,42 +1884,42 @@ export function makeAccordion(props = {}) {
2160
1884
 
2161
1885
  return {
2162
1886
  t: 'div',
2163
- a: { class: `bw-accordion ${className}`.trim() },
1887
+ a: { class: `bw_accordion ${className}`.trim() },
2164
1888
  c: items.map(function(item, index) {
2165
1889
  return {
2166
1890
  t: 'div',
2167
- a: { class: 'bw-accordion-item' },
1891
+ a: { class: 'bw_accordion_item' },
2168
1892
  c: [
2169
1893
  {
2170
1894
  t: 'h2',
2171
- a: { class: 'bw-accordion-header' },
1895
+ a: { class: 'bw_accordion_header' },
2172
1896
  c: {
2173
1897
  t: 'button',
2174
1898
  a: {
2175
- class: `bw-accordion-button ${item.open ? '' : 'bw-collapsed'}`.trim(),
1899
+ class: `bw_accordion_button ${item.open ? '' : 'bw_collapsed'}`.trim(),
2176
1900
  type: 'button',
2177
1901
  'aria-expanded': item.open ? 'true' : 'false',
2178
1902
  'data-accordion-index': index,
2179
1903
  onclick: function(e) {
2180
- var btn = e.target.closest('.bw-accordion-button');
2181
- var accordionEl = btn.closest('.bw-accordion');
2182
- var accordionItem = btn.closest('.bw-accordion-item');
2183
- var collapse = accordionItem.querySelector('.bw-accordion-collapse');
2184
- var isOpen = collapse.classList.contains('bw-collapse-show');
1904
+ var btn = e.target.closest('.bw_accordion_button');
1905
+ var accordionEl = btn.closest('.bw_accordion');
1906
+ var accordionItem = btn.closest('.bw_accordion_item');
1907
+ var collapse = accordionItem.querySelector('.bw_accordion_collapse');
1908
+ var isOpen = collapse.classList.contains('bw_collapse_show');
2185
1909
 
2186
1910
  if (!multiOpen) {
2187
1911
  // Animate-close all other open siblings
2188
- var allItems = accordionEl.querySelectorAll('.bw-accordion-item');
1912
+ var allItems = accordionEl.querySelectorAll('.bw_accordion_item');
2189
1913
  for (var j = 0; j < allItems.length; j++) {
2190
1914
  if (allItems[j] === accordionItem) continue;
2191
- var sibCollapse = allItems[j].querySelector('.bw-accordion-collapse');
2192
- var sibBtn = allItems[j].querySelector('.bw-accordion-button');
2193
- if (sibCollapse.classList.contains('bw-collapse-show')) {
1915
+ var sibCollapse = allItems[j].querySelector('.bw_accordion_collapse');
1916
+ var sibBtn = allItems[j].querySelector('.bw_accordion_button');
1917
+ if (sibCollapse.classList.contains('bw_collapse_show')) {
2194
1918
  sibCollapse.style.maxHeight = sibCollapse.scrollHeight + 'px';
2195
1919
  sibCollapse.offsetHeight; // force reflow
2196
1920
  sibCollapse.style.maxHeight = '0px';
2197
- sibCollapse.classList.remove('bw-collapse-show');
2198
- sibBtn.classList.add('bw-collapsed');
1921
+ sibCollapse.classList.remove('bw_collapse_show');
1922
+ sibBtn.classList.add('bw_collapsed');
2199
1923
  sibBtn.setAttribute('aria-expanded', 'false');
2200
1924
  }
2201
1925
  }
@@ -2206,20 +1930,20 @@ export function makeAccordion(props = {}) {
2206
1930
  collapse.style.maxHeight = collapse.scrollHeight + 'px';
2207
1931
  collapse.offsetHeight; // force reflow
2208
1932
  collapse.style.maxHeight = '0px';
2209
- collapse.classList.remove('bw-collapse-show');
2210
- btn.classList.add('bw-collapsed');
1933
+ collapse.classList.remove('bw_collapse_show');
1934
+ btn.classList.add('bw_collapsed');
2211
1935
  btn.setAttribute('aria-expanded', 'false');
2212
1936
  } else {
2213
1937
  // Animate open
2214
- collapse.classList.add('bw-collapse-show');
1938
+ collapse.classList.add('bw_collapse_show');
2215
1939
  collapse.style.maxHeight = '0px';
2216
1940
  collapse.offsetHeight; // force reflow
2217
1941
  collapse.style.maxHeight = collapse.scrollHeight + 'px';
2218
- btn.classList.remove('bw-collapsed');
1942
+ btn.classList.remove('bw_collapsed');
2219
1943
  btn.setAttribute('aria-expanded', 'true');
2220
1944
  // After transition, allow dynamic content sizing
2221
1945
  var onEnd = function(ev) {
2222
- if (ev.propertyName === 'max-height' && collapse.classList.contains('bw-collapse-show')) {
1946
+ if (ev.propertyName === 'max-height' && collapse.classList.contains('bw_collapse_show')) {
2223
1947
  collapse.style.maxHeight = 'none';
2224
1948
  }
2225
1949
  collapse.removeEventListener('transitionend', onEnd);
@@ -2233,10 +1957,10 @@ export function makeAccordion(props = {}) {
2233
1957
  },
2234
1958
  {
2235
1959
  t: 'div',
2236
- a: { class: `bw-accordion-collapse ${item.open ? 'bw-collapse-show' : ''}`.trim() },
1960
+ a: { class: `bw_accordion_collapse ${item.open ? 'bw_collapse_show' : ''}`.trim() },
2237
1961
  c: {
2238
1962
  t: 'div',
2239
- a: { class: 'bw-accordion-body' },
1963
+ a: { class: 'bw_accordion_body' },
2240
1964
  c: item.content
2241
1965
  },
2242
1966
  o: item.open ? {
@@ -2255,60 +1979,8 @@ export function makeAccordion(props = {}) {
2255
1979
  };
2256
1980
  }
2257
1981
 
2258
- /**
2259
- * Imperative handle for a rendered modal component
2260
- *
2261
- * Provides `.show()`, `.hide()`, `.toggle()`, and `.destroy()` methods
2262
- * for controlling the modal programmatically.
2263
- *
2264
- * @category Component Handles
2265
- */
2266
- export class ModalHandle {
2267
- /**
2268
- * @param {Element} element - The modal backdrop DOM element
2269
- * @param {Object} taco - The original TACO object
2270
- */
2271
- constructor(element, taco) {
2272
- this.element = element;
2273
- this._taco = taco;
2274
- this._escHandler = null;
2275
- }
2276
-
2277
- /** Show the modal */
2278
- show() {
2279
- this.element.classList.add('bw-modal-show');
2280
- document.body.style.overflow = 'hidden';
2281
- return this;
2282
- }
2283
-
2284
- /** Hide the modal */
2285
- hide() {
2286
- this.element.classList.remove('bw-modal-show');
2287
- document.body.style.overflow = '';
2288
- return this;
2289
- }
2290
-
2291
- /** Toggle modal visibility */
2292
- toggle() {
2293
- if (this.element.classList.contains('bw-modal-show')) {
2294
- this.hide();
2295
- } else {
2296
- this.show();
2297
- }
2298
- return this;
2299
- }
2300
-
2301
- /** Remove the modal from DOM and clean up */
2302
- destroy() {
2303
- this.hide();
2304
- if (this._escHandler) {
2305
- document.removeEventListener('keydown', this._escHandler);
2306
- }
2307
- if (this.element.parentNode) {
2308
- this.element.parentNode.removeChild(this.element);
2309
- }
2310
- }
2311
- }
1982
+ // ModalHandle removed in v2.0.15 — superseded by ComponentHandle.
1983
+ // See dev/dead-code-elimination-v2.0.15.md for recovery.
2312
1984
 
2313
1985
  /**
2314
1986
  * Create a modal dialog overlay
@@ -2342,9 +2014,9 @@ export function makeModal(props = {}) {
2342
2014
  } = props;
2343
2015
 
2344
2016
  function closeModal(el) {
2345
- var backdrop = el.closest('.bw-modal');
2017
+ var backdrop = el.closest('.bw_modal');
2346
2018
  if (backdrop) {
2347
- backdrop.classList.remove('bw-modal-show');
2019
+ backdrop.classList.remove('bw_modal_show');
2348
2020
  document.body.style.overflow = '';
2349
2021
  }
2350
2022
  if (onClose) onClose();
@@ -2352,24 +2024,24 @@ export function makeModal(props = {}) {
2352
2024
 
2353
2025
  return {
2354
2026
  t: 'div',
2355
- a: { class: `bw-modal ${className}`.trim() },
2027
+ a: { class: `bw_modal ${className}`.trim() },
2356
2028
  c: {
2357
2029
  t: 'div',
2358
- a: { class: `bw-modal-dialog ${size ? 'bw-modal-' + size : ''}`.trim() },
2030
+ a: { class: `bw_modal_dialog ${size ? 'bw_modal_' + size : ''}`.trim() },
2359
2031
  c: {
2360
2032
  t: 'div',
2361
- a: { class: 'bw-modal-content' },
2033
+ a: { class: 'bw_modal_content' },
2362
2034
  c: [
2363
2035
  (title || closeButton) && {
2364
2036
  t: 'div',
2365
- a: { class: 'bw-modal-header' },
2037
+ a: { class: 'bw_modal_header' },
2366
2038
  c: [
2367
- title && { t: 'h5', a: { class: 'bw-modal-title' }, c: title },
2039
+ title && { t: 'h5', a: { class: 'bw_modal_title' }, c: title },
2368
2040
  closeButton && {
2369
2041
  t: 'button',
2370
2042
  a: {
2371
2043
  type: 'button',
2372
- class: 'bw-close',
2044
+ class: 'bw_close',
2373
2045
  'aria-label': 'Close',
2374
2046
  onclick: function(e) { closeModal(e.target); }
2375
2047
  },
@@ -2379,12 +2051,12 @@ export function makeModal(props = {}) {
2379
2051
  },
2380
2052
  content && {
2381
2053
  t: 'div',
2382
- a: { class: 'bw-modal-body' },
2054
+ a: { class: 'bw_modal_body' },
2383
2055
  c: content
2384
2056
  },
2385
2057
  footer && {
2386
2058
  t: 'div',
2387
- a: { class: 'bw-modal-footer' },
2059
+ a: { class: 'bw_modal_footer' },
2388
2060
  c: footer
2389
2061
  }
2390
2062
  ].filter(Boolean)
@@ -2399,7 +2071,7 @@ export function makeModal(props = {}) {
2399
2071
  });
2400
2072
  // Escape key to close
2401
2073
  var escHandler = function(e) {
2402
- if (e.key === 'Escape' && el.classList.contains('bw-modal-show')) {
2074
+ if (e.key === 'Escape' && el.classList.contains('bw_modal_show')) {
2403
2075
  closeModal(el);
2404
2076
  }
2405
2077
  };
@@ -2450,26 +2122,26 @@ export function makeToast(props = {}) {
2450
2122
  return {
2451
2123
  t: 'div',
2452
2124
  a: {
2453
- class: `bw-toast bw-toast-${variant} ${className}`.trim(),
2125
+ class: `bw_toast ${variantClass(variant)} ${className}`.trim(),
2454
2126
  role: 'alert',
2455
2127
  'data-position': position
2456
2128
  },
2457
2129
  c: [
2458
2130
  (title) && {
2459
2131
  t: 'div',
2460
- a: { class: 'bw-toast-header' },
2132
+ a: { class: 'bw_toast_header' },
2461
2133
  c: [
2462
2134
  { t: 'strong', c: title },
2463
2135
  {
2464
2136
  t: 'button',
2465
2137
  a: {
2466
2138
  type: 'button',
2467
- class: 'bw-close',
2139
+ class: 'bw_close',
2468
2140
  'aria-label': 'Close',
2469
2141
  onclick: function(e) {
2470
- var toast = e.target.closest('.bw-toast');
2142
+ var toast = e.target.closest('.bw_toast');
2471
2143
  if (toast) {
2472
- toast.classList.add('bw-toast-hiding');
2144
+ toast.classList.add('bw_toast_hiding');
2473
2145
  setTimeout(function() { if (toast.parentNode) toast.parentNode.removeChild(toast); }, 300);
2474
2146
  }
2475
2147
  }
@@ -2480,7 +2152,7 @@ export function makeToast(props = {}) {
2480
2152
  },
2481
2153
  content && {
2482
2154
  t: 'div',
2483
- a: { class: 'bw-toast-body' },
2155
+ a: { class: 'bw_toast_body' },
2484
2156
  c: content
2485
2157
  }
2486
2158
  ].filter(Boolean),
@@ -2489,12 +2161,12 @@ export function makeToast(props = {}) {
2489
2161
  mounted: function(el) {
2490
2162
  // Trigger show animation
2491
2163
  requestAnimationFrame(function() {
2492
- el.classList.add('bw-toast-show');
2164
+ el.classList.add('bw_toast_show');
2493
2165
  });
2494
2166
  // Auto-dismiss
2495
2167
  if (autoDismiss) {
2496
2168
  setTimeout(function() {
2497
- el.classList.add('bw-toast-hiding');
2169
+ el.classList.add('bw_toast_hiding');
2498
2170
  setTimeout(function() { if (el.parentNode) el.parentNode.removeChild(el); }, 300);
2499
2171
  }, delay);
2500
2172
  }
@@ -2547,12 +2219,12 @@ export function makeDropdown(props = {}) {
2547
2219
  triggerTaco = {
2548
2220
  t: 'button',
2549
2221
  a: {
2550
- class: `bw-btn bw-btn-${variant} bw-dropdown-toggle`,
2222
+ class: `bw_btn ${variantClass(variant)} bw_dropdown_toggle`,
2551
2223
  type: 'button',
2552
2224
  onclick: function(e) {
2553
- var dropdown = e.target.closest('.bw-dropdown');
2554
- var menu = dropdown.querySelector('.bw-dropdown-menu');
2555
- menu.classList.toggle('bw-dropdown-show');
2225
+ var dropdown = e.target.closest('.bw_dropdown');
2226
+ var menu = dropdown.querySelector('.bw_dropdown_menu');
2227
+ menu.classList.toggle('bw_dropdown_show');
2556
2228
  }
2557
2229
  },
2558
2230
  c: trigger || 'Dropdown'
@@ -2563,26 +2235,26 @@ export function makeDropdown(props = {}) {
2563
2235
 
2564
2236
  return {
2565
2237
  t: 'div',
2566
- a: { class: `bw-dropdown ${className}`.trim() },
2238
+ a: { class: `bw_dropdown ${className}`.trim() },
2567
2239
  c: [
2568
2240
  triggerTaco,
2569
2241
  {
2570
2242
  t: 'div',
2571
- a: { class: `bw-dropdown-menu ${align === 'end' ? 'bw-dropdown-menu-end' : ''}`.trim() },
2243
+ a: { class: `bw_dropdown_menu ${align === 'end' ? 'bw_dropdown_menu_end' : ''}`.trim() },
2572
2244
  c: items.map(function(item) {
2573
2245
  if (item.divider) {
2574
- return { t: 'hr', a: { class: 'bw-dropdown-divider' } };
2246
+ return { t: 'hr', a: { class: 'bw_dropdown_divider' } };
2575
2247
  }
2576
2248
  return {
2577
2249
  t: 'a',
2578
2250
  a: {
2579
- class: `bw-dropdown-item ${item.disabled ? 'disabled' : ''}`.trim(),
2251
+ class: `bw_dropdown_item ${item.disabled ? 'disabled' : ''}`.trim(),
2580
2252
  href: item.href || '#',
2581
2253
  onclick: item.disabled ? undefined : function(e) {
2582
2254
  if (!item.href) e.preventDefault();
2583
- var dropdown = e.target.closest('.bw-dropdown');
2584
- var menu = dropdown.querySelector('.bw-dropdown-menu');
2585
- menu.classList.remove('bw-dropdown-show');
2255
+ var dropdown = e.target.closest('.bw_dropdown');
2256
+ var menu = dropdown.querySelector('.bw_dropdown_menu');
2257
+ menu.classList.remove('bw_dropdown_show');
2586
2258
  if (item.onclick) item.onclick(e);
2587
2259
  }
2588
2260
  },
@@ -2597,8 +2269,8 @@ export function makeDropdown(props = {}) {
2597
2269
  // Click outside to close
2598
2270
  var outsideHandler = function(e) {
2599
2271
  if (!el.contains(e.target)) {
2600
- var menu = el.querySelector('.bw-dropdown-menu');
2601
- if (menu) menu.classList.remove('bw-dropdown-show');
2272
+ var menu = el.querySelector('.bw_dropdown_menu');
2273
+ if (menu) menu.classList.remove('bw_dropdown_show');
2602
2274
  }
2603
2275
  };
2604
2276
  document.addEventListener('click', outsideHandler);
@@ -2645,13 +2317,13 @@ export function makeSwitch(props = {}) {
2645
2317
 
2646
2318
  return {
2647
2319
  t: 'div',
2648
- a: { class: `bw-form-check bw-form-switch ${className}`.trim() },
2320
+ a: { class: `bw_form_check bw_form_switch ${className}`.trim() },
2649
2321
  c: [
2650
2322
  {
2651
2323
  t: 'input',
2652
2324
  a: {
2653
2325
  type: 'checkbox',
2654
- class: 'bw-form-check-input bw-switch-input',
2326
+ class: 'bw_form_check_input bw_switch_input',
2655
2327
  role: 'switch',
2656
2328
  checked,
2657
2329
  id,
@@ -2662,7 +2334,7 @@ export function makeSwitch(props = {}) {
2662
2334
  },
2663
2335
  label && {
2664
2336
  t: 'label',
2665
- a: { class: 'bw-form-check-label', for: id },
2337
+ a: { class: 'bw_form_check_label', for: id },
2666
2338
  c: label
2667
2339
  }
2668
2340
  ].filter(Boolean)
@@ -2697,7 +2369,7 @@ export function makeSkeleton(props = {}) {
2697
2369
  return {
2698
2370
  t: 'div',
2699
2371
  a: {
2700
- class: `bw-skeleton bw-skeleton-circle ${className}`.trim(),
2372
+ class: `bw_skeleton bw_skeleton_circle ${className}`.trim(),
2701
2373
  style: { width: circleSize, height: circleSize }
2702
2374
  }
2703
2375
  };
@@ -2707,7 +2379,7 @@ export function makeSkeleton(props = {}) {
2707
2379
  return {
2708
2380
  t: 'div',
2709
2381
  a: {
2710
- class: `bw-skeleton bw-skeleton-rect ${className}`.trim(),
2382
+ class: `bw_skeleton bw_skeleton_rect ${className}`.trim(),
2711
2383
  style: {
2712
2384
  width: width || '100%',
2713
2385
  height: height || '120px'
@@ -2721,7 +2393,7 @@ export function makeSkeleton(props = {}) {
2721
2393
  return {
2722
2394
  t: 'div',
2723
2395
  a: {
2724
- class: `bw-skeleton bw-skeleton-text ${className}`.trim(),
2396
+ class: `bw_skeleton bw_skeleton_text ${className}`.trim(),
2725
2397
  style: {
2726
2398
  width: width || '100%',
2727
2399
  height: height || '1em'
@@ -2735,7 +2407,7 @@ export function makeSkeleton(props = {}) {
2735
2407
  lines.push({
2736
2408
  t: 'div',
2737
2409
  a: {
2738
- class: 'bw-skeleton bw-skeleton-text',
2410
+ class: 'bw_skeleton bw_skeleton_text',
2739
2411
  style: {
2740
2412
  width: i === count - 1 ? '75%' : (width || '100%'),
2741
2413
  height: height || '1em'
@@ -2746,7 +2418,7 @@ export function makeSkeleton(props = {}) {
2746
2418
 
2747
2419
  return {
2748
2420
  t: 'div',
2749
- a: { class: `bw-skeleton-group ${className}`.trim() },
2421
+ a: { class: `bw_skeleton_group ${className}`.trim() },
2750
2422
  c: lines
2751
2423
  };
2752
2424
  }
@@ -2781,7 +2453,7 @@ export function makeAvatar(props = {}) {
2781
2453
  return {
2782
2454
  t: 'img',
2783
2455
  a: {
2784
- class: `bw-avatar bw-avatar-${size} ${className}`.trim(),
2456
+ class: `bw_avatar bw_avatar_${size} ${className}`.trim(),
2785
2457
  src: src,
2786
2458
  alt: alt
2787
2459
  }
@@ -2791,7 +2463,7 @@ export function makeAvatar(props = {}) {
2791
2463
  return {
2792
2464
  t: 'div',
2793
2465
  a: {
2794
- class: `bw-avatar bw-avatar-${size} bw-avatar-${variant} ${className}`.trim()
2466
+ class: `bw_avatar bw_avatar_${size} ${variantClass(variant)} ${className}`.trim()
2795
2467
  },
2796
2468
  c: initials || ''
2797
2469
  };
@@ -2840,14 +2512,14 @@ export function makeCarousel(props = {}) {
2840
2512
 
2841
2513
  // Shared navigation logic
2842
2514
  function goToSlide(carouselEl, index) {
2843
- var total = carouselEl.querySelectorAll('.bw-carousel-slide').length;
2515
+ var total = carouselEl.querySelectorAll('.bw_carousel_slide').length;
2844
2516
  if (index < 0) index = total - 1;
2845
2517
  if (index >= total) index = 0;
2846
2518
  carouselEl.setAttribute('data-carousel-index', index);
2847
- var track = carouselEl.querySelector('.bw-carousel-track');
2519
+ var track = carouselEl.querySelector('.bw_carousel_track');
2848
2520
  track.style.transform = 'translateX(-' + (index * 100) + '%)';
2849
2521
  // Update indicators
2850
- var indicators = carouselEl.querySelectorAll('.bw-carousel-indicator');
2522
+ var indicators = carouselEl.querySelectorAll('.bw_carousel_indicator');
2851
2523
  for (var i = 0; i < indicators.length; i++) {
2852
2524
  if (i === index) {
2853
2525
  indicators[i].classList.add('active');
@@ -2866,14 +2538,14 @@ export function makeCarousel(props = {}) {
2866
2538
  item.content,
2867
2539
  item.caption && {
2868
2540
  t: 'div',
2869
- a: { class: 'bw-carousel-caption' },
2541
+ a: { class: 'bw_carousel_caption' },
2870
2542
  c: item.caption
2871
2543
  }
2872
2544
  ].filter(Boolean);
2873
2545
 
2874
2546
  return {
2875
2547
  t: 'div',
2876
- a: { class: 'bw-carousel-slide' },
2548
+ a: { class: 'bw_carousel_slide' },
2877
2549
  c: slideContent.length === 1 ? slideContent[0] : slideContent
2878
2550
  };
2879
2551
  });
@@ -2883,7 +2555,7 @@ export function makeCarousel(props = {}) {
2883
2555
  {
2884
2556
  t: 'div',
2885
2557
  a: {
2886
- class: 'bw-carousel-track',
2558
+ class: 'bw_carousel_track',
2887
2559
  style: 'transform: translateX(-' + (startIndex * 100) + '%)'
2888
2560
  },
2889
2561
  c: slides
@@ -2895,11 +2567,11 @@ export function makeCarousel(props = {}) {
2895
2567
  children.push({
2896
2568
  t: 'button',
2897
2569
  a: {
2898
- class: 'bw-carousel-control bw-carousel-control-prev',
2570
+ class: 'bw_carousel_control bw_carousel_control_prev',
2899
2571
  type: 'button',
2900
2572
  'aria-label': 'Previous slide',
2901
2573
  onclick: function(e) {
2902
- var carousel = e.target.closest('.bw-carousel');
2574
+ var carousel = e.target.closest('.bw_carousel');
2903
2575
  var idx = parseInt(carousel.getAttribute('data-carousel-index') || '0');
2904
2576
  goToSlide(carousel, idx - 1);
2905
2577
  }
@@ -2909,11 +2581,11 @@ export function makeCarousel(props = {}) {
2909
2581
  children.push({
2910
2582
  t: 'button',
2911
2583
  a: {
2912
- class: 'bw-carousel-control bw-carousel-control-next',
2584
+ class: 'bw_carousel_control bw_carousel_control_next',
2913
2585
  type: 'button',
2914
2586
  'aria-label': 'Next slide',
2915
2587
  onclick: function(e) {
2916
- var carousel = e.target.closest('.bw-carousel');
2588
+ var carousel = e.target.closest('.bw_carousel');
2917
2589
  var idx = parseInt(carousel.getAttribute('data-carousel-index') || '0');
2918
2590
  goToSlide(carousel, idx + 1);
2919
2591
  }
@@ -2926,17 +2598,17 @@ export function makeCarousel(props = {}) {
2926
2598
  if (showIndicators && items.length > 1) {
2927
2599
  children.push({
2928
2600
  t: 'div',
2929
- a: { class: 'bw-carousel-indicators' },
2601
+ a: { class: 'bw_carousel_indicators' },
2930
2602
  c: items.map(function(_, i) {
2931
2603
  return {
2932
2604
  t: 'button',
2933
2605
  a: {
2934
- class: 'bw-carousel-indicator' + (i === startIndex ? ' active' : ''),
2606
+ class: 'bw_carousel_indicator' + (i === startIndex ? ' active' : ''),
2935
2607
  type: 'button',
2936
2608
  'aria-label': 'Go to slide ' + (i + 1),
2937
2609
  'data-slide-index': i,
2938
2610
  onclick: function(e) {
2939
- var carousel = e.target.closest('.bw-carousel');
2611
+ var carousel = e.target.closest('.bw_carousel');
2940
2612
  var idx = parseInt(e.target.getAttribute('data-slide-index'));
2941
2613
  goToSlide(carousel, idx);
2942
2614
  }
@@ -2949,7 +2621,7 @@ export function makeCarousel(props = {}) {
2949
2621
  return {
2950
2622
  t: 'div',
2951
2623
  a: {
2952
- class: ('bw-carousel ' + className).trim(),
2624
+ class: ('bw_carousel ' + className).trim(),
2953
2625
  style: 'height: ' + height,
2954
2626
  tabindex: '0',
2955
2627
  'aria-roledescription': 'carousel',
@@ -3057,8 +2729,8 @@ export function makeStatCard(props = {}) {
3057
2729
  }
3058
2730
 
3059
2731
  var classes = [
3060
- 'bw-stat-card',
3061
- variant ? 'bw-stat-card-' + variant : '',
2732
+ 'bw_stat_card',
2733
+ variantClass(variant),
3062
2734
  className
3063
2735
  ].filter(Boolean).join(' ').trim();
3064
2736
 
@@ -3067,21 +2739,21 @@ export function makeStatCard(props = {}) {
3067
2739
  if (icon) {
3068
2740
  children.push({
3069
2741
  t: 'div',
3070
- a: { class: 'bw-stat-icon' },
2742
+ a: { class: 'bw_stat_icon' },
3071
2743
  c: icon
3072
2744
  });
3073
2745
  }
3074
2746
 
3075
2747
  children.push({
3076
2748
  t: 'div',
3077
- a: { class: 'bw-stat-value' },
2749
+ a: { class: 'bw_stat_value' },
3078
2750
  c: formatValue(value, format)
3079
2751
  });
3080
2752
 
3081
2753
  if (label) {
3082
2754
  children.push({
3083
2755
  t: 'div',
3084
- a: { class: 'bw-stat-label' },
2756
+ a: { class: 'bw_stat_label' },
3085
2757
  c: label
3086
2758
  });
3087
2759
  }
@@ -3090,7 +2762,7 @@ export function makeStatCard(props = {}) {
3090
2762
  children.push({
3091
2763
  t: 'div',
3092
2764
  a: {
3093
- class: 'bw-stat-change ' + (change >= 0 ? 'bw-stat-change-up' : 'bw-stat-change-down')
2765
+ class: 'bw_stat_change ' + (change >= 0 ? 'bw_stat_change_up' : 'bw_stat_change_down')
3094
2766
  },
3095
2767
  c: (change >= 0 ? '\u2191 +' : '\u2193 ') + change + '%'
3096
2768
  });
@@ -3139,13 +2811,13 @@ export function makeTooltip(props = {}) {
3139
2811
 
3140
2812
  return {
3141
2813
  t: 'span',
3142
- a: { class: ('bw-tooltip-wrapper ' + className).trim() },
2814
+ a: { class: ('bw_tooltip_wrapper ' + className).trim() },
3143
2815
  c: [
3144
2816
  content,
3145
2817
  {
3146
2818
  t: 'span',
3147
2819
  a: {
3148
- class: 'bw-tooltip bw-tooltip-' + placement,
2820
+ class: 'bw_tooltip bw_tooltip_' + placement,
3149
2821
  role: 'tooltip'
3150
2822
  },
3151
2823
  c: text
@@ -3154,18 +2826,18 @@ export function makeTooltip(props = {}) {
3154
2826
  o: {
3155
2827
  type: 'tooltip',
3156
2828
  mounted: function(el) {
3157
- var tip = el.querySelector('.bw-tooltip');
2829
+ var tip = el.querySelector('.bw_tooltip');
3158
2830
  el.addEventListener('mouseenter', function() {
3159
- tip.classList.add('bw-tooltip-show');
2831
+ tip.classList.add('bw_tooltip_show');
3160
2832
  });
3161
2833
  el.addEventListener('mouseleave', function() {
3162
- tip.classList.remove('bw-tooltip-show');
2834
+ tip.classList.remove('bw_tooltip_show');
3163
2835
  });
3164
2836
  el.addEventListener('focusin', function() {
3165
- tip.classList.add('bw-tooltip-show');
2837
+ tip.classList.add('bw_tooltip_show');
3166
2838
  });
3167
2839
  el.addEventListener('focusout', function() {
3168
- tip.classList.remove('bw-tooltip-show');
2840
+ tip.classList.remove('bw_tooltip_show');
3169
2841
  });
3170
2842
  }
3171
2843
  }
@@ -3206,28 +2878,28 @@ export function makePopover(props = {}) {
3206
2878
  var popoverContent = [
3207
2879
  title && {
3208
2880
  t: 'div',
3209
- a: { class: 'bw-popover-header' },
2881
+ a: { class: 'bw_popover_header' },
3210
2882
  c: title
3211
2883
  },
3212
2884
  content && {
3213
2885
  t: 'div',
3214
- a: { class: 'bw-popover-body' },
2886
+ a: { class: 'bw_popover_body' },
3215
2887
  c: content
3216
2888
  }
3217
2889
  ].filter(Boolean);
3218
2890
 
3219
2891
  return {
3220
2892
  t: 'span',
3221
- a: { class: ('bw-popover-wrapper ' + className).trim() },
2893
+ a: { class: ('bw_popover_wrapper ' + className).trim() },
3222
2894
  c: [
3223
2895
  {
3224
2896
  t: 'span',
3225
2897
  a: {
3226
- class: 'bw-popover-trigger',
2898
+ class: 'bw_popover_trigger',
3227
2899
  onclick: function(e) {
3228
- var wrapper = e.target.closest('.bw-popover-wrapper');
3229
- var pop = wrapper.querySelector('.bw-popover');
3230
- pop.classList.toggle('bw-popover-show');
2900
+ var wrapper = e.target.closest('.bw_popover_wrapper');
2901
+ var pop = wrapper.querySelector('.bw_popover');
2902
+ pop.classList.toggle('bw_popover_show');
3231
2903
  }
3232
2904
  },
3233
2905
  c: trigger
@@ -3235,7 +2907,7 @@ export function makePopover(props = {}) {
3235
2907
  {
3236
2908
  t: 'div',
3237
2909
  a: {
3238
- class: 'bw-popover bw-popover-' + placement
2910
+ class: 'bw_popover bw_popover_' + placement
3239
2911
  },
3240
2912
  c: popoverContent
3241
2913
  }
@@ -3246,8 +2918,8 @@ export function makePopover(props = {}) {
3246
2918
  // Click outside to close
3247
2919
  var outsideHandler = function(e) {
3248
2920
  if (!el.contains(e.target)) {
3249
- var pop = el.querySelector('.bw-popover');
3250
- if (pop) pop.classList.remove('bw-popover-show');
2921
+ var pop = el.querySelector('.bw_popover');
2922
+ if (pop) pop.classList.remove('bw_popover_show');
3251
2923
  }
3252
2924
  };
3253
2925
  document.addEventListener('click', outsideHandler);
@@ -3302,13 +2974,13 @@ export function makeSearchInput(props = {}) {
3302
2974
 
3303
2975
  return {
3304
2976
  t: 'div',
3305
- a: { class: ('bw-search-input ' + className).trim() },
2977
+ a: { class: ('bw_search_input ' + className).trim() },
3306
2978
  c: [
3307
2979
  {
3308
2980
  t: 'input',
3309
2981
  a: {
3310
2982
  type: 'search',
3311
- class: 'bw-form-control bw-search-field',
2983
+ class: 'bw_form_control bw_search_field',
3312
2984
  placeholder: placeholder,
3313
2985
  value: value,
3314
2986
  id: id,
@@ -3320,8 +2992,8 @@ export function makeSearchInput(props = {}) {
3320
2992
  }
3321
2993
  },
3322
2994
  oninput: function(e) {
3323
- var wrapper = e.target.closest('.bw-search-input');
3324
- var clearBtn = wrapper.querySelector('.bw-search-clear');
2995
+ var wrapper = e.target.closest('.bw_search_input');
2996
+ var clearBtn = wrapper.querySelector('.bw_search_clear');
3325
2997
  if (clearBtn) {
3326
2998
  clearBtn.style.display = e.target.value ? 'flex' : 'none';
3327
2999
  }
@@ -3333,12 +3005,12 @@ export function makeSearchInput(props = {}) {
3333
3005
  t: 'button',
3334
3006
  a: {
3335
3007
  type: 'button',
3336
- class: 'bw-search-clear',
3008
+ class: 'bw_search_clear',
3337
3009
  'aria-label': 'Clear search',
3338
3010
  style: value ? undefined : 'display: none',
3339
3011
  onclick: function(e) {
3340
- var wrapper = e.target.closest('.bw-search-input');
3341
- var input = wrapper.querySelector('.bw-search-field');
3012
+ var wrapper = e.target.closest('.bw_search_input');
3013
+ var input = wrapper.querySelector('.bw_search_field');
3342
3014
  input.value = '';
3343
3015
  e.target.style.display = 'none';
3344
3016
  input.focus();
@@ -3405,13 +3077,13 @@ export function makeRange(props = {}) {
3405
3077
  if (showValue) {
3406
3078
  labelContent.push({
3407
3079
  t: 'span',
3408
- a: { class: 'bw-range-value' },
3080
+ a: { class: 'bw_range_value' },
3409
3081
  c: '' + value
3410
3082
  });
3411
3083
  }
3412
3084
  children.push({
3413
3085
  t: 'div',
3414
- a: { class: 'bw-range-label' },
3086
+ a: { class: 'bw_range_label' },
3415
3087
  c: labelContent
3416
3088
  });
3417
3089
  }
@@ -3420,8 +3092,8 @@ export function makeRange(props = {}) {
3420
3092
  var userOnInput = eventHandlers.oninput;
3421
3093
  if (showValue) {
3422
3094
  eventHandlers.oninput = function(e) {
3423
- var wrapper = e.target.closest('.bw-range-wrapper');
3424
- var valDisplay = wrapper.querySelector('.bw-range-value');
3095
+ var wrapper = e.target.closest('.bw_range_wrapper');
3096
+ var valDisplay = wrapper.querySelector('.bw_range_value');
3425
3097
  if (valDisplay) valDisplay.textContent = e.target.value;
3426
3098
  if (userOnInput) userOnInput(e);
3427
3099
  };
@@ -3431,7 +3103,7 @@ export function makeRange(props = {}) {
3431
3103
  t: 'input',
3432
3104
  a: {
3433
3105
  type: 'range',
3434
- class: 'bw-range',
3106
+ class: 'bw_range',
3435
3107
  min: min,
3436
3108
  max: max,
3437
3109
  step: step,
@@ -3445,7 +3117,7 @@ export function makeRange(props = {}) {
3445
3117
 
3446
3118
  return {
3447
3119
  t: 'div',
3448
- a: { class: ('bw-range-wrapper ' + className).trim() },
3120
+ a: { class: ('bw_range_wrapper ' + className).trim() },
3449
3121
  c: children,
3450
3122
  o: { type: 'range' }
3451
3123
  };
@@ -3488,7 +3160,7 @@ export function makeMediaObject(props = {}) {
3488
3160
  var imgEl = src ? {
3489
3161
  t: 'img',
3490
3162
  a: {
3491
- class: 'bw-media-img',
3163
+ class: 'bw_media_img',
3492
3164
  src: src,
3493
3165
  alt: alt,
3494
3166
  style: 'width:' + imageSize + ';height:' + imageSize
@@ -3497,16 +3169,16 @@ export function makeMediaObject(props = {}) {
3497
3169
 
3498
3170
  var bodyEl = {
3499
3171
  t: 'div',
3500
- a: { class: 'bw-media-body' },
3172
+ a: { class: 'bw_media_body' },
3501
3173
  c: [
3502
- title && { t: 'h5', a: { class: 'bw-media-title' }, c: title },
3174
+ title && { t: 'h5', a: { class: 'bw_media_title' }, c: title },
3503
3175
  content
3504
3176
  ].filter(Boolean)
3505
3177
  };
3506
3178
 
3507
3179
  return {
3508
3180
  t: 'div',
3509
- a: { class: ('bw-media ' + (reverse ? 'bw-media-reverse ' : '') + className).trim() },
3181
+ a: { class: ('bw_media ' + (reverse ? 'bw_media_reverse ' : '') + className).trim() },
3510
3182
  c: reverse
3511
3183
  ? [bodyEl, imgEl].filter(Boolean)
3512
3184
  : [imgEl, bodyEl].filter(Boolean),
@@ -3549,19 +3221,19 @@ export function makeFileUpload(props = {}) {
3549
3221
  return {
3550
3222
  t: 'div',
3551
3223
  a: {
3552
- class: ('bw-file-upload ' + className).trim(),
3224
+ class: ('bw_file_upload ' + className).trim(),
3553
3225
  tabindex: '0',
3554
3226
  role: 'button',
3555
3227
  'aria-label': text
3556
3228
  },
3557
3229
  c: [
3558
- { t: 'div', a: { class: 'bw-file-upload-icon' }, c: '\uD83D\uDCC1' },
3559
- { t: 'div', a: { class: 'bw-file-upload-text' }, c: text },
3230
+ { t: 'div', a: { class: 'bw_file_upload_icon' }, c: '\uD83D\uDCC1' },
3231
+ { t: 'div', a: { class: 'bw_file_upload_text' }, c: text },
3560
3232
  {
3561
3233
  t: 'input',
3562
3234
  a: {
3563
3235
  type: 'file',
3564
- class: 'bw-file-upload-input',
3236
+ class: 'bw_file_upload_input',
3565
3237
  accept: accept,
3566
3238
  multiple: multiple,
3567
3239
  id: id,
@@ -3574,7 +3246,7 @@ export function makeFileUpload(props = {}) {
3574
3246
  o: {
3575
3247
  type: 'file-upload',
3576
3248
  mounted: function(el) {
3577
- var input = el.querySelector('.bw-file-upload-input');
3249
+ var input = el.querySelector('.bw_file_upload_input');
3578
3250
 
3579
3251
  // Click zone to trigger file input
3580
3252
  el.addEventListener('click', function(e) {
@@ -3592,14 +3264,14 @@ export function makeFileUpload(props = {}) {
3592
3264
  // Drag-and-drop visuals
3593
3265
  el.addEventListener('dragover', function(e) {
3594
3266
  e.preventDefault();
3595
- el.classList.add('bw-file-upload-active');
3267
+ el.classList.add('bw_file_upload_active');
3596
3268
  });
3597
3269
  el.addEventListener('dragleave', function() {
3598
- el.classList.remove('bw-file-upload-active');
3270
+ el.classList.remove('bw_file_upload_active');
3599
3271
  });
3600
3272
  el.addEventListener('drop', function(e) {
3601
3273
  e.preventDefault();
3602
- el.classList.remove('bw-file-upload-active');
3274
+ el.classList.remove('bw_file_upload_active');
3603
3275
  if (onFiles && e.dataTransfer.files.length) onFiles(e.dataTransfer.files);
3604
3276
  });
3605
3277
  }
@@ -3643,32 +3315,32 @@ export function makeTimeline(props = {}) {
3643
3315
 
3644
3316
  return {
3645
3317
  t: 'div',
3646
- a: { class: ('bw-timeline ' + className).trim() },
3318
+ a: { class: ('bw_timeline ' + className).trim() },
3647
3319
  c: items.map(function(item) {
3648
3320
  return {
3649
3321
  t: 'div',
3650
- a: { class: 'bw-timeline-item' },
3322
+ a: { class: 'bw_timeline_item' },
3651
3323
  c: [
3652
3324
  {
3653
3325
  t: 'div',
3654
- a: { class: 'bw-timeline-marker bw-timeline-marker-' + (item.variant || 'primary') }
3326
+ a: { class: 'bw_timeline_marker ' + variantClass(item.variant || 'primary') }
3655
3327
  },
3656
3328
  {
3657
3329
  t: 'div',
3658
- a: { class: 'bw-timeline-content' },
3330
+ a: { class: 'bw_timeline_content' },
3659
3331
  c: [
3660
3332
  item.date && {
3661
3333
  t: 'div',
3662
- a: { class: 'bw-timeline-date' },
3334
+ a: { class: 'bw_timeline_date' },
3663
3335
  c: item.date
3664
3336
  },
3665
3337
  item.title && {
3666
3338
  t: 'h5',
3667
- a: { class: 'bw-timeline-title' },
3339
+ a: { class: 'bw_timeline_title' },
3668
3340
  c: item.title
3669
3341
  },
3670
3342
  item.content && (typeof item.content === 'string'
3671
- ? { t: 'p', a: { class: 'bw-timeline-text' }, c: item.content }
3343
+ ? { t: 'p', a: { class: 'bw_timeline_text' }, c: item.content }
3672
3344
  : item.content)
3673
3345
  ].filter(Boolean)
3674
3346
  }
@@ -3713,28 +3385,28 @@ export function makeStepper(props = {}) {
3713
3385
 
3714
3386
  return {
3715
3387
  t: 'div',
3716
- a: { class: ('bw-stepper ' + className).trim(), role: 'list' },
3388
+ a: { class: ('bw_stepper ' + className).trim(), role: 'list' },
3717
3389
  c: steps.map(function(step, index) {
3718
3390
  var state = index < currentStep ? 'completed' : index === currentStep ? 'active' : 'pending';
3719
3391
  return {
3720
3392
  t: 'div',
3721
3393
  a: {
3722
- class: 'bw-step bw-step-' + state,
3394
+ class: 'bw_step bw_step_' + state,
3723
3395
  role: 'listitem',
3724
3396
  'aria-current': state === 'active' ? 'step' : undefined
3725
3397
  },
3726
3398
  c: [
3727
3399
  {
3728
3400
  t: 'div',
3729
- a: { class: 'bw-step-indicator' },
3401
+ a: { class: 'bw_step_indicator' },
3730
3402
  c: state === 'completed' ? '\u2713' : '' + (index + 1)
3731
3403
  },
3732
3404
  {
3733
3405
  t: 'div',
3734
- a: { class: 'bw-step-body' },
3406
+ a: { class: 'bw_step_body' },
3735
3407
  c: [
3736
- { t: 'div', a: { class: 'bw-step-label' }, c: step.label },
3737
- step.description && { t: 'div', a: { class: 'bw-step-description' }, c: step.description }
3408
+ { t: 'div', a: { class: 'bw_step_label' }, c: step.label },
3409
+ step.description && { t: 'div', a: { class: 'bw_step_description' }, c: step.description }
3738
3410
  ].filter(Boolean)
3739
3411
  }
3740
3412
  ]
@@ -3779,17 +3451,17 @@ export function makeChipInput(props = {}) {
3779
3451
  function makeChipEl(text) {
3780
3452
  return {
3781
3453
  t: 'span',
3782
- a: { class: 'bw-chip', 'data-chip-value': text },
3454
+ a: { class: 'bw_chip', 'data-chip-value': text },
3783
3455
  c: [
3784
3456
  text,
3785
3457
  {
3786
3458
  t: 'button',
3787
3459
  a: {
3788
3460
  type: 'button',
3789
- class: 'bw-chip-remove',
3461
+ class: 'bw_chip_remove',
3790
3462
  'aria-label': 'Remove ' + text,
3791
3463
  onclick: function(e) {
3792
- var chip = e.target.closest('.bw-chip');
3464
+ var chip = e.target.closest('.bw_chip');
3793
3465
  var val = chip.getAttribute('data-chip-value');
3794
3466
  chip.parentNode.removeChild(chip);
3795
3467
  if (onRemove) onRemove(val);
@@ -3803,29 +3475,29 @@ export function makeChipInput(props = {}) {
3803
3475
 
3804
3476
  return {
3805
3477
  t: 'div',
3806
- a: { class: ('bw-chip-input ' + className).trim() },
3478
+ a: { class: ('bw_chip_input ' + className).trim() },
3807
3479
  c: [
3808
3480
  ...chips.map(makeChipEl),
3809
3481
  {
3810
3482
  t: 'input',
3811
3483
  a: {
3812
3484
  type: 'text',
3813
- class: 'bw-chip-field',
3485
+ class: 'bw_chip_field',
3814
3486
  placeholder: placeholder,
3815
3487
  onkeydown: function(e) {
3816
3488
  if (e.key === 'Enter' && e.target.value.trim()) {
3817
3489
  e.preventDefault();
3818
3490
  var val = e.target.value.trim();
3819
- var wrapper = e.target.closest('.bw-chip-input');
3491
+ var wrapper = e.target.closest('.bw_chip_input');
3820
3492
  // Insert chip before the input
3821
3493
  var chipEl = document.createElement('span');
3822
- chipEl.className = 'bw-chip';
3494
+ chipEl.className = 'bw_chip';
3823
3495
  chipEl.setAttribute('data-chip-value', val);
3824
3496
  chipEl.innerHTML = '';
3825
3497
  chipEl.textContent = val;
3826
3498
  var removeBtn = document.createElement('button');
3827
3499
  removeBtn.type = 'button';
3828
- removeBtn.className = 'bw-chip-remove';
3500
+ removeBtn.className = 'bw_chip_remove';
3829
3501
  removeBtn.setAttribute('aria-label', 'Remove ' + val);
3830
3502
  removeBtn.textContent = '\u00D7';
3831
3503
  removeBtn.onclick = function() {
@@ -3839,8 +3511,8 @@ export function makeChipInput(props = {}) {
3839
3511
  }
3840
3512
  // Backspace on empty input removes last chip
3841
3513
  if (e.key === 'Backspace' && !e.target.value) {
3842
- var wrapper = e.target.closest('.bw-chip-input');
3843
- var chipEls = wrapper.querySelectorAll('.bw-chip');
3514
+ var wrapper = e.target.closest('.bw_chip_input');
3515
+ var chipEls = wrapper.querySelectorAll('.bw_chip');
3844
3516
  if (chipEls.length) {
3845
3517
  var last = chipEls[chipEls.length - 1];
3846
3518
  var removedVal = last.getAttribute('data-chip-value');
@@ -3856,10 +3528,87 @@ export function makeChipInput(props = {}) {
3856
3528
  };
3857
3529
  }
3858
3530
 
3859
- export const componentHandles = {
3860
- card: CardHandle,
3861
- table: TableHandle,
3862
- navbar: NavbarHandle,
3863
- tabs: TabsHandle,
3864
- modal: ModalHandle
3531
+ // componentHandles registry removed in v2.0.15.
3532
+ // See dev/dead-code-elimination-v2.0.15.md for recovery.
3533
+
3534
+ // =========================================================================
3535
+ // BCCL Component Registry
3536
+ //
3537
+ // Single registry mapping type names to their factory functions.
3538
+ // Enables bw.make('card', props) dispatch and introspection via
3539
+ // Object.keys(BCCL).
3540
+ // =========================================================================
3541
+
3542
+ /**
3543
+ * BCCL component registry — maps component type names to factory functions.
3544
+ * Each entry's `make` function is the corresponding exported makeXxx().
3545
+ *
3546
+ * @type {Object.<string, {make: Function}>}
3547
+ */
3548
+ export var BCCL = {
3549
+ card: { make: makeCard },
3550
+ button: { make: makeButton },
3551
+ container: { make: makeContainer },
3552
+ row: { make: makeRow },
3553
+ col: { make: makeCol },
3554
+ nav: { make: makeNav },
3555
+ navbar: { make: makeNavbar },
3556
+ tabs: { make: makeTabs },
3557
+ alert: { make: makeAlert },
3558
+ badge: { make: makeBadge },
3559
+ progress: { make: makeProgress },
3560
+ listGroup: { make: makeListGroup },
3561
+ breadcrumb: { make: makeBreadcrumb },
3562
+ form: { make: makeForm },
3563
+ formGroup: { make: makeFormGroup },
3564
+ input: { make: makeInput },
3565
+ textarea: { make: makeTextarea },
3566
+ select: { make: makeSelect },
3567
+ checkbox: { make: makeCheckbox },
3568
+ stack: { make: makeStack },
3569
+ spinner: { make: makeSpinner },
3570
+ hero: { make: makeHero },
3571
+ featureGrid: { make: makeFeatureGrid },
3572
+ cta: { make: makeCTA },
3573
+ section: { make: makeSection },
3574
+ codeDemo: { make: makeCodeDemo },
3575
+ pagination: { make: makePagination },
3576
+ radio: { make: makeRadio },
3577
+ buttonGroup: { make: makeButtonGroup },
3578
+ accordion: { make: makeAccordion },
3579
+ modal: { make: makeModal },
3580
+ toast: { make: makeToast },
3581
+ dropdown: { make: makeDropdown },
3582
+ switch: { make: makeSwitch },
3583
+ skeleton: { make: makeSkeleton },
3584
+ avatar: { make: makeAvatar },
3585
+ carousel: { make: makeCarousel },
3586
+ statCard: { make: makeStatCard },
3587
+ tooltip: { make: makeTooltip },
3588
+ popover: { make: makePopover },
3589
+ searchInput: { make: makeSearchInput },
3590
+ range: { make: makeRange },
3591
+ mediaObject: { make: makeMediaObject },
3592
+ fileUpload: { make: makeFileUpload },
3593
+ timeline: { make: makeTimeline },
3594
+ stepper: { make: makeStepper },
3595
+ chipInput: { make: makeChipInput }
3865
3596
  };
3597
+
3598
+ /**
3599
+ * Factory function — create any BCCL component by type name.
3600
+ *
3601
+ * @param {string} type - Component type (e.g. 'card', 'button', 'alert')
3602
+ * @param {Object} [props] - Component properties
3603
+ * @returns {Object} TACO object
3604
+ * @throws {Error} If type is not found in the registry
3605
+ * @example
3606
+ * var card = make('card', { title: 'Hello', variant: 'primary' });
3607
+ * var btn = make('button', { text: 'Click', variant: 'success' });
3608
+ * var types = Object.keys(BCCL); // list all available types
3609
+ */
3610
+ export function make(type, props) {
3611
+ var def = BCCL[type];
3612
+ if (!def) throw new Error('bw.make: unknown component type "' + type + '". Available: ' + Object.keys(BCCL).join(', '));
3613
+ return def.make(props || {});
3614
+ }