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