@telesign/boreal-web-components 0.1.0-alpha.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 (216) hide show
  1. package/LICENSE +21 -0
  2. package/components-build/bds-banner.d.ts +11 -0
  3. package/components-build/bds-banner.js +1 -0
  4. package/components-build/bds-typography.d.ts +11 -0
  5. package/components-build/bds-typography.js +1 -0
  6. package/components-build/index.d.ts +35 -0
  7. package/components-build/index.js +1 -0
  8. package/components-build/my-component.d.ts +11 -0
  9. package/components-build/my-component.js +1 -0
  10. package/components-build/p-B9wshZ_4.js +1 -0
  11. package/components-build/p-noyWJ11s.js +1 -0
  12. package/custom-elements.json +764 -0
  13. package/dist/boreal-web-components/boreal-web-components.css +1 -0
  14. package/dist/boreal-web-components/boreal-web-components.esm.js +1 -0
  15. package/dist/boreal-web-components/boreal-web-components.js +127 -0
  16. package/dist/boreal-web-components/css/boreal.css +1594 -0
  17. package/dist/boreal-web-components/css/global.css +682 -0
  18. package/dist/boreal-web-components/css/theme-connect.css +227 -0
  19. package/dist/boreal-web-components/css/theme-engage.css +227 -0
  20. package/dist/boreal-web-components/css/theme-protect.css +227 -0
  21. package/dist/boreal-web-components/css/theme-proximus.css +227 -0
  22. package/dist/boreal-web-components/index.esm.js +0 -0
  23. package/dist/boreal-web-components/p-412d037b.system.entry.js +1 -0
  24. package/dist/boreal-web-components/p-527a761b.entry.js +1 -0
  25. package/dist/boreal-web-components/p-5666a22a.system.entry.js +1 -0
  26. package/dist/boreal-web-components/p-B-PpI2Xv.system.js +1 -0
  27. package/dist/boreal-web-components/p-B9wshZ_4.js +1 -0
  28. package/dist/boreal-web-components/p-BQdH0ijK.system.js +2 -0
  29. package/dist/boreal-web-components/p-BbPAtVJG.system.js +1 -0
  30. package/dist/boreal-web-components/p-CMd-Mv-5.system.js +1 -0
  31. package/dist/boreal-web-components/p-CaVEtaG3.system.js +1 -0
  32. package/dist/boreal-web-components/p-DQuL1Twl.js +1 -0
  33. package/dist/boreal-web-components/p-DgFiTd6X.js +2 -0
  34. package/dist/boreal-web-components/p-YWpyar7R.system.js +1 -0
  35. package/dist/boreal-web-components/p-b818618b.entry.js +1 -0
  36. package/dist/boreal-web-components/p-d596406b.entry.js +1 -0
  37. package/dist/boreal-web-components/p-e37e7dba.system.entry.js +1 -0
  38. package/dist/boreal-web-components/p-noyWJ11s.js +1 -0
  39. package/dist/boreal-web-components/scss/global/_fonts.scss +1 -0
  40. package/dist/boreal-web-components/scss/global/_index.scss +3 -0
  41. package/dist/boreal-web-components/scss/global/_reset.scss +50 -0
  42. package/dist/boreal-web-components/scss/global/_typography.scss +156 -0
  43. package/dist/boreal-web-components/scss/maps/_primitives.scss +461 -0
  44. package/dist/boreal-web-components/scss/maps/_theme-connect.scss +227 -0
  45. package/dist/boreal-web-components/scss/maps/_theme-engage.scss +227 -0
  46. package/dist/boreal-web-components/scss/maps/_theme-protect.scss +227 -0
  47. package/dist/boreal-web-components/scss/maps/_theme-proximus.scss +227 -0
  48. package/dist/boreal-web-components/scss/variables/_primitives.scss +459 -0
  49. package/dist/boreal-web-components/scss/variables/_theme-connect.scss +225 -0
  50. package/dist/boreal-web-components/scss/variables/_theme-engage.scss +225 -0
  51. package/dist/boreal-web-components/scss/variables/_theme-protect.scss +225 -0
  52. package/dist/boreal-web-components/scss/variables/_theme-proximus.scss +225 -0
  53. package/dist/cjs/app-globals-V2Kpy_OQ.js +5 -0
  54. package/dist/cjs/attributes-RPVEtBdj.js +90 -0
  55. package/dist/cjs/bds-banner.cjs.entry.js +130 -0
  56. package/dist/cjs/bds-typography.cjs.entry.js +167 -0
  57. package/dist/cjs/boreal-web-components.cjs.js +46 -0
  58. package/dist/cjs/index-CD9v53WJ.js +133 -0
  59. package/dist/cjs/index-Cdb66Tqj.js +2342 -0
  60. package/dist/cjs/index.cjs.js +2 -0
  61. package/dist/cjs/loader.cjs.js +13 -0
  62. package/dist/cjs/my-component.cjs.entry.js +29 -0
  63. package/dist/collection/collection-manifest.json +14 -0
  64. package/dist/collection/components/feedback/bds-banner/bds-banner.css +101 -0
  65. package/dist/collection/components/feedback/bds-banner/bds-banner.js +266 -0
  66. package/dist/collection/components/feedback/bds-banner/types/IBanner.js +1 -0
  67. package/dist/collection/components/my-component/my-component.css +3 -0
  68. package/dist/collection/components/my-component/my-component.js +95 -0
  69. package/dist/collection/components/titles-text/bds-typography/bds-typography.css +183 -0
  70. package/dist/collection/components/titles-text/bds-typography/bds-typography.js +577 -0
  71. package/dist/collection/components/titles-text/bds-typography/types/ITypography.js +1 -0
  72. package/dist/collection/components/titles-text/bds-typography/types/enum.js +23 -0
  73. package/dist/collection/components/titles-text/bds-typography/types/types.js +1 -0
  74. package/dist/collection/components/titles-text/bds-typography/utils/bds-typography-utils.js +41 -0
  75. package/dist/collection/css/boreal.css +1594 -0
  76. package/dist/collection/css/global.css +682 -0
  77. package/dist/collection/css/theme-connect.css +227 -0
  78. package/dist/collection/css/theme-engage.css +227 -0
  79. package/dist/collection/css/theme-protect.css +227 -0
  80. package/dist/collection/css/theme-proximus.css +227 -0
  81. package/dist/collection/index.js +1 -0
  82. package/dist/collection/mixins/anchored.mixin.js +272 -0
  83. package/dist/collection/mixins/floating.mixin.js +181 -0
  84. package/dist/collection/mixins/form-associated.mixin.js +95 -0
  85. package/dist/collection/mixins/index.js +3 -0
  86. package/dist/collection/scss/global/_fonts.scss +1 -0
  87. package/dist/collection/scss/global/_index.scss +3 -0
  88. package/dist/collection/scss/global/_reset.scss +50 -0
  89. package/dist/collection/scss/global/_typography.scss +156 -0
  90. package/dist/collection/scss/maps/_primitives.scss +461 -0
  91. package/dist/collection/scss/maps/_theme-connect.scss +227 -0
  92. package/dist/collection/scss/maps/_theme-engage.scss +227 -0
  93. package/dist/collection/scss/maps/_theme-protect.scss +227 -0
  94. package/dist/collection/scss/maps/_theme-proximus.scss +227 -0
  95. package/dist/collection/scss/variables/_primitives.scss +459 -0
  96. package/dist/collection/scss/variables/_theme-connect.scss +225 -0
  97. package/dist/collection/scss/variables/_theme-engage.scss +225 -0
  98. package/dist/collection/scss/variables/_theme-protect.scss +225 -0
  99. package/dist/collection/scss/variables/_theme-proximus.scss +225 -0
  100. package/dist/collection/services/floating/interfaces/Floating.js +1 -0
  101. package/dist/collection/services/floating/interfaces/Positioning.js +1 -0
  102. package/dist/collection/services/floating/interfaces/Props.js +1 -0
  103. package/dist/collection/services/floating/positioning.service.js +71 -0
  104. package/dist/collection/services/floating/types/Arrow.js +1 -0
  105. package/dist/collection/services/logger/Logger.js +47 -0
  106. package/dist/collection/types/alignment.js +6 -0
  107. package/dist/collection/types/form.js +1 -0
  108. package/dist/collection/types/index.js +5 -0
  109. package/dist/collection/types/position.js +11 -0
  110. package/dist/collection/types/size.js +7 -0
  111. package/dist/collection/types/states.js +22 -0
  112. package/dist/collection/types/stylesMap.js +1 -0
  113. package/dist/collection/utils/a11y/attributes.js +80 -0
  114. package/dist/collection/utils/a11y/index.js +1 -0
  115. package/dist/collection/utils/constants/common/Events.js +10 -0
  116. package/dist/collection/utils/constants/common/Keys.js +16 -0
  117. package/dist/collection/utils/dom/elements.js +22 -0
  118. package/dist/collection/utils/dom/index.js +1 -0
  119. package/dist/collection/utils/form/index.js +1 -0
  120. package/dist/collection/utils/form/internals.js +79 -0
  121. package/dist/collection/utils/helpers/common/BaseAttributes.js +17 -0
  122. package/dist/collection/utils/index.js +3 -0
  123. package/dist/css/boreal.css +1594 -0
  124. package/dist/css/global.css +682 -0
  125. package/dist/css/theme-connect.css +227 -0
  126. package/dist/css/theme-engage.css +227 -0
  127. package/dist/css/theme-protect.css +227 -0
  128. package/dist/css/theme-proximus.css +227 -0
  129. package/dist/esm/app-globals-DQuL1Twl.js +3 -0
  130. package/dist/esm/attributes-B9wshZ_4.js +86 -0
  131. package/dist/esm/bds-banner.entry.js +128 -0
  132. package/dist/esm/bds-typography.entry.js +165 -0
  133. package/dist/esm/boreal-web-components.js +42 -0
  134. package/dist/esm/index-DgFiTd6X.js +2332 -0
  135. package/dist/esm/index-noyWJ11s.js +131 -0
  136. package/dist/esm/index.js +1 -0
  137. package/dist/esm/loader.js +11 -0
  138. package/dist/esm/my-component.entry.js +27 -0
  139. package/dist/esm/polyfills/core-js.js +11 -0
  140. package/dist/esm/polyfills/dom.js +79 -0
  141. package/dist/esm/polyfills/es5-html-element.js +1 -0
  142. package/dist/esm/polyfills/index.js +34 -0
  143. package/dist/esm/polyfills/system.js +6 -0
  144. package/dist/esm-es5/app-globals-DQuL1Twl.js +1 -0
  145. package/dist/esm-es5/attributes-B9wshZ_4.js +1 -0
  146. package/dist/esm-es5/bds-banner.entry.js +1 -0
  147. package/dist/esm-es5/bds-typography.entry.js +1 -0
  148. package/dist/esm-es5/boreal-web-components.js +1 -0
  149. package/dist/esm-es5/index-DgFiTd6X.js +2 -0
  150. package/dist/esm-es5/index-noyWJ11s.js +1 -0
  151. package/dist/esm-es5/index.js +0 -0
  152. package/dist/esm-es5/loader.js +1 -0
  153. package/dist/esm-es5/my-component.entry.js +1 -0
  154. package/dist/index.cjs.js +1 -0
  155. package/dist/index.js +1 -0
  156. package/dist/scss/global/_fonts.scss +1 -0
  157. package/dist/scss/global/_index.scss +3 -0
  158. package/dist/scss/global/_reset.scss +50 -0
  159. package/dist/scss/global/_typography.scss +156 -0
  160. package/dist/scss/maps/_primitives.scss +461 -0
  161. package/dist/scss/maps/_theme-connect.scss +227 -0
  162. package/dist/scss/maps/_theme-engage.scss +227 -0
  163. package/dist/scss/maps/_theme-protect.scss +227 -0
  164. package/dist/scss/maps/_theme-proximus.scss +227 -0
  165. package/dist/scss/variables/_primitives.scss +459 -0
  166. package/dist/scss/variables/_theme-connect.scss +225 -0
  167. package/dist/scss/variables/_theme-engage.scss +225 -0
  168. package/dist/scss/variables/_theme-protect.scss +225 -0
  169. package/dist/scss/variables/_theme-proximus.scss +225 -0
  170. package/dist/types/components/feedback/bds-banner/bds-banner.d.ts +71 -0
  171. package/dist/types/components/feedback/bds-banner/types/IBanner.d.ts +10 -0
  172. package/dist/types/components/my-component/my-component.d.ts +16 -0
  173. package/dist/types/components/titles-text/bds-typography/bds-typography.d.ts +99 -0
  174. package/dist/types/components/titles-text/bds-typography/types/ITypography.d.ts +22 -0
  175. package/dist/types/components/titles-text/bds-typography/types/enum.d.ts +24 -0
  176. package/dist/types/components/titles-text/bds-typography/types/types.d.ts +4 -0
  177. package/dist/types/components/titles-text/bds-typography/utils/bds-typography-utils.d.ts +15 -0
  178. package/dist/types/components.d.ts +565 -0
  179. package/dist/types/index.d.ts +11 -0
  180. package/dist/types/mixins/anchored.mixin.d.ts +228 -0
  181. package/dist/types/mixins/floating.mixin.d.ts +158 -0
  182. package/dist/types/mixins/form-associated.mixin.d.ts +127 -0
  183. package/dist/types/mixins/index.d.ts +4 -0
  184. package/dist/types/services/floating/interfaces/Floating.d.ts +38 -0
  185. package/dist/types/services/floating/interfaces/Positioning.d.ts +22 -0
  186. package/dist/types/services/floating/interfaces/Props.d.ts +35 -0
  187. package/dist/types/services/floating/positioning.service.d.ts +45 -0
  188. package/dist/types/services/floating/types/Arrow.d.ts +7 -0
  189. package/dist/types/services/logger/Logger.d.ts +50 -0
  190. package/dist/types/stencil-public-runtime.d.ts +1858 -0
  191. package/dist/types/types/alignment.d.ts +8 -0
  192. package/dist/types/types/form.d.ts +7 -0
  193. package/dist/types/types/index.d.ts +6 -0
  194. package/dist/types/types/position.d.ts +13 -0
  195. package/dist/types/types/size.d.ts +9 -0
  196. package/dist/types/types/states.d.ts +26 -0
  197. package/dist/types/types/stylesMap.d.ts +2 -0
  198. package/dist/types/utils/a11y/attributes.d.ts +13 -0
  199. package/dist/types/utils/a11y/index.d.ts +2 -0
  200. package/dist/types/utils/constants/common/Events.d.ts +14 -0
  201. package/dist/types/utils/constants/common/Keys.d.ts +18 -0
  202. package/dist/types/utils/dom/elements.d.ts +12 -0
  203. package/dist/types/utils/dom/index.d.ts +2 -0
  204. package/dist/types/utils/form/index.d.ts +2 -0
  205. package/dist/types/utils/form/internals.d.ts +66 -0
  206. package/dist/types/utils/helpers/common/BaseAttributes.d.ts +16 -0
  207. package/dist/types/utils/index.d.ts +4 -0
  208. package/loader/cdn.js +2 -0
  209. package/loader/index.cjs.js +2 -0
  210. package/loader/index.d.ts +24 -0
  211. package/loader/index.es2017.js +2 -0
  212. package/loader/index.js +3 -0
  213. package/package.json +96 -0
  214. package/readme.md +111 -0
  215. package/scripts/copy-styles.js +18 -0
  216. package/scripts/postbuild.js +21 -0
