@brandsync/wc 1.2.0 → 1.3.0

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.
Files changed (188) hide show
  1. package/custom-elements.json +417 -0
  2. package/dist/brandsync-wc/brandsync-wc.esm.js +1 -1
  3. package/dist/brandsync-wc/p-03676f8a.entry.js +1 -0
  4. package/dist/brandsync-wc/{p-718ce85f.entry.js → p-04c26826.entry.js} +1 -1
  5. package/dist/brandsync-wc/{p-f94449da.entry.js → p-0c8050e8.entry.js} +1 -1
  6. package/dist/brandsync-wc/p-0d5cc77f.entry.js +1 -0
  7. package/dist/brandsync-wc/{p-7f14660d.entry.js → p-19c98e13.entry.js} +1 -1
  8. package/dist/brandsync-wc/{p-a35d5ea7.entry.js → p-23758f81.entry.js} +1 -1
  9. package/dist/brandsync-wc/{p-b6425ac2.entry.js → p-29c360f3.entry.js} +1 -1
  10. package/dist/brandsync-wc/{p-260e14de.entry.js → p-35d98580.entry.js} +1 -1
  11. package/dist/brandsync-wc/{p-aac6b59b.entry.js → p-39204377.entry.js} +1 -1
  12. package/dist/brandsync-wc/p-5a4db480.entry.js +1 -0
  13. package/dist/brandsync-wc/{p-8f2cfe31.entry.js → p-5d118af1.entry.js} +1 -1
  14. package/dist/brandsync-wc/{p-c76ca89f.entry.js → p-76d391d7.entry.js} +1 -1
  15. package/dist/brandsync-wc/{p-7655e741.entry.js → p-7de20530.entry.js} +1 -1
  16. package/dist/brandsync-wc/{p-1e9ceb9d.entry.js → p-7ea0133f.entry.js} +1 -1
  17. package/dist/brandsync-wc/{p-650b7eef.entry.js → p-8acb623e.entry.js} +1 -1
  18. package/dist/brandsync-wc/{p-f6fafbd6.entry.js → p-8ec95da5.entry.js} +1 -1
  19. package/dist/brandsync-wc/{p-0921ca0d.entry.js → p-94266d72.entry.js} +1 -1
  20. package/dist/brandsync-wc/{p-205deb32.entry.js → p-96d1ce21.entry.js} +1 -1
  21. package/dist/brandsync-wc/{p-67bc2ec8.entry.js → p-9ad278f4.entry.js} +1 -1
  22. package/dist/brandsync-wc/{p-c8480d21.entry.js → p-9f9c1a1d.entry.js} +1 -1
  23. package/dist/brandsync-wc/{p-ed704126.entry.js → p-b2704450.entry.js} +1 -1
  24. package/dist/brandsync-wc/{p-a597dc2b.entry.js → p-bcafba87.entry.js} +1 -1
  25. package/dist/brandsync-wc/{p-3e6d37a4.entry.js → p-bd2cc2c6.entry.js} +1 -1
  26. package/dist/brandsync-wc/{p-2e7586dd.entry.js → p-be675b82.entry.js} +1 -1
  27. package/dist/brandsync-wc/{p-6970a24c.entry.js → p-bf4a5b1b.entry.js} +1 -1
  28. package/dist/brandsync-wc/{p-ce08c9d7.entry.js → p-c4a16564.entry.js} +1 -1
  29. package/dist/brandsync-wc/{p-393efb5d.entry.js → p-cd703364.entry.js} +1 -1
  30. package/dist/brandsync-wc/{p-c7b9a5c7.entry.js → p-d589fa72.entry.js} +1 -1
  31. package/dist/brandsync-wc/{p-03f9fd54.entry.js → p-d5d15d9f.entry.js} +1 -1
  32. package/dist/brandsync-wc/{p-41732f18.entry.js → p-e58b1dbb.entry.js} +1 -1
  33. package/dist/brandsync-wc/{p-17dbd4b3.entry.js → p-eae7587e.entry.js} +1 -1
  34. package/dist/brandsync-wc/{p-cff188cb.entry.js → p-f196642a.entry.js} +1 -1
  35. package/dist/cjs/brandsync-wc.cjs.js +1 -1
  36. package/dist/cjs/bs-ai-disclaimer.cjs.entry.js +1 -1
  37. package/dist/cjs/bs-ai-greeting.cjs.entry.js +1 -1
  38. package/dist/cjs/bs-ai-thinking.cjs.entry.js +1 -1
  39. package/dist/cjs/bs-attachment-list.cjs.entry.js +1 -1
  40. package/dist/cjs/bs-breadcrumbs.cjs.entry.js +1 -1
  41. package/dist/cjs/bs-button-skeleton.cjs.entry.js +1 -1
  42. package/dist/cjs/bs-chatbot-feedback.cjs.entry.js +1 -1
  43. package/dist/cjs/bs-chatbot-header.cjs.entry.js +1 -1
  44. package/dist/cjs/bs-chatbot-response-action.cjs.entry.js +1 -1
  45. package/dist/cjs/bs-chatbot-sources-drawer.cjs.entry.js +1 -1
  46. package/dist/cjs/bs-chatbot-suggestion-button.cjs.entry.js +1 -1
  47. package/dist/cjs/bs-checkbox-skeleton.cjs.entry.js +1 -1
  48. package/dist/cjs/bs-inline-tab.cjs.entry.js +1 -1
  49. package/dist/cjs/bs-input.cjs.entry.js +2 -2
  50. package/dist/cjs/bs-menu-item.cjs.entry.js +1 -1
  51. package/dist/cjs/bs-menu.cjs.entry.js +1 -1
  52. package/dist/cjs/bs-navigation-drawer-item.cjs.entry.js +1 -1
  53. package/dist/cjs/bs-navigation-drawer.cjs.entry.js +1 -1
  54. package/dist/cjs/bs-pagination.cjs.entry.js +1 -1
  55. package/dist/cjs/bs-progress-linear.cjs.entry.js +65 -0
  56. package/dist/cjs/bs-progress.cjs.entry.js +69 -0
  57. package/dist/cjs/bs-radio.cjs.entry.js +1 -1
  58. package/dist/cjs/bs-slider.cjs.entry.js +1 -1
  59. package/dist/cjs/bs-snackbar.cjs.entry.js +1 -1
  60. package/dist/cjs/bs-source-link.cjs.entry.js +2 -2
  61. package/dist/cjs/bs-stepper-step.cjs.entry.js +1 -1
  62. package/dist/cjs/bs-stepper.cjs.entry.js +1 -1
  63. package/dist/cjs/bs-switch.cjs.entry.js +1 -1
  64. package/dist/cjs/bs-tab.cjs.entry.js +1 -1
  65. package/dist/cjs/bs-tabs.cjs.entry.js +1 -1
  66. package/dist/cjs/bs-toast.cjs.entry.js +1 -1
  67. package/dist/cjs/bs-tooltip.cjs.entry.js +1 -1
  68. package/dist/cjs/loader.cjs.js +1 -1
  69. package/dist/collection/collection-manifest.json +2 -0
  70. package/dist/collection/components/bs-ai-disclaimer/bs-ai-disclaimer.js +1 -1
  71. package/dist/collection/components/bs-ai-greeting/bs-ai-greeting.js +1 -1
  72. package/dist/collection/components/bs-ai-thinking/bs-ai-thinking.js +1 -1
  73. package/dist/collection/components/bs-attachment-list/bs-attachment-list.js +1 -1
  74. package/dist/collection/components/bs-breadcrumb/bs-breadcrumbs/bs-breadcrumbs.js +1 -1
  75. package/dist/collection/components/bs-button-skeleton/bs-button-skeleton.js +1 -1
  76. package/dist/collection/components/bs-chatbot-feedback/bs-chatbot-feedback.js +1 -1
  77. package/dist/collection/components/bs-chatbot-header/bs-chatbot-header.js +1 -1
  78. package/dist/collection/components/bs-chatbot-response-action/bs-chatbot-response-action.js +1 -1
  79. package/dist/collection/components/bs-chatbot-sources-drawer/bs-chatbot-sources-drawer.js +1 -1
  80. package/dist/collection/components/bs-chatbot-suggestion-button/bs-chatbot-suggestion-button.js +1 -1
  81. package/dist/collection/components/bs-checkbox/bs-checkbox-skeleton/bs-checkbox-skeleton.js +1 -1
  82. package/dist/collection/components/bs-input/bs-input.js +2 -2
  83. package/dist/collection/components/bs-menu/bs-menu.js +1 -1
  84. package/dist/collection/components/bs-menu-item/bs-menu-item.js +1 -1
  85. package/dist/collection/components/bs-pagination/bs-pagination.js +1 -1
  86. package/dist/collection/components/bs-progress/bs-progress.cmp.test.js +96 -0
  87. package/dist/collection/components/bs-progress/bs-progress.css +119 -0
  88. package/dist/collection/components/bs-progress/bs-progress.js +212 -0
  89. package/dist/collection/components/bs-progress/bs-progress.stories.js +72 -0
  90. package/dist/collection/components/bs-progress-linear/bs-progress-linear.cmp.test.js +92 -0
  91. package/dist/collection/components/bs-progress-linear/bs-progress-linear.css +108 -0
  92. package/dist/collection/components/bs-progress-linear/bs-progress-linear.js +192 -0
  93. package/dist/collection/components/bs-progress-linear/bs-progress-linear.stories.js +66 -0
  94. package/dist/collection/components/bs-radio/bs-radio.js +1 -1
  95. package/dist/collection/components/bs-slider/bs-slider.js +1 -1
  96. package/dist/collection/components/bs-snackbar/bs-snackbar.js +1 -1
  97. package/dist/collection/components/bs-source-link/bs-source-link.js +2 -2
  98. package/dist/collection/components/bs-stepper/bs-stepper/bs-stepper.js +1 -1
  99. package/dist/collection/components/bs-stepper/bs-stepper-step/bs-stepper-step.js +1 -1
  100. package/dist/collection/components/bs-switch/bs-switch.js +1 -1
  101. package/dist/collection/components/bs-tab/bs-inline-tab/bs-inline-tab.js +1 -1
  102. package/dist/collection/components/bs-tab/bs-tab/bs-tab.js +1 -1
  103. package/dist/collection/components/bs-tab/bs-tabs/bs-tabs.js +1 -1
  104. package/dist/collection/components/bs-toast/bs-toast.js +1 -1
  105. package/dist/collection/components/bs-tooltip/bs-tooltip.js +1 -1
  106. package/dist/collection/components/ui-shell/bs-navigation-drawer/bs-navigation-drawer/bs-navigation-drawer.js +1 -1
  107. package/dist/collection/components/ui-shell/bs-navigation-drawer/bs-navigation-drawer-item/bs-navigation-drawer-item.js +1 -1
  108. package/dist/components/bs-ai-disclaimer.js +1 -1
  109. package/dist/components/bs-ai-greeting.js +1 -1
  110. package/dist/components/bs-ai-thinking.js +1 -1
  111. package/dist/components/bs-attachment-list.js +1 -1
  112. package/dist/components/bs-breadcrumb-overflow.js +1 -1
  113. package/dist/components/bs-breadcrumbs.js +1 -1
  114. package/dist/components/bs-button-skeleton.js +1 -1
  115. package/dist/components/bs-chatbot-feedback.js +1 -1
  116. package/dist/components/bs-chatbot-header.js +1 -1
  117. package/dist/components/bs-chatbot-response-action.js +1 -1
  118. package/dist/components/bs-chatbot-sources-drawer.js +1 -1
  119. package/dist/components/bs-chatbot-suggestion-button.js +1 -1
  120. package/dist/components/bs-checkbox-skeleton.js +1 -1
  121. package/dist/components/bs-inline-tab.js +1 -1
  122. package/dist/components/bs-input.js +1 -1
  123. package/dist/components/bs-menu-item.js +1 -1
  124. package/dist/components/bs-menu.js +1 -1
  125. package/dist/components/bs-navigation-drawer-item.js +1 -1
  126. package/dist/components/bs-navigation-drawer.js +1 -1
  127. package/dist/components/bs-pagination.js +1 -1
  128. package/dist/components/bs-progress-linear.d.ts +11 -0
  129. package/dist/components/bs-progress-linear.js +1 -0
  130. package/dist/components/bs-progress.d.ts +11 -0
  131. package/dist/components/bs-progress.js +1 -0
  132. package/dist/components/bs-radio.js +1 -1
  133. package/dist/components/bs-slider.js +1 -1
  134. package/dist/components/bs-snackbar.js +1 -1
  135. package/dist/components/bs-source-link.js +1 -1
  136. package/dist/components/bs-stepper-step.js +1 -1
  137. package/dist/components/bs-stepper.js +1 -1
  138. package/dist/components/bs-switch.js +1 -1
  139. package/dist/components/bs-tab.js +1 -1
  140. package/dist/components/bs-tabs.js +1 -1
  141. package/dist/components/bs-toast.js +1 -1
  142. package/dist/components/bs-tooltip.js +1 -1
  143. package/dist/components/{p-BlCziuES.js → p-BIgPiuc-.js} +1 -1
  144. package/dist/components/{p-Bg98eW9H.js → p-BUtz3sub.js} +1 -1
  145. package/dist/components/{p-BOP5EJrQ.js → p-Rn1IWs8l.js} +1 -1
  146. package/dist/esm/brandsync-wc.js +1 -1
  147. package/dist/esm/bs-ai-disclaimer.entry.js +1 -1
  148. package/dist/esm/bs-ai-greeting.entry.js +1 -1
  149. package/dist/esm/bs-ai-thinking.entry.js +1 -1
  150. package/dist/esm/bs-attachment-list.entry.js +1 -1
  151. package/dist/esm/bs-breadcrumbs.entry.js +1 -1
  152. package/dist/esm/bs-button-skeleton.entry.js +1 -1
  153. package/dist/esm/bs-chatbot-feedback.entry.js +1 -1
  154. package/dist/esm/bs-chatbot-header.entry.js +1 -1
  155. package/dist/esm/bs-chatbot-response-action.entry.js +1 -1
  156. package/dist/esm/bs-chatbot-sources-drawer.entry.js +1 -1
  157. package/dist/esm/bs-chatbot-suggestion-button.entry.js +1 -1
  158. package/dist/esm/bs-checkbox-skeleton.entry.js +1 -1
  159. package/dist/esm/bs-inline-tab.entry.js +1 -1
  160. package/dist/esm/bs-input.entry.js +2 -2
  161. package/dist/esm/bs-menu-item.entry.js +1 -1
  162. package/dist/esm/bs-menu.entry.js +1 -1
  163. package/dist/esm/bs-navigation-drawer-item.entry.js +1 -1
  164. package/dist/esm/bs-navigation-drawer.entry.js +1 -1
  165. package/dist/esm/bs-pagination.entry.js +1 -1
  166. package/dist/esm/bs-progress-linear.entry.js +63 -0
  167. package/dist/esm/bs-progress.entry.js +67 -0
  168. package/dist/esm/bs-radio.entry.js +1 -1
  169. package/dist/esm/bs-slider.entry.js +1 -1
  170. package/dist/esm/bs-snackbar.entry.js +1 -1
  171. package/dist/esm/bs-source-link.entry.js +2 -2
  172. package/dist/esm/bs-stepper-step.entry.js +1 -1
  173. package/dist/esm/bs-stepper.entry.js +1 -1
  174. package/dist/esm/bs-switch.entry.js +1 -1
  175. package/dist/esm/bs-tab.entry.js +1 -1
  176. package/dist/esm/bs-tabs.entry.js +1 -1
  177. package/dist/esm/bs-toast.entry.js +1 -1
  178. package/dist/esm/bs-tooltip.entry.js +1 -1
  179. package/dist/esm/loader.js +1 -1
  180. package/dist/types/components/bs-progress/bs-progress.cmp.test.d.ts +1 -0
  181. package/dist/types/components/bs-progress/bs-progress.d.ts +46 -0
  182. package/dist/types/components/bs-progress/bs-progress.stories.d.ts +18 -0
  183. package/dist/types/components/bs-progress-linear/bs-progress-linear.cmp.test.d.ts +1 -0
  184. package/dist/types/components/bs-progress-linear/bs-progress-linear.d.ts +50 -0
  185. package/dist/types/components/bs-progress-linear/bs-progress-linear.stories.d.ts +17 -0
  186. package/dist/types/components.d.ts +241 -0
  187. package/package.json +1 -1
  188. package/dist/brandsync-wc/p-04d6aa95.entry.js +0 -1
