bitwrench 2.0.11 → 2.0.13

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.
@@ -1,18 +1,18 @@
1
- /*! bitwrench v2.0.11 | BSD-2-Clause | https://deftio.github.com/bitwrench/pages */
1
+ /*! bitwrench v2.0.13 | BSD-2-Clause | https://deftio.github.com/bitwrench/pages */
2
2
  /**
3
3
  * Auto-generated version file from package.json
4
4
  * DO NOT EDIT DIRECTLY - Use npm run generate-version
5
5
  */
6
6
 
7
7
  const VERSION_INFO = {
8
- version: '2.0.11',
8
+ version: '2.0.13',
9
9
  name: 'bitwrench',
10
10
  description: 'A library for javascript UI functions.',
11
11
  license: 'BSD-2-Clause',
12
12
  homepage: 'https://deftio.github.com/bitwrench/pages',
13
13
  repository: 'git+https://github.com/deftio/bitwrench.git',
14
14
  author: 'manu a. chatterjee <deftio@deftio.com> (https://deftio.com/)',
15
- buildDate: '2026-03-07T11:05:08.522Z'
15
+ buildDate: '2026-03-07T22:35:06.056Z'
16
16
  };
17
17
 
18
18
  /**
@@ -887,15 +887,36 @@ function generateAccordionThemed(scope, palette) {
887
887
  rules[scopeSelector(scope, '.bw-accordion-button')] = {
888
888
  'color': palette.dark.base
889
889
  };
890
+ rules[scopeSelector(scope, '.bw-accordion-button:not(.bw-collapsed)')] = {
891
+ 'color': palette.primary.darkText,
892
+ 'background-color': palette.primary.light
893
+ };
890
894
  rules[scopeSelector(scope, '.bw-accordion-button:hover')] = {
891
895
  'background-color': palette.light.light
892
896
  };
897
+ rules[scopeSelector(scope, '.bw-accordion-button:not(.bw-collapsed):hover')] = {
898
+ 'background-color': palette.primary.hover
899
+ };
900
+ rules[scopeSelector(scope, '.bw-accordion-button:focus-visible')] = {
901
+ 'box-shadow': '0 0 0 0.2rem ' + palette.primary.focus
902
+ };
893
903
  rules[scopeSelector(scope, '.bw-accordion-body')] = {
894
904
  'border-top': '1px solid ' + palette.light.border
895
905
  };
896
906
  return rules;
897
907
  }
898
908
 
909
+ function generateCarouselThemed(scope, palette) {
910
+ var rules = {};
911
+ rules[scopeSelector(scope, '.bw-carousel')] = {
912
+ 'background-color': palette.light.light
913
+ };
914
+ rules[scopeSelector(scope, '.bw-carousel-indicator.active')] = {
915
+ 'background-color': palette.primary.base
916
+ };
917
+ return rules;
918
+ }
919
+
899
920
  function generateModalThemed(scope, palette) {
900
921
  var rules = {};
901
922
  rules[scopeSelector(scope, '.bw-modal-content')] = {
@@ -976,7 +997,7 @@ function generateSwitchThemed(scope, palette) {
976
997
  function generateSkeletonThemed(scope, palette) {
977
998
  var rules = {};
978
999
  rules[scopeSelector(scope, '.bw-skeleton')] = {
979
- 'background-color': palette.light.border
1000
+ 'background': 'linear-gradient(90deg, ' + palette.light.border + ' 25%, ' + palette.light.light + ' 37%, ' + palette.light.border + ' 63%)'
980
1001
  };
981
1002
  return rules;
982
1003
  }
@@ -1023,6 +1044,7 @@ function generateThemedCSS(scopeName, palette, layout) {
1023
1044
  generateCloseButtonThemed(scopeName, palette),
1024
1045
  generateSectionsThemed(scopeName, palette),
1025
1046
  generateAccordionThemed(scopeName, palette),
1047
+ generateCarouselThemed(scopeName, palette),
1026
1048
  generateModalThemed(scopeName, palette),
1027
1049
  generateToastThemed(scopeName, palette),
1028
1050
  generateDropdownThemed(scopeName, palette),
@@ -1382,6 +1404,8 @@ function getStructuralStyles() {
1382
1404
  rules['.bw-tab-content'] = { 'padding': '1.25rem 0' };
1383
1405
  rules['.bw-tab-pane'] = { 'display': 'none' };
1384
1406
  rules['.bw-tab-pane.active'] = { 'display': 'block' };
1407
+ rules['.bw-nav-scrollable'] = { 'flex-wrap': 'nowrap', 'overflow-x': 'auto', '-webkit-overflow-scrolling': 'touch', 'scrollbar-width': 'none' };
1408
+ rules['.bw-nav-scrollable .bw-nav-link'] = { 'white-space': 'nowrap' };
1385
1409
 
1386
1410
  // List groups (structural)
1387
1411
  rules['.bw-list-group'] = { 'display': 'flex', 'flex-direction': 'column', 'padding-left': '0', 'margin-bottom': '0', 'border-radius': '0.375rem' };
@@ -1548,6 +1572,29 @@ function getStructuralStyles() {
1548
1572
  rules['.bw-modal-body'] = { 'position': 'relative', 'flex': '1 1 auto', 'padding': '1.5rem' };
1549
1573
  rules['.bw-modal-footer'] = { 'display': 'flex', 'flex-wrap': 'wrap', 'align-items': 'center', 'justify-content': 'flex-end', 'padding': '0.75rem 1.5rem', 'gap': '0.5rem' };
1550
1574
 
1575
+ // Carousel (structural)
1576
+ rules['.bw-carousel'] = { 'position': 'relative', 'overflow': 'hidden', 'border-radius': '8px' };
1577
+ rules['.bw-carousel-track'] = { 'display': 'flex', 'transition': 'transform 0.4s ease', 'height': '100%' };
1578
+ rules['.bw-carousel-slide'] = { 'min-width': '100%', 'flex-shrink': '0', 'overflow': 'hidden', 'position': 'relative', 'display': 'flex', 'align-items': 'center', 'justify-content': 'center' };
1579
+ rules['.bw-carousel-slide img'] = { 'width': '100%', 'height': '100%', 'object-fit': 'cover' };
1580
+ rules['.bw-carousel-caption'] = { 'position': 'absolute', 'bottom': '0', 'left': '0', 'right': '0', 'padding': '0.75rem 1rem' };
1581
+ rules['.bw-carousel-control'] = {
1582
+ 'position': 'absolute', 'top': '50%', 'transform': 'translateY(-50%)', 'width': '40px', 'height': '40px',
1583
+ 'border': 'none', 'border-radius': '50%', 'cursor': 'pointer', 'display': 'flex', 'align-items': 'center',
1584
+ 'justify-content': 'center', 'z-index': '2', 'padding': '0', 'transition': 'background-color 0.2s ease'
1585
+ };
1586
+ rules['.bw-carousel-control img'] = { 'width': '20px', 'height': '20px', 'pointer-events': 'none' };
1587
+ rules['.bw-carousel-control-prev'] = { 'left': '10px' };
1588
+ rules['.bw-carousel-control-next'] = { 'right': '10px' };
1589
+ rules['.bw-carousel-indicators'] = {
1590
+ 'position': 'absolute', 'bottom': '12px', 'left': '50%', 'transform': 'translateX(-50%)',
1591
+ 'display': 'flex', 'gap': '6px', 'z-index': '2'
1592
+ };
1593
+ rules['.bw-carousel-indicator'] = {
1594
+ 'width': '10px', 'height': '10px', 'border-radius': '50%', 'border': '2px solid transparent',
1595
+ 'padding': '0', 'cursor': 'pointer', 'transition': 'opacity 0.2s ease, background-color 0.2s ease'
1596
+ };
1597
+
1551
1598
  // Toast (structural)
1552
1599
  rules['.bw-toast-container'] = {
1553
1600
  'position': 'fixed', 'z-index': '1080', 'pointer-events': 'none',
@@ -1597,12 +1644,12 @@ function getStructuralStyles() {
1597
1644
  rules['.bw-form-switch .bw-switch-input:disabled'] = { 'opacity': '0.5', 'cursor': 'not-allowed' };
1598
1645
 
1599
1646
  // Skeleton (structural)
1600
- rules['.bw-skeleton'] = { 'border-radius': '4px', 'animation': 'bw-skeleton-pulse 1.5s ease-in-out infinite' };
1647
+ rules['.bw-skeleton'] = { 'border-radius': '4px', 'background-size': '400% 100%', 'animation': 'bw-skeleton-shimmer 1.4s ease infinite' };
1601
1648
  rules['.bw-skeleton-text'] = { 'height': '1em', 'margin-bottom': '0.5rem' };
1602
1649
  rules['.bw-skeleton-circle'] = { 'border-radius': '50%' };
1603
1650
  rules['.bw-skeleton-rect'] = { 'border-radius': '8px' };
1604
1651
  rules['.bw-skeleton-group'] = { 'display': 'flex', 'flex-direction': 'column' };
1605
- rules['@keyframes bw-skeleton-pulse'] = { '0%': { 'opacity': '1' }, '50%': { 'opacity': '0.4' }, '100%': { 'opacity': '1' } };
1652
+ rules['@keyframes bw-skeleton-shimmer'] = { '0%': { 'background-position': '100% 50%' }, '100%': { 'background-position': '0 50%' } };
1606
1653
 
1607
1654
  // Avatar (structural)
1608
1655
  rules['.bw-avatar'] = {
@@ -1964,12 +2011,31 @@ function generateDarkModeCSS(palette) {
1964
2011
  '.bw-dark .bw-accordion-button': {
1965
2012
  'color': textColor
1966
2013
  },
2014
+ '.bw-dark .bw-accordion-button:not(.bw-collapsed)': {
2015
+ 'color': '#7dd3e0',
2016
+ 'background-color': 'rgba(125, 211, 224, 0.1)'
2017
+ },
1967
2018
  '.bw-dark .bw-accordion-button:hover': {
1968
2019
  'background-color': bodyBg
1969
2020
  },
2021
+ '.bw-dark .bw-accordion-button:not(.bw-collapsed):hover': {
2022
+ 'background-color': 'rgba(125, 211, 224, 0.15)'
2023
+ },
2024
+ '.bw-dark .bw-accordion-button:focus-visible': {
2025
+ 'box-shadow': '0 0 0 0.2rem rgba(125, 211, 224, 0.3)'
2026
+ },
1970
2027
  '.bw-dark .bw-accordion-body': {
1971
2028
  'border-top-color': borderColor
1972
2029
  },
2030
+ '.bw-dark .bw-carousel': {
2031
+ 'background-color': bodyBg
2032
+ },
2033
+ '.bw-dark .bw-carousel-control': {
2034
+ 'background-color': 'rgba(255,255,255,0.15)'
2035
+ },
2036
+ '.bw-dark .bw-carousel-control:hover': {
2037
+ 'background-color': 'rgba(255,255,255,0.25)'
2038
+ },
1973
2039
  '.bw-dark .bw-modal-content': {
1974
2040
  'background-color': surfaceBg,
1975
2041
  'border-color': borderColor
@@ -2005,7 +2071,7 @@ function generateDarkModeCSS(palette) {
2005
2071
  'border-top-color': borderColor
2006
2072
  },
2007
2073
  '.bw-dark .bw-skeleton': {
2008
- 'background-color': borderColor
2074
+ 'background': 'linear-gradient(90deg, ' + borderColor + ' 25%, ' + surfaceBg + ' 37%, ' + borderColor + ' 63%)'
2009
2075
  },
2010
2076
  '.bw-dark h1, .bw-dark h2, .bw-dark h3, .bw-dark h4, .bw-dark h5, .bw-dark h6': {
2011
2077
  'color': textColor
@@ -4755,6 +4821,182 @@ function makeAvatar(props = {}) {
4755
4821
  };
4756
4822
  }
4757
4823
 
4824
+ /**
4825
+ * Create a carousel/slideshow component with slide transitions
4826
+ *
4827
+ * Supports image slides, TACO content slides, captions, prev/next controls,
4828
+ * dot indicators, and optional auto-play. Uses CSS translateX transitions.
4829
+ *
4830
+ * @param {Object} [props] - Carousel configuration
4831
+ * @param {Array<Object>} [props.items=[]] - Slide items
4832
+ * @param {string|Object} props.items[].content - Slide content (TACO, string, or img element)
4833
+ * @param {string} [props.items[].caption] - Caption text shown at bottom of slide
4834
+ * @param {boolean} [props.showControls=true] - Show prev/next arrow buttons
4835
+ * @param {boolean} [props.showIndicators=true] - Show dot navigation
4836
+ * @param {boolean} [props.autoPlay=false] - Auto-advance slides
4837
+ * @param {number} [props.interval=5000] - Auto-advance interval in ms
4838
+ * @param {string} [props.height='300px'] - Carousel height
4839
+ * @param {number} [props.startIndex=0] - Initial slide index
4840
+ * @param {string} [props.className] - Additional CSS classes
4841
+ * @returns {Object} TACO object representing a carousel
4842
+ * @category Component Builders
4843
+ * @example
4844
+ * const carousel = makeCarousel({
4845
+ * items: [
4846
+ * { content: { t: 'img', a: { src: 'photo.jpg' } }, caption: 'Photo 1' },
4847
+ * { content: { t: 'div', c: 'Text slide' } }
4848
+ * ],
4849
+ * autoPlay: true,
4850
+ * interval: 3000
4851
+ * });
4852
+ */
4853
+ function makeCarousel(props = {}) {
4854
+ const {
4855
+ items = [],
4856
+ showControls = true,
4857
+ showIndicators = true,
4858
+ autoPlay = false,
4859
+ interval = 5000,
4860
+ height = '300px',
4861
+ startIndex = 0,
4862
+ className = ''
4863
+ } = props;
4864
+
4865
+ // Shared navigation logic
4866
+ function goToSlide(carouselEl, index) {
4867
+ var total = carouselEl.querySelectorAll('.bw-carousel-slide').length;
4868
+ if (index < 0) index = total - 1;
4869
+ if (index >= total) index = 0;
4870
+ carouselEl.setAttribute('data-carousel-index', index);
4871
+ var track = carouselEl.querySelector('.bw-carousel-track');
4872
+ track.style.transform = 'translateX(-' + (index * 100) + '%)';
4873
+ // Update indicators
4874
+ var indicators = carouselEl.querySelectorAll('.bw-carousel-indicator');
4875
+ for (var i = 0; i < indicators.length; i++) {
4876
+ if (i === index) {
4877
+ indicators[i].classList.add('active');
4878
+ } else {
4879
+ indicators[i].classList.remove('active');
4880
+ }
4881
+ }
4882
+ }
4883
+
4884
+ // Arrow SVGs (inline data URIs, same pattern as accordion chevrons)
4885
+ var prevArrow = "data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e";
4886
+ var nextArrow = "data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e";
4887
+
4888
+ var slides = items.map(function(item) {
4889
+ var slideContent = [
4890
+ item.content,
4891
+ item.caption && {
4892
+ t: 'div',
4893
+ a: { class: 'bw-carousel-caption' },
4894
+ c: item.caption
4895
+ }
4896
+ ].filter(Boolean);
4897
+
4898
+ return {
4899
+ t: 'div',
4900
+ a: { class: 'bw-carousel-slide' },
4901
+ c: slideContent.length === 1 ? slideContent[0] : slideContent
4902
+ };
4903
+ });
4904
+
4905
+ var children = [
4906
+ // Track
4907
+ {
4908
+ t: 'div',
4909
+ a: {
4910
+ class: 'bw-carousel-track',
4911
+ style: 'transform: translateX(-' + (startIndex * 100) + '%)'
4912
+ },
4913
+ c: slides
4914
+ }
4915
+ ];
4916
+
4917
+ // Prev/Next controls
4918
+ if (showControls && items.length > 1) {
4919
+ children.push({
4920
+ t: 'button',
4921
+ a: {
4922
+ class: 'bw-carousel-control bw-carousel-control-prev',
4923
+ type: 'button',
4924
+ 'aria-label': 'Previous slide',
4925
+ onclick: function(e) {
4926
+ var carousel = e.target.closest('.bw-carousel');
4927
+ var idx = parseInt(carousel.getAttribute('data-carousel-index') || '0');
4928
+ goToSlide(carousel, idx - 1);
4929
+ }
4930
+ },
4931
+ c: { t: 'img', a: { src: prevArrow, alt: '', role: 'presentation' } }
4932
+ });
4933
+ children.push({
4934
+ t: 'button',
4935
+ a: {
4936
+ class: 'bw-carousel-control bw-carousel-control-next',
4937
+ type: 'button',
4938
+ 'aria-label': 'Next slide',
4939
+ onclick: function(e) {
4940
+ var carousel = e.target.closest('.bw-carousel');
4941
+ var idx = parseInt(carousel.getAttribute('data-carousel-index') || '0');
4942
+ goToSlide(carousel, idx + 1);
4943
+ }
4944
+ },
4945
+ c: { t: 'img', a: { src: nextArrow, alt: '', role: 'presentation' } }
4946
+ });
4947
+ }
4948
+
4949
+ // Indicators
4950
+ if (showIndicators && items.length > 1) {
4951
+ children.push({
4952
+ t: 'div',
4953
+ a: { class: 'bw-carousel-indicators' },
4954
+ c: items.map(function(_, i) {
4955
+ return {
4956
+ t: 'button',
4957
+ a: {
4958
+ class: 'bw-carousel-indicator' + (i === startIndex ? ' active' : ''),
4959
+ type: 'button',
4960
+ 'aria-label': 'Go to slide ' + (i + 1),
4961
+ 'data-slide-index': i,
4962
+ onclick: function(e) {
4963
+ var carousel = e.target.closest('.bw-carousel');
4964
+ var idx = parseInt(e.target.getAttribute('data-slide-index'));
4965
+ goToSlide(carousel, idx);
4966
+ }
4967
+ }
4968
+ };
4969
+ })
4970
+ });
4971
+ }
4972
+
4973
+ return {
4974
+ t: 'div',
4975
+ a: {
4976
+ class: ('bw-carousel ' + className).trim(),
4977
+ style: 'height: ' + height,
4978
+ 'data-carousel-index': startIndex
4979
+ },
4980
+ c: children,
4981
+ o: {
4982
+ type: 'carousel',
4983
+ state: { activeIndex: startIndex, autoPlay: autoPlay, interval: interval },
4984
+ mounted: autoPlay ? function(el) {
4985
+ var intervalId = setInterval(function() {
4986
+ var idx = parseInt(el.getAttribute('data-carousel-index') || '0');
4987
+ goToSlide(el, idx + 1);
4988
+ }, interval);
4989
+ el._bw_carouselInterval = intervalId;
4990
+ } : undefined,
4991
+ unmount: autoPlay ? function(el) {
4992
+ if (el._bw_carouselInterval) {
4993
+ clearInterval(el._bw_carouselInterval);
4994
+ }
4995
+ } : undefined
4996
+ }
4997
+ };
4998
+ }
4999
+
4758
5000
  const componentHandles = {
4759
5001
  card: CardHandle,
4760
5002
  table: TableHandle,
@@ -4780,6 +5022,7 @@ var components = /*#__PURE__*/Object.freeze({
4780
5022
  makeButtonGroup: makeButtonGroup,
4781
5023
  makeCTA: makeCTA,
4782
5024
  makeCard: makeCard,
5025
+ makeCarousel: makeCarousel,
4783
5026
  makeCheckbox: makeCheckbox,
4784
5027
  makeCodeDemo: makeCodeDemo,
4785
5028
  makeCol: makeCol,
@@ -5229,6 +5472,26 @@ bw.escapeHTML = function(str) {
5229
5472
  return str.replace(/[&<>"'/]/g, (char) => escapeMap[char]);
5230
5473
  };
5231
5474
 
5475
+ /**
5476
+ * Mark a string as raw HTML so it will not be escaped by bw.html() or bw.createDOM().
5477
+ *
5478
+ * By default, bitwrench escapes all text content to prevent XSS. Use bw.raw()
5479
+ * when you need to embed pre-sanitized HTML, entities, or inline markup.
5480
+ *
5481
+ * @param {string} str - HTML string to mark as raw
5482
+ * @returns {Object} Marked object recognized by bw.html() and bw.createDOM()
5483
+ * @category DOM Generation
5484
+ * @see bw.escapeHTML
5485
+ * @see bw.html
5486
+ * @example
5487
+ * bw.raw('Hello &mdash; World')
5488
+ * // Used in TACO content:
5489
+ * { t: 'p', c: bw.raw('Price: <strong>$9.99</strong>') }
5490
+ */
5491
+ bw.raw = function(str) {
5492
+ return { __bw_raw: true, v: String(str) };
5493
+ };
5494
+
5232
5495
  /**
5233
5496
  * Normalize CSS class names by converting underscores to hyphens for bw-prefixed classes.
5234
5497
  *
@@ -5280,6 +5543,11 @@ bw.html = function(taco, options = {}) {
5280
5543
  return taco.map(t => bw.html(t, options)).join('');
5281
5544
  }
5282
5545
 
5546
+ // Handle bw.raw() marked content
5547
+ if (taco && taco.__bw_raw) {
5548
+ return taco.v;
5549
+ }
5550
+
5283
5551
  // Handle primitives and non-TACO objects
5284
5552
  if (typeof taco !== 'object' || !taco.t) {
5285
5553
  return options.raw ? String(taco) : bw.escapeHTML(String(taco));
@@ -5380,12 +5648,21 @@ bw.createDOM = function(taco, options = {}) {
5380
5648
 
5381
5649
  // Handle null/undefined
5382
5650
  if (taco == null) return document.createTextNode('');
5383
-
5651
+
5652
+ // Handle bw.raw() marked content — inject as HTML
5653
+ if (taco && taco.__bw_raw) {
5654
+ var frag = document.createDocumentFragment();
5655
+ var tmp = document.createElement('span');
5656
+ tmp.innerHTML = taco.v;
5657
+ while (tmp.firstChild) frag.appendChild(tmp.firstChild);
5658
+ return frag;
5659
+ }
5660
+
5384
5661
  // Handle text nodes
5385
5662
  if (typeof taco !== 'object' || !taco.t) {
5386
5663
  return document.createTextNode(String(taco));
5387
5664
  }
5388
-
5665
+
5389
5666
  const { t: tag, a: attrs = {}, c: content, o: opts = {} } = taco;
5390
5667
 
5391
5668
  // Create element
@@ -5450,6 +5727,9 @@ bw.createDOM = function(taco, options = {}) {
5450
5727
  }
5451
5728
  }
5452
5729
  });
5730
+ } else if (typeof content === 'object' && content.__bw_raw) {
5731
+ // Raw HTML content — inject via innerHTML
5732
+ el.innerHTML = content.v;
5453
5733
  } else if (typeof content === 'object' && content.t) {
5454
5734
  var childEl = bw.createDOM(content, options);
5455
5735
  el.appendChild(childEl);