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