bitwrench 2.0.16 → 2.0.18
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.
- package/README.md +127 -38
- package/dist/bitwrench-bccl.cjs.js +13 -9
- package/dist/bitwrench-bccl.cjs.min.js +2 -2
- package/dist/bitwrench-bccl.esm.js +13 -9
- package/dist/bitwrench-bccl.esm.min.js +2 -2
- package/dist/bitwrench-bccl.umd.js +13 -9
- package/dist/bitwrench-bccl.umd.min.js +2 -2
- package/dist/bitwrench-code-edit.cjs.js +1 -1
- package/dist/bitwrench-code-edit.cjs.min.js +1 -1
- package/dist/bitwrench-code-edit.es5.js +1 -1
- package/dist/bitwrench-code-edit.es5.min.js +1 -1
- package/dist/bitwrench-code-edit.esm.js +1 -1
- package/dist/bitwrench-code-edit.esm.min.js +1 -1
- package/dist/bitwrench-code-edit.umd.js +1 -1
- package/dist/bitwrench-code-edit.umd.min.js +1 -1
- package/dist/bitwrench-lean.cjs.js +1438 -920
- package/dist/bitwrench-lean.cjs.min.js +20 -20
- package/dist/bitwrench-lean.es5.js +1518 -1105
- package/dist/bitwrench-lean.es5.min.js +18 -18
- package/dist/bitwrench-lean.esm.js +1437 -920
- package/dist/bitwrench-lean.esm.min.js +20 -20
- package/dist/bitwrench-lean.umd.js +1438 -920
- package/dist/bitwrench-lean.umd.min.js +20 -20
- package/dist/bitwrench-util-css.cjs.js +236 -0
- package/dist/bitwrench-util-css.cjs.min.js +22 -0
- package/dist/bitwrench-util-css.es5.js +414 -0
- package/dist/bitwrench-util-css.es5.min.js +21 -0
- package/dist/bitwrench-util-css.esm.js +230 -0
- package/dist/bitwrench-util-css.esm.min.js +21 -0
- package/dist/bitwrench-util-css.umd.js +242 -0
- package/dist/bitwrench-util-css.umd.min.js +21 -0
- package/dist/bitwrench.cjs.js +1450 -928
- package/dist/bitwrench.cjs.min.js +21 -21
- package/dist/bitwrench.css +456 -132
- package/dist/bitwrench.es5.js +1563 -1140
- package/dist/bitwrench.es5.min.js +19 -19
- package/dist/bitwrench.esm.js +1450 -929
- package/dist/bitwrench.esm.min.js +21 -21
- package/dist/bitwrench.min.css +1 -1
- package/dist/bitwrench.umd.js +1450 -928
- package/dist/bitwrench.umd.min.js +21 -21
- package/dist/builds.json +178 -90
- package/dist/bwserve.cjs.js +528 -68
- package/dist/bwserve.esm.js +527 -69
- package/dist/sri.json +44 -36
- package/package.json +5 -2
- package/readme.html +136 -49
- package/src/bitwrench-bccl.js +12 -8
- package/src/bitwrench-color-utils.js +31 -9
- package/src/bitwrench-esm-entry.js +11 -0
- package/src/bitwrench-styles.js +439 -232
- package/src/bitwrench-util-css.js +229 -0
- package/src/bitwrench.js +979 -630
- package/src/bwserve/attach.js +57 -0
- package/src/bwserve/bwclient.js +141 -0
- package/src/bwserve/bwshell.js +102 -0
- package/src/bwserve/client.js +151 -1
- package/src/bwserve/index.js +139 -29
- package/src/cli/attach.js +555 -0
- package/src/cli/convert.js +2 -5
- package/src/cli/index.js +7 -0
- package/src/cli/inject.js +1 -1
- package/src/cli/layout-default.js +47 -32
- package/src/cli/serve.js +6 -2
- package/src/generate-css.js +11 -4
- package/src/vendor/html2canvas.min.js +20 -0
- package/src/version.js +3 -3
- package/src/bwserve/shell.js +0 -103
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
/*! bitwrench-lean v2.0.
|
|
1
|
+
/*! bitwrench-lean v2.0.18 | BSD-2-Clause | https://deftio.github.com/bitwrench/pages */
|
|
2
2
|
/**
|
|
3
3
|
* Auto-generated version file from package.json
|
|
4
4
|
* DO NOT EDIT DIRECTLY - Use npm run generate-version
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const VERSION_INFO = {
|
|
8
|
-
version: '2.0.
|
|
8
|
+
version: '2.0.18',
|
|
9
9
|
name: 'bitwrench',
|
|
10
10
|
description: 'A library for javascript UI functions.',
|
|
11
11
|
license: 'BSD-2-Clause',
|
|
12
12
|
homepage: 'https://deftio.github.com/bitwrench/pages',
|
|
13
13
|
repository: 'git+https://github.com/deftio/bitwrench.git',
|
|
14
14
|
author: 'manu a. chatterjee <deftio@deftio.com> (https://deftio.com/)',
|
|
15
|
-
buildDate: '2026-03-
|
|
15
|
+
buildDate: '2026-03-17T00:50:09.505Z'
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -306,13 +306,18 @@ function harmonize(sourceHex, targetHex, amount) {
|
|
|
306
306
|
*/
|
|
307
307
|
function deriveShades(hex) {
|
|
308
308
|
var rgb = colorParse(hex);
|
|
309
|
+
// For light input colors (L > 75), mixing toward white produces invisible borders.
|
|
310
|
+
// Darken instead so borders remain visible against light backgrounds.
|
|
311
|
+
var borderColor = hexToHsl(hex)[2] > 75
|
|
312
|
+
? adjustLightness(hex, -18)
|
|
313
|
+
: mixColor(hex, '#ffffff', 0.60);
|
|
309
314
|
return {
|
|
310
315
|
base: hex,
|
|
311
316
|
hover: adjustLightness(hex, -10),
|
|
312
317
|
active: adjustLightness(hex, -15),
|
|
313
318
|
light: mixColor(hex, '#ffffff', 0.85),
|
|
314
319
|
darkText: adjustLightness(hex, -40),
|
|
315
|
-
border:
|
|
320
|
+
border: borderColor,
|
|
316
321
|
focus: 'rgba(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ',0.25)',
|
|
317
322
|
textOn: textOnColor(hex)
|
|
318
323
|
};
|
|
@@ -371,19 +376,27 @@ function deriveAlternateConfig(config) {
|
|
|
371
376
|
alt.secondary = deriveAlternateSeed(config.secondary);
|
|
372
377
|
alt.tertiary = config.tertiary ? deriveAlternateSeed(config.tertiary) : alt.primary;
|
|
373
378
|
|
|
374
|
-
// Derive alternate surface colors from primary hue
|
|
379
|
+
// Derive alternate surface colors from primary hue.
|
|
380
|
+
// Check actual page surface brightness (not seed color brightness) to decide
|
|
381
|
+
// whether alternate should be dark or light. The page surface is what the
|
|
382
|
+
// user sees; seeds can be dark while the page is still light (default L=96).
|
|
375
383
|
var priHsl = hexToHsl(config.primary);
|
|
376
384
|
var h = priHsl[0];
|
|
377
|
-
var
|
|
385
|
+
var primarySurface = config.surface || hslToHex([h, 8, 96]);
|
|
386
|
+
var isLight = relativeLuminance(primarySurface) > 0.179;
|
|
378
387
|
|
|
379
388
|
if (isLight) {
|
|
380
|
-
//
|
|
389
|
+
// Page surface is light → alternate needs dark surfaces
|
|
381
390
|
alt.light = hslToHex([h, Math.min(priHsl[1], 15), 15]);
|
|
382
391
|
alt.dark = hslToHex([h, 5, 88]);
|
|
392
|
+
alt.surface = hslToHex([h, 12, 18]);
|
|
393
|
+
alt.background = hslToHex([h, 10, 14]);
|
|
383
394
|
} else {
|
|
384
|
-
//
|
|
395
|
+
// Page surface is dark → alternate needs light surfaces
|
|
385
396
|
alt.light = hslToHex([h, Math.min(priHsl[1], 10), 96]);
|
|
386
397
|
alt.dark = hslToHex([h, 10, 18]);
|
|
398
|
+
alt.surface = hslToHex([h, 8, 96]);
|
|
399
|
+
alt.background = hslToHex([h, 6, 98]);
|
|
387
400
|
}
|
|
388
401
|
|
|
389
402
|
// Semantic colors: harmonize toward primary, then invert for alternate
|
|
@@ -431,10 +444,18 @@ function derivePalette(config) {
|
|
|
431
444
|
var darkBase = config.dark || hslToHex([h, 10, 13]);
|
|
432
445
|
|
|
433
446
|
// Background & surface tokens — tinted with primary hue for theme personality.
|
|
434
|
-
//
|
|
447
|
+
// Saturation high enough that the hue is visible (each theme feels distinct)
|
|
448
|
+
// but low enough to stay neutral and readable.
|
|
435
449
|
// User can override with config.background / config.surface.
|
|
436
|
-
var bgBase = config.background || hslToHex([h,
|
|
437
|
-
var surfBase = config.surface || hslToHex([h,
|
|
450
|
+
var bgBase = config.background || hslToHex([h, 22, 96]);
|
|
451
|
+
var surfBase = config.surface || hslToHex([h, 25, 94]);
|
|
452
|
+
|
|
453
|
+
// surfaceAlt: subtle background variant for striped rows, hover states, headers.
|
|
454
|
+
// Slightly lighter than surface in dark mode, slightly darker in light mode.
|
|
455
|
+
var surfHsl = hexToHsl(surfBase);
|
|
456
|
+
var surfAlt = surfHsl[2] <= 50
|
|
457
|
+
? hslToHex([surfHsl[0], surfHsl[1], Math.min(surfHsl[2] + 8, 100)])
|
|
458
|
+
: hslToHex([surfHsl[0], surfHsl[1], Math.max(surfHsl[2] - 3, 0)]);
|
|
438
459
|
|
|
439
460
|
var palette = {
|
|
440
461
|
primary: deriveShades(config.primary),
|
|
@@ -447,7 +468,8 @@ function derivePalette(config) {
|
|
|
447
468
|
light: deriveShades(lightBase),
|
|
448
469
|
dark: deriveShades(darkBase),
|
|
449
470
|
background: bgBase,
|
|
450
|
-
surface: surfBase
|
|
471
|
+
surface: surfBase,
|
|
472
|
+
surfaceAlt: surfAlt
|
|
451
473
|
};
|
|
452
474
|
|
|
453
475
|
return palette;
|
|
@@ -494,10 +516,12 @@ var SPACING_SCALE = {
|
|
|
494
516
|
5: '1.5rem', // 24px
|
|
495
517
|
6: '2rem'};
|
|
496
518
|
|
|
519
|
+
let _S=SPACING_SCALE;
|
|
520
|
+
|
|
497
521
|
var SPACING_PRESETS = {
|
|
498
|
-
compact: { btn:
|
|
499
|
-
normal: { btn:
|
|
500
|
-
spacious: { btn:
|
|
522
|
+
compact: { btn: _S[1] + ' ' + _S[3], card: _S[3] + ' ' + _S[4], alert: _S[2] + ' ' + _S[4], cell: _S[2] + ' ' + _S[3], input: _S[1] + ' ' + _S[3] },
|
|
523
|
+
normal: { btn: _S[2] + ' ' + _S[4], card: _S[5] + ' ' + _S[5], alert: _S[3] + ' ' + _S[5], cell: _S[3] + ' ' + _S[4], input: _S[2] + ' ' + _S[3] },
|
|
524
|
+
spacious: { btn: _S[3] + ' ' + _S[5], card: _S[6] + ' ' + _S[6], alert: _S[4] + ' ' + _S[5], cell: _S[4] + ' ' + _S[5], input: _S[3] + ' ' + _S[4] }
|
|
501
525
|
};
|
|
502
526
|
|
|
503
527
|
var RADIUS_PRESETS = {
|
|
@@ -609,20 +633,14 @@ var DEFAULT_PALETTE_CONFIG = {
|
|
|
609
633
|
* Built-in theme presets — named color combinations
|
|
610
634
|
* Each preset provides primary, secondary, and tertiary seed colors.
|
|
611
635
|
*/
|
|
612
|
-
var THEME_PRESETS =
|
|
613
|
-
teal
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
amber: { primary: '#d97706', secondary: '#fbbf24', tertiary: '#f59e0b' },
|
|
621
|
-
emerald: { primary: '#059669', secondary: '#6ee7b7', tertiary: '#34d399' },
|
|
622
|
-
nord: { primary: '#5e81ac', secondary: '#88c0d0', tertiary: '#81a1c1' },
|
|
623
|
-
coral: { primary: '#ef6461', secondary: '#4a7c7e', tertiary: '#e8a87c' },
|
|
624
|
-
midnight: { primary: '#1e3a5f', secondary: '#7c8db5', tertiary: '#3d5a80' }
|
|
625
|
-
};
|
|
636
|
+
var THEME_PRESETS = Object.fromEntries([
|
|
637
|
+
['teal','#006666','#6c757d','#006666'],['ocean','#0077b6','#90e0ef','#00b4d8'],
|
|
638
|
+
['sunset','#e76f51','#264653','#e9c46a'],['forest','#2d6a4f','#95d5b2','#52b788'],
|
|
639
|
+
['slate','#343a40','#adb5bd','#6c757d'],['rose','#e11d48','#fda4af','#fb7185'],
|
|
640
|
+
['indigo','#4f46e5','#a5b4fc','#818cf8'],['amber','#d97706','#fbbf24','#f59e0b'],
|
|
641
|
+
['emerald','#059669','#6ee7b7','#34d399'],['nord','#5e81ac','#88c0d0','#81a1c1'],
|
|
642
|
+
['coral','#ef6461','#4a7c7e','#e8a87c'],['midnight','#1e3a5f','#7c8db5','#3d5a80']
|
|
643
|
+
].map(function(e) { return [e[0], {primary:e[1],secondary:e[2],tertiary:e[3]}]; }));
|
|
626
644
|
|
|
627
645
|
/**
|
|
628
646
|
* Resolve layout config to spacing, radius, typeScale, elevation, and motion objects.
|
|
@@ -669,6 +687,7 @@ function scopeSelector(name, sel) {
|
|
|
669
687
|
if (sel.includes(',')) return sel.split(',').map(function(s) { return '.' + name + ' ' + s.trim(); }).join(', ');
|
|
670
688
|
return '.' + name + ' ' + sel;
|
|
671
689
|
}
|
|
690
|
+
var _sx=scopeSelector;
|
|
672
691
|
|
|
673
692
|
// =========================================================================
|
|
674
693
|
// Themed CSS generators
|
|
@@ -677,12 +696,12 @@ function scopeSelector(name, sel) {
|
|
|
677
696
|
function generateTypographyThemed(scope, palette, layout) {
|
|
678
697
|
var mot = layout.motion;
|
|
679
698
|
var rules = {};
|
|
680
|
-
rules[
|
|
699
|
+
rules[_sx(scope, 'a')] = {
|
|
681
700
|
'color': palette.primary.base,
|
|
682
701
|
'text-decoration': 'none',
|
|
683
702
|
'transition': 'color ' + mot.fast + ' ' + mot.easing
|
|
684
703
|
};
|
|
685
|
-
rules[
|
|
704
|
+
rules[_sx(scope, 'a:hover')] = {
|
|
686
705
|
'color': palette.primary.hover,
|
|
687
706
|
'text-decoration': 'underline'
|
|
688
707
|
};
|
|
@@ -695,11 +714,11 @@ function generateButtons(scope, palette, layout) {
|
|
|
695
714
|
var rd = layout.radius;
|
|
696
715
|
|
|
697
716
|
// Base button (only when scoped — unscoped uses defaultStyles)
|
|
698
|
-
rules[
|
|
717
|
+
rules[_sx(scope, '.bw_btn')] = {
|
|
699
718
|
'padding': sp.btn,
|
|
700
719
|
'border-radius': rd.btn
|
|
701
720
|
};
|
|
702
|
-
rules[
|
|
721
|
+
rules[_sx(scope, '.bw_btn:focus-visible')] = {
|
|
703
722
|
'outline': '2px solid currentColor',
|
|
704
723
|
'outline-offset': '2px',
|
|
705
724
|
'box-shadow': '0 0 0 3px ' + palette.primary.focus
|
|
@@ -708,12 +727,12 @@ function generateButtons(scope, palette, layout) {
|
|
|
708
727
|
// Variant colors handled by palette class on component root
|
|
709
728
|
|
|
710
729
|
// Size variants (structural, reuse layout radius)
|
|
711
|
-
rules[
|
|
730
|
+
rules[_sx(scope, '.bw_btn_lg')] = {
|
|
712
731
|
'padding': '0.625rem 1.5rem',
|
|
713
732
|
'font-size': '1rem',
|
|
714
733
|
'border-radius': rd.btn === '50rem' ? '50rem' : (parseInt(rd.btn) + 2) + 'px'
|
|
715
734
|
};
|
|
716
|
-
rules[
|
|
735
|
+
rules[_sx(scope, '.bw_btn_sm')] = {
|
|
717
736
|
'padding': '0.25rem 0.75rem',
|
|
718
737
|
'font-size': '0.8125rem',
|
|
719
738
|
'border-radius': rd.btn === '50rem' ? '50rem' : (Math.max(parseInt(rd.btn) - 1, 0)) + 'px'
|
|
@@ -727,7 +746,7 @@ function generateAlerts(scope, palette, layout) {
|
|
|
727
746
|
var sp = layout.spacing;
|
|
728
747
|
var rd = layout.radius;
|
|
729
748
|
|
|
730
|
-
rules[
|
|
749
|
+
rules[_sx(scope, '.bw_alert')] = {
|
|
731
750
|
'padding': sp.alert,
|
|
732
751
|
'border-radius': rd.alert
|
|
733
752
|
};
|
|
@@ -746,36 +765,36 @@ function generateCards(scope, palette, layout) {
|
|
|
746
765
|
|
|
747
766
|
var elev = layout.elevation;
|
|
748
767
|
var motion = layout.motion;
|
|
749
|
-
rules[
|
|
768
|
+
rules[_sx(scope, '.bw_card')] = {
|
|
750
769
|
'background-color': palette.surface || '#fff',
|
|
751
770
|
'border': '1px solid ' + palette.light.border,
|
|
752
771
|
'border-radius': rd.card,
|
|
753
772
|
'box-shadow': elev.sm,
|
|
754
773
|
'transition': 'box-shadow ' + motion.normal + ' ' + motion.easing + ', transform ' + motion.normal + ' ' + motion.easing
|
|
755
774
|
};
|
|
756
|
-
rules[
|
|
775
|
+
rules[_sx(scope, '.bw_card:hover')] = {
|
|
757
776
|
'box-shadow': elev.md
|
|
758
777
|
};
|
|
759
|
-
rules[
|
|
778
|
+
rules[_sx(scope, '.bw_card_hoverable:hover')] = {
|
|
760
779
|
'box-shadow': elev.lg
|
|
761
780
|
};
|
|
762
|
-
rules[
|
|
781
|
+
rules[_sx(scope, '.bw_card_body')] = {
|
|
763
782
|
'padding': sp.card
|
|
764
783
|
};
|
|
765
|
-
rules[
|
|
784
|
+
rules[_sx(scope, '.bw_card_header')] = {
|
|
766
785
|
'padding': sp.card.split(' ').map(function(v) { return (parseFloat(v) * 0.7).toFixed(3).replace(/\.?0+$/, '') + 'rem'; }).join(' '),
|
|
767
|
-
'background-color': palette.
|
|
786
|
+
'background-color': palette.surfaceAlt,
|
|
768
787
|
'border-bottom': '1px solid ' + palette.light.border
|
|
769
788
|
};
|
|
770
|
-
rules[
|
|
771
|
-
'background-color': palette.
|
|
789
|
+
rules[_sx(scope, '.bw_card_footer')] = {
|
|
790
|
+
'background-color': palette.surfaceAlt,
|
|
772
791
|
'border-top': '1px solid ' + palette.light.border,
|
|
773
792
|
'color': palette.secondary.base
|
|
774
793
|
};
|
|
775
|
-
rules[
|
|
794
|
+
rules[_sx(scope, '.bw_card_title')] = {
|
|
776
795
|
'color': palette.dark.base
|
|
777
796
|
};
|
|
778
|
-
rules[
|
|
797
|
+
rules[_sx(scope, '.bw_card_subtitle')] = {
|
|
779
798
|
'color': palette.secondary.base
|
|
780
799
|
};
|
|
781
800
|
|
|
@@ -789,55 +808,55 @@ function generateForms(scope, palette, layout) {
|
|
|
789
808
|
var sp = layout.spacing;
|
|
790
809
|
var rd = layout.radius;
|
|
791
810
|
|
|
792
|
-
rules[
|
|
811
|
+
rules[_sx(scope, '.bw_form_control')] = {
|
|
793
812
|
'padding': sp.input,
|
|
794
813
|
'border-radius': rd.input,
|
|
795
814
|
'color': palette.dark.base,
|
|
796
815
|
'background-color': palette.surface || '#fff',
|
|
797
816
|
'border-color': palette.light.border
|
|
798
817
|
};
|
|
799
|
-
rules[
|
|
818
|
+
rules[_sx(scope, '.bw_form_control:focus')] = {
|
|
800
819
|
'border-color': palette.primary.border,
|
|
801
820
|
'outline': '2px solid ' + palette.primary.base,
|
|
802
821
|
'outline-offset': '-1px',
|
|
803
822
|
'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus
|
|
804
823
|
};
|
|
805
|
-
rules[
|
|
824
|
+
rules[_sx(scope, '.bw_form_control::placeholder')] = {
|
|
806
825
|
'color': palette.secondary.base
|
|
807
826
|
};
|
|
808
|
-
rules[
|
|
827
|
+
rules[_sx(scope, '.bw_form_label')] = {
|
|
809
828
|
'color': palette.dark.base
|
|
810
829
|
};
|
|
811
|
-
rules[
|
|
830
|
+
rules[_sx(scope, '.bw_form_text')] = {
|
|
812
831
|
'color': palette.secondary.base
|
|
813
832
|
};
|
|
814
|
-
rules[
|
|
833
|
+
rules[_sx(scope, '.bw_form_check_input:checked')] = {
|
|
815
834
|
'background-color': palette.primary.base,
|
|
816
835
|
'border-color': palette.primary.base
|
|
817
836
|
};
|
|
818
|
-
rules[
|
|
837
|
+
rules[_sx(scope, '.bw_form_check_input:focus')] = {
|
|
819
838
|
'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus
|
|
820
839
|
};
|
|
821
840
|
// Validation states
|
|
822
|
-
rules[
|
|
823
|
-
rules[
|
|
841
|
+
rules[_sx(scope, '.bw_form_control.bw_is_valid')] = { 'border-color': palette.success.base };
|
|
842
|
+
rules[_sx(scope, '.bw_form_control.bw_is_valid:focus')] = {
|
|
824
843
|
'border-color': palette.success.base,
|
|
825
844
|
'box-shadow': '0 0 0 0.2rem ' + palette.success.focus
|
|
826
845
|
};
|
|
827
|
-
rules[
|
|
828
|
-
rules[
|
|
846
|
+
rules[_sx(scope, '.bw_form_control.bw_is_invalid')] = { 'border-color': palette.danger.base };
|
|
847
|
+
rules[_sx(scope, '.bw_form_control.bw_is_invalid:focus')] = {
|
|
829
848
|
'border-color': palette.danger.base,
|
|
830
849
|
'box-shadow': '0 0 0 0.2rem ' + palette.danger.focus
|
|
831
850
|
};
|
|
832
851
|
// Form select
|
|
833
|
-
rules[
|
|
852
|
+
rules[_sx(scope, '.bw_form_select')] = {
|
|
834
853
|
'padding': sp.input,
|
|
835
854
|
'border-radius': rd.input,
|
|
836
855
|
'color': palette.dark.base,
|
|
837
856
|
'background-color': palette.surface || '#fff',
|
|
838
857
|
'border-color': palette.light.border
|
|
839
858
|
};
|
|
840
|
-
rules[
|
|
859
|
+
rules[_sx(scope, '.bw_form_select:focus')] = {
|
|
841
860
|
'border-color': palette.primary.border,
|
|
842
861
|
'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus
|
|
843
862
|
};
|
|
@@ -845,43 +864,46 @@ function generateForms(scope, palette, layout) {
|
|
|
845
864
|
return rules;
|
|
846
865
|
}
|
|
847
866
|
|
|
848
|
-
function generateNavigation(scope, palette) {
|
|
867
|
+
function generateNavigation(scope, palette, layout) {
|
|
849
868
|
var rules = {};
|
|
850
|
-
rules[
|
|
851
|
-
'background-color': palette.
|
|
869
|
+
rules[_sx(scope, '.bw_navbar')] = {
|
|
870
|
+
'background-color': palette.surfaceAlt,
|
|
852
871
|
'border-bottom-color': palette.light.border
|
|
853
872
|
};
|
|
854
|
-
rules[
|
|
873
|
+
rules[_sx(scope, '.bw_navbar_brand')] = {
|
|
855
874
|
'color': palette.dark.base
|
|
856
875
|
};
|
|
857
|
-
rules[
|
|
858
|
-
'color': palette.secondary.base
|
|
876
|
+
rules[_sx(scope, '.bw_navbar_nav .bw_nav_link')] = {
|
|
877
|
+
'color': palette.secondary.base,
|
|
878
|
+
'border-radius': layout.radius.btn
|
|
859
879
|
};
|
|
860
|
-
rules[
|
|
861
|
-
'color': palette.dark.base
|
|
880
|
+
rules[_sx(scope, '.bw_navbar_nav .bw_nav_link:hover')] = {
|
|
881
|
+
'color': palette.dark.base,
|
|
882
|
+
'background-color': palette.surfaceAlt
|
|
862
883
|
};
|
|
863
|
-
rules[
|
|
884
|
+
rules[_sx(scope, '.bw_navbar_nav .bw_nav_link.active')] = {
|
|
864
885
|
'color': palette.primary.base,
|
|
865
|
-
'background-color': palette.primary.focus
|
|
886
|
+
'background-color': palette.primary.focus,
|
|
887
|
+
'font-weight': '600'
|
|
866
888
|
};
|
|
867
|
-
rules[
|
|
889
|
+
rules[_sx(scope, '.bw_navbar_dark')] = {
|
|
868
890
|
'background-color': palette.dark.base,
|
|
869
891
|
'border-bottom-color': palette.dark.hover
|
|
870
892
|
};
|
|
871
|
-
rules[
|
|
893
|
+
rules[_sx(scope, '.bw_navbar_dark .bw_navbar_brand')] = {
|
|
872
894
|
'color': palette.light.base
|
|
873
895
|
};
|
|
874
|
-
rules[
|
|
875
|
-
'color':
|
|
896
|
+
rules[_sx(scope, '.bw_navbar_dark .bw_nav_link')] = {
|
|
897
|
+
'color': palette.light.border
|
|
876
898
|
};
|
|
877
|
-
rules[
|
|
878
|
-
'color':
|
|
899
|
+
rules[_sx(scope, '.bw_navbar_dark .bw_nav_link:hover')] = {
|
|
900
|
+
'color': palette.light.base
|
|
879
901
|
};
|
|
880
|
-
rules[
|
|
881
|
-
'color':
|
|
902
|
+
rules[_sx(scope, '.bw_navbar_dark .bw_nav_link.active')] = {
|
|
903
|
+
'color': palette.light.base,
|
|
882
904
|
'font-weight': '600'
|
|
883
905
|
};
|
|
884
|
-
rules[
|
|
906
|
+
rules[_sx(scope, '.bw_nav_pills .bw_nav_link.active')] = {
|
|
885
907
|
'color': palette.primary.textOn,
|
|
886
908
|
'background-color': palette.primary.base
|
|
887
909
|
};
|
|
@@ -892,49 +914,58 @@ function generateTables(scope, palette, layout) {
|
|
|
892
914
|
var rules = {};
|
|
893
915
|
var sp = layout.spacing;
|
|
894
916
|
|
|
895
|
-
rules[
|
|
917
|
+
rules[_sx(scope, '.bw_table')] = {
|
|
896
918
|
'color': palette.dark.base,
|
|
897
919
|
'border-color': palette.light.border
|
|
898
920
|
};
|
|
899
|
-
rules[
|
|
921
|
+
rules[_sx(scope, '.bw_table > :not(caption) > * > *')] = {
|
|
900
922
|
'padding': sp.cell,
|
|
901
923
|
'border-bottom-color': palette.light.border
|
|
902
924
|
};
|
|
903
|
-
rules[
|
|
925
|
+
rules[_sx(scope, '.bw_table > thead > tr > *')] = {
|
|
904
926
|
'color': palette.secondary.base,
|
|
905
927
|
'border-bottom-color': palette.light.border,
|
|
906
|
-
'background-color': palette.
|
|
928
|
+
'background-color': palette.surfaceAlt
|
|
907
929
|
};
|
|
908
|
-
rules[
|
|
909
|
-
'background-color':
|
|
930
|
+
rules[_sx(scope, '.bw_table_striped > tbody > tr:nth-of-type(odd) > *')] = {
|
|
931
|
+
'background-color': palette.surfaceAlt
|
|
910
932
|
};
|
|
911
|
-
rules[
|
|
933
|
+
rules[_sx(scope, '.bw_table_hover > tbody > tr:hover > *')] = {
|
|
912
934
|
'background-color': palette.primary.focus
|
|
913
935
|
};
|
|
914
|
-
rules[
|
|
936
|
+
rules[_sx(scope, '.bw_table_selectable > tbody > tr')] = {
|
|
937
|
+
'cursor': 'pointer'
|
|
938
|
+
};
|
|
939
|
+
rules[_sx(scope, '.bw_table > tbody > tr.bw_table_row_selected > *')] = {
|
|
940
|
+
'background-color': palette.primary.light
|
|
941
|
+
};
|
|
942
|
+
rules[_sx(scope, '.bw_table_bordered')] = {
|
|
915
943
|
'border-color': palette.light.border
|
|
916
944
|
};
|
|
917
|
-
rules[
|
|
945
|
+
rules[_sx(scope, '.bw_table caption')] = {
|
|
918
946
|
'color': palette.secondary.base
|
|
919
947
|
};
|
|
920
948
|
|
|
921
949
|
return rules;
|
|
922
950
|
}
|
|
923
951
|
|
|
924
|
-
function generateTabs(scope, palette) {
|
|
925
|
-
var rules = {};
|
|
926
|
-
rules[
|
|
952
|
+
function generateTabs(scope, palette, layout) {
|
|
953
|
+
var rules = {}, mo = layout.motion;
|
|
954
|
+
rules[_sx(scope, '.bw_nav_tabs')] = {
|
|
927
955
|
'border-bottom-color': palette.light.border
|
|
928
956
|
};
|
|
929
|
-
rules[
|
|
930
|
-
'color': palette.secondary.base
|
|
957
|
+
rules[_sx(scope, '.bw_nav_link')] = {
|
|
958
|
+
'color': palette.secondary.base,
|
|
959
|
+
'transition': 'color ' + mo.fast + ' ' + mo.easing + ', border-color ' + mo.fast + ' ' + mo.easing + ', background-color ' + mo.fast + ' ' + mo.easing
|
|
931
960
|
};
|
|
932
|
-
rules[
|
|
961
|
+
rules[_sx(scope, '.bw_nav_tabs .bw_nav_link:hover')] = {
|
|
933
962
|
'color': palette.dark.base,
|
|
963
|
+
'background-color': palette.surfaceAlt,
|
|
934
964
|
'border-bottom-color': palette.light.border
|
|
935
965
|
};
|
|
936
|
-
rules[
|
|
966
|
+
rules[_sx(scope, '.bw_nav_tabs .bw_nav_link.active')] = {
|
|
937
967
|
'color': palette.primary.base,
|
|
968
|
+
'background-color': palette.primary.focus,
|
|
938
969
|
'border-bottom': '2px solid ' + palette.primary.base
|
|
939
970
|
};
|
|
940
971
|
return rules;
|
|
@@ -943,23 +974,25 @@ function generateTabs(scope, palette) {
|
|
|
943
974
|
function generateListGroups(scope, palette, layout) {
|
|
944
975
|
var rules = {};
|
|
945
976
|
var sp = layout.spacing;
|
|
977
|
+
var mo = layout.motion;
|
|
946
978
|
|
|
947
|
-
rules[
|
|
979
|
+
rules[_sx(scope, '.bw_list_group_item')] = {
|
|
948
980
|
'padding': sp.cell,
|
|
949
981
|
'color': palette.dark.base,
|
|
950
982
|
'background-color': palette.surface || '#fff',
|
|
951
|
-
'border-color': palette.light.border
|
|
983
|
+
'border-color': palette.light.border,
|
|
984
|
+
'transition': 'color ' + mo.fast + ' ' + mo.easing + ', background-color ' + mo.fast + ' ' + mo.easing
|
|
952
985
|
};
|
|
953
|
-
rules[
|
|
954
|
-
'background-color': palette.
|
|
986
|
+
rules[_sx(scope, 'a.bw_list_group_item:hover')] = {
|
|
987
|
+
'background-color': palette.surfaceAlt,
|
|
955
988
|
'color': palette.dark.hover
|
|
956
989
|
};
|
|
957
|
-
rules[
|
|
990
|
+
rules[_sx(scope, '.bw_list_group_item.active')] = {
|
|
958
991
|
'color': palette.primary.textOn,
|
|
959
992
|
'background-color': palette.primary.base,
|
|
960
993
|
'border-color': palette.primary.base
|
|
961
994
|
};
|
|
962
|
-
rules[
|
|
995
|
+
rules[_sx(scope, '.bw_list_group_item.disabled')] = {
|
|
963
996
|
'color': palette.secondary.base,
|
|
964
997
|
'background-color': palette.surface || '#fff'
|
|
965
998
|
};
|
|
@@ -967,28 +1000,37 @@ function generateListGroups(scope, palette, layout) {
|
|
|
967
1000
|
return rules;
|
|
968
1001
|
}
|
|
969
1002
|
|
|
970
|
-
function generatePagination(scope, palette) {
|
|
971
|
-
var rules = {};
|
|
972
|
-
rules[
|
|
1003
|
+
function generatePagination(scope, palette, layout) {
|
|
1004
|
+
var rules = {}, mo = layout.motion, rd = layout.radius;
|
|
1005
|
+
rules[_sx(scope, '.bw_page_item:first-child .bw_page_link')] = {
|
|
1006
|
+
'border-top-left-radius': rd.btn,
|
|
1007
|
+
'border-bottom-left-radius': rd.btn
|
|
1008
|
+
};
|
|
1009
|
+
rules[_sx(scope, '.bw_page_item:last-child .bw_page_link')] = {
|
|
1010
|
+
'border-top-right-radius': rd.btn,
|
|
1011
|
+
'border-bottom-right-radius': rd.btn
|
|
1012
|
+
};
|
|
1013
|
+
rules[_sx(scope, '.bw_page_link')] = {
|
|
973
1014
|
'color': palette.primary.base,
|
|
974
1015
|
'background-color': palette.surface || '#fff',
|
|
975
|
-
'border-color': palette.light.border
|
|
1016
|
+
'border-color': palette.light.border,
|
|
1017
|
+
'transition': 'color ' + mo.fast + ' ' + mo.easing + ', background-color ' + mo.fast + ' ' + mo.easing
|
|
976
1018
|
};
|
|
977
|
-
rules[
|
|
1019
|
+
rules[_sx(scope, '.bw_page_link:hover')] = {
|
|
978
1020
|
'color': palette.primary.hover,
|
|
979
|
-
'background-color': palette.
|
|
1021
|
+
'background-color': palette.surfaceAlt,
|
|
980
1022
|
'border-color': palette.light.border
|
|
981
1023
|
};
|
|
982
|
-
rules[
|
|
1024
|
+
rules[_sx(scope, '.bw_page_link:focus')] = {
|
|
983
1025
|
'outline': '2px solid ' + palette.primary.base,
|
|
984
1026
|
'outline-offset': '-2px'
|
|
985
1027
|
};
|
|
986
|
-
rules[
|
|
1028
|
+
rules[_sx(scope, '.bw_page_item.bw_active .bw_page_link')] = {
|
|
987
1029
|
'color': palette.primary.textOn,
|
|
988
1030
|
'background-color': palette.primary.base,
|
|
989
1031
|
'border-color': palette.primary.base
|
|
990
1032
|
};
|
|
991
|
-
rules[
|
|
1033
|
+
rules[_sx(scope, '.bw_page_item.bw_disabled .bw_page_link')] = {
|
|
992
1034
|
'color': palette.secondary.base,
|
|
993
1035
|
'background-color': palette.surface || '#fff',
|
|
994
1036
|
'border-color': palette.light.border
|
|
@@ -998,12 +1040,12 @@ function generatePagination(scope, palette) {
|
|
|
998
1040
|
|
|
999
1041
|
function generateProgress(scope, palette) {
|
|
1000
1042
|
var rules = {};
|
|
1001
|
-
rules[
|
|
1002
|
-
'background-color': palette.
|
|
1043
|
+
rules[_sx(scope, '.bw_progress')] = {
|
|
1044
|
+
'background-color': palette.surfaceAlt,
|
|
1003
1045
|
'box-shadow': 'inset 0 1px 2px rgba(0,0,0,.1)'
|
|
1004
1046
|
};
|
|
1005
|
-
rules[
|
|
1006
|
-
'color':
|
|
1047
|
+
rules[_sx(scope, '.bw_progress_bar')] = {
|
|
1048
|
+
'color': palette.primary.textOn,
|
|
1007
1049
|
'background-color': palette.primary.base,
|
|
1008
1050
|
'box-shadow': 'inset 0 -1px 0 rgba(0,0,0,.15)'
|
|
1009
1051
|
};
|
|
@@ -1022,26 +1064,31 @@ function generateResetThemed(scope, palette) {
|
|
|
1022
1064
|
'color': palette.dark.base,
|
|
1023
1065
|
'background-color': bg
|
|
1024
1066
|
};
|
|
1025
|
-
rules[
|
|
1026
|
-
// Also apply to the scope element itself so themes work on any container, not just body
|
|
1027
|
-
if (scope) {
|
|
1028
|
-
rules['.' + scope] = baseReset;
|
|
1029
|
-
}
|
|
1067
|
+
rules[_sx(scope, 'body')] = baseReset;
|
|
1030
1068
|
return rules;
|
|
1031
1069
|
}
|
|
1032
1070
|
|
|
1033
|
-
function generateBreadcrumbThemed(scope, palette) {
|
|
1034
|
-
var rules = {};
|
|
1035
|
-
rules[
|
|
1036
|
-
'color': palette.
|
|
1071
|
+
function generateBreadcrumbThemed(scope, palette, layout) {
|
|
1072
|
+
var rules = {}, mo = layout.motion;
|
|
1073
|
+
rules[_sx(scope, '.bw_breadcrumb')] = {
|
|
1074
|
+
'background-color': palette.surfaceAlt,
|
|
1075
|
+
'padding': '0.625rem 1rem',
|
|
1076
|
+
'border-radius': layout.radius.btn
|
|
1037
1077
|
};
|
|
1038
|
-
rules[
|
|
1078
|
+
rules[_sx(scope, '.bw_breadcrumb_item + .bw_breadcrumb_item::before')] = {
|
|
1039
1079
|
'color': palette.secondary.base
|
|
1040
1080
|
};
|
|
1041
|
-
rules[
|
|
1081
|
+
rules[_sx(scope, '.bw_breadcrumb_item a')] = {
|
|
1082
|
+
'color': palette.primary.base,
|
|
1083
|
+
'transition': 'color ' + mo.fast + ' ' + mo.easing
|
|
1084
|
+
};
|
|
1085
|
+
rules[_sx(scope, '.bw_breadcrumb_item a:hover')] = {
|
|
1042
1086
|
'color': palette.primary.hover,
|
|
1043
1087
|
'text-decoration': 'underline'
|
|
1044
1088
|
};
|
|
1089
|
+
rules[_sx(scope, '.bw_breadcrumb_item.active')] = {
|
|
1090
|
+
'color': palette.dark.base
|
|
1091
|
+
};
|
|
1045
1092
|
return rules;
|
|
1046
1093
|
}
|
|
1047
1094
|
|
|
@@ -1049,11 +1096,11 @@ function generateBreadcrumbThemed(scope, palette) {
|
|
|
1049
1096
|
|
|
1050
1097
|
function generateCloseButtonThemed(scope, palette) {
|
|
1051
1098
|
var rules = {};
|
|
1052
|
-
rules[
|
|
1099
|
+
rules[_sx(scope, '.bw_close')] = {
|
|
1053
1100
|
'color': palette.dark.base,
|
|
1054
1101
|
'opacity': '0.5'
|
|
1055
1102
|
};
|
|
1056
|
-
rules[
|
|
1103
|
+
rules[_sx(scope, '.bw_close:focus')] = {
|
|
1057
1104
|
'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus
|
|
1058
1105
|
};
|
|
1059
1106
|
return rules;
|
|
@@ -1061,82 +1108,94 @@ function generateCloseButtonThemed(scope, palette) {
|
|
|
1061
1108
|
|
|
1062
1109
|
function generateSectionsThemed(scope, palette) {
|
|
1063
1110
|
var rules = {};
|
|
1064
|
-
rules[
|
|
1111
|
+
rules[_sx(scope, '.bw_section_subtitle')] = {
|
|
1065
1112
|
'color': palette.secondary.base
|
|
1066
1113
|
};
|
|
1067
|
-
rules[
|
|
1114
|
+
rules[_sx(scope, '.bw_feature_description')] = {
|
|
1068
1115
|
'color': palette.secondary.base
|
|
1069
1116
|
};
|
|
1070
|
-
rules[
|
|
1117
|
+
rules[_sx(scope, '.bw_cta_description')] = {
|
|
1071
1118
|
'color': palette.secondary.base
|
|
1072
1119
|
};
|
|
1073
1120
|
return rules;
|
|
1074
1121
|
}
|
|
1075
1122
|
|
|
1076
|
-
function generateAccordionThemed(scope, palette) {
|
|
1123
|
+
function generateAccordionThemed(scope, palette, layout) {
|
|
1077
1124
|
var rules = {};
|
|
1078
|
-
|
|
1125
|
+
var rd = layout ? layout.radius : { card: '8px' };
|
|
1126
|
+
rules[_sx(scope, '.bw_accordion_item')] = {
|
|
1079
1127
|
'background-color': palette.surface || '#fff',
|
|
1080
1128
|
'border-color': palette.light.border
|
|
1081
1129
|
};
|
|
1082
|
-
rules[
|
|
1130
|
+
rules[_sx(scope, '.bw_accordion_item:first-child')] = {
|
|
1131
|
+
'border-top-left-radius': rd.card,
|
|
1132
|
+
'border-top-right-radius': rd.card
|
|
1133
|
+
};
|
|
1134
|
+
rules[_sx(scope, '.bw_accordion_item:last-child')] = {
|
|
1135
|
+
'border-bottom-left-radius': rd.card,
|
|
1136
|
+
'border-bottom-right-radius': rd.card
|
|
1137
|
+
};
|
|
1138
|
+
rules[_sx(scope, '.bw_accordion_button')] = {
|
|
1083
1139
|
'color': palette.dark.base
|
|
1084
1140
|
};
|
|
1085
|
-
rules[
|
|
1141
|
+
rules[_sx(scope, '.bw_accordion_button:not(.bw_collapsed)')] = {
|
|
1086
1142
|
'color': palette.primary.darkText,
|
|
1087
|
-
'background-color': palette.primary.light
|
|
1143
|
+
'background-color': palette.primary.light,
|
|
1144
|
+
'border-left': '3px solid ' + palette.primary.base
|
|
1088
1145
|
};
|
|
1089
|
-
rules[
|
|
1090
|
-
'background-color': palette.
|
|
1146
|
+
rules[_sx(scope, '.bw_accordion_button:hover')] = {
|
|
1147
|
+
'background-color': palette.surfaceAlt
|
|
1091
1148
|
};
|
|
1092
|
-
rules[
|
|
1093
|
-
'background-color': palette.primary.
|
|
1149
|
+
rules[_sx(scope, '.bw_accordion_button:not(.bw_collapsed):hover')] = {
|
|
1150
|
+
'background-color': palette.primary.base,
|
|
1151
|
+
'color': palette.primary.textOn
|
|
1094
1152
|
};
|
|
1095
|
-
rules[
|
|
1153
|
+
rules[_sx(scope, '.bw_accordion_button:focus-visible')] = {
|
|
1096
1154
|
'box-shadow': '0 0 0 0.2rem ' + palette.primary.focus
|
|
1097
1155
|
};
|
|
1098
|
-
rules[
|
|
1099
|
-
'border-top': '1px solid ' + palette.light.border
|
|
1156
|
+
rules[_sx(scope, '.bw_accordion_body')] = {
|
|
1157
|
+
'border-top': '1px solid ' + palette.light.border,
|
|
1158
|
+
'background-color': palette.surfaceAlt
|
|
1100
1159
|
};
|
|
1101
1160
|
return rules;
|
|
1102
1161
|
}
|
|
1103
1162
|
|
|
1104
1163
|
function generateCarouselThemed(scope, palette) {
|
|
1105
1164
|
var rules = {};
|
|
1106
|
-
rules[
|
|
1107
|
-
'background-color': palette.
|
|
1165
|
+
rules[_sx(scope, '.bw_carousel')] = {
|
|
1166
|
+
'background-color': palette.surfaceAlt
|
|
1108
1167
|
};
|
|
1109
|
-
rules[
|
|
1168
|
+
rules[_sx(scope, '.bw_carousel_indicator.active')] = {
|
|
1110
1169
|
'background-color': palette.primary.base
|
|
1111
1170
|
};
|
|
1112
|
-
rules[
|
|
1113
|
-
'background-color':
|
|
1114
|
-
'color':
|
|
1171
|
+
rules[_sx(scope, '.bw_carousel_control')] = {
|
|
1172
|
+
'background-color': palette.dark.base,
|
|
1173
|
+
'color': palette.dark.textOn
|
|
1115
1174
|
};
|
|
1116
|
-
rules[
|
|
1117
|
-
'background-color':
|
|
1175
|
+
rules[_sx(scope, '.bw_carousel_control:hover')] = {
|
|
1176
|
+
'background-color': palette.dark.hover
|
|
1118
1177
|
};
|
|
1119
|
-
rules[
|
|
1120
|
-
'background': 'linear-gradient(transparent,
|
|
1121
|
-
'color':
|
|
1178
|
+
rules[_sx(scope, '.bw_carousel_caption')] = {
|
|
1179
|
+
'background': 'linear-gradient(transparent, ' + palette.dark.base + ')',
|
|
1180
|
+
'color': palette.dark.textOn
|
|
1122
1181
|
};
|
|
1123
1182
|
return rules;
|
|
1124
1183
|
}
|
|
1125
1184
|
|
|
1126
1185
|
function generateModalThemed(scope, palette, layout) {
|
|
1127
1186
|
var rules = {};
|
|
1128
|
-
rules[
|
|
1187
|
+
rules[_sx(scope, '.bw_modal_content')] = {
|
|
1129
1188
|
'background-color': palette.surface || '#fff',
|
|
1130
1189
|
'border-color': palette.light.border,
|
|
1131
1190
|
'box-shadow': layout.elevation.lg
|
|
1132
1191
|
};
|
|
1133
|
-
rules[
|
|
1192
|
+
rules[_sx(scope, '.bw_modal_header')] = {
|
|
1134
1193
|
'border-bottom-color': palette.light.border
|
|
1135
1194
|
};
|
|
1136
|
-
rules[
|
|
1195
|
+
rules[_sx(scope, '.bw_modal_footer')] = {
|
|
1137
1196
|
'border-top-color': palette.light.border
|
|
1138
1197
|
};
|
|
1139
|
-
rules[
|
|
1198
|
+
rules[_sx(scope, '.bw_modal_title')] = {
|
|
1140
1199
|
'color': palette.dark.base
|
|
1141
1200
|
};
|
|
1142
1201
|
return rules;
|
|
@@ -1144,13 +1203,13 @@ function generateModalThemed(scope, palette, layout) {
|
|
|
1144
1203
|
|
|
1145
1204
|
function generateToastThemed(scope, palette, layout) {
|
|
1146
1205
|
var rules = {};
|
|
1147
|
-
rules[
|
|
1206
|
+
rules[_sx(scope, '.bw_toast')] = {
|
|
1148
1207
|
'background-color': palette.surface || '#fff',
|
|
1149
|
-
'border-color':
|
|
1208
|
+
'border-color': palette.light.border,
|
|
1150
1209
|
'box-shadow': layout.elevation.lg
|
|
1151
1210
|
};
|
|
1152
|
-
rules[
|
|
1153
|
-
'border-bottom-color':
|
|
1211
|
+
rules[_sx(scope, '.bw_toast_header')] = {
|
|
1212
|
+
'border-bottom-color': palette.light.border
|
|
1154
1213
|
};
|
|
1155
1214
|
// Variant toast borders handled by palette class
|
|
1156
1215
|
return rules;
|
|
@@ -1158,22 +1217,23 @@ function generateToastThemed(scope, palette, layout) {
|
|
|
1158
1217
|
|
|
1159
1218
|
function generateDropdownThemed(scope, palette, layout) {
|
|
1160
1219
|
var rules = {};
|
|
1161
|
-
rules[
|
|
1220
|
+
rules[_sx(scope, '.bw_dropdown_menu')] = {
|
|
1162
1221
|
'background-color': palette.surface || '#fff',
|
|
1163
1222
|
'border-color': palette.light.border,
|
|
1164
1223
|
'box-shadow': layout.elevation.md
|
|
1165
1224
|
};
|
|
1166
|
-
rules[
|
|
1167
|
-
'color': palette.dark.base
|
|
1225
|
+
rules[_sx(scope, '.bw_dropdown_item')] = {
|
|
1226
|
+
'color': palette.dark.base,
|
|
1227
|
+
'transition': 'background-color ' + layout.motion.fast + ' ' + layout.motion.easing
|
|
1168
1228
|
};
|
|
1169
|
-
rules[
|
|
1229
|
+
rules[_sx(scope, '.bw_dropdown_item:hover')] = {
|
|
1170
1230
|
'color': palette.dark.hover,
|
|
1171
|
-
'background-color': palette.
|
|
1231
|
+
'background-color': palette.surfaceAlt
|
|
1172
1232
|
};
|
|
1173
|
-
rules[
|
|
1233
|
+
rules[_sx(scope, '.bw_dropdown_item.disabled')] = {
|
|
1174
1234
|
'color': palette.secondary.base
|
|
1175
1235
|
};
|
|
1176
|
-
rules[
|
|
1236
|
+
rules[_sx(scope, '.bw_dropdown_divider')] = {
|
|
1177
1237
|
'border-top-color': palette.light.border
|
|
1178
1238
|
};
|
|
1179
1239
|
return rules;
|
|
@@ -1181,15 +1241,15 @@ function generateDropdownThemed(scope, palette, layout) {
|
|
|
1181
1241
|
|
|
1182
1242
|
function generateSwitchThemed(scope, palette) {
|
|
1183
1243
|
var rules = {};
|
|
1184
|
-
rules[
|
|
1244
|
+
rules[_sx(scope, '.bw_form_switch .bw_switch_input')] = {
|
|
1185
1245
|
'background-color': palette.secondary.base,
|
|
1186
1246
|
'border-color': palette.secondary.base
|
|
1187
1247
|
};
|
|
1188
|
-
rules[
|
|
1248
|
+
rules[_sx(scope, '.bw_form_switch .bw_switch_input:checked')] = {
|
|
1189
1249
|
'background-color': palette.primary.base,
|
|
1190
1250
|
'border-color': palette.primary.base
|
|
1191
1251
|
};
|
|
1192
|
-
rules[
|
|
1252
|
+
rules[_sx(scope, '.bw_form_switch .bw_switch_input:focus')] = {
|
|
1193
1253
|
'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus
|
|
1194
1254
|
};
|
|
1195
1255
|
return rules;
|
|
@@ -1197,88 +1257,102 @@ function generateSwitchThemed(scope, palette) {
|
|
|
1197
1257
|
|
|
1198
1258
|
function generateSkeletonThemed(scope, palette) {
|
|
1199
1259
|
var rules = {};
|
|
1200
|
-
rules[
|
|
1201
|
-
'background': 'linear-gradient(90deg, ' + palette.light.border + ' 25%, ' + palette.
|
|
1260
|
+
rules[_sx(scope, '.bw_skeleton')] = {
|
|
1261
|
+
'background': 'linear-gradient(90deg, ' + palette.light.border + ' 25%, ' + palette.surfaceAlt + ' 37%, ' + palette.light.border + ' 63%)'
|
|
1202
1262
|
};
|
|
1203
1263
|
return rules;
|
|
1204
1264
|
}
|
|
1205
1265
|
|
|
1206
1266
|
// generateAvatarThemed: removed — palette class on root handles variants
|
|
1207
1267
|
|
|
1208
|
-
function generateStatCardThemed(scope, palette) {
|
|
1209
|
-
var rules = {};
|
|
1268
|
+
function generateStatCardThemed(scope, palette, layout) {
|
|
1269
|
+
var rules = {}, mo = layout.motion, el = layout.elevation, rd = layout.radius;
|
|
1270
|
+
rules[_sx(scope, '.bw_stat_card')] = {
|
|
1271
|
+
'background-color': palette.surface || '#fff',
|
|
1272
|
+
'color': palette.dark.base,
|
|
1273
|
+
'border': '1px solid ' + palette.light.border,
|
|
1274
|
+
'border-radius': rd.card,
|
|
1275
|
+
'box-shadow': el.sm,
|
|
1276
|
+
'transition': 'box-shadow ' + mo.fast + ' ' + mo.easing + ', transform ' + mo.fast + ' ' + mo.easing
|
|
1277
|
+
};
|
|
1278
|
+
rules[_sx(scope, '.bw_stat_card:hover')] = { 'box-shadow': el.md };
|
|
1210
1279
|
// Variant border colors handled by palette class
|
|
1211
|
-
rules[
|
|
1212
|
-
rules[
|
|
1280
|
+
rules[_sx(scope, '.bw_stat_change_up')] = { 'color': palette.success.base };
|
|
1281
|
+
rules[_sx(scope, '.bw_stat_change_down')] = { 'color': palette.danger.base };
|
|
1213
1282
|
return rules;
|
|
1214
1283
|
}
|
|
1215
1284
|
|
|
1216
1285
|
function generateTimelineThemed(scope, palette) {
|
|
1217
1286
|
var rules = {};
|
|
1218
|
-
rules[
|
|
1287
|
+
rules[_sx(scope, '.bw_timeline::before')] = { 'background-color': palette.light.border };
|
|
1219
1288
|
// Variant marker colors handled by palette class
|
|
1220
|
-
rules[
|
|
1289
|
+
rules[_sx(scope, '.bw_timeline_date')] = { 'color': palette.secondary.base };
|
|
1221
1290
|
return rules;
|
|
1222
1291
|
}
|
|
1223
1292
|
|
|
1224
1293
|
function generateStepperThemed(scope, palette) {
|
|
1225
1294
|
var rules = {};
|
|
1226
|
-
rules[
|
|
1227
|
-
'background-color': palette.
|
|
1295
|
+
rules[_sx(scope, '.bw_step_indicator')] = {
|
|
1296
|
+
'background-color': palette.surfaceAlt,
|
|
1228
1297
|
'border': '2px solid ' + palette.light.border,
|
|
1229
1298
|
'color': palette.secondary.base
|
|
1230
1299
|
};
|
|
1231
|
-
rules[
|
|
1232
|
-
rules[
|
|
1300
|
+
rules[_sx(scope, '.bw_step + .bw_step::before')] = { 'background-color': palette.light.border };
|
|
1301
|
+
rules[_sx(scope, '.bw_step_active .bw_step_indicator')] = {
|
|
1233
1302
|
'background-color': palette.primary.base,
|
|
1234
1303
|
'color': palette.primary.textOn
|
|
1235
1304
|
};
|
|
1236
|
-
rules[
|
|
1305
|
+
rules[_sx(scope, '.bw_step_active .bw_step_label')] = {
|
|
1237
1306
|
'color': palette.dark.base,
|
|
1238
1307
|
'font-weight': '600'
|
|
1239
1308
|
};
|
|
1240
|
-
rules[
|
|
1309
|
+
rules[_sx(scope, '.bw_step_completed .bw_step_indicator')] = {
|
|
1241
1310
|
'background-color': palette.primary.base,
|
|
1242
1311
|
'color': palette.primary.textOn
|
|
1243
1312
|
};
|
|
1244
|
-
rules[
|
|
1245
|
-
rules[
|
|
1313
|
+
rules[_sx(scope, '.bw_step_completed .bw_step_label')] = { 'color': palette.primary.base };
|
|
1314
|
+
rules[_sx(scope, '.bw_step_completed + .bw_step::before')] = { 'background-color': palette.primary.base };
|
|
1246
1315
|
return rules;
|
|
1247
1316
|
}
|
|
1248
1317
|
|
|
1249
1318
|
function generateChipInputThemed(scope, palette) {
|
|
1250
1319
|
var rules = {};
|
|
1251
|
-
rules[
|
|
1252
|
-
|
|
1320
|
+
rules[_sx(scope, '.bw_chip_input')] = {
|
|
1321
|
+
'border-color': palette.light.border,
|
|
1322
|
+
'background-color': palette.surface || '#fff',
|
|
1323
|
+
'color': palette.dark.base
|
|
1324
|
+
};
|
|
1325
|
+
rules[_sx(scope, '.bw_chip_input:focus-within')] = {
|
|
1253
1326
|
'border-color': palette.primary.base,
|
|
1254
1327
|
'box-shadow': '0 0 0 0.2rem ' + palette.primary.focus
|
|
1255
1328
|
};
|
|
1256
|
-
rules[
|
|
1257
|
-
'background-color': palette.
|
|
1329
|
+
rules[_sx(scope, '.bw_chip')] = {
|
|
1330
|
+
'background-color': palette.surfaceAlt,
|
|
1258
1331
|
'color': palette.dark.base
|
|
1259
1332
|
};
|
|
1260
|
-
rules[
|
|
1333
|
+
rules[_sx(scope, '.bw_chip_remove:hover')] = {
|
|
1261
1334
|
'color': palette.danger.base,
|
|
1262
1335
|
'background-color': palette.danger.light
|
|
1263
1336
|
};
|
|
1264
1337
|
return rules;
|
|
1265
1338
|
}
|
|
1266
1339
|
|
|
1267
|
-
function generateFileUploadThemed(scope, palette) {
|
|
1268
|
-
var rules = {};
|
|
1269
|
-
rules[
|
|
1340
|
+
function generateFileUploadThemed(scope, palette, layout) {
|
|
1341
|
+
var rules = {}, mo = layout.motion;
|
|
1342
|
+
rules[_sx(scope, '.bw_file_upload')] = {
|
|
1270
1343
|
'border-color': palette.light.border,
|
|
1271
|
-
'background-color': palette.
|
|
1344
|
+
'background-color': palette.surfaceAlt,
|
|
1345
|
+
'transition': 'border-color ' + mo.fast + ' ' + mo.easing + ', background-color ' + mo.fast + ' ' + mo.easing
|
|
1272
1346
|
};
|
|
1273
|
-
rules[
|
|
1347
|
+
rules[_sx(scope, '.bw_file_upload:hover')] = {
|
|
1274
1348
|
'border-color': palette.primary.base,
|
|
1275
1349
|
'background-color': palette.primary.light
|
|
1276
1350
|
};
|
|
1277
|
-
rules[
|
|
1351
|
+
rules[_sx(scope, '.bw_file_upload:focus')] = {
|
|
1278
1352
|
'outline': '2px solid ' + palette.primary.base,
|
|
1279
1353
|
'outline-offset': '2px'
|
|
1280
1354
|
};
|
|
1281
|
-
rules[
|
|
1355
|
+
rules[_sx(scope, '.bw_file_upload.bw_file_upload_active')] = {
|
|
1282
1356
|
'border-color': palette.primary.base,
|
|
1283
1357
|
'background-color': palette.primary.light,
|
|
1284
1358
|
'border-style': 'solid'
|
|
@@ -1288,35 +1362,73 @@ function generateFileUploadThemed(scope, palette) {
|
|
|
1288
1362
|
|
|
1289
1363
|
function generateRangeThemed(scope, palette) {
|
|
1290
1364
|
var rules = {};
|
|
1291
|
-
rules[
|
|
1292
|
-
rules[
|
|
1365
|
+
rules[_sx(scope, '.bw_range')] = { 'background-color': palette.light.border };
|
|
1366
|
+
rules[_sx(scope, '.bw_range::-webkit-slider-thumb')] = {
|
|
1293
1367
|
'background-color': palette.primary.base,
|
|
1294
|
-
'border-color': '#fff',
|
|
1368
|
+
'border-color': palette.surface || '#fff',
|
|
1295
1369
|
'box-shadow': '0 1px 3px rgba(0,0,0,0.2)',
|
|
1296
1370
|
'transition': 'background-color 0.15s ease-out, transform 0.15s ease-out'
|
|
1297
1371
|
};
|
|
1298
|
-
rules[
|
|
1372
|
+
rules[_sx(scope, '.bw_range::-moz-range-thumb')] = {
|
|
1299
1373
|
'background-color': palette.primary.base,
|
|
1300
|
-
'border-color': '#fff',
|
|
1374
|
+
'border-color': palette.surface || '#fff',
|
|
1301
1375
|
'box-shadow': '0 1px 3px rgba(0,0,0,0.2)'
|
|
1302
1376
|
};
|
|
1303
1377
|
return rules;
|
|
1304
1378
|
}
|
|
1305
1379
|
|
|
1306
|
-
function
|
|
1307
|
-
var rules = {};
|
|
1308
|
-
rules[
|
|
1380
|
+
function generateTooltipThemed(scope, palette, layout) {
|
|
1381
|
+
var rules = {}, sp = layout.spacing, rd = layout.radius, el = layout.elevation, mo = layout.motion;
|
|
1382
|
+
rules[_sx(scope, '.bw_tooltip')] = {
|
|
1383
|
+
'background-color': palette.dark.base, 'color': palette.dark.textOn,
|
|
1384
|
+
'padding': sp.input, 'border-radius': rd.badge, 'box-shadow': el.md,
|
|
1385
|
+
'transition': 'opacity ' + mo.fast + ' ' + mo.easing + ', transform ' + mo.fast + ' ' + mo.easing
|
|
1386
|
+
};
|
|
1387
|
+
return rules;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
function generatePopoverThemed(scope, palette, layout) {
|
|
1391
|
+
var rules = {}, sp = layout.spacing, rd = layout.radius, el = layout.elevation, mo = layout.motion;
|
|
1392
|
+
rules[_sx(scope, '.bw_popover')] = {
|
|
1393
|
+
'background-color': palette.surface || '#fff', 'color': palette.dark.base,
|
|
1394
|
+
'border': '1px solid ' + palette.light.border, 'border-radius': rd.card, 'box-shadow': el.lg,
|
|
1395
|
+
'transition': 'opacity ' + mo.fast + ' ' + mo.easing + ', transform ' + mo.fast + ' ' + mo.easing
|
|
1396
|
+
};
|
|
1397
|
+
rules[_sx(scope, '.bw_popover_header')] = {
|
|
1398
|
+
'background-color': palette.surfaceAlt, 'border-bottom': '1px solid ' + palette.light.border,
|
|
1399
|
+
'padding': sp.input
|
|
1400
|
+
};
|
|
1401
|
+
rules[_sx(scope, '.bw_popover_body')] = { 'padding': sp.card };
|
|
1309
1402
|
return rules;
|
|
1310
1403
|
}
|
|
1311
1404
|
|
|
1312
|
-
function
|
|
1405
|
+
function generateSearchThemed(scope, palette, layout) {
|
|
1406
|
+
var rules = {}, mo = layout.motion;
|
|
1407
|
+
rules[_sx(scope, '.bw_search_input')] = {
|
|
1408
|
+
'background-color': palette.surface || '#fff',
|
|
1409
|
+
'color': palette.dark.base
|
|
1410
|
+
};
|
|
1411
|
+
rules[_sx(scope, '.bw_search_clear')] = {
|
|
1412
|
+
'transition': 'color ' + mo.fast + ' ' + mo.easing + ', background-color ' + mo.fast + ' ' + mo.easing
|
|
1413
|
+
};
|
|
1414
|
+
rules[_sx(scope, '.bw_search_clear:hover')] = { 'color': palette.dark.base };
|
|
1415
|
+
return rules;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
function generateCodeDemoThemed(scope, palette, layout) {
|
|
1313
1419
|
var rules = {};
|
|
1314
|
-
|
|
1420
|
+
var rd = layout ? layout.radius : { card: '0.375rem' };
|
|
1421
|
+
rules[_sx(scope, '.bw_code_demo')] = {
|
|
1422
|
+
'background-color': palette.surface || '#fff',
|
|
1423
|
+
'color': palette.dark.base,
|
|
1424
|
+
'border-radius': rd.card
|
|
1425
|
+
};
|
|
1426
|
+
rules[_sx(scope, '.bw_code_copy_btn_copied')] = {
|
|
1315
1427
|
'background': palette.success.base,
|
|
1316
1428
|
'color': palette.success.textOn,
|
|
1317
1429
|
'border-color': palette.success.base
|
|
1318
1430
|
};
|
|
1319
|
-
rules[
|
|
1431
|
+
rules[_sx(scope, '.bw_copy_btn:hover')] = {
|
|
1320
1432
|
'background': 'rgba(255,255,255,0.2)',
|
|
1321
1433
|
'color': '#fff'
|
|
1322
1434
|
};
|
|
@@ -1326,7 +1438,7 @@ function generateCodeDemoThemed(scope, palette) {
|
|
|
1326
1438
|
function generateNavPillsThemed(scope, palette, layout) {
|
|
1327
1439
|
var rules = {};
|
|
1328
1440
|
var rd = layout.radius;
|
|
1329
|
-
rules[
|
|
1441
|
+
rules[_sx(scope, '.bw_nav_pills .bw_nav_link')] = { 'border-radius': rd.btn };
|
|
1330
1442
|
return rules;
|
|
1331
1443
|
}
|
|
1332
1444
|
|
|
@@ -1352,21 +1464,21 @@ function generatePaletteClasses(scope, palette) {
|
|
|
1352
1464
|
var s = palette[k];
|
|
1353
1465
|
|
|
1354
1466
|
// --- Root palette class: sets default bg/color/border ---
|
|
1355
|
-
rules[
|
|
1467
|
+
rules[_sx(scope, '.bw_' + k)] = {
|
|
1356
1468
|
'background-color': s.base,
|
|
1357
1469
|
'color': s.textOn,
|
|
1358
1470
|
'border-color': s.base
|
|
1359
1471
|
};
|
|
1360
1472
|
|
|
1361
1473
|
// --- Pseudo-states (shared across all components) ---
|
|
1362
|
-
rules[
|
|
1474
|
+
rules[_sx(scope, '.bw_' + k + ':hover')] = {
|
|
1363
1475
|
'background-color': s.hover,
|
|
1364
1476
|
'border-color': s.active
|
|
1365
1477
|
};
|
|
1366
|
-
rules[
|
|
1478
|
+
rules[_sx(scope, '.bw_' + k + ':active')] = {
|
|
1367
1479
|
'background-color': s.active
|
|
1368
1480
|
};
|
|
1369
|
-
rules[
|
|
1481
|
+
rules[_sx(scope, '.bw_' + k + ':focus-visible')] = {
|
|
1370
1482
|
'box-shadow': '0 0 0 3px ' + s.focus,
|
|
1371
1483
|
'outline': 'none'
|
|
1372
1484
|
};
|
|
@@ -1374,70 +1486,99 @@ function generatePaletteClasses(scope, palette) {
|
|
|
1374
1486
|
// --- Component-specific overrides ---
|
|
1375
1487
|
|
|
1376
1488
|
// Alerts: light bg, dark text, subtle border
|
|
1377
|
-
rules[
|
|
1489
|
+
rules[_sx(scope, '.bw_alert.bw_' + k)] = {
|
|
1378
1490
|
'background-color': s.light,
|
|
1379
1491
|
'color': s.darkText,
|
|
1380
1492
|
'border-color': s.border
|
|
1381
1493
|
};
|
|
1382
1494
|
|
|
1383
1495
|
// Toast: inherit bg, left border accent
|
|
1384
|
-
rules[
|
|
1496
|
+
rules[_sx(scope, '.bw_toast.bw_' + k)] = {
|
|
1385
1497
|
'background-color': 'inherit',
|
|
1386
1498
|
'color': 'inherit',
|
|
1387
1499
|
'border-left': '4px solid ' + s.base
|
|
1388
1500
|
};
|
|
1389
1501
|
|
|
1390
1502
|
// Stat card: inherit bg, left border accent
|
|
1391
|
-
rules[
|
|
1503
|
+
rules[_sx(scope, '.bw_stat_card.bw_' + k)] = {
|
|
1392
1504
|
'background-color': 'inherit',
|
|
1393
1505
|
'color': 'inherit',
|
|
1394
1506
|
'border-left-color': s.base
|
|
1395
1507
|
};
|
|
1396
1508
|
|
|
1397
1509
|
// Card accent: left border accent, inherit bg
|
|
1398
|
-
rules[
|
|
1510
|
+
rules[_sx(scope, '.bw_card.bw_' + k)] = {
|
|
1399
1511
|
'background-color': 'inherit',
|
|
1400
1512
|
'color': 'inherit',
|
|
1401
1513
|
'border-left': '4px solid ' + s.base
|
|
1402
1514
|
};
|
|
1403
1515
|
|
|
1404
1516
|
// Timeline marker: colored dot
|
|
1405
|
-
rules[
|
|
1517
|
+
rules[_sx(scope, '.bw_timeline_marker.bw_' + k)] = {
|
|
1406
1518
|
'box-shadow': '0 0 0 2px ' + s.base
|
|
1407
1519
|
};
|
|
1408
1520
|
|
|
1409
|
-
// Spinner:
|
|
1410
|
-
|
|
1521
|
+
// Spinner: set color, re-apply border pattern so the root palette class
|
|
1522
|
+
// border-color doesn't fill in the transparent gap that makes it spin.
|
|
1523
|
+
// Also neutralize hover/active which would override border-right-color.
|
|
1524
|
+
rules[_sx(scope, '.bw_spinner_border.bw_' + k)] = {
|
|
1411
1525
|
'background-color': 'transparent',
|
|
1412
1526
|
'color': s.base,
|
|
1413
|
-
'border-color':
|
|
1527
|
+
'border-color': s.base,
|
|
1528
|
+
'border-right-color': 'transparent'
|
|
1529
|
+
};
|
|
1530
|
+
rules[_sx(scope, '.bw_spinner_border.bw_' + k + ':hover')] = {
|
|
1531
|
+
'background-color': 'transparent',
|
|
1532
|
+
'border-color': s.base,
|
|
1533
|
+
'border-right-color': 'transparent'
|
|
1534
|
+
};
|
|
1535
|
+
rules[_sx(scope, '.bw_spinner_grow.bw_' + k)] = {
|
|
1536
|
+
'background-color': s.base,
|
|
1537
|
+
'color': s.base
|
|
1414
1538
|
};
|
|
1415
1539
|
|
|
1416
1540
|
// Outline button: transparent bg, colored border+text, solid on hover
|
|
1417
|
-
rules[
|
|
1541
|
+
rules[_sx(scope, '.bw_btn_outline.bw_' + k)] = {
|
|
1418
1542
|
'background-color': 'transparent',
|
|
1419
1543
|
'color': s.base,
|
|
1420
1544
|
'border-color': s.base
|
|
1421
1545
|
};
|
|
1422
|
-
rules[
|
|
1546
|
+
rules[_sx(scope, '.bw_btn_outline.bw_' + k + ':hover')] = {
|
|
1423
1547
|
'background-color': s.base,
|
|
1424
1548
|
'color': s.textOn
|
|
1425
1549
|
};
|
|
1426
1550
|
|
|
1427
1551
|
// Hero: gradient background
|
|
1428
|
-
rules[
|
|
1552
|
+
rules[_sx(scope, '.bw_hero.bw_' + k)] = {
|
|
1429
1553
|
'background': 'linear-gradient(135deg, ' + s.base + ' 0%, ' + s.hover + ' 100%)',
|
|
1430
1554
|
'color': s.textOn
|
|
1431
1555
|
};
|
|
1432
1556
|
|
|
1433
|
-
// Progress bar:
|
|
1434
|
-
rules[
|
|
1435
|
-
'color':
|
|
1557
|
+
// Progress bar: contrasting text on colored bg
|
|
1558
|
+
rules[_sx(scope, '.bw_progress_bar.bw_' + k)] = {
|
|
1559
|
+
'color': s.textOn
|
|
1560
|
+
};
|
|
1561
|
+
|
|
1562
|
+
// Background utility: .bw_bg_primary, .bw_bg_secondary, etc.
|
|
1563
|
+
rules[_sx(scope, '.bw_bg_' + k)] = {
|
|
1564
|
+
'background-color': s.base,
|
|
1565
|
+
'color': s.textOn
|
|
1566
|
+
};
|
|
1567
|
+
|
|
1568
|
+
// Text color utility: .bw_text_primary, .bw_text_secondary, etc.
|
|
1569
|
+
rules[_sx(scope, '.bw_text_' + k)] = {
|
|
1570
|
+
'color': s.base
|
|
1436
1571
|
};
|
|
1437
1572
|
});
|
|
1438
1573
|
|
|
1439
|
-
// Text muted
|
|
1440
|
-
rules[
|
|
1574
|
+
// Text muted — always a neutral gray, never a brand color
|
|
1575
|
+
rules[_sx(scope, '.bw_text_muted')] = { 'color': '#6c757d' };
|
|
1576
|
+
|
|
1577
|
+
// Common bg/text utilities that aren't per-variant
|
|
1578
|
+
rules[_sx(scope, '.bw_bg_dark')] = { 'background-color': '#212529', 'color': '#f8f9fa' };
|
|
1579
|
+
rules[_sx(scope, '.bw_bg_light')] = { 'background-color': '#f8f9fa', 'color': '#212529' };
|
|
1580
|
+
rules[_sx(scope, '.bw_text_light')] = { 'color': '#f8f9fa' };
|
|
1581
|
+
rules[_sx(scope, '.bw_text_dark')] = { 'color': '#212529' };
|
|
1441
1582
|
|
|
1442
1583
|
return rules;
|
|
1443
1584
|
}
|
|
@@ -1459,30 +1600,32 @@ function generateThemedCSS(scopeName, palette, layout) {
|
|
|
1459
1600
|
generateAlerts(scopeName, palette, layout),
|
|
1460
1601
|
generateCards(scopeName, palette, layout),
|
|
1461
1602
|
generateForms(scopeName, palette, layout),
|
|
1462
|
-
generateNavigation(scopeName, palette),
|
|
1603
|
+
generateNavigation(scopeName, palette, layout),
|
|
1463
1604
|
generateTables(scopeName, palette, layout),
|
|
1464
|
-
generateTabs(scopeName, palette),
|
|
1605
|
+
generateTabs(scopeName, palette, layout),
|
|
1465
1606
|
generateListGroups(scopeName, palette, layout),
|
|
1466
|
-
generatePagination(scopeName, palette),
|
|
1607
|
+
generatePagination(scopeName, palette, layout),
|
|
1467
1608
|
generateProgress(scopeName, palette),
|
|
1468
|
-
generateBreadcrumbThemed(scopeName, palette),
|
|
1609
|
+
generateBreadcrumbThemed(scopeName, palette, layout),
|
|
1469
1610
|
generateCloseButtonThemed(scopeName, palette),
|
|
1470
1611
|
generateSectionsThemed(scopeName, palette),
|
|
1471
|
-
generateAccordionThemed(scopeName, palette),
|
|
1612
|
+
generateAccordionThemed(scopeName, palette, layout),
|
|
1472
1613
|
generateCarouselThemed(scopeName, palette),
|
|
1473
1614
|
generateModalThemed(scopeName, palette, layout),
|
|
1474
1615
|
generateToastThemed(scopeName, palette, layout),
|
|
1475
1616
|
generateDropdownThemed(scopeName, palette, layout),
|
|
1476
1617
|
generateSwitchThemed(scopeName, palette),
|
|
1477
1618
|
generateSkeletonThemed(scopeName, palette),
|
|
1478
|
-
generateStatCardThemed(scopeName, palette),
|
|
1619
|
+
generateStatCardThemed(scopeName, palette, layout),
|
|
1479
1620
|
generateTimelineThemed(scopeName, palette),
|
|
1480
1621
|
generateStepperThemed(scopeName, palette),
|
|
1481
1622
|
generateChipInputThemed(scopeName, palette),
|
|
1482
|
-
generateFileUploadThemed(scopeName, palette),
|
|
1623
|
+
generateFileUploadThemed(scopeName, palette, layout),
|
|
1483
1624
|
generateRangeThemed(scopeName, palette),
|
|
1484
|
-
generateSearchThemed(scopeName, palette),
|
|
1485
|
-
|
|
1625
|
+
generateSearchThemed(scopeName, palette, layout),
|
|
1626
|
+
generateTooltipThemed(scopeName, palette, layout),
|
|
1627
|
+
generatePopoverThemed(scopeName, palette, layout),
|
|
1628
|
+
generateCodeDemoThemed(scopeName, palette, layout),
|
|
1486
1629
|
generateNavPillsThemed(scopeName, palette, layout),
|
|
1487
1630
|
generatePaletteClasses(scopeName, palette)
|
|
1488
1631
|
);
|
|
@@ -1707,6 +1850,8 @@ var structuralRules = {
|
|
|
1707
1850
|
},
|
|
1708
1851
|
'.bw_table caption': { 'font-size': '0.875rem', 'caption-side': 'bottom' },
|
|
1709
1852
|
'.bw_table_bordered > :not(caption) > * > *': { 'border-width': '1px', 'border-style': 'solid' },
|
|
1853
|
+
'.bw_table_selectable > tbody > tr': { 'cursor': 'pointer' },
|
|
1854
|
+
'.bw_table > tbody > tr.bw_table_row_selected > *': { 'background-color': 'rgba(0, 102, 102, 0.1)' },
|
|
1710
1855
|
'.bw_table_responsive': { 'overflow-x': 'auto', '-webkit-overflow-scrolling': 'touch' }
|
|
1711
1856
|
},
|
|
1712
1857
|
|
|
@@ -1760,6 +1905,7 @@ var structuralRules = {
|
|
|
1760
1905
|
'.bw_nav_tabs .bw_nav_item': { 'margin-bottom': '-2px' },
|
|
1761
1906
|
'.bw_nav_link': {
|
|
1762
1907
|
'display': 'block', 'font-size': '0.875rem', 'font-weight': '500',
|
|
1908
|
+
'padding': '0.625rem 1rem',
|
|
1763
1909
|
'text-decoration': 'none', 'cursor': 'pointer',
|
|
1764
1910
|
'border': 'none', 'background': 'transparent', 'font-family': 'inherit'
|
|
1765
1911
|
},
|
|
@@ -1794,10 +1940,11 @@ var structuralRules = {
|
|
|
1794
1940
|
'.bw_page_item': { 'display': 'list-item', 'list-style': 'none' },
|
|
1795
1941
|
'.bw_page_link': {
|
|
1796
1942
|
'position': 'relative', 'display': 'block', 'padding': '0.375rem 0.75rem',
|
|
1797
|
-
'margin-left': '-1px', 'line-height': '1.25', 'text-decoration': 'none'
|
|
1943
|
+
'margin-left': '-1px', 'line-height': '1.25', 'text-decoration': 'none',
|
|
1944
|
+
'border': '1px solid transparent', 'cursor': 'pointer',
|
|
1945
|
+
'font-family': 'inherit', 'font-size': 'inherit', 'background': 'none'
|
|
1798
1946
|
},
|
|
1799
|
-
'.bw_page_item:first-child .bw_page_link': { 'margin-left': '0'
|
|
1800
|
-
'.bw_page_item:last-child .bw_page_link': { 'border-top-right-radius': '0.375rem', 'border-bottom-right-radius': '0.375rem' },
|
|
1947
|
+
'.bw_page_item:first-child .bw_page_link': { 'margin-left': '0' },
|
|
1801
1948
|
'.bw_page_link:focus-visible': { 'z-index': '3', 'outline': '2px solid currentColor', 'outline-offset': '-2px' }
|
|
1802
1949
|
},
|
|
1803
1950
|
|
|
@@ -1954,6 +2101,7 @@ var structuralRules = {
|
|
|
1954
2101
|
'.bw_accordion_header': { 'margin': '0' },
|
|
1955
2102
|
'.bw_accordion_button': {
|
|
1956
2103
|
'position': 'relative', 'display': 'flex', 'align-items': 'center', 'width': '100%',
|
|
2104
|
+
'padding': '0.875rem 1.25rem',
|
|
1957
2105
|
'font-size': '1rem', 'font-weight': '500', 'text-align': 'left',
|
|
1958
2106
|
'background-color': 'transparent', 'border': '0', 'overflow-anchor': 'none', 'cursor': 'pointer',
|
|
1959
2107
|
'font-family': 'inherit'
|
|
@@ -1965,10 +2113,9 @@ var structuralRules = {
|
|
|
1965
2113
|
'background-repeat': 'no-repeat', 'background-size': '1.25rem'
|
|
1966
2114
|
},
|
|
1967
2115
|
'.bw_accordion_button:not(.bw_collapsed)::after': { 'transform': 'rotate(-180deg)' },
|
|
1968
|
-
'.
|
|
1969
|
-
'.bw_accordion_collapse
|
|
1970
|
-
'.
|
|
1971
|
-
'.bw_accordion_item:last-child': { 'border-bottom-left-radius': '8px', 'border-bottom-right-radius': '8px' }
|
|
2116
|
+
'.bw_accordion_body': { 'padding': '1rem 1.25rem' },
|
|
2117
|
+
'.bw_accordion_collapse': { 'max-height': '0', 'overflow': 'hidden', 'transition': 'max-height 0.3s ease' },
|
|
2118
|
+
'.bw_accordion_collapse.bw_collapse_show': { 'max-height': 'none' }
|
|
1972
2119
|
},
|
|
1973
2120
|
|
|
1974
2121
|
// ---- Carousel ----
|
|
@@ -2114,7 +2261,13 @@ var structuralRules = {
|
|
|
2114
2261
|
|
|
2115
2262
|
// ---- Stat card ----
|
|
2116
2263
|
statCard: {
|
|
2117
|
-
'.bw_stat_card': {
|
|
2264
|
+
'.bw_stat_card': {
|
|
2265
|
+
'padding': '1.25rem',
|
|
2266
|
+
'border-left': '4px solid transparent',
|
|
2267
|
+
'border-radius': '0.375rem',
|
|
2268
|
+
'background-color': 'inherit',
|
|
2269
|
+
'transition': 'transform 0.15s ease'
|
|
2270
|
+
},
|
|
2118
2271
|
'.bw_stat_card:hover': { 'transform': 'translateY(-1px)' },
|
|
2119
2272
|
'.bw_stat_icon': { 'font-size': '1.5rem', 'margin-bottom': '0.5rem' },
|
|
2120
2273
|
'.bw_stat_value': { 'font-size': '2rem', 'font-weight': '700', 'line-height': '1.2' },
|
|
@@ -2477,6 +2630,20 @@ function generateUtilityRules() {
|
|
|
2477
2630
|
rules['.list-inline-item'] = { 'display': 'inline-block' };
|
|
2478
2631
|
rules['.list-inline-item:not(:last-child)'] = { 'margin-right': '.5rem' };
|
|
2479
2632
|
|
|
2633
|
+
// Typography — bw_ prefixed utilities via loops
|
|
2634
|
+
var _imp = function(p, v) { var o = {}; o[p] = v + ' !important'; return o; };
|
|
2635
|
+
[['fs',{'xs':'0.75rem','sm':'0.875rem','base':'1rem','lg':'1.125rem','xl':'1.25rem','2xl':'1.5rem'},'font-size'],
|
|
2636
|
+
['fw',{light:'300',normal:'400',medium:'500',semibold:'600',bold:'700'},'font-weight'],
|
|
2637
|
+
['lh',{tight:'1.25',normal:'1.5',relaxed:'1.75'},'line-height']
|
|
2638
|
+
].forEach(function(d) { for (var dk in d[1]) rules['.bw_'+d[0]+'_'+dk] = _imp(d[2], d[1][dk]); });
|
|
2639
|
+
|
|
2640
|
+
// Flex utilities
|
|
2641
|
+
rules['.bw_flex'] = { 'display': 'flex' };
|
|
2642
|
+
rules['.bw_flex_column'] = { 'flex-direction': 'column' };
|
|
2643
|
+
rules['.bw_flex_wrap'] = { 'flex-wrap': 'wrap' };
|
|
2644
|
+
rules['.bw_flex_center'] = { 'display': 'flex', 'align-items': 'center', 'justify-content': 'center' };
|
|
2645
|
+
for (var gk in spacingValues) rules['.bw_gap_' + gk] = { 'gap': spacingValues[gk] + ' !important' };
|
|
2646
|
+
|
|
2480
2647
|
// Visibility
|
|
2481
2648
|
rules['.bw_visible, .visible'] = { 'visibility': 'visible !important' };
|
|
2482
2649
|
rules['.bw_invisible, .invisible'] = { 'visibility': 'hidden !important' };
|
|
@@ -2537,6 +2704,26 @@ function getStructuralStyles() {
|
|
|
2537
2704
|
return getStructuralCSS();
|
|
2538
2705
|
}
|
|
2539
2706
|
|
|
2707
|
+
/**
|
|
2708
|
+
* Get CSS reset rules only (box-sizing, html/body font, reduced-motion).
|
|
2709
|
+
* Separate from themed/structural rules for independent injection.
|
|
2710
|
+
* @returns {Object} CSS rules object for the reset layer
|
|
2711
|
+
*/
|
|
2712
|
+
function getResetStyles() {
|
|
2713
|
+
var rules = {};
|
|
2714
|
+
Object.assign(rules, structuralRules.base);
|
|
2715
|
+
// Include reduced-motion preference
|
|
2716
|
+
rules['@media (prefers-reduced-motion: reduce)'] = {
|
|
2717
|
+
'*, *::before, *::after': {
|
|
2718
|
+
'animation-duration': '0.01ms !important',
|
|
2719
|
+
'animation-iteration-count': '1 !important',
|
|
2720
|
+
'transition-duration': '0.01ms !important',
|
|
2721
|
+
'scroll-behavior': 'auto !important'
|
|
2722
|
+
}
|
|
2723
|
+
};
|
|
2724
|
+
return rules;
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2540
2727
|
// =========================================================================
|
|
2541
2728
|
// defaultStyles — backward-compatible categorized view
|
|
2542
2729
|
// =========================================================================
|
|
@@ -2566,60 +2753,41 @@ Object.assign({}, structuralRules, {
|
|
|
2566
2753
|
});
|
|
2567
2754
|
|
|
2568
2755
|
/**
|
|
2569
|
-
*
|
|
2570
|
-
*
|
|
2571
|
-
*
|
|
2572
|
-
*
|
|
2573
|
-
* @param {
|
|
2574
|
-
*
|
|
2575
|
-
* @
|
|
2576
|
-
* @returns {Object} CSS rules object scoped under .bw_theme_alt (+ optional .name)
|
|
2756
|
+
* Prefix every selector in a rules object with a scope selector.
|
|
2757
|
+
* Handles @media/@keyframes blocks and comma-separated selectors.
|
|
2758
|
+
* @param {Object} rules - CSS rules object
|
|
2759
|
+
* @param {string} prefix - Scope prefix (e.g. '#my-dashboard', '.bw_theme_alt')
|
|
2760
|
+
* @param {boolean} [compound=false] - If true, use compound selector (no space)
|
|
2761
|
+
* for the first segment: `#scope.bw_theme_alt .sel` vs `#scope .sel`
|
|
2762
|
+
* @returns {Object} New rules object with scoped selectors
|
|
2577
2763
|
*/
|
|
2578
|
-
function
|
|
2579
|
-
|
|
2580
|
-
var
|
|
2581
|
-
|
|
2582
|
-
// Re-scope every selector under .bw_theme_alt (+ optional theme name)
|
|
2583
|
-
var altPrefix = name ? '.' + name + '.bw_theme_alt' : '.bw_theme_alt';
|
|
2584
|
-
var altRules = {};
|
|
2585
|
-
|
|
2586
|
-
for (var sel in rawRules) {
|
|
2587
|
-
if (!rawRules.hasOwnProperty(sel)) continue;
|
|
2588
|
-
|
|
2764
|
+
function scopeRulesUnder(rules, prefix, compound) {
|
|
2765
|
+
var scoped = {};
|
|
2766
|
+
for (var sel in rules) {
|
|
2767
|
+
if (!rules.hasOwnProperty(sel)) continue;
|
|
2589
2768
|
if (sel.charAt(0) === '@') {
|
|
2590
2769
|
// @media / @keyframes — recurse into the block
|
|
2591
|
-
var innerBlock =
|
|
2592
|
-
var
|
|
2770
|
+
var innerBlock = rules[sel];
|
|
2771
|
+
var scopedInner = {};
|
|
2593
2772
|
for (var innerSel in innerBlock) {
|
|
2594
2773
|
if (!innerBlock.hasOwnProperty(innerSel)) continue;
|
|
2595
|
-
|
|
2774
|
+
scopedInner[_prefixSelector(innerSel, prefix)] = innerBlock[innerSel];
|
|
2596
2775
|
}
|
|
2597
|
-
|
|
2776
|
+
scoped[sel] = scopedInner;
|
|
2598
2777
|
} else {
|
|
2599
|
-
|
|
2600
|
-
// Handle comma-separated selectors
|
|
2601
|
-
var parts = sel.split(',');
|
|
2602
|
-
var scopedParts = [];
|
|
2603
|
-
for (var i = 0; i < parts.length; i++) {
|
|
2604
|
-
var s = parts[i].trim();
|
|
2605
|
-
// 'body' selector gets special treatment: .bw_theme_alt body
|
|
2606
|
-
if (s === 'body' || s.indexOf('body') === 0) {
|
|
2607
|
-
scopedParts.push(altPrefix + ' ' + s);
|
|
2608
|
-
} else {
|
|
2609
|
-
scopedParts.push(altPrefix + ' ' + s);
|
|
2610
|
-
}
|
|
2611
|
-
}
|
|
2612
|
-
altRules[scopedParts.join(', ')] = rawRules[sel];
|
|
2778
|
+
scoped[_prefixSelector(sel, prefix)] = rules[sel];
|
|
2613
2779
|
}
|
|
2614
2780
|
}
|
|
2781
|
+
return scoped;
|
|
2782
|
+
}
|
|
2615
2783
|
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
return
|
|
2784
|
+
function _prefixSelector(sel, prefix) {
|
|
2785
|
+
var parts = sel.split(',');
|
|
2786
|
+
var result = [];
|
|
2787
|
+
for (var i = 0; i < parts.length; i++) {
|
|
2788
|
+
result.push(prefix + ' ' + parts[i].trim());
|
|
2789
|
+
}
|
|
2790
|
+
return result.join(', ');
|
|
2623
2791
|
}
|
|
2624
2792
|
|
|
2625
2793
|
/**
|
|
@@ -3343,7 +3511,7 @@ const bw = {
|
|
|
3343
3511
|
__monkey_patch_is_nodejs__: {
|
|
3344
3512
|
_value: 'ignore',
|
|
3345
3513
|
set: function(x) {
|
|
3346
|
-
this._value = (
|
|
3514
|
+
this._value = _is(x, 'boolean') ? x : 'ignore';
|
|
3347
3515
|
},
|
|
3348
3516
|
get: function() {
|
|
3349
3517
|
return this._value;
|
|
@@ -3391,6 +3559,67 @@ Object.defineProperty(bw, '_isBrowser', {
|
|
|
3391
3559
|
configurable: true
|
|
3392
3560
|
});
|
|
3393
3561
|
|
|
3562
|
+
// ── Internal aliases ─────────────────────────────────────────────────────
|
|
3563
|
+
// Short names for frequently-used builtins and internal methods.
|
|
3564
|
+
// Same pattern as v1 (_to = bw.typeOf, etc.).
|
|
3565
|
+
//
|
|
3566
|
+
// Why: Terser can't shorten global property chains (console.warn,
|
|
3567
|
+
// Object.prototype.hasOwnProperty, Array.isArray, document.createElement)
|
|
3568
|
+
// because it can't prove they're side-effect-free. We can, so we alias
|
|
3569
|
+
// them here. Each alias saves bytes in the minified output, and the short
|
|
3570
|
+
// names also reduce visual noise in the hot paths (binding pipeline,
|
|
3571
|
+
// createDOM, etc.).
|
|
3572
|
+
//
|
|
3573
|
+
// Alias Target Sites
|
|
3574
|
+
// ───────── ────────────────────────────────────── ─────
|
|
3575
|
+
// _hop Object.prototype.hasOwnProperty 15
|
|
3576
|
+
// _isA Array.isArray 25
|
|
3577
|
+
// _keys Object.keys 7
|
|
3578
|
+
// _to bw.typeOf (type string) 26
|
|
3579
|
+
// _is type check boolean: _is(x,'string') ~50
|
|
3580
|
+
// _cw console.warn 8
|
|
3581
|
+
// _cl console.log 11
|
|
3582
|
+
// _ce console.error 4
|
|
3583
|
+
// _chp ComponentHandle.prototype 28 (defined after constructor)
|
|
3584
|
+
//
|
|
3585
|
+
// Note: document.createElement etc. are NOT aliased because they require
|
|
3586
|
+
// `this === document` and .bind() would add overhead on every call.
|
|
3587
|
+
// Console aliases use thin wrappers (not direct refs) so test monkey-
|
|
3588
|
+
// patching of console.warn/log/error continues to work.
|
|
3589
|
+
//
|
|
3590
|
+
// `typeof x` for UNDECLARED globals (window, document, process, require,
|
|
3591
|
+
// EventSource, navigator, Promise, __filename, import.meta) MUST stay as
|
|
3592
|
+
// raw `typeof` — calling _to(x) when x doesn't exist throws ReferenceError.
|
|
3593
|
+
//
|
|
3594
|
+
// ── v1 functional type helpers (kept for reference, not currently used) ──
|
|
3595
|
+
// _toa(x, type, trueVal, falseVal) — bw.typeAssign:
|
|
3596
|
+
// returns trueVal if _to(x)===type, else falseVal.
|
|
3597
|
+
// Replaces: (typeof x === 'string') ? A : B → _toa(x,'string',A,B)
|
|
3598
|
+
// _toc(x, type, trueVal, falseVal) — bw.typeConvert:
|
|
3599
|
+
// same as _toa but if trueVal/falseVal are functions, calls them with x.
|
|
3600
|
+
// Replaces: typeof x === 'string' ? fn(x) : default → _toc(x,'string',fn,default)
|
|
3601
|
+
// Uncomment if pattern frequency justifies them:
|
|
3602
|
+
// var _toa = function(x, t, y, n) { return _to(x) === t ? y : n; };
|
|
3603
|
+
// var _toc = function(x, t, y, n) { var r = _to(x)===t; return r ? (_to(y)==='function'?y(x):y) : (_to(n)==='function'?n(x):n); };
|
|
3604
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
3605
|
+
var _hop = Object.prototype.hasOwnProperty;
|
|
3606
|
+
var _isA = Array.isArray;
|
|
3607
|
+
var _keys = Object.keys;
|
|
3608
|
+
var _to = typeOf; // imported from bitwrench-utils.js
|
|
3609
|
+
var _is = function(x, t) { var r = _to(x); return r === t || r.toLowerCase() === t; };
|
|
3610
|
+
// Console aliases use thin wrappers (not direct references) so that test
|
|
3611
|
+
// code can monkey-patch console.warn/log/error and the patches take effect.
|
|
3612
|
+
var _cw = function() { console.warn.apply(console, arguments); };
|
|
3613
|
+
var _cl = function() { console.log.apply(console, arguments); };
|
|
3614
|
+
var _ce = function() { console.error.apply(console, arguments); };
|
|
3615
|
+
|
|
3616
|
+
/**
|
|
3617
|
+
* Debug flag. When true, emits console.warn for silent binding failures
|
|
3618
|
+
* (missing paths, null refs, auto-created intermediate objects).
|
|
3619
|
+
* @type {boolean}
|
|
3620
|
+
*/
|
|
3621
|
+
bw.debug = false;
|
|
3622
|
+
|
|
3394
3623
|
/**
|
|
3395
3624
|
* Lazy-resolve Node.js `fs` module.
|
|
3396
3625
|
* Tries require('fs') first (available in CJS/UMD Node.js builds),
|
|
@@ -3538,7 +3767,7 @@ bw.uuid = function(prefix) {
|
|
|
3538
3767
|
*/
|
|
3539
3768
|
bw._el = function(id) {
|
|
3540
3769
|
// Pass-through for DOM elements
|
|
3541
|
-
if (
|
|
3770
|
+
if (!_is(id, 'string')) return id || null;
|
|
3542
3771
|
if (!id) return null;
|
|
3543
3772
|
if (!bw._isBrowser) return null;
|
|
3544
3773
|
|
|
@@ -3566,7 +3795,12 @@ bw._el = function(id) {
|
|
|
3566
3795
|
el = document.querySelector('[data-bw_id="' + id + '"]');
|
|
3567
3796
|
}
|
|
3568
3797
|
|
|
3569
|
-
// 5.
|
|
3798
|
+
// 5. Try class-based lookup for bw_uuid_* tokens (UUID addressing)
|
|
3799
|
+
if (!el && id.indexOf('bw_uuid_') === 0) {
|
|
3800
|
+
el = document.querySelector('.' + id);
|
|
3801
|
+
}
|
|
3802
|
+
|
|
3803
|
+
// 6. Cache the result for next time
|
|
3570
3804
|
if (el) {
|
|
3571
3805
|
bw._nodeMap[id] = el;
|
|
3572
3806
|
}
|
|
@@ -3619,6 +3853,84 @@ bw._deregisterNode = function(el, bwId) {
|
|
|
3619
3853
|
}
|
|
3620
3854
|
};
|
|
3621
3855
|
|
|
3856
|
+
// ===================================================================================
|
|
3857
|
+
// bw.assignUUID() / bw.getUUID() — Explicit UUID addressing for TACO objects
|
|
3858
|
+
// ===================================================================================
|
|
3859
|
+
|
|
3860
|
+
/**
|
|
3861
|
+
* Regex to match a bw_uuid_* token in a class string.
|
|
3862
|
+
* @private
|
|
3863
|
+
*/
|
|
3864
|
+
var _UUID_RE = /\bbw_uuid_[a-z0-9_]+\b/;
|
|
3865
|
+
|
|
3866
|
+
/**
|
|
3867
|
+
* Assign a UUID to a TACO object by appending a `bw_uuid_*` token to `taco.a.class`.
|
|
3868
|
+
*
|
|
3869
|
+
* Idempotent by default — calling twice returns the same UUID. Pass `forceNew=true`
|
|
3870
|
+
* to replace an existing UUID (useful in loops where each TACO needs a unique ID).
|
|
3871
|
+
*
|
|
3872
|
+
* @param {Object} taco - A TACO object `{t, a, c, o}`
|
|
3873
|
+
* @param {boolean} [forceNew=false] - If true, replaces any existing UUID with a new one
|
|
3874
|
+
* @returns {string} The UUID string (e.g. 'bw_uuid_a1b2c3d4e5')
|
|
3875
|
+
* @category Identifiers
|
|
3876
|
+
* @example
|
|
3877
|
+
* var card = bw.makeStatCard({ value: '0', label: 'Scans' });
|
|
3878
|
+
* var uuid = bw.assignUUID(card); // 'bw_uuid_a1b2c3d4e5'
|
|
3879
|
+
* var same = bw.assignUUID(card); // same UUID (idempotent)
|
|
3880
|
+
* var diff = bw.assignUUID(card, true); // new UUID (forced)
|
|
3881
|
+
*/
|
|
3882
|
+
bw.assignUUID = function(taco, forceNew) {
|
|
3883
|
+
if (!taco || !_is(taco, 'object')) return null;
|
|
3884
|
+
|
|
3885
|
+
// Ensure taco.a exists
|
|
3886
|
+
if (!taco.a) taco.a = {};
|
|
3887
|
+
if (!_is(taco.a.class, 'string')) taco.a.class = taco.a.class ? String(taco.a.class) : '';
|
|
3888
|
+
|
|
3889
|
+
var existing = taco.a.class.match(_UUID_RE);
|
|
3890
|
+
|
|
3891
|
+
if (existing && !forceNew) {
|
|
3892
|
+
return existing[0];
|
|
3893
|
+
}
|
|
3894
|
+
|
|
3895
|
+
// Remove old UUID if forceNew
|
|
3896
|
+
if (existing) {
|
|
3897
|
+
taco.a.class = taco.a.class.replace(_UUID_RE, '').replace(/\s+/g, ' ').trim();
|
|
3898
|
+
}
|
|
3899
|
+
|
|
3900
|
+
var uuid = bw.uuid('uuid');
|
|
3901
|
+
taco.a.class = (taco.a.class ? taco.a.class + ' ' : '') + uuid;
|
|
3902
|
+
return uuid;
|
|
3903
|
+
};
|
|
3904
|
+
|
|
3905
|
+
/**
|
|
3906
|
+
* Read the UUID from a TACO object or DOM element. Pure getter, no side effects.
|
|
3907
|
+
*
|
|
3908
|
+
* @param {Object|Element} tacoOrElement - A TACO object or DOM element
|
|
3909
|
+
* @returns {string|null} The UUID string, or null if none assigned
|
|
3910
|
+
* @category Identifiers
|
|
3911
|
+
* @example
|
|
3912
|
+
* bw.getUUID(card) // 'bw_uuid_a1b2c3d4e5' (from TACO)
|
|
3913
|
+
* bw.getUUID(domEl) // 'bw_uuid_a1b2c3d4e5' (from DOM element)
|
|
3914
|
+
* bw.getUUID({t:'div'}) // null (no UUID)
|
|
3915
|
+
*/
|
|
3916
|
+
bw.getUUID = function(tacoOrElement) {
|
|
3917
|
+
if (!tacoOrElement) return null;
|
|
3918
|
+
|
|
3919
|
+
var classStr;
|
|
3920
|
+
// DOM element: check className
|
|
3921
|
+
if (tacoOrElement.className !== undefined && tacoOrElement.tagName) {
|
|
3922
|
+
classStr = tacoOrElement.className;
|
|
3923
|
+
}
|
|
3924
|
+
// TACO object: check a.class
|
|
3925
|
+
else if (tacoOrElement.a && _is(tacoOrElement.a.class, 'string')) {
|
|
3926
|
+
classStr = tacoOrElement.a.class;
|
|
3927
|
+
}
|
|
3928
|
+
|
|
3929
|
+
if (!classStr) return null;
|
|
3930
|
+
var match = classStr.match(_UUID_RE);
|
|
3931
|
+
return match ? match[0] : null;
|
|
3932
|
+
};
|
|
3933
|
+
|
|
3622
3934
|
/**
|
|
3623
3935
|
* Escape HTML special characters to prevent XSS.
|
|
3624
3936
|
*
|
|
@@ -3634,7 +3946,7 @@ bw._deregisterNode = function(el, bwId) {
|
|
|
3634
3946
|
* // => '<b>Hello</b> & "world"'
|
|
3635
3947
|
*/
|
|
3636
3948
|
bw.escapeHTML = function(str) {
|
|
3637
|
-
if (
|
|
3949
|
+
if (!_is(str, 'string')) return '';
|
|
3638
3950
|
|
|
3639
3951
|
const escapeMap = {
|
|
3640
3952
|
'&': '&',
|
|
@@ -3668,6 +3980,42 @@ bw.raw = function(str) {
|
|
|
3668
3980
|
return { __bw_raw: true, v: String(str) };
|
|
3669
3981
|
};
|
|
3670
3982
|
|
|
3983
|
+
/**
|
|
3984
|
+
* Hyperscript-style TACO constructor.
|
|
3985
|
+
*
|
|
3986
|
+
* A convenience helper that returns a canonical TACO object from positional
|
|
3987
|
+
* arguments. The return value is a plain object — serializable, works with
|
|
3988
|
+
* bwserve, and accepted everywhere TACO is accepted.
|
|
3989
|
+
*
|
|
3990
|
+
* @param {string} tag - HTML tag name (e.g. 'div', 'p', 'section')
|
|
3991
|
+
* @param {Object|null} [attrs] - HTML attributes object. Pass null or omit to skip.
|
|
3992
|
+
* @param {*} [content] - Content: string, number, TACO object, or array of children.
|
|
3993
|
+
* @param {Object} [options] - TACO options (state, lifecycle hooks, render fn).
|
|
3994
|
+
* @returns {Object} Plain TACO object {t, a?, c?, o?}
|
|
3995
|
+
* @category Utilities
|
|
3996
|
+
* @see bw.html
|
|
3997
|
+
* @see bw.createDOM
|
|
3998
|
+
* @see bw.DOM
|
|
3999
|
+
* @example
|
|
4000
|
+
* bw.h('div')
|
|
4001
|
+
* // => { t: 'div' }
|
|
4002
|
+
*
|
|
4003
|
+
* bw.h('p', { class: 'bw_text_muted' }, 'Hello')
|
|
4004
|
+
* // => { t: 'p', a: { class: 'bw_text_muted' }, c: 'Hello' }
|
|
4005
|
+
*
|
|
4006
|
+
* bw.h('ul', null, [
|
|
4007
|
+
* bw.h('li', null, 'one'),
|
|
4008
|
+
* bw.h('li', null, 'two')
|
|
4009
|
+
* ])
|
|
4010
|
+
* // => { t: 'ul', c: [{ t: 'li', c: 'one' }, { t: 'li', c: 'two' }] }
|
|
4011
|
+
*/
|
|
4012
|
+
bw.h = function(tag, attrs, content, options) {
|
|
4013
|
+
var taco = { t: String(tag) };
|
|
4014
|
+
if (attrs !== null && attrs !== undefined) taco.a = attrs;
|
|
4015
|
+
if (content !== undefined) taco.c = content;
|
|
4016
|
+
if (options !== undefined) taco.o = options;
|
|
4017
|
+
return taco;
|
|
4018
|
+
};
|
|
3671
4019
|
|
|
3672
4020
|
/**
|
|
3673
4021
|
* Convert a TACO object (or array of TACOs) to an HTML string.
|
|
@@ -3707,7 +4055,7 @@ bw.html = function(taco, options = {}) {
|
|
|
3707
4055
|
}
|
|
3708
4056
|
|
|
3709
4057
|
// Handle arrays of TACOs
|
|
3710
|
-
if (
|
|
4058
|
+
if (_isA(taco)) {
|
|
3711
4059
|
return taco.map(t => bw.html(t, options)).join('');
|
|
3712
4060
|
}
|
|
3713
4061
|
|
|
@@ -3730,15 +4078,15 @@ bw.html = function(taco, options = {}) {
|
|
|
3730
4078
|
if (taco && taco._bwEach && options.state) {
|
|
3731
4079
|
var eachExpr = taco.expr.replace(/^\$\{|\}$/g, '');
|
|
3732
4080
|
var arr = bw._evaluatePath(options.state, eachExpr);
|
|
3733
|
-
if (!
|
|
4081
|
+
if (!_isA(arr)) return '';
|
|
3734
4082
|
return arr.map(function(item, idx) { return bw.html(taco.factory(item, idx), options); }).join('');
|
|
3735
4083
|
}
|
|
3736
4084
|
|
|
3737
4085
|
// Handle primitives and non-TACO objects
|
|
3738
|
-
if (
|
|
4086
|
+
if (!_is(taco, 'object') || !taco.t) {
|
|
3739
4087
|
var str = options.raw ? String(taco) : bw.escapeHTML(String(taco));
|
|
3740
4088
|
// Resolve template bindings if state provided
|
|
3741
|
-
if (options.state &&
|
|
4089
|
+
if (options.state && _is(str, 'string') && str.indexOf('${') >= 0) {
|
|
3742
4090
|
str = bw._resolveTemplate(str, options.state, !!options.compile);
|
|
3743
4091
|
}
|
|
3744
4092
|
return str;
|
|
@@ -3758,10 +4106,18 @@ bw.html = function(taco, options = {}) {
|
|
|
3758
4106
|
// Skip null, undefined, false
|
|
3759
4107
|
if (value == null || value === false) continue;
|
|
3760
4108
|
|
|
3761
|
-
//
|
|
3762
|
-
if (key.startsWith('on'))
|
|
4109
|
+
// Serialize event handlers via funcRegister
|
|
4110
|
+
if (key.startsWith('on')) {
|
|
4111
|
+
if (_is(value, 'function')) {
|
|
4112
|
+
var fnId = bw.funcRegister(value);
|
|
4113
|
+
attrStr += ' ' + key + '="' + bw.funcGetDispatchStr(fnId, 'event') + '"';
|
|
4114
|
+
} else if (_is(value, 'string')) {
|
|
4115
|
+
attrStr += ' ' + key + '="' + bw.escapeHTML(value) + '"';
|
|
4116
|
+
}
|
|
4117
|
+
continue;
|
|
4118
|
+
}
|
|
3763
4119
|
|
|
3764
|
-
if (key === 'style' &&
|
|
4120
|
+
if (key === 'style' && _is(value, 'object')) {
|
|
3765
4121
|
// Convert style object to string
|
|
3766
4122
|
const styleStr = Object.entries(value)
|
|
3767
4123
|
.filter(([, v]) => v != null)
|
|
@@ -3772,7 +4128,7 @@ bw.html = function(taco, options = {}) {
|
|
|
3772
4128
|
}
|
|
3773
4129
|
} else if (key === 'class') {
|
|
3774
4130
|
// Handle class as array or string
|
|
3775
|
-
const classStr =
|
|
4131
|
+
const classStr = _isA(value) ? value.filter(Boolean).join(' ') : String(value);
|
|
3776
4132
|
if (classStr) {
|
|
3777
4133
|
attrStr += ` class="${bw.escapeHTML(classStr)}"`;
|
|
3778
4134
|
}
|
|
@@ -3808,13 +4164,184 @@ bw.html = function(taco, options = {}) {
|
|
|
3808
4164
|
// Process content recursively
|
|
3809
4165
|
let contentStr = content != null ? bw.html(content, options) : '';
|
|
3810
4166
|
// Resolve template bindings in content if state provided
|
|
3811
|
-
if (options.state &&
|
|
4167
|
+
if (options.state && _is(contentStr, 'string') && contentStr.indexOf('${') >= 0) {
|
|
3812
4168
|
contentStr = bw._resolveTemplate(contentStr, options.state, !!options.compile);
|
|
3813
4169
|
}
|
|
3814
4170
|
|
|
3815
4171
|
return `<${tag}${attrStr}>${contentStr}</${tag}>`;
|
|
3816
4172
|
};
|
|
3817
4173
|
|
|
4174
|
+
/**
|
|
4175
|
+
* Generate a complete, self-contained HTML document from TACO content.
|
|
4176
|
+
*
|
|
4177
|
+
* Produces a full `<!DOCTYPE html>` page with configurable runtime injection,
|
|
4178
|
+
* func registry emission (so serialized event handlers work), optional theme,
|
|
4179
|
+
* and extra head elements. Designed for static site generation, offline/airgapped
|
|
4180
|
+
* use, and the "static site that isn't static" workflow.
|
|
4181
|
+
*
|
|
4182
|
+
* @param {Object} [opts={}] - Page options
|
|
4183
|
+
* @param {Object|string|Array} [opts.body=''] - Body content: TACO, string, or array
|
|
4184
|
+
* @param {string} [opts.title='bitwrench'] - Page title
|
|
4185
|
+
* @param {Object} [opts.state] - State for ${expr} resolution in bw.html()
|
|
4186
|
+
* @param {string} [opts.runtime='shim'] - Runtime level: 'inline'|'cdn'|'shim'|'none'
|
|
4187
|
+
* @param {string} [opts.css=''] - Additional CSS for <style> block
|
|
4188
|
+
* @param {string|Object} [opts.theme=null] - Theme preset name or config object
|
|
4189
|
+
* @param {Array} [opts.head=[]] - Extra TACO elements rendered into <head>
|
|
4190
|
+
* @param {string} [opts.favicon=''] - Favicon URL
|
|
4191
|
+
* @param {string} [opts.lang='en'] - HTML lang attribute
|
|
4192
|
+
* @returns {string} Complete HTML document string
|
|
4193
|
+
* @category DOM Generation
|
|
4194
|
+
* @see bw.html
|
|
4195
|
+
* @example
|
|
4196
|
+
* bw.htmlPage({
|
|
4197
|
+
* title: 'My App',
|
|
4198
|
+
* body: { t: 'h1', c: 'Hello World' },
|
|
4199
|
+
* runtime: 'shim'
|
|
4200
|
+
* })
|
|
4201
|
+
*/
|
|
4202
|
+
bw.htmlPage = function(opts) {
|
|
4203
|
+
opts = opts || {};
|
|
4204
|
+
var title = opts.title || 'bitwrench';
|
|
4205
|
+
var body = opts.body || '';
|
|
4206
|
+
var state = opts.state || undefined;
|
|
4207
|
+
var runtime = opts.runtime || 'shim';
|
|
4208
|
+
var css = opts.css || '';
|
|
4209
|
+
var theme = opts.theme || null;
|
|
4210
|
+
var headExtra = opts.head || [];
|
|
4211
|
+
var favicon = opts.favicon || '';
|
|
4212
|
+
var lang = opts.lang || 'en';
|
|
4213
|
+
|
|
4214
|
+
// Snapshot funcRegistry counter before rendering
|
|
4215
|
+
var fnCounterBefore = bw._fnIDCounter;
|
|
4216
|
+
|
|
4217
|
+
// Render body content
|
|
4218
|
+
var bodyHTML = '';
|
|
4219
|
+
if (_is(body, 'string')) {
|
|
4220
|
+
bodyHTML = body;
|
|
4221
|
+
} else {
|
|
4222
|
+
var htmlOpts = {};
|
|
4223
|
+
if (state) htmlOpts.state = state;
|
|
4224
|
+
bodyHTML = bw.html(body, htmlOpts);
|
|
4225
|
+
}
|
|
4226
|
+
|
|
4227
|
+
// Collect functions registered during this render
|
|
4228
|
+
var fnCounterAfter = bw._fnIDCounter;
|
|
4229
|
+
var registryEntries = '';
|
|
4230
|
+
for (var i = fnCounterBefore; i < fnCounterAfter; i++) {
|
|
4231
|
+
var fnKey = 'bw_fn_' + i;
|
|
4232
|
+
if (bw._fnRegistry[fnKey]) {
|
|
4233
|
+
registryEntries += 'bw._fnRegistry[\'' + fnKey + '\']=' +
|
|
4234
|
+
bw._fnRegistry[fnKey].toString() + ';\n';
|
|
4235
|
+
}
|
|
4236
|
+
}
|
|
4237
|
+
|
|
4238
|
+
// Build runtime script for <head>
|
|
4239
|
+
var runtimeHead = '';
|
|
4240
|
+
if (runtime === 'inline') {
|
|
4241
|
+
// Read UMD bundle synchronously if in Node.js
|
|
4242
|
+
var umdSource = null;
|
|
4243
|
+
if (bw._isNode) {
|
|
4244
|
+
try {
|
|
4245
|
+
var fs = (typeof require === 'function') ? require('fs') : null;
|
|
4246
|
+
var pathMod = (typeof require === 'function') ? require('path') : null;
|
|
4247
|
+
if (fs && pathMod) {
|
|
4248
|
+
// Resolve dist/ relative to this source file
|
|
4249
|
+
var srcDir = '';
|
|
4250
|
+
try { srcDir = pathMod.dirname((typeof __filename !== 'undefined') ? __filename : ''); }
|
|
4251
|
+
catch(e2) { /* ESM: __filename not available */ }
|
|
4252
|
+
if (!srcDir && typeof import.meta !== 'undefined' && import.meta.url) {
|
|
4253
|
+
var url = (typeof require === 'function') ? require('url') : null;
|
|
4254
|
+
if (url && url.fileURLToPath) srcDir = pathMod.dirname(url.fileURLToPath(import.meta.url));
|
|
4255
|
+
}
|
|
4256
|
+
if (srcDir) {
|
|
4257
|
+
var distPath = pathMod.resolve(srcDir, '../dist/bitwrench.umd.min.js');
|
|
4258
|
+
umdSource = fs.readFileSync(distPath, 'utf8');
|
|
4259
|
+
}
|
|
4260
|
+
}
|
|
4261
|
+
} catch(e) { /* fall through */ }
|
|
4262
|
+
}
|
|
4263
|
+
if (umdSource) {
|
|
4264
|
+
runtimeHead = '<script>' + umdSource + '</script>';
|
|
4265
|
+
} else {
|
|
4266
|
+
// Fallback to shim in browser or if dist not available
|
|
4267
|
+
runtimeHead = '<script>' + bw._FUNC_REGISTRY_SHIM + '</script>';
|
|
4268
|
+
}
|
|
4269
|
+
} else if (runtime === 'cdn') {
|
|
4270
|
+
runtimeHead = '<script src="https://cdn.jsdelivr.net/npm/bitwrench@2/dist/bitwrench.umd.min.js"></script>';
|
|
4271
|
+
} else if (runtime === 'shim') {
|
|
4272
|
+
runtimeHead = '<script>' + bw._FUNC_REGISTRY_SHIM + '</script>';
|
|
4273
|
+
}
|
|
4274
|
+
// runtime === 'none' → empty
|
|
4275
|
+
|
|
4276
|
+
// Theme CSS
|
|
4277
|
+
var themeCSS = '';
|
|
4278
|
+
if (theme) {
|
|
4279
|
+
var themeConfig = _is(theme, 'string')
|
|
4280
|
+
? (THEME_PRESETS[theme.toLowerCase()] || null)
|
|
4281
|
+
: theme;
|
|
4282
|
+
if (themeConfig) {
|
|
4283
|
+
var themeResult = bw.makeStyles(themeConfig);
|
|
4284
|
+
themeCSS = themeResult.css;
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
|
|
4288
|
+
// Extra <head> elements
|
|
4289
|
+
var headHTML = '';
|
|
4290
|
+
if (_isA(headExtra) && headExtra.length > 0) {
|
|
4291
|
+
headHTML = headExtra.map(function(el) { return bw.html(el); }).join('\n');
|
|
4292
|
+
}
|
|
4293
|
+
|
|
4294
|
+
// Favicon
|
|
4295
|
+
var faviconTag = '';
|
|
4296
|
+
if (favicon) {
|
|
4297
|
+
var safeFavicon = favicon.replace(/[&<>"']/g, function(c) {
|
|
4298
|
+
return ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c];
|
|
4299
|
+
});
|
|
4300
|
+
faviconTag = '<link rel="icon" href="' + safeFavicon + '">';
|
|
4301
|
+
}
|
|
4302
|
+
|
|
4303
|
+
// Escaped title
|
|
4304
|
+
var safeTitle = bw.escapeHTML(title);
|
|
4305
|
+
|
|
4306
|
+
// Combine all CSS
|
|
4307
|
+
var allCSS = (themeCSS ? themeCSS + '\n' : '') + css;
|
|
4308
|
+
|
|
4309
|
+
// Body-end script: registry entries + optional loadStyles
|
|
4310
|
+
var bodyEndScript = '';
|
|
4311
|
+
var bodyEndParts = [];
|
|
4312
|
+
if (registryEntries) {
|
|
4313
|
+
bodyEndParts.push(registryEntries);
|
|
4314
|
+
}
|
|
4315
|
+
if (runtime === 'inline' || runtime === 'cdn') {
|
|
4316
|
+
bodyEndParts.push('if(typeof bw!=="undefined"){bw.loadStyles();}');
|
|
4317
|
+
}
|
|
4318
|
+
if (bodyEndParts.length > 0) {
|
|
4319
|
+
bodyEndScript = '<script>\n' + bodyEndParts.join('\n') + '\n</script>';
|
|
4320
|
+
}
|
|
4321
|
+
|
|
4322
|
+
// Assemble document
|
|
4323
|
+
var parts = [
|
|
4324
|
+
'<!DOCTYPE html>',
|
|
4325
|
+
'<html lang="' + lang + '">',
|
|
4326
|
+
'<head>',
|
|
4327
|
+
'<meta charset="UTF-8">',
|
|
4328
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">'
|
|
4329
|
+
];
|
|
4330
|
+
parts.push('<title>' + safeTitle + '</title>');
|
|
4331
|
+
if (faviconTag) parts.push(faviconTag);
|
|
4332
|
+
if (runtimeHead) parts.push(runtimeHead);
|
|
4333
|
+
if (headHTML) parts.push(headHTML);
|
|
4334
|
+
if (allCSS) parts.push('<style>' + allCSS + '</style>');
|
|
4335
|
+
parts.push('</head>');
|
|
4336
|
+
parts.push('<body>');
|
|
4337
|
+
parts.push(bodyHTML);
|
|
4338
|
+
if (bodyEndScript) parts.push(bodyEndScript);
|
|
4339
|
+
parts.push('</body>');
|
|
4340
|
+
parts.push('</html>');
|
|
4341
|
+
|
|
4342
|
+
return parts.join('\n');
|
|
4343
|
+
};
|
|
4344
|
+
|
|
3818
4345
|
/**
|
|
3819
4346
|
* Create a live DOM element from a TACO object (browser only).
|
|
3820
4347
|
*
|
|
@@ -3859,7 +4386,7 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
3859
4386
|
}
|
|
3860
4387
|
|
|
3861
4388
|
// Handle text nodes
|
|
3862
|
-
if (
|
|
4389
|
+
if (!_is(taco, 'object') || !taco.t) {
|
|
3863
4390
|
return document.createTextNode(String(taco));
|
|
3864
4391
|
}
|
|
3865
4392
|
|
|
@@ -3872,16 +4399,16 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
3872
4399
|
for (const [key, value] of Object.entries(attrs)) {
|
|
3873
4400
|
if (value == null || value === false) continue;
|
|
3874
4401
|
|
|
3875
|
-
if (key === 'style' &&
|
|
4402
|
+
if (key === 'style' && _is(value, 'object')) {
|
|
3876
4403
|
// Apply styles directly
|
|
3877
4404
|
Object.assign(el.style, value);
|
|
3878
4405
|
} else if (key === 'class') {
|
|
3879
4406
|
// Handle class as array or string
|
|
3880
|
-
const classStr =
|
|
4407
|
+
const classStr = _isA(value) ? value.filter(Boolean).join(' ') : String(value);
|
|
3881
4408
|
if (classStr) {
|
|
3882
4409
|
el.className = classStr;
|
|
3883
4410
|
}
|
|
3884
|
-
} else if (key.startsWith('on') &&
|
|
4411
|
+
} else if (key.startsWith('on') && _is(value, 'function')) {
|
|
3885
4412
|
// Event handlers
|
|
3886
4413
|
const eventName = key.slice(2).toLowerCase();
|
|
3887
4414
|
el.addEventListener(eventName, value);
|
|
@@ -3901,7 +4428,7 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
3901
4428
|
// Children with data-bw_id or id attributes get local refs on the parent,
|
|
3902
4429
|
// so o.render functions can access them without any DOM lookup.
|
|
3903
4430
|
if (content != null) {
|
|
3904
|
-
if (
|
|
4431
|
+
if (_isA(content)) {
|
|
3905
4432
|
content.forEach(child => {
|
|
3906
4433
|
if (child != null) {
|
|
3907
4434
|
// Handle ComponentHandle in content arrays (Level 2 children)
|
|
@@ -3921,20 +4448,20 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
3921
4448
|
if (childEl._bw_refs) {
|
|
3922
4449
|
if (!el._bw_refs) el._bw_refs = {};
|
|
3923
4450
|
for (var rk in childEl._bw_refs) {
|
|
3924
|
-
if (
|
|
4451
|
+
if (_hop.call(childEl._bw_refs, rk)) {
|
|
3925
4452
|
el._bw_refs[rk] = childEl._bw_refs[rk];
|
|
3926
4453
|
}
|
|
3927
4454
|
}
|
|
3928
4455
|
}
|
|
3929
4456
|
}
|
|
3930
4457
|
});
|
|
3931
|
-
} else if (
|
|
4458
|
+
} else if (_is(content, 'object') && content.__bw_raw) {
|
|
3932
4459
|
// Raw HTML content — inject via innerHTML
|
|
3933
4460
|
el.innerHTML = content.v;
|
|
3934
4461
|
} else if (content._bwComponent === true) {
|
|
3935
4462
|
// Single ComponentHandle as content
|
|
3936
4463
|
content.mount(el);
|
|
3937
|
-
} else if (
|
|
4464
|
+
} else if (_is(content, 'object') && content.t) {
|
|
3938
4465
|
var childEl = bw.createDOM(content, options);
|
|
3939
4466
|
el.appendChild(childEl);
|
|
3940
4467
|
var childBwId = content.a ? (content.a['data-bw_id'] || content.a.id) : null;
|
|
@@ -3945,7 +4472,7 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
3945
4472
|
if (childEl._bw_refs) {
|
|
3946
4473
|
if (!el._bw_refs) el._bw_refs = {};
|
|
3947
4474
|
for (var rk in childEl._bw_refs) {
|
|
3948
|
-
if (
|
|
4475
|
+
if (_hop.call(childEl._bw_refs, rk)) {
|
|
3949
4476
|
el._bw_refs[rk] = childEl._bw_refs[rk];
|
|
3950
4477
|
}
|
|
3951
4478
|
}
|
|
@@ -3960,6 +4487,14 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
3960
4487
|
bw._registerNode(el, null);
|
|
3961
4488
|
}
|
|
3962
4489
|
|
|
4490
|
+
// Register UUID class in node cache (bw_uuid_* tokens in class string)
|
|
4491
|
+
if (el.className) {
|
|
4492
|
+
var uuidMatch = el.className.match(_UUID_RE);
|
|
4493
|
+
if (uuidMatch) {
|
|
4494
|
+
bw._nodeMap[uuidMatch[0]] = el;
|
|
4495
|
+
}
|
|
4496
|
+
}
|
|
4497
|
+
|
|
3963
4498
|
// Handle lifecycle hooks and state
|
|
3964
4499
|
if (opts.mounted || opts.unmount || opts.render || opts.state) {
|
|
3965
4500
|
const id = attrs['data-bw_id'] || bw.uuid();
|
|
@@ -3978,7 +4513,7 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
3978
4513
|
el._bw_render = opts.render;
|
|
3979
4514
|
|
|
3980
4515
|
if (opts.mounted) {
|
|
3981
|
-
|
|
4516
|
+
_cw('bw.createDOM: o.render and o.mounted are mutually exclusive. o.render wins.');
|
|
3982
4517
|
}
|
|
3983
4518
|
|
|
3984
4519
|
// Queue initial render (same timing as mounted)
|
|
@@ -4051,7 +4586,7 @@ bw.DOM = function(target, taco, options = {}) {
|
|
|
4051
4586
|
const targetEl = bw._el(target);
|
|
4052
4587
|
|
|
4053
4588
|
if (!targetEl) {
|
|
4054
|
-
|
|
4589
|
+
_ce('bw.DOM: Target element not found:', target);
|
|
4055
4590
|
return null;
|
|
4056
4591
|
}
|
|
4057
4592
|
|
|
@@ -4091,7 +4626,7 @@ bw.DOM = function(target, taco, options = {}) {
|
|
|
4091
4626
|
targetEl.appendChild(taco.element);
|
|
4092
4627
|
}
|
|
4093
4628
|
// Handle arrays
|
|
4094
|
-
else if (
|
|
4629
|
+
else if (_isA(taco)) {
|
|
4095
4630
|
taco.forEach(t => {
|
|
4096
4631
|
if (t != null) {
|
|
4097
4632
|
if (t._bwComponent === true) {
|
|
@@ -4127,7 +4662,7 @@ bw.DOM = function(target, taco, options = {}) {
|
|
|
4127
4662
|
bw.compileProps = function(handle, props = {}) {
|
|
4128
4663
|
const compiledProps = {};
|
|
4129
4664
|
|
|
4130
|
-
|
|
4665
|
+
_keys(props).forEach(key => {
|
|
4131
4666
|
// Create getter/setter for each prop
|
|
4132
4667
|
Object.defineProperty(compiledProps, key, {
|
|
4133
4668
|
get() {
|
|
@@ -4332,6 +4867,16 @@ bw.renderComponent = function(taco, options = {}) {
|
|
|
4332
4867
|
bw.cleanup = function(element) {
|
|
4333
4868
|
if (!bw._isBrowser || !element) return;
|
|
4334
4869
|
|
|
4870
|
+
// Deregister UUID classes from node cache (element + descendants)
|
|
4871
|
+
// Covers elements that have UUID but no data-bw_id
|
|
4872
|
+
var selfUuidMatch = element.className && element.className.match(_UUID_RE);
|
|
4873
|
+
if (selfUuidMatch) delete bw._nodeMap[selfUuidMatch[0]];
|
|
4874
|
+
var uuidEls = element.querySelectorAll('[class*="bw_uuid_"]');
|
|
4875
|
+
uuidEls.forEach(function(uel) {
|
|
4876
|
+
var m = uel.className && uel.className.match(_UUID_RE);
|
|
4877
|
+
if (m) delete bw._nodeMap[m[0]];
|
|
4878
|
+
});
|
|
4879
|
+
|
|
4335
4880
|
// Find all elements with data-bw_id
|
|
4336
4881
|
const elements = element.querySelectorAll('[data-bw_id]');
|
|
4337
4882
|
|
|
@@ -4347,6 +4892,10 @@ bw.cleanup = function(element) {
|
|
|
4347
4892
|
// Deregister from node cache
|
|
4348
4893
|
bw._deregisterNode(el, id);
|
|
4349
4894
|
|
|
4895
|
+
// Deregister UUID class from node cache
|
|
4896
|
+
var uuidMatch = el.className && el.className.match(_UUID_RE);
|
|
4897
|
+
if (uuidMatch) delete bw._nodeMap[uuidMatch[0]];
|
|
4898
|
+
|
|
4350
4899
|
// Clean up pub/sub subscriptions tied to this element
|
|
4351
4900
|
if (el._bw_subs) {
|
|
4352
4901
|
el._bw_subs.forEach(function(unsub) { unsub(); });
|
|
@@ -4371,6 +4920,10 @@ bw.cleanup = function(element) {
|
|
|
4371
4920
|
// Deregister from node cache
|
|
4372
4921
|
bw._deregisterNode(element, id);
|
|
4373
4922
|
|
|
4923
|
+
// Deregister UUID class from node cache
|
|
4924
|
+
var elemUuidMatch = element.className && element.className.match(_UUID_RE);
|
|
4925
|
+
if (elemUuidMatch) delete bw._nodeMap[elemUuidMatch[0]];
|
|
4926
|
+
|
|
4374
4927
|
// Clean up pub/sub subscriptions tied to element itself
|
|
4375
4928
|
if (element._bw_subs) {
|
|
4376
4929
|
element._bw_subs.forEach(function(unsub) { unsub(); });
|
|
@@ -4445,17 +4998,17 @@ bw.patch = function(id, content, attr) {
|
|
|
4445
4998
|
if (attr) {
|
|
4446
4999
|
// Patch an attribute
|
|
4447
5000
|
el.setAttribute(attr, String(content));
|
|
4448
|
-
} else if (
|
|
5001
|
+
} else if (_isA(content)) {
|
|
4449
5002
|
// Patch with array of children (strings and/or TACOs)
|
|
4450
5003
|
el.innerHTML = '';
|
|
4451
5004
|
content.forEach(function(item) {
|
|
4452
|
-
if (
|
|
5005
|
+
if (_is(item, 'string') || _is(item, 'number')) {
|
|
4453
5006
|
el.appendChild(document.createTextNode(String(item)));
|
|
4454
5007
|
} else if (item && item.t) {
|
|
4455
5008
|
el.appendChild(bw.createDOM(item));
|
|
4456
5009
|
}
|
|
4457
5010
|
});
|
|
4458
|
-
} else if (
|
|
5011
|
+
} else if (_is(content, 'object') && content.t) {
|
|
4459
5012
|
// Patch with a TACO — replace children
|
|
4460
5013
|
el.innerHTML = '';
|
|
4461
5014
|
el.appendChild(bw.createDOM(content));
|
|
@@ -4486,7 +5039,7 @@ bw.patch = function(id, content, attr) {
|
|
|
4486
5039
|
bw.patchAll = function(patches) {
|
|
4487
5040
|
var results = {};
|
|
4488
5041
|
for (var id in patches) {
|
|
4489
|
-
if (
|
|
5042
|
+
if (_hop.call(patches, id)) {
|
|
4490
5043
|
results[id] = bw.patch(id, patches[id]);
|
|
4491
5044
|
}
|
|
4492
5045
|
}
|
|
@@ -4583,7 +5136,7 @@ bw.pub = function(topic, detail) {
|
|
|
4583
5136
|
snapshot[i].handler(detail);
|
|
4584
5137
|
called++;
|
|
4585
5138
|
} catch (err) {
|
|
4586
|
-
|
|
5139
|
+
_cw('bw.pub: subscriber error on topic "' + topic + '":', err);
|
|
4587
5140
|
}
|
|
4588
5141
|
}
|
|
4589
5142
|
return called;
|
|
@@ -4679,8 +5232,8 @@ bw._fnIDCounter = 0;
|
|
|
4679
5232
|
* @see bw.funcGetDispatchStr
|
|
4680
5233
|
*/
|
|
4681
5234
|
bw.funcRegister = function(fn, name) {
|
|
4682
|
-
if (
|
|
4683
|
-
var fnID = (
|
|
5235
|
+
if (!_is(fn, 'function')) return '';
|
|
5236
|
+
var fnID = (_is(name, 'string') && name.length > 0) ? name : ('bw_fn_' + bw._fnIDCounter++);
|
|
4684
5237
|
bw._fnRegistry[fnID] = fn;
|
|
4685
5238
|
return fnID;
|
|
4686
5239
|
};
|
|
@@ -4699,7 +5252,7 @@ bw.funcRegister = function(fn, name) {
|
|
|
4699
5252
|
bw.funcGetById = function(name, errFn) {
|
|
4700
5253
|
name = String(name);
|
|
4701
5254
|
if (name in bw._fnRegistry) return bw._fnRegistry[name];
|
|
4702
|
-
return (
|
|
5255
|
+
return _is(errFn, 'function') ? errFn : function() { _cw('bw.funcGetById: unregistered fn "' + name + '"'); };
|
|
4703
5256
|
};
|
|
4704
5257
|
|
|
4705
5258
|
/**
|
|
@@ -4740,13 +5293,30 @@ bw.funcUnregister = function(name) {
|
|
|
4740
5293
|
bw.funcGetRegistry = function() {
|
|
4741
5294
|
var copy = {};
|
|
4742
5295
|
for (var k in bw._fnRegistry) {
|
|
4743
|
-
if (
|
|
5296
|
+
if (_hop.call(bw._fnRegistry, k)) {
|
|
4744
5297
|
copy[k] = bw._fnRegistry[k];
|
|
4745
5298
|
}
|
|
4746
5299
|
}
|
|
4747
5300
|
return copy;
|
|
4748
5301
|
};
|
|
4749
5302
|
|
|
5303
|
+
/**
|
|
5304
|
+
* Minimal runtime shim for funcRegister dispatch in static HTML.
|
|
5305
|
+
* When embedded in a `<script>` tag, provides just enough infrastructure
|
|
5306
|
+
* for `bw.funcGetById()` calls to resolve. The actual function bodies
|
|
5307
|
+
* are emitted separately as `bw._fnRegistry['bw_fn_X'] = ...;` assignments.
|
|
5308
|
+
* @type {string}
|
|
5309
|
+
* @category Function Registry
|
|
5310
|
+
*/
|
|
5311
|
+
bw._FUNC_REGISTRY_SHIM = '(function(){var bw=window.bw||(window.bw={});' +
|
|
5312
|
+
'if(!bw._fnRegistry)bw._fnRegistry={};' +
|
|
5313
|
+
'bw.funcGetById=function(n){return bw._fnRegistry[n]||function(){' +
|
|
5314
|
+
'console.warn("bw: unregistered fn "+n)};};' +
|
|
5315
|
+
'bw.funcRegister=function(fn,name){' +
|
|
5316
|
+
'var id=name||("bw_fn_"+(bw._fnIDCounter=(bw._fnIDCounter||0)+1));' +
|
|
5317
|
+
'bw._fnRegistry[id]=fn;return id;};' +
|
|
5318
|
+
'window.bw=bw;})();';
|
|
5319
|
+
|
|
4750
5320
|
// ===================================================================================
|
|
4751
5321
|
// Template Binding Utilities
|
|
4752
5322
|
// ===================================================================================
|
|
@@ -4774,7 +5344,10 @@ bw._evaluatePath = function(state, path) {
|
|
|
4774
5344
|
var parts = path.split('.');
|
|
4775
5345
|
var val = state;
|
|
4776
5346
|
for (var i = 0; i < parts.length; i++) {
|
|
4777
|
-
if (val == null)
|
|
5347
|
+
if (val == null) {
|
|
5348
|
+
if (bw.debug) _cw('bw.debug: _evaluatePath — null at key "' + parts[i] + '" in path "' + path + '"');
|
|
5349
|
+
return '';
|
|
5350
|
+
}
|
|
4778
5351
|
val = val[parts[i]];
|
|
4779
5352
|
}
|
|
4780
5353
|
return (val == null) ? '' : val;
|
|
@@ -4794,7 +5367,7 @@ bw._evaluatePath = function(state, path) {
|
|
|
4794
5367
|
*/
|
|
4795
5368
|
bw._compiledExprs = {};
|
|
4796
5369
|
bw._resolveTemplate = function(str, state, compile) {
|
|
4797
|
-
if (
|
|
5370
|
+
if (!_is(str, 'string') || str.indexOf('${') < 0) return str;
|
|
4798
5371
|
var bindings = bw._parseBindings(str);
|
|
4799
5372
|
if (bindings.length === 0) return str;
|
|
4800
5373
|
|
|
@@ -4816,6 +5389,7 @@ bw._resolveTemplate = function(str, state, compile) {
|
|
|
4816
5389
|
try {
|
|
4817
5390
|
val = bw._compiledExprs[b.expr](state);
|
|
4818
5391
|
} catch (e) {
|
|
5392
|
+
if (bw.debug) _cw('bw.debug: _resolveTemplate — Tier 2 eval failed for "${' + b.expr + '}":', e.message);
|
|
4819
5393
|
val = '';
|
|
4820
5394
|
}
|
|
4821
5395
|
} else {
|
|
@@ -4924,7 +5498,7 @@ function ComponentHandle(taco) {
|
|
|
4924
5498
|
this._state = {};
|
|
4925
5499
|
if (o.state) {
|
|
4926
5500
|
for (var k in o.state) {
|
|
4927
|
-
if (
|
|
5501
|
+
if (_hop.call(o.state, k)) {
|
|
4928
5502
|
this._state[k] = o.state[k];
|
|
4929
5503
|
}
|
|
4930
5504
|
}
|
|
@@ -4933,7 +5507,7 @@ function ComponentHandle(taco) {
|
|
|
4933
5507
|
this._actions = {};
|
|
4934
5508
|
if (o.actions) {
|
|
4935
5509
|
for (var k2 in o.actions) {
|
|
4936
|
-
if (
|
|
5510
|
+
if (_hop.call(o.actions, k2)) {
|
|
4937
5511
|
this._actions[k2] = o.actions[k2];
|
|
4938
5512
|
}
|
|
4939
5513
|
}
|
|
@@ -4943,7 +5517,7 @@ function ComponentHandle(taco) {
|
|
|
4943
5517
|
if (o.methods) {
|
|
4944
5518
|
var self = this;
|
|
4945
5519
|
for (var k3 in o.methods) {
|
|
4946
|
-
if (
|
|
5520
|
+
if (_hop.call(o.methods, k3)) {
|
|
4947
5521
|
this._methods[k3] = o.methods[k3];
|
|
4948
5522
|
(function(methodName, methodFn) {
|
|
4949
5523
|
self[methodName] = function() {
|
|
@@ -4961,7 +5535,7 @@ function ComponentHandle(taco) {
|
|
|
4961
5535
|
willMount: o.willMount || null,
|
|
4962
5536
|
mounted: o.mounted || null,
|
|
4963
5537
|
willUpdate: o.willUpdate || null,
|
|
4964
|
-
onUpdate: o.onUpdate || null,
|
|
5538
|
+
onUpdate: o.onUpdate || o.updated || null,
|
|
4965
5539
|
unmount: o.unmount || null,
|
|
4966
5540
|
willDestroy: o.willDestroy || null
|
|
4967
5541
|
};
|
|
@@ -4976,14 +5550,23 @@ function ComponentHandle(taco) {
|
|
|
4976
5550
|
this._compile = !!o.compile;
|
|
4977
5551
|
this._bw_refs = {};
|
|
4978
5552
|
this._refCounter = 0;
|
|
5553
|
+
// Child component ownership (Bug #5)
|
|
5554
|
+
this._children = [];
|
|
5555
|
+
this._parent = null;
|
|
5556
|
+
// Factory metadata for BCCL rebuild (Bug #6)
|
|
5557
|
+
this._factory = taco._bwFactory || null;
|
|
4979
5558
|
}
|
|
4980
5559
|
|
|
5560
|
+
// Short alias for ComponentHandle.prototype (see alias block at top of file).
|
|
5561
|
+
// 28 method definitions × 25 chars = ~700B raw savings in minified output.
|
|
5562
|
+
var _chp = ComponentHandle.prototype;
|
|
5563
|
+
|
|
4981
5564
|
// ── State Methods ──
|
|
4982
5565
|
|
|
4983
5566
|
/**
|
|
4984
5567
|
* Get a state value. Dot-path supported: `get('user.name')`
|
|
4985
5568
|
*/
|
|
4986
|
-
|
|
5569
|
+
_chp.get = function(key) {
|
|
4987
5570
|
return bw._evaluatePath(this._state, key);
|
|
4988
5571
|
};
|
|
4989
5572
|
|
|
@@ -4993,12 +5576,13 @@ ComponentHandle.prototype.get = function(key) {
|
|
|
4993
5576
|
* @param {*} value - New value
|
|
4994
5577
|
* @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
|
|
4995
5578
|
*/
|
|
4996
|
-
|
|
5579
|
+
_chp.set = function(key, value, opts) {
|
|
4997
5580
|
// Dot-path set
|
|
4998
5581
|
var parts = key.split('.');
|
|
4999
5582
|
var obj = this._state;
|
|
5000
5583
|
for (var i = 0; i < parts.length - 1; i++) {
|
|
5001
|
-
if (obj[parts[i]]
|
|
5584
|
+
if (!_is(obj[parts[i]], 'object')) {
|
|
5585
|
+
if (bw.debug) _cw('bw.debug: set() — auto-creating intermediate "' + parts[i] + '" in path "' + key + '"');
|
|
5002
5586
|
obj[parts[i]] = {};
|
|
5003
5587
|
}
|
|
5004
5588
|
obj = obj[parts[i]];
|
|
@@ -5018,10 +5602,10 @@ ComponentHandle.prototype.set = function(key, value, opts) {
|
|
|
5018
5602
|
/**
|
|
5019
5603
|
* Get a shallow clone of the full state.
|
|
5020
5604
|
*/
|
|
5021
|
-
|
|
5605
|
+
_chp.getState = function() {
|
|
5022
5606
|
var clone = {};
|
|
5023
5607
|
for (var k in this._state) {
|
|
5024
|
-
if (
|
|
5608
|
+
if (_hop.call(this._state, k)) {
|
|
5025
5609
|
clone[k] = this._state[k];
|
|
5026
5610
|
}
|
|
5027
5611
|
}
|
|
@@ -5033,9 +5617,9 @@ ComponentHandle.prototype.getState = function() {
|
|
|
5033
5617
|
* @param {Object} updates - Key-value pairs to merge
|
|
5034
5618
|
* @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
|
|
5035
5619
|
*/
|
|
5036
|
-
|
|
5620
|
+
_chp.setState = function(updates, opts) {
|
|
5037
5621
|
for (var k in updates) {
|
|
5038
|
-
if (
|
|
5622
|
+
if (_hop.call(updates, k)) {
|
|
5039
5623
|
this._state[k] = updates[k];
|
|
5040
5624
|
this._dirtyKeys[k] = true;
|
|
5041
5625
|
}
|
|
@@ -5052,9 +5636,9 @@ ComponentHandle.prototype.setState = function(updates, opts) {
|
|
|
5052
5636
|
/**
|
|
5053
5637
|
* Push a value onto an array in state. Clones the array.
|
|
5054
5638
|
*/
|
|
5055
|
-
|
|
5639
|
+
_chp.push = function(key, val) {
|
|
5056
5640
|
var arr = this.get(key);
|
|
5057
|
-
var newArr =
|
|
5641
|
+
var newArr = _isA(arr) ? arr.slice() : [];
|
|
5058
5642
|
newArr.push(val);
|
|
5059
5643
|
this.set(key, newArr);
|
|
5060
5644
|
};
|
|
@@ -5062,9 +5646,9 @@ ComponentHandle.prototype.push = function(key, val) {
|
|
|
5062
5646
|
/**
|
|
5063
5647
|
* Splice an array in state. Clones the array.
|
|
5064
5648
|
*/
|
|
5065
|
-
|
|
5649
|
+
_chp.splice = function(key, start, deleteCount) {
|
|
5066
5650
|
var arr = this.get(key);
|
|
5067
|
-
var newArr =
|
|
5651
|
+
var newArr = _isA(arr) ? arr.slice() : [];
|
|
5068
5652
|
var args = [start, deleteCount].concat(Array.prototype.slice.call(arguments, 3));
|
|
5069
5653
|
Array.prototype.splice.apply(newArr, args);
|
|
5070
5654
|
this.set(key, newArr);
|
|
@@ -5072,7 +5656,7 @@ ComponentHandle.prototype.splice = function(key, start, deleteCount) {
|
|
|
5072
5656
|
|
|
5073
5657
|
// ── Scheduling ──
|
|
5074
5658
|
|
|
5075
|
-
|
|
5659
|
+
_chp._scheduleDirty = function() {
|
|
5076
5660
|
if (!this._scheduled) {
|
|
5077
5661
|
this._scheduled = true;
|
|
5078
5662
|
bw._dirtyComponents.push(this);
|
|
@@ -5087,17 +5671,17 @@ ComponentHandle.prototype._scheduleDirty = function() {
|
|
|
5087
5671
|
* Creates binding descriptors with refIds for targeted DOM updates.
|
|
5088
5672
|
* @private
|
|
5089
5673
|
*/
|
|
5090
|
-
|
|
5674
|
+
_chp._compileBindings = function() {
|
|
5091
5675
|
this._bindings = [];
|
|
5092
5676
|
this._refCounter = 0;
|
|
5093
|
-
var stateKeys =
|
|
5677
|
+
var stateKeys = _keys(this._state);
|
|
5094
5678
|
var self = this;
|
|
5095
5679
|
|
|
5096
5680
|
function walkTaco(taco, path) {
|
|
5097
|
-
if (taco
|
|
5681
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
5098
5682
|
|
|
5099
5683
|
// Check content for bindings
|
|
5100
|
-
if (
|
|
5684
|
+
if (_is(taco.c, 'string') && taco.c.indexOf('${') >= 0) {
|
|
5101
5685
|
var refId = 'bw_ref_' + self._refCounter++;
|
|
5102
5686
|
var parsed = bw._parseBindings(taco.c);
|
|
5103
5687
|
var deps = [];
|
|
@@ -5119,10 +5703,10 @@ ComponentHandle.prototype._compileBindings = function() {
|
|
|
5119
5703
|
// Check attributes for bindings
|
|
5120
5704
|
if (taco.a) {
|
|
5121
5705
|
for (var attrName in taco.a) {
|
|
5122
|
-
if (!
|
|
5706
|
+
if (!_hop.call(taco.a, attrName)) continue;
|
|
5123
5707
|
if (attrName === 'data-bw_ref') continue;
|
|
5124
5708
|
var attrVal = taco.a[attrName];
|
|
5125
|
-
if (
|
|
5709
|
+
if (_is(attrVal, 'string') && attrVal.indexOf('${') >= 0) {
|
|
5126
5710
|
var refId2 = 'bw_ref_' + self._refCounter++;
|
|
5127
5711
|
var parsed2 = bw._parseBindings(attrVal);
|
|
5128
5712
|
var deps2 = [];
|
|
@@ -5148,9 +5732,27 @@ ComponentHandle.prototype._compileBindings = function() {
|
|
|
5148
5732
|
}
|
|
5149
5733
|
|
|
5150
5734
|
// Recurse into children
|
|
5151
|
-
if (
|
|
5735
|
+
if (_isA(taco.c)) {
|
|
5152
5736
|
for (var i = 0; i < taco.c.length; i++) {
|
|
5153
|
-
|
|
5737
|
+
// Wrap string children with ${expr} in a span so patches target the span, not the parent
|
|
5738
|
+
if (_is(taco.c[i], 'string') && taco.c[i].indexOf('${') >= 0) {
|
|
5739
|
+
var mixedRefId = 'bw_ref_' + self._refCounter++;
|
|
5740
|
+
var mixedParsed = bw._parseBindings(taco.c[i]);
|
|
5741
|
+
var mixedDeps = [];
|
|
5742
|
+
for (var mi = 0; mi < mixedParsed.length; mi++) {
|
|
5743
|
+
mixedDeps = mixedDeps.concat(bw._extractDeps(mixedParsed[mi].expr, stateKeys));
|
|
5744
|
+
}
|
|
5745
|
+
self._bindings.push({
|
|
5746
|
+
expr: taco.c[i],
|
|
5747
|
+
type: 'content',
|
|
5748
|
+
refId: mixedRefId,
|
|
5749
|
+
deps: mixedDeps,
|
|
5750
|
+
template: taco.c[i]
|
|
5751
|
+
});
|
|
5752
|
+
// Replace string with a span wrapper so textContent targets the span only
|
|
5753
|
+
taco.c[i] = { t: 'span', a: { 'data-bw_ref': mixedRefId, style: 'display:contents' }, c: taco.c[i] };
|
|
5754
|
+
}
|
|
5755
|
+
if (_is(taco.c[i], 'object') && taco.c[i].t) {
|
|
5154
5756
|
walkTaco(taco.c[i], path.concat(i));
|
|
5155
5757
|
}
|
|
5156
5758
|
// Handle bw.when/bw.each markers
|
|
@@ -5185,7 +5787,7 @@ ComponentHandle.prototype._compileBindings = function() {
|
|
|
5185
5787
|
taco.c[i]._refId = eachRefId;
|
|
5186
5788
|
}
|
|
5187
5789
|
}
|
|
5188
|
-
} else if (taco.c
|
|
5790
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
5189
5791
|
walkTaco(taco.c, path.concat(0));
|
|
5190
5792
|
}
|
|
5191
5793
|
|
|
@@ -5201,7 +5803,7 @@ ComponentHandle.prototype._compileBindings = function() {
|
|
|
5201
5803
|
* Build ref map from the live DOM after createDOM.
|
|
5202
5804
|
* @private
|
|
5203
5805
|
*/
|
|
5204
|
-
|
|
5806
|
+
_chp._collectRefs = function() {
|
|
5205
5807
|
this._bw_refs = {};
|
|
5206
5808
|
if (!this.element) return;
|
|
5207
5809
|
var els = this.element.querySelectorAll('[data-bw_ref]');
|
|
@@ -5222,7 +5824,7 @@ ComponentHandle.prototype._collectRefs = function() {
|
|
|
5222
5824
|
* Creates DOM, compiles bindings, registers actions, and calls lifecycle hooks.
|
|
5223
5825
|
* @param {Element} parentEl - DOM element to mount into
|
|
5224
5826
|
*/
|
|
5225
|
-
|
|
5827
|
+
_chp.mount = function(parentEl) {
|
|
5226
5828
|
// willMount hook
|
|
5227
5829
|
if (this._hooks.willMount) this._hooks.willMount(this);
|
|
5228
5830
|
|
|
@@ -5244,7 +5846,7 @@ ComponentHandle.prototype.mount = function(parentEl) {
|
|
|
5244
5846
|
// Register named actions in function registry
|
|
5245
5847
|
var self = this;
|
|
5246
5848
|
for (var actionName in this._actions) {
|
|
5247
|
-
if (
|
|
5849
|
+
if (_hop.call(this._actions, actionName)) {
|
|
5248
5850
|
var registeredName = this._bwId + '_' + actionName;
|
|
5249
5851
|
(function(aName) {
|
|
5250
5852
|
bw.funcRegister(function(evt) {
|
|
@@ -5263,6 +5865,11 @@ ComponentHandle.prototype.mount = function(parentEl) {
|
|
|
5263
5865
|
this.element = bw.createDOM(tacoForDOM);
|
|
5264
5866
|
this.element._bwComponentHandle = this;
|
|
5265
5867
|
this.element.setAttribute('data-bw_comp_id', this._bwId);
|
|
5868
|
+
|
|
5869
|
+
// Restore o.render from original TACO (stripped by _tacoForDOM)
|
|
5870
|
+
if (this.taco.o && this.taco.o.render) {
|
|
5871
|
+
this.element._bw_render = this.taco.o.render;
|
|
5872
|
+
}
|
|
5266
5873
|
if (this._userTag) {
|
|
5267
5874
|
this.element.classList.add(this._userTag);
|
|
5268
5875
|
}
|
|
@@ -5278,6 +5885,16 @@ ComponentHandle.prototype.mount = function(parentEl) {
|
|
|
5278
5885
|
|
|
5279
5886
|
this.mounted = true;
|
|
5280
5887
|
|
|
5888
|
+
// Scan for child ComponentHandles and link parent/child (Bug #5)
|
|
5889
|
+
var childEls = this.element.querySelectorAll('[data-bw_comp_id]');
|
|
5890
|
+
for (var ci = 0; ci < childEls.length; ci++) {
|
|
5891
|
+
var ch = childEls[ci]._bwComponentHandle;
|
|
5892
|
+
if (ch && ch !== this && !ch._parent) {
|
|
5893
|
+
ch._parent = this;
|
|
5894
|
+
this._children.push(ch);
|
|
5895
|
+
}
|
|
5896
|
+
}
|
|
5897
|
+
|
|
5281
5898
|
// mounted hook (backward compat: fn.length === 2 wraps (el, state))
|
|
5282
5899
|
if (this._hooks.mounted) {
|
|
5283
5900
|
if (this._hooks.mounted.length === 2) {
|
|
@@ -5286,16 +5903,21 @@ ComponentHandle.prototype.mount = function(parentEl) {
|
|
|
5286
5903
|
this._hooks.mounted(this);
|
|
5287
5904
|
}
|
|
5288
5905
|
}
|
|
5906
|
+
|
|
5907
|
+
// Invoke o.render on initial mount (if present)
|
|
5908
|
+
if (this.element._bw_render) {
|
|
5909
|
+
this.element._bw_render(this.element, this._state);
|
|
5910
|
+
}
|
|
5289
5911
|
};
|
|
5290
5912
|
|
|
5291
5913
|
/**
|
|
5292
5914
|
* Prepare TACO for initial render: resolve when/each markers.
|
|
5293
5915
|
* @private
|
|
5294
5916
|
*/
|
|
5295
|
-
|
|
5296
|
-
if (!taco
|
|
5917
|
+
_chp._prepareTaco = function(taco) {
|
|
5918
|
+
if (!_is(taco, 'object')) return;
|
|
5297
5919
|
|
|
5298
|
-
if (
|
|
5920
|
+
if (_isA(taco.c)) {
|
|
5299
5921
|
for (var i = taco.c.length - 1; i >= 0; i--) {
|
|
5300
5922
|
var child = taco.c[i];
|
|
5301
5923
|
if (child && child._bwWhen) {
|
|
@@ -5320,18 +5942,18 @@ ComponentHandle.prototype._prepareTaco = function(taco) {
|
|
|
5320
5942
|
var eachExprStr = child.expr.replace(/^\$\{|\}$/g, '');
|
|
5321
5943
|
var arr = bw._evaluatePath(this._state, eachExprStr);
|
|
5322
5944
|
var items = [];
|
|
5323
|
-
if (
|
|
5945
|
+
if (_isA(arr)) {
|
|
5324
5946
|
for (var j = 0; j < arr.length; j++) {
|
|
5325
5947
|
items.push(child.factory(arr[j], j));
|
|
5326
5948
|
}
|
|
5327
5949
|
}
|
|
5328
5950
|
taco.c[i] = { t: 'span', a: { 'data-bw_each': child._refId, style: 'display:contents' }, c: items };
|
|
5329
5951
|
}
|
|
5330
|
-
if (taco.c[i]
|
|
5952
|
+
if (_is(taco.c[i], 'object') && taco.c[i].t) {
|
|
5331
5953
|
this._prepareTaco(taco.c[i]);
|
|
5332
5954
|
}
|
|
5333
5955
|
}
|
|
5334
|
-
} else if (taco.c
|
|
5956
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
5335
5957
|
this._prepareTaco(taco.c);
|
|
5336
5958
|
}
|
|
5337
5959
|
};
|
|
@@ -5340,12 +5962,12 @@ ComponentHandle.prototype._prepareTaco = function(taco) {
|
|
|
5340
5962
|
* Wire action name strings (in onclick etc.) to dispatch function calls.
|
|
5341
5963
|
* @private
|
|
5342
5964
|
*/
|
|
5343
|
-
|
|
5344
|
-
if (!taco
|
|
5965
|
+
_chp._wireActions = function(taco) {
|
|
5966
|
+
if (!_is(taco, 'object') || !taco.t) return;
|
|
5345
5967
|
if (taco.a) {
|
|
5346
5968
|
for (var key in taco.a) {
|
|
5347
|
-
if (!
|
|
5348
|
-
if (key.startsWith('on') &&
|
|
5969
|
+
if (!_hop.call(taco.a, key)) continue;
|
|
5970
|
+
if (key.startsWith('on') && _is(taco.a[key], 'string')) {
|
|
5349
5971
|
var actionName = taco.a[key];
|
|
5350
5972
|
if (actionName in this._actions) {
|
|
5351
5973
|
var registeredName = this._bwId + '_' + actionName;
|
|
@@ -5359,11 +5981,11 @@ ComponentHandle.prototype._wireActions = function(taco) {
|
|
|
5359
5981
|
}
|
|
5360
5982
|
}
|
|
5361
5983
|
}
|
|
5362
|
-
if (
|
|
5984
|
+
if (_isA(taco.c)) {
|
|
5363
5985
|
for (var i = 0; i < taco.c.length; i++) {
|
|
5364
5986
|
this._wireActions(taco.c[i]);
|
|
5365
5987
|
}
|
|
5366
|
-
} else if (taco.c
|
|
5988
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
5367
5989
|
this._wireActions(taco.c);
|
|
5368
5990
|
}
|
|
5369
5991
|
};
|
|
@@ -5372,7 +5994,7 @@ ComponentHandle.prototype._wireActions = function(taco) {
|
|
|
5372
5994
|
* Deep-clone a TACO tree, preserving _bwWhen/_bwEach markers and their factories.
|
|
5373
5995
|
* @private
|
|
5374
5996
|
*/
|
|
5375
|
-
|
|
5997
|
+
_chp._deepCloneTaco = function(taco) {
|
|
5376
5998
|
if (taco == null) return taco;
|
|
5377
5999
|
// Preserve _bwWhen / _bwEach markers (contain functions)
|
|
5378
6000
|
if (taco._bwWhen) {
|
|
@@ -5384,18 +6006,18 @@ ComponentHandle.prototype._deepCloneTaco = function(taco) {
|
|
|
5384
6006
|
if (taco._bwEach) {
|
|
5385
6007
|
return { _bwEach: true, expr: taco.expr, factory: taco.factory, _refId: taco._refId };
|
|
5386
6008
|
}
|
|
5387
|
-
if (
|
|
6009
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
5388
6010
|
var result = { t: taco.t };
|
|
5389
6011
|
if (taco.a) {
|
|
5390
6012
|
result.a = {};
|
|
5391
6013
|
for (var k in taco.a) {
|
|
5392
|
-
if (
|
|
6014
|
+
if (_hop.call(taco.a, k)) result.a[k] = taco.a[k];
|
|
5393
6015
|
}
|
|
5394
6016
|
}
|
|
5395
6017
|
if (taco.c != null) {
|
|
5396
|
-
if (
|
|
6018
|
+
if (_isA(taco.c)) {
|
|
5397
6019
|
result.c = taco.c.map(function(child) { return this._deepCloneTaco(child); }.bind(this));
|
|
5398
|
-
} else if (
|
|
6020
|
+
} else if (_is(taco.c, 'object')) {
|
|
5399
6021
|
result.c = this._deepCloneTaco(taco.c);
|
|
5400
6022
|
} else {
|
|
5401
6023
|
result.c = taco.c;
|
|
@@ -5409,27 +6031,31 @@ ComponentHandle.prototype._deepCloneTaco = function(taco) {
|
|
|
5409
6031
|
* Create a copy of TACO suitable for createDOM (strips o to prevent double lifecycle).
|
|
5410
6032
|
* @private
|
|
5411
6033
|
*/
|
|
5412
|
-
|
|
5413
|
-
if (!taco
|
|
6034
|
+
_chp._tacoForDOM = function(taco) {
|
|
6035
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
5414
6036
|
var result = { t: taco.t };
|
|
5415
6037
|
if (taco.a) result.a = taco.a;
|
|
5416
6038
|
if (taco.c != null) {
|
|
5417
|
-
if (
|
|
6039
|
+
if (_isA(taco.c)) {
|
|
5418
6040
|
result.c = taco.c.map(function(child) { return this._tacoForDOM(child); }.bind(this));
|
|
5419
|
-
} else if (
|
|
6041
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
5420
6042
|
result.c = this._tacoForDOM(taco.c);
|
|
5421
6043
|
} else {
|
|
5422
6044
|
result.c = taco.c;
|
|
5423
6045
|
}
|
|
5424
6046
|
}
|
|
5425
6047
|
// Intentionally strip o (no mounted/unmount/state/render on sub-elements)
|
|
6048
|
+
if (taco.o && (taco.o.mounted || taco.o.render || taco.o.unmount)) {
|
|
6049
|
+
_cw('bw: _tacoForDOM stripped o.mounted/render/unmount from child <' + taco.t +
|
|
6050
|
+
'>. Use onclick attribute or bw.component() for child interactivity.');
|
|
6051
|
+
}
|
|
5426
6052
|
return result;
|
|
5427
6053
|
};
|
|
5428
6054
|
|
|
5429
6055
|
/**
|
|
5430
6056
|
* Unmount: remove from DOM, deactivate, preserve state for re-mount.
|
|
5431
6057
|
*/
|
|
5432
|
-
|
|
6058
|
+
_chp.unmount = function() {
|
|
5433
6059
|
if (!this.mounted) return;
|
|
5434
6060
|
|
|
5435
6061
|
// unmount hook
|
|
@@ -5464,12 +6090,23 @@ ComponentHandle.prototype.unmount = function() {
|
|
|
5464
6090
|
/**
|
|
5465
6091
|
* Destroy: unmount + clear state + unregister actions.
|
|
5466
6092
|
*/
|
|
5467
|
-
|
|
6093
|
+
_chp.destroy = function() {
|
|
5468
6094
|
// willDestroy hook
|
|
5469
6095
|
if (this._hooks.willDestroy) {
|
|
5470
6096
|
this._hooks.willDestroy(this);
|
|
5471
6097
|
}
|
|
5472
6098
|
|
|
6099
|
+
// Cascade destroy to children depth-first (Bug #5)
|
|
6100
|
+
for (var ci = this._children.length - 1; ci >= 0; ci--) {
|
|
6101
|
+
this._children[ci].destroy();
|
|
6102
|
+
}
|
|
6103
|
+
this._children = [];
|
|
6104
|
+
if (this._parent) {
|
|
6105
|
+
var idx = this._parent._children.indexOf(this);
|
|
6106
|
+
if (idx >= 0) this._parent._children.splice(idx, 1);
|
|
6107
|
+
this._parent = null;
|
|
6108
|
+
}
|
|
6109
|
+
|
|
5473
6110
|
this.unmount();
|
|
5474
6111
|
|
|
5475
6112
|
// Unregister actions from function registry
|
|
@@ -5496,12 +6133,36 @@ ComponentHandle.prototype.destroy = function() {
|
|
|
5496
6133
|
* Flush dirty state: resolve changed bindings and apply to DOM.
|
|
5497
6134
|
* @private
|
|
5498
6135
|
*/
|
|
5499
|
-
|
|
6136
|
+
_chp._flush = function() {
|
|
5500
6137
|
this._scheduled = false;
|
|
5501
|
-
var changedKeys =
|
|
6138
|
+
var changedKeys = _keys(this._dirtyKeys);
|
|
5502
6139
|
this._dirtyKeys = {};
|
|
5503
6140
|
if (changedKeys.length === 0 || !this.mounted) return;
|
|
5504
6141
|
|
|
6142
|
+
// Factory rebuild: if a BCCL factory exists and changed keys overlap factory props,
|
|
6143
|
+
// rebuild the TACO from the factory with merged state (Bug #6)
|
|
6144
|
+
if (this._factory) {
|
|
6145
|
+
var rebuildNeeded = false;
|
|
6146
|
+
for (var fi = 0; fi < changedKeys.length; fi++) {
|
|
6147
|
+
if (_hop.call(this._factory.props, changedKeys[fi])) {
|
|
6148
|
+
rebuildNeeded = true; break;
|
|
6149
|
+
}
|
|
6150
|
+
}
|
|
6151
|
+
if (rebuildNeeded) {
|
|
6152
|
+
var merged = {};
|
|
6153
|
+
for (var mk in this._factory.props) if (_hop.call(this._factory.props, mk)) merged[mk] = this._factory.props[mk];
|
|
6154
|
+
for (var sk in this._state) if (_hop.call(this._state, sk)) merged[sk] = this._state[sk];
|
|
6155
|
+
this._factory.props = merged;
|
|
6156
|
+
var newTaco = bw.make(this._factory.type, merged);
|
|
6157
|
+
newTaco._bwFactory = this._factory;
|
|
6158
|
+
this.taco = newTaco;
|
|
6159
|
+
this._originalTaco = this._deepCloneTaco(newTaco);
|
|
6160
|
+
this._render();
|
|
6161
|
+
if (this._hooks.onUpdate) this._hooks.onUpdate(this, changedKeys);
|
|
6162
|
+
return;
|
|
6163
|
+
}
|
|
6164
|
+
}
|
|
6165
|
+
|
|
5505
6166
|
// willUpdate hook
|
|
5506
6167
|
if (this._hooks.willUpdate) {
|
|
5507
6168
|
this._hooks.willUpdate(this, changedKeys);
|
|
@@ -5540,7 +6201,7 @@ ComponentHandle.prototype._flush = function() {
|
|
|
5540
6201
|
* Returns list of patches to apply.
|
|
5541
6202
|
* @private
|
|
5542
6203
|
*/
|
|
5543
|
-
|
|
6204
|
+
_chp._resolveBindings = function(changedKeys) {
|
|
5544
6205
|
var patches = [];
|
|
5545
6206
|
for (var i = 0; i < this._bindings.length; i++) {
|
|
5546
6207
|
var b = this._bindings[i];
|
|
@@ -5576,11 +6237,14 @@ ComponentHandle.prototype._resolveBindings = function(changedKeys) {
|
|
|
5576
6237
|
* Apply patches to DOM.
|
|
5577
6238
|
* @private
|
|
5578
6239
|
*/
|
|
5579
|
-
|
|
6240
|
+
_chp._applyPatches = function(patches) {
|
|
5580
6241
|
for (var i = 0; i < patches.length; i++) {
|
|
5581
6242
|
var p = patches[i];
|
|
5582
6243
|
var el = this._bw_refs[p.refId];
|
|
5583
|
-
if (!el)
|
|
6244
|
+
if (!el) {
|
|
6245
|
+
if (bw.debug) _cw('bw.debug: _applyPatches — ref "' + p.refId + '" not found in DOM');
|
|
6246
|
+
continue;
|
|
6247
|
+
}
|
|
5584
6248
|
if (p.type === 'content') {
|
|
5585
6249
|
el.textContent = p.value;
|
|
5586
6250
|
} else if (p.type === 'attribute') {
|
|
@@ -5597,7 +6261,7 @@ ComponentHandle.prototype._applyPatches = function(patches) {
|
|
|
5597
6261
|
* Resolve all bindings and apply (used for initial render).
|
|
5598
6262
|
* @private
|
|
5599
6263
|
*/
|
|
5600
|
-
|
|
6264
|
+
_chp._resolveAndApplyAll = function() {
|
|
5601
6265
|
var patches = [];
|
|
5602
6266
|
for (var i = 0; i < this._bindings.length; i++) {
|
|
5603
6267
|
var b = this._bindings[i];
|
|
@@ -5620,7 +6284,7 @@ ComponentHandle.prototype._resolveAndApplyAll = function() {
|
|
|
5620
6284
|
* Full re-render for structural changes (when/each branch switches).
|
|
5621
6285
|
* @private
|
|
5622
6286
|
*/
|
|
5623
|
-
|
|
6287
|
+
_chp._render = function() {
|
|
5624
6288
|
if (!this.element || !this.element.parentNode) return;
|
|
5625
6289
|
var parent = this.element.parentNode;
|
|
5626
6290
|
var nextSibling = this.element.nextSibling;
|
|
@@ -5660,7 +6324,7 @@ ComponentHandle.prototype._render = function() {
|
|
|
5660
6324
|
* @param {string} event - Event name (e.g., 'click')
|
|
5661
6325
|
* @param {Function} handler - Event handler
|
|
5662
6326
|
*/
|
|
5663
|
-
|
|
6327
|
+
_chp.on = function(event, handler) {
|
|
5664
6328
|
if (this.element) {
|
|
5665
6329
|
this.element.addEventListener(event, handler);
|
|
5666
6330
|
}
|
|
@@ -5672,7 +6336,7 @@ ComponentHandle.prototype.on = function(event, handler) {
|
|
|
5672
6336
|
* @param {string} event - Event name
|
|
5673
6337
|
* @param {Function} handler - Handler to remove
|
|
5674
6338
|
*/
|
|
5675
|
-
|
|
6339
|
+
_chp.off = function(event, handler) {
|
|
5676
6340
|
if (this.element) {
|
|
5677
6341
|
this.element.removeEventListener(event, handler);
|
|
5678
6342
|
}
|
|
@@ -5687,7 +6351,7 @@ ComponentHandle.prototype.off = function(event, handler) {
|
|
|
5687
6351
|
* @param {Function} handler - Handler function
|
|
5688
6352
|
* @returns {Function} Unsubscribe function
|
|
5689
6353
|
*/
|
|
5690
|
-
|
|
6354
|
+
_chp.sub = function(topic, handler) {
|
|
5691
6355
|
var unsub = bw.sub(topic, handler);
|
|
5692
6356
|
this._subs.push(unsub);
|
|
5693
6357
|
return unsub;
|
|
@@ -5698,10 +6362,10 @@ ComponentHandle.prototype.sub = function(topic, handler) {
|
|
|
5698
6362
|
* @param {string} name - Action name
|
|
5699
6363
|
* @param {...*} args - Arguments passed after comp
|
|
5700
6364
|
*/
|
|
5701
|
-
|
|
6365
|
+
_chp.action = function(name) {
|
|
5702
6366
|
var fn = this._actions[name];
|
|
5703
6367
|
if (!fn) {
|
|
5704
|
-
|
|
6368
|
+
_cw('ComponentHandle.action: unknown action "' + name + '"');
|
|
5705
6369
|
return;
|
|
5706
6370
|
}
|
|
5707
6371
|
var args = [this].concat(Array.prototype.slice.call(arguments, 1));
|
|
@@ -5713,7 +6377,7 @@ ComponentHandle.prototype.action = function(name) {
|
|
|
5713
6377
|
* @param {string} sel - CSS selector
|
|
5714
6378
|
* @returns {Element|null}
|
|
5715
6379
|
*/
|
|
5716
|
-
|
|
6380
|
+
_chp.select = function(sel) {
|
|
5717
6381
|
return this.element ? this.element.querySelector(sel) : null;
|
|
5718
6382
|
};
|
|
5719
6383
|
|
|
@@ -5722,7 +6386,7 @@ ComponentHandle.prototype.select = function(sel) {
|
|
|
5722
6386
|
* @param {string} sel - CSS selector
|
|
5723
6387
|
* @returns {Element[]}
|
|
5724
6388
|
*/
|
|
5725
|
-
|
|
6389
|
+
_chp.selectAll = function(sel) {
|
|
5726
6390
|
if (!this.element) return [];
|
|
5727
6391
|
return Array.prototype.slice.call(this.element.querySelectorAll(sel));
|
|
5728
6392
|
};
|
|
@@ -5733,7 +6397,7 @@ ComponentHandle.prototype.selectAll = function(sel) {
|
|
|
5733
6397
|
* @param {string} tag - User-defined identifier (e.g. 'dashboard_prod_east')
|
|
5734
6398
|
* @returns {ComponentHandle} this (for chaining)
|
|
5735
6399
|
*/
|
|
5736
|
-
|
|
6400
|
+
_chp.userTag = function(tag) {
|
|
5737
6401
|
this._userTag = tag;
|
|
5738
6402
|
if (this.element) {
|
|
5739
6403
|
this.element.classList.add(tag);
|
|
@@ -5810,7 +6474,7 @@ bw.component = function(taco) {
|
|
|
5810
6474
|
* and calls the named method. This is the bitwrench equivalent of
|
|
5811
6475
|
* Win32 SendMessage(hwnd, msg, wParam, lParam).
|
|
5812
6476
|
*
|
|
5813
|
-
* @param {string} target - Component UUID (data-bw_comp_id) or user tag (CSS class)
|
|
6477
|
+
* @param {string} target - Component UUID (bw_uuid_*), comp ID (data-bw_comp_id), or user tag (CSS class)
|
|
5814
6478
|
* @param {string} action - Method name to call on the component
|
|
5815
6479
|
* @param {*} data - Data to pass to the method
|
|
5816
6480
|
* @returns {boolean} True if message was dispatched successfully
|
|
@@ -5827,15 +6491,20 @@ bw.component = function(taco) {
|
|
|
5827
6491
|
* };
|
|
5828
6492
|
*/
|
|
5829
6493
|
bw.message = function(target, action, data) {
|
|
5830
|
-
// Try
|
|
5831
|
-
var el = bw
|
|
5832
|
-
|
|
6494
|
+
// Try bw._el() first (handles UUID class, nodeMap cache, getElementById)
|
|
6495
|
+
var el = bw._el(target);
|
|
6496
|
+
// Then try data-bw_comp_id attribute
|
|
6497
|
+
if (!el || !el._bwComponentHandle) {
|
|
6498
|
+
el = bw.$('[data-bw_comp_id="' + target + '"]')[0];
|
|
6499
|
+
}
|
|
6500
|
+
// Then try CSS class (user tag)
|
|
6501
|
+
if (!el || !el._bwComponentHandle) {
|
|
5833
6502
|
el = bw.$('.' + target)[0];
|
|
5834
6503
|
}
|
|
5835
6504
|
if (!el || !el._bwComponentHandle) return false;
|
|
5836
6505
|
var comp = el._bwComponentHandle;
|
|
5837
|
-
if (
|
|
5838
|
-
|
|
6506
|
+
if (!_is(comp[action], 'function')) {
|
|
6507
|
+
_cw('bw.message: unknown action "' + action + '" on component ' + target);
|
|
5839
6508
|
return false;
|
|
5840
6509
|
}
|
|
5841
6510
|
comp[action](data);
|
|
@@ -5843,59 +6512,24 @@ bw.message = function(target, action, data) {
|
|
|
5843
6512
|
};
|
|
5844
6513
|
|
|
5845
6514
|
// ===================================================================================
|
|
5846
|
-
// bw.
|
|
6515
|
+
// bw.apply() / bw.parseJSONFlex() — Server-driven UI protocol
|
|
5847
6516
|
// ===================================================================================
|
|
5848
6517
|
|
|
5849
6518
|
/**
|
|
5850
6519
|
* Registry of named functions sent via register messages.
|
|
5851
|
-
* Populated by
|
|
5852
|
-
* Invoked by
|
|
6520
|
+
* Populated by bw.apply({ type: 'register', name, body }).
|
|
6521
|
+
* Invoked by bw.apply({ type: 'call', name, args }).
|
|
5853
6522
|
* @private
|
|
5854
6523
|
*/
|
|
5855
6524
|
bw._clientFunctions = {};
|
|
5856
6525
|
|
|
5857
6526
|
/**
|
|
5858
|
-
* Whether exec messages are allowed. Set by
|
|
6527
|
+
* Whether exec messages are allowed. Set by bwclient connect opts.allowExec.
|
|
5859
6528
|
* Default false — exec messages are rejected unless explicitly opted in.
|
|
5860
6529
|
* @private
|
|
5861
6530
|
*/
|
|
5862
6531
|
bw._allowExec = false;
|
|
5863
6532
|
|
|
5864
|
-
/**
|
|
5865
|
-
* Built-in client functions available via call() without registration.
|
|
5866
|
-
* @private
|
|
5867
|
-
*/
|
|
5868
|
-
bw._builtinClientFunctions = {
|
|
5869
|
-
scrollTo: function(selector) {
|
|
5870
|
-
var el = bw._el(selector);
|
|
5871
|
-
if (el) el.scrollTop = el.scrollHeight;
|
|
5872
|
-
},
|
|
5873
|
-
focus: function(selector) {
|
|
5874
|
-
var el = bw._el(selector);
|
|
5875
|
-
if (el && typeof el.focus === 'function') el.focus();
|
|
5876
|
-
},
|
|
5877
|
-
download: function(filename, content, mimeType) {
|
|
5878
|
-
if (typeof document === 'undefined') return;
|
|
5879
|
-
var blob = new Blob([content], { type: mimeType || 'text/plain' });
|
|
5880
|
-
var a = document.createElement('a');
|
|
5881
|
-
a.href = URL.createObjectURL(blob);
|
|
5882
|
-
a.download = filename;
|
|
5883
|
-
a.click();
|
|
5884
|
-
URL.revokeObjectURL(a.href);
|
|
5885
|
-
},
|
|
5886
|
-
clipboard: function(text) {
|
|
5887
|
-
if (typeof navigator !== 'undefined' && navigator.clipboard) {
|
|
5888
|
-
navigator.clipboard.writeText(text);
|
|
5889
|
-
}
|
|
5890
|
-
},
|
|
5891
|
-
redirect: function(url) {
|
|
5892
|
-
if (typeof window !== 'undefined') window.location.href = url;
|
|
5893
|
-
},
|
|
5894
|
-
log: function() {
|
|
5895
|
-
console.log.apply(console, arguments);
|
|
5896
|
-
}
|
|
5897
|
-
};
|
|
5898
|
-
|
|
5899
6533
|
/**
|
|
5900
6534
|
* Parse a bwserve protocol message string, supporting both strict JSON
|
|
5901
6535
|
* and r-prefixed relaxed JSON (single-quoted strings, trailing commas).
|
|
@@ -5910,9 +6544,9 @@ bw._builtinClientFunctions = {
|
|
|
5910
6544
|
* @param {string} str - JSON or r-prefixed relaxed JSON string
|
|
5911
6545
|
* @returns {Object} Parsed message object
|
|
5912
6546
|
* @throws {SyntaxError} If the string is not valid JSON or relaxed JSON
|
|
5913
|
-
* @category
|
|
6547
|
+
* @category Core
|
|
5914
6548
|
*/
|
|
5915
|
-
bw.
|
|
6549
|
+
bw.parseJSONFlex = function(str) {
|
|
5916
6550
|
str = (str || '').trim();
|
|
5917
6551
|
if (str.charAt(0) !== 'r') return JSON.parse(str);
|
|
5918
6552
|
str = str.slice(1);
|
|
@@ -5997,10 +6631,10 @@ bw.clientParse = function(str) {
|
|
|
5997
6631
|
* append — target.appendChild(bw.createDOM(node))
|
|
5998
6632
|
* remove — bw.cleanup(target); target.remove()
|
|
5999
6633
|
* patch — bw.patch(target, content, attr)
|
|
6000
|
-
* batch — iterate ops, call
|
|
6634
|
+
* batch — iterate ops, call bw.apply for each
|
|
6001
6635
|
* message — bw.message(target, action, data)
|
|
6002
6636
|
* register — store a named function for later call()
|
|
6003
|
-
* call — invoke a registered
|
|
6637
|
+
* call — invoke a registered function
|
|
6004
6638
|
* exec — execute arbitrary JS (requires allowExec)
|
|
6005
6639
|
*
|
|
6006
6640
|
* Target resolution:
|
|
@@ -6009,9 +6643,9 @@ bw.clientParse = function(str) {
|
|
|
6009
6643
|
*
|
|
6010
6644
|
* @param {Object} msg - Protocol message
|
|
6011
6645
|
* @returns {boolean} true if the message was applied successfully
|
|
6012
|
-
* @category
|
|
6646
|
+
* @category Core
|
|
6013
6647
|
*/
|
|
6014
|
-
bw.
|
|
6648
|
+
bw.apply = function(msg) {
|
|
6015
6649
|
if (!msg || !msg.type) return false;
|
|
6016
6650
|
|
|
6017
6651
|
var type = msg.type;
|
|
@@ -6037,15 +6671,15 @@ bw.clientApply = function(msg) {
|
|
|
6037
6671
|
} else if (type === 'remove') {
|
|
6038
6672
|
var toRemove = bw._el(target);
|
|
6039
6673
|
if (!toRemove) return false;
|
|
6040
|
-
if (
|
|
6674
|
+
if (_is(bw.cleanup, 'function')) bw.cleanup(toRemove);
|
|
6041
6675
|
toRemove.remove();
|
|
6042
6676
|
return true;
|
|
6043
6677
|
|
|
6044
6678
|
} else if (type === 'batch') {
|
|
6045
|
-
if (!
|
|
6679
|
+
if (!_isA(msg.ops)) return false;
|
|
6046
6680
|
var allOk = true;
|
|
6047
6681
|
msg.ops.forEach(function(op) {
|
|
6048
|
-
if (!bw.
|
|
6682
|
+
if (!bw.apply(op)) allOk = false;
|
|
6049
6683
|
});
|
|
6050
6684
|
return allOk;
|
|
6051
6685
|
|
|
@@ -6058,26 +6692,26 @@ bw.clientApply = function(msg) {
|
|
|
6058
6692
|
bw._clientFunctions[msg.name] = new Function('return ' + msg.body)();
|
|
6059
6693
|
return true;
|
|
6060
6694
|
} catch (e) {
|
|
6061
|
-
|
|
6695
|
+
_ce('[bw] register error:', msg.name, e);
|
|
6062
6696
|
return false;
|
|
6063
6697
|
}
|
|
6064
6698
|
|
|
6065
6699
|
} else if (type === 'call') {
|
|
6066
6700
|
if (!msg.name) return false;
|
|
6067
|
-
var fn = bw._clientFunctions[msg.name]
|
|
6068
|
-
if (
|
|
6701
|
+
var fn = bw._clientFunctions[msg.name];
|
|
6702
|
+
if (!_is(fn, 'function')) return false;
|
|
6069
6703
|
try {
|
|
6070
|
-
var args =
|
|
6704
|
+
var args = _isA(msg.args) ? msg.args : [];
|
|
6071
6705
|
fn.apply(null, args);
|
|
6072
6706
|
return true;
|
|
6073
6707
|
} catch (e) {
|
|
6074
|
-
|
|
6708
|
+
_ce('[bw] call error:', msg.name, e);
|
|
6075
6709
|
return false;
|
|
6076
6710
|
}
|
|
6077
6711
|
|
|
6078
6712
|
} else if (type === 'exec') {
|
|
6079
6713
|
if (!bw._allowExec) {
|
|
6080
|
-
|
|
6714
|
+
_cw('[bw] exec rejected: allowExec is not enabled');
|
|
6081
6715
|
return false;
|
|
6082
6716
|
}
|
|
6083
6717
|
if (!msg.code) return false;
|
|
@@ -6085,7 +6719,7 @@ bw.clientApply = function(msg) {
|
|
|
6085
6719
|
new Function(msg.code)();
|
|
6086
6720
|
return true;
|
|
6087
6721
|
} catch (e) {
|
|
6088
|
-
|
|
6722
|
+
_ce('[bw] exec error:', e);
|
|
6089
6723
|
return false;
|
|
6090
6724
|
}
|
|
6091
6725
|
}
|
|
@@ -6093,139 +6727,6 @@ bw.clientApply = function(msg) {
|
|
|
6093
6727
|
return false;
|
|
6094
6728
|
};
|
|
6095
6729
|
|
|
6096
|
-
/**
|
|
6097
|
-
* Connect to a bwserve SSE endpoint and apply protocol messages automatically.
|
|
6098
|
-
*
|
|
6099
|
-
* Returns a connection object with sendAction(), on(), and close() methods.
|
|
6100
|
-
*
|
|
6101
|
-
* @param {string} url - SSE endpoint URL (e.g., '/__bw/events/client-1')
|
|
6102
|
-
* @param {Object} [opts] - Connection options
|
|
6103
|
-
* @param {string} [opts.transport='sse'] - Transport type: 'sse' (default) or 'poll'
|
|
6104
|
-
* @param {number} [opts.interval=2000] - Poll interval in ms (only for 'poll' transport)
|
|
6105
|
-
* @param {string} [opts.actionUrl] - POST endpoint for actions (default: derived from url)
|
|
6106
|
-
* @param {boolean} [opts.reconnect=true] - Auto-reconnect on disconnect
|
|
6107
|
-
* @param {boolean} [opts.allowExec=false] - Enable exec message type (arbitrary JS execution)
|
|
6108
|
-
* @param {Function} [opts.onStatus] - Status callback: 'connecting'|'connected'|'disconnected'
|
|
6109
|
-
* @param {Function} [opts.onMessage] - Raw message callback (before clientApply)
|
|
6110
|
-
* @returns {Object} Connection object { sendAction, on, close, status }
|
|
6111
|
-
* @category Server
|
|
6112
|
-
*/
|
|
6113
|
-
bw.clientConnect = function(url, opts) {
|
|
6114
|
-
opts = opts || {};
|
|
6115
|
-
var transport = opts.transport || 'sse';
|
|
6116
|
-
var actionUrl = opts.actionUrl || url.replace(/\/events\//, '/action/');
|
|
6117
|
-
var reconnect = opts.reconnect !== false;
|
|
6118
|
-
var onStatus = opts.onStatus || function() {};
|
|
6119
|
-
var onMessage = opts.onMessage || null;
|
|
6120
|
-
var handlers = {};
|
|
6121
|
-
// Set the global allowExec flag from connection options
|
|
6122
|
-
bw._allowExec = !!opts.allowExec;
|
|
6123
|
-
var conn = {
|
|
6124
|
-
status: 'connecting',
|
|
6125
|
-
_es: null,
|
|
6126
|
-
_pollTimer: null
|
|
6127
|
-
};
|
|
6128
|
-
|
|
6129
|
-
function setStatus(s) {
|
|
6130
|
-
conn.status = s;
|
|
6131
|
-
onStatus(s);
|
|
6132
|
-
}
|
|
6133
|
-
|
|
6134
|
-
function handleMessage(data) {
|
|
6135
|
-
try {
|
|
6136
|
-
var msg = typeof data === 'string' ? bw.clientParse(data) : data;
|
|
6137
|
-
if (onMessage) onMessage(msg);
|
|
6138
|
-
if (handlers.message) handlers.message(msg);
|
|
6139
|
-
bw.clientApply(msg);
|
|
6140
|
-
} catch (e) {
|
|
6141
|
-
if (handlers.error) handlers.error(e);
|
|
6142
|
-
}
|
|
6143
|
-
}
|
|
6144
|
-
|
|
6145
|
-
if (transport === 'sse' && typeof EventSource !== 'undefined') {
|
|
6146
|
-
setStatus('connecting');
|
|
6147
|
-
var es = new EventSource(url);
|
|
6148
|
-
conn._es = es;
|
|
6149
|
-
|
|
6150
|
-
es.onopen = function() {
|
|
6151
|
-
setStatus('connected');
|
|
6152
|
-
if (handlers.open) handlers.open();
|
|
6153
|
-
};
|
|
6154
|
-
|
|
6155
|
-
es.onmessage = function(e) {
|
|
6156
|
-
handleMessage(e.data);
|
|
6157
|
-
};
|
|
6158
|
-
|
|
6159
|
-
es.onerror = function() {
|
|
6160
|
-
if (conn.status === 'connected') {
|
|
6161
|
-
setStatus('disconnected');
|
|
6162
|
-
}
|
|
6163
|
-
if (handlers.error) handlers.error(new Error('SSE connection error'));
|
|
6164
|
-
if (!reconnect) {
|
|
6165
|
-
es.close();
|
|
6166
|
-
}
|
|
6167
|
-
// EventSource auto-reconnects by default when reconnect=true
|
|
6168
|
-
};
|
|
6169
|
-
} else if (transport === 'poll') {
|
|
6170
|
-
var interval = opts.interval || 2000;
|
|
6171
|
-
setStatus('connected');
|
|
6172
|
-
conn._pollTimer = setInterval(function() {
|
|
6173
|
-
fetch(url).then(function(r) { return r.json(); }).then(function(msgs) {
|
|
6174
|
-
if (Array.isArray(msgs)) {
|
|
6175
|
-
msgs.forEach(handleMessage);
|
|
6176
|
-
} else if (msgs && msgs.type) {
|
|
6177
|
-
handleMessage(msgs);
|
|
6178
|
-
}
|
|
6179
|
-
}).catch(function(e) {
|
|
6180
|
-
if (handlers.error) handlers.error(e);
|
|
6181
|
-
});
|
|
6182
|
-
}, interval);
|
|
6183
|
-
}
|
|
6184
|
-
|
|
6185
|
-
/**
|
|
6186
|
-
* Send an action to the server via POST.
|
|
6187
|
-
* @param {string} action - Action name
|
|
6188
|
-
* @param {Object} [data] - Action payload
|
|
6189
|
-
*/
|
|
6190
|
-
conn.sendAction = function(action, data) {
|
|
6191
|
-
var body = JSON.stringify({ type: 'action', action: action, data: data || {} });
|
|
6192
|
-
fetch(actionUrl, {
|
|
6193
|
-
method: 'POST',
|
|
6194
|
-
headers: { 'Content-Type': 'application/json' },
|
|
6195
|
-
body: body
|
|
6196
|
-
}).catch(function(e) {
|
|
6197
|
-
if (handlers.error) handlers.error(e);
|
|
6198
|
-
});
|
|
6199
|
-
};
|
|
6200
|
-
|
|
6201
|
-
/**
|
|
6202
|
-
* Register an event handler.
|
|
6203
|
-
* @param {string} event - 'open'|'message'|'error'|'close'
|
|
6204
|
-
* @param {Function} handler
|
|
6205
|
-
*/
|
|
6206
|
-
conn.on = function(event, handler) {
|
|
6207
|
-
handlers[event] = handler;
|
|
6208
|
-
return conn;
|
|
6209
|
-
};
|
|
6210
|
-
|
|
6211
|
-
/**
|
|
6212
|
-
* Close the connection.
|
|
6213
|
-
*/
|
|
6214
|
-
conn.close = function() {
|
|
6215
|
-
if (conn._es) {
|
|
6216
|
-
conn._es.close();
|
|
6217
|
-
conn._es = null;
|
|
6218
|
-
}
|
|
6219
|
-
if (conn._pollTimer) {
|
|
6220
|
-
clearInterval(conn._pollTimer);
|
|
6221
|
-
conn._pollTimer = null;
|
|
6222
|
-
}
|
|
6223
|
-
setStatus('disconnected');
|
|
6224
|
-
if (handlers.close) handlers.close();
|
|
6225
|
-
};
|
|
6226
|
-
|
|
6227
|
-
return conn;
|
|
6228
|
-
};
|
|
6229
6730
|
|
|
6230
6731
|
// ===================================================================================
|
|
6231
6732
|
// bw.inspect() — Debug utility
|
|
@@ -6253,33 +6754,33 @@ bw.inspect = function(target) {
|
|
|
6253
6754
|
el = target.element;
|
|
6254
6755
|
comp = target;
|
|
6255
6756
|
} else {
|
|
6256
|
-
if (
|
|
6757
|
+
if (_is(target, 'string')) {
|
|
6257
6758
|
el = bw.$(target)[0];
|
|
6258
6759
|
}
|
|
6259
6760
|
if (!el) {
|
|
6260
|
-
|
|
6761
|
+
_cw('bw.inspect: element not found');
|
|
6261
6762
|
return null;
|
|
6262
6763
|
}
|
|
6263
6764
|
comp = el._bwComponentHandle;
|
|
6264
6765
|
}
|
|
6265
6766
|
if (!comp) {
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6767
|
+
_cl('bw.inspect: no ComponentHandle on this element');
|
|
6768
|
+
_cl(' Tag:', el.tagName);
|
|
6769
|
+
_cl(' Classes:', el.className);
|
|
6770
|
+
_cl(' _bw_state:', el._bw_state || '(none)');
|
|
6270
6771
|
return null;
|
|
6271
6772
|
}
|
|
6272
6773
|
var deps = comp._bindings.reduce(function(s, b) {
|
|
6273
6774
|
return s.concat(b.deps || []);
|
|
6274
6775
|
}, []).filter(function(v, i, a) { return a.indexOf(v) === i; });
|
|
6275
6776
|
console.group('Component: ' + comp._bwId);
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6777
|
+
_cl('State:', comp._state);
|
|
6778
|
+
_cl('Bindings:', comp._bindings.length, '(deps:', deps, ')');
|
|
6779
|
+
_cl('Methods:', _keys(comp._methods));
|
|
6780
|
+
_cl('Actions:', _keys(comp._actions));
|
|
6781
|
+
_cl('User tag:', comp._userTag || '(none)');
|
|
6782
|
+
_cl('Mounted:', comp.mounted);
|
|
6783
|
+
_cl('Element:', comp.element);
|
|
6283
6784
|
console.groupEnd();
|
|
6284
6785
|
return comp;
|
|
6285
6786
|
};
|
|
@@ -6302,8 +6803,8 @@ bw.compile = function(taco) {
|
|
|
6302
6803
|
// Pre-extract all binding expressions
|
|
6303
6804
|
var precompiled = [];
|
|
6304
6805
|
function walkExpressions(node) {
|
|
6305
|
-
if (!node
|
|
6306
|
-
if (
|
|
6806
|
+
if (!_is(node, 'object')) return;
|
|
6807
|
+
if (_is(node.c, 'string') && node.c.indexOf('${') >= 0) {
|
|
6307
6808
|
var parsed = bw._parseBindings(node.c);
|
|
6308
6809
|
for (var i = 0; i < parsed.length; i++) {
|
|
6309
6810
|
try {
|
|
@@ -6318,9 +6819,9 @@ bw.compile = function(taco) {
|
|
|
6318
6819
|
}
|
|
6319
6820
|
if (node.a) {
|
|
6320
6821
|
for (var key in node.a) {
|
|
6321
|
-
if (
|
|
6822
|
+
if (_hop.call(node.a, key)) {
|
|
6322
6823
|
var v = node.a[key];
|
|
6323
|
-
if (
|
|
6824
|
+
if (_is(v, 'string') && v.indexOf('${') >= 0) {
|
|
6324
6825
|
var parsed2 = bw._parseBindings(v);
|
|
6325
6826
|
for (var j = 0; j < parsed2.length; j++) {
|
|
6326
6827
|
try {
|
|
@@ -6336,9 +6837,9 @@ bw.compile = function(taco) {
|
|
|
6336
6837
|
}
|
|
6337
6838
|
}
|
|
6338
6839
|
}
|
|
6339
|
-
if (
|
|
6840
|
+
if (_isA(node.c)) {
|
|
6340
6841
|
for (var k = 0; k < node.c.length; k++) walkExpressions(node.c[k]);
|
|
6341
|
-
} else if (node.c
|
|
6842
|
+
} else if (_is(node.c, 'object') && node.c.t) {
|
|
6342
6843
|
walkExpressions(node.c);
|
|
6343
6844
|
}
|
|
6344
6845
|
}
|
|
@@ -6350,7 +6851,7 @@ bw.compile = function(taco) {
|
|
|
6350
6851
|
handle._precompiledBindings = precompiled;
|
|
6351
6852
|
if (initialState) {
|
|
6352
6853
|
for (var k in initialState) {
|
|
6353
|
-
if (
|
|
6854
|
+
if (_hop.call(initialState, k)) {
|
|
6354
6855
|
handle._state[k] = initialState[k];
|
|
6355
6856
|
}
|
|
6356
6857
|
}
|
|
@@ -6381,18 +6882,18 @@ bw.compile = function(taco) {
|
|
|
6381
6882
|
bw.css = function(rules, options = {}) {
|
|
6382
6883
|
const { minify = false, pretty = !minify } = options;
|
|
6383
6884
|
|
|
6384
|
-
if (
|
|
6885
|
+
if (_is(rules, 'string')) return rules;
|
|
6385
6886
|
|
|
6386
6887
|
let css = '';
|
|
6387
6888
|
const indent = pretty ? ' ' : '';
|
|
6388
6889
|
const newline = pretty ? '\n' : '';
|
|
6389
6890
|
const space = pretty ? ' ' : '';
|
|
6390
6891
|
|
|
6391
|
-
if (
|
|
6892
|
+
if (_isA(rules)) {
|
|
6392
6893
|
css = rules.map(rule => bw.css(rule, options)).join(newline);
|
|
6393
|
-
} else if (
|
|
6894
|
+
} else if (_is(rules, 'object')) {
|
|
6394
6895
|
Object.entries(rules).forEach(([selector, styles]) => {
|
|
6395
|
-
if (
|
|
6896
|
+
if (_is(styles, 'object')) {
|
|
6396
6897
|
// Handle @media, @keyframes, @supports — recurse into nested block
|
|
6397
6898
|
if (selector.charAt(0) === '@') {
|
|
6398
6899
|
const inner = bw.css(styles, options);
|
|
@@ -6434,14 +6935,14 @@ bw.css = function(rules, options = {}) {
|
|
|
6434
6935
|
* @returns {Element} The style element
|
|
6435
6936
|
* @category CSS & Styling
|
|
6436
6937
|
* @see bw.css
|
|
6437
|
-
* @see bw.
|
|
6938
|
+
* @see bw.loadStyles
|
|
6438
6939
|
* @example
|
|
6439
6940
|
* bw.injectCSS('.my-class { color: red; }');
|
|
6440
6941
|
* bw.injectCSS({ '.card': { padding: '1rem' } }, { id: 'card-styles' });
|
|
6441
6942
|
*/
|
|
6442
6943
|
bw.injectCSS = function(css, options = {}) {
|
|
6443
6944
|
if (!bw._isBrowser) {
|
|
6444
|
-
|
|
6945
|
+
_cw('bw.injectCSS requires a DOM environment');
|
|
6445
6946
|
return null;
|
|
6446
6947
|
}
|
|
6447
6948
|
|
|
@@ -6458,7 +6959,7 @@ bw.injectCSS = function(css, options = {}) {
|
|
|
6458
6959
|
}
|
|
6459
6960
|
|
|
6460
6961
|
// Convert CSS if needed
|
|
6461
|
-
const cssStr =
|
|
6962
|
+
const cssStr = _is(css, 'string') ? css : bw.css(css, options);
|
|
6462
6963
|
|
|
6463
6964
|
// Set or append CSS
|
|
6464
6965
|
if (append && styleEl.textContent) {
|
|
@@ -6479,113 +6980,19 @@ bw.injectCSS = function(css, options = {}) {
|
|
|
6479
6980
|
* @param {...Object} styles - Style objects to merge (left-to-right)
|
|
6480
6981
|
* @returns {Object} Merged style object
|
|
6481
6982
|
* @category CSS & Styling
|
|
6482
|
-
* @see bw.u
|
|
6483
6983
|
* @example
|
|
6484
|
-
* var style = bw.s(
|
|
6984
|
+
* var style = bw.s({ display: 'flex' }, { gap: '1rem' }, { color: 'red' });
|
|
6485
6985
|
* // => { display: 'flex', gap: '1rem', color: 'red' }
|
|
6486
6986
|
*/
|
|
6487
6987
|
bw.s = function() {
|
|
6488
6988
|
var result = {};
|
|
6489
6989
|
for (var i = 0; i < arguments.length; i++) {
|
|
6490
6990
|
var arg = arguments[i];
|
|
6491
|
-
if (arg
|
|
6991
|
+
if (_is(arg, 'object')) Object.assign(result, arg);
|
|
6492
6992
|
}
|
|
6493
6993
|
return result;
|
|
6494
6994
|
};
|
|
6495
6995
|
|
|
6496
|
-
/**
|
|
6497
|
-
* Pre-built CSS utility objects (like Tailwind utilities, but in JS).
|
|
6498
|
-
*
|
|
6499
|
-
* Compose with `bw.s()` to build inline styles without writing raw CSS strings.
|
|
6500
|
-
* Includes flex, padding, margin, typography, color, border, and transition utilities.
|
|
6501
|
-
*
|
|
6502
|
-
* @category CSS & Styling
|
|
6503
|
-
* @see bw.s
|
|
6504
|
-
* @example
|
|
6505
|
-
* { t: 'div', a: { style: bw.s(bw.u.flex, bw.u.gap4, bw.u.p4) },
|
|
6506
|
-
* c: 'Flexbox with 1rem gap and padding' }
|
|
6507
|
-
*/
|
|
6508
|
-
bw.u = {
|
|
6509
|
-
// Display
|
|
6510
|
-
flex: { display: 'flex' },
|
|
6511
|
-
flexCol: { display: 'flex', flexDirection: 'column' },
|
|
6512
|
-
flexRow: { display: 'flex', flexDirection: 'row' },
|
|
6513
|
-
flexWrap: { display: 'flex', flexWrap: 'wrap' },
|
|
6514
|
-
block: { display: 'block' },
|
|
6515
|
-
inline: { display: 'inline' },
|
|
6516
|
-
hidden: { display: 'none' },
|
|
6517
|
-
|
|
6518
|
-
// Flex alignment
|
|
6519
|
-
justifyCenter: { justifyContent: 'center' },
|
|
6520
|
-
justifyBetween: { justifyContent: 'space-between' },
|
|
6521
|
-
justifyEnd: { justifyContent: 'flex-end' },
|
|
6522
|
-
alignCenter: { alignItems: 'center' },
|
|
6523
|
-
alignStart: { alignItems: 'flex-start' },
|
|
6524
|
-
alignEnd: { alignItems: 'flex-end' },
|
|
6525
|
-
|
|
6526
|
-
// Gap (0.25rem increments)
|
|
6527
|
-
gap1: { gap: '0.25rem' },
|
|
6528
|
-
gap2: { gap: '0.5rem' },
|
|
6529
|
-
gap3: { gap: '0.75rem' },
|
|
6530
|
-
gap4: { gap: '1rem' },
|
|
6531
|
-
gap6: { gap: '1.5rem' },
|
|
6532
|
-
gap8: { gap: '2rem' },
|
|
6533
|
-
|
|
6534
|
-
// Padding
|
|
6535
|
-
p0: { padding: '0' },
|
|
6536
|
-
p1: { padding: '0.25rem' },
|
|
6537
|
-
p2: { padding: '0.5rem' },
|
|
6538
|
-
p3: { padding: '0.75rem' },
|
|
6539
|
-
p4: { padding: '1rem' },
|
|
6540
|
-
p6: { padding: '1.5rem' },
|
|
6541
|
-
p8: { padding: '2rem' },
|
|
6542
|
-
px4: { paddingLeft: '1rem', paddingRight: '1rem' },
|
|
6543
|
-
py2: { paddingTop: '0.5rem', paddingBottom: '0.5rem' },
|
|
6544
|
-
py4: { paddingTop: '1rem', paddingBottom: '1rem' },
|
|
6545
|
-
|
|
6546
|
-
// Margin (same scale)
|
|
6547
|
-
m0: { margin: '0' },
|
|
6548
|
-
m4: { margin: '1rem' },
|
|
6549
|
-
mt2: { marginTop: '0.5rem' },
|
|
6550
|
-
mt4: { marginTop: '1rem' },
|
|
6551
|
-
mb2: { marginBottom: '0.5rem' },
|
|
6552
|
-
mb4: { marginBottom: '1rem' },
|
|
6553
|
-
mx_auto: { marginLeft: 'auto', marginRight: 'auto' },
|
|
6554
|
-
|
|
6555
|
-
// Typography
|
|
6556
|
-
textSm: { fontSize: '0.875rem' },
|
|
6557
|
-
textBase: { fontSize: '1rem' },
|
|
6558
|
-
textLg: { fontSize: '1.125rem' },
|
|
6559
|
-
textXl: { fontSize: '1.25rem' },
|
|
6560
|
-
text2xl: { fontSize: '1.5rem' },
|
|
6561
|
-
text3xl: { fontSize: '1.875rem' },
|
|
6562
|
-
bold: { fontWeight: '700' },
|
|
6563
|
-
semibold: { fontWeight: '600' },
|
|
6564
|
-
italic: { fontStyle: 'italic' },
|
|
6565
|
-
textCenter: { textAlign: 'center' },
|
|
6566
|
-
textRight: { textAlign: 'right' },
|
|
6567
|
-
|
|
6568
|
-
// Colors (from design tokens)
|
|
6569
|
-
bgWhite: { background: '#ffffff' },
|
|
6570
|
-
bgTeal: { background: '#006666', color: '#ffffff' },
|
|
6571
|
-
textWhite: { color: '#ffffff' },
|
|
6572
|
-
textTeal: { color: '#006666' },
|
|
6573
|
-
textMuted: { color: '#888' },
|
|
6574
|
-
|
|
6575
|
-
// Borders
|
|
6576
|
-
rounded: { borderRadius: '0.375rem' },
|
|
6577
|
-
roundedLg: { borderRadius: '0.5rem' },
|
|
6578
|
-
roundedFull: { borderRadius: '9999px' },
|
|
6579
|
-
border: { border: '1px solid #d8d8d8' },
|
|
6580
|
-
|
|
6581
|
-
// Sizing
|
|
6582
|
-
wFull: { width: '100%' },
|
|
6583
|
-
hFull: { height: '100%' },
|
|
6584
|
-
|
|
6585
|
-
// Transitions
|
|
6586
|
-
transition: { transition: 'all 0.2s ease' }
|
|
6587
|
-
};
|
|
6588
|
-
|
|
6589
6996
|
/**
|
|
6590
6997
|
* Generate responsive CSS with media query breakpoints.
|
|
6591
6998
|
*
|
|
@@ -6611,7 +7018,7 @@ bw.u = {
|
|
|
6611
7018
|
bw.responsive = function(selector, breakpoints) {
|
|
6612
7019
|
var sizes = { sm: '576px', md: '768px', lg: '992px', xl: '1200px' };
|
|
6613
7020
|
var parts = [];
|
|
6614
|
-
|
|
7021
|
+
_keys(breakpoints).forEach(function(key) {
|
|
6615
7022
|
var rules = {};
|
|
6616
7023
|
if (key === 'base') {
|
|
6617
7024
|
rules[selector] = breakpoints[key];
|
|
@@ -6683,18 +7090,18 @@ if (bw._isBrowser) {
|
|
|
6683
7090
|
if (!selector) return [];
|
|
6684
7091
|
|
|
6685
7092
|
// Already an array
|
|
6686
|
-
if (
|
|
7093
|
+
if (_isA(selector)) return selector;
|
|
6687
7094
|
|
|
6688
7095
|
// Single element
|
|
6689
7096
|
if (selector.nodeType) return [selector];
|
|
6690
7097
|
|
|
6691
7098
|
// NodeList or HTMLCollection
|
|
6692
|
-
if (selector.length !== undefined &&
|
|
7099
|
+
if (selector.length !== undefined && !_is(selector, 'string')) {
|
|
6693
7100
|
return Array.from(selector);
|
|
6694
7101
|
}
|
|
6695
7102
|
|
|
6696
7103
|
// CSS selector string
|
|
6697
|
-
if (
|
|
7104
|
+
if (_is(selector, 'string')) {
|
|
6698
7105
|
return Array.from(document.querySelectorAll(selector));
|
|
6699
7106
|
}
|
|
6700
7107
|
|
|
@@ -6707,103 +7114,49 @@ if (bw._isBrowser) {
|
|
|
6707
7114
|
};
|
|
6708
7115
|
}
|
|
6709
7116
|
|
|
6710
|
-
/**
|
|
6711
|
-
* Load the built-in Bootstrap-inspired default stylesheet.
|
|
6712
|
-
*
|
|
6713
|
-
* Injects bitwrench's batteries-included CSS (buttons, cards, grids, forms,
|
|
6714
|
-
* alerts, badges, nav, tabs, etc.) into the document head. Call once at app startup.
|
|
6715
|
-
* Returns null in Node.js (no DOM).
|
|
6716
|
-
*
|
|
6717
|
-
* @param {Object} [options] - Style loading options
|
|
6718
|
-
* @param {boolean} [options.minify=true] - Minify the CSS output
|
|
6719
|
-
* @returns {Element|null} Style element if in browser, null in Node.js
|
|
6720
|
-
* @category CSS & Styling
|
|
6721
|
-
* @see bw.setTheme
|
|
6722
|
-
* @see bw.applyTheme
|
|
6723
|
-
* @see bw.toggleTheme
|
|
6724
|
-
* @example
|
|
6725
|
-
* bw.loadDefaultStyles(); // inject all default CSS
|
|
6726
|
-
*/
|
|
6727
|
-
bw.loadDefaultStyles = function(options = {}) {
|
|
6728
|
-
const { minify = true, palette } = options;
|
|
6729
|
-
|
|
6730
|
-
// 1. Inject structural CSS (layout, sizing — never changes with theme)
|
|
6731
|
-
if (bw._isBrowser) {
|
|
6732
|
-
var structuralCSS = bw.css(getStructuralStyles());
|
|
6733
|
-
bw.injectCSS(structuralCSS, { id: 'bw_structural', append: false, minify: minify });
|
|
6734
|
-
}
|
|
6735
7117
|
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
return result;
|
|
6740
|
-
};
|
|
7118
|
+
// =========================================================================
|
|
7119
|
+
// v2.0.18 Clean Styles API — makeStyles / applyStyles / loadStyles / etc.
|
|
7120
|
+
// =========================================================================
|
|
6741
7121
|
|
|
7122
|
+
/**
|
|
7123
|
+
* Convert a scope selector to a <style> element id.
|
|
7124
|
+
* @private
|
|
7125
|
+
* @param {string} [scope] - Scope selector (e.g. '#my-dashboard', '.preview')
|
|
7126
|
+
* @returns {string} Style element id (e.g. 'bw_style_my_dashboard')
|
|
7127
|
+
*/
|
|
7128
|
+
function _scopeToStyleId(scope) {
|
|
7129
|
+
if (!scope || scope === '' || scope === 'global') return 'bw_style_global';
|
|
7130
|
+
if (scope === 'reset') return 'bw_style_reset';
|
|
7131
|
+
// Strip leading # or . and convert - to _
|
|
7132
|
+
var clean = scope.replace(/^[#.]/, '').replace(/-/g, '_');
|
|
7133
|
+
return 'bw_style_' + clean;
|
|
7134
|
+
}
|
|
6742
7135
|
|
|
6743
7136
|
/**
|
|
6744
|
-
* Generate a complete
|
|
7137
|
+
* Generate a complete styles object from seed colors and layout config.
|
|
7138
|
+
* Pure function — no DOM, no state, no side effects.
|
|
6745
7139
|
*
|
|
6746
|
-
*
|
|
6747
|
-
* forms, nav, tables, tabs, list groups, pagination, progress, hero, utilities)
|
|
6748
|
-
* scoped under `.name` class. Multiple themes can coexist in the stylesheet.
|
|
6749
|
-
* Swap themes by changing the class on a container element.
|
|
7140
|
+
* All parameters are optional. Defaults to the bitwrench default palette.
|
|
6750
7141
|
*
|
|
6751
|
-
* @param {
|
|
6752
|
-
* @param {
|
|
6753
|
-
* @param {string} config.
|
|
6754
|
-
* @param {string} config.
|
|
6755
|
-
* @param {string} [config.tertiary] - Tertiary/accent color hex (defaults to primary)
|
|
6756
|
-
* @param {string} [config.success='#198754'] - Success color hex
|
|
6757
|
-
* @param {string} [config.danger='#dc3545'] - Danger color hex
|
|
6758
|
-
* @param {string} [config.warning='#ffc107'] - Warning color hex
|
|
6759
|
-
* @param {string} [config.info='#0dcaf0'] - Info color hex
|
|
6760
|
-
* @param {string} [config.light='#f8f9fa'] - Light color hex
|
|
6761
|
-
* @param {string} [config.dark='#212529'] - Dark color hex
|
|
6762
|
-
* @param {string} [config.background] - Page background hex (default: '#ffffff' light, derived dark)
|
|
6763
|
-
* @param {string} [config.surface] - Surface/card background hex (default: '#f8f9fa' light, derived dark)
|
|
7142
|
+
* @param {Object} [config] - Style configuration
|
|
7143
|
+
* @param {string} [config.primary='#006666'] - Primary brand color hex
|
|
7144
|
+
* @param {string} [config.secondary='#6c757d'] - Secondary color hex
|
|
7145
|
+
* @param {string} [config.tertiary] - Tertiary color hex (defaults to primary)
|
|
6764
7146
|
* @param {string} [config.spacing='normal'] - 'compact' | 'normal' | 'spacious'
|
|
6765
7147
|
* @param {string} [config.radius='md'] - 'none' | 'sm' | 'md' | 'lg' | 'pill'
|
|
6766
|
-
* @
|
|
6767
|
-
* @param {string|number} [config.typeRatio='normal'] - 'tight' | 'normal' | 'relaxed' | 'dramatic' or a number
|
|
6768
|
-
* @param {string} [config.elevation='md'] - 'flat' | 'sm' | 'md' | 'lg'
|
|
6769
|
-
* @param {string} [config.motion='standard'] - 'reduced' | 'standard' | 'expressive'
|
|
6770
|
-
* @param {number} [config.harmonize=0.20] - 0-1, semantic color hue shift toward primary
|
|
6771
|
-
* @param {boolean} [config.inject=true] - Inject into DOM (browser only)
|
|
6772
|
-
* @returns {Object} { css, palette, name, isLightPrimary, alternate: { css, palette } }
|
|
7148
|
+
* @returns {Object} { css, alternateCss, rules, alternateRules, palette, alternatePalette, isLightPrimary }
|
|
6773
7149
|
* @category CSS & Styling
|
|
6774
|
-
* @see bw.
|
|
6775
|
-
* @see bw.
|
|
6776
|
-
* @see bw.loadDefaultStyles
|
|
7150
|
+
* @see bw.applyStyles
|
|
7151
|
+
* @see bw.loadStyles
|
|
6777
7152
|
* @example
|
|
6778
|
-
*
|
|
6779
|
-
*
|
|
6780
|
-
*
|
|
6781
|
-
* secondary: '#90e0ef',
|
|
6782
|
-
* tertiary: '#00b4d8'
|
|
6783
|
-
* });
|
|
6784
|
-
*
|
|
6785
|
-
* // Apply to a container
|
|
6786
|
-
* document.getElementById('app').classList.add('ocean');
|
|
6787
|
-
*
|
|
6788
|
-
* // Toggle to alternate palette
|
|
6789
|
-
* bw.toggleTheme();
|
|
6790
|
-
*
|
|
6791
|
-
* // Generate CSS for static export (Node.js)
|
|
6792
|
-
* var result = bw.generateTheme('sunset', {
|
|
6793
|
-
* primary: '#e76f51',
|
|
6794
|
-
* secondary: '#264653',
|
|
6795
|
-
* inject: false
|
|
6796
|
-
* });
|
|
6797
|
-
* fs.writeFileSync('sunset.css', result.css + result.alternate.css);
|
|
7153
|
+
* var styles = bw.makeStyles({ primary: '#4f46e5', secondary: '#d97706' });
|
|
7154
|
+
* console.log(styles.palette.primary.base); // '#4f46e5'
|
|
7155
|
+
* // styles.css contains all themed CSS — nothing injected
|
|
6798
7156
|
*/
|
|
6799
|
-
bw.
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
}
|
|
6803
|
-
|
|
6804
|
-
// Merge with defaults; if user didn't supply tertiary, default to their primary
|
|
6805
|
-
var fullConfig = Object.assign({}, DEFAULT_PALETTE_CONFIG, config);
|
|
6806
|
-
if (!config.tertiary) fullConfig.tertiary = fullConfig.primary;
|
|
7157
|
+
bw.makeStyles = function(config) {
|
|
7158
|
+
var fullConfig = Object.assign({}, DEFAULT_PALETTE_CONFIG, config || {});
|
|
7159
|
+
if (config && !config.tertiary) fullConfig.tertiary = fullConfig.primary;
|
|
6807
7160
|
|
|
6808
7161
|
// Derive primary palette
|
|
6809
7162
|
var palette = derivePalette(fullConfig);
|
|
@@ -6811,131 +7164,207 @@ bw.generateTheme = function(name, config) {
|
|
|
6811
7164
|
// Resolve layout
|
|
6812
7165
|
var layout = resolveLayout(fullConfig);
|
|
6813
7166
|
|
|
6814
|
-
// Generate primary themed CSS rules
|
|
6815
|
-
var themedRules = generateThemedCSS(
|
|
7167
|
+
// Generate primary themed CSS rules (unscoped)
|
|
7168
|
+
var themedRules = generateThemedCSS('', palette, layout);
|
|
6816
7169
|
var cssStr = bw.css(themedRules);
|
|
6817
7170
|
|
|
6818
7171
|
// Derive alternate palette (luminance-inverted)
|
|
6819
7172
|
var altConfig = deriveAlternateConfig(fullConfig);
|
|
6820
7173
|
var altPalette = derivePalette(altConfig);
|
|
6821
7174
|
|
|
6822
|
-
// Generate alternate CSS
|
|
6823
|
-
|
|
6824
|
-
var
|
|
7175
|
+
// Generate alternate CSS rules WITHOUT .bw_theme_alt prefix (raw rules)
|
|
7176
|
+
// applyStyles() wraps them appropriately based on scope
|
|
7177
|
+
var altRawRules = generateThemedCSS('', altPalette, layout);
|
|
7178
|
+
|
|
7179
|
+
// Add body-level surface overrides for the alternate palette.
|
|
7180
|
+
// When .bw_theme_alt is on <html>, ".bw_theme_alt body" correctly matches.
|
|
7181
|
+
altRawRules['body'] = {
|
|
7182
|
+
'color': altPalette.dark.base,
|
|
7183
|
+
'background-color': altPalette.surface || altPalette.light.base
|
|
7184
|
+
};
|
|
7185
|
+
|
|
7186
|
+
var altCssStr = bw.css(altRawRules);
|
|
6825
7187
|
|
|
6826
7188
|
// Determine if primary is light-flavored
|
|
6827
7189
|
var lightPrimary = isLightPalette(fullConfig);
|
|
6828
7190
|
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
7191
|
+
return {
|
|
7192
|
+
css: cssStr,
|
|
7193
|
+
alternateCss: altCssStr,
|
|
7194
|
+
rules: themedRules,
|
|
7195
|
+
alternateRules: altRawRules,
|
|
7196
|
+
palette: palette,
|
|
7197
|
+
alternatePalette: altPalette,
|
|
7198
|
+
isLightPrimary: lightPrimary
|
|
7199
|
+
};
|
|
7200
|
+
};
|
|
6838
7201
|
|
|
6839
|
-
|
|
7202
|
+
/**
|
|
7203
|
+
* Inject styles into the DOM with optional scoping.
|
|
7204
|
+
*
|
|
7205
|
+
* Takes a styles object from `makeStyles()` and creates a single `<style>`
|
|
7206
|
+
* element in `<head>`. If a scope selector is provided, all CSS rules are
|
|
7207
|
+
* wrapped under that selector. Alternate CSS is wrapped under `.bw_theme_alt`.
|
|
7208
|
+
*
|
|
7209
|
+
* @param {Object} styles - Result of `bw.makeStyles()`
|
|
7210
|
+
* @param {string} [scope] - Scope selector (e.g. '#my-dashboard', '.preview'). Omit for global.
|
|
7211
|
+
* @returns {Element|null} The `<style>` element, or null in Node.js
|
|
7212
|
+
* @category CSS & Styling
|
|
7213
|
+
* @see bw.makeStyles
|
|
7214
|
+
* @see bw.loadStyles
|
|
7215
|
+
* @see bw.clearStyles
|
|
7216
|
+
* @example
|
|
7217
|
+
* var styles = bw.makeStyles({ primary: '#4f46e5' });
|
|
7218
|
+
* bw.applyStyles(styles); // global
|
|
7219
|
+
* bw.applyStyles(styles, '#my-dashboard'); // scoped
|
|
7220
|
+
*/
|
|
7221
|
+
bw.applyStyles = function(styles, scope) {
|
|
7222
|
+
if (!bw._isBrowser) return null;
|
|
7223
|
+
if (!styles || !styles.rules) {
|
|
7224
|
+
_cw('bw.applyStyles: invalid styles object');
|
|
7225
|
+
return null;
|
|
6840
7226
|
}
|
|
6841
7227
|
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
7228
|
+
var styleId = _scopeToStyleId(scope);
|
|
7229
|
+
|
|
7230
|
+
// Scope the primary rules if a scope is provided
|
|
7231
|
+
var primaryRules = styles.rules;
|
|
7232
|
+
if (scope) {
|
|
7233
|
+
primaryRules = scopeRulesUnder(primaryRules, scope);
|
|
6848
7234
|
}
|
|
6849
7235
|
|
|
6850
|
-
//
|
|
6851
|
-
var
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
7236
|
+
// Wrap alternate rules with .bw_theme_alt
|
|
7237
|
+
var altRules = styles.alternateRules;
|
|
7238
|
+
if (altRules) {
|
|
7239
|
+
if (scope) {
|
|
7240
|
+
// Scoped compound: #scope.bw_theme_alt .bw_card
|
|
7241
|
+
altRules = scopeRulesUnder(altRules, scope + '.bw_theme_alt');
|
|
7242
|
+
} else {
|
|
7243
|
+
// Global: .bw_theme_alt .bw_card
|
|
7244
|
+
altRules = scopeRulesUnder(altRules, '.bw_theme_alt');
|
|
6859
7245
|
}
|
|
6860
|
-
}
|
|
6861
|
-
bw._activeTheme = result;
|
|
6862
|
-
bw._activeThemeMode = 'primary';
|
|
7246
|
+
}
|
|
6863
7247
|
|
|
6864
|
-
|
|
7248
|
+
// Combine primary + alternate into one CSS string
|
|
7249
|
+
var combined = bw.css(primaryRules);
|
|
7250
|
+
if (altRules) {
|
|
7251
|
+
combined += '\n' + bw.css(altRules);
|
|
7252
|
+
}
|
|
7253
|
+
|
|
7254
|
+
return bw.injectCSS(combined, { id: styleId, append: false });
|
|
6865
7255
|
};
|
|
6866
7256
|
|
|
6867
7257
|
/**
|
|
6868
|
-
*
|
|
6869
|
-
*
|
|
7258
|
+
* Generate and apply styles in one call. Convenience wrapper.
|
|
7259
|
+
*
|
|
7260
|
+
* Equivalent to: `bw.applyStyles(bw.makeStyles(config), scope)`
|
|
6870
7261
|
*
|
|
6871
|
-
* @param {
|
|
6872
|
-
* @
|
|
7262
|
+
* @param {Object} [config] - Style configuration (same as `makeStyles`)
|
|
7263
|
+
* @param {string} [scope] - Scope selector (same as `applyStyles`)
|
|
7264
|
+
* @returns {Element|null} The `<style>` element, or null in Node.js
|
|
6873
7265
|
* @category CSS & Styling
|
|
6874
|
-
* @see bw.
|
|
6875
|
-
* @see bw.
|
|
7266
|
+
* @see bw.makeStyles
|
|
7267
|
+
* @see bw.applyStyles
|
|
6876
7268
|
* @example
|
|
6877
|
-
* bw.
|
|
6878
|
-
* bw.
|
|
6879
|
-
* bw.
|
|
6880
|
-
*/
|
|
6881
|
-
bw.
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
else if (mode === 'light') wantAlt = !isLight;
|
|
6890
|
-
else if (mode === 'dark') wantAlt = isLight;
|
|
6891
|
-
else wantAlt = false;
|
|
6892
|
-
|
|
6893
|
-
if (wantAlt) {
|
|
6894
|
-
root.classList.add('bw_theme_alt');
|
|
6895
|
-
} else {
|
|
6896
|
-
root.classList.remove('bw_theme_alt');
|
|
7269
|
+
* bw.loadStyles(); // defaults, global
|
|
7270
|
+
* bw.loadStyles({ primary: '#4f46e5' }); // custom, global
|
|
7271
|
+
* bw.loadStyles({ primary: '#4f46e5' }, '#my-dashboard'); // custom, scoped
|
|
7272
|
+
*/
|
|
7273
|
+
bw.loadStyles = function(config, scope) {
|
|
7274
|
+
// Also inject structural CSS first (only once)
|
|
7275
|
+
if (bw._isBrowser) {
|
|
7276
|
+
var existing = document.getElementById('bw_structural');
|
|
7277
|
+
if (!existing) {
|
|
7278
|
+
var structuralCSS = bw.css(getStructuralStyles());
|
|
7279
|
+
bw.injectCSS(structuralCSS, { id: 'bw_structural', append: false });
|
|
7280
|
+
}
|
|
6897
7281
|
}
|
|
7282
|
+
return bw.applyStyles(bw.makeStyles(config), scope);
|
|
7283
|
+
};
|
|
6898
7284
|
|
|
6899
|
-
|
|
6900
|
-
|
|
7285
|
+
/**
|
|
7286
|
+
* Inject the CSS reset (box-sizing, html/body font, reduced-motion).
|
|
7287
|
+
* Idempotent — if already injected, returns the existing `<style>` element.
|
|
7288
|
+
*
|
|
7289
|
+
* @returns {Element|null} The `<style>` element, or null in Node.js
|
|
7290
|
+
* @category CSS & Styling
|
|
7291
|
+
* @see bw.loadStyles
|
|
7292
|
+
* @see bw.clearStyles
|
|
7293
|
+
* @example
|
|
7294
|
+
* bw.loadReset(); // inject once, safe to call multiple times
|
|
7295
|
+
*/
|
|
7296
|
+
bw.loadReset = function() {
|
|
7297
|
+
if (!bw._isBrowser) return null;
|
|
7298
|
+
var existing = document.getElementById('bw_style_reset');
|
|
7299
|
+
if (existing) return existing;
|
|
7300
|
+
return bw.injectCSS(bw.css(getResetStyles()), { id: 'bw_style_reset', append: false });
|
|
6901
7301
|
};
|
|
6902
7302
|
|
|
6903
7303
|
/**
|
|
6904
|
-
* Toggle between primary and alternate
|
|
7304
|
+
* Toggle between primary and alternate palettes.
|
|
7305
|
+
*
|
|
7306
|
+
* Adds/removes the `bw_theme_alt` class on the scoping element.
|
|
7307
|
+
* Without a scope, toggles on `<html>` (global).
|
|
7308
|
+
* With a scope, toggles on the first matching element.
|
|
6905
7309
|
*
|
|
7310
|
+
* @param {string} [scope] - Scope selector (e.g. '#my-dashboard'). Omit for global.
|
|
6906
7311
|
* @returns {string} Active mode after toggle: 'primary' or 'alternate'
|
|
6907
7312
|
* @category CSS & Styling
|
|
6908
|
-
* @see bw.
|
|
6909
|
-
* @see bw.
|
|
7313
|
+
* @see bw.applyStyles
|
|
7314
|
+
* @see bw.clearStyles
|
|
6910
7315
|
* @example
|
|
6911
|
-
* bw.
|
|
7316
|
+
* bw.toggleStyles(); // global toggle on <html>
|
|
7317
|
+
* bw.toggleStyles('#my-dashboard'); // scoped toggle
|
|
6912
7318
|
*/
|
|
6913
|
-
bw.
|
|
6914
|
-
|
|
6915
|
-
|
|
7319
|
+
bw.toggleStyles = function(scope) {
|
|
7320
|
+
if (!bw._isBrowser) return 'primary';
|
|
7321
|
+
var target;
|
|
7322
|
+
if (scope) {
|
|
7323
|
+
var els = bw.$(scope);
|
|
7324
|
+
target = els[0];
|
|
7325
|
+
} else {
|
|
7326
|
+
target = document.documentElement;
|
|
7327
|
+
}
|
|
7328
|
+
if (!target) return 'primary';
|
|
7329
|
+
|
|
7330
|
+
var hasAlt = target.classList.contains('bw_theme_alt');
|
|
7331
|
+
if (hasAlt) {
|
|
7332
|
+
target.classList.remove('bw_theme_alt');
|
|
7333
|
+
return 'primary';
|
|
7334
|
+
} else {
|
|
7335
|
+
target.classList.add('bw_theme_alt');
|
|
7336
|
+
return 'alternate';
|
|
7337
|
+
}
|
|
6916
7338
|
};
|
|
6917
7339
|
|
|
6918
7340
|
/**
|
|
6919
|
-
* Remove
|
|
6920
|
-
*
|
|
6921
|
-
*
|
|
7341
|
+
* Remove injected styles for a given scope.
|
|
7342
|
+
*
|
|
7343
|
+
* Finds the `<style>` element by id and removes it. Also removes
|
|
7344
|
+
* the `bw_theme_alt` class from the relevant element.
|
|
6922
7345
|
*
|
|
7346
|
+
* @param {string} [scope] - Scope selector. Omit to remove global styles.
|
|
6923
7347
|
* @category CSS & Styling
|
|
6924
|
-
* @see bw.
|
|
7348
|
+
* @see bw.applyStyles
|
|
7349
|
+
* @see bw.loadStyles
|
|
6925
7350
|
* @example
|
|
6926
|
-
* bw.
|
|
6927
|
-
* bw.
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
7351
|
+
* bw.clearStyles(); // remove global styles
|
|
7352
|
+
* bw.clearStyles('#my-dashboard'); // remove scoped styles
|
|
7353
|
+
* bw.clearStyles('reset'); // remove the CSS reset
|
|
7354
|
+
*/
|
|
7355
|
+
bw.clearStyles = function(scope) {
|
|
7356
|
+
if (!bw._isBrowser) return;
|
|
7357
|
+
var styleId = _scopeToStyleId(scope);
|
|
7358
|
+
var el = document.getElementById(styleId);
|
|
7359
|
+
if (el) el.remove();
|
|
7360
|
+
|
|
7361
|
+
// Also remove bw_theme_alt from the relevant element
|
|
7362
|
+
if (scope && scope !== 'reset' && scope !== 'global') {
|
|
7363
|
+
var targets = bw.$(scope);
|
|
7364
|
+
if (targets[0]) targets[0].classList.remove('bw_theme_alt');
|
|
7365
|
+
} else if (!scope || scope === 'global') {
|
|
7366
|
+
document.documentElement.classList.remove('bw_theme_alt');
|
|
6936
7367
|
}
|
|
6937
|
-
bw._activeTheme = null;
|
|
6938
|
-
bw._activeThemeMode = 'primary';
|
|
6939
7368
|
};
|
|
6940
7369
|
|
|
6941
7370
|
// Expose color utility functions on bw namespace
|
|
@@ -7158,10 +7587,15 @@ bw.copyToClipboard = function(text) {
|
|
|
7158
7587
|
* @param {Object} config - Table configuration
|
|
7159
7588
|
* @param {Array<Object>} config.data - Array of row objects to display
|
|
7160
7589
|
* @param {Array<Object>} [config.columns] - Column definitions with key, label, render
|
|
7161
|
-
* @param {string} [config.className='
|
|
7590
|
+
* @param {string} [config.className=''] - Additional CSS classes for table element
|
|
7162
7591
|
* @param {boolean} [config.sortable=true] - Enable click-to-sort headers
|
|
7163
7592
|
* @param {Function} [config.onSort] - Sort callback (column, direction)
|
|
7164
|
-
* @
|
|
7593
|
+
* @param {boolean} [config.selectable=false] - Enable row selection on click
|
|
7594
|
+
* @param {Function} [config.onRowClick] - Row click callback (row, index, event)
|
|
7595
|
+
* @param {number} [config.pageSize] - Rows per page (enables pagination when set)
|
|
7596
|
+
* @param {number} [config.currentPage=1] - Current page number (1-based)
|
|
7597
|
+
* @param {Function} [config.onPageChange] - Page change callback (newPage)
|
|
7598
|
+
* @returns {Object} TACO object for table (with optional pagination controls)
|
|
7165
7599
|
* @category Component Builders
|
|
7166
7600
|
* @see bw.makeDataTable
|
|
7167
7601
|
* @example
|
|
@@ -7173,7 +7607,12 @@ bw.copyToClipboard = function(text) {
|
|
|
7173
7607
|
* columns: [
|
|
7174
7608
|
* { key: 'name', label: 'Name' },
|
|
7175
7609
|
* { key: 'age', label: 'Age' }
|
|
7176
|
-
* ]
|
|
7610
|
+
* ],
|
|
7611
|
+
* selectable: true,
|
|
7612
|
+
* onRowClick: function(row, i) { console.log('clicked', row.name); },
|
|
7613
|
+
* pageSize: 10,
|
|
7614
|
+
* currentPage: 1,
|
|
7615
|
+
* onPageChange: function(page) { console.log('page', page); }
|
|
7177
7616
|
* });
|
|
7178
7617
|
*/
|
|
7179
7618
|
bw.makeTable = function(config) {
|
|
@@ -7186,41 +7625,47 @@ bw.makeTable = function(config) {
|
|
|
7186
7625
|
sortable = true,
|
|
7187
7626
|
onSort,
|
|
7188
7627
|
sortColumn,
|
|
7189
|
-
sortDirection = 'asc'
|
|
7628
|
+
sortDirection = 'asc',
|
|
7629
|
+
selectable = false,
|
|
7630
|
+
onRowClick,
|
|
7631
|
+
pageSize,
|
|
7632
|
+
currentPage = 1,
|
|
7633
|
+
onPageChange
|
|
7190
7634
|
} = config;
|
|
7191
7635
|
|
|
7192
|
-
// Build class list: always include bw_table, add striped/hover, append user className
|
|
7636
|
+
// Build class list: always include bw_table, add striped/hover/selectable, append user className
|
|
7193
7637
|
let cls = 'bw_table';
|
|
7194
7638
|
if (striped) cls += ' bw_table_striped';
|
|
7195
|
-
if (hover) cls += ' bw_table_hover';
|
|
7639
|
+
if (hover || selectable) cls += ' bw_table_hover';
|
|
7640
|
+
if (selectable) cls += ' bw_table_selectable';
|
|
7196
7641
|
if (className) cls += ' ' + className;
|
|
7197
7642
|
cls = cls.trim();
|
|
7198
|
-
|
|
7643
|
+
|
|
7199
7644
|
// Auto-detect columns if not provided
|
|
7200
|
-
const cols = columns || (data.length > 0
|
|
7201
|
-
?
|
|
7645
|
+
const cols = columns || (data.length > 0
|
|
7646
|
+
? _keys(data[0]).map(key => ({ key, label: key }))
|
|
7202
7647
|
: []);
|
|
7203
|
-
|
|
7648
|
+
|
|
7204
7649
|
// Current sort state
|
|
7205
7650
|
let currentSortColumn = sortColumn || null;
|
|
7206
7651
|
let currentSortDirection = sortDirection;
|
|
7207
|
-
|
|
7652
|
+
|
|
7208
7653
|
// Sort data if column specified
|
|
7209
7654
|
let sortedData = [...data];
|
|
7210
7655
|
if (currentSortColumn) {
|
|
7211
7656
|
sortedData.sort((a, b) => {
|
|
7212
7657
|
const aVal = a[currentSortColumn];
|
|
7213
7658
|
const bVal = b[currentSortColumn];
|
|
7214
|
-
|
|
7659
|
+
|
|
7215
7660
|
// Handle different types
|
|
7216
|
-
if (
|
|
7661
|
+
if (_is(aVal, 'number') && _is(bVal, 'number')) {
|
|
7217
7662
|
return currentSortDirection === 'asc' ? aVal - bVal : bVal - aVal;
|
|
7218
7663
|
}
|
|
7219
|
-
|
|
7664
|
+
|
|
7220
7665
|
// String comparison
|
|
7221
7666
|
const aStr = String(aVal || '').toLowerCase();
|
|
7222
7667
|
const bStr = String(bVal || '').toLowerCase();
|
|
7223
|
-
|
|
7668
|
+
|
|
7224
7669
|
if (currentSortDirection === 'asc') {
|
|
7225
7670
|
return aStr.localeCompare(bStr);
|
|
7226
7671
|
} else {
|
|
@@ -7228,23 +7673,32 @@ bw.makeTable = function(config) {
|
|
|
7228
7673
|
}
|
|
7229
7674
|
});
|
|
7230
7675
|
}
|
|
7231
|
-
|
|
7676
|
+
|
|
7677
|
+
// Pagination
|
|
7678
|
+
const totalRows = sortedData.length;
|
|
7679
|
+
const totalPages = pageSize ? Math.max(1, Math.ceil(totalRows / pageSize)) : 1;
|
|
7680
|
+
const page = Math.max(1, Math.min(currentPage, totalPages));
|
|
7681
|
+
if (pageSize) {
|
|
7682
|
+
const start = (page - 1) * pageSize;
|
|
7683
|
+
sortedData = sortedData.slice(start, start + pageSize);
|
|
7684
|
+
}
|
|
7685
|
+
|
|
7232
7686
|
// Create sort handler
|
|
7233
7687
|
const handleSort = (column) => {
|
|
7234
7688
|
if (!sortable) return;
|
|
7235
|
-
|
|
7689
|
+
|
|
7236
7690
|
if (currentSortColumn === column) {
|
|
7237
7691
|
currentSortDirection = currentSortDirection === 'asc' ? 'desc' : 'asc';
|
|
7238
7692
|
} else {
|
|
7239
7693
|
currentSortColumn = column;
|
|
7240
7694
|
currentSortDirection = 'asc';
|
|
7241
7695
|
}
|
|
7242
|
-
|
|
7696
|
+
|
|
7243
7697
|
if (onSort) {
|
|
7244
7698
|
onSort(column, currentSortDirection);
|
|
7245
7699
|
}
|
|
7246
7700
|
};
|
|
7247
|
-
|
|
7701
|
+
|
|
7248
7702
|
// Build table header
|
|
7249
7703
|
const thead = {
|
|
7250
7704
|
t: 'thead',
|
|
@@ -7267,24 +7721,87 @@ bw.makeTable = function(config) {
|
|
|
7267
7721
|
}))
|
|
7268
7722
|
}
|
|
7269
7723
|
};
|
|
7270
|
-
|
|
7271
|
-
// Build table body
|
|
7724
|
+
|
|
7725
|
+
// Build table body with selectable/onRowClick support
|
|
7272
7726
|
const tbody = {
|
|
7273
7727
|
t: 'tbody',
|
|
7274
|
-
c: sortedData.map(row =>
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7728
|
+
c: sortedData.map((row, idx) => {
|
|
7729
|
+
const globalIdx = pageSize ? (page - 1) * pageSize + idx : idx;
|
|
7730
|
+
const rowAttrs = {};
|
|
7731
|
+
if (selectable || onRowClick) {
|
|
7732
|
+
rowAttrs.style = 'cursor:pointer;';
|
|
7733
|
+
rowAttrs.onclick = function(e) {
|
|
7734
|
+
if (selectable) {
|
|
7735
|
+
// Toggle selected class on this row
|
|
7736
|
+
var tr = e.currentTarget;
|
|
7737
|
+
tr.classList.toggle('bw_table_row_selected');
|
|
7738
|
+
}
|
|
7739
|
+
if (onRowClick) {
|
|
7740
|
+
onRowClick(row, globalIdx, e);
|
|
7741
|
+
}
|
|
7742
|
+
};
|
|
7743
|
+
}
|
|
7744
|
+
return {
|
|
7745
|
+
t: 'tr',
|
|
7746
|
+
a: rowAttrs,
|
|
7747
|
+
c: cols.map(col => ({
|
|
7748
|
+
t: 'td',
|
|
7749
|
+
c: col.render ? col.render(row[col.key], row) : String(row[col.key] || '')
|
|
7750
|
+
}))
|
|
7751
|
+
};
|
|
7752
|
+
})
|
|
7281
7753
|
};
|
|
7282
|
-
|
|
7283
|
-
|
|
7754
|
+
|
|
7755
|
+
const table = {
|
|
7284
7756
|
t: 'table',
|
|
7285
7757
|
a: { class: cls },
|
|
7286
7758
|
c: [thead, tbody]
|
|
7287
7759
|
};
|
|
7760
|
+
|
|
7761
|
+
// If no pagination, return table directly
|
|
7762
|
+
if (!pageSize) return table;
|
|
7763
|
+
|
|
7764
|
+
// Build pagination controls
|
|
7765
|
+
const pageButtons = [];
|
|
7766
|
+
// Previous button
|
|
7767
|
+
pageButtons.push({
|
|
7768
|
+
t: 'button',
|
|
7769
|
+
a: {
|
|
7770
|
+
class: 'bw_btn bw_btn_sm',
|
|
7771
|
+
disabled: page <= 1 ? 'disabled' : undefined,
|
|
7772
|
+
onclick: page > 1 && onPageChange ? function() { onPageChange(page - 1); } : undefined
|
|
7773
|
+
},
|
|
7774
|
+
c: 'Prev'
|
|
7775
|
+
});
|
|
7776
|
+
// Page info
|
|
7777
|
+
pageButtons.push({
|
|
7778
|
+
t: 'span',
|
|
7779
|
+
a: { style: 'margin:0 0.5rem;font-size:0.875rem;' },
|
|
7780
|
+
c: 'Page ' + page + ' of ' + totalPages
|
|
7781
|
+
});
|
|
7782
|
+
// Next button
|
|
7783
|
+
pageButtons.push({
|
|
7784
|
+
t: 'button',
|
|
7785
|
+
a: {
|
|
7786
|
+
class: 'bw_btn bw_btn_sm',
|
|
7787
|
+
disabled: page >= totalPages ? 'disabled' : undefined,
|
|
7788
|
+
onclick: page < totalPages && onPageChange ? function() { onPageChange(page + 1); } : undefined
|
|
7789
|
+
},
|
|
7790
|
+
c: 'Next'
|
|
7791
|
+
});
|
|
7792
|
+
|
|
7793
|
+
return {
|
|
7794
|
+
t: 'div',
|
|
7795
|
+
a: { class: 'bw_table_paginated' },
|
|
7796
|
+
c: [
|
|
7797
|
+
table,
|
|
7798
|
+
{
|
|
7799
|
+
t: 'div',
|
|
7800
|
+
a: { class: 'bw_table_pagination', style: 'display:flex;align-items:center;justify-content:flex-end;padding:0.5rem 0;gap:0.25rem;' },
|
|
7801
|
+
c: pageButtons
|
|
7802
|
+
}
|
|
7803
|
+
]
|
|
7804
|
+
};
|
|
7288
7805
|
};
|
|
7289
7806
|
|
|
7290
7807
|
/**
|
|
@@ -7323,7 +7840,7 @@ bw.makeTable = function(config) {
|
|
|
7323
7840
|
bw.makeTableFromArray = function(config) {
|
|
7324
7841
|
const { data = [], headerRow = true, columns, ...rest } = config;
|
|
7325
7842
|
|
|
7326
|
-
if (!
|
|
7843
|
+
if (!_isA(data) || data.length === 0) {
|
|
7327
7844
|
return bw.makeTable({ data: [], columns: columns || [], ...rest });
|
|
7328
7845
|
}
|
|
7329
7846
|
|
|
@@ -7405,7 +7922,7 @@ bw.makeBarChart = function(config) {
|
|
|
7405
7922
|
className = ''
|
|
7406
7923
|
} = config;
|
|
7407
7924
|
|
|
7408
|
-
if (!
|
|
7925
|
+
if (!_isA(data) || data.length === 0) {
|
|
7409
7926
|
return { t: 'div', a: { class: ('bw_bar_chart_container ' + className).trim() }, c: '' };
|
|
7410
7927
|
}
|
|
7411
7928
|
|
|
@@ -7554,7 +8071,7 @@ bw._componentRegistry = new Map();
|
|
|
7554
8071
|
*/
|
|
7555
8072
|
bw.render = function(element, position, taco) {
|
|
7556
8073
|
// Get target element
|
|
7557
|
-
const targetEl =
|
|
8074
|
+
const targetEl = _is(element, 'string')
|
|
7558
8075
|
? document.querySelector(element)
|
|
7559
8076
|
: element;
|
|
7560
8077
|
|
|
@@ -7704,7 +8221,7 @@ bw.render = function(element, position, taco) {
|
|
|
7704
8221
|
setContent(content) {
|
|
7705
8222
|
this._taco.c = content;
|
|
7706
8223
|
if (this.element) {
|
|
7707
|
-
if (
|
|
8224
|
+
if (_is(content, 'string')) {
|
|
7708
8225
|
this.element.textContent = content;
|
|
7709
8226
|
} else {
|
|
7710
8227
|
// Re-render for complex content
|