bitwrench 2.0.17 → 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 +8 -8
- package/dist/bitwrench-bccl.cjs.min.js +3 -3
- package/dist/bitwrench-bccl.esm.js +8 -8
- package/dist/bitwrench-bccl.esm.min.js +3 -3
- package/dist/bitwrench-bccl.umd.js +8 -8
- 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 +941 -775
- package/dist/bitwrench-lean.cjs.min.js +20 -20
- package/dist/bitwrench-lean.es5.js +1012 -961
- package/dist/bitwrench-lean.es5.min.js +18 -18
- package/dist/bitwrench-lean.esm.js +941 -775
- package/dist/bitwrench-lean.esm.min.js +20 -20
- package/dist/bitwrench-lean.umd.js +941 -775
- 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 +948 -782
- package/dist/bitwrench.cjs.min.js +21 -21
- package/dist/bitwrench.css +456 -132
- package/dist/bitwrench.es5.js +1024 -970
- package/dist/bitwrench.es5.min.js +19 -19
- package/dist/bitwrench.esm.js +949 -783
- package/dist/bitwrench.esm.min.js +21 -21
- package/dist/bitwrench.min.css +1 -1
- package/dist/bitwrench.umd.js +948 -782
- package/dist/bitwrench.umd.min.js +21 -21
- package/dist/builds.json +178 -90
- package/dist/bwserve.cjs.js +514 -68
- package/dist/bwserve.esm.js +513 -69
- package/dist/sri.json +44 -36
- package/package.json +3 -2
- package/readme.html +136 -49
- package/src/bitwrench-bccl.js +7 -7
- 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 +483 -485
- 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 +127 -28
- 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/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 -106
package/dist/bitwrench.umd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! bitwrench v2.0.
|
|
1
|
+
/*! bitwrench v2.0.18 | BSD-2-Clause | https://deftio.github.com/bitwrench/pages */
|
|
2
2
|
(function (global, factory) {
|
|
3
3
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
4
4
|
typeof define === 'function' && define.amd ? define(factory) :
|
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const VERSION_INFO = {
|
|
15
|
-
version: '2.0.
|
|
15
|
+
version: '2.0.18',
|
|
16
16
|
name: 'bitwrench',
|
|
17
17
|
description: 'A library for javascript UI functions.',
|
|
18
18
|
license: 'BSD-2-Clause',
|
|
19
19
|
homepage: 'https://deftio.github.com/bitwrench/pages',
|
|
20
20
|
repository: 'git+https://github.com/deftio/bitwrench.git',
|
|
21
21
|
author: 'manu a. chatterjee <deftio@deftio.com> (https://deftio.com/)',
|
|
22
|
-
buildDate: '2026-03-
|
|
22
|
+
buildDate: '2026-03-17T00:50:09.505Z'
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -313,13 +313,18 @@
|
|
|
313
313
|
*/
|
|
314
314
|
function deriveShades(hex) {
|
|
315
315
|
var rgb = colorParse(hex);
|
|
316
|
+
// For light input colors (L > 75), mixing toward white produces invisible borders.
|
|
317
|
+
// Darken instead so borders remain visible against light backgrounds.
|
|
318
|
+
var borderColor = hexToHsl(hex)[2] > 75
|
|
319
|
+
? adjustLightness(hex, -18)
|
|
320
|
+
: mixColor(hex, '#ffffff', 0.60);
|
|
316
321
|
return {
|
|
317
322
|
base: hex,
|
|
318
323
|
hover: adjustLightness(hex, -10),
|
|
319
324
|
active: adjustLightness(hex, -15),
|
|
320
325
|
light: mixColor(hex, '#ffffff', 0.85),
|
|
321
326
|
darkText: adjustLightness(hex, -40),
|
|
322
|
-
border:
|
|
327
|
+
border: borderColor,
|
|
323
328
|
focus: 'rgba(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ',0.25)',
|
|
324
329
|
textOn: textOnColor(hex)
|
|
325
330
|
};
|
|
@@ -378,19 +383,27 @@
|
|
|
378
383
|
alt.secondary = deriveAlternateSeed(config.secondary);
|
|
379
384
|
alt.tertiary = config.tertiary ? deriveAlternateSeed(config.tertiary) : alt.primary;
|
|
380
385
|
|
|
381
|
-
// Derive alternate surface colors from primary hue
|
|
386
|
+
// Derive alternate surface colors from primary hue.
|
|
387
|
+
// Check actual page surface brightness (not seed color brightness) to decide
|
|
388
|
+
// whether alternate should be dark or light. The page surface is what the
|
|
389
|
+
// user sees; seeds can be dark while the page is still light (default L=96).
|
|
382
390
|
var priHsl = hexToHsl(config.primary);
|
|
383
391
|
var h = priHsl[0];
|
|
384
|
-
var
|
|
392
|
+
var primarySurface = config.surface || hslToHex([h, 8, 96]);
|
|
393
|
+
var isLight = relativeLuminance(primarySurface) > 0.179;
|
|
385
394
|
|
|
386
395
|
if (isLight) {
|
|
387
|
-
//
|
|
396
|
+
// Page surface is light → alternate needs dark surfaces
|
|
388
397
|
alt.light = hslToHex([h, Math.min(priHsl[1], 15), 15]);
|
|
389
398
|
alt.dark = hslToHex([h, 5, 88]);
|
|
399
|
+
alt.surface = hslToHex([h, 12, 18]);
|
|
400
|
+
alt.background = hslToHex([h, 10, 14]);
|
|
390
401
|
} else {
|
|
391
|
-
//
|
|
402
|
+
// Page surface is dark → alternate needs light surfaces
|
|
392
403
|
alt.light = hslToHex([h, Math.min(priHsl[1], 10), 96]);
|
|
393
404
|
alt.dark = hslToHex([h, 10, 18]);
|
|
405
|
+
alt.surface = hslToHex([h, 8, 96]);
|
|
406
|
+
alt.background = hslToHex([h, 6, 98]);
|
|
394
407
|
}
|
|
395
408
|
|
|
396
409
|
// Semantic colors: harmonize toward primary, then invert for alternate
|
|
@@ -438,10 +451,18 @@
|
|
|
438
451
|
var darkBase = config.dark || hslToHex([h, 10, 13]);
|
|
439
452
|
|
|
440
453
|
// Background & surface tokens — tinted with primary hue for theme personality.
|
|
441
|
-
//
|
|
454
|
+
// Saturation high enough that the hue is visible (each theme feels distinct)
|
|
455
|
+
// but low enough to stay neutral and readable.
|
|
442
456
|
// User can override with config.background / config.surface.
|
|
443
|
-
var bgBase = config.background || hslToHex([h,
|
|
444
|
-
var surfBase = config.surface || hslToHex([h,
|
|
457
|
+
var bgBase = config.background || hslToHex([h, 22, 96]);
|
|
458
|
+
var surfBase = config.surface || hslToHex([h, 25, 94]);
|
|
459
|
+
|
|
460
|
+
// surfaceAlt: subtle background variant for striped rows, hover states, headers.
|
|
461
|
+
// Slightly lighter than surface in dark mode, slightly darker in light mode.
|
|
462
|
+
var surfHsl = hexToHsl(surfBase);
|
|
463
|
+
var surfAlt = surfHsl[2] <= 50
|
|
464
|
+
? hslToHex([surfHsl[0], surfHsl[1], Math.min(surfHsl[2] + 8, 100)])
|
|
465
|
+
: hslToHex([surfHsl[0], surfHsl[1], Math.max(surfHsl[2] - 3, 0)]);
|
|
445
466
|
|
|
446
467
|
var palette = {
|
|
447
468
|
primary: deriveShades(config.primary),
|
|
@@ -454,7 +475,8 @@
|
|
|
454
475
|
light: deriveShades(lightBase),
|
|
455
476
|
dark: deriveShades(darkBase),
|
|
456
477
|
background: bgBase,
|
|
457
|
-
surface: surfBase
|
|
478
|
+
surface: surfBase,
|
|
479
|
+
surfaceAlt: surfAlt
|
|
458
480
|
};
|
|
459
481
|
|
|
460
482
|
return palette;
|
|
@@ -501,10 +523,12 @@
|
|
|
501
523
|
5: '1.5rem', // 24px
|
|
502
524
|
6: '2rem'};
|
|
503
525
|
|
|
526
|
+
let _S=SPACING_SCALE;
|
|
527
|
+
|
|
504
528
|
var SPACING_PRESETS = {
|
|
505
|
-
compact: { btn:
|
|
506
|
-
normal: { btn:
|
|
507
|
-
spacious: { btn:
|
|
529
|
+
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] },
|
|
530
|
+
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] },
|
|
531
|
+
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] }
|
|
508
532
|
};
|
|
509
533
|
|
|
510
534
|
var RADIUS_PRESETS = {
|
|
@@ -616,20 +640,14 @@
|
|
|
616
640
|
* Built-in theme presets — named color combinations
|
|
617
641
|
* Each preset provides primary, secondary, and tertiary seed colors.
|
|
618
642
|
*/
|
|
619
|
-
var THEME_PRESETS =
|
|
620
|
-
teal
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
amber: { primary: '#d97706', secondary: '#fbbf24', tertiary: '#f59e0b' },
|
|
628
|
-
emerald: { primary: '#059669', secondary: '#6ee7b7', tertiary: '#34d399' },
|
|
629
|
-
nord: { primary: '#5e81ac', secondary: '#88c0d0', tertiary: '#81a1c1' },
|
|
630
|
-
coral: { primary: '#ef6461', secondary: '#4a7c7e', tertiary: '#e8a87c' },
|
|
631
|
-
midnight: { primary: '#1e3a5f', secondary: '#7c8db5', tertiary: '#3d5a80' }
|
|
632
|
-
};
|
|
643
|
+
var THEME_PRESETS = Object.fromEntries([
|
|
644
|
+
['teal','#006666','#6c757d','#006666'],['ocean','#0077b6','#90e0ef','#00b4d8'],
|
|
645
|
+
['sunset','#e76f51','#264653','#e9c46a'],['forest','#2d6a4f','#95d5b2','#52b788'],
|
|
646
|
+
['slate','#343a40','#adb5bd','#6c757d'],['rose','#e11d48','#fda4af','#fb7185'],
|
|
647
|
+
['indigo','#4f46e5','#a5b4fc','#818cf8'],['amber','#d97706','#fbbf24','#f59e0b'],
|
|
648
|
+
['emerald','#059669','#6ee7b7','#34d399'],['nord','#5e81ac','#88c0d0','#81a1c1'],
|
|
649
|
+
['coral','#ef6461','#4a7c7e','#e8a87c'],['midnight','#1e3a5f','#7c8db5','#3d5a80']
|
|
650
|
+
].map(function(e) { return [e[0], {primary:e[1],secondary:e[2],tertiary:e[3]}]; }));
|
|
633
651
|
|
|
634
652
|
/**
|
|
635
653
|
* Resolve layout config to spacing, radius, typeScale, elevation, and motion objects.
|
|
@@ -676,6 +694,7 @@
|
|
|
676
694
|
if (sel.includes(',')) return sel.split(',').map(function(s) { return '.' + name + ' ' + s.trim(); }).join(', ');
|
|
677
695
|
return '.' + name + ' ' + sel;
|
|
678
696
|
}
|
|
697
|
+
var _sx=scopeSelector;
|
|
679
698
|
|
|
680
699
|
// =========================================================================
|
|
681
700
|
// Themed CSS generators
|
|
@@ -684,12 +703,12 @@
|
|
|
684
703
|
function generateTypographyThemed(scope, palette, layout) {
|
|
685
704
|
var mot = layout.motion;
|
|
686
705
|
var rules = {};
|
|
687
|
-
rules[
|
|
706
|
+
rules[_sx(scope, 'a')] = {
|
|
688
707
|
'color': palette.primary.base,
|
|
689
708
|
'text-decoration': 'none',
|
|
690
709
|
'transition': 'color ' + mot.fast + ' ' + mot.easing
|
|
691
710
|
};
|
|
692
|
-
rules[
|
|
711
|
+
rules[_sx(scope, 'a:hover')] = {
|
|
693
712
|
'color': palette.primary.hover,
|
|
694
713
|
'text-decoration': 'underline'
|
|
695
714
|
};
|
|
@@ -702,11 +721,11 @@
|
|
|
702
721
|
var rd = layout.radius;
|
|
703
722
|
|
|
704
723
|
// Base button (only when scoped — unscoped uses defaultStyles)
|
|
705
|
-
rules[
|
|
724
|
+
rules[_sx(scope, '.bw_btn')] = {
|
|
706
725
|
'padding': sp.btn,
|
|
707
726
|
'border-radius': rd.btn
|
|
708
727
|
};
|
|
709
|
-
rules[
|
|
728
|
+
rules[_sx(scope, '.bw_btn:focus-visible')] = {
|
|
710
729
|
'outline': '2px solid currentColor',
|
|
711
730
|
'outline-offset': '2px',
|
|
712
731
|
'box-shadow': '0 0 0 3px ' + palette.primary.focus
|
|
@@ -715,12 +734,12 @@
|
|
|
715
734
|
// Variant colors handled by palette class on component root
|
|
716
735
|
|
|
717
736
|
// Size variants (structural, reuse layout radius)
|
|
718
|
-
rules[
|
|
737
|
+
rules[_sx(scope, '.bw_btn_lg')] = {
|
|
719
738
|
'padding': '0.625rem 1.5rem',
|
|
720
739
|
'font-size': '1rem',
|
|
721
740
|
'border-radius': rd.btn === '50rem' ? '50rem' : (parseInt(rd.btn) + 2) + 'px'
|
|
722
741
|
};
|
|
723
|
-
rules[
|
|
742
|
+
rules[_sx(scope, '.bw_btn_sm')] = {
|
|
724
743
|
'padding': '0.25rem 0.75rem',
|
|
725
744
|
'font-size': '0.8125rem',
|
|
726
745
|
'border-radius': rd.btn === '50rem' ? '50rem' : (Math.max(parseInt(rd.btn) - 1, 0)) + 'px'
|
|
@@ -734,7 +753,7 @@
|
|
|
734
753
|
var sp = layout.spacing;
|
|
735
754
|
var rd = layout.radius;
|
|
736
755
|
|
|
737
|
-
rules[
|
|
756
|
+
rules[_sx(scope, '.bw_alert')] = {
|
|
738
757
|
'padding': sp.alert,
|
|
739
758
|
'border-radius': rd.alert
|
|
740
759
|
};
|
|
@@ -753,36 +772,36 @@
|
|
|
753
772
|
|
|
754
773
|
var elev = layout.elevation;
|
|
755
774
|
var motion = layout.motion;
|
|
756
|
-
rules[
|
|
775
|
+
rules[_sx(scope, '.bw_card')] = {
|
|
757
776
|
'background-color': palette.surface || '#fff',
|
|
758
777
|
'border': '1px solid ' + palette.light.border,
|
|
759
778
|
'border-radius': rd.card,
|
|
760
779
|
'box-shadow': elev.sm,
|
|
761
780
|
'transition': 'box-shadow ' + motion.normal + ' ' + motion.easing + ', transform ' + motion.normal + ' ' + motion.easing
|
|
762
781
|
};
|
|
763
|
-
rules[
|
|
782
|
+
rules[_sx(scope, '.bw_card:hover')] = {
|
|
764
783
|
'box-shadow': elev.md
|
|
765
784
|
};
|
|
766
|
-
rules[
|
|
785
|
+
rules[_sx(scope, '.bw_card_hoverable:hover')] = {
|
|
767
786
|
'box-shadow': elev.lg
|
|
768
787
|
};
|
|
769
|
-
rules[
|
|
788
|
+
rules[_sx(scope, '.bw_card_body')] = {
|
|
770
789
|
'padding': sp.card
|
|
771
790
|
};
|
|
772
|
-
rules[
|
|
791
|
+
rules[_sx(scope, '.bw_card_header')] = {
|
|
773
792
|
'padding': sp.card.split(' ').map(function(v) { return (parseFloat(v) * 0.7).toFixed(3).replace(/\.?0+$/, '') + 'rem'; }).join(' '),
|
|
774
|
-
'background-color': palette.
|
|
793
|
+
'background-color': palette.surfaceAlt,
|
|
775
794
|
'border-bottom': '1px solid ' + palette.light.border
|
|
776
795
|
};
|
|
777
|
-
rules[
|
|
778
|
-
'background-color': palette.
|
|
796
|
+
rules[_sx(scope, '.bw_card_footer')] = {
|
|
797
|
+
'background-color': palette.surfaceAlt,
|
|
779
798
|
'border-top': '1px solid ' + palette.light.border,
|
|
780
799
|
'color': palette.secondary.base
|
|
781
800
|
};
|
|
782
|
-
rules[
|
|
801
|
+
rules[_sx(scope, '.bw_card_title')] = {
|
|
783
802
|
'color': palette.dark.base
|
|
784
803
|
};
|
|
785
|
-
rules[
|
|
804
|
+
rules[_sx(scope, '.bw_card_subtitle')] = {
|
|
786
805
|
'color': palette.secondary.base
|
|
787
806
|
};
|
|
788
807
|
|
|
@@ -796,55 +815,55 @@
|
|
|
796
815
|
var sp = layout.spacing;
|
|
797
816
|
var rd = layout.radius;
|
|
798
817
|
|
|
799
|
-
rules[
|
|
818
|
+
rules[_sx(scope, '.bw_form_control')] = {
|
|
800
819
|
'padding': sp.input,
|
|
801
820
|
'border-radius': rd.input,
|
|
802
821
|
'color': palette.dark.base,
|
|
803
822
|
'background-color': palette.surface || '#fff',
|
|
804
823
|
'border-color': palette.light.border
|
|
805
824
|
};
|
|
806
|
-
rules[
|
|
825
|
+
rules[_sx(scope, '.bw_form_control:focus')] = {
|
|
807
826
|
'border-color': palette.primary.border,
|
|
808
827
|
'outline': '2px solid ' + palette.primary.base,
|
|
809
828
|
'outline-offset': '-1px',
|
|
810
829
|
'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus
|
|
811
830
|
};
|
|
812
|
-
rules[
|
|
831
|
+
rules[_sx(scope, '.bw_form_control::placeholder')] = {
|
|
813
832
|
'color': palette.secondary.base
|
|
814
833
|
};
|
|
815
|
-
rules[
|
|
834
|
+
rules[_sx(scope, '.bw_form_label')] = {
|
|
816
835
|
'color': palette.dark.base
|
|
817
836
|
};
|
|
818
|
-
rules[
|
|
837
|
+
rules[_sx(scope, '.bw_form_text')] = {
|
|
819
838
|
'color': palette.secondary.base
|
|
820
839
|
};
|
|
821
|
-
rules[
|
|
840
|
+
rules[_sx(scope, '.bw_form_check_input:checked')] = {
|
|
822
841
|
'background-color': palette.primary.base,
|
|
823
842
|
'border-color': palette.primary.base
|
|
824
843
|
};
|
|
825
|
-
rules[
|
|
844
|
+
rules[_sx(scope, '.bw_form_check_input:focus')] = {
|
|
826
845
|
'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus
|
|
827
846
|
};
|
|
828
847
|
// Validation states
|
|
829
|
-
rules[
|
|
830
|
-
rules[
|
|
848
|
+
rules[_sx(scope, '.bw_form_control.bw_is_valid')] = { 'border-color': palette.success.base };
|
|
849
|
+
rules[_sx(scope, '.bw_form_control.bw_is_valid:focus')] = {
|
|
831
850
|
'border-color': palette.success.base,
|
|
832
851
|
'box-shadow': '0 0 0 0.2rem ' + palette.success.focus
|
|
833
852
|
};
|
|
834
|
-
rules[
|
|
835
|
-
rules[
|
|
853
|
+
rules[_sx(scope, '.bw_form_control.bw_is_invalid')] = { 'border-color': palette.danger.base };
|
|
854
|
+
rules[_sx(scope, '.bw_form_control.bw_is_invalid:focus')] = {
|
|
836
855
|
'border-color': palette.danger.base,
|
|
837
856
|
'box-shadow': '0 0 0 0.2rem ' + palette.danger.focus
|
|
838
857
|
};
|
|
839
858
|
// Form select
|
|
840
|
-
rules[
|
|
859
|
+
rules[_sx(scope, '.bw_form_select')] = {
|
|
841
860
|
'padding': sp.input,
|
|
842
861
|
'border-radius': rd.input,
|
|
843
862
|
'color': palette.dark.base,
|
|
844
863
|
'background-color': palette.surface || '#fff',
|
|
845
864
|
'border-color': palette.light.border
|
|
846
865
|
};
|
|
847
|
-
rules[
|
|
866
|
+
rules[_sx(scope, '.bw_form_select:focus')] = {
|
|
848
867
|
'border-color': palette.primary.border,
|
|
849
868
|
'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus
|
|
850
869
|
};
|
|
@@ -852,43 +871,46 @@
|
|
|
852
871
|
return rules;
|
|
853
872
|
}
|
|
854
873
|
|
|
855
|
-
function generateNavigation(scope, palette) {
|
|
874
|
+
function generateNavigation(scope, palette, layout) {
|
|
856
875
|
var rules = {};
|
|
857
|
-
rules[
|
|
858
|
-
'background-color': palette.
|
|
876
|
+
rules[_sx(scope, '.bw_navbar')] = {
|
|
877
|
+
'background-color': palette.surfaceAlt,
|
|
859
878
|
'border-bottom-color': palette.light.border
|
|
860
879
|
};
|
|
861
|
-
rules[
|
|
880
|
+
rules[_sx(scope, '.bw_navbar_brand')] = {
|
|
862
881
|
'color': palette.dark.base
|
|
863
882
|
};
|
|
864
|
-
rules[
|
|
865
|
-
'color': palette.secondary.base
|
|
883
|
+
rules[_sx(scope, '.bw_navbar_nav .bw_nav_link')] = {
|
|
884
|
+
'color': palette.secondary.base,
|
|
885
|
+
'border-radius': layout.radius.btn
|
|
866
886
|
};
|
|
867
|
-
rules[
|
|
868
|
-
'color': palette.dark.base
|
|
887
|
+
rules[_sx(scope, '.bw_navbar_nav .bw_nav_link:hover')] = {
|
|
888
|
+
'color': palette.dark.base,
|
|
889
|
+
'background-color': palette.surfaceAlt
|
|
869
890
|
};
|
|
870
|
-
rules[
|
|
891
|
+
rules[_sx(scope, '.bw_navbar_nav .bw_nav_link.active')] = {
|
|
871
892
|
'color': palette.primary.base,
|
|
872
|
-
'background-color': palette.primary.focus
|
|
893
|
+
'background-color': palette.primary.focus,
|
|
894
|
+
'font-weight': '600'
|
|
873
895
|
};
|
|
874
|
-
rules[
|
|
896
|
+
rules[_sx(scope, '.bw_navbar_dark')] = {
|
|
875
897
|
'background-color': palette.dark.base,
|
|
876
898
|
'border-bottom-color': palette.dark.hover
|
|
877
899
|
};
|
|
878
|
-
rules[
|
|
900
|
+
rules[_sx(scope, '.bw_navbar_dark .bw_navbar_brand')] = {
|
|
879
901
|
'color': palette.light.base
|
|
880
902
|
};
|
|
881
|
-
rules[
|
|
882
|
-
'color':
|
|
903
|
+
rules[_sx(scope, '.bw_navbar_dark .bw_nav_link')] = {
|
|
904
|
+
'color': palette.light.border
|
|
883
905
|
};
|
|
884
|
-
rules[
|
|
885
|
-
'color':
|
|
906
|
+
rules[_sx(scope, '.bw_navbar_dark .bw_nav_link:hover')] = {
|
|
907
|
+
'color': palette.light.base
|
|
886
908
|
};
|
|
887
|
-
rules[
|
|
888
|
-
'color':
|
|
909
|
+
rules[_sx(scope, '.bw_navbar_dark .bw_nav_link.active')] = {
|
|
910
|
+
'color': palette.light.base,
|
|
889
911
|
'font-weight': '600'
|
|
890
912
|
};
|
|
891
|
-
rules[
|
|
913
|
+
rules[_sx(scope, '.bw_nav_pills .bw_nav_link.active')] = {
|
|
892
914
|
'color': palette.primary.textOn,
|
|
893
915
|
'background-color': palette.primary.base
|
|
894
916
|
};
|
|
@@ -899,49 +921,58 @@
|
|
|
899
921
|
var rules = {};
|
|
900
922
|
var sp = layout.spacing;
|
|
901
923
|
|
|
902
|
-
rules[
|
|
924
|
+
rules[_sx(scope, '.bw_table')] = {
|
|
903
925
|
'color': palette.dark.base,
|
|
904
926
|
'border-color': palette.light.border
|
|
905
927
|
};
|
|
906
|
-
rules[
|
|
928
|
+
rules[_sx(scope, '.bw_table > :not(caption) > * > *')] = {
|
|
907
929
|
'padding': sp.cell,
|
|
908
930
|
'border-bottom-color': palette.light.border
|
|
909
931
|
};
|
|
910
|
-
rules[
|
|
932
|
+
rules[_sx(scope, '.bw_table > thead > tr > *')] = {
|
|
911
933
|
'color': palette.secondary.base,
|
|
912
934
|
'border-bottom-color': palette.light.border,
|
|
913
|
-
'background-color': palette.
|
|
935
|
+
'background-color': palette.surfaceAlt
|
|
914
936
|
};
|
|
915
|
-
rules[
|
|
916
|
-
'background-color':
|
|
937
|
+
rules[_sx(scope, '.bw_table_striped > tbody > tr:nth-of-type(odd) > *')] = {
|
|
938
|
+
'background-color': palette.surfaceAlt
|
|
917
939
|
};
|
|
918
|
-
rules[
|
|
940
|
+
rules[_sx(scope, '.bw_table_hover > tbody > tr:hover > *')] = {
|
|
919
941
|
'background-color': palette.primary.focus
|
|
920
942
|
};
|
|
921
|
-
rules[
|
|
943
|
+
rules[_sx(scope, '.bw_table_selectable > tbody > tr')] = {
|
|
944
|
+
'cursor': 'pointer'
|
|
945
|
+
};
|
|
946
|
+
rules[_sx(scope, '.bw_table > tbody > tr.bw_table_row_selected > *')] = {
|
|
947
|
+
'background-color': palette.primary.light
|
|
948
|
+
};
|
|
949
|
+
rules[_sx(scope, '.bw_table_bordered')] = {
|
|
922
950
|
'border-color': palette.light.border
|
|
923
951
|
};
|
|
924
|
-
rules[
|
|
952
|
+
rules[_sx(scope, '.bw_table caption')] = {
|
|
925
953
|
'color': palette.secondary.base
|
|
926
954
|
};
|
|
927
955
|
|
|
928
956
|
return rules;
|
|
929
957
|
}
|
|
930
958
|
|
|
931
|
-
function generateTabs(scope, palette) {
|
|
932
|
-
var rules = {};
|
|
933
|
-
rules[
|
|
959
|
+
function generateTabs(scope, palette, layout) {
|
|
960
|
+
var rules = {}, mo = layout.motion;
|
|
961
|
+
rules[_sx(scope, '.bw_nav_tabs')] = {
|
|
934
962
|
'border-bottom-color': palette.light.border
|
|
935
963
|
};
|
|
936
|
-
rules[
|
|
937
|
-
'color': palette.secondary.base
|
|
964
|
+
rules[_sx(scope, '.bw_nav_link')] = {
|
|
965
|
+
'color': palette.secondary.base,
|
|
966
|
+
'transition': 'color ' + mo.fast + ' ' + mo.easing + ', border-color ' + mo.fast + ' ' + mo.easing + ', background-color ' + mo.fast + ' ' + mo.easing
|
|
938
967
|
};
|
|
939
|
-
rules[
|
|
968
|
+
rules[_sx(scope, '.bw_nav_tabs .bw_nav_link:hover')] = {
|
|
940
969
|
'color': palette.dark.base,
|
|
970
|
+
'background-color': palette.surfaceAlt,
|
|
941
971
|
'border-bottom-color': palette.light.border
|
|
942
972
|
};
|
|
943
|
-
rules[
|
|
973
|
+
rules[_sx(scope, '.bw_nav_tabs .bw_nav_link.active')] = {
|
|
944
974
|
'color': palette.primary.base,
|
|
975
|
+
'background-color': palette.primary.focus,
|
|
945
976
|
'border-bottom': '2px solid ' + palette.primary.base
|
|
946
977
|
};
|
|
947
978
|
return rules;
|
|
@@ -950,23 +981,25 @@
|
|
|
950
981
|
function generateListGroups(scope, palette, layout) {
|
|
951
982
|
var rules = {};
|
|
952
983
|
var sp = layout.spacing;
|
|
984
|
+
var mo = layout.motion;
|
|
953
985
|
|
|
954
|
-
rules[
|
|
986
|
+
rules[_sx(scope, '.bw_list_group_item')] = {
|
|
955
987
|
'padding': sp.cell,
|
|
956
988
|
'color': palette.dark.base,
|
|
957
989
|
'background-color': palette.surface || '#fff',
|
|
958
|
-
'border-color': palette.light.border
|
|
990
|
+
'border-color': palette.light.border,
|
|
991
|
+
'transition': 'color ' + mo.fast + ' ' + mo.easing + ', background-color ' + mo.fast + ' ' + mo.easing
|
|
959
992
|
};
|
|
960
|
-
rules[
|
|
961
|
-
'background-color': palette.
|
|
993
|
+
rules[_sx(scope, 'a.bw_list_group_item:hover')] = {
|
|
994
|
+
'background-color': palette.surfaceAlt,
|
|
962
995
|
'color': palette.dark.hover
|
|
963
996
|
};
|
|
964
|
-
rules[
|
|
997
|
+
rules[_sx(scope, '.bw_list_group_item.active')] = {
|
|
965
998
|
'color': palette.primary.textOn,
|
|
966
999
|
'background-color': palette.primary.base,
|
|
967
1000
|
'border-color': palette.primary.base
|
|
968
1001
|
};
|
|
969
|
-
rules[
|
|
1002
|
+
rules[_sx(scope, '.bw_list_group_item.disabled')] = {
|
|
970
1003
|
'color': palette.secondary.base,
|
|
971
1004
|
'background-color': palette.surface || '#fff'
|
|
972
1005
|
};
|
|
@@ -974,28 +1007,37 @@
|
|
|
974
1007
|
return rules;
|
|
975
1008
|
}
|
|
976
1009
|
|
|
977
|
-
function generatePagination(scope, palette) {
|
|
978
|
-
var rules = {};
|
|
979
|
-
rules[
|
|
1010
|
+
function generatePagination(scope, palette, layout) {
|
|
1011
|
+
var rules = {}, mo = layout.motion, rd = layout.radius;
|
|
1012
|
+
rules[_sx(scope, '.bw_page_item:first-child .bw_page_link')] = {
|
|
1013
|
+
'border-top-left-radius': rd.btn,
|
|
1014
|
+
'border-bottom-left-radius': rd.btn
|
|
1015
|
+
};
|
|
1016
|
+
rules[_sx(scope, '.bw_page_item:last-child .bw_page_link')] = {
|
|
1017
|
+
'border-top-right-radius': rd.btn,
|
|
1018
|
+
'border-bottom-right-radius': rd.btn
|
|
1019
|
+
};
|
|
1020
|
+
rules[_sx(scope, '.bw_page_link')] = {
|
|
980
1021
|
'color': palette.primary.base,
|
|
981
1022
|
'background-color': palette.surface || '#fff',
|
|
982
|
-
'border-color': palette.light.border
|
|
1023
|
+
'border-color': palette.light.border,
|
|
1024
|
+
'transition': 'color ' + mo.fast + ' ' + mo.easing + ', background-color ' + mo.fast + ' ' + mo.easing
|
|
983
1025
|
};
|
|
984
|
-
rules[
|
|
1026
|
+
rules[_sx(scope, '.bw_page_link:hover')] = {
|
|
985
1027
|
'color': palette.primary.hover,
|
|
986
|
-
'background-color': palette.
|
|
1028
|
+
'background-color': palette.surfaceAlt,
|
|
987
1029
|
'border-color': palette.light.border
|
|
988
1030
|
};
|
|
989
|
-
rules[
|
|
1031
|
+
rules[_sx(scope, '.bw_page_link:focus')] = {
|
|
990
1032
|
'outline': '2px solid ' + palette.primary.base,
|
|
991
1033
|
'outline-offset': '-2px'
|
|
992
1034
|
};
|
|
993
|
-
rules[
|
|
1035
|
+
rules[_sx(scope, '.bw_page_item.bw_active .bw_page_link')] = {
|
|
994
1036
|
'color': palette.primary.textOn,
|
|
995
1037
|
'background-color': palette.primary.base,
|
|
996
1038
|
'border-color': palette.primary.base
|
|
997
1039
|
};
|
|
998
|
-
rules[
|
|
1040
|
+
rules[_sx(scope, '.bw_page_item.bw_disabled .bw_page_link')] = {
|
|
999
1041
|
'color': palette.secondary.base,
|
|
1000
1042
|
'background-color': palette.surface || '#fff',
|
|
1001
1043
|
'border-color': palette.light.border
|
|
@@ -1005,12 +1047,12 @@
|
|
|
1005
1047
|
|
|
1006
1048
|
function generateProgress(scope, palette) {
|
|
1007
1049
|
var rules = {};
|
|
1008
|
-
rules[
|
|
1009
|
-
'background-color': palette.
|
|
1050
|
+
rules[_sx(scope, '.bw_progress')] = {
|
|
1051
|
+
'background-color': palette.surfaceAlt,
|
|
1010
1052
|
'box-shadow': 'inset 0 1px 2px rgba(0,0,0,.1)'
|
|
1011
1053
|
};
|
|
1012
|
-
rules[
|
|
1013
|
-
'color':
|
|
1054
|
+
rules[_sx(scope, '.bw_progress_bar')] = {
|
|
1055
|
+
'color': palette.primary.textOn,
|
|
1014
1056
|
'background-color': palette.primary.base,
|
|
1015
1057
|
'box-shadow': 'inset 0 -1px 0 rgba(0,0,0,.15)'
|
|
1016
1058
|
};
|
|
@@ -1029,26 +1071,31 @@
|
|
|
1029
1071
|
'color': palette.dark.base,
|
|
1030
1072
|
'background-color': bg
|
|
1031
1073
|
};
|
|
1032
|
-
rules[
|
|
1033
|
-
// Also apply to the scope element itself so themes work on any container, not just body
|
|
1034
|
-
if (scope) {
|
|
1035
|
-
rules['.' + scope] = baseReset;
|
|
1036
|
-
}
|
|
1074
|
+
rules[_sx(scope, 'body')] = baseReset;
|
|
1037
1075
|
return rules;
|
|
1038
1076
|
}
|
|
1039
1077
|
|
|
1040
|
-
function generateBreadcrumbThemed(scope, palette) {
|
|
1041
|
-
var rules = {};
|
|
1042
|
-
rules[
|
|
1043
|
-
'color': palette.
|
|
1078
|
+
function generateBreadcrumbThemed(scope, palette, layout) {
|
|
1079
|
+
var rules = {}, mo = layout.motion;
|
|
1080
|
+
rules[_sx(scope, '.bw_breadcrumb')] = {
|
|
1081
|
+
'background-color': palette.surfaceAlt,
|
|
1082
|
+
'padding': '0.625rem 1rem',
|
|
1083
|
+
'border-radius': layout.radius.btn
|
|
1044
1084
|
};
|
|
1045
|
-
rules[
|
|
1085
|
+
rules[_sx(scope, '.bw_breadcrumb_item + .bw_breadcrumb_item::before')] = {
|
|
1046
1086
|
'color': palette.secondary.base
|
|
1047
1087
|
};
|
|
1048
|
-
rules[
|
|
1088
|
+
rules[_sx(scope, '.bw_breadcrumb_item a')] = {
|
|
1089
|
+
'color': palette.primary.base,
|
|
1090
|
+
'transition': 'color ' + mo.fast + ' ' + mo.easing
|
|
1091
|
+
};
|
|
1092
|
+
rules[_sx(scope, '.bw_breadcrumb_item a:hover')] = {
|
|
1049
1093
|
'color': palette.primary.hover,
|
|
1050
1094
|
'text-decoration': 'underline'
|
|
1051
1095
|
};
|
|
1096
|
+
rules[_sx(scope, '.bw_breadcrumb_item.active')] = {
|
|
1097
|
+
'color': palette.dark.base
|
|
1098
|
+
};
|
|
1052
1099
|
return rules;
|
|
1053
1100
|
}
|
|
1054
1101
|
|
|
@@ -1056,11 +1103,11 @@
|
|
|
1056
1103
|
|
|
1057
1104
|
function generateCloseButtonThemed(scope, palette) {
|
|
1058
1105
|
var rules = {};
|
|
1059
|
-
rules[
|
|
1106
|
+
rules[_sx(scope, '.bw_close')] = {
|
|
1060
1107
|
'color': palette.dark.base,
|
|
1061
1108
|
'opacity': '0.5'
|
|
1062
1109
|
};
|
|
1063
|
-
rules[
|
|
1110
|
+
rules[_sx(scope, '.bw_close:focus')] = {
|
|
1064
1111
|
'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus
|
|
1065
1112
|
};
|
|
1066
1113
|
return rules;
|
|
@@ -1068,82 +1115,94 @@
|
|
|
1068
1115
|
|
|
1069
1116
|
function generateSectionsThemed(scope, palette) {
|
|
1070
1117
|
var rules = {};
|
|
1071
|
-
rules[
|
|
1118
|
+
rules[_sx(scope, '.bw_section_subtitle')] = {
|
|
1072
1119
|
'color': palette.secondary.base
|
|
1073
1120
|
};
|
|
1074
|
-
rules[
|
|
1121
|
+
rules[_sx(scope, '.bw_feature_description')] = {
|
|
1075
1122
|
'color': palette.secondary.base
|
|
1076
1123
|
};
|
|
1077
|
-
rules[
|
|
1124
|
+
rules[_sx(scope, '.bw_cta_description')] = {
|
|
1078
1125
|
'color': palette.secondary.base
|
|
1079
1126
|
};
|
|
1080
1127
|
return rules;
|
|
1081
1128
|
}
|
|
1082
1129
|
|
|
1083
|
-
function generateAccordionThemed(scope, palette) {
|
|
1130
|
+
function generateAccordionThemed(scope, palette, layout) {
|
|
1084
1131
|
var rules = {};
|
|
1085
|
-
|
|
1132
|
+
var rd = layout ? layout.radius : { card: '8px' };
|
|
1133
|
+
rules[_sx(scope, '.bw_accordion_item')] = {
|
|
1086
1134
|
'background-color': palette.surface || '#fff',
|
|
1087
1135
|
'border-color': palette.light.border
|
|
1088
1136
|
};
|
|
1089
|
-
rules[
|
|
1137
|
+
rules[_sx(scope, '.bw_accordion_item:first-child')] = {
|
|
1138
|
+
'border-top-left-radius': rd.card,
|
|
1139
|
+
'border-top-right-radius': rd.card
|
|
1140
|
+
};
|
|
1141
|
+
rules[_sx(scope, '.bw_accordion_item:last-child')] = {
|
|
1142
|
+
'border-bottom-left-radius': rd.card,
|
|
1143
|
+
'border-bottom-right-radius': rd.card
|
|
1144
|
+
};
|
|
1145
|
+
rules[_sx(scope, '.bw_accordion_button')] = {
|
|
1090
1146
|
'color': palette.dark.base
|
|
1091
1147
|
};
|
|
1092
|
-
rules[
|
|
1148
|
+
rules[_sx(scope, '.bw_accordion_button:not(.bw_collapsed)')] = {
|
|
1093
1149
|
'color': palette.primary.darkText,
|
|
1094
|
-
'background-color': palette.primary.light
|
|
1150
|
+
'background-color': palette.primary.light,
|
|
1151
|
+
'border-left': '3px solid ' + palette.primary.base
|
|
1095
1152
|
};
|
|
1096
|
-
rules[
|
|
1097
|
-
'background-color': palette.
|
|
1153
|
+
rules[_sx(scope, '.bw_accordion_button:hover')] = {
|
|
1154
|
+
'background-color': palette.surfaceAlt
|
|
1098
1155
|
};
|
|
1099
|
-
rules[
|
|
1100
|
-
'background-color': palette.primary.
|
|
1156
|
+
rules[_sx(scope, '.bw_accordion_button:not(.bw_collapsed):hover')] = {
|
|
1157
|
+
'background-color': palette.primary.base,
|
|
1158
|
+
'color': palette.primary.textOn
|
|
1101
1159
|
};
|
|
1102
|
-
rules[
|
|
1160
|
+
rules[_sx(scope, '.bw_accordion_button:focus-visible')] = {
|
|
1103
1161
|
'box-shadow': '0 0 0 0.2rem ' + palette.primary.focus
|
|
1104
1162
|
};
|
|
1105
|
-
rules[
|
|
1106
|
-
'border-top': '1px solid ' + palette.light.border
|
|
1163
|
+
rules[_sx(scope, '.bw_accordion_body')] = {
|
|
1164
|
+
'border-top': '1px solid ' + palette.light.border,
|
|
1165
|
+
'background-color': palette.surfaceAlt
|
|
1107
1166
|
};
|
|
1108
1167
|
return rules;
|
|
1109
1168
|
}
|
|
1110
1169
|
|
|
1111
1170
|
function generateCarouselThemed(scope, palette) {
|
|
1112
1171
|
var rules = {};
|
|
1113
|
-
rules[
|
|
1114
|
-
'background-color': palette.
|
|
1172
|
+
rules[_sx(scope, '.bw_carousel')] = {
|
|
1173
|
+
'background-color': palette.surfaceAlt
|
|
1115
1174
|
};
|
|
1116
|
-
rules[
|
|
1175
|
+
rules[_sx(scope, '.bw_carousel_indicator.active')] = {
|
|
1117
1176
|
'background-color': palette.primary.base
|
|
1118
1177
|
};
|
|
1119
|
-
rules[
|
|
1120
|
-
'background-color':
|
|
1121
|
-
'color':
|
|
1178
|
+
rules[_sx(scope, '.bw_carousel_control')] = {
|
|
1179
|
+
'background-color': palette.dark.base,
|
|
1180
|
+
'color': palette.dark.textOn
|
|
1122
1181
|
};
|
|
1123
|
-
rules[
|
|
1124
|
-
'background-color':
|
|
1182
|
+
rules[_sx(scope, '.bw_carousel_control:hover')] = {
|
|
1183
|
+
'background-color': palette.dark.hover
|
|
1125
1184
|
};
|
|
1126
|
-
rules[
|
|
1127
|
-
'background': 'linear-gradient(transparent,
|
|
1128
|
-
'color':
|
|
1185
|
+
rules[_sx(scope, '.bw_carousel_caption')] = {
|
|
1186
|
+
'background': 'linear-gradient(transparent, ' + palette.dark.base + ')',
|
|
1187
|
+
'color': palette.dark.textOn
|
|
1129
1188
|
};
|
|
1130
1189
|
return rules;
|
|
1131
1190
|
}
|
|
1132
1191
|
|
|
1133
1192
|
function generateModalThemed(scope, palette, layout) {
|
|
1134
1193
|
var rules = {};
|
|
1135
|
-
rules[
|
|
1194
|
+
rules[_sx(scope, '.bw_modal_content')] = {
|
|
1136
1195
|
'background-color': palette.surface || '#fff',
|
|
1137
1196
|
'border-color': palette.light.border,
|
|
1138
1197
|
'box-shadow': layout.elevation.lg
|
|
1139
1198
|
};
|
|
1140
|
-
rules[
|
|
1199
|
+
rules[_sx(scope, '.bw_modal_header')] = {
|
|
1141
1200
|
'border-bottom-color': palette.light.border
|
|
1142
1201
|
};
|
|
1143
|
-
rules[
|
|
1202
|
+
rules[_sx(scope, '.bw_modal_footer')] = {
|
|
1144
1203
|
'border-top-color': palette.light.border
|
|
1145
1204
|
};
|
|
1146
|
-
rules[
|
|
1205
|
+
rules[_sx(scope, '.bw_modal_title')] = {
|
|
1147
1206
|
'color': palette.dark.base
|
|
1148
1207
|
};
|
|
1149
1208
|
return rules;
|
|
@@ -1151,13 +1210,13 @@
|
|
|
1151
1210
|
|
|
1152
1211
|
function generateToastThemed(scope, palette, layout) {
|
|
1153
1212
|
var rules = {};
|
|
1154
|
-
rules[
|
|
1213
|
+
rules[_sx(scope, '.bw_toast')] = {
|
|
1155
1214
|
'background-color': palette.surface || '#fff',
|
|
1156
|
-
'border-color':
|
|
1215
|
+
'border-color': palette.light.border,
|
|
1157
1216
|
'box-shadow': layout.elevation.lg
|
|
1158
1217
|
};
|
|
1159
|
-
rules[
|
|
1160
|
-
'border-bottom-color':
|
|
1218
|
+
rules[_sx(scope, '.bw_toast_header')] = {
|
|
1219
|
+
'border-bottom-color': palette.light.border
|
|
1161
1220
|
};
|
|
1162
1221
|
// Variant toast borders handled by palette class
|
|
1163
1222
|
return rules;
|
|
@@ -1165,22 +1224,23 @@
|
|
|
1165
1224
|
|
|
1166
1225
|
function generateDropdownThemed(scope, palette, layout) {
|
|
1167
1226
|
var rules = {};
|
|
1168
|
-
rules[
|
|
1227
|
+
rules[_sx(scope, '.bw_dropdown_menu')] = {
|
|
1169
1228
|
'background-color': palette.surface || '#fff',
|
|
1170
1229
|
'border-color': palette.light.border,
|
|
1171
1230
|
'box-shadow': layout.elevation.md
|
|
1172
1231
|
};
|
|
1173
|
-
rules[
|
|
1174
|
-
'color': palette.dark.base
|
|
1232
|
+
rules[_sx(scope, '.bw_dropdown_item')] = {
|
|
1233
|
+
'color': palette.dark.base,
|
|
1234
|
+
'transition': 'background-color ' + layout.motion.fast + ' ' + layout.motion.easing
|
|
1175
1235
|
};
|
|
1176
|
-
rules[
|
|
1236
|
+
rules[_sx(scope, '.bw_dropdown_item:hover')] = {
|
|
1177
1237
|
'color': palette.dark.hover,
|
|
1178
|
-
'background-color': palette.
|
|
1238
|
+
'background-color': palette.surfaceAlt
|
|
1179
1239
|
};
|
|
1180
|
-
rules[
|
|
1240
|
+
rules[_sx(scope, '.bw_dropdown_item.disabled')] = {
|
|
1181
1241
|
'color': palette.secondary.base
|
|
1182
1242
|
};
|
|
1183
|
-
rules[
|
|
1243
|
+
rules[_sx(scope, '.bw_dropdown_divider')] = {
|
|
1184
1244
|
'border-top-color': palette.light.border
|
|
1185
1245
|
};
|
|
1186
1246
|
return rules;
|
|
@@ -1188,15 +1248,15 @@
|
|
|
1188
1248
|
|
|
1189
1249
|
function generateSwitchThemed(scope, palette) {
|
|
1190
1250
|
var rules = {};
|
|
1191
|
-
rules[
|
|
1251
|
+
rules[_sx(scope, '.bw_form_switch .bw_switch_input')] = {
|
|
1192
1252
|
'background-color': palette.secondary.base,
|
|
1193
1253
|
'border-color': palette.secondary.base
|
|
1194
1254
|
};
|
|
1195
|
-
rules[
|
|
1255
|
+
rules[_sx(scope, '.bw_form_switch .bw_switch_input:checked')] = {
|
|
1196
1256
|
'background-color': palette.primary.base,
|
|
1197
1257
|
'border-color': palette.primary.base
|
|
1198
1258
|
};
|
|
1199
|
-
rules[
|
|
1259
|
+
rules[_sx(scope, '.bw_form_switch .bw_switch_input:focus')] = {
|
|
1200
1260
|
'box-shadow': '0 0 0 0.25rem ' + palette.primary.focus
|
|
1201
1261
|
};
|
|
1202
1262
|
return rules;
|
|
@@ -1204,88 +1264,102 @@
|
|
|
1204
1264
|
|
|
1205
1265
|
function generateSkeletonThemed(scope, palette) {
|
|
1206
1266
|
var rules = {};
|
|
1207
|
-
rules[
|
|
1208
|
-
'background': 'linear-gradient(90deg, ' + palette.light.border + ' 25%, ' + palette.
|
|
1267
|
+
rules[_sx(scope, '.bw_skeleton')] = {
|
|
1268
|
+
'background': 'linear-gradient(90deg, ' + palette.light.border + ' 25%, ' + palette.surfaceAlt + ' 37%, ' + palette.light.border + ' 63%)'
|
|
1209
1269
|
};
|
|
1210
1270
|
return rules;
|
|
1211
1271
|
}
|
|
1212
1272
|
|
|
1213
1273
|
// generateAvatarThemed: removed — palette class on root handles variants
|
|
1214
1274
|
|
|
1215
|
-
function generateStatCardThemed(scope, palette) {
|
|
1216
|
-
var rules = {};
|
|
1275
|
+
function generateStatCardThemed(scope, palette, layout) {
|
|
1276
|
+
var rules = {}, mo = layout.motion, el = layout.elevation, rd = layout.radius;
|
|
1277
|
+
rules[_sx(scope, '.bw_stat_card')] = {
|
|
1278
|
+
'background-color': palette.surface || '#fff',
|
|
1279
|
+
'color': palette.dark.base,
|
|
1280
|
+
'border': '1px solid ' + palette.light.border,
|
|
1281
|
+
'border-radius': rd.card,
|
|
1282
|
+
'box-shadow': el.sm,
|
|
1283
|
+
'transition': 'box-shadow ' + mo.fast + ' ' + mo.easing + ', transform ' + mo.fast + ' ' + mo.easing
|
|
1284
|
+
};
|
|
1285
|
+
rules[_sx(scope, '.bw_stat_card:hover')] = { 'box-shadow': el.md };
|
|
1217
1286
|
// Variant border colors handled by palette class
|
|
1218
|
-
rules[
|
|
1219
|
-
rules[
|
|
1287
|
+
rules[_sx(scope, '.bw_stat_change_up')] = { 'color': palette.success.base };
|
|
1288
|
+
rules[_sx(scope, '.bw_stat_change_down')] = { 'color': palette.danger.base };
|
|
1220
1289
|
return rules;
|
|
1221
1290
|
}
|
|
1222
1291
|
|
|
1223
1292
|
function generateTimelineThemed(scope, palette) {
|
|
1224
1293
|
var rules = {};
|
|
1225
|
-
rules[
|
|
1294
|
+
rules[_sx(scope, '.bw_timeline::before')] = { 'background-color': palette.light.border };
|
|
1226
1295
|
// Variant marker colors handled by palette class
|
|
1227
|
-
rules[
|
|
1296
|
+
rules[_sx(scope, '.bw_timeline_date')] = { 'color': palette.secondary.base };
|
|
1228
1297
|
return rules;
|
|
1229
1298
|
}
|
|
1230
1299
|
|
|
1231
1300
|
function generateStepperThemed(scope, palette) {
|
|
1232
1301
|
var rules = {};
|
|
1233
|
-
rules[
|
|
1234
|
-
'background-color': palette.
|
|
1302
|
+
rules[_sx(scope, '.bw_step_indicator')] = {
|
|
1303
|
+
'background-color': palette.surfaceAlt,
|
|
1235
1304
|
'border': '2px solid ' + palette.light.border,
|
|
1236
1305
|
'color': palette.secondary.base
|
|
1237
1306
|
};
|
|
1238
|
-
rules[
|
|
1239
|
-
rules[
|
|
1307
|
+
rules[_sx(scope, '.bw_step + .bw_step::before')] = { 'background-color': palette.light.border };
|
|
1308
|
+
rules[_sx(scope, '.bw_step_active .bw_step_indicator')] = {
|
|
1240
1309
|
'background-color': palette.primary.base,
|
|
1241
1310
|
'color': palette.primary.textOn
|
|
1242
1311
|
};
|
|
1243
|
-
rules[
|
|
1312
|
+
rules[_sx(scope, '.bw_step_active .bw_step_label')] = {
|
|
1244
1313
|
'color': palette.dark.base,
|
|
1245
1314
|
'font-weight': '600'
|
|
1246
1315
|
};
|
|
1247
|
-
rules[
|
|
1316
|
+
rules[_sx(scope, '.bw_step_completed .bw_step_indicator')] = {
|
|
1248
1317
|
'background-color': palette.primary.base,
|
|
1249
1318
|
'color': palette.primary.textOn
|
|
1250
1319
|
};
|
|
1251
|
-
rules[
|
|
1252
|
-
rules[
|
|
1320
|
+
rules[_sx(scope, '.bw_step_completed .bw_step_label')] = { 'color': palette.primary.base };
|
|
1321
|
+
rules[_sx(scope, '.bw_step_completed + .bw_step::before')] = { 'background-color': palette.primary.base };
|
|
1253
1322
|
return rules;
|
|
1254
1323
|
}
|
|
1255
1324
|
|
|
1256
1325
|
function generateChipInputThemed(scope, palette) {
|
|
1257
1326
|
var rules = {};
|
|
1258
|
-
rules[
|
|
1259
|
-
|
|
1327
|
+
rules[_sx(scope, '.bw_chip_input')] = {
|
|
1328
|
+
'border-color': palette.light.border,
|
|
1329
|
+
'background-color': palette.surface || '#fff',
|
|
1330
|
+
'color': palette.dark.base
|
|
1331
|
+
};
|
|
1332
|
+
rules[_sx(scope, '.bw_chip_input:focus-within')] = {
|
|
1260
1333
|
'border-color': palette.primary.base,
|
|
1261
1334
|
'box-shadow': '0 0 0 0.2rem ' + palette.primary.focus
|
|
1262
1335
|
};
|
|
1263
|
-
rules[
|
|
1264
|
-
'background-color': palette.
|
|
1336
|
+
rules[_sx(scope, '.bw_chip')] = {
|
|
1337
|
+
'background-color': palette.surfaceAlt,
|
|
1265
1338
|
'color': palette.dark.base
|
|
1266
1339
|
};
|
|
1267
|
-
rules[
|
|
1340
|
+
rules[_sx(scope, '.bw_chip_remove:hover')] = {
|
|
1268
1341
|
'color': palette.danger.base,
|
|
1269
1342
|
'background-color': palette.danger.light
|
|
1270
1343
|
};
|
|
1271
1344
|
return rules;
|
|
1272
1345
|
}
|
|
1273
1346
|
|
|
1274
|
-
function generateFileUploadThemed(scope, palette) {
|
|
1275
|
-
var rules = {};
|
|
1276
|
-
rules[
|
|
1347
|
+
function generateFileUploadThemed(scope, palette, layout) {
|
|
1348
|
+
var rules = {}, mo = layout.motion;
|
|
1349
|
+
rules[_sx(scope, '.bw_file_upload')] = {
|
|
1277
1350
|
'border-color': palette.light.border,
|
|
1278
|
-
'background-color': palette.
|
|
1351
|
+
'background-color': palette.surfaceAlt,
|
|
1352
|
+
'transition': 'border-color ' + mo.fast + ' ' + mo.easing + ', background-color ' + mo.fast + ' ' + mo.easing
|
|
1279
1353
|
};
|
|
1280
|
-
rules[
|
|
1354
|
+
rules[_sx(scope, '.bw_file_upload:hover')] = {
|
|
1281
1355
|
'border-color': palette.primary.base,
|
|
1282
1356
|
'background-color': palette.primary.light
|
|
1283
1357
|
};
|
|
1284
|
-
rules[
|
|
1358
|
+
rules[_sx(scope, '.bw_file_upload:focus')] = {
|
|
1285
1359
|
'outline': '2px solid ' + palette.primary.base,
|
|
1286
1360
|
'outline-offset': '2px'
|
|
1287
1361
|
};
|
|
1288
|
-
rules[
|
|
1362
|
+
rules[_sx(scope, '.bw_file_upload.bw_file_upload_active')] = {
|
|
1289
1363
|
'border-color': palette.primary.base,
|
|
1290
1364
|
'background-color': palette.primary.light,
|
|
1291
1365
|
'border-style': 'solid'
|
|
@@ -1295,35 +1369,73 @@
|
|
|
1295
1369
|
|
|
1296
1370
|
function generateRangeThemed(scope, palette) {
|
|
1297
1371
|
var rules = {};
|
|
1298
|
-
rules[
|
|
1299
|
-
rules[
|
|
1372
|
+
rules[_sx(scope, '.bw_range')] = { 'background-color': palette.light.border };
|
|
1373
|
+
rules[_sx(scope, '.bw_range::-webkit-slider-thumb')] = {
|
|
1300
1374
|
'background-color': palette.primary.base,
|
|
1301
|
-
'border-color': '#fff',
|
|
1375
|
+
'border-color': palette.surface || '#fff',
|
|
1302
1376
|
'box-shadow': '0 1px 3px rgba(0,0,0,0.2)',
|
|
1303
1377
|
'transition': 'background-color 0.15s ease-out, transform 0.15s ease-out'
|
|
1304
1378
|
};
|
|
1305
|
-
rules[
|
|
1379
|
+
rules[_sx(scope, '.bw_range::-moz-range-thumb')] = {
|
|
1306
1380
|
'background-color': palette.primary.base,
|
|
1307
|
-
'border-color': '#fff',
|
|
1381
|
+
'border-color': palette.surface || '#fff',
|
|
1308
1382
|
'box-shadow': '0 1px 3px rgba(0,0,0,0.2)'
|
|
1309
1383
|
};
|
|
1310
1384
|
return rules;
|
|
1311
1385
|
}
|
|
1312
1386
|
|
|
1313
|
-
function
|
|
1314
|
-
var rules = {};
|
|
1315
|
-
rules[
|
|
1387
|
+
function generateTooltipThemed(scope, palette, layout) {
|
|
1388
|
+
var rules = {}, sp = layout.spacing, rd = layout.radius, el = layout.elevation, mo = layout.motion;
|
|
1389
|
+
rules[_sx(scope, '.bw_tooltip')] = {
|
|
1390
|
+
'background-color': palette.dark.base, 'color': palette.dark.textOn,
|
|
1391
|
+
'padding': sp.input, 'border-radius': rd.badge, 'box-shadow': el.md,
|
|
1392
|
+
'transition': 'opacity ' + mo.fast + ' ' + mo.easing + ', transform ' + mo.fast + ' ' + mo.easing
|
|
1393
|
+
};
|
|
1394
|
+
return rules;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
function generatePopoverThemed(scope, palette, layout) {
|
|
1398
|
+
var rules = {}, sp = layout.spacing, rd = layout.radius, el = layout.elevation, mo = layout.motion;
|
|
1399
|
+
rules[_sx(scope, '.bw_popover')] = {
|
|
1400
|
+
'background-color': palette.surface || '#fff', 'color': palette.dark.base,
|
|
1401
|
+
'border': '1px solid ' + palette.light.border, 'border-radius': rd.card, 'box-shadow': el.lg,
|
|
1402
|
+
'transition': 'opacity ' + mo.fast + ' ' + mo.easing + ', transform ' + mo.fast + ' ' + mo.easing
|
|
1403
|
+
};
|
|
1404
|
+
rules[_sx(scope, '.bw_popover_header')] = {
|
|
1405
|
+
'background-color': palette.surfaceAlt, 'border-bottom': '1px solid ' + palette.light.border,
|
|
1406
|
+
'padding': sp.input
|
|
1407
|
+
};
|
|
1408
|
+
rules[_sx(scope, '.bw_popover_body')] = { 'padding': sp.card };
|
|
1409
|
+
return rules;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
function generateSearchThemed(scope, palette, layout) {
|
|
1413
|
+
var rules = {}, mo = layout.motion;
|
|
1414
|
+
rules[_sx(scope, '.bw_search_input')] = {
|
|
1415
|
+
'background-color': palette.surface || '#fff',
|
|
1416
|
+
'color': palette.dark.base
|
|
1417
|
+
};
|
|
1418
|
+
rules[_sx(scope, '.bw_search_clear')] = {
|
|
1419
|
+
'transition': 'color ' + mo.fast + ' ' + mo.easing + ', background-color ' + mo.fast + ' ' + mo.easing
|
|
1420
|
+
};
|
|
1421
|
+
rules[_sx(scope, '.bw_search_clear:hover')] = { 'color': palette.dark.base };
|
|
1316
1422
|
return rules;
|
|
1317
1423
|
}
|
|
1318
1424
|
|
|
1319
|
-
function generateCodeDemoThemed(scope, palette) {
|
|
1425
|
+
function generateCodeDemoThemed(scope, palette, layout) {
|
|
1320
1426
|
var rules = {};
|
|
1321
|
-
|
|
1427
|
+
var rd = layout ? layout.radius : { card: '0.375rem' };
|
|
1428
|
+
rules[_sx(scope, '.bw_code_demo')] = {
|
|
1429
|
+
'background-color': palette.surface || '#fff',
|
|
1430
|
+
'color': palette.dark.base,
|
|
1431
|
+
'border-radius': rd.card
|
|
1432
|
+
};
|
|
1433
|
+
rules[_sx(scope, '.bw_code_copy_btn_copied')] = {
|
|
1322
1434
|
'background': palette.success.base,
|
|
1323
1435
|
'color': palette.success.textOn,
|
|
1324
1436
|
'border-color': palette.success.base
|
|
1325
1437
|
};
|
|
1326
|
-
rules[
|
|
1438
|
+
rules[_sx(scope, '.bw_copy_btn:hover')] = {
|
|
1327
1439
|
'background': 'rgba(255,255,255,0.2)',
|
|
1328
1440
|
'color': '#fff'
|
|
1329
1441
|
};
|
|
@@ -1333,7 +1445,7 @@
|
|
|
1333
1445
|
function generateNavPillsThemed(scope, palette, layout) {
|
|
1334
1446
|
var rules = {};
|
|
1335
1447
|
var rd = layout.radius;
|
|
1336
|
-
rules[
|
|
1448
|
+
rules[_sx(scope, '.bw_nav_pills .bw_nav_link')] = { 'border-radius': rd.btn };
|
|
1337
1449
|
return rules;
|
|
1338
1450
|
}
|
|
1339
1451
|
|
|
@@ -1359,21 +1471,21 @@
|
|
|
1359
1471
|
var s = palette[k];
|
|
1360
1472
|
|
|
1361
1473
|
// --- Root palette class: sets default bg/color/border ---
|
|
1362
|
-
rules[
|
|
1474
|
+
rules[_sx(scope, '.bw_' + k)] = {
|
|
1363
1475
|
'background-color': s.base,
|
|
1364
1476
|
'color': s.textOn,
|
|
1365
1477
|
'border-color': s.base
|
|
1366
1478
|
};
|
|
1367
1479
|
|
|
1368
1480
|
// --- Pseudo-states (shared across all components) ---
|
|
1369
|
-
rules[
|
|
1481
|
+
rules[_sx(scope, '.bw_' + k + ':hover')] = {
|
|
1370
1482
|
'background-color': s.hover,
|
|
1371
1483
|
'border-color': s.active
|
|
1372
1484
|
};
|
|
1373
|
-
rules[
|
|
1485
|
+
rules[_sx(scope, '.bw_' + k + ':active')] = {
|
|
1374
1486
|
'background-color': s.active
|
|
1375
1487
|
};
|
|
1376
|
-
rules[
|
|
1488
|
+
rules[_sx(scope, '.bw_' + k + ':focus-visible')] = {
|
|
1377
1489
|
'box-shadow': '0 0 0 3px ' + s.focus,
|
|
1378
1490
|
'outline': 'none'
|
|
1379
1491
|
};
|
|
@@ -1381,70 +1493,99 @@
|
|
|
1381
1493
|
// --- Component-specific overrides ---
|
|
1382
1494
|
|
|
1383
1495
|
// Alerts: light bg, dark text, subtle border
|
|
1384
|
-
rules[
|
|
1496
|
+
rules[_sx(scope, '.bw_alert.bw_' + k)] = {
|
|
1385
1497
|
'background-color': s.light,
|
|
1386
1498
|
'color': s.darkText,
|
|
1387
1499
|
'border-color': s.border
|
|
1388
1500
|
};
|
|
1389
1501
|
|
|
1390
1502
|
// Toast: inherit bg, left border accent
|
|
1391
|
-
rules[
|
|
1503
|
+
rules[_sx(scope, '.bw_toast.bw_' + k)] = {
|
|
1392
1504
|
'background-color': 'inherit',
|
|
1393
1505
|
'color': 'inherit',
|
|
1394
1506
|
'border-left': '4px solid ' + s.base
|
|
1395
1507
|
};
|
|
1396
1508
|
|
|
1397
1509
|
// Stat card: inherit bg, left border accent
|
|
1398
|
-
rules[
|
|
1510
|
+
rules[_sx(scope, '.bw_stat_card.bw_' + k)] = {
|
|
1399
1511
|
'background-color': 'inherit',
|
|
1400
1512
|
'color': 'inherit',
|
|
1401
1513
|
'border-left-color': s.base
|
|
1402
1514
|
};
|
|
1403
1515
|
|
|
1404
1516
|
// Card accent: left border accent, inherit bg
|
|
1405
|
-
rules[
|
|
1517
|
+
rules[_sx(scope, '.bw_card.bw_' + k)] = {
|
|
1406
1518
|
'background-color': 'inherit',
|
|
1407
1519
|
'color': 'inherit',
|
|
1408
1520
|
'border-left': '4px solid ' + s.base
|
|
1409
1521
|
};
|
|
1410
1522
|
|
|
1411
1523
|
// Timeline marker: colored dot
|
|
1412
|
-
rules[
|
|
1524
|
+
rules[_sx(scope, '.bw_timeline_marker.bw_' + k)] = {
|
|
1413
1525
|
'box-shadow': '0 0 0 2px ' + s.base
|
|
1414
1526
|
};
|
|
1415
1527
|
|
|
1416
|
-
// Spinner:
|
|
1417
|
-
|
|
1528
|
+
// Spinner: set color, re-apply border pattern so the root palette class
|
|
1529
|
+
// border-color doesn't fill in the transparent gap that makes it spin.
|
|
1530
|
+
// Also neutralize hover/active which would override border-right-color.
|
|
1531
|
+
rules[_sx(scope, '.bw_spinner_border.bw_' + k)] = {
|
|
1418
1532
|
'background-color': 'transparent',
|
|
1419
1533
|
'color': s.base,
|
|
1420
|
-
'border-color':
|
|
1534
|
+
'border-color': s.base,
|
|
1535
|
+
'border-right-color': 'transparent'
|
|
1536
|
+
};
|
|
1537
|
+
rules[_sx(scope, '.bw_spinner_border.bw_' + k + ':hover')] = {
|
|
1538
|
+
'background-color': 'transparent',
|
|
1539
|
+
'border-color': s.base,
|
|
1540
|
+
'border-right-color': 'transparent'
|
|
1541
|
+
};
|
|
1542
|
+
rules[_sx(scope, '.bw_spinner_grow.bw_' + k)] = {
|
|
1543
|
+
'background-color': s.base,
|
|
1544
|
+
'color': s.base
|
|
1421
1545
|
};
|
|
1422
1546
|
|
|
1423
1547
|
// Outline button: transparent bg, colored border+text, solid on hover
|
|
1424
|
-
rules[
|
|
1548
|
+
rules[_sx(scope, '.bw_btn_outline.bw_' + k)] = {
|
|
1425
1549
|
'background-color': 'transparent',
|
|
1426
1550
|
'color': s.base,
|
|
1427
1551
|
'border-color': s.base
|
|
1428
1552
|
};
|
|
1429
|
-
rules[
|
|
1553
|
+
rules[_sx(scope, '.bw_btn_outline.bw_' + k + ':hover')] = {
|
|
1430
1554
|
'background-color': s.base,
|
|
1431
1555
|
'color': s.textOn
|
|
1432
1556
|
};
|
|
1433
1557
|
|
|
1434
1558
|
// Hero: gradient background
|
|
1435
|
-
rules[
|
|
1559
|
+
rules[_sx(scope, '.bw_hero.bw_' + k)] = {
|
|
1436
1560
|
'background': 'linear-gradient(135deg, ' + s.base + ' 0%, ' + s.hover + ' 100%)',
|
|
1437
1561
|
'color': s.textOn
|
|
1438
1562
|
};
|
|
1439
1563
|
|
|
1440
|
-
// Progress bar:
|
|
1441
|
-
rules[
|
|
1442
|
-
'color':
|
|
1564
|
+
// Progress bar: contrasting text on colored bg
|
|
1565
|
+
rules[_sx(scope, '.bw_progress_bar.bw_' + k)] = {
|
|
1566
|
+
'color': s.textOn
|
|
1567
|
+
};
|
|
1568
|
+
|
|
1569
|
+
// Background utility: .bw_bg_primary, .bw_bg_secondary, etc.
|
|
1570
|
+
rules[_sx(scope, '.bw_bg_' + k)] = {
|
|
1571
|
+
'background-color': s.base,
|
|
1572
|
+
'color': s.textOn
|
|
1573
|
+
};
|
|
1574
|
+
|
|
1575
|
+
// Text color utility: .bw_text_primary, .bw_text_secondary, etc.
|
|
1576
|
+
rules[_sx(scope, '.bw_text_' + k)] = {
|
|
1577
|
+
'color': s.base
|
|
1443
1578
|
};
|
|
1444
1579
|
});
|
|
1445
1580
|
|
|
1446
|
-
// Text muted
|
|
1447
|
-
rules[
|
|
1581
|
+
// Text muted — always a neutral gray, never a brand color
|
|
1582
|
+
rules[_sx(scope, '.bw_text_muted')] = { 'color': '#6c757d' };
|
|
1583
|
+
|
|
1584
|
+
// Common bg/text utilities that aren't per-variant
|
|
1585
|
+
rules[_sx(scope, '.bw_bg_dark')] = { 'background-color': '#212529', 'color': '#f8f9fa' };
|
|
1586
|
+
rules[_sx(scope, '.bw_bg_light')] = { 'background-color': '#f8f9fa', 'color': '#212529' };
|
|
1587
|
+
rules[_sx(scope, '.bw_text_light')] = { 'color': '#f8f9fa' };
|
|
1588
|
+
rules[_sx(scope, '.bw_text_dark')] = { 'color': '#212529' };
|
|
1448
1589
|
|
|
1449
1590
|
return rules;
|
|
1450
1591
|
}
|
|
@@ -1466,30 +1607,32 @@
|
|
|
1466
1607
|
generateAlerts(scopeName, palette, layout),
|
|
1467
1608
|
generateCards(scopeName, palette, layout),
|
|
1468
1609
|
generateForms(scopeName, palette, layout),
|
|
1469
|
-
generateNavigation(scopeName, palette),
|
|
1610
|
+
generateNavigation(scopeName, palette, layout),
|
|
1470
1611
|
generateTables(scopeName, palette, layout),
|
|
1471
|
-
generateTabs(scopeName, palette),
|
|
1612
|
+
generateTabs(scopeName, palette, layout),
|
|
1472
1613
|
generateListGroups(scopeName, palette, layout),
|
|
1473
|
-
generatePagination(scopeName, palette),
|
|
1614
|
+
generatePagination(scopeName, palette, layout),
|
|
1474
1615
|
generateProgress(scopeName, palette),
|
|
1475
|
-
generateBreadcrumbThemed(scopeName, palette),
|
|
1616
|
+
generateBreadcrumbThemed(scopeName, palette, layout),
|
|
1476
1617
|
generateCloseButtonThemed(scopeName, palette),
|
|
1477
1618
|
generateSectionsThemed(scopeName, palette),
|
|
1478
|
-
generateAccordionThemed(scopeName, palette),
|
|
1619
|
+
generateAccordionThemed(scopeName, palette, layout),
|
|
1479
1620
|
generateCarouselThemed(scopeName, palette),
|
|
1480
1621
|
generateModalThemed(scopeName, palette, layout),
|
|
1481
1622
|
generateToastThemed(scopeName, palette, layout),
|
|
1482
1623
|
generateDropdownThemed(scopeName, palette, layout),
|
|
1483
1624
|
generateSwitchThemed(scopeName, palette),
|
|
1484
1625
|
generateSkeletonThemed(scopeName, palette),
|
|
1485
|
-
generateStatCardThemed(scopeName, palette),
|
|
1626
|
+
generateStatCardThemed(scopeName, palette, layout),
|
|
1486
1627
|
generateTimelineThemed(scopeName, palette),
|
|
1487
1628
|
generateStepperThemed(scopeName, palette),
|
|
1488
1629
|
generateChipInputThemed(scopeName, palette),
|
|
1489
|
-
generateFileUploadThemed(scopeName, palette),
|
|
1630
|
+
generateFileUploadThemed(scopeName, palette, layout),
|
|
1490
1631
|
generateRangeThemed(scopeName, palette),
|
|
1491
|
-
generateSearchThemed(scopeName, palette),
|
|
1492
|
-
|
|
1632
|
+
generateSearchThemed(scopeName, palette, layout),
|
|
1633
|
+
generateTooltipThemed(scopeName, palette, layout),
|
|
1634
|
+
generatePopoverThemed(scopeName, palette, layout),
|
|
1635
|
+
generateCodeDemoThemed(scopeName, palette, layout),
|
|
1493
1636
|
generateNavPillsThemed(scopeName, palette, layout),
|
|
1494
1637
|
generatePaletteClasses(scopeName, palette)
|
|
1495
1638
|
);
|
|
@@ -1714,6 +1857,8 @@
|
|
|
1714
1857
|
},
|
|
1715
1858
|
'.bw_table caption': { 'font-size': '0.875rem', 'caption-side': 'bottom' },
|
|
1716
1859
|
'.bw_table_bordered > :not(caption) > * > *': { 'border-width': '1px', 'border-style': 'solid' },
|
|
1860
|
+
'.bw_table_selectable > tbody > tr': { 'cursor': 'pointer' },
|
|
1861
|
+
'.bw_table > tbody > tr.bw_table_row_selected > *': { 'background-color': 'rgba(0, 102, 102, 0.1)' },
|
|
1717
1862
|
'.bw_table_responsive': { 'overflow-x': 'auto', '-webkit-overflow-scrolling': 'touch' }
|
|
1718
1863
|
},
|
|
1719
1864
|
|
|
@@ -1767,6 +1912,7 @@
|
|
|
1767
1912
|
'.bw_nav_tabs .bw_nav_item': { 'margin-bottom': '-2px' },
|
|
1768
1913
|
'.bw_nav_link': {
|
|
1769
1914
|
'display': 'block', 'font-size': '0.875rem', 'font-weight': '500',
|
|
1915
|
+
'padding': '0.625rem 1rem',
|
|
1770
1916
|
'text-decoration': 'none', 'cursor': 'pointer',
|
|
1771
1917
|
'border': 'none', 'background': 'transparent', 'font-family': 'inherit'
|
|
1772
1918
|
},
|
|
@@ -1801,10 +1947,11 @@
|
|
|
1801
1947
|
'.bw_page_item': { 'display': 'list-item', 'list-style': 'none' },
|
|
1802
1948
|
'.bw_page_link': {
|
|
1803
1949
|
'position': 'relative', 'display': 'block', 'padding': '0.375rem 0.75rem',
|
|
1804
|
-
'margin-left': '-1px', 'line-height': '1.25', 'text-decoration': 'none'
|
|
1950
|
+
'margin-left': '-1px', 'line-height': '1.25', 'text-decoration': 'none',
|
|
1951
|
+
'border': '1px solid transparent', 'cursor': 'pointer',
|
|
1952
|
+
'font-family': 'inherit', 'font-size': 'inherit', 'background': 'none'
|
|
1805
1953
|
},
|
|
1806
|
-
'.bw_page_item:first-child .bw_page_link': { 'margin-left': '0'
|
|
1807
|
-
'.bw_page_item:last-child .bw_page_link': { 'border-top-right-radius': '0.375rem', 'border-bottom-right-radius': '0.375rem' },
|
|
1954
|
+
'.bw_page_item:first-child .bw_page_link': { 'margin-left': '0' },
|
|
1808
1955
|
'.bw_page_link:focus-visible': { 'z-index': '3', 'outline': '2px solid currentColor', 'outline-offset': '-2px' }
|
|
1809
1956
|
},
|
|
1810
1957
|
|
|
@@ -1961,6 +2108,7 @@
|
|
|
1961
2108
|
'.bw_accordion_header': { 'margin': '0' },
|
|
1962
2109
|
'.bw_accordion_button': {
|
|
1963
2110
|
'position': 'relative', 'display': 'flex', 'align-items': 'center', 'width': '100%',
|
|
2111
|
+
'padding': '0.875rem 1.25rem',
|
|
1964
2112
|
'font-size': '1rem', 'font-weight': '500', 'text-align': 'left',
|
|
1965
2113
|
'background-color': 'transparent', 'border': '0', 'overflow-anchor': 'none', 'cursor': 'pointer',
|
|
1966
2114
|
'font-family': 'inherit'
|
|
@@ -1972,10 +2120,9 @@
|
|
|
1972
2120
|
'background-repeat': 'no-repeat', 'background-size': '1.25rem'
|
|
1973
2121
|
},
|
|
1974
2122
|
'.bw_accordion_button:not(.bw_collapsed)::after': { 'transform': 'rotate(-180deg)' },
|
|
1975
|
-
'.
|
|
1976
|
-
'.bw_accordion_collapse
|
|
1977
|
-
'.
|
|
1978
|
-
'.bw_accordion_item:last-child': { 'border-bottom-left-radius': '8px', 'border-bottom-right-radius': '8px' }
|
|
2123
|
+
'.bw_accordion_body': { 'padding': '1rem 1.25rem' },
|
|
2124
|
+
'.bw_accordion_collapse': { 'max-height': '0', 'overflow': 'hidden', 'transition': 'max-height 0.3s ease' },
|
|
2125
|
+
'.bw_accordion_collapse.bw_collapse_show': { 'max-height': 'none' }
|
|
1979
2126
|
},
|
|
1980
2127
|
|
|
1981
2128
|
// ---- Carousel ----
|
|
@@ -2121,7 +2268,13 @@
|
|
|
2121
2268
|
|
|
2122
2269
|
// ---- Stat card ----
|
|
2123
2270
|
statCard: {
|
|
2124
|
-
'.bw_stat_card': {
|
|
2271
|
+
'.bw_stat_card': {
|
|
2272
|
+
'padding': '1.25rem',
|
|
2273
|
+
'border-left': '4px solid transparent',
|
|
2274
|
+
'border-radius': '0.375rem',
|
|
2275
|
+
'background-color': 'inherit',
|
|
2276
|
+
'transition': 'transform 0.15s ease'
|
|
2277
|
+
},
|
|
2125
2278
|
'.bw_stat_card:hover': { 'transform': 'translateY(-1px)' },
|
|
2126
2279
|
'.bw_stat_icon': { 'font-size': '1.5rem', 'margin-bottom': '0.5rem' },
|
|
2127
2280
|
'.bw_stat_value': { 'font-size': '2rem', 'font-weight': '700', 'line-height': '1.2' },
|
|
@@ -2484,6 +2637,20 @@
|
|
|
2484
2637
|
rules['.list-inline-item'] = { 'display': 'inline-block' };
|
|
2485
2638
|
rules['.list-inline-item:not(:last-child)'] = { 'margin-right': '.5rem' };
|
|
2486
2639
|
|
|
2640
|
+
// Typography — bw_ prefixed utilities via loops
|
|
2641
|
+
var _imp = function(p, v) { var o = {}; o[p] = v + ' !important'; return o; };
|
|
2642
|
+
[['fs',{'xs':'0.75rem','sm':'0.875rem','base':'1rem','lg':'1.125rem','xl':'1.25rem','2xl':'1.5rem'},'font-size'],
|
|
2643
|
+
['fw',{light:'300',normal:'400',medium:'500',semibold:'600',bold:'700'},'font-weight'],
|
|
2644
|
+
['lh',{tight:'1.25',normal:'1.5',relaxed:'1.75'},'line-height']
|
|
2645
|
+
].forEach(function(d) { for (var dk in d[1]) rules['.bw_'+d[0]+'_'+dk] = _imp(d[2], d[1][dk]); });
|
|
2646
|
+
|
|
2647
|
+
// Flex utilities
|
|
2648
|
+
rules['.bw_flex'] = { 'display': 'flex' };
|
|
2649
|
+
rules['.bw_flex_column'] = { 'flex-direction': 'column' };
|
|
2650
|
+
rules['.bw_flex_wrap'] = { 'flex-wrap': 'wrap' };
|
|
2651
|
+
rules['.bw_flex_center'] = { 'display': 'flex', 'align-items': 'center', 'justify-content': 'center' };
|
|
2652
|
+
for (var gk in spacingValues) rules['.bw_gap_' + gk] = { 'gap': spacingValues[gk] + ' !important' };
|
|
2653
|
+
|
|
2487
2654
|
// Visibility
|
|
2488
2655
|
rules['.bw_visible, .visible'] = { 'visibility': 'visible !important' };
|
|
2489
2656
|
rules['.bw_invisible, .invisible'] = { 'visibility': 'hidden !important' };
|
|
@@ -2544,6 +2711,26 @@
|
|
|
2544
2711
|
return getStructuralCSS();
|
|
2545
2712
|
}
|
|
2546
2713
|
|
|
2714
|
+
/**
|
|
2715
|
+
* Get CSS reset rules only (box-sizing, html/body font, reduced-motion).
|
|
2716
|
+
* Separate from themed/structural rules for independent injection.
|
|
2717
|
+
* @returns {Object} CSS rules object for the reset layer
|
|
2718
|
+
*/
|
|
2719
|
+
function getResetStyles() {
|
|
2720
|
+
var rules = {};
|
|
2721
|
+
Object.assign(rules, structuralRules.base);
|
|
2722
|
+
// Include reduced-motion preference
|
|
2723
|
+
rules['@media (prefers-reduced-motion: reduce)'] = {
|
|
2724
|
+
'*, *::before, *::after': {
|
|
2725
|
+
'animation-duration': '0.01ms !important',
|
|
2726
|
+
'animation-iteration-count': '1 !important',
|
|
2727
|
+
'transition-duration': '0.01ms !important',
|
|
2728
|
+
'scroll-behavior': 'auto !important'
|
|
2729
|
+
}
|
|
2730
|
+
};
|
|
2731
|
+
return rules;
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2547
2734
|
// =========================================================================
|
|
2548
2735
|
// defaultStyles — backward-compatible categorized view
|
|
2549
2736
|
// =========================================================================
|
|
@@ -2573,60 +2760,41 @@
|
|
|
2573
2760
|
});
|
|
2574
2761
|
|
|
2575
2762
|
/**
|
|
2576
|
-
*
|
|
2577
|
-
*
|
|
2578
|
-
*
|
|
2579
|
-
*
|
|
2580
|
-
* @param {
|
|
2581
|
-
*
|
|
2582
|
-
* @
|
|
2583
|
-
* @returns {Object} CSS rules object scoped under .bw_theme_alt (+ optional .name)
|
|
2763
|
+
* Prefix every selector in a rules object with a scope selector.
|
|
2764
|
+
* Handles @media/@keyframes blocks and comma-separated selectors.
|
|
2765
|
+
* @param {Object} rules - CSS rules object
|
|
2766
|
+
* @param {string} prefix - Scope prefix (e.g. '#my-dashboard', '.bw_theme_alt')
|
|
2767
|
+
* @param {boolean} [compound=false] - If true, use compound selector (no space)
|
|
2768
|
+
* for the first segment: `#scope.bw_theme_alt .sel` vs `#scope .sel`
|
|
2769
|
+
* @returns {Object} New rules object with scoped selectors
|
|
2584
2770
|
*/
|
|
2585
|
-
function
|
|
2586
|
-
|
|
2587
|
-
var
|
|
2588
|
-
|
|
2589
|
-
// Re-scope every selector under .bw_theme_alt (+ optional theme name)
|
|
2590
|
-
var altPrefix = name ? '.' + name + '.bw_theme_alt' : '.bw_theme_alt';
|
|
2591
|
-
var altRules = {};
|
|
2592
|
-
|
|
2593
|
-
for (var sel in rawRules) {
|
|
2594
|
-
if (!rawRules.hasOwnProperty(sel)) continue;
|
|
2595
|
-
|
|
2771
|
+
function scopeRulesUnder(rules, prefix, compound) {
|
|
2772
|
+
var scoped = {};
|
|
2773
|
+
for (var sel in rules) {
|
|
2774
|
+
if (!rules.hasOwnProperty(sel)) continue;
|
|
2596
2775
|
if (sel.charAt(0) === '@') {
|
|
2597
2776
|
// @media / @keyframes — recurse into the block
|
|
2598
|
-
var innerBlock =
|
|
2599
|
-
var
|
|
2777
|
+
var innerBlock = rules[sel];
|
|
2778
|
+
var scopedInner = {};
|
|
2600
2779
|
for (var innerSel in innerBlock) {
|
|
2601
2780
|
if (!innerBlock.hasOwnProperty(innerSel)) continue;
|
|
2602
|
-
|
|
2781
|
+
scopedInner[_prefixSelector(innerSel, prefix)] = innerBlock[innerSel];
|
|
2603
2782
|
}
|
|
2604
|
-
|
|
2783
|
+
scoped[sel] = scopedInner;
|
|
2605
2784
|
} else {
|
|
2606
|
-
|
|
2607
|
-
// Handle comma-separated selectors
|
|
2608
|
-
var parts = sel.split(',');
|
|
2609
|
-
var scopedParts = [];
|
|
2610
|
-
for (var i = 0; i < parts.length; i++) {
|
|
2611
|
-
var s = parts[i].trim();
|
|
2612
|
-
// 'body' selector gets special treatment: .bw_theme_alt body
|
|
2613
|
-
if (s === 'body' || s.indexOf('body') === 0) {
|
|
2614
|
-
scopedParts.push(altPrefix + ' ' + s);
|
|
2615
|
-
} else {
|
|
2616
|
-
scopedParts.push(altPrefix + ' ' + s);
|
|
2617
|
-
}
|
|
2618
|
-
}
|
|
2619
|
-
altRules[scopedParts.join(', ')] = rawRules[sel];
|
|
2785
|
+
scoped[_prefixSelector(sel, prefix)] = rules[sel];
|
|
2620
2786
|
}
|
|
2621
2787
|
}
|
|
2788
|
+
return scoped;
|
|
2789
|
+
}
|
|
2622
2790
|
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
return
|
|
2791
|
+
function _prefixSelector(sel, prefix) {
|
|
2792
|
+
var parts = sel.split(',');
|
|
2793
|
+
var result = [];
|
|
2794
|
+
for (var i = 0; i < parts.length; i++) {
|
|
2795
|
+
result.push(prefix + ' ' + parts[i].trim());
|
|
2796
|
+
}
|
|
2797
|
+
return result.join(', ');
|
|
2630
2798
|
}
|
|
2631
2799
|
|
|
2632
2800
|
/**
|
|
@@ -3598,7 +3766,7 @@
|
|
|
3598
3766
|
if (breakpoint === 'xs') {
|
|
3599
3767
|
classes.push(`bw_col_${value}`);
|
|
3600
3768
|
} else {
|
|
3601
|
-
classes.push(`bw_col_${breakpoint}
|
|
3769
|
+
classes.push(`bw_col_${breakpoint}_${value}`);
|
|
3602
3770
|
}
|
|
3603
3771
|
});
|
|
3604
3772
|
} else if (size) {
|
|
@@ -4981,8 +5149,8 @@
|
|
|
4981
5149
|
t: 'li',
|
|
4982
5150
|
a: { class: `bw_page_item ${currentPage <= 1 ? 'bw_disabled' : ''}`.trim() },
|
|
4983
5151
|
c: {
|
|
4984
|
-
t: '
|
|
4985
|
-
a: { class: 'bw_page_link',
|
|
5152
|
+
t: 'button',
|
|
5153
|
+
a: { class: 'bw_page_link', type: 'button', onclick: handleClick(currentPage - 1), 'aria-label': 'Previous', disabled: currentPage <= 1 ? true : undefined },
|
|
4986
5154
|
c: '\u2039'
|
|
4987
5155
|
}
|
|
4988
5156
|
});
|
|
@@ -4994,8 +5162,8 @@
|
|
|
4994
5162
|
t: 'li',
|
|
4995
5163
|
a: { class: `bw_page_item ${pageNum === currentPage ? 'bw_active' : ''}`.trim() },
|
|
4996
5164
|
c: {
|
|
4997
|
-
t: '
|
|
4998
|
-
a: { class: 'bw_page_link',
|
|
5165
|
+
t: 'button',
|
|
5166
|
+
a: { class: 'bw_page_link', type: 'button', onclick: handleClick(pageNum), 'aria-current': pageNum === currentPage ? 'page' : undefined },
|
|
4999
5167
|
c: '' + pageNum
|
|
5000
5168
|
}
|
|
5001
5169
|
});
|
|
@@ -5007,8 +5175,8 @@
|
|
|
5007
5175
|
t: 'li',
|
|
5008
5176
|
a: { class: `bw_page_item ${currentPage >= pages ? 'bw_disabled' : ''}`.trim() },
|
|
5009
5177
|
c: {
|
|
5010
|
-
t: '
|
|
5011
|
-
a: { class: 'bw_page_link',
|
|
5178
|
+
t: 'button',
|
|
5179
|
+
a: { class: 'bw_page_link', type: 'button', onclick: handleClick(currentPage + 1), 'aria-label': 'Next', disabled: currentPage >= pages ? true : undefined },
|
|
5012
5180
|
c: '\u203A'
|
|
5013
5181
|
}
|
|
5014
5182
|
});
|
|
@@ -7290,7 +7458,12 @@
|
|
|
7290
7458
|
el = document.querySelector('[data-bw_id="' + id + '"]');
|
|
7291
7459
|
}
|
|
7292
7460
|
|
|
7293
|
-
// 5.
|
|
7461
|
+
// 5. Try class-based lookup for bw_uuid_* tokens (UUID addressing)
|
|
7462
|
+
if (!el && id.indexOf('bw_uuid_') === 0) {
|
|
7463
|
+
el = document.querySelector('.' + id);
|
|
7464
|
+
}
|
|
7465
|
+
|
|
7466
|
+
// 6. Cache the result for next time
|
|
7294
7467
|
if (el) {
|
|
7295
7468
|
bw._nodeMap[id] = el;
|
|
7296
7469
|
}
|
|
@@ -7343,6 +7516,84 @@
|
|
|
7343
7516
|
}
|
|
7344
7517
|
};
|
|
7345
7518
|
|
|
7519
|
+
// ===================================================================================
|
|
7520
|
+
// bw.assignUUID() / bw.getUUID() — Explicit UUID addressing for TACO objects
|
|
7521
|
+
// ===================================================================================
|
|
7522
|
+
|
|
7523
|
+
/**
|
|
7524
|
+
* Regex to match a bw_uuid_* token in a class string.
|
|
7525
|
+
* @private
|
|
7526
|
+
*/
|
|
7527
|
+
var _UUID_RE = /\bbw_uuid_[a-z0-9_]+\b/;
|
|
7528
|
+
|
|
7529
|
+
/**
|
|
7530
|
+
* Assign a UUID to a TACO object by appending a `bw_uuid_*` token to `taco.a.class`.
|
|
7531
|
+
*
|
|
7532
|
+
* Idempotent by default — calling twice returns the same UUID. Pass `forceNew=true`
|
|
7533
|
+
* to replace an existing UUID (useful in loops where each TACO needs a unique ID).
|
|
7534
|
+
*
|
|
7535
|
+
* @param {Object} taco - A TACO object `{t, a, c, o}`
|
|
7536
|
+
* @param {boolean} [forceNew=false] - If true, replaces any existing UUID with a new one
|
|
7537
|
+
* @returns {string} The UUID string (e.g. 'bw_uuid_a1b2c3d4e5')
|
|
7538
|
+
* @category Identifiers
|
|
7539
|
+
* @example
|
|
7540
|
+
* var card = bw.makeStatCard({ value: '0', label: 'Scans' });
|
|
7541
|
+
* var uuid = bw.assignUUID(card); // 'bw_uuid_a1b2c3d4e5'
|
|
7542
|
+
* var same = bw.assignUUID(card); // same UUID (idempotent)
|
|
7543
|
+
* var diff = bw.assignUUID(card, true); // new UUID (forced)
|
|
7544
|
+
*/
|
|
7545
|
+
bw.assignUUID = function(taco, forceNew) {
|
|
7546
|
+
if (!taco || !_is(taco, 'object')) return null;
|
|
7547
|
+
|
|
7548
|
+
// Ensure taco.a exists
|
|
7549
|
+
if (!taco.a) taco.a = {};
|
|
7550
|
+
if (!_is(taco.a.class, 'string')) taco.a.class = taco.a.class ? String(taco.a.class) : '';
|
|
7551
|
+
|
|
7552
|
+
var existing = taco.a.class.match(_UUID_RE);
|
|
7553
|
+
|
|
7554
|
+
if (existing && !forceNew) {
|
|
7555
|
+
return existing[0];
|
|
7556
|
+
}
|
|
7557
|
+
|
|
7558
|
+
// Remove old UUID if forceNew
|
|
7559
|
+
if (existing) {
|
|
7560
|
+
taco.a.class = taco.a.class.replace(_UUID_RE, '').replace(/\s+/g, ' ').trim();
|
|
7561
|
+
}
|
|
7562
|
+
|
|
7563
|
+
var uuid = bw.uuid('uuid');
|
|
7564
|
+
taco.a.class = (taco.a.class ? taco.a.class + ' ' : '') + uuid;
|
|
7565
|
+
return uuid;
|
|
7566
|
+
};
|
|
7567
|
+
|
|
7568
|
+
/**
|
|
7569
|
+
* Read the UUID from a TACO object or DOM element. Pure getter, no side effects.
|
|
7570
|
+
*
|
|
7571
|
+
* @param {Object|Element} tacoOrElement - A TACO object or DOM element
|
|
7572
|
+
* @returns {string|null} The UUID string, or null if none assigned
|
|
7573
|
+
* @category Identifiers
|
|
7574
|
+
* @example
|
|
7575
|
+
* bw.getUUID(card) // 'bw_uuid_a1b2c3d4e5' (from TACO)
|
|
7576
|
+
* bw.getUUID(domEl) // 'bw_uuid_a1b2c3d4e5' (from DOM element)
|
|
7577
|
+
* bw.getUUID({t:'div'}) // null (no UUID)
|
|
7578
|
+
*/
|
|
7579
|
+
bw.getUUID = function(tacoOrElement) {
|
|
7580
|
+
if (!tacoOrElement) return null;
|
|
7581
|
+
|
|
7582
|
+
var classStr;
|
|
7583
|
+
// DOM element: check className
|
|
7584
|
+
if (tacoOrElement.className !== undefined && tacoOrElement.tagName) {
|
|
7585
|
+
classStr = tacoOrElement.className;
|
|
7586
|
+
}
|
|
7587
|
+
// TACO object: check a.class
|
|
7588
|
+
else if (tacoOrElement.a && _is(tacoOrElement.a.class, 'string')) {
|
|
7589
|
+
classStr = tacoOrElement.a.class;
|
|
7590
|
+
}
|
|
7591
|
+
|
|
7592
|
+
if (!classStr) return null;
|
|
7593
|
+
var match = classStr.match(_UUID_RE);
|
|
7594
|
+
return match ? match[0] : null;
|
|
7595
|
+
};
|
|
7596
|
+
|
|
7346
7597
|
/**
|
|
7347
7598
|
* Escape HTML special characters to prevent XSS.
|
|
7348
7599
|
*
|
|
@@ -7392,6 +7643,42 @@
|
|
|
7392
7643
|
return { __bw_raw: true, v: String(str) };
|
|
7393
7644
|
};
|
|
7394
7645
|
|
|
7646
|
+
/**
|
|
7647
|
+
* Hyperscript-style TACO constructor.
|
|
7648
|
+
*
|
|
7649
|
+
* A convenience helper that returns a canonical TACO object from positional
|
|
7650
|
+
* arguments. The return value is a plain object — serializable, works with
|
|
7651
|
+
* bwserve, and accepted everywhere TACO is accepted.
|
|
7652
|
+
*
|
|
7653
|
+
* @param {string} tag - HTML tag name (e.g. 'div', 'p', 'section')
|
|
7654
|
+
* @param {Object|null} [attrs] - HTML attributes object. Pass null or omit to skip.
|
|
7655
|
+
* @param {*} [content] - Content: string, number, TACO object, or array of children.
|
|
7656
|
+
* @param {Object} [options] - TACO options (state, lifecycle hooks, render fn).
|
|
7657
|
+
* @returns {Object} Plain TACO object {t, a?, c?, o?}
|
|
7658
|
+
* @category Utilities
|
|
7659
|
+
* @see bw.html
|
|
7660
|
+
* @see bw.createDOM
|
|
7661
|
+
* @see bw.DOM
|
|
7662
|
+
* @example
|
|
7663
|
+
* bw.h('div')
|
|
7664
|
+
* // => { t: 'div' }
|
|
7665
|
+
*
|
|
7666
|
+
* bw.h('p', { class: 'bw_text_muted' }, 'Hello')
|
|
7667
|
+
* // => { t: 'p', a: { class: 'bw_text_muted' }, c: 'Hello' }
|
|
7668
|
+
*
|
|
7669
|
+
* bw.h('ul', null, [
|
|
7670
|
+
* bw.h('li', null, 'one'),
|
|
7671
|
+
* bw.h('li', null, 'two')
|
|
7672
|
+
* ])
|
|
7673
|
+
* // => { t: 'ul', c: [{ t: 'li', c: 'one' }, { t: 'li', c: 'two' }] }
|
|
7674
|
+
*/
|
|
7675
|
+
bw.h = function(tag, attrs, content, options) {
|
|
7676
|
+
var taco = { t: String(tag) };
|
|
7677
|
+
if (attrs !== null && attrs !== undefined) taco.a = attrs;
|
|
7678
|
+
if (content !== undefined) taco.c = content;
|
|
7679
|
+
if (options !== undefined) taco.o = options;
|
|
7680
|
+
return taco;
|
|
7681
|
+
};
|
|
7395
7682
|
|
|
7396
7683
|
/**
|
|
7397
7684
|
* Convert a TACO object (or array of TACOs) to an HTML string.
|
|
@@ -7656,7 +7943,7 @@
|
|
|
7656
7943
|
? (THEME_PRESETS[theme.toLowerCase()] || null)
|
|
7657
7944
|
: theme;
|
|
7658
7945
|
if (themeConfig) {
|
|
7659
|
-
var themeResult = bw.
|
|
7946
|
+
var themeResult = bw.makeStyles(themeConfig);
|
|
7660
7947
|
themeCSS = themeResult.css;
|
|
7661
7948
|
}
|
|
7662
7949
|
}
|
|
@@ -7682,14 +7969,14 @@
|
|
|
7682
7969
|
// Combine all CSS
|
|
7683
7970
|
var allCSS = (themeCSS ? themeCSS + '\n' : '') + css;
|
|
7684
7971
|
|
|
7685
|
-
// Body-end script: registry entries + optional
|
|
7972
|
+
// Body-end script: registry entries + optional loadStyles
|
|
7686
7973
|
var bodyEndScript = '';
|
|
7687
7974
|
var bodyEndParts = [];
|
|
7688
7975
|
if (registryEntries) {
|
|
7689
7976
|
bodyEndParts.push(registryEntries);
|
|
7690
7977
|
}
|
|
7691
7978
|
if (runtime === 'inline' || runtime === 'cdn') {
|
|
7692
|
-
bodyEndParts.push('if(typeof bw!=="undefined"){bw.
|
|
7979
|
+
bodyEndParts.push('if(typeof bw!=="undefined"){bw.loadStyles();}');
|
|
7693
7980
|
}
|
|
7694
7981
|
if (bodyEndParts.length > 0) {
|
|
7695
7982
|
bodyEndScript = '<script>\n' + bodyEndParts.join('\n') + '\n</script>';
|
|
@@ -7863,6 +8150,14 @@
|
|
|
7863
8150
|
bw._registerNode(el, null);
|
|
7864
8151
|
}
|
|
7865
8152
|
|
|
8153
|
+
// Register UUID class in node cache (bw_uuid_* tokens in class string)
|
|
8154
|
+
if (el.className) {
|
|
8155
|
+
var uuidMatch = el.className.match(_UUID_RE);
|
|
8156
|
+
if (uuidMatch) {
|
|
8157
|
+
bw._nodeMap[uuidMatch[0]] = el;
|
|
8158
|
+
}
|
|
8159
|
+
}
|
|
8160
|
+
|
|
7866
8161
|
// Handle lifecycle hooks and state
|
|
7867
8162
|
if (opts.mounted || opts.unmount || opts.render || opts.state) {
|
|
7868
8163
|
const id = attrs['data-bw_id'] || bw.uuid();
|
|
@@ -8235,6 +8530,16 @@
|
|
|
8235
8530
|
bw.cleanup = function(element) {
|
|
8236
8531
|
if (!bw._isBrowser || !element) return;
|
|
8237
8532
|
|
|
8533
|
+
// Deregister UUID classes from node cache (element + descendants)
|
|
8534
|
+
// Covers elements that have UUID but no data-bw_id
|
|
8535
|
+
var selfUuidMatch = element.className && element.className.match(_UUID_RE);
|
|
8536
|
+
if (selfUuidMatch) delete bw._nodeMap[selfUuidMatch[0]];
|
|
8537
|
+
var uuidEls = element.querySelectorAll('[class*="bw_uuid_"]');
|
|
8538
|
+
uuidEls.forEach(function(uel) {
|
|
8539
|
+
var m = uel.className && uel.className.match(_UUID_RE);
|
|
8540
|
+
if (m) delete bw._nodeMap[m[0]];
|
|
8541
|
+
});
|
|
8542
|
+
|
|
8238
8543
|
// Find all elements with data-bw_id
|
|
8239
8544
|
const elements = element.querySelectorAll('[data-bw_id]');
|
|
8240
8545
|
|
|
@@ -8250,6 +8555,10 @@
|
|
|
8250
8555
|
// Deregister from node cache
|
|
8251
8556
|
bw._deregisterNode(el, id);
|
|
8252
8557
|
|
|
8558
|
+
// Deregister UUID class from node cache
|
|
8559
|
+
var uuidMatch = el.className && el.className.match(_UUID_RE);
|
|
8560
|
+
if (uuidMatch) delete bw._nodeMap[uuidMatch[0]];
|
|
8561
|
+
|
|
8253
8562
|
// Clean up pub/sub subscriptions tied to this element
|
|
8254
8563
|
if (el._bw_subs) {
|
|
8255
8564
|
el._bw_subs.forEach(function(unsub) { unsub(); });
|
|
@@ -8274,6 +8583,10 @@
|
|
|
8274
8583
|
// Deregister from node cache
|
|
8275
8584
|
bw._deregisterNode(element, id);
|
|
8276
8585
|
|
|
8586
|
+
// Deregister UUID class from node cache
|
|
8587
|
+
var elemUuidMatch = element.className && element.className.match(_UUID_RE);
|
|
8588
|
+
if (elemUuidMatch) delete bw._nodeMap[elemUuidMatch[0]];
|
|
8589
|
+
|
|
8277
8590
|
// Clean up pub/sub subscriptions tied to element itself
|
|
8278
8591
|
if (element._bw_subs) {
|
|
8279
8592
|
element._bw_subs.forEach(function(unsub) { unsub(); });
|
|
@@ -8885,7 +9198,7 @@
|
|
|
8885
9198
|
willMount: o.willMount || null,
|
|
8886
9199
|
mounted: o.mounted || null,
|
|
8887
9200
|
willUpdate: o.willUpdate || null,
|
|
8888
|
-
onUpdate: o.onUpdate || null,
|
|
9201
|
+
onUpdate: o.onUpdate || o.updated || null,
|
|
8889
9202
|
unmount: o.unmount || null,
|
|
8890
9203
|
willDestroy: o.willDestroy || null
|
|
8891
9204
|
};
|
|
@@ -9824,7 +10137,7 @@
|
|
|
9824
10137
|
* and calls the named method. This is the bitwrench equivalent of
|
|
9825
10138
|
* Win32 SendMessage(hwnd, msg, wParam, lParam).
|
|
9826
10139
|
*
|
|
9827
|
-
* @param {string} target - Component UUID (data-bw_comp_id) or user tag (CSS class)
|
|
10140
|
+
* @param {string} target - Component UUID (bw_uuid_*), comp ID (data-bw_comp_id), or user tag (CSS class)
|
|
9828
10141
|
* @param {string} action - Method name to call on the component
|
|
9829
10142
|
* @param {*} data - Data to pass to the method
|
|
9830
10143
|
* @returns {boolean} True if message was dispatched successfully
|
|
@@ -9841,9 +10154,14 @@
|
|
|
9841
10154
|
* };
|
|
9842
10155
|
*/
|
|
9843
10156
|
bw.message = function(target, action, data) {
|
|
9844
|
-
// Try
|
|
9845
|
-
var el = bw
|
|
9846
|
-
|
|
10157
|
+
// Try bw._el() first (handles UUID class, nodeMap cache, getElementById)
|
|
10158
|
+
var el = bw._el(target);
|
|
10159
|
+
// Then try data-bw_comp_id attribute
|
|
10160
|
+
if (!el || !el._bwComponentHandle) {
|
|
10161
|
+
el = bw.$('[data-bw_comp_id="' + target + '"]')[0];
|
|
10162
|
+
}
|
|
10163
|
+
// Then try CSS class (user tag)
|
|
10164
|
+
if (!el || !el._bwComponentHandle) {
|
|
9847
10165
|
el = bw.$('.' + target)[0];
|
|
9848
10166
|
}
|
|
9849
10167
|
if (!el || !el._bwComponentHandle) return false;
|
|
@@ -9857,59 +10175,24 @@
|
|
|
9857
10175
|
};
|
|
9858
10176
|
|
|
9859
10177
|
// ===================================================================================
|
|
9860
|
-
// bw.
|
|
10178
|
+
// bw.apply() / bw.parseJSONFlex() — Server-driven UI protocol
|
|
9861
10179
|
// ===================================================================================
|
|
9862
10180
|
|
|
9863
10181
|
/**
|
|
9864
10182
|
* Registry of named functions sent via register messages.
|
|
9865
|
-
* Populated by
|
|
9866
|
-
* Invoked by
|
|
10183
|
+
* Populated by bw.apply({ type: 'register', name, body }).
|
|
10184
|
+
* Invoked by bw.apply({ type: 'call', name, args }).
|
|
9867
10185
|
* @private
|
|
9868
10186
|
*/
|
|
9869
10187
|
bw._clientFunctions = {};
|
|
9870
10188
|
|
|
9871
10189
|
/**
|
|
9872
|
-
* Whether exec messages are allowed. Set by
|
|
10190
|
+
* Whether exec messages are allowed. Set by bwclient connect opts.allowExec.
|
|
9873
10191
|
* Default false — exec messages are rejected unless explicitly opted in.
|
|
9874
10192
|
* @private
|
|
9875
10193
|
*/
|
|
9876
10194
|
bw._allowExec = false;
|
|
9877
10195
|
|
|
9878
|
-
/**
|
|
9879
|
-
* Built-in client functions available via call() without registration.
|
|
9880
|
-
* @private
|
|
9881
|
-
*/
|
|
9882
|
-
bw._builtinClientFunctions = {
|
|
9883
|
-
scrollTo: function(selector) {
|
|
9884
|
-
var el = bw._el(selector);
|
|
9885
|
-
if (el) el.scrollTop = el.scrollHeight;
|
|
9886
|
-
},
|
|
9887
|
-
focus: function(selector) {
|
|
9888
|
-
var el = bw._el(selector);
|
|
9889
|
-
if (el && _is(el.focus, 'function')) el.focus();
|
|
9890
|
-
},
|
|
9891
|
-
download: function(filename, content, mimeType) {
|
|
9892
|
-
if (typeof document === 'undefined') return;
|
|
9893
|
-
var blob = new Blob([content], { type: mimeType || 'text/plain' });
|
|
9894
|
-
var a = document.createElement('a');
|
|
9895
|
-
a.href = URL.createObjectURL(blob);
|
|
9896
|
-
a.download = filename;
|
|
9897
|
-
a.click();
|
|
9898
|
-
URL.revokeObjectURL(a.href);
|
|
9899
|
-
},
|
|
9900
|
-
clipboard: function(text) {
|
|
9901
|
-
if (typeof navigator !== 'undefined' && navigator.clipboard) {
|
|
9902
|
-
navigator.clipboard.writeText(text);
|
|
9903
|
-
}
|
|
9904
|
-
},
|
|
9905
|
-
redirect: function(url) {
|
|
9906
|
-
if (typeof window !== 'undefined') window.location.href = url;
|
|
9907
|
-
},
|
|
9908
|
-
log: function() {
|
|
9909
|
-
console.log.apply(console, arguments);
|
|
9910
|
-
}
|
|
9911
|
-
};
|
|
9912
|
-
|
|
9913
10196
|
/**
|
|
9914
10197
|
* Parse a bwserve protocol message string, supporting both strict JSON
|
|
9915
10198
|
* and r-prefixed relaxed JSON (single-quoted strings, trailing commas).
|
|
@@ -9924,9 +10207,9 @@
|
|
|
9924
10207
|
* @param {string} str - JSON or r-prefixed relaxed JSON string
|
|
9925
10208
|
* @returns {Object} Parsed message object
|
|
9926
10209
|
* @throws {SyntaxError} If the string is not valid JSON or relaxed JSON
|
|
9927
|
-
* @category
|
|
10210
|
+
* @category Core
|
|
9928
10211
|
*/
|
|
9929
|
-
bw.
|
|
10212
|
+
bw.parseJSONFlex = function(str) {
|
|
9930
10213
|
str = (str || '').trim();
|
|
9931
10214
|
if (str.charAt(0) !== 'r') return JSON.parse(str);
|
|
9932
10215
|
str = str.slice(1);
|
|
@@ -10011,10 +10294,10 @@
|
|
|
10011
10294
|
* append — target.appendChild(bw.createDOM(node))
|
|
10012
10295
|
* remove — bw.cleanup(target); target.remove()
|
|
10013
10296
|
* patch — bw.patch(target, content, attr)
|
|
10014
|
-
* batch — iterate ops, call
|
|
10297
|
+
* batch — iterate ops, call bw.apply for each
|
|
10015
10298
|
* message — bw.message(target, action, data)
|
|
10016
10299
|
* register — store a named function for later call()
|
|
10017
|
-
* call — invoke a registered
|
|
10300
|
+
* call — invoke a registered function
|
|
10018
10301
|
* exec — execute arbitrary JS (requires allowExec)
|
|
10019
10302
|
*
|
|
10020
10303
|
* Target resolution:
|
|
@@ -10023,9 +10306,9 @@
|
|
|
10023
10306
|
*
|
|
10024
10307
|
* @param {Object} msg - Protocol message
|
|
10025
10308
|
* @returns {boolean} true if the message was applied successfully
|
|
10026
|
-
* @category
|
|
10309
|
+
* @category Core
|
|
10027
10310
|
*/
|
|
10028
|
-
bw.
|
|
10311
|
+
bw.apply = function(msg) {
|
|
10029
10312
|
if (!msg || !msg.type) return false;
|
|
10030
10313
|
|
|
10031
10314
|
var type = msg.type;
|
|
@@ -10059,7 +10342,7 @@
|
|
|
10059
10342
|
if (!_isA(msg.ops)) return false;
|
|
10060
10343
|
var allOk = true;
|
|
10061
10344
|
msg.ops.forEach(function(op) {
|
|
10062
|
-
if (!bw.
|
|
10345
|
+
if (!bw.apply(op)) allOk = false;
|
|
10063
10346
|
});
|
|
10064
10347
|
return allOk;
|
|
10065
10348
|
|
|
@@ -10078,7 +10361,7 @@
|
|
|
10078
10361
|
|
|
10079
10362
|
} else if (type === 'call') {
|
|
10080
10363
|
if (!msg.name) return false;
|
|
10081
|
-
var fn = bw._clientFunctions[msg.name]
|
|
10364
|
+
var fn = bw._clientFunctions[msg.name];
|
|
10082
10365
|
if (!_is(fn, 'function')) return false;
|
|
10083
10366
|
try {
|
|
10084
10367
|
var args = _isA(msg.args) ? msg.args : [];
|
|
@@ -10107,139 +10390,6 @@
|
|
|
10107
10390
|
return false;
|
|
10108
10391
|
};
|
|
10109
10392
|
|
|
10110
|
-
/**
|
|
10111
|
-
* Connect to a bwserve SSE endpoint and apply protocol messages automatically.
|
|
10112
|
-
*
|
|
10113
|
-
* Returns a connection object with sendAction(), on(), and close() methods.
|
|
10114
|
-
*
|
|
10115
|
-
* @param {string} url - SSE endpoint URL (e.g., '/__bw/events/client-1')
|
|
10116
|
-
* @param {Object} [opts] - Connection options
|
|
10117
|
-
* @param {string} [opts.transport='sse'] - Transport type: 'sse' (default) or 'poll'
|
|
10118
|
-
* @param {number} [opts.interval=2000] - Poll interval in ms (only for 'poll' transport)
|
|
10119
|
-
* @param {string} [opts.actionUrl] - POST endpoint for actions (default: derived from url)
|
|
10120
|
-
* @param {boolean} [opts.reconnect=true] - Auto-reconnect on disconnect
|
|
10121
|
-
* @param {boolean} [opts.allowExec=false] - Enable exec message type (arbitrary JS execution)
|
|
10122
|
-
* @param {Function} [opts.onStatus] - Status callback: 'connecting'|'connected'|'disconnected'
|
|
10123
|
-
* @param {Function} [opts.onMessage] - Raw message callback (before clientApply)
|
|
10124
|
-
* @returns {Object} Connection object { sendAction, on, close, status }
|
|
10125
|
-
* @category Server
|
|
10126
|
-
*/
|
|
10127
|
-
bw.clientConnect = function(url, opts) {
|
|
10128
|
-
opts = opts || {};
|
|
10129
|
-
var transport = opts.transport || 'sse';
|
|
10130
|
-
var actionUrl = opts.actionUrl || url.replace(/\/events\//, '/action/');
|
|
10131
|
-
var reconnect = opts.reconnect !== false;
|
|
10132
|
-
var onStatus = opts.onStatus || function() {};
|
|
10133
|
-
var onMessage = opts.onMessage || null;
|
|
10134
|
-
var handlers = {};
|
|
10135
|
-
// Set the global allowExec flag from connection options
|
|
10136
|
-
bw._allowExec = !!opts.allowExec;
|
|
10137
|
-
var conn = {
|
|
10138
|
-
status: 'connecting',
|
|
10139
|
-
_es: null,
|
|
10140
|
-
_pollTimer: null
|
|
10141
|
-
};
|
|
10142
|
-
|
|
10143
|
-
function setStatus(s) {
|
|
10144
|
-
conn.status = s;
|
|
10145
|
-
onStatus(s);
|
|
10146
|
-
}
|
|
10147
|
-
|
|
10148
|
-
function handleMessage(data) {
|
|
10149
|
-
try {
|
|
10150
|
-
var msg = _is(data, 'string') ? bw.clientParse(data) : data;
|
|
10151
|
-
if (onMessage) onMessage(msg);
|
|
10152
|
-
if (handlers.message) handlers.message(msg);
|
|
10153
|
-
bw.clientApply(msg);
|
|
10154
|
-
} catch (e) {
|
|
10155
|
-
if (handlers.error) handlers.error(e);
|
|
10156
|
-
}
|
|
10157
|
-
}
|
|
10158
|
-
|
|
10159
|
-
if (transport === 'sse' && typeof EventSource !== 'undefined') {
|
|
10160
|
-
setStatus('connecting');
|
|
10161
|
-
var es = new EventSource(url);
|
|
10162
|
-
conn._es = es;
|
|
10163
|
-
|
|
10164
|
-
es.onopen = function() {
|
|
10165
|
-
setStatus('connected');
|
|
10166
|
-
if (handlers.open) handlers.open();
|
|
10167
|
-
};
|
|
10168
|
-
|
|
10169
|
-
es.onmessage = function(e) {
|
|
10170
|
-
handleMessage(e.data);
|
|
10171
|
-
};
|
|
10172
|
-
|
|
10173
|
-
es.onerror = function() {
|
|
10174
|
-
if (conn.status === 'connected') {
|
|
10175
|
-
setStatus('disconnected');
|
|
10176
|
-
}
|
|
10177
|
-
if (handlers.error) handlers.error(new Error('SSE connection error'));
|
|
10178
|
-
if (!reconnect) {
|
|
10179
|
-
es.close();
|
|
10180
|
-
}
|
|
10181
|
-
// EventSource auto-reconnects by default when reconnect=true
|
|
10182
|
-
};
|
|
10183
|
-
} else if (transport === 'poll') {
|
|
10184
|
-
var interval = opts.interval || 2000;
|
|
10185
|
-
setStatus('connected');
|
|
10186
|
-
conn._pollTimer = setInterval(function() {
|
|
10187
|
-
fetch(url).then(function(r) { return r.json(); }).then(function(msgs) {
|
|
10188
|
-
if (_isA(msgs)) {
|
|
10189
|
-
msgs.forEach(handleMessage);
|
|
10190
|
-
} else if (msgs && msgs.type) {
|
|
10191
|
-
handleMessage(msgs);
|
|
10192
|
-
}
|
|
10193
|
-
}).catch(function(e) {
|
|
10194
|
-
if (handlers.error) handlers.error(e);
|
|
10195
|
-
});
|
|
10196
|
-
}, interval);
|
|
10197
|
-
}
|
|
10198
|
-
|
|
10199
|
-
/**
|
|
10200
|
-
* Send an action to the server via POST.
|
|
10201
|
-
* @param {string} action - Action name
|
|
10202
|
-
* @param {Object} [data] - Action payload
|
|
10203
|
-
*/
|
|
10204
|
-
conn.sendAction = function(action, data) {
|
|
10205
|
-
var body = JSON.stringify({ type: 'action', action: action, data: data || {} });
|
|
10206
|
-
fetch(actionUrl, {
|
|
10207
|
-
method: 'POST',
|
|
10208
|
-
headers: { 'Content-Type': 'application/json' },
|
|
10209
|
-
body: body
|
|
10210
|
-
}).catch(function(e) {
|
|
10211
|
-
if (handlers.error) handlers.error(e);
|
|
10212
|
-
});
|
|
10213
|
-
};
|
|
10214
|
-
|
|
10215
|
-
/**
|
|
10216
|
-
* Register an event handler.
|
|
10217
|
-
* @param {string} event - 'open'|'message'|'error'|'close'
|
|
10218
|
-
* @param {Function} handler
|
|
10219
|
-
*/
|
|
10220
|
-
conn.on = function(event, handler) {
|
|
10221
|
-
handlers[event] = handler;
|
|
10222
|
-
return conn;
|
|
10223
|
-
};
|
|
10224
|
-
|
|
10225
|
-
/**
|
|
10226
|
-
* Close the connection.
|
|
10227
|
-
*/
|
|
10228
|
-
conn.close = function() {
|
|
10229
|
-
if (conn._es) {
|
|
10230
|
-
conn._es.close();
|
|
10231
|
-
conn._es = null;
|
|
10232
|
-
}
|
|
10233
|
-
if (conn._pollTimer) {
|
|
10234
|
-
clearInterval(conn._pollTimer);
|
|
10235
|
-
conn._pollTimer = null;
|
|
10236
|
-
}
|
|
10237
|
-
setStatus('disconnected');
|
|
10238
|
-
if (handlers.close) handlers.close();
|
|
10239
|
-
};
|
|
10240
|
-
|
|
10241
|
-
return conn;
|
|
10242
|
-
};
|
|
10243
10393
|
|
|
10244
10394
|
// ===================================================================================
|
|
10245
10395
|
// bw.inspect() — Debug utility
|
|
@@ -10448,7 +10598,7 @@
|
|
|
10448
10598
|
* @returns {Element} The style element
|
|
10449
10599
|
* @category CSS & Styling
|
|
10450
10600
|
* @see bw.css
|
|
10451
|
-
* @see bw.
|
|
10601
|
+
* @see bw.loadStyles
|
|
10452
10602
|
* @example
|
|
10453
10603
|
* bw.injectCSS('.my-class { color: red; }');
|
|
10454
10604
|
* bw.injectCSS({ '.card': { padding: '1rem' } }, { id: 'card-styles' });
|
|
@@ -10493,9 +10643,8 @@
|
|
|
10493
10643
|
* @param {...Object} styles - Style objects to merge (left-to-right)
|
|
10494
10644
|
* @returns {Object} Merged style object
|
|
10495
10645
|
* @category CSS & Styling
|
|
10496
|
-
* @see bw.u
|
|
10497
10646
|
* @example
|
|
10498
|
-
* var style = bw.s(
|
|
10647
|
+
* var style = bw.s({ display: 'flex' }, { gap: '1rem' }, { color: 'red' });
|
|
10499
10648
|
* // => { display: 'flex', gap: '1rem', color: 'red' }
|
|
10500
10649
|
*/
|
|
10501
10650
|
bw.s = function() {
|
|
@@ -10507,99 +10656,6 @@
|
|
|
10507
10656
|
return result;
|
|
10508
10657
|
};
|
|
10509
10658
|
|
|
10510
|
-
/**
|
|
10511
|
-
* Pre-built CSS utility objects (like Tailwind utilities, but in JS).
|
|
10512
|
-
*
|
|
10513
|
-
* Compose with `bw.s()` to build inline styles without writing raw CSS strings.
|
|
10514
|
-
* Includes flex, padding, margin, typography, color, border, and transition utilities.
|
|
10515
|
-
*
|
|
10516
|
-
* @category CSS & Styling
|
|
10517
|
-
* @see bw.s
|
|
10518
|
-
* @example
|
|
10519
|
-
* { t: 'div', a: { style: bw.s(bw.u.flex, bw.u.gap4, bw.u.p4) },
|
|
10520
|
-
* c: 'Flexbox with 1rem gap and padding' }
|
|
10521
|
-
*/
|
|
10522
|
-
bw.u = {
|
|
10523
|
-
// Display
|
|
10524
|
-
flex: { display: 'flex' },
|
|
10525
|
-
flexCol: { display: 'flex', flexDirection: 'column' },
|
|
10526
|
-
flexRow: { display: 'flex', flexDirection: 'row' },
|
|
10527
|
-
flexWrap: { display: 'flex', flexWrap: 'wrap' },
|
|
10528
|
-
block: { display: 'block' },
|
|
10529
|
-
inline: { display: 'inline' },
|
|
10530
|
-
hidden: { display: 'none' },
|
|
10531
|
-
|
|
10532
|
-
// Flex alignment
|
|
10533
|
-
justifyCenter: { justifyContent: 'center' },
|
|
10534
|
-
justifyBetween: { justifyContent: 'space-between' },
|
|
10535
|
-
justifyEnd: { justifyContent: 'flex-end' },
|
|
10536
|
-
alignCenter: { alignItems: 'center' },
|
|
10537
|
-
alignStart: { alignItems: 'flex-start' },
|
|
10538
|
-
alignEnd: { alignItems: 'flex-end' },
|
|
10539
|
-
|
|
10540
|
-
// Gap (0.25rem increments)
|
|
10541
|
-
gap1: { gap: '0.25rem' },
|
|
10542
|
-
gap2: { gap: '0.5rem' },
|
|
10543
|
-
gap3: { gap: '0.75rem' },
|
|
10544
|
-
gap4: { gap: '1rem' },
|
|
10545
|
-
gap6: { gap: '1.5rem' },
|
|
10546
|
-
gap8: { gap: '2rem' },
|
|
10547
|
-
|
|
10548
|
-
// Padding
|
|
10549
|
-
p0: { padding: '0' },
|
|
10550
|
-
p1: { padding: '0.25rem' },
|
|
10551
|
-
p2: { padding: '0.5rem' },
|
|
10552
|
-
p3: { padding: '0.75rem' },
|
|
10553
|
-
p4: { padding: '1rem' },
|
|
10554
|
-
p6: { padding: '1.5rem' },
|
|
10555
|
-
p8: { padding: '2rem' },
|
|
10556
|
-
px4: { paddingLeft: '1rem', paddingRight: '1rem' },
|
|
10557
|
-
py2: { paddingTop: '0.5rem', paddingBottom: '0.5rem' },
|
|
10558
|
-
py4: { paddingTop: '1rem', paddingBottom: '1rem' },
|
|
10559
|
-
|
|
10560
|
-
// Margin (same scale)
|
|
10561
|
-
m0: { margin: '0' },
|
|
10562
|
-
m4: { margin: '1rem' },
|
|
10563
|
-
mt2: { marginTop: '0.5rem' },
|
|
10564
|
-
mt4: { marginTop: '1rem' },
|
|
10565
|
-
mb2: { marginBottom: '0.5rem' },
|
|
10566
|
-
mb4: { marginBottom: '1rem' },
|
|
10567
|
-
mx_auto: { marginLeft: 'auto', marginRight: 'auto' },
|
|
10568
|
-
|
|
10569
|
-
// Typography
|
|
10570
|
-
textSm: { fontSize: '0.875rem' },
|
|
10571
|
-
textBase: { fontSize: '1rem' },
|
|
10572
|
-
textLg: { fontSize: '1.125rem' },
|
|
10573
|
-
textXl: { fontSize: '1.25rem' },
|
|
10574
|
-
text2xl: { fontSize: '1.5rem' },
|
|
10575
|
-
text3xl: { fontSize: '1.875rem' },
|
|
10576
|
-
bold: { fontWeight: '700' },
|
|
10577
|
-
semibold: { fontWeight: '600' },
|
|
10578
|
-
italic: { fontStyle: 'italic' },
|
|
10579
|
-
textCenter: { textAlign: 'center' },
|
|
10580
|
-
textRight: { textAlign: 'right' },
|
|
10581
|
-
|
|
10582
|
-
// Colors (from design tokens)
|
|
10583
|
-
bgWhite: { background: '#ffffff' },
|
|
10584
|
-
bgTeal: { background: '#006666', color: '#ffffff' },
|
|
10585
|
-
textWhite: { color: '#ffffff' },
|
|
10586
|
-
textTeal: { color: '#006666' },
|
|
10587
|
-
textMuted: { color: '#888' },
|
|
10588
|
-
|
|
10589
|
-
// Borders
|
|
10590
|
-
rounded: { borderRadius: '0.375rem' },
|
|
10591
|
-
roundedLg: { borderRadius: '0.5rem' },
|
|
10592
|
-
roundedFull: { borderRadius: '9999px' },
|
|
10593
|
-
border: { border: '1px solid #d8d8d8' },
|
|
10594
|
-
|
|
10595
|
-
// Sizing
|
|
10596
|
-
wFull: { width: '100%' },
|
|
10597
|
-
hFull: { height: '100%' },
|
|
10598
|
-
|
|
10599
|
-
// Transitions
|
|
10600
|
-
transition: { transition: 'all 0.2s ease' }
|
|
10601
|
-
};
|
|
10602
|
-
|
|
10603
10659
|
/**
|
|
10604
10660
|
* Generate responsive CSS with media query breakpoints.
|
|
10605
10661
|
*
|
|
@@ -10721,103 +10777,49 @@
|
|
|
10721
10777
|
};
|
|
10722
10778
|
}
|
|
10723
10779
|
|
|
10724
|
-
/**
|
|
10725
|
-
* Load the built-in Bootstrap-inspired default stylesheet.
|
|
10726
|
-
*
|
|
10727
|
-
* Injects bitwrench's batteries-included CSS (buttons, cards, grids, forms,
|
|
10728
|
-
* alerts, badges, nav, tabs, etc.) into the document head. Call once at app startup.
|
|
10729
|
-
* Returns null in Node.js (no DOM).
|
|
10730
|
-
*
|
|
10731
|
-
* @param {Object} [options] - Style loading options
|
|
10732
|
-
* @param {boolean} [options.minify=true] - Minify the CSS output
|
|
10733
|
-
* @returns {Element|null} Style element if in browser, null in Node.js
|
|
10734
|
-
* @category CSS & Styling
|
|
10735
|
-
* @see bw.setTheme
|
|
10736
|
-
* @see bw.applyTheme
|
|
10737
|
-
* @see bw.toggleTheme
|
|
10738
|
-
* @example
|
|
10739
|
-
* bw.loadDefaultStyles(); // inject all default CSS
|
|
10740
|
-
*/
|
|
10741
|
-
bw.loadDefaultStyles = function(options = {}) {
|
|
10742
|
-
const { minify = true, palette } = options;
|
|
10743
|
-
|
|
10744
|
-
// 1. Inject structural CSS (layout, sizing — never changes with theme)
|
|
10745
|
-
if (bw._isBrowser) {
|
|
10746
|
-
var structuralCSS = bw.css(getStructuralStyles());
|
|
10747
|
-
bw.injectCSS(structuralCSS, { id: 'bw_structural', append: false, minify: minify });
|
|
10748
|
-
}
|
|
10749
10780
|
|
|
10750
|
-
|
|
10751
|
-
|
|
10752
|
-
|
|
10753
|
-
return result;
|
|
10754
|
-
};
|
|
10781
|
+
// =========================================================================
|
|
10782
|
+
// v2.0.18 Clean Styles API — makeStyles / applyStyles / loadStyles / etc.
|
|
10783
|
+
// =========================================================================
|
|
10755
10784
|
|
|
10785
|
+
/**
|
|
10786
|
+
* Convert a scope selector to a <style> element id.
|
|
10787
|
+
* @private
|
|
10788
|
+
* @param {string} [scope] - Scope selector (e.g. '#my-dashboard', '.preview')
|
|
10789
|
+
* @returns {string} Style element id (e.g. 'bw_style_my_dashboard')
|
|
10790
|
+
*/
|
|
10791
|
+
function _scopeToStyleId(scope) {
|
|
10792
|
+
if (!scope || scope === '' || scope === 'global') return 'bw_style_global';
|
|
10793
|
+
if (scope === 'reset') return 'bw_style_reset';
|
|
10794
|
+
// Strip leading # or . and convert - to _
|
|
10795
|
+
var clean = scope.replace(/^[#.]/, '').replace(/-/g, '_');
|
|
10796
|
+
return 'bw_style_' + clean;
|
|
10797
|
+
}
|
|
10756
10798
|
|
|
10757
10799
|
/**
|
|
10758
|
-
* Generate a complete
|
|
10800
|
+
* Generate a complete styles object from seed colors and layout config.
|
|
10801
|
+
* Pure function — no DOM, no state, no side effects.
|
|
10759
10802
|
*
|
|
10760
|
-
*
|
|
10761
|
-
* forms, nav, tables, tabs, list groups, pagination, progress, hero, utilities)
|
|
10762
|
-
* scoped under `.name` class. Multiple themes can coexist in the stylesheet.
|
|
10763
|
-
* Swap themes by changing the class on a container element.
|
|
10803
|
+
* All parameters are optional. Defaults to the bitwrench default palette.
|
|
10764
10804
|
*
|
|
10765
|
-
* @param {
|
|
10766
|
-
* @param {
|
|
10767
|
-
* @param {string} config.
|
|
10768
|
-
* @param {string} config.
|
|
10769
|
-
* @param {string} [config.tertiary] - Tertiary/accent color hex (defaults to primary)
|
|
10770
|
-
* @param {string} [config.success='#198754'] - Success color hex
|
|
10771
|
-
* @param {string} [config.danger='#dc3545'] - Danger color hex
|
|
10772
|
-
* @param {string} [config.warning='#ffc107'] - Warning color hex
|
|
10773
|
-
* @param {string} [config.info='#0dcaf0'] - Info color hex
|
|
10774
|
-
* @param {string} [config.light='#f8f9fa'] - Light color hex
|
|
10775
|
-
* @param {string} [config.dark='#212529'] - Dark color hex
|
|
10776
|
-
* @param {string} [config.background] - Page background hex (default: '#ffffff' light, derived dark)
|
|
10777
|
-
* @param {string} [config.surface] - Surface/card background hex (default: '#f8f9fa' light, derived dark)
|
|
10805
|
+
* @param {Object} [config] - Style configuration
|
|
10806
|
+
* @param {string} [config.primary='#006666'] - Primary brand color hex
|
|
10807
|
+
* @param {string} [config.secondary='#6c757d'] - Secondary color hex
|
|
10808
|
+
* @param {string} [config.tertiary] - Tertiary color hex (defaults to primary)
|
|
10778
10809
|
* @param {string} [config.spacing='normal'] - 'compact' | 'normal' | 'spacious'
|
|
10779
10810
|
* @param {string} [config.radius='md'] - 'none' | 'sm' | 'md' | 'lg' | 'pill'
|
|
10780
|
-
* @
|
|
10781
|
-
* @param {string|number} [config.typeRatio='normal'] - 'tight' | 'normal' | 'relaxed' | 'dramatic' or a number
|
|
10782
|
-
* @param {string} [config.elevation='md'] - 'flat' | 'sm' | 'md' | 'lg'
|
|
10783
|
-
* @param {string} [config.motion='standard'] - 'reduced' | 'standard' | 'expressive'
|
|
10784
|
-
* @param {number} [config.harmonize=0.20] - 0-1, semantic color hue shift toward primary
|
|
10785
|
-
* @param {boolean} [config.inject=true] - Inject into DOM (browser only)
|
|
10786
|
-
* @returns {Object} { css, palette, name, isLightPrimary, alternate: { css, palette } }
|
|
10811
|
+
* @returns {Object} { css, alternateCss, rules, alternateRules, palette, alternatePalette, isLightPrimary }
|
|
10787
10812
|
* @category CSS & Styling
|
|
10788
|
-
* @see bw.
|
|
10789
|
-
* @see bw.
|
|
10790
|
-
* @see bw.loadDefaultStyles
|
|
10813
|
+
* @see bw.applyStyles
|
|
10814
|
+
* @see bw.loadStyles
|
|
10791
10815
|
* @example
|
|
10792
|
-
*
|
|
10793
|
-
*
|
|
10794
|
-
*
|
|
10795
|
-
* secondary: '#90e0ef',
|
|
10796
|
-
* tertiary: '#00b4d8'
|
|
10797
|
-
* });
|
|
10798
|
-
*
|
|
10799
|
-
* // Apply to a container
|
|
10800
|
-
* document.getElementById('app').classList.add('ocean');
|
|
10801
|
-
*
|
|
10802
|
-
* // Toggle to alternate palette
|
|
10803
|
-
* bw.toggleTheme();
|
|
10804
|
-
*
|
|
10805
|
-
* // Generate CSS for static export (Node.js)
|
|
10806
|
-
* var result = bw.generateTheme('sunset', {
|
|
10807
|
-
* primary: '#e76f51',
|
|
10808
|
-
* secondary: '#264653',
|
|
10809
|
-
* inject: false
|
|
10810
|
-
* });
|
|
10811
|
-
* fs.writeFileSync('sunset.css', result.css + result.alternate.css);
|
|
10816
|
+
* var styles = bw.makeStyles({ primary: '#4f46e5', secondary: '#d97706' });
|
|
10817
|
+
* console.log(styles.palette.primary.base); // '#4f46e5'
|
|
10818
|
+
* // styles.css contains all themed CSS — nothing injected
|
|
10812
10819
|
*/
|
|
10813
|
-
bw.
|
|
10814
|
-
|
|
10815
|
-
|
|
10816
|
-
}
|
|
10817
|
-
|
|
10818
|
-
// Merge with defaults; if user didn't supply tertiary, default to their primary
|
|
10819
|
-
var fullConfig = Object.assign({}, DEFAULT_PALETTE_CONFIG, config);
|
|
10820
|
-
if (!config.tertiary) fullConfig.tertiary = fullConfig.primary;
|
|
10820
|
+
bw.makeStyles = function(config) {
|
|
10821
|
+
var fullConfig = Object.assign({}, DEFAULT_PALETTE_CONFIG, config || {});
|
|
10822
|
+
if (config && !config.tertiary) fullConfig.tertiary = fullConfig.primary;
|
|
10821
10823
|
|
|
10822
10824
|
// Derive primary palette
|
|
10823
10825
|
var palette = derivePalette(fullConfig);
|
|
@@ -10825,131 +10827,207 @@
|
|
|
10825
10827
|
// Resolve layout
|
|
10826
10828
|
var layout = resolveLayout(fullConfig);
|
|
10827
10829
|
|
|
10828
|
-
// Generate primary themed CSS rules
|
|
10829
|
-
var themedRules = generateThemedCSS(
|
|
10830
|
+
// Generate primary themed CSS rules (unscoped)
|
|
10831
|
+
var themedRules = generateThemedCSS('', palette, layout);
|
|
10830
10832
|
var cssStr = bw.css(themedRules);
|
|
10831
10833
|
|
|
10832
10834
|
// Derive alternate palette (luminance-inverted)
|
|
10833
10835
|
var altConfig = deriveAlternateConfig(fullConfig);
|
|
10834
10836
|
var altPalette = derivePalette(altConfig);
|
|
10835
10837
|
|
|
10836
|
-
// Generate alternate CSS
|
|
10837
|
-
|
|
10838
|
-
var
|
|
10838
|
+
// Generate alternate CSS rules WITHOUT .bw_theme_alt prefix (raw rules)
|
|
10839
|
+
// applyStyles() wraps them appropriately based on scope
|
|
10840
|
+
var altRawRules = generateThemedCSS('', altPalette, layout);
|
|
10841
|
+
|
|
10842
|
+
// Add body-level surface overrides for the alternate palette.
|
|
10843
|
+
// When .bw_theme_alt is on <html>, ".bw_theme_alt body" correctly matches.
|
|
10844
|
+
altRawRules['body'] = {
|
|
10845
|
+
'color': altPalette.dark.base,
|
|
10846
|
+
'background-color': altPalette.surface || altPalette.light.base
|
|
10847
|
+
};
|
|
10848
|
+
|
|
10849
|
+
var altCssStr = bw.css(altRawRules);
|
|
10839
10850
|
|
|
10840
10851
|
// Determine if primary is light-flavored
|
|
10841
10852
|
var lightPrimary = isLightPalette(fullConfig);
|
|
10842
10853
|
|
|
10843
|
-
|
|
10844
|
-
|
|
10845
|
-
|
|
10846
|
-
|
|
10847
|
-
|
|
10848
|
-
|
|
10849
|
-
|
|
10850
|
-
|
|
10851
|
-
|
|
10854
|
+
return {
|
|
10855
|
+
css: cssStr,
|
|
10856
|
+
alternateCss: altCssStr,
|
|
10857
|
+
rules: themedRules,
|
|
10858
|
+
alternateRules: altRawRules,
|
|
10859
|
+
palette: palette,
|
|
10860
|
+
alternatePalette: altPalette,
|
|
10861
|
+
isLightPrimary: lightPrimary
|
|
10862
|
+
};
|
|
10863
|
+
};
|
|
10852
10864
|
|
|
10853
|
-
|
|
10865
|
+
/**
|
|
10866
|
+
* Inject styles into the DOM with optional scoping.
|
|
10867
|
+
*
|
|
10868
|
+
* Takes a styles object from `makeStyles()` and creates a single `<style>`
|
|
10869
|
+
* element in `<head>`. If a scope selector is provided, all CSS rules are
|
|
10870
|
+
* wrapped under that selector. Alternate CSS is wrapped under `.bw_theme_alt`.
|
|
10871
|
+
*
|
|
10872
|
+
* @param {Object} styles - Result of `bw.makeStyles()`
|
|
10873
|
+
* @param {string} [scope] - Scope selector (e.g. '#my-dashboard', '.preview'). Omit for global.
|
|
10874
|
+
* @returns {Element|null} The `<style>` element, or null in Node.js
|
|
10875
|
+
* @category CSS & Styling
|
|
10876
|
+
* @see bw.makeStyles
|
|
10877
|
+
* @see bw.loadStyles
|
|
10878
|
+
* @see bw.clearStyles
|
|
10879
|
+
* @example
|
|
10880
|
+
* var styles = bw.makeStyles({ primary: '#4f46e5' });
|
|
10881
|
+
* bw.applyStyles(styles); // global
|
|
10882
|
+
* bw.applyStyles(styles, '#my-dashboard'); // scoped
|
|
10883
|
+
*/
|
|
10884
|
+
bw.applyStyles = function(styles, scope) {
|
|
10885
|
+
if (!bw._isBrowser) return null;
|
|
10886
|
+
if (!styles || !styles.rules) {
|
|
10887
|
+
_cw('bw.applyStyles: invalid styles object');
|
|
10888
|
+
return null;
|
|
10854
10889
|
}
|
|
10855
10890
|
|
|
10856
|
-
|
|
10857
|
-
|
|
10858
|
-
|
|
10859
|
-
|
|
10860
|
-
|
|
10861
|
-
|
|
10891
|
+
var styleId = _scopeToStyleId(scope);
|
|
10892
|
+
|
|
10893
|
+
// Scope the primary rules if a scope is provided
|
|
10894
|
+
var primaryRules = styles.rules;
|
|
10895
|
+
if (scope) {
|
|
10896
|
+
primaryRules = scopeRulesUnder(primaryRules, scope);
|
|
10862
10897
|
}
|
|
10863
10898
|
|
|
10864
|
-
//
|
|
10865
|
-
var
|
|
10866
|
-
|
|
10867
|
-
|
|
10868
|
-
|
|
10869
|
-
|
|
10870
|
-
|
|
10871
|
-
|
|
10872
|
-
|
|
10899
|
+
// Wrap alternate rules with .bw_theme_alt
|
|
10900
|
+
var altRules = styles.alternateRules;
|
|
10901
|
+
if (altRules) {
|
|
10902
|
+
if (scope) {
|
|
10903
|
+
// Scoped compound: #scope.bw_theme_alt .bw_card
|
|
10904
|
+
altRules = scopeRulesUnder(altRules, scope + '.bw_theme_alt');
|
|
10905
|
+
} else {
|
|
10906
|
+
// Global: .bw_theme_alt .bw_card
|
|
10907
|
+
altRules = scopeRulesUnder(altRules, '.bw_theme_alt');
|
|
10873
10908
|
}
|
|
10874
|
-
}
|
|
10875
|
-
bw._activeTheme = result;
|
|
10876
|
-
bw._activeThemeMode = 'primary';
|
|
10909
|
+
}
|
|
10877
10910
|
|
|
10878
|
-
|
|
10911
|
+
// Combine primary + alternate into one CSS string
|
|
10912
|
+
var combined = bw.css(primaryRules);
|
|
10913
|
+
if (altRules) {
|
|
10914
|
+
combined += '\n' + bw.css(altRules);
|
|
10915
|
+
}
|
|
10916
|
+
|
|
10917
|
+
return bw.injectCSS(combined, { id: styleId, append: false });
|
|
10879
10918
|
};
|
|
10880
10919
|
|
|
10881
10920
|
/**
|
|
10882
|
-
*
|
|
10883
|
-
*
|
|
10921
|
+
* Generate and apply styles in one call. Convenience wrapper.
|
|
10922
|
+
*
|
|
10923
|
+
* Equivalent to: `bw.applyStyles(bw.makeStyles(config), scope)`
|
|
10884
10924
|
*
|
|
10885
|
-
* @param {
|
|
10886
|
-
* @
|
|
10925
|
+
* @param {Object} [config] - Style configuration (same as `makeStyles`)
|
|
10926
|
+
* @param {string} [scope] - Scope selector (same as `applyStyles`)
|
|
10927
|
+
* @returns {Element|null} The `<style>` element, or null in Node.js
|
|
10887
10928
|
* @category CSS & Styling
|
|
10888
|
-
* @see bw.
|
|
10889
|
-
* @see bw.
|
|
10929
|
+
* @see bw.makeStyles
|
|
10930
|
+
* @see bw.applyStyles
|
|
10890
10931
|
* @example
|
|
10891
|
-
* bw.
|
|
10892
|
-
* bw.
|
|
10893
|
-
* bw.
|
|
10894
|
-
*/
|
|
10895
|
-
bw.
|
|
10896
|
-
|
|
10897
|
-
|
|
10898
|
-
|
|
10899
|
-
|
|
10900
|
-
|
|
10901
|
-
|
|
10902
|
-
|
|
10903
|
-
else if (mode === 'light') wantAlt = !isLight;
|
|
10904
|
-
else if (mode === 'dark') wantAlt = isLight;
|
|
10905
|
-
else wantAlt = false;
|
|
10906
|
-
|
|
10907
|
-
if (wantAlt) {
|
|
10908
|
-
root.classList.add('bw_theme_alt');
|
|
10909
|
-
} else {
|
|
10910
|
-
root.classList.remove('bw_theme_alt');
|
|
10932
|
+
* bw.loadStyles(); // defaults, global
|
|
10933
|
+
* bw.loadStyles({ primary: '#4f46e5' }); // custom, global
|
|
10934
|
+
* bw.loadStyles({ primary: '#4f46e5' }, '#my-dashboard'); // custom, scoped
|
|
10935
|
+
*/
|
|
10936
|
+
bw.loadStyles = function(config, scope) {
|
|
10937
|
+
// Also inject structural CSS first (only once)
|
|
10938
|
+
if (bw._isBrowser) {
|
|
10939
|
+
var existing = document.getElementById('bw_structural');
|
|
10940
|
+
if (!existing) {
|
|
10941
|
+
var structuralCSS = bw.css(getStructuralStyles());
|
|
10942
|
+
bw.injectCSS(structuralCSS, { id: 'bw_structural', append: false });
|
|
10943
|
+
}
|
|
10911
10944
|
}
|
|
10945
|
+
return bw.applyStyles(bw.makeStyles(config), scope);
|
|
10946
|
+
};
|
|
10912
10947
|
|
|
10913
|
-
|
|
10914
|
-
|
|
10948
|
+
/**
|
|
10949
|
+
* Inject the CSS reset (box-sizing, html/body font, reduced-motion).
|
|
10950
|
+
* Idempotent — if already injected, returns the existing `<style>` element.
|
|
10951
|
+
*
|
|
10952
|
+
* @returns {Element|null} The `<style>` element, or null in Node.js
|
|
10953
|
+
* @category CSS & Styling
|
|
10954
|
+
* @see bw.loadStyles
|
|
10955
|
+
* @see bw.clearStyles
|
|
10956
|
+
* @example
|
|
10957
|
+
* bw.loadReset(); // inject once, safe to call multiple times
|
|
10958
|
+
*/
|
|
10959
|
+
bw.loadReset = function() {
|
|
10960
|
+
if (!bw._isBrowser) return null;
|
|
10961
|
+
var existing = document.getElementById('bw_style_reset');
|
|
10962
|
+
if (existing) return existing;
|
|
10963
|
+
return bw.injectCSS(bw.css(getResetStyles()), { id: 'bw_style_reset', append: false });
|
|
10915
10964
|
};
|
|
10916
10965
|
|
|
10917
10966
|
/**
|
|
10918
|
-
* Toggle between primary and alternate
|
|
10967
|
+
* Toggle between primary and alternate palettes.
|
|
10968
|
+
*
|
|
10969
|
+
* Adds/removes the `bw_theme_alt` class on the scoping element.
|
|
10970
|
+
* Without a scope, toggles on `<html>` (global).
|
|
10971
|
+
* With a scope, toggles on the first matching element.
|
|
10919
10972
|
*
|
|
10973
|
+
* @param {string} [scope] - Scope selector (e.g. '#my-dashboard'). Omit for global.
|
|
10920
10974
|
* @returns {string} Active mode after toggle: 'primary' or 'alternate'
|
|
10921
10975
|
* @category CSS & Styling
|
|
10922
|
-
* @see bw.
|
|
10923
|
-
* @see bw.
|
|
10976
|
+
* @see bw.applyStyles
|
|
10977
|
+
* @see bw.clearStyles
|
|
10924
10978
|
* @example
|
|
10925
|
-
* bw.
|
|
10979
|
+
* bw.toggleStyles(); // global toggle on <html>
|
|
10980
|
+
* bw.toggleStyles('#my-dashboard'); // scoped toggle
|
|
10926
10981
|
*/
|
|
10927
|
-
bw.
|
|
10928
|
-
|
|
10929
|
-
|
|
10982
|
+
bw.toggleStyles = function(scope) {
|
|
10983
|
+
if (!bw._isBrowser) return 'primary';
|
|
10984
|
+
var target;
|
|
10985
|
+
if (scope) {
|
|
10986
|
+
var els = bw.$(scope);
|
|
10987
|
+
target = els[0];
|
|
10988
|
+
} else {
|
|
10989
|
+
target = document.documentElement;
|
|
10990
|
+
}
|
|
10991
|
+
if (!target) return 'primary';
|
|
10992
|
+
|
|
10993
|
+
var hasAlt = target.classList.contains('bw_theme_alt');
|
|
10994
|
+
if (hasAlt) {
|
|
10995
|
+
target.classList.remove('bw_theme_alt');
|
|
10996
|
+
return 'primary';
|
|
10997
|
+
} else {
|
|
10998
|
+
target.classList.add('bw_theme_alt');
|
|
10999
|
+
return 'alternate';
|
|
11000
|
+
}
|
|
10930
11001
|
};
|
|
10931
11002
|
|
|
10932
11003
|
/**
|
|
10933
|
-
* Remove
|
|
10934
|
-
*
|
|
10935
|
-
*
|
|
11004
|
+
* Remove injected styles for a given scope.
|
|
11005
|
+
*
|
|
11006
|
+
* Finds the `<style>` element by id and removes it. Also removes
|
|
11007
|
+
* the `bw_theme_alt` class from the relevant element.
|
|
10936
11008
|
*
|
|
11009
|
+
* @param {string} [scope] - Scope selector. Omit to remove global styles.
|
|
10937
11010
|
* @category CSS & Styling
|
|
10938
|
-
* @see bw.
|
|
11011
|
+
* @see bw.applyStyles
|
|
11012
|
+
* @see bw.loadStyles
|
|
10939
11013
|
* @example
|
|
10940
|
-
* bw.
|
|
10941
|
-
* bw.
|
|
10942
|
-
|
|
10943
|
-
|
|
10944
|
-
|
|
10945
|
-
|
|
10946
|
-
|
|
10947
|
-
|
|
10948
|
-
|
|
10949
|
-
|
|
11014
|
+
* bw.clearStyles(); // remove global styles
|
|
11015
|
+
* bw.clearStyles('#my-dashboard'); // remove scoped styles
|
|
11016
|
+
* bw.clearStyles('reset'); // remove the CSS reset
|
|
11017
|
+
*/
|
|
11018
|
+
bw.clearStyles = function(scope) {
|
|
11019
|
+
if (!bw._isBrowser) return;
|
|
11020
|
+
var styleId = _scopeToStyleId(scope);
|
|
11021
|
+
var el = document.getElementById(styleId);
|
|
11022
|
+
if (el) el.remove();
|
|
11023
|
+
|
|
11024
|
+
// Also remove bw_theme_alt from the relevant element
|
|
11025
|
+
if (scope && scope !== 'reset' && scope !== 'global') {
|
|
11026
|
+
var targets = bw.$(scope);
|
|
11027
|
+
if (targets[0]) targets[0].classList.remove('bw_theme_alt');
|
|
11028
|
+
} else if (!scope || scope === 'global') {
|
|
11029
|
+
document.documentElement.classList.remove('bw_theme_alt');
|
|
10950
11030
|
}
|
|
10951
|
-
bw._activeTheme = null;
|
|
10952
|
-
bw._activeThemeMode = 'primary';
|
|
10953
11031
|
};
|
|
10954
11032
|
|
|
10955
11033
|
// Expose color utility functions on bw namespace
|
|
@@ -11172,10 +11250,15 @@
|
|
|
11172
11250
|
* @param {Object} config - Table configuration
|
|
11173
11251
|
* @param {Array<Object>} config.data - Array of row objects to display
|
|
11174
11252
|
* @param {Array<Object>} [config.columns] - Column definitions with key, label, render
|
|
11175
|
-
* @param {string} [config.className='
|
|
11253
|
+
* @param {string} [config.className=''] - Additional CSS classes for table element
|
|
11176
11254
|
* @param {boolean} [config.sortable=true] - Enable click-to-sort headers
|
|
11177
11255
|
* @param {Function} [config.onSort] - Sort callback (column, direction)
|
|
11178
|
-
* @
|
|
11256
|
+
* @param {boolean} [config.selectable=false] - Enable row selection on click
|
|
11257
|
+
* @param {Function} [config.onRowClick] - Row click callback (row, index, event)
|
|
11258
|
+
* @param {number} [config.pageSize] - Rows per page (enables pagination when set)
|
|
11259
|
+
* @param {number} [config.currentPage=1] - Current page number (1-based)
|
|
11260
|
+
* @param {Function} [config.onPageChange] - Page change callback (newPage)
|
|
11261
|
+
* @returns {Object} TACO object for table (with optional pagination controls)
|
|
11179
11262
|
* @category Component Builders
|
|
11180
11263
|
* @see bw.makeDataTable
|
|
11181
11264
|
* @example
|
|
@@ -11187,7 +11270,12 @@
|
|
|
11187
11270
|
* columns: [
|
|
11188
11271
|
* { key: 'name', label: 'Name' },
|
|
11189
11272
|
* { key: 'age', label: 'Age' }
|
|
11190
|
-
* ]
|
|
11273
|
+
* ],
|
|
11274
|
+
* selectable: true,
|
|
11275
|
+
* onRowClick: function(row, i) { console.log('clicked', row.name); },
|
|
11276
|
+
* pageSize: 10,
|
|
11277
|
+
* currentPage: 1,
|
|
11278
|
+
* onPageChange: function(page) { console.log('page', page); }
|
|
11191
11279
|
* });
|
|
11192
11280
|
*/
|
|
11193
11281
|
bw.makeTable = function(config) {
|
|
@@ -11200,41 +11288,47 @@
|
|
|
11200
11288
|
sortable = true,
|
|
11201
11289
|
onSort,
|
|
11202
11290
|
sortColumn,
|
|
11203
|
-
sortDirection = 'asc'
|
|
11291
|
+
sortDirection = 'asc',
|
|
11292
|
+
selectable = false,
|
|
11293
|
+
onRowClick,
|
|
11294
|
+
pageSize,
|
|
11295
|
+
currentPage = 1,
|
|
11296
|
+
onPageChange
|
|
11204
11297
|
} = config;
|
|
11205
11298
|
|
|
11206
|
-
// Build class list: always include bw_table, add striped/hover, append user className
|
|
11299
|
+
// Build class list: always include bw_table, add striped/hover/selectable, append user className
|
|
11207
11300
|
let cls = 'bw_table';
|
|
11208
11301
|
if (striped) cls += ' bw_table_striped';
|
|
11209
|
-
if (hover) cls += ' bw_table_hover';
|
|
11302
|
+
if (hover || selectable) cls += ' bw_table_hover';
|
|
11303
|
+
if (selectable) cls += ' bw_table_selectable';
|
|
11210
11304
|
if (className) cls += ' ' + className;
|
|
11211
11305
|
cls = cls.trim();
|
|
11212
|
-
|
|
11306
|
+
|
|
11213
11307
|
// Auto-detect columns if not provided
|
|
11214
|
-
const cols = columns || (data.length > 0
|
|
11308
|
+
const cols = columns || (data.length > 0
|
|
11215
11309
|
? _keys(data[0]).map(key => ({ key, label: key }))
|
|
11216
11310
|
: []);
|
|
11217
|
-
|
|
11311
|
+
|
|
11218
11312
|
// Current sort state
|
|
11219
11313
|
let currentSortColumn = sortColumn || null;
|
|
11220
11314
|
let currentSortDirection = sortDirection;
|
|
11221
|
-
|
|
11315
|
+
|
|
11222
11316
|
// Sort data if column specified
|
|
11223
11317
|
let sortedData = [...data];
|
|
11224
11318
|
if (currentSortColumn) {
|
|
11225
11319
|
sortedData.sort((a, b) => {
|
|
11226
11320
|
const aVal = a[currentSortColumn];
|
|
11227
11321
|
const bVal = b[currentSortColumn];
|
|
11228
|
-
|
|
11322
|
+
|
|
11229
11323
|
// Handle different types
|
|
11230
11324
|
if (_is(aVal, 'number') && _is(bVal, 'number')) {
|
|
11231
11325
|
return currentSortDirection === 'asc' ? aVal - bVal : bVal - aVal;
|
|
11232
11326
|
}
|
|
11233
|
-
|
|
11327
|
+
|
|
11234
11328
|
// String comparison
|
|
11235
11329
|
const aStr = String(aVal || '').toLowerCase();
|
|
11236
11330
|
const bStr = String(bVal || '').toLowerCase();
|
|
11237
|
-
|
|
11331
|
+
|
|
11238
11332
|
if (currentSortDirection === 'asc') {
|
|
11239
11333
|
return aStr.localeCompare(bStr);
|
|
11240
11334
|
} else {
|
|
@@ -11242,23 +11336,32 @@
|
|
|
11242
11336
|
}
|
|
11243
11337
|
});
|
|
11244
11338
|
}
|
|
11245
|
-
|
|
11339
|
+
|
|
11340
|
+
// Pagination
|
|
11341
|
+
const totalRows = sortedData.length;
|
|
11342
|
+
const totalPages = pageSize ? Math.max(1, Math.ceil(totalRows / pageSize)) : 1;
|
|
11343
|
+
const page = Math.max(1, Math.min(currentPage, totalPages));
|
|
11344
|
+
if (pageSize) {
|
|
11345
|
+
const start = (page - 1) * pageSize;
|
|
11346
|
+
sortedData = sortedData.slice(start, start + pageSize);
|
|
11347
|
+
}
|
|
11348
|
+
|
|
11246
11349
|
// Create sort handler
|
|
11247
11350
|
const handleSort = (column) => {
|
|
11248
11351
|
if (!sortable) return;
|
|
11249
|
-
|
|
11352
|
+
|
|
11250
11353
|
if (currentSortColumn === column) {
|
|
11251
11354
|
currentSortDirection = currentSortDirection === 'asc' ? 'desc' : 'asc';
|
|
11252
11355
|
} else {
|
|
11253
11356
|
currentSortColumn = column;
|
|
11254
11357
|
currentSortDirection = 'asc';
|
|
11255
11358
|
}
|
|
11256
|
-
|
|
11359
|
+
|
|
11257
11360
|
if (onSort) {
|
|
11258
11361
|
onSort(column, currentSortDirection);
|
|
11259
11362
|
}
|
|
11260
11363
|
};
|
|
11261
|
-
|
|
11364
|
+
|
|
11262
11365
|
// Build table header
|
|
11263
11366
|
const thead = {
|
|
11264
11367
|
t: 'thead',
|
|
@@ -11281,24 +11384,87 @@
|
|
|
11281
11384
|
}))
|
|
11282
11385
|
}
|
|
11283
11386
|
};
|
|
11284
|
-
|
|
11285
|
-
// Build table body
|
|
11387
|
+
|
|
11388
|
+
// Build table body with selectable/onRowClick support
|
|
11286
11389
|
const tbody = {
|
|
11287
11390
|
t: 'tbody',
|
|
11288
|
-
c: sortedData.map(row =>
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
|
|
11294
|
-
|
|
11391
|
+
c: sortedData.map((row, idx) => {
|
|
11392
|
+
const globalIdx = pageSize ? (page - 1) * pageSize + idx : idx;
|
|
11393
|
+
const rowAttrs = {};
|
|
11394
|
+
if (selectable || onRowClick) {
|
|
11395
|
+
rowAttrs.style = 'cursor:pointer;';
|
|
11396
|
+
rowAttrs.onclick = function(e) {
|
|
11397
|
+
if (selectable) {
|
|
11398
|
+
// Toggle selected class on this row
|
|
11399
|
+
var tr = e.currentTarget;
|
|
11400
|
+
tr.classList.toggle('bw_table_row_selected');
|
|
11401
|
+
}
|
|
11402
|
+
if (onRowClick) {
|
|
11403
|
+
onRowClick(row, globalIdx, e);
|
|
11404
|
+
}
|
|
11405
|
+
};
|
|
11406
|
+
}
|
|
11407
|
+
return {
|
|
11408
|
+
t: 'tr',
|
|
11409
|
+
a: rowAttrs,
|
|
11410
|
+
c: cols.map(col => ({
|
|
11411
|
+
t: 'td',
|
|
11412
|
+
c: col.render ? col.render(row[col.key], row) : String(row[col.key] || '')
|
|
11413
|
+
}))
|
|
11414
|
+
};
|
|
11415
|
+
})
|
|
11295
11416
|
};
|
|
11296
|
-
|
|
11297
|
-
|
|
11417
|
+
|
|
11418
|
+
const table = {
|
|
11298
11419
|
t: 'table',
|
|
11299
11420
|
a: { class: cls },
|
|
11300
11421
|
c: [thead, tbody]
|
|
11301
11422
|
};
|
|
11423
|
+
|
|
11424
|
+
// If no pagination, return table directly
|
|
11425
|
+
if (!pageSize) return table;
|
|
11426
|
+
|
|
11427
|
+
// Build pagination controls
|
|
11428
|
+
const pageButtons = [];
|
|
11429
|
+
// Previous button
|
|
11430
|
+
pageButtons.push({
|
|
11431
|
+
t: 'button',
|
|
11432
|
+
a: {
|
|
11433
|
+
class: 'bw_btn bw_btn_sm',
|
|
11434
|
+
disabled: page <= 1 ? 'disabled' : undefined,
|
|
11435
|
+
onclick: page > 1 && onPageChange ? function() { onPageChange(page - 1); } : undefined
|
|
11436
|
+
},
|
|
11437
|
+
c: 'Prev'
|
|
11438
|
+
});
|
|
11439
|
+
// Page info
|
|
11440
|
+
pageButtons.push({
|
|
11441
|
+
t: 'span',
|
|
11442
|
+
a: { style: 'margin:0 0.5rem;font-size:0.875rem;' },
|
|
11443
|
+
c: 'Page ' + page + ' of ' + totalPages
|
|
11444
|
+
});
|
|
11445
|
+
// Next button
|
|
11446
|
+
pageButtons.push({
|
|
11447
|
+
t: 'button',
|
|
11448
|
+
a: {
|
|
11449
|
+
class: 'bw_btn bw_btn_sm',
|
|
11450
|
+
disabled: page >= totalPages ? 'disabled' : undefined,
|
|
11451
|
+
onclick: page < totalPages && onPageChange ? function() { onPageChange(page + 1); } : undefined
|
|
11452
|
+
},
|
|
11453
|
+
c: 'Next'
|
|
11454
|
+
});
|
|
11455
|
+
|
|
11456
|
+
return {
|
|
11457
|
+
t: 'div',
|
|
11458
|
+
a: { class: 'bw_table_paginated' },
|
|
11459
|
+
c: [
|
|
11460
|
+
table,
|
|
11461
|
+
{
|
|
11462
|
+
t: 'div',
|
|
11463
|
+
a: { class: 'bw_table_pagination', style: 'display:flex;align-items:center;justify-content:flex-end;padding:0.5rem 0;gap:0.25rem;' },
|
|
11464
|
+
c: pageButtons
|
|
11465
|
+
}
|
|
11466
|
+
]
|
|
11467
|
+
};
|
|
11302
11468
|
};
|
|
11303
11469
|
|
|
11304
11470
|
/**
|