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