@@ -0,0 +1,119 @@
1
+ /**
2
+ * @prop --bs-progress-gap: Gap between the ring and the label. Aliased to --bs-spacing-100.
3
+ * @prop --bs-progress-track: Track circle color (background ring). Aliased to --bs-neutral-200.
4
+ * @prop --bs-progress-arc: Arc/spinner circle color. Aliased to --bs-color-primary-default (the
5
+ * same brand-blue accent bs-button's primary variant uses).
6
+ * @prop --bs-progress-label-color: Percentage label text color. Aliased to --bs-color-primary-default.
7
+ * @prop --bs-progress-label-font-size-sm: Label font size, size="small"/"medium". Aliased to --bs-fontsize-caption-md.
8
+ * @prop --bs-progress-label-line-height-sm: Label line height, size="small"/"medium". Aliased to --bs-line-height-caption-md.
9
+ * @prop --bs-progress-label-font-size-lg: Label font size, size="large". Aliased to --bs-fontsize-body-md.
10
+ * @prop --bs-progress-label-line-height-lg: Label line height, size="large". Aliased to --bs-line-height-body-md.
11
+ * @prop --bs-progress-label-font-weight: Label font weight. Aliased to --bs-font-weight-medium.
12
+ * @prop --bs-progress-spin-duration: Indeterminate spin duration. Aliased to --bs-duration-slower
13
+ * if defined by brandsync-tokens, otherwise a plain 1.4s fallback.
14
+ */
15
+ :host {
16
+ display: inline-flex;
17
+
18
+ --bs-progress-gap: var(--bs-spacing-100);
19
+ --bs-progress-track: var(--bs-neutral-200);
20
+ --bs-progress-arc: var(--bs-color-primary-default);
21
+ --bs-progress-label-color: var(--bs-color-primary-default);
22
+ --bs-progress-label-font-size-sm: var(--bs-fontsize-caption-md);
23
+ --bs-progress-label-line-height-sm: var(--bs-line-height-caption-md);
24
+ --bs-progress-label-font-size-lg: var(--bs-fontsize-body-md);
25
+ --bs-progress-label-line-height-lg: var(--bs-line-height-body-md);
26
+ --bs-progress-label-font-weight: var(--bs-font-weight-medium);
27
+ --bs-progress-spin-duration: 1.4s;
28
+ }
29
+
30
+ .bs-progress {
31
+ display: inline-flex;
32
+ flex-direction: column;
33
+ align-items: center;
34
+ gap: var(--bs-progress-gap);
35
+ font-family: inherit;
36
+ }
37
+
38
+ .bs-progress__box {
39
+ position: relative;
40
+ display: inline-flex;
41
+ }
42
+
43
+ .bs-progress__ring {
44
+ /* SVG rings are drawn starting at 3 o'clock by default -- rotate -90deg so the determinate
45
+ sweep starts at 12 o'clock, matching the Figma spec. */
46
+ transform: rotate(-90deg);
47
+ overflow: visible;
48
+ }
49
+
50
+ .bs-progress__track {
51
+ stroke: var(--bs-progress-track);
52
+ }
53
+
54
+ .bs-progress__arc {
55
+ stroke: var(--bs-progress-arc);
56
+ transition: stroke-dashoffset 0.3s ease;
57
+ }
58
+
59
+ /* Indeterminate: Figma's ring is a fading "comet tail" (transparent -> solid color) sweeping
60
+ around with a visible gap, not a flat-colored partial arc -- SVG has no angular/conic gradient
61
+ primitive, so this uses a CSS conic-gradient (color varies by angle) for the fade, masked down
62
+ from a solid disc to a ring of exactly `strokeWidth` via a radial-gradient mask, then rotated
63
+ continuously. `--bs-progress-spinner-stroke-width`/`--bs-progress-spinner-sweep` are set
64
+ per-instance inline (they depend on the `strokeWidth` prop). */
65
+ .bs-progress__spinner {
66
+ width: 100%;
67
+ height: 100%;
68
+ border-radius: 50%;
69
+ background: conic-gradient(from 0deg, transparent 0deg, var(--bs-progress-arc) var(--bs-progress-spinner-sweep), transparent var(--bs-progress-spinner-sweep));
70
+ -webkit-mask: radial-gradient(
71
+ farthest-side,
72
+ transparent calc(100% - var(--bs-progress-spinner-stroke-width) - 1px),
73
+ #000 calc(100% - var(--bs-progress-spinner-stroke-width))
74
+ );
75
+ mask: radial-gradient(
76
+ farthest-side,
77
+ transparent calc(100% - var(--bs-progress-spinner-stroke-width) - 1px),
78
+ #000 calc(100% - var(--bs-progress-spinner-stroke-width))
79
+ );
80
+ animation: bs-progress-spin var(--bs-progress-spin-duration) linear infinite;
81
+ }
82
+
83
+ @keyframes bs-progress-spin {
84
+ from {
85
+ transform: rotate(0deg);
86
+ }
87
+ to {
88
+ transform: rotate(360deg);
89
+ }
90
+ }
91
+
92
+ /* Reduced motion: freezing the spin entirely would remove the only visual signal that work is
93
+ still in progress (there's no label for indeterminate to fall back on), so this slows the
94
+ rotation to a slow, low-frequency pulse instead of stopping it -- still obviously "in progress"
95
+ without being motion-sickness-inducing. Same intent as bs-snackbar's spinner, applied as a
96
+ duration change rather than `animation: none` since indeterminate has no static fallback state
97
+ to freeze on. */
98
+ @media (prefers-reduced-motion: reduce) {
99
+ .bs-progress__spinner {
100
+ animation-duration: calc(var(--bs-progress-spin-duration) * 4);
101
+ }
102
+ }
103
+
104
+ .bs-progress__label {
105
+ color: var(--bs-progress-label-color);
106
+ font-weight: var(--bs-progress-label-font-weight);
107
+ text-align: center;
108
+ }
109
+
110
+ .bs-progress__label--small,
111
+ .bs-progress__label--medium {
112
+ font-size: var(--bs-progress-label-font-size-sm);
113
+ line-height: calc(var(--bs-progress-label-line-height-sm) * 1px);
114
+ }
115
+
116
+ .bs-progress__label--large {
117
+ font-size: var(--bs-progress-label-font-size-lg);
118
+ line-height: calc(var(--bs-progress-label-line-height-lg) * 1px);
119
+ }
@@ -0,0 +1,212 @@
1
+ import { h } from "@stencil/core";
2
+ // Diameter in px per `size` -- matches the Figma spec's three fixed sizes exactly.
3
+ const DIAMETERS = {
4
+ small: 44,
5
+ medium: 48,
6
+ large: 64,
7
+ };
8
+ // How much of the ring the indeterminate sweep covers before fading back to transparent -- the
9
+ // remainder (to 360deg) is the visible "gap" in the ring. Matches the Figma mock's proportions.
10
+ const INDETERMINATE_SWEEP_DEGREES = 300;
11
+ /**
12
+ * A circular progress indicator -- either a static ring showing a percentage sweep
13
+ * (`type="determinate"`) or a continuously spinning ring signaling in-progress work with no known
14
+ * completion percentage (`type="indeterminate"`).
15
+ *
16
+ * ## When to use
17
+ * - Showing the progress of a task with a known, quantifiable completion percentage (`determinate`).
18
+ * - Signaling that work is happening in the background with no meaningful percentage to report
19
+ * (`indeterminate`), e.g. while waiting on a network response.
20
+ *
21
+ * ## When not to use
22
+ * - For a horizontal, bar-shaped progress indicator -- this component is a ring, not a bar.
23
+ * - For a percentage that's better expressed as plain text, without the reserved space and visual
24
+ * weight of a ring.
25
+ *
26
+ * @part container - The outer wrapper (ring + optional label).
27
+ * @part track - The background track circle.
28
+ * @part arc - The colored progress/spinner arc circle.
29
+ * @part label - The percentage label (`determinate` + `showLabel` only).
30
+ */
31
+ export class BsProgress {
32
+ /** `determinate` renders a static arc sweep reflecting `value` (with an optional percentage
33
+ * label); `indeterminate` renders a continuously spinning ring and never shows a label, since
34
+ * there's no known percentage to report. */
35
+ type = 'determinate';
36
+ /** Overall diameter: `small` (44px), `medium` (48px), or `large` (64px). */
37
+ size = 'small';
38
+ /** Ring stroke width in px -- `4` or `8`, matching Figma's two stroke-width variants. A number,
39
+ * not the Figma label's literal `"4 px"` string. */
40
+ strokeWidth = 4;
41
+ /** Progress percentage, `0`-`100`. Only meaningful for `type="determinate"` -- ignored (and
42
+ * never rendered) for `type="indeterminate"`. Out-of-range values are clamped defensively. */
43
+ value = 0;
44
+ /** Shows the `${value}%` label below the ring. Only takes effect for `type="determinate"` --
45
+ * `indeterminate` never shows a label regardless of this prop, since there's no percentage to
46
+ * show. Defaults to `true`. */
47
+ showLabel = true;
48
+ get clampedValue() {
49
+ return Math.min(100, Math.max(0, this.value));
50
+ }
51
+ get diameter() {
52
+ return DIAMETERS[this.size];
53
+ }
54
+ get radius() {
55
+ return (this.diameter - this.strokeWidth) / 2;
56
+ }
57
+ get circumference() {
58
+ return 2 * Math.PI * this.radius;
59
+ }
60
+ render() {
61
+ const diameter = this.diameter;
62
+ const radius = this.radius;
63
+ const circumference = this.circumference;
64
+ const isDeterminate = this.type === 'determinate';
65
+ const value = this.clampedValue;
66
+ const dashOffset = circumference * (1 - value / 100);
67
+ return (h("div", { key: '359251959b842695100d52b8fb4707708ed8fb5d', part: "container", class: "bs-progress" }, h("div", { key: '397c8cecc86bc564c9c7e4fadbbbcf05bae71c58', class: "bs-progress__box", style: { width: `${diameter}px`, height: `${diameter}px` }, role: "progressbar", "aria-valuemin": isDeterminate ? '0' : undefined, "aria-valuemax": isDeterminate ? '100' : undefined, "aria-valuenow": isDeterminate ? String(value) : undefined, "aria-busy": isDeterminate ? undefined : 'true' }, isDeterminate ? (h("svg", { class: "bs-progress__ring", width: diameter, height: diameter, viewBox: `0 0 ${diameter} ${diameter}` }, h("circle", { part: "track", class: "bs-progress__track", cx: diameter / 2, cy: diameter / 2, r: radius, "stroke-width": this.strokeWidth, fill: "none" }), h("circle", { part: "arc", class: "bs-progress__arc", cx: diameter / 2, cy: diameter / 2, r: radius, "stroke-width": this.strokeWidth, fill: "none", "stroke-linecap": "round", "stroke-dasharray": circumference, "stroke-dashoffset": dashOffset }))) : (
68
+ // Figma's indeterminate ring is a fading "comet tail" (transparent -> solid color),
69
+ // not a flat-colored partial arc -- SVG has no angular/conic gradient primitive, so a
70
+ // CSS conic-gradient (color varying by angle) + a radial-gradient mask (carving the
71
+ // gradient disc down to a ring of exactly `strokeWidth`) is used instead of the
72
+ // stroke-dasharray technique the determinate ring uses.
73
+ h("div", { part: "arc", class: "bs-progress__spinner", style: {
74
+ '--bs-progress-spinner-stroke-width': `${this.strokeWidth}px`,
75
+ '--bs-progress-spinner-sweep': `${INDETERMINATE_SWEEP_DEGREES}deg`,
76
+ } }))), isDeterminate && this.showLabel && (h("span", { key: '90b9d66a1e5cb7c8bc392e99636386c765e6479c', part: "label", class: `bs-progress__label bs-progress__label--${this.size}` }, value, "%"))));
77
+ }
78
+ static get is() { return "bs-progress"; }
79
+ static get encapsulation() { return "shadow"; }
80
+ static get originalStyleUrls() {
81
+ return {
82
+ "$": ["bs-progress.css"]
83
+ };
84
+ }
85
+ static get styleUrls() {
86
+ return {
87
+ "$": ["bs-progress.css"]
88
+ };
89
+ }
90
+ static get properties() {
91
+ return {
92
+ "type": {
93
+ "type": "string",
94
+ "mutable": false,
95
+ "complexType": {
96
+ "original": "BsProgressType",
97
+ "resolved": "\"determinate\" | \"indeterminate\"",
98
+ "references": {
99
+ "BsProgressType": {
100
+ "location": "local",
101
+ "path": "/home/runner/work/Brandsync-webcomponents/Brandsync-webcomponents/packages/wc/src/components/bs-progress/bs-progress.tsx",
102
+ "id": "src/components/bs-progress/bs-progress.tsx::BsProgressType"
103
+ }
104
+ }
105
+ },
106
+ "required": false,
107
+ "optional": false,
108
+ "docs": {
109
+ "tags": [],
110
+ "text": "`determinate` renders a static arc sweep reflecting `value` (with an optional percentage\nlabel); `indeterminate` renders a continuously spinning ring and never shows a label, since\nthere's no known percentage to report."
111
+ },
112
+ "getter": false,
113
+ "setter": false,
114
+ "reflect": false,
115
+ "attribute": "type",
116
+ "defaultValue": "'determinate'"
117
+ },
118
+ "size": {
119
+ "type": "string",
120
+ "mutable": false,
121
+ "complexType": {
122
+ "original": "BsProgressSize",
123
+ "resolved": "\"large\" | \"medium\" | \"small\"",
124
+ "references": {
125
+ "BsProgressSize": {
126
+ "location": "local",
127
+ "path": "/home/runner/work/Brandsync-webcomponents/Brandsync-webcomponents/packages/wc/src/components/bs-progress/bs-progress.tsx",
128
+ "id": "src/components/bs-progress/bs-progress.tsx::BsProgressSize"
129
+ }
130
+ }
131
+ },
132
+ "required": false,
133
+ "optional": false,
134
+ "docs": {
135
+ "tags": [],
136
+ "text": "Overall diameter: `small` (44px), `medium` (48px), or `large` (64px)."
137
+ },
138
+ "getter": false,
139
+ "setter": false,
140
+ "reflect": false,
141
+ "attribute": "size",
142
+ "defaultValue": "'small'"
143
+ },
144
+ "strokeWidth": {
145
+ "type": "number",
146
+ "mutable": false,
147
+ "complexType": {
148
+ "original": "BsProgressStrokeWidth",
149
+ "resolved": "4 | 8",
150
+ "references": {
151
+ "BsProgressStrokeWidth": {
152
+ "location": "local",
153
+ "path": "/home/runner/work/Brandsync-webcomponents/Brandsync-webcomponents/packages/wc/src/components/bs-progress/bs-progress.tsx",
154
+ "id": "src/components/bs-progress/bs-progress.tsx::BsProgressStrokeWidth"
155
+ }
156
+ }
157
+ },
158
+ "required": false,
159
+ "optional": false,
160
+ "docs": {
161
+ "tags": [],
162
+ "text": "Ring stroke width in px -- `4` or `8`, matching Figma's two stroke-width variants. A number,\nnot the Figma label's literal `\"4 px\"` string."
163
+ },
164
+ "getter": false,
165
+ "setter": false,
166
+ "reflect": false,
167
+ "attribute": "stroke-width",
168
+ "defaultValue": "4"
169
+ },
170
+ "value": {
171
+ "type": "number",
172
+ "mutable": false,
173
+ "complexType": {
174
+ "original": "number",
175
+ "resolved": "number",
176
+ "references": {}
177
+ },
178
+ "required": false,
179
+ "optional": false,
180
+ "docs": {
181
+ "tags": [],
182
+ "text": "Progress percentage, `0`-`100`. Only meaningful for `type=\"determinate\"` -- ignored (and\nnever rendered) for `type=\"indeterminate\"`. Out-of-range values are clamped defensively."
183
+ },
184
+ "getter": false,
185
+ "setter": false,
186
+ "reflect": false,
187
+ "attribute": "value",
188
+ "defaultValue": "0"
189
+ },
190
+ "showLabel": {
191
+ "type": "boolean",
192
+ "mutable": false,
193
+ "complexType": {
194
+ "original": "boolean",
195
+ "resolved": "boolean",
196
+ "references": {}
197
+ },
198
+ "required": false,
199
+ "optional": false,
200
+ "docs": {
201
+ "tags": [],
202
+ "text": "Shows the `${value}%` label below the ring. Only takes effect for `type=\"determinate\"` --\n`indeterminate` never shows a label regardless of this prop, since there's no percentage to\nshow. Defaults to `true`."
203
+ },
204
+ "getter": false,
205
+ "setter": false,
206
+ "reflect": false,
207
+ "attribute": "show-label",
208
+ "defaultValue": "true"
209
+ }
210
+ };
211
+ }
212
+ }
@@ -0,0 +1,72 @@
1
+ import { html } from "lit";
2
+ import { componentDescription, propDescription } from "../../stories-utils";
3
+ const types = ['determinate', 'indeterminate'];
4
+ const sizes = ['small', 'medium', 'large'];
5
+ const strokeWidths = [4, 8];
6
+ const meta = {
7
+ title: 'Components/bs-progress/Circular',
8
+ parameters: { docs: { description: { component: componentDescription('bs-progress') } } },
9
+ render: args => html `
10
+ <bs-progress
11
+ type=${args.type}
12
+ size=${args.size}
13
+ stroke-width=${args.strokeWidth}
14
+ value=${args.value}
15
+ show-label=${args.showLabel}
16
+ ></bs-progress>
17
+ `,
18
+ argTypes: {
19
+ type: { control: { type: 'select' }, options: types, description: propDescription('bs-progress', 'type') },
20
+ size: { control: { type: 'select' }, options: sizes, description: propDescription('bs-progress', 'size') },
21
+ strokeWidth: {
22
+ control: { type: 'select' },
23
+ options: strokeWidths,
24
+ description: propDescription('bs-progress', 'stroke-width'),
25
+ },
26
+ value: { control: { type: 'range', min: 0, max: 100 }, description: propDescription('bs-progress', 'value') },
27
+ showLabel: { control: 'boolean', description: propDescription('bs-progress', 'show-label') },
28
+ },
29
+ args: {
30
+ type: 'determinate',
31
+ size: 'small',
32
+ strokeWidth: 4,
33
+ value: 50,
34
+ showLabel: true,
35
+ },
36
+ };
37
+ export default meta;
38
+ export const Default = {};
39
+ // One instance per size, side by side, at a fixed mid-range value.
40
+ export const Sizes = {
41
+ name: 'Sizes',
42
+ render: () => html `
43
+ <div style="display: flex; align-items: flex-end; gap: 24px;">
44
+ ${sizes.map(size => html `<bs-progress size=${size} value="50"></bs-progress>`)}
45
+ </div>
46
+ `,
47
+ };
48
+ // One instance per stroke width, at a fixed size/value, to compare ring thickness directly.
49
+ export const StrokeWidths = {
50
+ name: 'Stroke widths',
51
+ render: () => html `
52
+ <div style="display: flex; align-items: flex-end; gap: 24px;">
53
+ ${strokeWidths.map(strokeWidth => html `<bs-progress size="large" stroke-width=${strokeWidth} value="50"></bs-progress>`)}
54
+ </div>
55
+ `,
56
+ };
57
+ // A few sample values across the range, illustrating the arc sweep from empty to full.
58
+ export const Values = {
59
+ name: 'Values',
60
+ render: () => html `
61
+ <div style="display: flex; align-items: flex-end; gap: 24px;">
62
+ ${[0, 25, 50, 75, 100].map(value => html `<bs-progress value=${value}></bs-progress>`)}
63
+ </div>
64
+ `,
65
+ };
66
+ export const WithoutLabel = {
67
+ name: 'Without label',
68
+ args: { showLabel: false },
69
+ };
70
+ export const Indeterminate = {
71
+ args: { type: 'indeterminate' },
72
+ };
@@ -0,0 +1,92 @@
1
+ import { render, h, describe, it, expect } from "@stencil/vitest";
2
+ describe('bs-progress-linear', () => {
3
+ describe('size/type combinations', () => {
4
+ ['small', 'large'].forEach(size => {
5
+ it(`renders size="${size}" type="determinate"`, async () => {
6
+ const { root } = await render(h("bs-progress-linear", { size: size, type: "determinate", value: 40 }));
7
+ expect(root.shadowRoot.querySelector(`.bs-progress-linear__track--${size}`)).not.toBeNull();
8
+ expect(root.shadowRoot.querySelector('[part="track"]')).not.toBeNull();
9
+ expect(root.shadowRoot.querySelector('[part="fill"]')).not.toBeNull();
10
+ });
11
+ it(`renders size="${size}" type="indeterminate"`, async () => {
12
+ const { root } = await render(h("bs-progress-linear", { size: size, type: "indeterminate" }));
13
+ expect(root.shadowRoot.querySelector('.bs-progress-linear__fill--indeterminate')).not.toBeNull();
14
+ expect(root.shadowRoot.querySelector('[part="fill"]')).not.toBeNull();
15
+ });
16
+ });
17
+ });
18
+ describe('fill width', () => {
19
+ [0, 50, 100].forEach(value => {
20
+ it(`sets fill width to ${value}% for value=${value}`, async () => {
21
+ const { root } = await render(h("bs-progress-linear", { value: value }));
22
+ const fill = root.shadowRoot.querySelector('[part="fill"]');
23
+ expect(fill.style.width).toBe(`${value}%`);
24
+ });
25
+ });
26
+ it('produces a larger width for a larger value', async () => {
27
+ const { root: rootLow } = await render(h("bs-progress-linear", { value: 10 }));
28
+ const { root: rootHigh } = await render(h("bs-progress-linear", { value: 90 }));
29
+ const lowFill = rootLow.shadowRoot.querySelector('[part="fill"]');
30
+ const highFill = rootHigh.shadowRoot.querySelector('[part="fill"]');
31
+ expect(parseFloat(highFill.style.width)).toBeGreaterThan(parseFloat(lowFill.style.width));
32
+ });
33
+ });
34
+ describe('value clamping', () => {
35
+ it('clamps a value above 100 down to 100', async () => {
36
+ const { root } = await render(h("bs-progress-linear", { value: 150 }));
37
+ const fill = root.shadowRoot.querySelector('[part="fill"]');
38
+ expect(fill.style.width).toBe('100%');
39
+ const track = root.shadowRoot.querySelector('[part="track"]');
40
+ expect(track).toEqualAttribute('aria-valuenow', '100');
41
+ });
42
+ it('clamps a negative value up to 0', async () => {
43
+ const { root } = await render(h("bs-progress-linear", { value: -10 }));
44
+ const fill = root.shadowRoot.querySelector('[part="fill"]');
45
+ expect(fill.style.width).toBe('0%');
46
+ const track = root.shadowRoot.querySelector('[part="track"]');
47
+ expect(track).toEqualAttribute('aria-valuenow', '0');
48
+ });
49
+ });
50
+ describe('label', () => {
51
+ it('renders slotted content when showLabel is true (default)', async () => {
52
+ const { root } = await render(h("bs-progress-linear", { value: 42 }, "Uploading..."));
53
+ const label = root.shadowRoot.querySelector('[part="label"]');
54
+ expect(label).not.toBeNull();
55
+ // `label.textContent` only reflects a <slot>'s own fallback content, not its assigned
56
+ // (projected) nodes -- the projected text lives on the host itself.
57
+ expect(root.textContent.trim()).toBe('Uploading...');
58
+ });
59
+ it('omits the label when showLabel is false', async () => {
60
+ const { root } = await render(h("bs-progress-linear", { value: 42, showLabel: false }, "Uploading..."));
61
+ expect(root.shadowRoot.querySelector('[part="label"]')).toBeNull();
62
+ });
63
+ it('omits the label when showLabel is true but there is no slotted content', async () => {
64
+ const { root } = await render(h("bs-progress-linear", { value: 42 }));
65
+ expect(root.shadowRoot.querySelector('[part="label"]')).toBeNull();
66
+ });
67
+ it('omits the label when slotted content is only whitespace', async () => {
68
+ const { root } = await render(h("bs-progress-linear", { value: 42 }, " "));
69
+ expect(root.shadowRoot.querySelector('[part="label"]')).toBeNull();
70
+ });
71
+ });
72
+ describe('aria attributes', () => {
73
+ it('sets aria-valuemin/max/now for determinate', async () => {
74
+ const { root } = await render(h("bs-progress-linear", { type: "determinate", value: 65 }));
75
+ const track = root.shadowRoot.querySelector('[part="track"]');
76
+ expect(track).toEqualAttribute('role', 'progressbar');
77
+ expect(track).toEqualAttribute('aria-valuemin', '0');
78
+ expect(track).toEqualAttribute('aria-valuemax', '100');
79
+ expect(track).toEqualAttribute('aria-valuenow', '65');
80
+ expect(track.hasAttribute('aria-busy')).toBe(false);
81
+ });
82
+ it('omits aria-valuenow and sets aria-busy for indeterminate', async () => {
83
+ const { root } = await render(h("bs-progress-linear", { type: "indeterminate" }));
84
+ const track = root.shadowRoot.querySelector('[part="track"]');
85
+ expect(track).toEqualAttribute('role', 'progressbar');
86
+ expect(track.hasAttribute('aria-valuenow')).toBe(false);
87
+ expect(track.hasAttribute('aria-valuemin')).toBe(false);
88
+ expect(track.hasAttribute('aria-valuemax')).toBe(false);
89
+ expect(track).toEqualAttribute('aria-busy', 'true');
90
+ });
91
+ });
92
+ });
@@ -0,0 +1,108 @@
1
+ /**
2
+ * @prop --bs-progress-linear-gap: Gap between the bar and the label. Aliased to --bs-spacing-100.
3
+ * @prop --bs-progress-linear-track: Track bar background color. Aliased to --bs-surface-container
4
+ * (matches the Figma spec's `--color/surface/container` token exactly).
5
+ * @prop --bs-progress-linear-fill: Determinate fill / indeterminate comet color. Aliased to
6
+ * --bs-color-primary-default (the same brand-blue accent bs-progress's arc and bs-button's
7
+ * primary variant already use).
8
+ * @prop --bs-progress-linear-radius: Track/fill border-radius (full pill). A fixed 120px, matching
9
+ * the Figma spec's "rounded-full" treatment.
10
+ * @prop --bs-progress-linear-height-sm: Bar height, size="small". Fixed at 4px per the Figma spec.
11
+ * @prop --bs-progress-linear-height-lg: Bar height, size="large". Fixed at 8px per the Figma spec.
12
+ * @prop --bs-progress-linear-label-color: Label text color. Aliased to --bs-text-default.
13
+ * @prop --bs-progress-linear-label-font-size: Label font size (body/md). Aliased to --bs-fontsize-body-md.
14
+ * @prop --bs-progress-linear-label-line-height: Label line height (body/md). Aliased to --bs-line-height-body-md.
15
+ * @prop --bs-progress-linear-transition-duration: Determinate fill width transition. Aliased to
16
+ * --bs-duration-default if defined by brandsync-tokens, otherwise a plain 0.3s fallback.
17
+ * @prop --bs-progress-linear-slide-duration: Indeterminate comet slide duration. Aliased to
18
+ * --bs-duration-slower if defined by brandsync-tokens, otherwise a plain 1.6s fallback.
19
+ */
20
+ :host {
21
+ display: flex;
22
+
23
+ --bs-progress-linear-gap: var(--bs-spacing-100);
24
+ --bs-progress-linear-track: var(--bs-surface-container);
25
+ --bs-progress-linear-fill: var(--bs-color-primary-default);
26
+ --bs-progress-linear-radius: 120px;
27
+ --bs-progress-linear-height-sm: 4px;
28
+ --bs-progress-linear-height-lg: 8px;
29
+ --bs-progress-linear-label-color: var(--bs-text-default);
30
+ --bs-progress-linear-label-font-size: var(--bs-fontsize-body-md);
31
+ --bs-progress-linear-label-line-height: var(--bs-line-height-body-md);
32
+ --bs-progress-linear-transition-duration: 0.3s;
33
+ --bs-progress-linear-slide-duration: 1.6s;
34
+ }
35
+
36
+ .bs-progress-linear {
37
+ display: flex;
38
+ flex-direction: column;
39
+ width: 100%;
40
+ gap: var(--bs-progress-linear-gap);
41
+ font-family: inherit;
42
+ }
43
+
44
+ .bs-progress-linear__track {
45
+ position: relative;
46
+ width: 100%;
47
+ border-radius: var(--bs-progress-linear-radius);
48
+ background: var(--bs-progress-linear-track);
49
+ overflow: hidden;
50
+ }
51
+
52
+ .bs-progress-linear__track--small {
53
+ height: var(--bs-progress-linear-height-sm);
54
+ }
55
+
56
+ .bs-progress-linear__track--large {
57
+ height: var(--bs-progress-linear-height-lg);
58
+ }
59
+
60
+ .bs-progress-linear__fill {
61
+ height: 100%;
62
+ border-radius: var(--bs-progress-linear-radius);
63
+ background: var(--bs-progress-linear-fill);
64
+ transition: width var(--bs-progress-linear-transition-duration) ease;
65
+ }
66
+
67
+ /* Indeterminate: Figma's bar is a soft "comet" gradient segment (transparent -> solid color ->
68
+ transparent) sliding across the track's full width, not a flat-colored partial fill -- the same
69
+ fade concept as bs-progress's indeterminate ring's conic-gradient comet tail, but along a
70
+ straight line. A fixed-width gradient band is animated via `transform: translateX(...)` from
71
+ fully off-screen left to fully off-screen right, looping continuously. */
72
+ .bs-progress-linear__fill--indeterminate {
73
+ position: absolute;
74
+ top: 0;
75
+ left: 0;
76
+ /* Figma's static snapshot (left-9.01%/right-66.58% of the track) puts the comet band at ~24% of
77
+ the track's width -- matching that proportion here rather than the wider 40% guess this was
78
+ originally built with. */
79
+ width: 24%;
80
+ min-width: 80px;
81
+ background: linear-gradient(90deg, transparent 0%, var(--bs-progress-linear-fill) 50%, transparent 100%);
82
+ animation: bs-progress-linear-slide var(--bs-progress-linear-slide-duration) ease-in-out infinite;
83
+ }
84
+
85
+ @keyframes bs-progress-linear-slide {
86
+ from {
87
+ transform: translateX(-100%);
88
+ }
89
+ to {
90
+ transform: translateX(250%);
91
+ }
92
+ }
93
+
94
+ /* Reduced motion: freezing the slide entirely would remove the only visual signal that work is
95
+ still in progress (indeterminate has no percentage to fall back on), so this slows the slide to
96
+ a much slower loop instead of stopping it -- still obviously "in progress" without being
97
+ motion-sickness-inducing. Same intent/rationale as bs-progress's indeterminate ring. */
98
+ @media (prefers-reduced-motion: reduce) {
99
+ .bs-progress-linear__fill--indeterminate {
100
+ animation-duration: calc(var(--bs-progress-linear-slide-duration) * 4);
101
+ }
102
+ }
103
+
104
+ .bs-progress-linear__label {
105
+ color: var(--bs-progress-linear-label-color);
106
+ font-size: var(--bs-progress-linear-label-font-size);
107
+ line-height: calc(var(--bs-progress-linear-label-line-height) * 1px);
108
+ }