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
package/dist/bitwrench.esm.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
/*! bitwrench v2.0.
|
|
1
|
+
/*! bitwrench 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
|
/**
|
|
@@ -3591,7 +3759,7 @@ function makeCol(props = {}) {
|
|
|
3591
3759
|
if (breakpoint === 'xs') {
|
|
3592
3760
|
classes.push(`bw_col_${value}`);
|
|
3593
3761
|
} else {
|
|
3594
|
-
classes.push(`bw_col_${breakpoint}
|
|
3762
|
+
classes.push(`bw_col_${breakpoint}_${value}`);
|
|
3595
3763
|
}
|
|
3596
3764
|
});
|
|
3597
3765
|
} else if (size) {
|
|
@@ -4974,8 +5142,8 @@ function makePagination(props = {}) {
|
|
|
4974
5142
|
t: 'li',
|
|
4975
5143
|
a: { class: `bw_page_item ${currentPage <= 1 ? 'bw_disabled' : ''}`.trim() },
|
|
4976
5144
|
c: {
|
|
4977
|
-
t: '
|
|
4978
|
-
a: { class: 'bw_page_link',
|
|
5145
|
+
t: 'button',
|
|
5146
|
+
a: { class: 'bw_page_link', type: 'button', onclick: handleClick(currentPage - 1), 'aria-label': 'Previous', disabled: currentPage <= 1 ? true : undefined },
|
|
4979
5147
|
c: '\u2039'
|
|
4980
5148
|
}
|
|
4981
5149
|
});
|
|
@@ -4987,8 +5155,8 @@ function makePagination(props = {}) {
|
|
|
4987
5155
|
t: 'li',
|
|
4988
5156
|
a: { class: `bw_page_item ${pageNum === currentPage ? 'bw_active' : ''}`.trim() },
|
|
4989
5157
|
c: {
|
|
4990
|
-
t: '
|
|
4991
|
-
a: { class: 'bw_page_link',
|
|
5158
|
+
t: 'button',
|
|
5159
|
+
a: { class: 'bw_page_link', type: 'button', onclick: handleClick(pageNum), 'aria-current': pageNum === currentPage ? 'page' : undefined },
|
|
4992
5160
|
c: '' + pageNum
|
|
4993
5161
|
}
|
|
4994
5162
|
});
|
|
@@ -5000,8 +5168,8 @@ function makePagination(props = {}) {
|
|
|
5000
5168
|
t: 'li',
|
|
5001
5169
|
a: { class: `bw_page_item ${currentPage >= pages ? 'bw_disabled' : ''}`.trim() },
|
|
5002
5170
|
c: {
|
|
5003
|
-
t: '
|
|
5004
|
-
a: { class: 'bw_page_link',
|
|
5171
|
+
t: 'button',
|
|
5172
|
+
a: { class: 'bw_page_link', type: 'button', onclick: handleClick(currentPage + 1), 'aria-label': 'Next', disabled: currentPage >= pages ? true : undefined },
|
|
5005
5173
|
c: '\u203A'
|
|
5006
5174
|
}
|
|
5007
5175
|
});
|
|
@@ -6874,7 +7042,11 @@ var BCCL = {
|
|
|
6874
7042
|
function make(type, props) {
|
|
6875
7043
|
var def = BCCL[type];
|
|
6876
7044
|
if (!def) throw new Error('bw.make: unknown component type "' + type + '". Available: ' + Object.keys(BCCL).join(', '));
|
|
6877
|
-
|
|
7045
|
+
var taco = def.make(props || {});
|
|
7046
|
+
if (taco && typeof taco === 'object') {
|
|
7047
|
+
taco._bwFactory = { type: type, props: props || {} };
|
|
7048
|
+
}
|
|
7049
|
+
return taco;
|
|
6878
7050
|
}
|
|
6879
7051
|
|
|
6880
7052
|
var components = /*#__PURE__*/Object.freeze({
|
|
@@ -6995,7 +7167,7 @@ const bw = {
|
|
|
6995
7167
|
__monkey_patch_is_nodejs__: {
|
|
6996
7168
|
_value: 'ignore',
|
|
6997
7169
|
set: function(x) {
|
|
6998
|
-
this._value = (
|
|
7170
|
+
this._value = _is(x, 'boolean') ? x : 'ignore';
|
|
6999
7171
|
},
|
|
7000
7172
|
get: function() {
|
|
7001
7173
|
return this._value;
|
|
@@ -7043,6 +7215,67 @@ Object.defineProperty(bw, '_isBrowser', {
|
|
|
7043
7215
|
configurable: true
|
|
7044
7216
|
});
|
|
7045
7217
|
|
|
7218
|
+
// ── Internal aliases ─────────────────────────────────────────────────────
|
|
7219
|
+
// Short names for frequently-used builtins and internal methods.
|
|
7220
|
+
// Same pattern as v1 (_to = bw.typeOf, etc.).
|
|
7221
|
+
//
|
|
7222
|
+
// Why: Terser can't shorten global property chains (console.warn,
|
|
7223
|
+
// Object.prototype.hasOwnProperty, Array.isArray, document.createElement)
|
|
7224
|
+
// because it can't prove they're side-effect-free. We can, so we alias
|
|
7225
|
+
// them here. Each alias saves bytes in the minified output, and the short
|
|
7226
|
+
// names also reduce visual noise in the hot paths (binding pipeline,
|
|
7227
|
+
// createDOM, etc.).
|
|
7228
|
+
//
|
|
7229
|
+
// Alias Target Sites
|
|
7230
|
+
// ───────── ────────────────────────────────────── ─────
|
|
7231
|
+
// _hop Object.prototype.hasOwnProperty 15
|
|
7232
|
+
// _isA Array.isArray 25
|
|
7233
|
+
// _keys Object.keys 7
|
|
7234
|
+
// _to bw.typeOf (type string) 26
|
|
7235
|
+
// _is type check boolean: _is(x,'string') ~50
|
|
7236
|
+
// _cw console.warn 8
|
|
7237
|
+
// _cl console.log 11
|
|
7238
|
+
// _ce console.error 4
|
|
7239
|
+
// _chp ComponentHandle.prototype 28 (defined after constructor)
|
|
7240
|
+
//
|
|
7241
|
+
// Note: document.createElement etc. are NOT aliased because they require
|
|
7242
|
+
// `this === document` and .bind() would add overhead on every call.
|
|
7243
|
+
// Console aliases use thin wrappers (not direct refs) so test monkey-
|
|
7244
|
+
// patching of console.warn/log/error continues to work.
|
|
7245
|
+
//
|
|
7246
|
+
// `typeof x` for UNDECLARED globals (window, document, process, require,
|
|
7247
|
+
// EventSource, navigator, Promise, __filename, import.meta) MUST stay as
|
|
7248
|
+
// raw `typeof` — calling _to(x) when x doesn't exist throws ReferenceError.
|
|
7249
|
+
//
|
|
7250
|
+
// ── v1 functional type helpers (kept for reference, not currently used) ──
|
|
7251
|
+
// _toa(x, type, trueVal, falseVal) — bw.typeAssign:
|
|
7252
|
+
// returns trueVal if _to(x)===type, else falseVal.
|
|
7253
|
+
// Replaces: (typeof x === 'string') ? A : B → _toa(x,'string',A,B)
|
|
7254
|
+
// _toc(x, type, trueVal, falseVal) — bw.typeConvert:
|
|
7255
|
+
// same as _toa but if trueVal/falseVal are functions, calls them with x.
|
|
7256
|
+
// Replaces: typeof x === 'string' ? fn(x) : default → _toc(x,'string',fn,default)
|
|
7257
|
+
// Uncomment if pattern frequency justifies them:
|
|
7258
|
+
// var _toa = function(x, t, y, n) { return _to(x) === t ? y : n; };
|
|
7259
|
+
// 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); };
|
|
7260
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
7261
|
+
var _hop = Object.prototype.hasOwnProperty;
|
|
7262
|
+
var _isA = Array.isArray;
|
|
7263
|
+
var _keys = Object.keys;
|
|
7264
|
+
var _to = typeOf; // imported from bitwrench-utils.js
|
|
7265
|
+
var _is = function(x, t) { var r = _to(x); return r === t || r.toLowerCase() === t; };
|
|
7266
|
+
// Console aliases use thin wrappers (not direct references) so that test
|
|
7267
|
+
// code can monkey-patch console.warn/log/error and the patches take effect.
|
|
7268
|
+
var _cw = function() { console.warn.apply(console, arguments); };
|
|
7269
|
+
var _cl = function() { console.log.apply(console, arguments); };
|
|
7270
|
+
var _ce = function() { console.error.apply(console, arguments); };
|
|
7271
|
+
|
|
7272
|
+
/**
|
|
7273
|
+
* Debug flag. When true, emits console.warn for silent binding failures
|
|
7274
|
+
* (missing paths, null refs, auto-created intermediate objects).
|
|
7275
|
+
* @type {boolean}
|
|
7276
|
+
*/
|
|
7277
|
+
bw.debug = false;
|
|
7278
|
+
|
|
7046
7279
|
/**
|
|
7047
7280
|
* Lazy-resolve Node.js `fs` module.
|
|
7048
7281
|
* Tries require('fs') first (available in CJS/UMD Node.js builds),
|
|
@@ -7190,7 +7423,7 @@ bw.uuid = function(prefix) {
|
|
|
7190
7423
|
*/
|
|
7191
7424
|
bw._el = function(id) {
|
|
7192
7425
|
// Pass-through for DOM elements
|
|
7193
|
-
if (
|
|
7426
|
+
if (!_is(id, 'string')) return id || null;
|
|
7194
7427
|
if (!id) return null;
|
|
7195
7428
|
if (!bw._isBrowser) return null;
|
|
7196
7429
|
|
|
@@ -7218,7 +7451,12 @@ bw._el = function(id) {
|
|
|
7218
7451
|
el = document.querySelector('[data-bw_id="' + id + '"]');
|
|
7219
7452
|
}
|
|
7220
7453
|
|
|
7221
|
-
// 5.
|
|
7454
|
+
// 5. Try class-based lookup for bw_uuid_* tokens (UUID addressing)
|
|
7455
|
+
if (!el && id.indexOf('bw_uuid_') === 0) {
|
|
7456
|
+
el = document.querySelector('.' + id);
|
|
7457
|
+
}
|
|
7458
|
+
|
|
7459
|
+
// 6. Cache the result for next time
|
|
7222
7460
|
if (el) {
|
|
7223
7461
|
bw._nodeMap[id] = el;
|
|
7224
7462
|
}
|
|
@@ -7271,6 +7509,84 @@ bw._deregisterNode = function(el, bwId) {
|
|
|
7271
7509
|
}
|
|
7272
7510
|
};
|
|
7273
7511
|
|
|
7512
|
+
// ===================================================================================
|
|
7513
|
+
// bw.assignUUID() / bw.getUUID() — Explicit UUID addressing for TACO objects
|
|
7514
|
+
// ===================================================================================
|
|
7515
|
+
|
|
7516
|
+
/**
|
|
7517
|
+
* Regex to match a bw_uuid_* token in a class string.
|
|
7518
|
+
* @private
|
|
7519
|
+
*/
|
|
7520
|
+
var _UUID_RE = /\bbw_uuid_[a-z0-9_]+\b/;
|
|
7521
|
+
|
|
7522
|
+
/**
|
|
7523
|
+
* Assign a UUID to a TACO object by appending a `bw_uuid_*` token to `taco.a.class`.
|
|
7524
|
+
*
|
|
7525
|
+
* Idempotent by default — calling twice returns the same UUID. Pass `forceNew=true`
|
|
7526
|
+
* to replace an existing UUID (useful in loops where each TACO needs a unique ID).
|
|
7527
|
+
*
|
|
7528
|
+
* @param {Object} taco - A TACO object `{t, a, c, o}`
|
|
7529
|
+
* @param {boolean} [forceNew=false] - If true, replaces any existing UUID with a new one
|
|
7530
|
+
* @returns {string} The UUID string (e.g. 'bw_uuid_a1b2c3d4e5')
|
|
7531
|
+
* @category Identifiers
|
|
7532
|
+
* @example
|
|
7533
|
+
* var card = bw.makeStatCard({ value: '0', label: 'Scans' });
|
|
7534
|
+
* var uuid = bw.assignUUID(card); // 'bw_uuid_a1b2c3d4e5'
|
|
7535
|
+
* var same = bw.assignUUID(card); // same UUID (idempotent)
|
|
7536
|
+
* var diff = bw.assignUUID(card, true); // new UUID (forced)
|
|
7537
|
+
*/
|
|
7538
|
+
bw.assignUUID = function(taco, forceNew) {
|
|
7539
|
+
if (!taco || !_is(taco, 'object')) return null;
|
|
7540
|
+
|
|
7541
|
+
// Ensure taco.a exists
|
|
7542
|
+
if (!taco.a) taco.a = {};
|
|
7543
|
+
if (!_is(taco.a.class, 'string')) taco.a.class = taco.a.class ? String(taco.a.class) : '';
|
|
7544
|
+
|
|
7545
|
+
var existing = taco.a.class.match(_UUID_RE);
|
|
7546
|
+
|
|
7547
|
+
if (existing && !forceNew) {
|
|
7548
|
+
return existing[0];
|
|
7549
|
+
}
|
|
7550
|
+
|
|
7551
|
+
// Remove old UUID if forceNew
|
|
7552
|
+
if (existing) {
|
|
7553
|
+
taco.a.class = taco.a.class.replace(_UUID_RE, '').replace(/\s+/g, ' ').trim();
|
|
7554
|
+
}
|
|
7555
|
+
|
|
7556
|
+
var uuid = bw.uuid('uuid');
|
|
7557
|
+
taco.a.class = (taco.a.class ? taco.a.class + ' ' : '') + uuid;
|
|
7558
|
+
return uuid;
|
|
7559
|
+
};
|
|
7560
|
+
|
|
7561
|
+
/**
|
|
7562
|
+
* Read the UUID from a TACO object or DOM element. Pure getter, no side effects.
|
|
7563
|
+
*
|
|
7564
|
+
* @param {Object|Element} tacoOrElement - A TACO object or DOM element
|
|
7565
|
+
* @returns {string|null} The UUID string, or null if none assigned
|
|
7566
|
+
* @category Identifiers
|
|
7567
|
+
* @example
|
|
7568
|
+
* bw.getUUID(card) // 'bw_uuid_a1b2c3d4e5' (from TACO)
|
|
7569
|
+
* bw.getUUID(domEl) // 'bw_uuid_a1b2c3d4e5' (from DOM element)
|
|
7570
|
+
* bw.getUUID({t:'div'}) // null (no UUID)
|
|
7571
|
+
*/
|
|
7572
|
+
bw.getUUID = function(tacoOrElement) {
|
|
7573
|
+
if (!tacoOrElement) return null;
|
|
7574
|
+
|
|
7575
|
+
var classStr;
|
|
7576
|
+
// DOM element: check className
|
|
7577
|
+
if (tacoOrElement.className !== undefined && tacoOrElement.tagName) {
|
|
7578
|
+
classStr = tacoOrElement.className;
|
|
7579
|
+
}
|
|
7580
|
+
// TACO object: check a.class
|
|
7581
|
+
else if (tacoOrElement.a && _is(tacoOrElement.a.class, 'string')) {
|
|
7582
|
+
classStr = tacoOrElement.a.class;
|
|
7583
|
+
}
|
|
7584
|
+
|
|
7585
|
+
if (!classStr) return null;
|
|
7586
|
+
var match = classStr.match(_UUID_RE);
|
|
7587
|
+
return match ? match[0] : null;
|
|
7588
|
+
};
|
|
7589
|
+
|
|
7274
7590
|
/**
|
|
7275
7591
|
* Escape HTML special characters to prevent XSS.
|
|
7276
7592
|
*
|
|
@@ -7286,7 +7602,7 @@ bw._deregisterNode = function(el, bwId) {
|
|
|
7286
7602
|
* // => '<b>Hello</b> & "world"'
|
|
7287
7603
|
*/
|
|
7288
7604
|
bw.escapeHTML = function(str) {
|
|
7289
|
-
if (
|
|
7605
|
+
if (!_is(str, 'string')) return '';
|
|
7290
7606
|
|
|
7291
7607
|
const escapeMap = {
|
|
7292
7608
|
'&': '&',
|
|
@@ -7320,6 +7636,42 @@ bw.raw = function(str) {
|
|
|
7320
7636
|
return { __bw_raw: true, v: String(str) };
|
|
7321
7637
|
};
|
|
7322
7638
|
|
|
7639
|
+
/**
|
|
7640
|
+
* Hyperscript-style TACO constructor.
|
|
7641
|
+
*
|
|
7642
|
+
* A convenience helper that returns a canonical TACO object from positional
|
|
7643
|
+
* arguments. The return value is a plain object — serializable, works with
|
|
7644
|
+
* bwserve, and accepted everywhere TACO is accepted.
|
|
7645
|
+
*
|
|
7646
|
+
* @param {string} tag - HTML tag name (e.g. 'div', 'p', 'section')
|
|
7647
|
+
* @param {Object|null} [attrs] - HTML attributes object. Pass null or omit to skip.
|
|
7648
|
+
* @param {*} [content] - Content: string, number, TACO object, or array of children.
|
|
7649
|
+
* @param {Object} [options] - TACO options (state, lifecycle hooks, render fn).
|
|
7650
|
+
* @returns {Object} Plain TACO object {t, a?, c?, o?}
|
|
7651
|
+
* @category Utilities
|
|
7652
|
+
* @see bw.html
|
|
7653
|
+
* @see bw.createDOM
|
|
7654
|
+
* @see bw.DOM
|
|
7655
|
+
* @example
|
|
7656
|
+
* bw.h('div')
|
|
7657
|
+
* // => { t: 'div' }
|
|
7658
|
+
*
|
|
7659
|
+
* bw.h('p', { class: 'bw_text_muted' }, 'Hello')
|
|
7660
|
+
* // => { t: 'p', a: { class: 'bw_text_muted' }, c: 'Hello' }
|
|
7661
|
+
*
|
|
7662
|
+
* bw.h('ul', null, [
|
|
7663
|
+
* bw.h('li', null, 'one'),
|
|
7664
|
+
* bw.h('li', null, 'two')
|
|
7665
|
+
* ])
|
|
7666
|
+
* // => { t: 'ul', c: [{ t: 'li', c: 'one' }, { t: 'li', c: 'two' }] }
|
|
7667
|
+
*/
|
|
7668
|
+
bw.h = function(tag, attrs, content, options) {
|
|
7669
|
+
var taco = { t: String(tag) };
|
|
7670
|
+
if (attrs !== null && attrs !== undefined) taco.a = attrs;
|
|
7671
|
+
if (content !== undefined) taco.c = content;
|
|
7672
|
+
if (options !== undefined) taco.o = options;
|
|
7673
|
+
return taco;
|
|
7674
|
+
};
|
|
7323
7675
|
|
|
7324
7676
|
/**
|
|
7325
7677
|
* Convert a TACO object (or array of TACOs) to an HTML string.
|
|
@@ -7359,7 +7711,7 @@ bw.html = function(taco, options = {}) {
|
|
|
7359
7711
|
}
|
|
7360
7712
|
|
|
7361
7713
|
// Handle arrays of TACOs
|
|
7362
|
-
if (
|
|
7714
|
+
if (_isA(taco)) {
|
|
7363
7715
|
return taco.map(t => bw.html(t, options)).join('');
|
|
7364
7716
|
}
|
|
7365
7717
|
|
|
@@ -7382,15 +7734,15 @@ bw.html = function(taco, options = {}) {
|
|
|
7382
7734
|
if (taco && taco._bwEach && options.state) {
|
|
7383
7735
|
var eachExpr = taco.expr.replace(/^\$\{|\}$/g, '');
|
|
7384
7736
|
var arr = bw._evaluatePath(options.state, eachExpr);
|
|
7385
|
-
if (!
|
|
7737
|
+
if (!_isA(arr)) return '';
|
|
7386
7738
|
return arr.map(function(item, idx) { return bw.html(taco.factory(item, idx), options); }).join('');
|
|
7387
7739
|
}
|
|
7388
7740
|
|
|
7389
7741
|
// Handle primitives and non-TACO objects
|
|
7390
|
-
if (
|
|
7742
|
+
if (!_is(taco, 'object') || !taco.t) {
|
|
7391
7743
|
var str = options.raw ? String(taco) : bw.escapeHTML(String(taco));
|
|
7392
7744
|
// Resolve template bindings if state provided
|
|
7393
|
-
if (options.state &&
|
|
7745
|
+
if (options.state && _is(str, 'string') && str.indexOf('${') >= 0) {
|
|
7394
7746
|
str = bw._resolveTemplate(str, options.state, !!options.compile);
|
|
7395
7747
|
}
|
|
7396
7748
|
return str;
|
|
@@ -7410,10 +7762,18 @@ bw.html = function(taco, options = {}) {
|
|
|
7410
7762
|
// Skip null, undefined, false
|
|
7411
7763
|
if (value == null || value === false) continue;
|
|
7412
7764
|
|
|
7413
|
-
//
|
|
7414
|
-
if (key.startsWith('on'))
|
|
7765
|
+
// Serialize event handlers via funcRegister
|
|
7766
|
+
if (key.startsWith('on')) {
|
|
7767
|
+
if (_is(value, 'function')) {
|
|
7768
|
+
var fnId = bw.funcRegister(value);
|
|
7769
|
+
attrStr += ' ' + key + '="' + bw.funcGetDispatchStr(fnId, 'event') + '"';
|
|
7770
|
+
} else if (_is(value, 'string')) {
|
|
7771
|
+
attrStr += ' ' + key + '="' + bw.escapeHTML(value) + '"';
|
|
7772
|
+
}
|
|
7773
|
+
continue;
|
|
7774
|
+
}
|
|
7415
7775
|
|
|
7416
|
-
if (key === 'style' &&
|
|
7776
|
+
if (key === 'style' && _is(value, 'object')) {
|
|
7417
7777
|
// Convert style object to string
|
|
7418
7778
|
const styleStr = Object.entries(value)
|
|
7419
7779
|
.filter(([, v]) => v != null)
|
|
@@ -7424,7 +7784,7 @@ bw.html = function(taco, options = {}) {
|
|
|
7424
7784
|
}
|
|
7425
7785
|
} else if (key === 'class') {
|
|
7426
7786
|
// Handle class as array or string
|
|
7427
|
-
const classStr =
|
|
7787
|
+
const classStr = _isA(value) ? value.filter(Boolean).join(' ') : String(value);
|
|
7428
7788
|
if (classStr) {
|
|
7429
7789
|
attrStr += ` class="${bw.escapeHTML(classStr)}"`;
|
|
7430
7790
|
}
|
|
@@ -7460,13 +7820,184 @@ bw.html = function(taco, options = {}) {
|
|
|
7460
7820
|
// Process content recursively
|
|
7461
7821
|
let contentStr = content != null ? bw.html(content, options) : '';
|
|
7462
7822
|
// Resolve template bindings in content if state provided
|
|
7463
|
-
if (options.state &&
|
|
7823
|
+
if (options.state && _is(contentStr, 'string') && contentStr.indexOf('${') >= 0) {
|
|
7464
7824
|
contentStr = bw._resolveTemplate(contentStr, options.state, !!options.compile);
|
|
7465
7825
|
}
|
|
7466
7826
|
|
|
7467
7827
|
return `<${tag}${attrStr}>${contentStr}</${tag}>`;
|
|
7468
7828
|
};
|
|
7469
7829
|
|
|
7830
|
+
/**
|
|
7831
|
+
* Generate a complete, self-contained HTML document from TACO content.
|
|
7832
|
+
*
|
|
7833
|
+
* Produces a full `<!DOCTYPE html>` page with configurable runtime injection,
|
|
7834
|
+
* func registry emission (so serialized event handlers work), optional theme,
|
|
7835
|
+
* and extra head elements. Designed for static site generation, offline/airgapped
|
|
7836
|
+
* use, and the "static site that isn't static" workflow.
|
|
7837
|
+
*
|
|
7838
|
+
* @param {Object} [opts={}] - Page options
|
|
7839
|
+
* @param {Object|string|Array} [opts.body=''] - Body content: TACO, string, or array
|
|
7840
|
+
* @param {string} [opts.title='bitwrench'] - Page title
|
|
7841
|
+
* @param {Object} [opts.state] - State for ${expr} resolution in bw.html()
|
|
7842
|
+
* @param {string} [opts.runtime='shim'] - Runtime level: 'inline'|'cdn'|'shim'|'none'
|
|
7843
|
+
* @param {string} [opts.css=''] - Additional CSS for <style> block
|
|
7844
|
+
* @param {string|Object} [opts.theme=null] - Theme preset name or config object
|
|
7845
|
+
* @param {Array} [opts.head=[]] - Extra TACO elements rendered into <head>
|
|
7846
|
+
* @param {string} [opts.favicon=''] - Favicon URL
|
|
7847
|
+
* @param {string} [opts.lang='en'] - HTML lang attribute
|
|
7848
|
+
* @returns {string} Complete HTML document string
|
|
7849
|
+
* @category DOM Generation
|
|
7850
|
+
* @see bw.html
|
|
7851
|
+
* @example
|
|
7852
|
+
* bw.htmlPage({
|
|
7853
|
+
* title: 'My App',
|
|
7854
|
+
* body: { t: 'h1', c: 'Hello World' },
|
|
7855
|
+
* runtime: 'shim'
|
|
7856
|
+
* })
|
|
7857
|
+
*/
|
|
7858
|
+
bw.htmlPage = function(opts) {
|
|
7859
|
+
opts = opts || {};
|
|
7860
|
+
var title = opts.title || 'bitwrench';
|
|
7861
|
+
var body = opts.body || '';
|
|
7862
|
+
var state = opts.state || undefined;
|
|
7863
|
+
var runtime = opts.runtime || 'shim';
|
|
7864
|
+
var css = opts.css || '';
|
|
7865
|
+
var theme = opts.theme || null;
|
|
7866
|
+
var headExtra = opts.head || [];
|
|
7867
|
+
var favicon = opts.favicon || '';
|
|
7868
|
+
var lang = opts.lang || 'en';
|
|
7869
|
+
|
|
7870
|
+
// Snapshot funcRegistry counter before rendering
|
|
7871
|
+
var fnCounterBefore = bw._fnIDCounter;
|
|
7872
|
+
|
|
7873
|
+
// Render body content
|
|
7874
|
+
var bodyHTML = '';
|
|
7875
|
+
if (_is(body, 'string')) {
|
|
7876
|
+
bodyHTML = body;
|
|
7877
|
+
} else {
|
|
7878
|
+
var htmlOpts = {};
|
|
7879
|
+
if (state) htmlOpts.state = state;
|
|
7880
|
+
bodyHTML = bw.html(body, htmlOpts);
|
|
7881
|
+
}
|
|
7882
|
+
|
|
7883
|
+
// Collect functions registered during this render
|
|
7884
|
+
var fnCounterAfter = bw._fnIDCounter;
|
|
7885
|
+
var registryEntries = '';
|
|
7886
|
+
for (var i = fnCounterBefore; i < fnCounterAfter; i++) {
|
|
7887
|
+
var fnKey = 'bw_fn_' + i;
|
|
7888
|
+
if (bw._fnRegistry[fnKey]) {
|
|
7889
|
+
registryEntries += 'bw._fnRegistry[\'' + fnKey + '\']=' +
|
|
7890
|
+
bw._fnRegistry[fnKey].toString() + ';\n';
|
|
7891
|
+
}
|
|
7892
|
+
}
|
|
7893
|
+
|
|
7894
|
+
// Build runtime script for <head>
|
|
7895
|
+
var runtimeHead = '';
|
|
7896
|
+
if (runtime === 'inline') {
|
|
7897
|
+
// Read UMD bundle synchronously if in Node.js
|
|
7898
|
+
var umdSource = null;
|
|
7899
|
+
if (bw._isNode) {
|
|
7900
|
+
try {
|
|
7901
|
+
var fs = (typeof require === 'function') ? require('fs') : null;
|
|
7902
|
+
var pathMod = (typeof require === 'function') ? require('path') : null;
|
|
7903
|
+
if (fs && pathMod) {
|
|
7904
|
+
// Resolve dist/ relative to this source file
|
|
7905
|
+
var srcDir = '';
|
|
7906
|
+
try { srcDir = pathMod.dirname((typeof __filename !== 'undefined') ? __filename : ''); }
|
|
7907
|
+
catch(e2) { /* ESM: __filename not available */ }
|
|
7908
|
+
if (!srcDir && typeof import.meta !== 'undefined' && import.meta.url) {
|
|
7909
|
+
var url = (typeof require === 'function') ? require('url') : null;
|
|
7910
|
+
if (url && url.fileURLToPath) srcDir = pathMod.dirname(url.fileURLToPath(import.meta.url));
|
|
7911
|
+
}
|
|
7912
|
+
if (srcDir) {
|
|
7913
|
+
var distPath = pathMod.resolve(srcDir, '../dist/bitwrench.umd.min.js');
|
|
7914
|
+
umdSource = fs.readFileSync(distPath, 'utf8');
|
|
7915
|
+
}
|
|
7916
|
+
}
|
|
7917
|
+
} catch(e) { /* fall through */ }
|
|
7918
|
+
}
|
|
7919
|
+
if (umdSource) {
|
|
7920
|
+
runtimeHead = '<script>' + umdSource + '</script>';
|
|
7921
|
+
} else {
|
|
7922
|
+
// Fallback to shim in browser or if dist not available
|
|
7923
|
+
runtimeHead = '<script>' + bw._FUNC_REGISTRY_SHIM + '</script>';
|
|
7924
|
+
}
|
|
7925
|
+
} else if (runtime === 'cdn') {
|
|
7926
|
+
runtimeHead = '<script src="https://cdn.jsdelivr.net/npm/bitwrench@2/dist/bitwrench.umd.min.js"></script>';
|
|
7927
|
+
} else if (runtime === 'shim') {
|
|
7928
|
+
runtimeHead = '<script>' + bw._FUNC_REGISTRY_SHIM + '</script>';
|
|
7929
|
+
}
|
|
7930
|
+
// runtime === 'none' → empty
|
|
7931
|
+
|
|
7932
|
+
// Theme CSS
|
|
7933
|
+
var themeCSS = '';
|
|
7934
|
+
if (theme) {
|
|
7935
|
+
var themeConfig = _is(theme, 'string')
|
|
7936
|
+
? (THEME_PRESETS[theme.toLowerCase()] || null)
|
|
7937
|
+
: theme;
|
|
7938
|
+
if (themeConfig) {
|
|
7939
|
+
var themeResult = bw.makeStyles(themeConfig);
|
|
7940
|
+
themeCSS = themeResult.css;
|
|
7941
|
+
}
|
|
7942
|
+
}
|
|
7943
|
+
|
|
7944
|
+
// Extra <head> elements
|
|
7945
|
+
var headHTML = '';
|
|
7946
|
+
if (_isA(headExtra) && headExtra.length > 0) {
|
|
7947
|
+
headHTML = headExtra.map(function(el) { return bw.html(el); }).join('\n');
|
|
7948
|
+
}
|
|
7949
|
+
|
|
7950
|
+
// Favicon
|
|
7951
|
+
var faviconTag = '';
|
|
7952
|
+
if (favicon) {
|
|
7953
|
+
var safeFavicon = favicon.replace(/[&<>"']/g, function(c) {
|
|
7954
|
+
return ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c];
|
|
7955
|
+
});
|
|
7956
|
+
faviconTag = '<link rel="icon" href="' + safeFavicon + '">';
|
|
7957
|
+
}
|
|
7958
|
+
|
|
7959
|
+
// Escaped title
|
|
7960
|
+
var safeTitle = bw.escapeHTML(title);
|
|
7961
|
+
|
|
7962
|
+
// Combine all CSS
|
|
7963
|
+
var allCSS = (themeCSS ? themeCSS + '\n' : '') + css;
|
|
7964
|
+
|
|
7965
|
+
// Body-end script: registry entries + optional loadStyles
|
|
7966
|
+
var bodyEndScript = '';
|
|
7967
|
+
var bodyEndParts = [];
|
|
7968
|
+
if (registryEntries) {
|
|
7969
|
+
bodyEndParts.push(registryEntries);
|
|
7970
|
+
}
|
|
7971
|
+
if (runtime === 'inline' || runtime === 'cdn') {
|
|
7972
|
+
bodyEndParts.push('if(typeof bw!=="undefined"){bw.loadStyles();}');
|
|
7973
|
+
}
|
|
7974
|
+
if (bodyEndParts.length > 0) {
|
|
7975
|
+
bodyEndScript = '<script>\n' + bodyEndParts.join('\n') + '\n</script>';
|
|
7976
|
+
}
|
|
7977
|
+
|
|
7978
|
+
// Assemble document
|
|
7979
|
+
var parts = [
|
|
7980
|
+
'<!DOCTYPE html>',
|
|
7981
|
+
'<html lang="' + lang + '">',
|
|
7982
|
+
'<head>',
|
|
7983
|
+
'<meta charset="UTF-8">',
|
|
7984
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">'
|
|
7985
|
+
];
|
|
7986
|
+
parts.push('<title>' + safeTitle + '</title>');
|
|
7987
|
+
if (faviconTag) parts.push(faviconTag);
|
|
7988
|
+
if (runtimeHead) parts.push(runtimeHead);
|
|
7989
|
+
if (headHTML) parts.push(headHTML);
|
|
7990
|
+
if (allCSS) parts.push('<style>' + allCSS + '</style>');
|
|
7991
|
+
parts.push('</head>');
|
|
7992
|
+
parts.push('<body>');
|
|
7993
|
+
parts.push(bodyHTML);
|
|
7994
|
+
if (bodyEndScript) parts.push(bodyEndScript);
|
|
7995
|
+
parts.push('</body>');
|
|
7996
|
+
parts.push('</html>');
|
|
7997
|
+
|
|
7998
|
+
return parts.join('\n');
|
|
7999
|
+
};
|
|
8000
|
+
|
|
7470
8001
|
/**
|
|
7471
8002
|
* Create a live DOM element from a TACO object (browser only).
|
|
7472
8003
|
*
|
|
@@ -7511,7 +8042,7 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
7511
8042
|
}
|
|
7512
8043
|
|
|
7513
8044
|
// Handle text nodes
|
|
7514
|
-
if (
|
|
8045
|
+
if (!_is(taco, 'object') || !taco.t) {
|
|
7515
8046
|
return document.createTextNode(String(taco));
|
|
7516
8047
|
}
|
|
7517
8048
|
|
|
@@ -7524,16 +8055,16 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
7524
8055
|
for (const [key, value] of Object.entries(attrs)) {
|
|
7525
8056
|
if (value == null || value === false) continue;
|
|
7526
8057
|
|
|
7527
|
-
if (key === 'style' &&
|
|
8058
|
+
if (key === 'style' && _is(value, 'object')) {
|
|
7528
8059
|
// Apply styles directly
|
|
7529
8060
|
Object.assign(el.style, value);
|
|
7530
8061
|
} else if (key === 'class') {
|
|
7531
8062
|
// Handle class as array or string
|
|
7532
|
-
const classStr =
|
|
8063
|
+
const classStr = _isA(value) ? value.filter(Boolean).join(' ') : String(value);
|
|
7533
8064
|
if (classStr) {
|
|
7534
8065
|
el.className = classStr;
|
|
7535
8066
|
}
|
|
7536
|
-
} else if (key.startsWith('on') &&
|
|
8067
|
+
} else if (key.startsWith('on') && _is(value, 'function')) {
|
|
7537
8068
|
// Event handlers
|
|
7538
8069
|
const eventName = key.slice(2).toLowerCase();
|
|
7539
8070
|
el.addEventListener(eventName, value);
|
|
@@ -7553,7 +8084,7 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
7553
8084
|
// Children with data-bw_id or id attributes get local refs on the parent,
|
|
7554
8085
|
// so o.render functions can access them without any DOM lookup.
|
|
7555
8086
|
if (content != null) {
|
|
7556
|
-
if (
|
|
8087
|
+
if (_isA(content)) {
|
|
7557
8088
|
content.forEach(child => {
|
|
7558
8089
|
if (child != null) {
|
|
7559
8090
|
// Handle ComponentHandle in content arrays (Level 2 children)
|
|
@@ -7573,20 +8104,20 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
7573
8104
|
if (childEl._bw_refs) {
|
|
7574
8105
|
if (!el._bw_refs) el._bw_refs = {};
|
|
7575
8106
|
for (var rk in childEl._bw_refs) {
|
|
7576
|
-
if (
|
|
8107
|
+
if (_hop.call(childEl._bw_refs, rk)) {
|
|
7577
8108
|
el._bw_refs[rk] = childEl._bw_refs[rk];
|
|
7578
8109
|
}
|
|
7579
8110
|
}
|
|
7580
8111
|
}
|
|
7581
8112
|
}
|
|
7582
8113
|
});
|
|
7583
|
-
} else if (
|
|
8114
|
+
} else if (_is(content, 'object') && content.__bw_raw) {
|
|
7584
8115
|
// Raw HTML content — inject via innerHTML
|
|
7585
8116
|
el.innerHTML = content.v;
|
|
7586
8117
|
} else if (content._bwComponent === true) {
|
|
7587
8118
|
// Single ComponentHandle as content
|
|
7588
8119
|
content.mount(el);
|
|
7589
|
-
} else if (
|
|
8120
|
+
} else if (_is(content, 'object') && content.t) {
|
|
7590
8121
|
var childEl = bw.createDOM(content, options);
|
|
7591
8122
|
el.appendChild(childEl);
|
|
7592
8123
|
var childBwId = content.a ? (content.a['data-bw_id'] || content.a.id) : null;
|
|
@@ -7597,7 +8128,7 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
7597
8128
|
if (childEl._bw_refs) {
|
|
7598
8129
|
if (!el._bw_refs) el._bw_refs = {};
|
|
7599
8130
|
for (var rk in childEl._bw_refs) {
|
|
7600
|
-
if (
|
|
8131
|
+
if (_hop.call(childEl._bw_refs, rk)) {
|
|
7601
8132
|
el._bw_refs[rk] = childEl._bw_refs[rk];
|
|
7602
8133
|
}
|
|
7603
8134
|
}
|
|
@@ -7612,6 +8143,14 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
7612
8143
|
bw._registerNode(el, null);
|
|
7613
8144
|
}
|
|
7614
8145
|
|
|
8146
|
+
// Register UUID class in node cache (bw_uuid_* tokens in class string)
|
|
8147
|
+
if (el.className) {
|
|
8148
|
+
var uuidMatch = el.className.match(_UUID_RE);
|
|
8149
|
+
if (uuidMatch) {
|
|
8150
|
+
bw._nodeMap[uuidMatch[0]] = el;
|
|
8151
|
+
}
|
|
8152
|
+
}
|
|
8153
|
+
|
|
7615
8154
|
// Handle lifecycle hooks and state
|
|
7616
8155
|
if (opts.mounted || opts.unmount || opts.render || opts.state) {
|
|
7617
8156
|
const id = attrs['data-bw_id'] || bw.uuid();
|
|
@@ -7630,7 +8169,7 @@ bw.createDOM = function(taco, options = {}) {
|
|
|
7630
8169
|
el._bw_render = opts.render;
|
|
7631
8170
|
|
|
7632
8171
|
if (opts.mounted) {
|
|
7633
|
-
|
|
8172
|
+
_cw('bw.createDOM: o.render and o.mounted are mutually exclusive. o.render wins.');
|
|
7634
8173
|
}
|
|
7635
8174
|
|
|
7636
8175
|
// Queue initial render (same timing as mounted)
|
|
@@ -7703,7 +8242,7 @@ bw.DOM = function(target, taco, options = {}) {
|
|
|
7703
8242
|
const targetEl = bw._el(target);
|
|
7704
8243
|
|
|
7705
8244
|
if (!targetEl) {
|
|
7706
|
-
|
|
8245
|
+
_ce('bw.DOM: Target element not found:', target);
|
|
7707
8246
|
return null;
|
|
7708
8247
|
}
|
|
7709
8248
|
|
|
@@ -7743,7 +8282,7 @@ bw.DOM = function(target, taco, options = {}) {
|
|
|
7743
8282
|
targetEl.appendChild(taco.element);
|
|
7744
8283
|
}
|
|
7745
8284
|
// Handle arrays
|
|
7746
|
-
else if (
|
|
8285
|
+
else if (_isA(taco)) {
|
|
7747
8286
|
taco.forEach(t => {
|
|
7748
8287
|
if (t != null) {
|
|
7749
8288
|
if (t._bwComponent === true) {
|
|
@@ -7779,7 +8318,7 @@ bw.DOM = function(target, taco, options = {}) {
|
|
|
7779
8318
|
bw.compileProps = function(handle, props = {}) {
|
|
7780
8319
|
const compiledProps = {};
|
|
7781
8320
|
|
|
7782
|
-
|
|
8321
|
+
_keys(props).forEach(key => {
|
|
7783
8322
|
// Create getter/setter for each prop
|
|
7784
8323
|
Object.defineProperty(compiledProps, key, {
|
|
7785
8324
|
get() {
|
|
@@ -7984,6 +8523,16 @@ bw.renderComponent = function(taco, options = {}) {
|
|
|
7984
8523
|
bw.cleanup = function(element) {
|
|
7985
8524
|
if (!bw._isBrowser || !element) return;
|
|
7986
8525
|
|
|
8526
|
+
// Deregister UUID classes from node cache (element + descendants)
|
|
8527
|
+
// Covers elements that have UUID but no data-bw_id
|
|
8528
|
+
var selfUuidMatch = element.className && element.className.match(_UUID_RE);
|
|
8529
|
+
if (selfUuidMatch) delete bw._nodeMap[selfUuidMatch[0]];
|
|
8530
|
+
var uuidEls = element.querySelectorAll('[class*="bw_uuid_"]');
|
|
8531
|
+
uuidEls.forEach(function(uel) {
|
|
8532
|
+
var m = uel.className && uel.className.match(_UUID_RE);
|
|
8533
|
+
if (m) delete bw._nodeMap[m[0]];
|
|
8534
|
+
});
|
|
8535
|
+
|
|
7987
8536
|
// Find all elements with data-bw_id
|
|
7988
8537
|
const elements = element.querySelectorAll('[data-bw_id]');
|
|
7989
8538
|
|
|
@@ -7999,6 +8548,10 @@ bw.cleanup = function(element) {
|
|
|
7999
8548
|
// Deregister from node cache
|
|
8000
8549
|
bw._deregisterNode(el, id);
|
|
8001
8550
|
|
|
8551
|
+
// Deregister UUID class from node cache
|
|
8552
|
+
var uuidMatch = el.className && el.className.match(_UUID_RE);
|
|
8553
|
+
if (uuidMatch) delete bw._nodeMap[uuidMatch[0]];
|
|
8554
|
+
|
|
8002
8555
|
// Clean up pub/sub subscriptions tied to this element
|
|
8003
8556
|
if (el._bw_subs) {
|
|
8004
8557
|
el._bw_subs.forEach(function(unsub) { unsub(); });
|
|
@@ -8023,6 +8576,10 @@ bw.cleanup = function(element) {
|
|
|
8023
8576
|
// Deregister from node cache
|
|
8024
8577
|
bw._deregisterNode(element, id);
|
|
8025
8578
|
|
|
8579
|
+
// Deregister UUID class from node cache
|
|
8580
|
+
var elemUuidMatch = element.className && element.className.match(_UUID_RE);
|
|
8581
|
+
if (elemUuidMatch) delete bw._nodeMap[elemUuidMatch[0]];
|
|
8582
|
+
|
|
8026
8583
|
// Clean up pub/sub subscriptions tied to element itself
|
|
8027
8584
|
if (element._bw_subs) {
|
|
8028
8585
|
element._bw_subs.forEach(function(unsub) { unsub(); });
|
|
@@ -8097,17 +8654,17 @@ bw.patch = function(id, content, attr) {
|
|
|
8097
8654
|
if (attr) {
|
|
8098
8655
|
// Patch an attribute
|
|
8099
8656
|
el.setAttribute(attr, String(content));
|
|
8100
|
-
} else if (
|
|
8657
|
+
} else if (_isA(content)) {
|
|
8101
8658
|
// Patch with array of children (strings and/or TACOs)
|
|
8102
8659
|
el.innerHTML = '';
|
|
8103
8660
|
content.forEach(function(item) {
|
|
8104
|
-
if (
|
|
8661
|
+
if (_is(item, 'string') || _is(item, 'number')) {
|
|
8105
8662
|
el.appendChild(document.createTextNode(String(item)));
|
|
8106
8663
|
} else if (item && item.t) {
|
|
8107
8664
|
el.appendChild(bw.createDOM(item));
|
|
8108
8665
|
}
|
|
8109
8666
|
});
|
|
8110
|
-
} else if (
|
|
8667
|
+
} else if (_is(content, 'object') && content.t) {
|
|
8111
8668
|
// Patch with a TACO — replace children
|
|
8112
8669
|
el.innerHTML = '';
|
|
8113
8670
|
el.appendChild(bw.createDOM(content));
|
|
@@ -8138,7 +8695,7 @@ bw.patch = function(id, content, attr) {
|
|
|
8138
8695
|
bw.patchAll = function(patches) {
|
|
8139
8696
|
var results = {};
|
|
8140
8697
|
for (var id in patches) {
|
|
8141
|
-
if (
|
|
8698
|
+
if (_hop.call(patches, id)) {
|
|
8142
8699
|
results[id] = bw.patch(id, patches[id]);
|
|
8143
8700
|
}
|
|
8144
8701
|
}
|
|
@@ -8235,7 +8792,7 @@ bw.pub = function(topic, detail) {
|
|
|
8235
8792
|
snapshot[i].handler(detail);
|
|
8236
8793
|
called++;
|
|
8237
8794
|
} catch (err) {
|
|
8238
|
-
|
|
8795
|
+
_cw('bw.pub: subscriber error on topic "' + topic + '":', err);
|
|
8239
8796
|
}
|
|
8240
8797
|
}
|
|
8241
8798
|
return called;
|
|
@@ -8331,8 +8888,8 @@ bw._fnIDCounter = 0;
|
|
|
8331
8888
|
* @see bw.funcGetDispatchStr
|
|
8332
8889
|
*/
|
|
8333
8890
|
bw.funcRegister = function(fn, name) {
|
|
8334
|
-
if (
|
|
8335
|
-
var fnID = (
|
|
8891
|
+
if (!_is(fn, 'function')) return '';
|
|
8892
|
+
var fnID = (_is(name, 'string') && name.length > 0) ? name : ('bw_fn_' + bw._fnIDCounter++);
|
|
8336
8893
|
bw._fnRegistry[fnID] = fn;
|
|
8337
8894
|
return fnID;
|
|
8338
8895
|
};
|
|
@@ -8351,7 +8908,7 @@ bw.funcRegister = function(fn, name) {
|
|
|
8351
8908
|
bw.funcGetById = function(name, errFn) {
|
|
8352
8909
|
name = String(name);
|
|
8353
8910
|
if (name in bw._fnRegistry) return bw._fnRegistry[name];
|
|
8354
|
-
return (
|
|
8911
|
+
return _is(errFn, 'function') ? errFn : function() { _cw('bw.funcGetById: unregistered fn "' + name + '"'); };
|
|
8355
8912
|
};
|
|
8356
8913
|
|
|
8357
8914
|
/**
|
|
@@ -8392,13 +8949,30 @@ bw.funcUnregister = function(name) {
|
|
|
8392
8949
|
bw.funcGetRegistry = function() {
|
|
8393
8950
|
var copy = {};
|
|
8394
8951
|
for (var k in bw._fnRegistry) {
|
|
8395
|
-
if (
|
|
8952
|
+
if (_hop.call(bw._fnRegistry, k)) {
|
|
8396
8953
|
copy[k] = bw._fnRegistry[k];
|
|
8397
8954
|
}
|
|
8398
8955
|
}
|
|
8399
8956
|
return copy;
|
|
8400
8957
|
};
|
|
8401
8958
|
|
|
8959
|
+
/**
|
|
8960
|
+
* Minimal runtime shim for funcRegister dispatch in static HTML.
|
|
8961
|
+
* When embedded in a `<script>` tag, provides just enough infrastructure
|
|
8962
|
+
* for `bw.funcGetById()` calls to resolve. The actual function bodies
|
|
8963
|
+
* are emitted separately as `bw._fnRegistry['bw_fn_X'] = ...;` assignments.
|
|
8964
|
+
* @type {string}
|
|
8965
|
+
* @category Function Registry
|
|
8966
|
+
*/
|
|
8967
|
+
bw._FUNC_REGISTRY_SHIM = '(function(){var bw=window.bw||(window.bw={});' +
|
|
8968
|
+
'if(!bw._fnRegistry)bw._fnRegistry={};' +
|
|
8969
|
+
'bw.funcGetById=function(n){return bw._fnRegistry[n]||function(){' +
|
|
8970
|
+
'console.warn("bw: unregistered fn "+n)};};' +
|
|
8971
|
+
'bw.funcRegister=function(fn,name){' +
|
|
8972
|
+
'var id=name||("bw_fn_"+(bw._fnIDCounter=(bw._fnIDCounter||0)+1));' +
|
|
8973
|
+
'bw._fnRegistry[id]=fn;return id;};' +
|
|
8974
|
+
'window.bw=bw;})();';
|
|
8975
|
+
|
|
8402
8976
|
// ===================================================================================
|
|
8403
8977
|
// Template Binding Utilities
|
|
8404
8978
|
// ===================================================================================
|
|
@@ -8426,7 +9000,10 @@ bw._evaluatePath = function(state, path) {
|
|
|
8426
9000
|
var parts = path.split('.');
|
|
8427
9001
|
var val = state;
|
|
8428
9002
|
for (var i = 0; i < parts.length; i++) {
|
|
8429
|
-
if (val == null)
|
|
9003
|
+
if (val == null) {
|
|
9004
|
+
if (bw.debug) _cw('bw.debug: _evaluatePath — null at key "' + parts[i] + '" in path "' + path + '"');
|
|
9005
|
+
return '';
|
|
9006
|
+
}
|
|
8430
9007
|
val = val[parts[i]];
|
|
8431
9008
|
}
|
|
8432
9009
|
return (val == null) ? '' : val;
|
|
@@ -8446,7 +9023,7 @@ bw._evaluatePath = function(state, path) {
|
|
|
8446
9023
|
*/
|
|
8447
9024
|
bw._compiledExprs = {};
|
|
8448
9025
|
bw._resolveTemplate = function(str, state, compile) {
|
|
8449
|
-
if (
|
|
9026
|
+
if (!_is(str, 'string') || str.indexOf('${') < 0) return str;
|
|
8450
9027
|
var bindings = bw._parseBindings(str);
|
|
8451
9028
|
if (bindings.length === 0) return str;
|
|
8452
9029
|
|
|
@@ -8468,6 +9045,7 @@ bw._resolveTemplate = function(str, state, compile) {
|
|
|
8468
9045
|
try {
|
|
8469
9046
|
val = bw._compiledExprs[b.expr](state);
|
|
8470
9047
|
} catch (e) {
|
|
9048
|
+
if (bw.debug) _cw('bw.debug: _resolveTemplate — Tier 2 eval failed for "${' + b.expr + '}":', e.message);
|
|
8471
9049
|
val = '';
|
|
8472
9050
|
}
|
|
8473
9051
|
} else {
|
|
@@ -8576,7 +9154,7 @@ function ComponentHandle(taco) {
|
|
|
8576
9154
|
this._state = {};
|
|
8577
9155
|
if (o.state) {
|
|
8578
9156
|
for (var k in o.state) {
|
|
8579
|
-
if (
|
|
9157
|
+
if (_hop.call(o.state, k)) {
|
|
8580
9158
|
this._state[k] = o.state[k];
|
|
8581
9159
|
}
|
|
8582
9160
|
}
|
|
@@ -8585,7 +9163,7 @@ function ComponentHandle(taco) {
|
|
|
8585
9163
|
this._actions = {};
|
|
8586
9164
|
if (o.actions) {
|
|
8587
9165
|
for (var k2 in o.actions) {
|
|
8588
|
-
if (
|
|
9166
|
+
if (_hop.call(o.actions, k2)) {
|
|
8589
9167
|
this._actions[k2] = o.actions[k2];
|
|
8590
9168
|
}
|
|
8591
9169
|
}
|
|
@@ -8595,7 +9173,7 @@ function ComponentHandle(taco) {
|
|
|
8595
9173
|
if (o.methods) {
|
|
8596
9174
|
var self = this;
|
|
8597
9175
|
for (var k3 in o.methods) {
|
|
8598
|
-
if (
|
|
9176
|
+
if (_hop.call(o.methods, k3)) {
|
|
8599
9177
|
this._methods[k3] = o.methods[k3];
|
|
8600
9178
|
(function(methodName, methodFn) {
|
|
8601
9179
|
self[methodName] = function() {
|
|
@@ -8613,7 +9191,7 @@ function ComponentHandle(taco) {
|
|
|
8613
9191
|
willMount: o.willMount || null,
|
|
8614
9192
|
mounted: o.mounted || null,
|
|
8615
9193
|
willUpdate: o.willUpdate || null,
|
|
8616
|
-
onUpdate: o.onUpdate || null,
|
|
9194
|
+
onUpdate: o.onUpdate || o.updated || null,
|
|
8617
9195
|
unmount: o.unmount || null,
|
|
8618
9196
|
willDestroy: o.willDestroy || null
|
|
8619
9197
|
};
|
|
@@ -8628,14 +9206,23 @@ function ComponentHandle(taco) {
|
|
|
8628
9206
|
this._compile = !!o.compile;
|
|
8629
9207
|
this._bw_refs = {};
|
|
8630
9208
|
this._refCounter = 0;
|
|
9209
|
+
// Child component ownership (Bug #5)
|
|
9210
|
+
this._children = [];
|
|
9211
|
+
this._parent = null;
|
|
9212
|
+
// Factory metadata for BCCL rebuild (Bug #6)
|
|
9213
|
+
this._factory = taco._bwFactory || null;
|
|
8631
9214
|
}
|
|
8632
9215
|
|
|
9216
|
+
// Short alias for ComponentHandle.prototype (see alias block at top of file).
|
|
9217
|
+
// 28 method definitions × 25 chars = ~700B raw savings in minified output.
|
|
9218
|
+
var _chp = ComponentHandle.prototype;
|
|
9219
|
+
|
|
8633
9220
|
// ── State Methods ──
|
|
8634
9221
|
|
|
8635
9222
|
/**
|
|
8636
9223
|
* Get a state value. Dot-path supported: `get('user.name')`
|
|
8637
9224
|
*/
|
|
8638
|
-
|
|
9225
|
+
_chp.get = function(key) {
|
|
8639
9226
|
return bw._evaluatePath(this._state, key);
|
|
8640
9227
|
};
|
|
8641
9228
|
|
|
@@ -8645,12 +9232,13 @@ ComponentHandle.prototype.get = function(key) {
|
|
|
8645
9232
|
* @param {*} value - New value
|
|
8646
9233
|
* @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
|
|
8647
9234
|
*/
|
|
8648
|
-
|
|
9235
|
+
_chp.set = function(key, value, opts) {
|
|
8649
9236
|
// Dot-path set
|
|
8650
9237
|
var parts = key.split('.');
|
|
8651
9238
|
var obj = this._state;
|
|
8652
9239
|
for (var i = 0; i < parts.length - 1; i++) {
|
|
8653
|
-
if (obj[parts[i]]
|
|
9240
|
+
if (!_is(obj[parts[i]], 'object')) {
|
|
9241
|
+
if (bw.debug) _cw('bw.debug: set() — auto-creating intermediate "' + parts[i] + '" in path "' + key + '"');
|
|
8654
9242
|
obj[parts[i]] = {};
|
|
8655
9243
|
}
|
|
8656
9244
|
obj = obj[parts[i]];
|
|
@@ -8670,10 +9258,10 @@ ComponentHandle.prototype.set = function(key, value, opts) {
|
|
|
8670
9258
|
/**
|
|
8671
9259
|
* Get a shallow clone of the full state.
|
|
8672
9260
|
*/
|
|
8673
|
-
|
|
9261
|
+
_chp.getState = function() {
|
|
8674
9262
|
var clone = {};
|
|
8675
9263
|
for (var k in this._state) {
|
|
8676
|
-
if (
|
|
9264
|
+
if (_hop.call(this._state, k)) {
|
|
8677
9265
|
clone[k] = this._state[k];
|
|
8678
9266
|
}
|
|
8679
9267
|
}
|
|
@@ -8685,9 +9273,9 @@ ComponentHandle.prototype.getState = function() {
|
|
|
8685
9273
|
* @param {Object} updates - Key-value pairs to merge
|
|
8686
9274
|
* @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
|
|
8687
9275
|
*/
|
|
8688
|
-
|
|
9276
|
+
_chp.setState = function(updates, opts) {
|
|
8689
9277
|
for (var k in updates) {
|
|
8690
|
-
if (
|
|
9278
|
+
if (_hop.call(updates, k)) {
|
|
8691
9279
|
this._state[k] = updates[k];
|
|
8692
9280
|
this._dirtyKeys[k] = true;
|
|
8693
9281
|
}
|
|
@@ -8704,9 +9292,9 @@ ComponentHandle.prototype.setState = function(updates, opts) {
|
|
|
8704
9292
|
/**
|
|
8705
9293
|
* Push a value onto an array in state. Clones the array.
|
|
8706
9294
|
*/
|
|
8707
|
-
|
|
9295
|
+
_chp.push = function(key, val) {
|
|
8708
9296
|
var arr = this.get(key);
|
|
8709
|
-
var newArr =
|
|
9297
|
+
var newArr = _isA(arr) ? arr.slice() : [];
|
|
8710
9298
|
newArr.push(val);
|
|
8711
9299
|
this.set(key, newArr);
|
|
8712
9300
|
};
|
|
@@ -8714,9 +9302,9 @@ ComponentHandle.prototype.push = function(key, val) {
|
|
|
8714
9302
|
/**
|
|
8715
9303
|
* Splice an array in state. Clones the array.
|
|
8716
9304
|
*/
|
|
8717
|
-
|
|
9305
|
+
_chp.splice = function(key, start, deleteCount) {
|
|
8718
9306
|
var arr = this.get(key);
|
|
8719
|
-
var newArr =
|
|
9307
|
+
var newArr = _isA(arr) ? arr.slice() : [];
|
|
8720
9308
|
var args = [start, deleteCount].concat(Array.prototype.slice.call(arguments, 3));
|
|
8721
9309
|
Array.prototype.splice.apply(newArr, args);
|
|
8722
9310
|
this.set(key, newArr);
|
|
@@ -8724,7 +9312,7 @@ ComponentHandle.prototype.splice = function(key, start, deleteCount) {
|
|
|
8724
9312
|
|
|
8725
9313
|
// ── Scheduling ──
|
|
8726
9314
|
|
|
8727
|
-
|
|
9315
|
+
_chp._scheduleDirty = function() {
|
|
8728
9316
|
if (!this._scheduled) {
|
|
8729
9317
|
this._scheduled = true;
|
|
8730
9318
|
bw._dirtyComponents.push(this);
|
|
@@ -8739,17 +9327,17 @@ ComponentHandle.prototype._scheduleDirty = function() {
|
|
|
8739
9327
|
* Creates binding descriptors with refIds for targeted DOM updates.
|
|
8740
9328
|
* @private
|
|
8741
9329
|
*/
|
|
8742
|
-
|
|
9330
|
+
_chp._compileBindings = function() {
|
|
8743
9331
|
this._bindings = [];
|
|
8744
9332
|
this._refCounter = 0;
|
|
8745
|
-
var stateKeys =
|
|
9333
|
+
var stateKeys = _keys(this._state);
|
|
8746
9334
|
var self = this;
|
|
8747
9335
|
|
|
8748
9336
|
function walkTaco(taco, path) {
|
|
8749
|
-
if (taco
|
|
9337
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
8750
9338
|
|
|
8751
9339
|
// Check content for bindings
|
|
8752
|
-
if (
|
|
9340
|
+
if (_is(taco.c, 'string') && taco.c.indexOf('${') >= 0) {
|
|
8753
9341
|
var refId = 'bw_ref_' + self._refCounter++;
|
|
8754
9342
|
var parsed = bw._parseBindings(taco.c);
|
|
8755
9343
|
var deps = [];
|
|
@@ -8771,10 +9359,10 @@ ComponentHandle.prototype._compileBindings = function() {
|
|
|
8771
9359
|
// Check attributes for bindings
|
|
8772
9360
|
if (taco.a) {
|
|
8773
9361
|
for (var attrName in taco.a) {
|
|
8774
|
-
if (!
|
|
9362
|
+
if (!_hop.call(taco.a, attrName)) continue;
|
|
8775
9363
|
if (attrName === 'data-bw_ref') continue;
|
|
8776
9364
|
var attrVal = taco.a[attrName];
|
|
8777
|
-
if (
|
|
9365
|
+
if (_is(attrVal, 'string') && attrVal.indexOf('${') >= 0) {
|
|
8778
9366
|
var refId2 = 'bw_ref_' + self._refCounter++;
|
|
8779
9367
|
var parsed2 = bw._parseBindings(attrVal);
|
|
8780
9368
|
var deps2 = [];
|
|
@@ -8800,9 +9388,27 @@ ComponentHandle.prototype._compileBindings = function() {
|
|
|
8800
9388
|
}
|
|
8801
9389
|
|
|
8802
9390
|
// Recurse into children
|
|
8803
|
-
if (
|
|
9391
|
+
if (_isA(taco.c)) {
|
|
8804
9392
|
for (var i = 0; i < taco.c.length; i++) {
|
|
8805
|
-
|
|
9393
|
+
// Wrap string children with ${expr} in a span so patches target the span, not the parent
|
|
9394
|
+
if (_is(taco.c[i], 'string') && taco.c[i].indexOf('${') >= 0) {
|
|
9395
|
+
var mixedRefId = 'bw_ref_' + self._refCounter++;
|
|
9396
|
+
var mixedParsed = bw._parseBindings(taco.c[i]);
|
|
9397
|
+
var mixedDeps = [];
|
|
9398
|
+
for (var mi = 0; mi < mixedParsed.length; mi++) {
|
|
9399
|
+
mixedDeps = mixedDeps.concat(bw._extractDeps(mixedParsed[mi].expr, stateKeys));
|
|
9400
|
+
}
|
|
9401
|
+
self._bindings.push({
|
|
9402
|
+
expr: taco.c[i],
|
|
9403
|
+
type: 'content',
|
|
9404
|
+
refId: mixedRefId,
|
|
9405
|
+
deps: mixedDeps,
|
|
9406
|
+
template: taco.c[i]
|
|
9407
|
+
});
|
|
9408
|
+
// Replace string with a span wrapper so textContent targets the span only
|
|
9409
|
+
taco.c[i] = { t: 'span', a: { 'data-bw_ref': mixedRefId, style: 'display:contents' }, c: taco.c[i] };
|
|
9410
|
+
}
|
|
9411
|
+
if (_is(taco.c[i], 'object') && taco.c[i].t) {
|
|
8806
9412
|
walkTaco(taco.c[i], path.concat(i));
|
|
8807
9413
|
}
|
|
8808
9414
|
// Handle bw.when/bw.each markers
|
|
@@ -8837,7 +9443,7 @@ ComponentHandle.prototype._compileBindings = function() {
|
|
|
8837
9443
|
taco.c[i]._refId = eachRefId;
|
|
8838
9444
|
}
|
|
8839
9445
|
}
|
|
8840
|
-
} else if (taco.c
|
|
9446
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
8841
9447
|
walkTaco(taco.c, path.concat(0));
|
|
8842
9448
|
}
|
|
8843
9449
|
|
|
@@ -8853,7 +9459,7 @@ ComponentHandle.prototype._compileBindings = function() {
|
|
|
8853
9459
|
* Build ref map from the live DOM after createDOM.
|
|
8854
9460
|
* @private
|
|
8855
9461
|
*/
|
|
8856
|
-
|
|
9462
|
+
_chp._collectRefs = function() {
|
|
8857
9463
|
this._bw_refs = {};
|
|
8858
9464
|
if (!this.element) return;
|
|
8859
9465
|
var els = this.element.querySelectorAll('[data-bw_ref]');
|
|
@@ -8874,7 +9480,7 @@ ComponentHandle.prototype._collectRefs = function() {
|
|
|
8874
9480
|
* Creates DOM, compiles bindings, registers actions, and calls lifecycle hooks.
|
|
8875
9481
|
* @param {Element} parentEl - DOM element to mount into
|
|
8876
9482
|
*/
|
|
8877
|
-
|
|
9483
|
+
_chp.mount = function(parentEl) {
|
|
8878
9484
|
// willMount hook
|
|
8879
9485
|
if (this._hooks.willMount) this._hooks.willMount(this);
|
|
8880
9486
|
|
|
@@ -8896,7 +9502,7 @@ ComponentHandle.prototype.mount = function(parentEl) {
|
|
|
8896
9502
|
// Register named actions in function registry
|
|
8897
9503
|
var self = this;
|
|
8898
9504
|
for (var actionName in this._actions) {
|
|
8899
|
-
if (
|
|
9505
|
+
if (_hop.call(this._actions, actionName)) {
|
|
8900
9506
|
var registeredName = this._bwId + '_' + actionName;
|
|
8901
9507
|
(function(aName) {
|
|
8902
9508
|
bw.funcRegister(function(evt) {
|
|
@@ -8915,6 +9521,11 @@ ComponentHandle.prototype.mount = function(parentEl) {
|
|
|
8915
9521
|
this.element = bw.createDOM(tacoForDOM);
|
|
8916
9522
|
this.element._bwComponentHandle = this;
|
|
8917
9523
|
this.element.setAttribute('data-bw_comp_id', this._bwId);
|
|
9524
|
+
|
|
9525
|
+
// Restore o.render from original TACO (stripped by _tacoForDOM)
|
|
9526
|
+
if (this.taco.o && this.taco.o.render) {
|
|
9527
|
+
this.element._bw_render = this.taco.o.render;
|
|
9528
|
+
}
|
|
8918
9529
|
if (this._userTag) {
|
|
8919
9530
|
this.element.classList.add(this._userTag);
|
|
8920
9531
|
}
|
|
@@ -8930,6 +9541,16 @@ ComponentHandle.prototype.mount = function(parentEl) {
|
|
|
8930
9541
|
|
|
8931
9542
|
this.mounted = true;
|
|
8932
9543
|
|
|
9544
|
+
// Scan for child ComponentHandles and link parent/child (Bug #5)
|
|
9545
|
+
var childEls = this.element.querySelectorAll('[data-bw_comp_id]');
|
|
9546
|
+
for (var ci = 0; ci < childEls.length; ci++) {
|
|
9547
|
+
var ch = childEls[ci]._bwComponentHandle;
|
|
9548
|
+
if (ch && ch !== this && !ch._parent) {
|
|
9549
|
+
ch._parent = this;
|
|
9550
|
+
this._children.push(ch);
|
|
9551
|
+
}
|
|
9552
|
+
}
|
|
9553
|
+
|
|
8933
9554
|
// mounted hook (backward compat: fn.length === 2 wraps (el, state))
|
|
8934
9555
|
if (this._hooks.mounted) {
|
|
8935
9556
|
if (this._hooks.mounted.length === 2) {
|
|
@@ -8938,16 +9559,21 @@ ComponentHandle.prototype.mount = function(parentEl) {
|
|
|
8938
9559
|
this._hooks.mounted(this);
|
|
8939
9560
|
}
|
|
8940
9561
|
}
|
|
9562
|
+
|
|
9563
|
+
// Invoke o.render on initial mount (if present)
|
|
9564
|
+
if (this.element._bw_render) {
|
|
9565
|
+
this.element._bw_render(this.element, this._state);
|
|
9566
|
+
}
|
|
8941
9567
|
};
|
|
8942
9568
|
|
|
8943
9569
|
/**
|
|
8944
9570
|
* Prepare TACO for initial render: resolve when/each markers.
|
|
8945
9571
|
* @private
|
|
8946
9572
|
*/
|
|
8947
|
-
|
|
8948
|
-
if (!taco
|
|
9573
|
+
_chp._prepareTaco = function(taco) {
|
|
9574
|
+
if (!_is(taco, 'object')) return;
|
|
8949
9575
|
|
|
8950
|
-
if (
|
|
9576
|
+
if (_isA(taco.c)) {
|
|
8951
9577
|
for (var i = taco.c.length - 1; i >= 0; i--) {
|
|
8952
9578
|
var child = taco.c[i];
|
|
8953
9579
|
if (child && child._bwWhen) {
|
|
@@ -8972,18 +9598,18 @@ ComponentHandle.prototype._prepareTaco = function(taco) {
|
|
|
8972
9598
|
var eachExprStr = child.expr.replace(/^\$\{|\}$/g, '');
|
|
8973
9599
|
var arr = bw._evaluatePath(this._state, eachExprStr);
|
|
8974
9600
|
var items = [];
|
|
8975
|
-
if (
|
|
9601
|
+
if (_isA(arr)) {
|
|
8976
9602
|
for (var j = 0; j < arr.length; j++) {
|
|
8977
9603
|
items.push(child.factory(arr[j], j));
|
|
8978
9604
|
}
|
|
8979
9605
|
}
|
|
8980
9606
|
taco.c[i] = { t: 'span', a: { 'data-bw_each': child._refId, style: 'display:contents' }, c: items };
|
|
8981
9607
|
}
|
|
8982
|
-
if (taco.c[i]
|
|
9608
|
+
if (_is(taco.c[i], 'object') && taco.c[i].t) {
|
|
8983
9609
|
this._prepareTaco(taco.c[i]);
|
|
8984
9610
|
}
|
|
8985
9611
|
}
|
|
8986
|
-
} else if (taco.c
|
|
9612
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
8987
9613
|
this._prepareTaco(taco.c);
|
|
8988
9614
|
}
|
|
8989
9615
|
};
|
|
@@ -8992,12 +9618,12 @@ ComponentHandle.prototype._prepareTaco = function(taco) {
|
|
|
8992
9618
|
* Wire action name strings (in onclick etc.) to dispatch function calls.
|
|
8993
9619
|
* @private
|
|
8994
9620
|
*/
|
|
8995
|
-
|
|
8996
|
-
if (!taco
|
|
9621
|
+
_chp._wireActions = function(taco) {
|
|
9622
|
+
if (!_is(taco, 'object') || !taco.t) return;
|
|
8997
9623
|
if (taco.a) {
|
|
8998
9624
|
for (var key in taco.a) {
|
|
8999
|
-
if (!
|
|
9000
|
-
if (key.startsWith('on') &&
|
|
9625
|
+
if (!_hop.call(taco.a, key)) continue;
|
|
9626
|
+
if (key.startsWith('on') && _is(taco.a[key], 'string')) {
|
|
9001
9627
|
var actionName = taco.a[key];
|
|
9002
9628
|
if (actionName in this._actions) {
|
|
9003
9629
|
var registeredName = this._bwId + '_' + actionName;
|
|
@@ -9011,11 +9637,11 @@ ComponentHandle.prototype._wireActions = function(taco) {
|
|
|
9011
9637
|
}
|
|
9012
9638
|
}
|
|
9013
9639
|
}
|
|
9014
|
-
if (
|
|
9640
|
+
if (_isA(taco.c)) {
|
|
9015
9641
|
for (var i = 0; i < taco.c.length; i++) {
|
|
9016
9642
|
this._wireActions(taco.c[i]);
|
|
9017
9643
|
}
|
|
9018
|
-
} else if (taco.c
|
|
9644
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
9019
9645
|
this._wireActions(taco.c);
|
|
9020
9646
|
}
|
|
9021
9647
|
};
|
|
@@ -9024,7 +9650,7 @@ ComponentHandle.prototype._wireActions = function(taco) {
|
|
|
9024
9650
|
* Deep-clone a TACO tree, preserving _bwWhen/_bwEach markers and their factories.
|
|
9025
9651
|
* @private
|
|
9026
9652
|
*/
|
|
9027
|
-
|
|
9653
|
+
_chp._deepCloneTaco = function(taco) {
|
|
9028
9654
|
if (taco == null) return taco;
|
|
9029
9655
|
// Preserve _bwWhen / _bwEach markers (contain functions)
|
|
9030
9656
|
if (taco._bwWhen) {
|
|
@@ -9036,18 +9662,18 @@ ComponentHandle.prototype._deepCloneTaco = function(taco) {
|
|
|
9036
9662
|
if (taco._bwEach) {
|
|
9037
9663
|
return { _bwEach: true, expr: taco.expr, factory: taco.factory, _refId: taco._refId };
|
|
9038
9664
|
}
|
|
9039
|
-
if (
|
|
9665
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
9040
9666
|
var result = { t: taco.t };
|
|
9041
9667
|
if (taco.a) {
|
|
9042
9668
|
result.a = {};
|
|
9043
9669
|
for (var k in taco.a) {
|
|
9044
|
-
if (
|
|
9670
|
+
if (_hop.call(taco.a, k)) result.a[k] = taco.a[k];
|
|
9045
9671
|
}
|
|
9046
9672
|
}
|
|
9047
9673
|
if (taco.c != null) {
|
|
9048
|
-
if (
|
|
9674
|
+
if (_isA(taco.c)) {
|
|
9049
9675
|
result.c = taco.c.map(function(child) { return this._deepCloneTaco(child); }.bind(this));
|
|
9050
|
-
} else if (
|
|
9676
|
+
} else if (_is(taco.c, 'object')) {
|
|
9051
9677
|
result.c = this._deepCloneTaco(taco.c);
|
|
9052
9678
|
} else {
|
|
9053
9679
|
result.c = taco.c;
|
|
@@ -9061,27 +9687,31 @@ ComponentHandle.prototype._deepCloneTaco = function(taco) {
|
|
|
9061
9687
|
* Create a copy of TACO suitable for createDOM (strips o to prevent double lifecycle).
|
|
9062
9688
|
* @private
|
|
9063
9689
|
*/
|
|
9064
|
-
|
|
9065
|
-
if (!taco
|
|
9690
|
+
_chp._tacoForDOM = function(taco) {
|
|
9691
|
+
if (!_is(taco, 'object') || !taco.t) return taco;
|
|
9066
9692
|
var result = { t: taco.t };
|
|
9067
9693
|
if (taco.a) result.a = taco.a;
|
|
9068
9694
|
if (taco.c != null) {
|
|
9069
|
-
if (
|
|
9695
|
+
if (_isA(taco.c)) {
|
|
9070
9696
|
result.c = taco.c.map(function(child) { return this._tacoForDOM(child); }.bind(this));
|
|
9071
|
-
} else if (
|
|
9697
|
+
} else if (_is(taco.c, 'object') && taco.c.t) {
|
|
9072
9698
|
result.c = this._tacoForDOM(taco.c);
|
|
9073
9699
|
} else {
|
|
9074
9700
|
result.c = taco.c;
|
|
9075
9701
|
}
|
|
9076
9702
|
}
|
|
9077
9703
|
// Intentionally strip o (no mounted/unmount/state/render on sub-elements)
|
|
9704
|
+
if (taco.o && (taco.o.mounted || taco.o.render || taco.o.unmount)) {
|
|
9705
|
+
_cw('bw: _tacoForDOM stripped o.mounted/render/unmount from child <' + taco.t +
|
|
9706
|
+
'>. Use onclick attribute or bw.component() for child interactivity.');
|
|
9707
|
+
}
|
|
9078
9708
|
return result;
|
|
9079
9709
|
};
|
|
9080
9710
|
|
|
9081
9711
|
/**
|
|
9082
9712
|
* Unmount: remove from DOM, deactivate, preserve state for re-mount.
|
|
9083
9713
|
*/
|
|
9084
|
-
|
|
9714
|
+
_chp.unmount = function() {
|
|
9085
9715
|
if (!this.mounted) return;
|
|
9086
9716
|
|
|
9087
9717
|
// unmount hook
|
|
@@ -9116,12 +9746,23 @@ ComponentHandle.prototype.unmount = function() {
|
|
|
9116
9746
|
/**
|
|
9117
9747
|
* Destroy: unmount + clear state + unregister actions.
|
|
9118
9748
|
*/
|
|
9119
|
-
|
|
9749
|
+
_chp.destroy = function() {
|
|
9120
9750
|
// willDestroy hook
|
|
9121
9751
|
if (this._hooks.willDestroy) {
|
|
9122
9752
|
this._hooks.willDestroy(this);
|
|
9123
9753
|
}
|
|
9124
9754
|
|
|
9755
|
+
// Cascade destroy to children depth-first (Bug #5)
|
|
9756
|
+
for (var ci = this._children.length - 1; ci >= 0; ci--) {
|
|
9757
|
+
this._children[ci].destroy();
|
|
9758
|
+
}
|
|
9759
|
+
this._children = [];
|
|
9760
|
+
if (this._parent) {
|
|
9761
|
+
var idx = this._parent._children.indexOf(this);
|
|
9762
|
+
if (idx >= 0) this._parent._children.splice(idx, 1);
|
|
9763
|
+
this._parent = null;
|
|
9764
|
+
}
|
|
9765
|
+
|
|
9125
9766
|
this.unmount();
|
|
9126
9767
|
|
|
9127
9768
|
// Unregister actions from function registry
|
|
@@ -9148,12 +9789,36 @@ ComponentHandle.prototype.destroy = function() {
|
|
|
9148
9789
|
* Flush dirty state: resolve changed bindings and apply to DOM.
|
|
9149
9790
|
* @private
|
|
9150
9791
|
*/
|
|
9151
|
-
|
|
9792
|
+
_chp._flush = function() {
|
|
9152
9793
|
this._scheduled = false;
|
|
9153
|
-
var changedKeys =
|
|
9794
|
+
var changedKeys = _keys(this._dirtyKeys);
|
|
9154
9795
|
this._dirtyKeys = {};
|
|
9155
9796
|
if (changedKeys.length === 0 || !this.mounted) return;
|
|
9156
9797
|
|
|
9798
|
+
// Factory rebuild: if a BCCL factory exists and changed keys overlap factory props,
|
|
9799
|
+
// rebuild the TACO from the factory with merged state (Bug #6)
|
|
9800
|
+
if (this._factory) {
|
|
9801
|
+
var rebuildNeeded = false;
|
|
9802
|
+
for (var fi = 0; fi < changedKeys.length; fi++) {
|
|
9803
|
+
if (_hop.call(this._factory.props, changedKeys[fi])) {
|
|
9804
|
+
rebuildNeeded = true; break;
|
|
9805
|
+
}
|
|
9806
|
+
}
|
|
9807
|
+
if (rebuildNeeded) {
|
|
9808
|
+
var merged = {};
|
|
9809
|
+
for (var mk in this._factory.props) if (_hop.call(this._factory.props, mk)) merged[mk] = this._factory.props[mk];
|
|
9810
|
+
for (var sk in this._state) if (_hop.call(this._state, sk)) merged[sk] = this._state[sk];
|
|
9811
|
+
this._factory.props = merged;
|
|
9812
|
+
var newTaco = bw.make(this._factory.type, merged);
|
|
9813
|
+
newTaco._bwFactory = this._factory;
|
|
9814
|
+
this.taco = newTaco;
|
|
9815
|
+
this._originalTaco = this._deepCloneTaco(newTaco);
|
|
9816
|
+
this._render();
|
|
9817
|
+
if (this._hooks.onUpdate) this._hooks.onUpdate(this, changedKeys);
|
|
9818
|
+
return;
|
|
9819
|
+
}
|
|
9820
|
+
}
|
|
9821
|
+
|
|
9157
9822
|
// willUpdate hook
|
|
9158
9823
|
if (this._hooks.willUpdate) {
|
|
9159
9824
|
this._hooks.willUpdate(this, changedKeys);
|
|
@@ -9192,7 +9857,7 @@ ComponentHandle.prototype._flush = function() {
|
|
|
9192
9857
|
* Returns list of patches to apply.
|
|
9193
9858
|
* @private
|
|
9194
9859
|
*/
|
|
9195
|
-
|
|
9860
|
+
_chp._resolveBindings = function(changedKeys) {
|
|
9196
9861
|
var patches = [];
|
|
9197
9862
|
for (var i = 0; i < this._bindings.length; i++) {
|
|
9198
9863
|
var b = this._bindings[i];
|
|
@@ -9228,11 +9893,14 @@ ComponentHandle.prototype._resolveBindings = function(changedKeys) {
|
|
|
9228
9893
|
* Apply patches to DOM.
|
|
9229
9894
|
* @private
|
|
9230
9895
|
*/
|
|
9231
|
-
|
|
9896
|
+
_chp._applyPatches = function(patches) {
|
|
9232
9897
|
for (var i = 0; i < patches.length; i++) {
|
|
9233
9898
|
var p = patches[i];
|
|
9234
9899
|
var el = this._bw_refs[p.refId];
|
|
9235
|
-
if (!el)
|
|
9900
|
+
if (!el) {
|
|
9901
|
+
if (bw.debug) _cw('bw.debug: _applyPatches — ref "' + p.refId + '" not found in DOM');
|
|
9902
|
+
continue;
|
|
9903
|
+
}
|
|
9236
9904
|
if (p.type === 'content') {
|
|
9237
9905
|
el.textContent = p.value;
|
|
9238
9906
|
} else if (p.type === 'attribute') {
|
|
@@ -9249,7 +9917,7 @@ ComponentHandle.prototype._applyPatches = function(patches) {
|
|
|
9249
9917
|
* Resolve all bindings and apply (used for initial render).
|
|
9250
9918
|
* @private
|
|
9251
9919
|
*/
|
|
9252
|
-
|
|
9920
|
+
_chp._resolveAndApplyAll = function() {
|
|
9253
9921
|
var patches = [];
|
|
9254
9922
|
for (var i = 0; i < this._bindings.length; i++) {
|
|
9255
9923
|
var b = this._bindings[i];
|
|
@@ -9272,7 +9940,7 @@ ComponentHandle.prototype._resolveAndApplyAll = function() {
|
|
|
9272
9940
|
* Full re-render for structural changes (when/each branch switches).
|
|
9273
9941
|
* @private
|
|
9274
9942
|
*/
|
|
9275
|
-
|
|
9943
|
+
_chp._render = function() {
|
|
9276
9944
|
if (!this.element || !this.element.parentNode) return;
|
|
9277
9945
|
var parent = this.element.parentNode;
|
|
9278
9946
|
var nextSibling = this.element.nextSibling;
|
|
@@ -9312,7 +9980,7 @@ ComponentHandle.prototype._render = function() {
|
|
|
9312
9980
|
* @param {string} event - Event name (e.g., 'click')
|
|
9313
9981
|
* @param {Function} handler - Event handler
|
|
9314
9982
|
*/
|
|
9315
|
-
|
|
9983
|
+
_chp.on = function(event, handler) {
|
|
9316
9984
|
if (this.element) {
|
|
9317
9985
|
this.element.addEventListener(event, handler);
|
|
9318
9986
|
}
|
|
@@ -9324,7 +9992,7 @@ ComponentHandle.prototype.on = function(event, handler) {
|
|
|
9324
9992
|
* @param {string} event - Event name
|
|
9325
9993
|
* @param {Function} handler - Handler to remove
|
|
9326
9994
|
*/
|
|
9327
|
-
|
|
9995
|
+
_chp.off = function(event, handler) {
|
|
9328
9996
|
if (this.element) {
|
|
9329
9997
|
this.element.removeEventListener(event, handler);
|
|
9330
9998
|
}
|
|
@@ -9339,7 +10007,7 @@ ComponentHandle.prototype.off = function(event, handler) {
|
|
|
9339
10007
|
* @param {Function} handler - Handler function
|
|
9340
10008
|
* @returns {Function} Unsubscribe function
|
|
9341
10009
|
*/
|
|
9342
|
-
|
|
10010
|
+
_chp.sub = function(topic, handler) {
|
|
9343
10011
|
var unsub = bw.sub(topic, handler);
|
|
9344
10012
|
this._subs.push(unsub);
|
|
9345
10013
|
return unsub;
|
|
@@ -9350,10 +10018,10 @@ ComponentHandle.prototype.sub = function(topic, handler) {
|
|
|
9350
10018
|
* @param {string} name - Action name
|
|
9351
10019
|
* @param {...*} args - Arguments passed after comp
|
|
9352
10020
|
*/
|
|
9353
|
-
|
|
10021
|
+
_chp.action = function(name) {
|
|
9354
10022
|
var fn = this._actions[name];
|
|
9355
10023
|
if (!fn) {
|
|
9356
|
-
|
|
10024
|
+
_cw('ComponentHandle.action: unknown action "' + name + '"');
|
|
9357
10025
|
return;
|
|
9358
10026
|
}
|
|
9359
10027
|
var args = [this].concat(Array.prototype.slice.call(arguments, 1));
|
|
@@ -9365,7 +10033,7 @@ ComponentHandle.prototype.action = function(name) {
|
|
|
9365
10033
|
* @param {string} sel - CSS selector
|
|
9366
10034
|
* @returns {Element|null}
|
|
9367
10035
|
*/
|
|
9368
|
-
|
|
10036
|
+
_chp.select = function(sel) {
|
|
9369
10037
|
return this.element ? this.element.querySelector(sel) : null;
|
|
9370
10038
|
};
|
|
9371
10039
|
|
|
@@ -9374,7 +10042,7 @@ ComponentHandle.prototype.select = function(sel) {
|
|
|
9374
10042
|
* @param {string} sel - CSS selector
|
|
9375
10043
|
* @returns {Element[]}
|
|
9376
10044
|
*/
|
|
9377
|
-
|
|
10045
|
+
_chp.selectAll = function(sel) {
|
|
9378
10046
|
if (!this.element) return [];
|
|
9379
10047
|
return Array.prototype.slice.call(this.element.querySelectorAll(sel));
|
|
9380
10048
|
};
|
|
@@ -9385,7 +10053,7 @@ ComponentHandle.prototype.selectAll = function(sel) {
|
|
|
9385
10053
|
* @param {string} tag - User-defined identifier (e.g. 'dashboard_prod_east')
|
|
9386
10054
|
* @returns {ComponentHandle} this (for chaining)
|
|
9387
10055
|
*/
|
|
9388
|
-
|
|
10056
|
+
_chp.userTag = function(tag) {
|
|
9389
10057
|
this._userTag = tag;
|
|
9390
10058
|
if (this.element) {
|
|
9391
10059
|
this.element.classList.add(tag);
|
|
@@ -9462,7 +10130,7 @@ bw.component = function(taco) {
|
|
|
9462
10130
|
* and calls the named method. This is the bitwrench equivalent of
|
|
9463
10131
|
* Win32 SendMessage(hwnd, msg, wParam, lParam).
|
|
9464
10132
|
*
|
|
9465
|
-
* @param {string} target - Component UUID (data-bw_comp_id) or user tag (CSS class)
|
|
10133
|
+
* @param {string} target - Component UUID (bw_uuid_*), comp ID (data-bw_comp_id), or user tag (CSS class)
|
|
9466
10134
|
* @param {string} action - Method name to call on the component
|
|
9467
10135
|
* @param {*} data - Data to pass to the method
|
|
9468
10136
|
* @returns {boolean} True if message was dispatched successfully
|
|
@@ -9479,15 +10147,20 @@ bw.component = function(taco) {
|
|
|
9479
10147
|
* };
|
|
9480
10148
|
*/
|
|
9481
10149
|
bw.message = function(target, action, data) {
|
|
9482
|
-
// Try
|
|
9483
|
-
var el = bw
|
|
9484
|
-
|
|
10150
|
+
// Try bw._el() first (handles UUID class, nodeMap cache, getElementById)
|
|
10151
|
+
var el = bw._el(target);
|
|
10152
|
+
// Then try data-bw_comp_id attribute
|
|
10153
|
+
if (!el || !el._bwComponentHandle) {
|
|
10154
|
+
el = bw.$('[data-bw_comp_id="' + target + '"]')[0];
|
|
10155
|
+
}
|
|
10156
|
+
// Then try CSS class (user tag)
|
|
10157
|
+
if (!el || !el._bwComponentHandle) {
|
|
9485
10158
|
el = bw.$('.' + target)[0];
|
|
9486
10159
|
}
|
|
9487
10160
|
if (!el || !el._bwComponentHandle) return false;
|
|
9488
10161
|
var comp = el._bwComponentHandle;
|
|
9489
|
-
if (
|
|
9490
|
-
|
|
10162
|
+
if (!_is(comp[action], 'function')) {
|
|
10163
|
+
_cw('bw.message: unknown action "' + action + '" on component ' + target);
|
|
9491
10164
|
return false;
|
|
9492
10165
|
}
|
|
9493
10166
|
comp[action](data);
|
|
@@ -9495,59 +10168,24 @@ bw.message = function(target, action, data) {
|
|
|
9495
10168
|
};
|
|
9496
10169
|
|
|
9497
10170
|
// ===================================================================================
|
|
9498
|
-
// bw.
|
|
10171
|
+
// bw.apply() / bw.parseJSONFlex() — Server-driven UI protocol
|
|
9499
10172
|
// ===================================================================================
|
|
9500
10173
|
|
|
9501
10174
|
/**
|
|
9502
10175
|
* Registry of named functions sent via register messages.
|
|
9503
|
-
* Populated by
|
|
9504
|
-
* Invoked by
|
|
10176
|
+
* Populated by bw.apply({ type: 'register', name, body }).
|
|
10177
|
+
* Invoked by bw.apply({ type: 'call', name, args }).
|
|
9505
10178
|
* @private
|
|
9506
10179
|
*/
|
|
9507
10180
|
bw._clientFunctions = {};
|
|
9508
10181
|
|
|
9509
10182
|
/**
|
|
9510
|
-
* Whether exec messages are allowed. Set by
|
|
10183
|
+
* Whether exec messages are allowed. Set by bwclient connect opts.allowExec.
|
|
9511
10184
|
* Default false — exec messages are rejected unless explicitly opted in.
|
|
9512
10185
|
* @private
|
|
9513
10186
|
*/
|
|
9514
10187
|
bw._allowExec = false;
|
|
9515
10188
|
|
|
9516
|
-
/**
|
|
9517
|
-
* Built-in client functions available via call() without registration.
|
|
9518
|
-
* @private
|
|
9519
|
-
*/
|
|
9520
|
-
bw._builtinClientFunctions = {
|
|
9521
|
-
scrollTo: function(selector) {
|
|
9522
|
-
var el = bw._el(selector);
|
|
9523
|
-
if (el) el.scrollTop = el.scrollHeight;
|
|
9524
|
-
},
|
|
9525
|
-
focus: function(selector) {
|
|
9526
|
-
var el = bw._el(selector);
|
|
9527
|
-
if (el && typeof el.focus === 'function') el.focus();
|
|
9528
|
-
},
|
|
9529
|
-
download: function(filename, content, mimeType) {
|
|
9530
|
-
if (typeof document === 'undefined') return;
|
|
9531
|
-
var blob = new Blob([content], { type: mimeType || 'text/plain' });
|
|
9532
|
-
var a = document.createElement('a');
|
|
9533
|
-
a.href = URL.createObjectURL(blob);
|
|
9534
|
-
a.download = filename;
|
|
9535
|
-
a.click();
|
|
9536
|
-
URL.revokeObjectURL(a.href);
|
|
9537
|
-
},
|
|
9538
|
-
clipboard: function(text) {
|
|
9539
|
-
if (typeof navigator !== 'undefined' && navigator.clipboard) {
|
|
9540
|
-
navigator.clipboard.writeText(text);
|
|
9541
|
-
}
|
|
9542
|
-
},
|
|
9543
|
-
redirect: function(url) {
|
|
9544
|
-
if (typeof window !== 'undefined') window.location.href = url;
|
|
9545
|
-
},
|
|
9546
|
-
log: function() {
|
|
9547
|
-
console.log.apply(console, arguments);
|
|
9548
|
-
}
|
|
9549
|
-
};
|
|
9550
|
-
|
|
9551
10189
|
/**
|
|
9552
10190
|
* Parse a bwserve protocol message string, supporting both strict JSON
|
|
9553
10191
|
* and r-prefixed relaxed JSON (single-quoted strings, trailing commas).
|
|
@@ -9562,9 +10200,9 @@ bw._builtinClientFunctions = {
|
|
|
9562
10200
|
* @param {string} str - JSON or r-prefixed relaxed JSON string
|
|
9563
10201
|
* @returns {Object} Parsed message object
|
|
9564
10202
|
* @throws {SyntaxError} If the string is not valid JSON or relaxed JSON
|
|
9565
|
-
* @category
|
|
10203
|
+
* @category Core
|
|
9566
10204
|
*/
|
|
9567
|
-
bw.
|
|
10205
|
+
bw.parseJSONFlex = function(str) {
|
|
9568
10206
|
str = (str || '').trim();
|
|
9569
10207
|
if (str.charAt(0) !== 'r') return JSON.parse(str);
|
|
9570
10208
|
str = str.slice(1);
|
|
@@ -9649,10 +10287,10 @@ bw.clientParse = function(str) {
|
|
|
9649
10287
|
* append — target.appendChild(bw.createDOM(node))
|
|
9650
10288
|
* remove — bw.cleanup(target); target.remove()
|
|
9651
10289
|
* patch — bw.patch(target, content, attr)
|
|
9652
|
-
* batch — iterate ops, call
|
|
10290
|
+
* batch — iterate ops, call bw.apply for each
|
|
9653
10291
|
* message — bw.message(target, action, data)
|
|
9654
10292
|
* register — store a named function for later call()
|
|
9655
|
-
* call — invoke a registered
|
|
10293
|
+
* call — invoke a registered function
|
|
9656
10294
|
* exec — execute arbitrary JS (requires allowExec)
|
|
9657
10295
|
*
|
|
9658
10296
|
* Target resolution:
|
|
@@ -9661,9 +10299,9 @@ bw.clientParse = function(str) {
|
|
|
9661
10299
|
*
|
|
9662
10300
|
* @param {Object} msg - Protocol message
|
|
9663
10301
|
* @returns {boolean} true if the message was applied successfully
|
|
9664
|
-
* @category
|
|
10302
|
+
* @category Core
|
|
9665
10303
|
*/
|
|
9666
|
-
bw.
|
|
10304
|
+
bw.apply = function(msg) {
|
|
9667
10305
|
if (!msg || !msg.type) return false;
|
|
9668
10306
|
|
|
9669
10307
|
var type = msg.type;
|
|
@@ -9689,15 +10327,15 @@ bw.clientApply = function(msg) {
|
|
|
9689
10327
|
} else if (type === 'remove') {
|
|
9690
10328
|
var toRemove = bw._el(target);
|
|
9691
10329
|
if (!toRemove) return false;
|
|
9692
|
-
if (
|
|
10330
|
+
if (_is(bw.cleanup, 'function')) bw.cleanup(toRemove);
|
|
9693
10331
|
toRemove.remove();
|
|
9694
10332
|
return true;
|
|
9695
10333
|
|
|
9696
10334
|
} else if (type === 'batch') {
|
|
9697
|
-
if (!
|
|
10335
|
+
if (!_isA(msg.ops)) return false;
|
|
9698
10336
|
var allOk = true;
|
|
9699
10337
|
msg.ops.forEach(function(op) {
|
|
9700
|
-
if (!bw.
|
|
10338
|
+
if (!bw.apply(op)) allOk = false;
|
|
9701
10339
|
});
|
|
9702
10340
|
return allOk;
|
|
9703
10341
|
|
|
@@ -9710,26 +10348,26 @@ bw.clientApply = function(msg) {
|
|
|
9710
10348
|
bw._clientFunctions[msg.name] = new Function('return ' + msg.body)();
|
|
9711
10349
|
return true;
|
|
9712
10350
|
} catch (e) {
|
|
9713
|
-
|
|
10351
|
+
_ce('[bw] register error:', msg.name, e);
|
|
9714
10352
|
return false;
|
|
9715
10353
|
}
|
|
9716
10354
|
|
|
9717
10355
|
} else if (type === 'call') {
|
|
9718
10356
|
if (!msg.name) return false;
|
|
9719
|
-
var fn = bw._clientFunctions[msg.name]
|
|
9720
|
-
if (
|
|
10357
|
+
var fn = bw._clientFunctions[msg.name];
|
|
10358
|
+
if (!_is(fn, 'function')) return false;
|
|
9721
10359
|
try {
|
|
9722
|
-
var args =
|
|
10360
|
+
var args = _isA(msg.args) ? msg.args : [];
|
|
9723
10361
|
fn.apply(null, args);
|
|
9724
10362
|
return true;
|
|
9725
10363
|
} catch (e) {
|
|
9726
|
-
|
|
10364
|
+
_ce('[bw] call error:', msg.name, e);
|
|
9727
10365
|
return false;
|
|
9728
10366
|
}
|
|
9729
10367
|
|
|
9730
10368
|
} else if (type === 'exec') {
|
|
9731
10369
|
if (!bw._allowExec) {
|
|
9732
|
-
|
|
10370
|
+
_cw('[bw] exec rejected: allowExec is not enabled');
|
|
9733
10371
|
return false;
|
|
9734
10372
|
}
|
|
9735
10373
|
if (!msg.code) return false;
|
|
@@ -9737,7 +10375,7 @@ bw.clientApply = function(msg) {
|
|
|
9737
10375
|
new Function(msg.code)();
|
|
9738
10376
|
return true;
|
|
9739
10377
|
} catch (e) {
|
|
9740
|
-
|
|
10378
|
+
_ce('[bw] exec error:', e);
|
|
9741
10379
|
return false;
|
|
9742
10380
|
}
|
|
9743
10381
|
}
|
|
@@ -9745,139 +10383,6 @@ bw.clientApply = function(msg) {
|
|
|
9745
10383
|
return false;
|
|
9746
10384
|
};
|
|
9747
10385
|
|
|
9748
|
-
/**
|
|
9749
|
-
* Connect to a bwserve SSE endpoint and apply protocol messages automatically.
|
|
9750
|
-
*
|
|
9751
|
-
* Returns a connection object with sendAction(), on(), and close() methods.
|
|
9752
|
-
*
|
|
9753
|
-
* @param {string} url - SSE endpoint URL (e.g., '/__bw/events/client-1')
|
|
9754
|
-
* @param {Object} [opts] - Connection options
|
|
9755
|
-
* @param {string} [opts.transport='sse'] - Transport type: 'sse' (default) or 'poll'
|
|
9756
|
-
* @param {number} [opts.interval=2000] - Poll interval in ms (only for 'poll' transport)
|
|
9757
|
-
* @param {string} [opts.actionUrl] - POST endpoint for actions (default: derived from url)
|
|
9758
|
-
* @param {boolean} [opts.reconnect=true] - Auto-reconnect on disconnect
|
|
9759
|
-
* @param {boolean} [opts.allowExec=false] - Enable exec message type (arbitrary JS execution)
|
|
9760
|
-
* @param {Function} [opts.onStatus] - Status callback: 'connecting'|'connected'|'disconnected'
|
|
9761
|
-
* @param {Function} [opts.onMessage] - Raw message callback (before clientApply)
|
|
9762
|
-
* @returns {Object} Connection object { sendAction, on, close, status }
|
|
9763
|
-
* @category Server
|
|
9764
|
-
*/
|
|
9765
|
-
bw.clientConnect = function(url, opts) {
|
|
9766
|
-
opts = opts || {};
|
|
9767
|
-
var transport = opts.transport || 'sse';
|
|
9768
|
-
var actionUrl = opts.actionUrl || url.replace(/\/events\//, '/action/');
|
|
9769
|
-
var reconnect = opts.reconnect !== false;
|
|
9770
|
-
var onStatus = opts.onStatus || function() {};
|
|
9771
|
-
var onMessage = opts.onMessage || null;
|
|
9772
|
-
var handlers = {};
|
|
9773
|
-
// Set the global allowExec flag from connection options
|
|
9774
|
-
bw._allowExec = !!opts.allowExec;
|
|
9775
|
-
var conn = {
|
|
9776
|
-
status: 'connecting',
|
|
9777
|
-
_es: null,
|
|
9778
|
-
_pollTimer: null
|
|
9779
|
-
};
|
|
9780
|
-
|
|
9781
|
-
function setStatus(s) {
|
|
9782
|
-
conn.status = s;
|
|
9783
|
-
onStatus(s);
|
|
9784
|
-
}
|
|
9785
|
-
|
|
9786
|
-
function handleMessage(data) {
|
|
9787
|
-
try {
|
|
9788
|
-
var msg = typeof data === 'string' ? bw.clientParse(data) : data;
|
|
9789
|
-
if (onMessage) onMessage(msg);
|
|
9790
|
-
if (handlers.message) handlers.message(msg);
|
|
9791
|
-
bw.clientApply(msg);
|
|
9792
|
-
} catch (e) {
|
|
9793
|
-
if (handlers.error) handlers.error(e);
|
|
9794
|
-
}
|
|
9795
|
-
}
|
|
9796
|
-
|
|
9797
|
-
if (transport === 'sse' && typeof EventSource !== 'undefined') {
|
|
9798
|
-
setStatus('connecting');
|
|
9799
|
-
var es = new EventSource(url);
|
|
9800
|
-
conn._es = es;
|
|
9801
|
-
|
|
9802
|
-
es.onopen = function() {
|
|
9803
|
-
setStatus('connected');
|
|
9804
|
-
if (handlers.open) handlers.open();
|
|
9805
|
-
};
|
|
9806
|
-
|
|
9807
|
-
es.onmessage = function(e) {
|
|
9808
|
-
handleMessage(e.data);
|
|
9809
|
-
};
|
|
9810
|
-
|
|
9811
|
-
es.onerror = function() {
|
|
9812
|
-
if (conn.status === 'connected') {
|
|
9813
|
-
setStatus('disconnected');
|
|
9814
|
-
}
|
|
9815
|
-
if (handlers.error) handlers.error(new Error('SSE connection error'));
|
|
9816
|
-
if (!reconnect) {
|
|
9817
|
-
es.close();
|
|
9818
|
-
}
|
|
9819
|
-
// EventSource auto-reconnects by default when reconnect=true
|
|
9820
|
-
};
|
|
9821
|
-
} else if (transport === 'poll') {
|
|
9822
|
-
var interval = opts.interval || 2000;
|
|
9823
|
-
setStatus('connected');
|
|
9824
|
-
conn._pollTimer = setInterval(function() {
|
|
9825
|
-
fetch(url).then(function(r) { return r.json(); }).then(function(msgs) {
|
|
9826
|
-
if (Array.isArray(msgs)) {
|
|
9827
|
-
msgs.forEach(handleMessage);
|
|
9828
|
-
} else if (msgs && msgs.type) {
|
|
9829
|
-
handleMessage(msgs);
|
|
9830
|
-
}
|
|
9831
|
-
}).catch(function(e) {
|
|
9832
|
-
if (handlers.error) handlers.error(e);
|
|
9833
|
-
});
|
|
9834
|
-
}, interval);
|
|
9835
|
-
}
|
|
9836
|
-
|
|
9837
|
-
/**
|
|
9838
|
-
* Send an action to the server via POST.
|
|
9839
|
-
* @param {string} action - Action name
|
|
9840
|
-
* @param {Object} [data] - Action payload
|
|
9841
|
-
*/
|
|
9842
|
-
conn.sendAction = function(action, data) {
|
|
9843
|
-
var body = JSON.stringify({ type: 'action', action: action, data: data || {} });
|
|
9844
|
-
fetch(actionUrl, {
|
|
9845
|
-
method: 'POST',
|
|
9846
|
-
headers: { 'Content-Type': 'application/json' },
|
|
9847
|
-
body: body
|
|
9848
|
-
}).catch(function(e) {
|
|
9849
|
-
if (handlers.error) handlers.error(e);
|
|
9850
|
-
});
|
|
9851
|
-
};
|
|
9852
|
-
|
|
9853
|
-
/**
|
|
9854
|
-
* Register an event handler.
|
|
9855
|
-
* @param {string} event - 'open'|'message'|'error'|'close'
|
|
9856
|
-
* @param {Function} handler
|
|
9857
|
-
*/
|
|
9858
|
-
conn.on = function(event, handler) {
|
|
9859
|
-
handlers[event] = handler;
|
|
9860
|
-
return conn;
|
|
9861
|
-
};
|
|
9862
|
-
|
|
9863
|
-
/**
|
|
9864
|
-
* Close the connection.
|
|
9865
|
-
*/
|
|
9866
|
-
conn.close = function() {
|
|
9867
|
-
if (conn._es) {
|
|
9868
|
-
conn._es.close();
|
|
9869
|
-
conn._es = null;
|
|
9870
|
-
}
|
|
9871
|
-
if (conn._pollTimer) {
|
|
9872
|
-
clearInterval(conn._pollTimer);
|
|
9873
|
-
conn._pollTimer = null;
|
|
9874
|
-
}
|
|
9875
|
-
setStatus('disconnected');
|
|
9876
|
-
if (handlers.close) handlers.close();
|
|
9877
|
-
};
|
|
9878
|
-
|
|
9879
|
-
return conn;
|
|
9880
|
-
};
|
|
9881
10386
|
|
|
9882
10387
|
// ===================================================================================
|
|
9883
10388
|
// bw.inspect() — Debug utility
|
|
@@ -9905,33 +10410,33 @@ bw.inspect = function(target) {
|
|
|
9905
10410
|
el = target.element;
|
|
9906
10411
|
comp = target;
|
|
9907
10412
|
} else {
|
|
9908
|
-
if (
|
|
10413
|
+
if (_is(target, 'string')) {
|
|
9909
10414
|
el = bw.$(target)[0];
|
|
9910
10415
|
}
|
|
9911
10416
|
if (!el) {
|
|
9912
|
-
|
|
10417
|
+
_cw('bw.inspect: element not found');
|
|
9913
10418
|
return null;
|
|
9914
10419
|
}
|
|
9915
10420
|
comp = el._bwComponentHandle;
|
|
9916
10421
|
}
|
|
9917
10422
|
if (!comp) {
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
10423
|
+
_cl('bw.inspect: no ComponentHandle on this element');
|
|
10424
|
+
_cl(' Tag:', el.tagName);
|
|
10425
|
+
_cl(' Classes:', el.className);
|
|
10426
|
+
_cl(' _bw_state:', el._bw_state || '(none)');
|
|
9922
10427
|
return null;
|
|
9923
10428
|
}
|
|
9924
10429
|
var deps = comp._bindings.reduce(function(s, b) {
|
|
9925
10430
|
return s.concat(b.deps || []);
|
|
9926
10431
|
}, []).filter(function(v, i, a) { return a.indexOf(v) === i; });
|
|
9927
10432
|
console.group('Component: ' + comp._bwId);
|
|
9928
|
-
|
|
9929
|
-
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
9934
|
-
|
|
10433
|
+
_cl('State:', comp._state);
|
|
10434
|
+
_cl('Bindings:', comp._bindings.length, '(deps:', deps, ')');
|
|
10435
|
+
_cl('Methods:', _keys(comp._methods));
|
|
10436
|
+
_cl('Actions:', _keys(comp._actions));
|
|
10437
|
+
_cl('User tag:', comp._userTag || '(none)');
|
|
10438
|
+
_cl('Mounted:', comp.mounted);
|
|
10439
|
+
_cl('Element:', comp.element);
|
|
9935
10440
|
console.groupEnd();
|
|
9936
10441
|
return comp;
|
|
9937
10442
|
};
|
|
@@ -9954,8 +10459,8 @@ bw.compile = function(taco) {
|
|
|
9954
10459
|
// Pre-extract all binding expressions
|
|
9955
10460
|
var precompiled = [];
|
|
9956
10461
|
function walkExpressions(node) {
|
|
9957
|
-
if (!node
|
|
9958
|
-
if (
|
|
10462
|
+
if (!_is(node, 'object')) return;
|
|
10463
|
+
if (_is(node.c, 'string') && node.c.indexOf('${') >= 0) {
|
|
9959
10464
|
var parsed = bw._parseBindings(node.c);
|
|
9960
10465
|
for (var i = 0; i < parsed.length; i++) {
|
|
9961
10466
|
try {
|
|
@@ -9970,9 +10475,9 @@ bw.compile = function(taco) {
|
|
|
9970
10475
|
}
|
|
9971
10476
|
if (node.a) {
|
|
9972
10477
|
for (var key in node.a) {
|
|
9973
|
-
if (
|
|
10478
|
+
if (_hop.call(node.a, key)) {
|
|
9974
10479
|
var v = node.a[key];
|
|
9975
|
-
if (
|
|
10480
|
+
if (_is(v, 'string') && v.indexOf('${') >= 0) {
|
|
9976
10481
|
var parsed2 = bw._parseBindings(v);
|
|
9977
10482
|
for (var j = 0; j < parsed2.length; j++) {
|
|
9978
10483
|
try {
|
|
@@ -9988,9 +10493,9 @@ bw.compile = function(taco) {
|
|
|
9988
10493
|
}
|
|
9989
10494
|
}
|
|
9990
10495
|
}
|
|
9991
|
-
if (
|
|
10496
|
+
if (_isA(node.c)) {
|
|
9992
10497
|
for (var k = 0; k < node.c.length; k++) walkExpressions(node.c[k]);
|
|
9993
|
-
} else if (node.c
|
|
10498
|
+
} else if (_is(node.c, 'object') && node.c.t) {
|
|
9994
10499
|
walkExpressions(node.c);
|
|
9995
10500
|
}
|
|
9996
10501
|
}
|
|
@@ -10002,7 +10507,7 @@ bw.compile = function(taco) {
|
|
|
10002
10507
|
handle._precompiledBindings = precompiled;
|
|
10003
10508
|
if (initialState) {
|
|
10004
10509
|
for (var k in initialState) {
|
|
10005
|
-
if (
|
|
10510
|
+
if (_hop.call(initialState, k)) {
|
|
10006
10511
|
handle._state[k] = initialState[k];
|
|
10007
10512
|
}
|
|
10008
10513
|
}
|
|
@@ -10033,18 +10538,18 @@ bw.compile = function(taco) {
|
|
|
10033
10538
|
bw.css = function(rules, options = {}) {
|
|
10034
10539
|
const { minify = false, pretty = !minify } = options;
|
|
10035
10540
|
|
|
10036
|
-
if (
|
|
10541
|
+
if (_is(rules, 'string')) return rules;
|
|
10037
10542
|
|
|
10038
10543
|
let css = '';
|
|
10039
10544
|
const indent = pretty ? ' ' : '';
|
|
10040
10545
|
const newline = pretty ? '\n' : '';
|
|
10041
10546
|
const space = pretty ? ' ' : '';
|
|
10042
10547
|
|
|
10043
|
-
if (
|
|
10548
|
+
if (_isA(rules)) {
|
|
10044
10549
|
css = rules.map(rule => bw.css(rule, options)).join(newline);
|
|
10045
|
-
} else if (
|
|
10550
|
+
} else if (_is(rules, 'object')) {
|
|
10046
10551
|
Object.entries(rules).forEach(([selector, styles]) => {
|
|
10047
|
-
if (
|
|
10552
|
+
if (_is(styles, 'object')) {
|
|
10048
10553
|
// Handle @media, @keyframes, @supports — recurse into nested block
|
|
10049
10554
|
if (selector.charAt(0) === '@') {
|
|
10050
10555
|
const inner = bw.css(styles, options);
|
|
@@ -10086,14 +10591,14 @@ bw.css = function(rules, options = {}) {
|
|
|
10086
10591
|
* @returns {Element} The style element
|
|
10087
10592
|
* @category CSS & Styling
|
|
10088
10593
|
* @see bw.css
|
|
10089
|
-
* @see bw.
|
|
10594
|
+
* @see bw.loadStyles
|
|
10090
10595
|
* @example
|
|
10091
10596
|
* bw.injectCSS('.my-class { color: red; }');
|
|
10092
10597
|
* bw.injectCSS({ '.card': { padding: '1rem' } }, { id: 'card-styles' });
|
|
10093
10598
|
*/
|
|
10094
10599
|
bw.injectCSS = function(css, options = {}) {
|
|
10095
10600
|
if (!bw._isBrowser) {
|
|
10096
|
-
|
|
10601
|
+
_cw('bw.injectCSS requires a DOM environment');
|
|
10097
10602
|
return null;
|
|
10098
10603
|
}
|
|
10099
10604
|
|
|
@@ -10110,7 +10615,7 @@ bw.injectCSS = function(css, options = {}) {
|
|
|
10110
10615
|
}
|
|
10111
10616
|
|
|
10112
10617
|
// Convert CSS if needed
|
|
10113
|
-
const cssStr =
|
|
10618
|
+
const cssStr = _is(css, 'string') ? css : bw.css(css, options);
|
|
10114
10619
|
|
|
10115
10620
|
// Set or append CSS
|
|
10116
10621
|
if (append && styleEl.textContent) {
|
|
@@ -10131,113 +10636,19 @@ bw.injectCSS = function(css, options = {}) {
|
|
|
10131
10636
|
* @param {...Object} styles - Style objects to merge (left-to-right)
|
|
10132
10637
|
* @returns {Object} Merged style object
|
|
10133
10638
|
* @category CSS & Styling
|
|
10134
|
-
* @see bw.u
|
|
10135
10639
|
* @example
|
|
10136
|
-
* var style = bw.s(
|
|
10640
|
+
* var style = bw.s({ display: 'flex' }, { gap: '1rem' }, { color: 'red' });
|
|
10137
10641
|
* // => { display: 'flex', gap: '1rem', color: 'red' }
|
|
10138
10642
|
*/
|
|
10139
10643
|
bw.s = function() {
|
|
10140
10644
|
var result = {};
|
|
10141
10645
|
for (var i = 0; i < arguments.length; i++) {
|
|
10142
10646
|
var arg = arguments[i];
|
|
10143
|
-
if (arg
|
|
10647
|
+
if (_is(arg, 'object')) Object.assign(result, arg);
|
|
10144
10648
|
}
|
|
10145
10649
|
return result;
|
|
10146
10650
|
};
|
|
10147
10651
|
|
|
10148
|
-
/**
|
|
10149
|
-
* Pre-built CSS utility objects (like Tailwind utilities, but in JS).
|
|
10150
|
-
*
|
|
10151
|
-
* Compose with `bw.s()` to build inline styles without writing raw CSS strings.
|
|
10152
|
-
* Includes flex, padding, margin, typography, color, border, and transition utilities.
|
|
10153
|
-
*
|
|
10154
|
-
* @category CSS & Styling
|
|
10155
|
-
* @see bw.s
|
|
10156
|
-
* @example
|
|
10157
|
-
* { t: 'div', a: { style: bw.s(bw.u.flex, bw.u.gap4, bw.u.p4) },
|
|
10158
|
-
* c: 'Flexbox with 1rem gap and padding' }
|
|
10159
|
-
*/
|
|
10160
|
-
bw.u = {
|
|
10161
|
-
// Display
|
|
10162
|
-
flex: { display: 'flex' },
|
|
10163
|
-
flexCol: { display: 'flex', flexDirection: 'column' },
|
|
10164
|
-
flexRow: { display: 'flex', flexDirection: 'row' },
|
|
10165
|
-
flexWrap: { display: 'flex', flexWrap: 'wrap' },
|
|
10166
|
-
block: { display: 'block' },
|
|
10167
|
-
inline: { display: 'inline' },
|
|
10168
|
-
hidden: { display: 'none' },
|
|
10169
|
-
|
|
10170
|
-
// Flex alignment
|
|
10171
|
-
justifyCenter: { justifyContent: 'center' },
|
|
10172
|
-
justifyBetween: { justifyContent: 'space-between' },
|
|
10173
|
-
justifyEnd: { justifyContent: 'flex-end' },
|
|
10174
|
-
alignCenter: { alignItems: 'center' },
|
|
10175
|
-
alignStart: { alignItems: 'flex-start' },
|
|
10176
|
-
alignEnd: { alignItems: 'flex-end' },
|
|
10177
|
-
|
|
10178
|
-
// Gap (0.25rem increments)
|
|
10179
|
-
gap1: { gap: '0.25rem' },
|
|
10180
|
-
gap2: { gap: '0.5rem' },
|
|
10181
|
-
gap3: { gap: '0.75rem' },
|
|
10182
|
-
gap4: { gap: '1rem' },
|
|
10183
|
-
gap6: { gap: '1.5rem' },
|
|
10184
|
-
gap8: { gap: '2rem' },
|
|
10185
|
-
|
|
10186
|
-
// Padding
|
|
10187
|
-
p0: { padding: '0' },
|
|
10188
|
-
p1: { padding: '0.25rem' },
|
|
10189
|
-
p2: { padding: '0.5rem' },
|
|
10190
|
-
p3: { padding: '0.75rem' },
|
|
10191
|
-
p4: { padding: '1rem' },
|
|
10192
|
-
p6: { padding: '1.5rem' },
|
|
10193
|
-
p8: { padding: '2rem' },
|
|
10194
|
-
px4: { paddingLeft: '1rem', paddingRight: '1rem' },
|
|
10195
|
-
py2: { paddingTop: '0.5rem', paddingBottom: '0.5rem' },
|
|
10196
|
-
py4: { paddingTop: '1rem', paddingBottom: '1rem' },
|
|
10197
|
-
|
|
10198
|
-
// Margin (same scale)
|
|
10199
|
-
m0: { margin: '0' },
|
|
10200
|
-
m4: { margin: '1rem' },
|
|
10201
|
-
mt2: { marginTop: '0.5rem' },
|
|
10202
|
-
mt4: { marginTop: '1rem' },
|
|
10203
|
-
mb2: { marginBottom: '0.5rem' },
|
|
10204
|
-
mb4: { marginBottom: '1rem' },
|
|
10205
|
-
mx_auto: { marginLeft: 'auto', marginRight: 'auto' },
|
|
10206
|
-
|
|
10207
|
-
// Typography
|
|
10208
|
-
textSm: { fontSize: '0.875rem' },
|
|
10209
|
-
textBase: { fontSize: '1rem' },
|
|
10210
|
-
textLg: { fontSize: '1.125rem' },
|
|
10211
|
-
textXl: { fontSize: '1.25rem' },
|
|
10212
|
-
text2xl: { fontSize: '1.5rem' },
|
|
10213
|
-
text3xl: { fontSize: '1.875rem' },
|
|
10214
|
-
bold: { fontWeight: '700' },
|
|
10215
|
-
semibold: { fontWeight: '600' },
|
|
10216
|
-
italic: { fontStyle: 'italic' },
|
|
10217
|
-
textCenter: { textAlign: 'center' },
|
|
10218
|
-
textRight: { textAlign: 'right' },
|
|
10219
|
-
|
|
10220
|
-
// Colors (from design tokens)
|
|
10221
|
-
bgWhite: { background: '#ffffff' },
|
|
10222
|
-
bgTeal: { background: '#006666', color: '#ffffff' },
|
|
10223
|
-
textWhite: { color: '#ffffff' },
|
|
10224
|
-
textTeal: { color: '#006666' },
|
|
10225
|
-
textMuted: { color: '#888' },
|
|
10226
|
-
|
|
10227
|
-
// Borders
|
|
10228
|
-
rounded: { borderRadius: '0.375rem' },
|
|
10229
|
-
roundedLg: { borderRadius: '0.5rem' },
|
|
10230
|
-
roundedFull: { borderRadius: '9999px' },
|
|
10231
|
-
border: { border: '1px solid #d8d8d8' },
|
|
10232
|
-
|
|
10233
|
-
// Sizing
|
|
10234
|
-
wFull: { width: '100%' },
|
|
10235
|
-
hFull: { height: '100%' },
|
|
10236
|
-
|
|
10237
|
-
// Transitions
|
|
10238
|
-
transition: { transition: 'all 0.2s ease' }
|
|
10239
|
-
};
|
|
10240
|
-
|
|
10241
10652
|
/**
|
|
10242
10653
|
* Generate responsive CSS with media query breakpoints.
|
|
10243
10654
|
*
|
|
@@ -10263,7 +10674,7 @@ bw.u = {
|
|
|
10263
10674
|
bw.responsive = function(selector, breakpoints) {
|
|
10264
10675
|
var sizes = { sm: '576px', md: '768px', lg: '992px', xl: '1200px' };
|
|
10265
10676
|
var parts = [];
|
|
10266
|
-
|
|
10677
|
+
_keys(breakpoints).forEach(function(key) {
|
|
10267
10678
|
var rules = {};
|
|
10268
10679
|
if (key === 'base') {
|
|
10269
10680
|
rules[selector] = breakpoints[key];
|
|
@@ -10335,18 +10746,18 @@ if (bw._isBrowser) {
|
|
|
10335
10746
|
if (!selector) return [];
|
|
10336
10747
|
|
|
10337
10748
|
// Already an array
|
|
10338
|
-
if (
|
|
10749
|
+
if (_isA(selector)) return selector;
|
|
10339
10750
|
|
|
10340
10751
|
// Single element
|
|
10341
10752
|
if (selector.nodeType) return [selector];
|
|
10342
10753
|
|
|
10343
10754
|
// NodeList or HTMLCollection
|
|
10344
|
-
if (selector.length !== undefined &&
|
|
10755
|
+
if (selector.length !== undefined && !_is(selector, 'string')) {
|
|
10345
10756
|
return Array.from(selector);
|
|
10346
10757
|
}
|
|
10347
10758
|
|
|
10348
10759
|
// CSS selector string
|
|
10349
|
-
if (
|
|
10760
|
+
if (_is(selector, 'string')) {
|
|
10350
10761
|
return Array.from(document.querySelectorAll(selector));
|
|
10351
10762
|
}
|
|
10352
10763
|
|
|
@@ -10359,103 +10770,49 @@ if (bw._isBrowser) {
|
|
|
10359
10770
|
};
|
|
10360
10771
|
}
|
|
10361
10772
|
|
|
10362
|
-
/**
|
|
10363
|
-
* Load the built-in Bootstrap-inspired default stylesheet.
|
|
10364
|
-
*
|
|
10365
|
-
* Injects bitwrench's batteries-included CSS (buttons, cards, grids, forms,
|
|
10366
|
-
* alerts, badges, nav, tabs, etc.) into the document head. Call once at app startup.
|
|
10367
|
-
* Returns null in Node.js (no DOM).
|
|
10368
|
-
*
|
|
10369
|
-
* @param {Object} [options] - Style loading options
|
|
10370
|
-
* @param {boolean} [options.minify=true] - Minify the CSS output
|
|
10371
|
-
* @returns {Element|null} Style element if in browser, null in Node.js
|
|
10372
|
-
* @category CSS & Styling
|
|
10373
|
-
* @see bw.setTheme
|
|
10374
|
-
* @see bw.applyTheme
|
|
10375
|
-
* @see bw.toggleTheme
|
|
10376
|
-
* @example
|
|
10377
|
-
* bw.loadDefaultStyles(); // inject all default CSS
|
|
10378
|
-
*/
|
|
10379
|
-
bw.loadDefaultStyles = function(options = {}) {
|
|
10380
|
-
const { minify = true, palette } = options;
|
|
10381
|
-
|
|
10382
|
-
// 1. Inject structural CSS (layout, sizing — never changes with theme)
|
|
10383
|
-
if (bw._isBrowser) {
|
|
10384
|
-
var structuralCSS = bw.css(getStructuralStyles());
|
|
10385
|
-
bw.injectCSS(structuralCSS, { id: 'bw_structural', append: false, minify: minify });
|
|
10386
|
-
}
|
|
10387
10773
|
|
|
10388
|
-
|
|
10389
|
-
|
|
10390
|
-
|
|
10391
|
-
return result;
|
|
10392
|
-
};
|
|
10774
|
+
// =========================================================================
|
|
10775
|
+
// v2.0.18 Clean Styles API — makeStyles / applyStyles / loadStyles / etc.
|
|
10776
|
+
// =========================================================================
|
|
10393
10777
|
|
|
10778
|
+
/**
|
|
10779
|
+
* Convert a scope selector to a <style> element id.
|
|
10780
|
+
* @private
|
|
10781
|
+
* @param {string} [scope] - Scope selector (e.g. '#my-dashboard', '.preview')
|
|
10782
|
+
* @returns {string} Style element id (e.g. 'bw_style_my_dashboard')
|
|
10783
|
+
*/
|
|
10784
|
+
function _scopeToStyleId(scope) {
|
|
10785
|
+
if (!scope || scope === '' || scope === 'global') return 'bw_style_global';
|
|
10786
|
+
if (scope === 'reset') return 'bw_style_reset';
|
|
10787
|
+
// Strip leading # or . and convert - to _
|
|
10788
|
+
var clean = scope.replace(/^[#.]/, '').replace(/-/g, '_');
|
|
10789
|
+
return 'bw_style_' + clean;
|
|
10790
|
+
}
|
|
10394
10791
|
|
|
10395
10792
|
/**
|
|
10396
|
-
* Generate a complete
|
|
10793
|
+
* Generate a complete styles object from seed colors and layout config.
|
|
10794
|
+
* Pure function — no DOM, no state, no side effects.
|
|
10397
10795
|
*
|
|
10398
|
-
*
|
|
10399
|
-
* forms, nav, tables, tabs, list groups, pagination, progress, hero, utilities)
|
|
10400
|
-
* scoped under `.name` class. Multiple themes can coexist in the stylesheet.
|
|
10401
|
-
* Swap themes by changing the class on a container element.
|
|
10796
|
+
* All parameters are optional. Defaults to the bitwrench default palette.
|
|
10402
10797
|
*
|
|
10403
|
-
* @param {
|
|
10404
|
-
* @param {
|
|
10405
|
-
* @param {string} config.
|
|
10406
|
-
* @param {string} config.
|
|
10407
|
-
* @param {string} [config.tertiary] - Tertiary/accent color hex (defaults to primary)
|
|
10408
|
-
* @param {string} [config.success='#198754'] - Success color hex
|
|
10409
|
-
* @param {string} [config.danger='#dc3545'] - Danger color hex
|
|
10410
|
-
* @param {string} [config.warning='#ffc107'] - Warning color hex
|
|
10411
|
-
* @param {string} [config.info='#0dcaf0'] - Info color hex
|
|
10412
|
-
* @param {string} [config.light='#f8f9fa'] - Light color hex
|
|
10413
|
-
* @param {string} [config.dark='#212529'] - Dark color hex
|
|
10414
|
-
* @param {string} [config.background] - Page background hex (default: '#ffffff' light, derived dark)
|
|
10415
|
-
* @param {string} [config.surface] - Surface/card background hex (default: '#f8f9fa' light, derived dark)
|
|
10798
|
+
* @param {Object} [config] - Style configuration
|
|
10799
|
+
* @param {string} [config.primary='#006666'] - Primary brand color hex
|
|
10800
|
+
* @param {string} [config.secondary='#6c757d'] - Secondary color hex
|
|
10801
|
+
* @param {string} [config.tertiary] - Tertiary color hex (defaults to primary)
|
|
10416
10802
|
* @param {string} [config.spacing='normal'] - 'compact' | 'normal' | 'spacious'
|
|
10417
10803
|
* @param {string} [config.radius='md'] - 'none' | 'sm' | 'md' | 'lg' | 'pill'
|
|
10418
|
-
* @
|
|
10419
|
-
* @param {string|number} [config.typeRatio='normal'] - 'tight' | 'normal' | 'relaxed' | 'dramatic' or a number
|
|
10420
|
-
* @param {string} [config.elevation='md'] - 'flat' | 'sm' | 'md' | 'lg'
|
|
10421
|
-
* @param {string} [config.motion='standard'] - 'reduced' | 'standard' | 'expressive'
|
|
10422
|
-
* @param {number} [config.harmonize=0.20] - 0-1, semantic color hue shift toward primary
|
|
10423
|
-
* @param {boolean} [config.inject=true] - Inject into DOM (browser only)
|
|
10424
|
-
* @returns {Object} { css, palette, name, isLightPrimary, alternate: { css, palette } }
|
|
10804
|
+
* @returns {Object} { css, alternateCss, rules, alternateRules, palette, alternatePalette, isLightPrimary }
|
|
10425
10805
|
* @category CSS & Styling
|
|
10426
|
-
* @see bw.
|
|
10427
|
-
* @see bw.
|
|
10428
|
-
* @see bw.loadDefaultStyles
|
|
10806
|
+
* @see bw.applyStyles
|
|
10807
|
+
* @see bw.loadStyles
|
|
10429
10808
|
* @example
|
|
10430
|
-
*
|
|
10431
|
-
*
|
|
10432
|
-
*
|
|
10433
|
-
* secondary: '#90e0ef',
|
|
10434
|
-
* tertiary: '#00b4d8'
|
|
10435
|
-
* });
|
|
10436
|
-
*
|
|
10437
|
-
* // Apply to a container
|
|
10438
|
-
* document.getElementById('app').classList.add('ocean');
|
|
10439
|
-
*
|
|
10440
|
-
* // Toggle to alternate palette
|
|
10441
|
-
* bw.toggleTheme();
|
|
10442
|
-
*
|
|
10443
|
-
* // Generate CSS for static export (Node.js)
|
|
10444
|
-
* var result = bw.generateTheme('sunset', {
|
|
10445
|
-
* primary: '#e76f51',
|
|
10446
|
-
* secondary: '#264653',
|
|
10447
|
-
* inject: false
|
|
10448
|
-
* });
|
|
10449
|
-
* fs.writeFileSync('sunset.css', result.css + result.alternate.css);
|
|
10809
|
+
* var styles = bw.makeStyles({ primary: '#4f46e5', secondary: '#d97706' });
|
|
10810
|
+
* console.log(styles.palette.primary.base); // '#4f46e5'
|
|
10811
|
+
* // styles.css contains all themed CSS — nothing injected
|
|
10450
10812
|
*/
|
|
10451
|
-
bw.
|
|
10452
|
-
|
|
10453
|
-
|
|
10454
|
-
}
|
|
10455
|
-
|
|
10456
|
-
// Merge with defaults; if user didn't supply tertiary, default to their primary
|
|
10457
|
-
var fullConfig = Object.assign({}, DEFAULT_PALETTE_CONFIG, config);
|
|
10458
|
-
if (!config.tertiary) fullConfig.tertiary = fullConfig.primary;
|
|
10813
|
+
bw.makeStyles = function(config) {
|
|
10814
|
+
var fullConfig = Object.assign({}, DEFAULT_PALETTE_CONFIG, config || {});
|
|
10815
|
+
if (config && !config.tertiary) fullConfig.tertiary = fullConfig.primary;
|
|
10459
10816
|
|
|
10460
10817
|
// Derive primary palette
|
|
10461
10818
|
var palette = derivePalette(fullConfig);
|
|
@@ -10463,131 +10820,207 @@ bw.generateTheme = function(name, config) {
|
|
|
10463
10820
|
// Resolve layout
|
|
10464
10821
|
var layout = resolveLayout(fullConfig);
|
|
10465
10822
|
|
|
10466
|
-
// Generate primary themed CSS rules
|
|
10467
|
-
var themedRules = generateThemedCSS(
|
|
10823
|
+
// Generate primary themed CSS rules (unscoped)
|
|
10824
|
+
var themedRules = generateThemedCSS('', palette, layout);
|
|
10468
10825
|
var cssStr = bw.css(themedRules);
|
|
10469
10826
|
|
|
10470
10827
|
// Derive alternate palette (luminance-inverted)
|
|
10471
10828
|
var altConfig = deriveAlternateConfig(fullConfig);
|
|
10472
10829
|
var altPalette = derivePalette(altConfig);
|
|
10473
10830
|
|
|
10474
|
-
// Generate alternate CSS
|
|
10475
|
-
|
|
10476
|
-
var
|
|
10831
|
+
// Generate alternate CSS rules WITHOUT .bw_theme_alt prefix (raw rules)
|
|
10832
|
+
// applyStyles() wraps them appropriately based on scope
|
|
10833
|
+
var altRawRules = generateThemedCSS('', altPalette, layout);
|
|
10834
|
+
|
|
10835
|
+
// Add body-level surface overrides for the alternate palette.
|
|
10836
|
+
// When .bw_theme_alt is on <html>, ".bw_theme_alt body" correctly matches.
|
|
10837
|
+
altRawRules['body'] = {
|
|
10838
|
+
'color': altPalette.dark.base,
|
|
10839
|
+
'background-color': altPalette.surface || altPalette.light.base
|
|
10840
|
+
};
|
|
10841
|
+
|
|
10842
|
+
var altCssStr = bw.css(altRawRules);
|
|
10477
10843
|
|
|
10478
10844
|
// Determine if primary is light-flavored
|
|
10479
10845
|
var lightPrimary = isLightPalette(fullConfig);
|
|
10480
10846
|
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
|
|
10487
|
-
|
|
10488
|
-
|
|
10489
|
-
|
|
10847
|
+
return {
|
|
10848
|
+
css: cssStr,
|
|
10849
|
+
alternateCss: altCssStr,
|
|
10850
|
+
rules: themedRules,
|
|
10851
|
+
alternateRules: altRawRules,
|
|
10852
|
+
palette: palette,
|
|
10853
|
+
alternatePalette: altPalette,
|
|
10854
|
+
isLightPrimary: lightPrimary
|
|
10855
|
+
};
|
|
10856
|
+
};
|
|
10490
10857
|
|
|
10491
|
-
|
|
10858
|
+
/**
|
|
10859
|
+
* Inject styles into the DOM with optional scoping.
|
|
10860
|
+
*
|
|
10861
|
+
* Takes a styles object from `makeStyles()` and creates a single `<style>`
|
|
10862
|
+
* element in `<head>`. If a scope selector is provided, all CSS rules are
|
|
10863
|
+
* wrapped under that selector. Alternate CSS is wrapped under `.bw_theme_alt`.
|
|
10864
|
+
*
|
|
10865
|
+
* @param {Object} styles - Result of `bw.makeStyles()`
|
|
10866
|
+
* @param {string} [scope] - Scope selector (e.g. '#my-dashboard', '.preview'). Omit for global.
|
|
10867
|
+
* @returns {Element|null} The `<style>` element, or null in Node.js
|
|
10868
|
+
* @category CSS & Styling
|
|
10869
|
+
* @see bw.makeStyles
|
|
10870
|
+
* @see bw.loadStyles
|
|
10871
|
+
* @see bw.clearStyles
|
|
10872
|
+
* @example
|
|
10873
|
+
* var styles = bw.makeStyles({ primary: '#4f46e5' });
|
|
10874
|
+
* bw.applyStyles(styles); // global
|
|
10875
|
+
* bw.applyStyles(styles, '#my-dashboard'); // scoped
|
|
10876
|
+
*/
|
|
10877
|
+
bw.applyStyles = function(styles, scope) {
|
|
10878
|
+
if (!bw._isBrowser) return null;
|
|
10879
|
+
if (!styles || !styles.rules) {
|
|
10880
|
+
_cw('bw.applyStyles: invalid styles object');
|
|
10881
|
+
return null;
|
|
10492
10882
|
}
|
|
10493
10883
|
|
|
10494
|
-
|
|
10495
|
-
|
|
10496
|
-
|
|
10497
|
-
|
|
10498
|
-
|
|
10499
|
-
|
|
10884
|
+
var styleId = _scopeToStyleId(scope);
|
|
10885
|
+
|
|
10886
|
+
// Scope the primary rules if a scope is provided
|
|
10887
|
+
var primaryRules = styles.rules;
|
|
10888
|
+
if (scope) {
|
|
10889
|
+
primaryRules = scopeRulesUnder(primaryRules, scope);
|
|
10500
10890
|
}
|
|
10501
10891
|
|
|
10502
|
-
//
|
|
10503
|
-
var
|
|
10504
|
-
|
|
10505
|
-
|
|
10506
|
-
|
|
10507
|
-
|
|
10508
|
-
|
|
10509
|
-
|
|
10510
|
-
|
|
10892
|
+
// Wrap alternate rules with .bw_theme_alt
|
|
10893
|
+
var altRules = styles.alternateRules;
|
|
10894
|
+
if (altRules) {
|
|
10895
|
+
if (scope) {
|
|
10896
|
+
// Scoped compound: #scope.bw_theme_alt .bw_card
|
|
10897
|
+
altRules = scopeRulesUnder(altRules, scope + '.bw_theme_alt');
|
|
10898
|
+
} else {
|
|
10899
|
+
// Global: .bw_theme_alt .bw_card
|
|
10900
|
+
altRules = scopeRulesUnder(altRules, '.bw_theme_alt');
|
|
10511
10901
|
}
|
|
10512
|
-
}
|
|
10513
|
-
bw._activeTheme = result;
|
|
10514
|
-
bw._activeThemeMode = 'primary';
|
|
10902
|
+
}
|
|
10515
10903
|
|
|
10516
|
-
|
|
10904
|
+
// Combine primary + alternate into one CSS string
|
|
10905
|
+
var combined = bw.css(primaryRules);
|
|
10906
|
+
if (altRules) {
|
|
10907
|
+
combined += '\n' + bw.css(altRules);
|
|
10908
|
+
}
|
|
10909
|
+
|
|
10910
|
+
return bw.injectCSS(combined, { id: styleId, append: false });
|
|
10517
10911
|
};
|
|
10518
10912
|
|
|
10519
10913
|
/**
|
|
10520
|
-
*
|
|
10521
|
-
*
|
|
10914
|
+
* Generate and apply styles in one call. Convenience wrapper.
|
|
10915
|
+
*
|
|
10916
|
+
* Equivalent to: `bw.applyStyles(bw.makeStyles(config), scope)`
|
|
10522
10917
|
*
|
|
10523
|
-
* @param {
|
|
10524
|
-
* @
|
|
10918
|
+
* @param {Object} [config] - Style configuration (same as `makeStyles`)
|
|
10919
|
+
* @param {string} [scope] - Scope selector (same as `applyStyles`)
|
|
10920
|
+
* @returns {Element|null} The `<style>` element, or null in Node.js
|
|
10525
10921
|
* @category CSS & Styling
|
|
10526
|
-
* @see bw.
|
|
10527
|
-
* @see bw.
|
|
10922
|
+
* @see bw.makeStyles
|
|
10923
|
+
* @see bw.applyStyles
|
|
10528
10924
|
* @example
|
|
10529
|
-
* bw.
|
|
10530
|
-
* bw.
|
|
10531
|
-
* bw.
|
|
10532
|
-
*/
|
|
10533
|
-
bw.
|
|
10534
|
-
|
|
10535
|
-
|
|
10536
|
-
|
|
10537
|
-
|
|
10538
|
-
|
|
10539
|
-
|
|
10540
|
-
|
|
10541
|
-
else if (mode === 'light') wantAlt = !isLight;
|
|
10542
|
-
else if (mode === 'dark') wantAlt = isLight;
|
|
10543
|
-
else wantAlt = false;
|
|
10544
|
-
|
|
10545
|
-
if (wantAlt) {
|
|
10546
|
-
root.classList.add('bw_theme_alt');
|
|
10547
|
-
} else {
|
|
10548
|
-
root.classList.remove('bw_theme_alt');
|
|
10925
|
+
* bw.loadStyles(); // defaults, global
|
|
10926
|
+
* bw.loadStyles({ primary: '#4f46e5' }); // custom, global
|
|
10927
|
+
* bw.loadStyles({ primary: '#4f46e5' }, '#my-dashboard'); // custom, scoped
|
|
10928
|
+
*/
|
|
10929
|
+
bw.loadStyles = function(config, scope) {
|
|
10930
|
+
// Also inject structural CSS first (only once)
|
|
10931
|
+
if (bw._isBrowser) {
|
|
10932
|
+
var existing = document.getElementById('bw_structural');
|
|
10933
|
+
if (!existing) {
|
|
10934
|
+
var structuralCSS = bw.css(getStructuralStyles());
|
|
10935
|
+
bw.injectCSS(structuralCSS, { id: 'bw_structural', append: false });
|
|
10936
|
+
}
|
|
10549
10937
|
}
|
|
10938
|
+
return bw.applyStyles(bw.makeStyles(config), scope);
|
|
10939
|
+
};
|
|
10550
10940
|
|
|
10551
|
-
|
|
10552
|
-
|
|
10941
|
+
/**
|
|
10942
|
+
* Inject the CSS reset (box-sizing, html/body font, reduced-motion).
|
|
10943
|
+
* Idempotent — if already injected, returns the existing `<style>` element.
|
|
10944
|
+
*
|
|
10945
|
+
* @returns {Element|null} The `<style>` element, or null in Node.js
|
|
10946
|
+
* @category CSS & Styling
|
|
10947
|
+
* @see bw.loadStyles
|
|
10948
|
+
* @see bw.clearStyles
|
|
10949
|
+
* @example
|
|
10950
|
+
* bw.loadReset(); // inject once, safe to call multiple times
|
|
10951
|
+
*/
|
|
10952
|
+
bw.loadReset = function() {
|
|
10953
|
+
if (!bw._isBrowser) return null;
|
|
10954
|
+
var existing = document.getElementById('bw_style_reset');
|
|
10955
|
+
if (existing) return existing;
|
|
10956
|
+
return bw.injectCSS(bw.css(getResetStyles()), { id: 'bw_style_reset', append: false });
|
|
10553
10957
|
};
|
|
10554
10958
|
|
|
10555
10959
|
/**
|
|
10556
|
-
* Toggle between primary and alternate
|
|
10960
|
+
* Toggle between primary and alternate palettes.
|
|
10961
|
+
*
|
|
10962
|
+
* Adds/removes the `bw_theme_alt` class on the scoping element.
|
|
10963
|
+
* Without a scope, toggles on `<html>` (global).
|
|
10964
|
+
* With a scope, toggles on the first matching element.
|
|
10557
10965
|
*
|
|
10966
|
+
* @param {string} [scope] - Scope selector (e.g. '#my-dashboard'). Omit for global.
|
|
10558
10967
|
* @returns {string} Active mode after toggle: 'primary' or 'alternate'
|
|
10559
10968
|
* @category CSS & Styling
|
|
10560
|
-
* @see bw.
|
|
10561
|
-
* @see bw.
|
|
10969
|
+
* @see bw.applyStyles
|
|
10970
|
+
* @see bw.clearStyles
|
|
10562
10971
|
* @example
|
|
10563
|
-
* bw.
|
|
10972
|
+
* bw.toggleStyles(); // global toggle on <html>
|
|
10973
|
+
* bw.toggleStyles('#my-dashboard'); // scoped toggle
|
|
10564
10974
|
*/
|
|
10565
|
-
bw.
|
|
10566
|
-
|
|
10567
|
-
|
|
10975
|
+
bw.toggleStyles = function(scope) {
|
|
10976
|
+
if (!bw._isBrowser) return 'primary';
|
|
10977
|
+
var target;
|
|
10978
|
+
if (scope) {
|
|
10979
|
+
var els = bw.$(scope);
|
|
10980
|
+
target = els[0];
|
|
10981
|
+
} else {
|
|
10982
|
+
target = document.documentElement;
|
|
10983
|
+
}
|
|
10984
|
+
if (!target) return 'primary';
|
|
10985
|
+
|
|
10986
|
+
var hasAlt = target.classList.contains('bw_theme_alt');
|
|
10987
|
+
if (hasAlt) {
|
|
10988
|
+
target.classList.remove('bw_theme_alt');
|
|
10989
|
+
return 'primary';
|
|
10990
|
+
} else {
|
|
10991
|
+
target.classList.add('bw_theme_alt');
|
|
10992
|
+
return 'alternate';
|
|
10993
|
+
}
|
|
10568
10994
|
};
|
|
10569
10995
|
|
|
10570
10996
|
/**
|
|
10571
|
-
* Remove
|
|
10572
|
-
*
|
|
10573
|
-
*
|
|
10997
|
+
* Remove injected styles for a given scope.
|
|
10998
|
+
*
|
|
10999
|
+
* Finds the `<style>` element by id and removes it. Also removes
|
|
11000
|
+
* the `bw_theme_alt` class from the relevant element.
|
|
10574
11001
|
*
|
|
11002
|
+
* @param {string} [scope] - Scope selector. Omit to remove global styles.
|
|
10575
11003
|
* @category CSS & Styling
|
|
10576
|
-
* @see bw.
|
|
11004
|
+
* @see bw.applyStyles
|
|
11005
|
+
* @see bw.loadStyles
|
|
10577
11006
|
* @example
|
|
10578
|
-
* bw.
|
|
10579
|
-
* bw.
|
|
10580
|
-
|
|
10581
|
-
|
|
10582
|
-
|
|
10583
|
-
|
|
10584
|
-
|
|
10585
|
-
|
|
10586
|
-
|
|
10587
|
-
|
|
11007
|
+
* bw.clearStyles(); // remove global styles
|
|
11008
|
+
* bw.clearStyles('#my-dashboard'); // remove scoped styles
|
|
11009
|
+
* bw.clearStyles('reset'); // remove the CSS reset
|
|
11010
|
+
*/
|
|
11011
|
+
bw.clearStyles = function(scope) {
|
|
11012
|
+
if (!bw._isBrowser) return;
|
|
11013
|
+
var styleId = _scopeToStyleId(scope);
|
|
11014
|
+
var el = document.getElementById(styleId);
|
|
11015
|
+
if (el) el.remove();
|
|
11016
|
+
|
|
11017
|
+
// Also remove bw_theme_alt from the relevant element
|
|
11018
|
+
if (scope && scope !== 'reset' && scope !== 'global') {
|
|
11019
|
+
var targets = bw.$(scope);
|
|
11020
|
+
if (targets[0]) targets[0].classList.remove('bw_theme_alt');
|
|
11021
|
+
} else if (!scope || scope === 'global') {
|
|
11022
|
+
document.documentElement.classList.remove('bw_theme_alt');
|
|
10588
11023
|
}
|
|
10589
|
-
bw._activeTheme = null;
|
|
10590
|
-
bw._activeThemeMode = 'primary';
|
|
10591
11024
|
};
|
|
10592
11025
|
|
|
10593
11026
|
// Expose color utility functions on bw namespace
|
|
@@ -10810,10 +11243,15 @@ bw.copyToClipboard = function(text) {
|
|
|
10810
11243
|
* @param {Object} config - Table configuration
|
|
10811
11244
|
* @param {Array<Object>} config.data - Array of row objects to display
|
|
10812
11245
|
* @param {Array<Object>} [config.columns] - Column definitions with key, label, render
|
|
10813
|
-
* @param {string} [config.className='
|
|
11246
|
+
* @param {string} [config.className=''] - Additional CSS classes for table element
|
|
10814
11247
|
* @param {boolean} [config.sortable=true] - Enable click-to-sort headers
|
|
10815
11248
|
* @param {Function} [config.onSort] - Sort callback (column, direction)
|
|
10816
|
-
* @
|
|
11249
|
+
* @param {boolean} [config.selectable=false] - Enable row selection on click
|
|
11250
|
+
* @param {Function} [config.onRowClick] - Row click callback (row, index, event)
|
|
11251
|
+
* @param {number} [config.pageSize] - Rows per page (enables pagination when set)
|
|
11252
|
+
* @param {number} [config.currentPage=1] - Current page number (1-based)
|
|
11253
|
+
* @param {Function} [config.onPageChange] - Page change callback (newPage)
|
|
11254
|
+
* @returns {Object} TACO object for table (with optional pagination controls)
|
|
10817
11255
|
* @category Component Builders
|
|
10818
11256
|
* @see bw.makeDataTable
|
|
10819
11257
|
* @example
|
|
@@ -10825,7 +11263,12 @@ bw.copyToClipboard = function(text) {
|
|
|
10825
11263
|
* columns: [
|
|
10826
11264
|
* { key: 'name', label: 'Name' },
|
|
10827
11265
|
* { key: 'age', label: 'Age' }
|
|
10828
|
-
* ]
|
|
11266
|
+
* ],
|
|
11267
|
+
* selectable: true,
|
|
11268
|
+
* onRowClick: function(row, i) { console.log('clicked', row.name); },
|
|
11269
|
+
* pageSize: 10,
|
|
11270
|
+
* currentPage: 1,
|
|
11271
|
+
* onPageChange: function(page) { console.log('page', page); }
|
|
10829
11272
|
* });
|
|
10830
11273
|
*/
|
|
10831
11274
|
bw.makeTable = function(config) {
|
|
@@ -10838,41 +11281,47 @@ bw.makeTable = function(config) {
|
|
|
10838
11281
|
sortable = true,
|
|
10839
11282
|
onSort,
|
|
10840
11283
|
sortColumn,
|
|
10841
|
-
sortDirection = 'asc'
|
|
11284
|
+
sortDirection = 'asc',
|
|
11285
|
+
selectable = false,
|
|
11286
|
+
onRowClick,
|
|
11287
|
+
pageSize,
|
|
11288
|
+
currentPage = 1,
|
|
11289
|
+
onPageChange
|
|
10842
11290
|
} = config;
|
|
10843
11291
|
|
|
10844
|
-
// Build class list: always include bw_table, add striped/hover, append user className
|
|
11292
|
+
// Build class list: always include bw_table, add striped/hover/selectable, append user className
|
|
10845
11293
|
let cls = 'bw_table';
|
|
10846
11294
|
if (striped) cls += ' bw_table_striped';
|
|
10847
|
-
if (hover) cls += ' bw_table_hover';
|
|
11295
|
+
if (hover || selectable) cls += ' bw_table_hover';
|
|
11296
|
+
if (selectable) cls += ' bw_table_selectable';
|
|
10848
11297
|
if (className) cls += ' ' + className;
|
|
10849
11298
|
cls = cls.trim();
|
|
10850
|
-
|
|
11299
|
+
|
|
10851
11300
|
// Auto-detect columns if not provided
|
|
10852
|
-
const cols = columns || (data.length > 0
|
|
10853
|
-
?
|
|
11301
|
+
const cols = columns || (data.length > 0
|
|
11302
|
+
? _keys(data[0]).map(key => ({ key, label: key }))
|
|
10854
11303
|
: []);
|
|
10855
|
-
|
|
11304
|
+
|
|
10856
11305
|
// Current sort state
|
|
10857
11306
|
let currentSortColumn = sortColumn || null;
|
|
10858
11307
|
let currentSortDirection = sortDirection;
|
|
10859
|
-
|
|
11308
|
+
|
|
10860
11309
|
// Sort data if column specified
|
|
10861
11310
|
let sortedData = [...data];
|
|
10862
11311
|
if (currentSortColumn) {
|
|
10863
11312
|
sortedData.sort((a, b) => {
|
|
10864
11313
|
const aVal = a[currentSortColumn];
|
|
10865
11314
|
const bVal = b[currentSortColumn];
|
|
10866
|
-
|
|
11315
|
+
|
|
10867
11316
|
// Handle different types
|
|
10868
|
-
if (
|
|
11317
|
+
if (_is(aVal, 'number') && _is(bVal, 'number')) {
|
|
10869
11318
|
return currentSortDirection === 'asc' ? aVal - bVal : bVal - aVal;
|
|
10870
11319
|
}
|
|
10871
|
-
|
|
11320
|
+
|
|
10872
11321
|
// String comparison
|
|
10873
11322
|
const aStr = String(aVal || '').toLowerCase();
|
|
10874
11323
|
const bStr = String(bVal || '').toLowerCase();
|
|
10875
|
-
|
|
11324
|
+
|
|
10876
11325
|
if (currentSortDirection === 'asc') {
|
|
10877
11326
|
return aStr.localeCompare(bStr);
|
|
10878
11327
|
} else {
|
|
@@ -10880,23 +11329,32 @@ bw.makeTable = function(config) {
|
|
|
10880
11329
|
}
|
|
10881
11330
|
});
|
|
10882
11331
|
}
|
|
10883
|
-
|
|
11332
|
+
|
|
11333
|
+
// Pagination
|
|
11334
|
+
const totalRows = sortedData.length;
|
|
11335
|
+
const totalPages = pageSize ? Math.max(1, Math.ceil(totalRows / pageSize)) : 1;
|
|
11336
|
+
const page = Math.max(1, Math.min(currentPage, totalPages));
|
|
11337
|
+
if (pageSize) {
|
|
11338
|
+
const start = (page - 1) * pageSize;
|
|
11339
|
+
sortedData = sortedData.slice(start, start + pageSize);
|
|
11340
|
+
}
|
|
11341
|
+
|
|
10884
11342
|
// Create sort handler
|
|
10885
11343
|
const handleSort = (column) => {
|
|
10886
11344
|
if (!sortable) return;
|
|
10887
|
-
|
|
11345
|
+
|
|
10888
11346
|
if (currentSortColumn === column) {
|
|
10889
11347
|
currentSortDirection = currentSortDirection === 'asc' ? 'desc' : 'asc';
|
|
10890
11348
|
} else {
|
|
10891
11349
|
currentSortColumn = column;
|
|
10892
11350
|
currentSortDirection = 'asc';
|
|
10893
11351
|
}
|
|
10894
|
-
|
|
11352
|
+
|
|
10895
11353
|
if (onSort) {
|
|
10896
11354
|
onSort(column, currentSortDirection);
|
|
10897
11355
|
}
|
|
10898
11356
|
};
|
|
10899
|
-
|
|
11357
|
+
|
|
10900
11358
|
// Build table header
|
|
10901
11359
|
const thead = {
|
|
10902
11360
|
t: 'thead',
|
|
@@ -10919,24 +11377,87 @@ bw.makeTable = function(config) {
|
|
|
10919
11377
|
}))
|
|
10920
11378
|
}
|
|
10921
11379
|
};
|
|
10922
|
-
|
|
10923
|
-
// Build table body
|
|
11380
|
+
|
|
11381
|
+
// Build table body with selectable/onRowClick support
|
|
10924
11382
|
const tbody = {
|
|
10925
11383
|
t: 'tbody',
|
|
10926
|
-
c: sortedData.map(row =>
|
|
10927
|
-
|
|
10928
|
-
|
|
10929
|
-
|
|
10930
|
-
|
|
10931
|
-
|
|
10932
|
-
|
|
11384
|
+
c: sortedData.map((row, idx) => {
|
|
11385
|
+
const globalIdx = pageSize ? (page - 1) * pageSize + idx : idx;
|
|
11386
|
+
const rowAttrs = {};
|
|
11387
|
+
if (selectable || onRowClick) {
|
|
11388
|
+
rowAttrs.style = 'cursor:pointer;';
|
|
11389
|
+
rowAttrs.onclick = function(e) {
|
|
11390
|
+
if (selectable) {
|
|
11391
|
+
// Toggle selected class on this row
|
|
11392
|
+
var tr = e.currentTarget;
|
|
11393
|
+
tr.classList.toggle('bw_table_row_selected');
|
|
11394
|
+
}
|
|
11395
|
+
if (onRowClick) {
|
|
11396
|
+
onRowClick(row, globalIdx, e);
|
|
11397
|
+
}
|
|
11398
|
+
};
|
|
11399
|
+
}
|
|
11400
|
+
return {
|
|
11401
|
+
t: 'tr',
|
|
11402
|
+
a: rowAttrs,
|
|
11403
|
+
c: cols.map(col => ({
|
|
11404
|
+
t: 'td',
|
|
11405
|
+
c: col.render ? col.render(row[col.key], row) : String(row[col.key] || '')
|
|
11406
|
+
}))
|
|
11407
|
+
};
|
|
11408
|
+
})
|
|
10933
11409
|
};
|
|
10934
|
-
|
|
10935
|
-
|
|
11410
|
+
|
|
11411
|
+
const table = {
|
|
10936
11412
|
t: 'table',
|
|
10937
11413
|
a: { class: cls },
|
|
10938
11414
|
c: [thead, tbody]
|
|
10939
11415
|
};
|
|
11416
|
+
|
|
11417
|
+
// If no pagination, return table directly
|
|
11418
|
+
if (!pageSize) return table;
|
|
11419
|
+
|
|
11420
|
+
// Build pagination controls
|
|
11421
|
+
const pageButtons = [];
|
|
11422
|
+
// Previous button
|
|
11423
|
+
pageButtons.push({
|
|
11424
|
+
t: 'button',
|
|
11425
|
+
a: {
|
|
11426
|
+
class: 'bw_btn bw_btn_sm',
|
|
11427
|
+
disabled: page <= 1 ? 'disabled' : undefined,
|
|
11428
|
+
onclick: page > 1 && onPageChange ? function() { onPageChange(page - 1); } : undefined
|
|
11429
|
+
},
|
|
11430
|
+
c: 'Prev'
|
|
11431
|
+
});
|
|
11432
|
+
// Page info
|
|
11433
|
+
pageButtons.push({
|
|
11434
|
+
t: 'span',
|
|
11435
|
+
a: { style: 'margin:0 0.5rem;font-size:0.875rem;' },
|
|
11436
|
+
c: 'Page ' + page + ' of ' + totalPages
|
|
11437
|
+
});
|
|
11438
|
+
// Next button
|
|
11439
|
+
pageButtons.push({
|
|
11440
|
+
t: 'button',
|
|
11441
|
+
a: {
|
|
11442
|
+
class: 'bw_btn bw_btn_sm',
|
|
11443
|
+
disabled: page >= totalPages ? 'disabled' : undefined,
|
|
11444
|
+
onclick: page < totalPages && onPageChange ? function() { onPageChange(page + 1); } : undefined
|
|
11445
|
+
},
|
|
11446
|
+
c: 'Next'
|
|
11447
|
+
});
|
|
11448
|
+
|
|
11449
|
+
return {
|
|
11450
|
+
t: 'div',
|
|
11451
|
+
a: { class: 'bw_table_paginated' },
|
|
11452
|
+
c: [
|
|
11453
|
+
table,
|
|
11454
|
+
{
|
|
11455
|
+
t: 'div',
|
|
11456
|
+
a: { class: 'bw_table_pagination', style: 'display:flex;align-items:center;justify-content:flex-end;padding:0.5rem 0;gap:0.25rem;' },
|
|
11457
|
+
c: pageButtons
|
|
11458
|
+
}
|
|
11459
|
+
]
|
|
11460
|
+
};
|
|
10940
11461
|
};
|
|
10941
11462
|
|
|
10942
11463
|
/**
|
|
@@ -10975,7 +11496,7 @@ bw.makeTable = function(config) {
|
|
|
10975
11496
|
bw.makeTableFromArray = function(config) {
|
|
10976
11497
|
const { data = [], headerRow = true, columns, ...rest } = config;
|
|
10977
11498
|
|
|
10978
|
-
if (!
|
|
11499
|
+
if (!_isA(data) || data.length === 0) {
|
|
10979
11500
|
return bw.makeTable({ data: [], columns: columns || [], ...rest });
|
|
10980
11501
|
}
|
|
10981
11502
|
|
|
@@ -11057,7 +11578,7 @@ bw.makeBarChart = function(config) {
|
|
|
11057
11578
|
className = ''
|
|
11058
11579
|
} = config;
|
|
11059
11580
|
|
|
11060
|
-
if (!
|
|
11581
|
+
if (!_isA(data) || data.length === 0) {
|
|
11061
11582
|
return { t: 'div', a: { class: ('bw_bar_chart_container ' + className).trim() }, c: '' };
|
|
11062
11583
|
}
|
|
11063
11584
|
|
|
@@ -11206,7 +11727,7 @@ bw._componentRegistry = new Map();
|
|
|
11206
11727
|
*/
|
|
11207
11728
|
bw.render = function(element, position, taco) {
|
|
11208
11729
|
// Get target element
|
|
11209
|
-
const targetEl =
|
|
11730
|
+
const targetEl = _is(element, 'string')
|
|
11210
11731
|
? document.querySelector(element)
|
|
11211
11732
|
: element;
|
|
11212
11733
|
|
|
@@ -11356,7 +11877,7 @@ bw.render = function(element, position, taco) {
|
|
|
11356
11877
|
setContent(content) {
|
|
11357
11878
|
this._taco.c = content;
|
|
11358
11879
|
if (this.element) {
|
|
11359
|
-
if (
|
|
11880
|
+
if (_is(content, 'string')) {
|
|
11360
11881
|
this.element.textContent = content;
|
|
11361
11882
|
} else {
|
|
11362
11883
|
// Re-render for complex content
|
|
@@ -11514,5 +12035,5 @@ if (bw._isBrowser && typeof window !== 'undefined') {
|
|
|
11514
12035
|
window.bw = bw;
|
|
11515
12036
|
}
|
|
11516
12037
|
|
|
11517
|
-
export { bw as default };
|
|
12038
|
+
export { BCCL, bw as default, make, makeAccordion, makeAlert, makeAvatar, makeBadge, makeBreadcrumb, makeButton, makeButtonGroup, makeCTA, makeCard, makeCarousel, makeCheckbox, makeChipInput, makeCodeDemo, makeCol, makeContainer, makeDropdown, makeFeatureGrid, makeFileUpload, makeForm, makeFormGroup, makeHero, makeInput, makeListGroup, makeMediaObject, makeModal, makeNav, makeNavbar, makePagination, makePopover, makeProgress, makeRadio, makeRange, makeRow, makeSearchInput, makeSection, makeSelect, makeSkeleton, makeSpinner, makeStack, makeStatCard, makeStepper, makeSwitch, makeTabs, makeTextarea, makeTimeline, makeToast, makeTooltip, variantClass };
|
|
11518
12039
|
//# sourceMappingURL=bitwrench.esm.js.map
|