@@ -0,0 +1,272 @@
1
+ import { PositioningEngine } from "../services/floating/positioning.service";
2
+ import { floatingMixin } from "./floating.mixin";
3
+ import { Logger } from "../services/logger/Logger";
4
+ import { autoUpdate } from "@floating-ui/dom";
5
+ import { KEYBOARD } from "../utils/constants/common/Keys";
6
+ import { EVENTS } from "../utils/constants/common/Events";
7
+ /**
8
+ * Positioning and trigger mixin for anchor-based floating elements.
9
+ *
10
+ * Extends `floatingMixin` with two additional responsibilities:
11
+ * - **Positioning** — computes and maintains the position of the floating element
12
+ * relative to a trigger element using `IFloatingAdapter` (wraps Floating UI).
13
+ * Position is kept in sync via `autoUpdate` while the element is visible.
14
+ * - **Triggers** — manages DOM event listeners on the trigger element
15
+ * (focus/blur, click) to show and hide the floating element.
16
+ *
17
+ * Use this mixin for components whose floating element must be anchored
18
+ * to a specific DOM element: `Tooltip`, `Popover`, `Dropdown`.
19
+ *
20
+ * For viewport-relative components (Dialog, Drawer, Toast),
21
+ * use `backdropMixin` instead.
22
+ *
23
+ * ## Lifecycle flow
24
+ * ```
25
+ * showElement() → showPopover() + startAutoUpdate()
26
+ * hideElement() → hidePopover() + stopAutoUpdate()
27
+ * ```
28
+ *
29
+ * ## Required by the component
30
+ * - `triggerSlot` — must be assigned to the trigger element before `show()` is called.
31
+ * - `floatingContent` — must be assigned via `ref` in the render method.
32
+ * - `options` — override this getter to customize placement, offset and strategy.
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * import { Component, Element, Mixin, Prop, h } from '@stencil/core';
37
+ * import { anchoredMixin } from '@/mixins/anchored.mixin';
38
+ * import { FloatingHooks, FloatingMixinOptions } from '@/services/floating/interfaces/Floating';
39
+ *
40
+ * @Component({ tag: 'bds-tooltip' })
41
+ * export class BdsTooltip extends Mixin(anchoredMixin) {
42
+ *
43
+ * get options(): FloatingMixinOptions {
44
+ * return { placement: 'bottom', offset: 8, strategy: 'fixed' };
45
+ * }
46
+ *
47
+ * get hooks(): FloatingHooks {
48
+ * return {
49
+ * onBeforeShow: () => !this.disabled,
50
+ * onPositionUpdate: result => this.handlePosition(result),
51
+ * };
52
+ * }
53
+ *
54
+ * componentDidLoad() {
55
+ * const trigger = document.getElementById(this.trigger);
56
+ * if (trigger) {
57
+ * this.triggerSlot = trigger;
58
+ * trigger.addEventListener('click', () => this.toggle());
59
+ * }
60
+ * }
61
+ *
62
+ * render() {
63
+ * return (
64
+ * <Host>
65
+ * <div
66
+ * popover="manual"
67
+ * ref={el => (this.floatingContent = el as HTMLElement)}
68
+ * />
69
+ * </Host>
70
+ * );
71
+ * }
72
+ * }
73
+ * ```
74
+ *
75
+ * @see floatingMixin - base lifecycle mixin
76
+ * @see IFloatingAdapter - positioning adapter interface
77
+ * @see FloatingMixinOptions - positioning configuration interface
78
+ * @see FloatingHooks - lifecycle hook interface, includes `onPositionUpdate`
79
+ */
80
+ export const anchoredMixin = (Base) => {
81
+ class Anchored extends floatingMixin(Base) {
82
+ /**
83
+ * Default positioning options.
84
+ * Override this getter in the component to customize placement,
85
+ * offset, strategy, flip, shift, and arrow.
86
+ */
87
+ get options() {
88
+ return { placement: 'bottom', offset: 8, strategy: 'fixed' };
89
+ }
90
+ /**
91
+ * Shows the floating element using the Popover API
92
+ * and starts position auto-update relative to `triggerSlot`.
93
+ *
94
+ * @override floatingMixin.showElement
95
+ */
96
+ showElement() {
97
+ this.floatingContent.showPopover();
98
+ this.isVisible = true;
99
+ this.startAutoUpdate(this.triggerSlot, this.floatingContent, this.options, result => {
100
+ this.hooks.onPositionUpdate?.(result);
101
+ });
102
+ }
103
+ /**
104
+ * Guards against showing without a valid trigger.
105
+ * Delegates to `floatingMixin.onBeforeShow` if the guard passes.
106
+ *
107
+ * @override floatingMixin.onBeforeShow
108
+ * @param target - Optional element that triggered the show action
109
+ * @returns `false` if `triggerSlot` is not set, otherwise delegates to super
110
+ */
111
+ onBeforeShow(target) {
112
+ if (this.triggerSlot === null) {
113
+ this.logger.error('AnchoredMixin.show', 'triggerSlot is required');
114
+ return false;
115
+ }
116
+ return super.onBeforeShow(target);
117
+ }
118
+ /**
119
+ * Hides the floating element using the Popover API
120
+ * and stops position auto-update.
121
+ *
122
+ * @override floatingMixin.hideElement
123
+ */
124
+ hideElement() {
125
+ this.stopAutoUpdate();
126
+ this.floatingContent?.hidePopover();
127
+ this.isVisible = false;
128
+ }
129
+ /**
130
+ * Handles slot change events to update the trigger element reference
131
+ * and re-attach event listeners to the new trigger.
132
+ *
133
+ * @param e - The slot change event
134
+ * @param showFn - Bound show function to attach as listener
135
+ * @param hideFn - Bound hide function to attach as listener
136
+ */
137
+ handleSlotChange(e, showFn, hideFn) {
138
+ const newTrigger = e.target;
139
+ if (this.previousTrigger !== undefined && this.previousTrigger !== null) {
140
+ this.detachTriggerListeners(this.previousTrigger, showFn, hideFn);
141
+ }
142
+ if (newTrigger !== undefined && newTrigger !== null) {
143
+ this.attachTriggerListeners(newTrigger, showFn, hideFn);
144
+ this.previousTrigger = newTrigger;
145
+ }
146
+ }
147
+ /**
148
+ * Handles keyboard events on the trigger element.
149
+ * - `Enter` / `Space` — shows the floating element
150
+ * - `Escape` — hides the floating element
151
+ *
152
+ * @param e - The keyboard event
153
+ * @param showFn - Bound show function
154
+ * @param hideFn - Bound hide function
155
+ */
156
+ handleKeydown(e, showFn, hideFn) {
157
+ if (e.key === KEYBOARD.Enter.key || e.key === ' ')
158
+ showFn();
159
+ if (e.key === KEYBOARD.Escape.key)
160
+ hideFn();
161
+ }
162
+ /**
163
+ * Computes the position of the floating element relative to the trigger
164
+ * using `IFloatingAdapter` and applies the result to the floating element's style.
165
+ *
166
+ * @param triggerElement - The anchor element
167
+ * @param floatingElement - The element to position
168
+ * @param options - Positioning options (placement, offset, flip, shift, arrow)
169
+ * @param onPositionUpdate - Optional callback invoked after each position update
170
+ */
171
+ async updatePosition(triggerElement, floatingElement, options, onPositionUpdate) {
172
+ const result = await this.positionEngine.computePosition(triggerElement, floatingElement, {
173
+ placement: options.placement,
174
+ offset: options.offset,
175
+ flip: options.flip ?? true,
176
+ shift: options.shift ?? true,
177
+ arrow: options.arrow,
178
+ strategy: options.strategy ?? 'fixed',
179
+ });
180
+ this.positionEngine.applyPosition(floatingElement, result);
181
+ onPositionUpdate?.(result);
182
+ return result;
183
+ }
184
+ /**
185
+ * Starts automatic position synchronization between the trigger
186
+ * and the floating element. Runs `updatePosition` once immediately,
187
+ * then subscribes to DOM/scroll/resize changes via `autoUpdate`.
188
+ *
189
+ * @param triggerElement - The anchor element
190
+ * @param floatingElement - The element to keep in sync
191
+ * @param options - Positioning options
192
+ * @param onPositionUpdate - Optional callback after each position update
193
+ */
194
+ startAutoUpdate(triggerElement, floatingElement, options, onPositionUpdate) {
195
+ const sync = () => {
196
+ void this.updatePosition(triggerElement, floatingElement, options, onPositionUpdate);
197
+ };
198
+ sync(); // run once immediately
199
+ this.cleanupAutoUpdate = autoUpdate(triggerElement, floatingElement, sync);
200
+ }
201
+ /**
202
+ * Stops automatic position synchronization and clears the cleanup reference.
203
+ */
204
+ stopAutoUpdate() {
205
+ this.cleanupAutoUpdate?.();
206
+ this.cleanupAutoUpdate = undefined;
207
+ }
208
+ /**
209
+ * Attaches focus and blur listeners to the trigger element.
210
+ *
211
+ * @param trigger - The trigger element
212
+ * @param showFn - Bound show function
213
+ * @param hideFn - Bound hide function
214
+ */
215
+ attachTriggerListeners(trigger, showFn, hideFn) {
216
+ trigger.addEventListener(EVENTS.Focus, showFn);
217
+ trigger.addEventListener(EVENTS.Blur, hideFn);
218
+ }
219
+ /**
220
+ * Detaches focus and blur listeners from the trigger element.
221
+ *
222
+ * @param trigger - The trigger element
223
+ * @param showFn - Bound show function to remove
224
+ * @param hideFn - Bound hide function to remove
225
+ */
226
+ detachTriggerListeners(trigger, showFn, hideFn) {
227
+ trigger.removeEventListener(EVENTS.Focus, showFn);
228
+ trigger.removeEventListener(EVENTS.Blur, hideFn);
229
+ }
230
+ /**
231
+ * Detaches trigger listeners and stops auto-update.
232
+ * Call this when the component disconnects from the DOM.
233
+ *
234
+ * @param showFn - Bound show function to remove
235
+ * @param hideFn - Bound hide function to remove
236
+ */
237
+ floatingDisconnect(showFn, hideFn) {
238
+ if (this.previousTrigger !== undefined && this.previousTrigger !== null) {
239
+ this.detachTriggerListeners(this.previousTrigger, showFn, hideFn);
240
+ }
241
+ this.stopAutoUpdate();
242
+ }
243
+ /**
244
+ * Initializes the positioning engine, logger, and binds lifecycle methods
245
+ * so they can be safely passed as event listener callbacks.
246
+ *
247
+ * @remarks `show`, `hide`, and `toggle` are bound here — not in `floatingMixin` —
248
+ * to ensure they resolve to the `anchoredMixin` overrides at call time.
249
+ */
250
+ componentWillLoad() {
251
+ this.toggle = this.toggle.bind(this);
252
+ this.show = this.show.bind(this);
253
+ this.hide = this.hide.bind(this);
254
+ this.positionEngine = new PositioningEngine();
255
+ this.logger = new Logger();
256
+ this.cleanupAutoUpdate = undefined;
257
+ this.previousTrigger = undefined;
258
+ }
259
+ /**
260
+ * Cleans up trigger listeners, stops auto-update, and hides the floating element
261
+ * when the component is removed from the DOM.
262
+ */
263
+ disconnectedCallback() {
264
+ if (this.previousTrigger !== undefined && this.previousTrigger !== null) {
265
+ this.floatingDisconnect(() => this.show(), () => this.hide());
266
+ }
267
+ this.stopAutoUpdate();
268
+ this.hide();
269
+ }
270
+ }
271
+ return Anchored;
272
+ };
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Core lifecycle mixin for all floating elements in the design system.
3
+ *
4
+ * Provides a unified lifecycle contract (`show`, `hide`, `toggle`, `isVisible`)
5
+ * and an extension mechanism via `FloatingHooks` that allows each component
6
+ * to inject its own logic at specific lifecycle points without modifying the mixin.
7
+ *
8
+ * This mixin is the base for all floating components. It does not handle
9
+ * positioning or triggers — use `anchoredMixin` for components that need
10
+ * to be anchored to a DOM element (Tooltip, Popover),
11
+ * or `backdropMixin` for viewport-relative components (Dialog, Drawer, Toast).
12
+ *
13
+ * ## Extension points
14
+ *
15
+ * Override `showElement` / `hideElement` to control how the element
16
+ * mounts and unmounts (e.g. `showPopover()`, `showModal()`, CSS animations).
17
+ *
18
+ * Override `hooks` to inject logic at lifecycle points:
19
+ * - `onBeforeShow` / `onBeforeHide` — return `false` to cancel the action
20
+ * - `onAfterShow` / `onAfterHide` — side effects after the action completes
21
+ * - `mounted` / `unmounted` — called after the element is shown or hidden
22
+ *
23
+ * ## Lifecycle flow
24
+ * ```
25
+ * show(): onBeforeShow() → showElement() → onAfterShow()
26
+ * hide(): onBeforeHide() → hideElement() → onAfterHide()
27
+ * ```
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * import { Component, Mixin, Prop } from '@stencil/core';
32
+ * import { floatingMixin } from '@/mixins/floating.mixin';
33
+ * import { FloatingHooks } from '@/services/floating/interfaces/Floating';
34
+ *
35
+ * @Component({ tag: 'bds-dialog' })
36
+ * export class BdsDialog extends Mixin(floatingMixin) {
37
+ * private dialogElement!: HTMLDialogElement;
38
+ *
39
+ * // Override showElement/hideElement to use the native <dialog> API
40
+ * showElement(): void {
41
+ * this.dialogElement.showModal();
42
+ * this.isVisible = true;
43
+ * }
44
+ * hideElement(): void {
45
+ * this.dialogElement.close();
46
+ * this.isVisible = false;
47
+ * }
48
+ *
49
+ * // Inject component-specific logic via hooks
50
+ * get hooks(): FloatingHooks {
51
+ * return {
52
+ * onBeforeShow: () => !this.disabled,
53
+ * onAfterShow: () => this.lockScroll(),
54
+ * onAfterHide: () => this.unlockScroll(),
55
+ * };
56
+ * }
57
+ * }
58
+ * ```
59
+ */
60
+ export const floatingMixin = (Base) => {
61
+ class Floating extends Base {
62
+ constructor() {
63
+ super();
64
+ }
65
+ /**
66
+ * Lifecycle hooks provided by the component.
67
+ * Override this getter to inject component-specific logic
68
+ * into the floating lifecycle without modifying the mixin.
69
+ *
70
+ * @returns FloatingHooks — an object with optional lifecycle callbacks
71
+ */
72
+ get hooks() {
73
+ return {};
74
+ }
75
+ /**
76
+ * Called before the element is shown.
77
+ * Aggregates both the component `hooks` and `floatingOptions` controls.
78
+ * If either returns `false`, the show action is cancelled.
79
+ *
80
+ * @param target - Optional element that triggered the show action
81
+ * @returns `true` if the element can be shown, `false` to cancel
82
+ */
83
+ onBeforeShow(target) {
84
+ const hookControl = this.hooks.onBeforeShow?.(target) ?? true;
85
+ const propControl = this.floatingOptions?.onBeforeShow?.(target) ?? true;
86
+ return hookControl && propControl;
87
+ }
88
+ /**
89
+ * Called before the element is hidden.
90
+ * Aggregates both the component `hooks` and `floatingOptions` controls.
91
+ * If either returns `false`, the hide action is cancelled.
92
+ *
93
+ * @param target - Optional element the mouse moved to (used for stayOnHover logic)
94
+ * @returns `true` if the element can be hidden, `false` to cancel
95
+ */
96
+ onBeforeHide(target) {
97
+ const hookControl = this.hooks.onBeforeHide?.(target) ?? true;
98
+ const propControl = this.floatingOptions?.onBeforeHide?.(target) ?? true;
99
+ return hookControl && propControl;
100
+ }
101
+ /**
102
+ * Mounts the floating element into the visible DOM.
103
+ * Override in subclasses to use a different mount strategy
104
+ * (e.g. `showPopover()`, `showModal()`, CSS class toggle).
105
+ */
106
+ showElement() {
107
+ this.isVisible = true;
108
+ this.hooks.mounted?.(this.floatingContent);
109
+ }
110
+ /**
111
+ * Unmounts the floating element from the visible DOM.
112
+ * Override in subclasses to use a different unmount strategy.
113
+ */
114
+ hideElement() {
115
+ this.isVisible = false;
116
+ this.hooks.unmounted?.(this.floatingContent);
117
+ }
118
+ /**
119
+ * Shows the floating element if not already visible.
120
+ * Runs the full lifecycle: `onBeforeShow` → `showElement` → `onAfterShow`.
121
+ *
122
+ * @param target - Optional element that triggered the action
123
+ */
124
+ show(target) {
125
+ if (this.isVisible)
126
+ return;
127
+ if (!this.onBeforeShow(target))
128
+ return;
129
+ this.showElement();
130
+ this.onAfterShow(target);
131
+ }
132
+ /**
133
+ * Hides the floating element if currently visible.
134
+ * Runs the full lifecycle: `onBeforeHide` → `hideElement` → `onAfterHide`.
135
+ *
136
+ * @param target - Optional element the mouse moved to
137
+ */
138
+ hide(target) {
139
+ if (!this.isVisible)
140
+ return;
141
+ if (!this.onBeforeHide(target))
142
+ return;
143
+ this.hideElement();
144
+ this.onAfterHide(target);
145
+ }
146
+ /**
147
+ * Toggles the floating element visibility.
148
+ *
149
+ * @param target - Optional element that triggered the action
150
+ */
151
+ toggle(target) {
152
+ this.isVisible ? this.hide(target) : this.show();
153
+ }
154
+ /**
155
+ * Called after the element is shown.
156
+ * Notifies both `hooks` and `floatingOptions` consumers.
157
+ *
158
+ * @param target - Optional element that triggered the action
159
+ */
160
+ onAfterShow(target) {
161
+ this.hooks.onAfterShow?.(target);
162
+ this.floatingOptions?.onAfterShow?.(target);
163
+ }
164
+ /**
165
+ * Called after the element is hidden.
166
+ * Notifies both `hooks` and `floatingOptions` consumers.
167
+ *
168
+ * @param target - Optional element that triggered the action
169
+ */
170
+ onAfterHide(target) {
171
+ this.hooks.onAfterHide?.(target);
172
+ this.floatingOptions?.onAfterHide?.(target);
173
+ }
174
+ static get states() {
175
+ return {
176
+ "isVisible": {}
177
+ };
178
+ }
179
+ }
180
+ return Floating;
181
+ };
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Shared base mixin for Form-Associated Custom Elements in Boreal DS.
3
+ *
4
+ * Provides:
5
+ * - `name`, `disabled`, and `required` form props
6
+ * - `formDisabledCallback` with universal disabled sync behavior
7
+ *
8
+ * Each component must declare `@AttachInternals() internals!: ElementInternals`
9
+ * directly on its class body — Stencil's compiler requires this decorator to be
10
+ * statically visible on the component class, not inside a mixin factory.
11
+ *
12
+ * Components must also implement `IFormControl<T>` for value registration,
13
+ * reset, state restoration, and 2-way binding event emission.
14
+ */
15
+ export const formAssociatedMixin = (Base) => {
16
+ class FormAssociated extends Base {
17
+ constructor() {
18
+ /** Disables the control. Synced automatically from a parent `<fieldset>` or `<form>` via `formDisabledCallback`. */
19
+ this.disabled = false;
20
+ /** Marks the control as required for form submission. */
21
+ this.required = false;
22
+ super();
23
+ }
24
+ /**
25
+ * Sync component disabled state with parent form disabled state.
26
+ */
27
+ formDisabledCallback(disabled) {
28
+ this.disabled = disabled;
29
+ }
30
+ static get properties() {
31
+ return {
32
+ "name": {
33
+ "type": "string",
34
+ "mutable": false,
35
+ "complexType": {
36
+ "original": "string",
37
+ "resolved": "string",
38
+ "references": {}
39
+ },
40
+ "required": true,
41
+ "optional": false,
42
+ "docs": {
43
+ "tags": [],
44
+ "text": "Name of the form control, submitted as a key in the form data."
45
+ },
46
+ "getter": false,
47
+ "setter": false,
48
+ "reflect": true,
49
+ "attribute": "name"
50
+ },
51
+ "disabled": {
52
+ "type": "boolean",
53
+ "mutable": true,
54
+ "complexType": {
55
+ "original": "boolean",
56
+ "resolved": "boolean",
57
+ "references": {}
58
+ },
59
+ "required": false,
60
+ "optional": false,
61
+ "docs": {
62
+ "tags": [],
63
+ "text": "Disables the control. Synced automatically from a parent `<fieldset>` or `<form>` via `formDisabledCallback`."
64
+ },
65
+ "getter": false,
66
+ "setter": false,
67
+ "reflect": true,
68
+ "attribute": "disabled",
69
+ "defaultValue": "false"
70
+ },
71
+ "required": {
72
+ "type": "boolean",
73
+ "mutable": false,
74
+ "complexType": {
75
+ "original": "boolean",
76
+ "resolved": "boolean",
77
+ "references": {}
78
+ },
79
+ "required": false,
80
+ "optional": false,
81
+ "docs": {
82
+ "tags": [],
83
+ "text": "Marks the control as required for form submission."
84
+ },
85
+ "getter": false,
86
+ "setter": false,
87
+ "reflect": true,
88
+ "attribute": "required",
89
+ "defaultValue": "false"
90
+ }
91
+ };
92
+ }
93
+ }
94
+ return FormAssociated;
95
+ };
@@ -0,0 +1,3 @@
1
+ export * from './form-associated.mixin';
2
+ export * from './floating.mixin';
3
+ export * from './anchored.mixin';
@@ -0,0 +1 @@
1
+ @import url('https://fonts.googleapis.com/css2?family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap');
@@ -0,0 +1,3 @@
1
+ @forward '_fonts';
2
+ @forward '_typography';
3
+ @forward '_reset';
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Boreal Design System - CSS Reset
3
+ * Minimal reset for consistent cross-browser rendering
4
+ */
5
+ *,
6
+ *::before,
7
+ *::after {
8
+ box-sizing: border-box;
9
+ }
10
+
11
+ * {
12
+ margin: 0;
13
+ padding: 0;
14
+ }
15
+
16
+ html {
17
+ -webkit-font-smoothing: antialiased;
18
+ -moz-osx-font-smoothing: grayscale;
19
+ }
20
+
21
+ body {
22
+ line-height: 1.5;
23
+ -webkit-text-size-adjust: 100%;
24
+ }
25
+
26
+ img,
27
+ picture,
28
+ video,
29
+ canvas,
30
+ svg {
31
+ display: block;
32
+ max-width: 100%;
33
+ }
34
+
35
+ input,
36
+ button,
37
+ textarea,
38
+ select {
39
+ font: inherit;
40
+ }
41
+
42
+ p,
43
+ h1,
44
+ h2,
45
+ h3,
46
+ h4,
47
+ h5,
48
+ h6 {
49
+ overflow-wrap: break-word;
50
+ }