baseline-foundry 0.1.3

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 (145) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +588 -0
  3. package/config/experiments/ibm-plex-engine-smoke.json +115 -0
  4. package/config/experiments/ubuntu-engine-smoke.json +115 -0
  5. package/config/foundation-theme.json +117 -0
  6. package/config/tiers/app.json +120 -0
  7. package/config/tiers/documentation.json +120 -0
  8. package/config/tiers/editorial.json +120 -0
  9. package/config/tiers/os.json +120 -0
  10. package/dist/accordion.d.ts +5 -0
  11. package/dist/accordion.js +32 -0
  12. package/dist/application-layout.d.ts +4 -0
  13. package/dist/application-layout.js +181 -0
  14. package/dist/baseline-grid-overlay.d.ts +5 -0
  15. package/dist/baseline-grid-overlay.js +50 -0
  16. package/dist/baseline-grid-theme.d.ts +3 -0
  17. package/dist/baseline-grid-theme.js +3 -0
  18. package/dist/baseline-grid.d.ts +7 -0
  19. package/dist/baseline-grid.js +35 -0
  20. package/dist/build.d.ts +21 -0
  21. package/dist/build.js +440 -0
  22. package/dist/code-snippet.d.ts +6 -0
  23. package/dist/code-snippet.js +95 -0
  24. package/dist/contextual-menu.d.ts +4 -0
  25. package/dist/contextual-menu.js +156 -0
  26. package/dist/css-app-tier.d.ts +1 -0
  27. package/dist/css-app-tier.js +77 -0
  28. package/dist/css-components/article-pagination.d.ts +13 -0
  29. package/dist/css-components/article-pagination.js +151 -0
  30. package/dist/css-components/button-actions.d.ts +7 -0
  31. package/dist/css-components/button-actions.js +165 -0
  32. package/dist/css-components/cards-options.d.ts +6 -0
  33. package/dist/css-components/cards-options.js +180 -0
  34. package/dist/css-components/chip-badge-status.d.ts +10 -0
  35. package/dist/css-components/chip-badge-status.js +162 -0
  36. package/dist/css-components/content-card.d.ts +25 -0
  37. package/dist/css-components/content-card.js +517 -0
  38. package/dist/css-components/control-row.d.ts +2 -0
  39. package/dist/css-components/control-row.js +18 -0
  40. package/dist/css-components/cta-figure-aspect.d.ts +1 -0
  41. package/dist/css-components/cta-figure-aspect.js +123 -0
  42. package/dist/css-components/document-navigation.d.ts +13 -0
  43. package/dist/css-components/document-navigation.js +400 -0
  44. package/dist/css-components/editorial-content.d.ts +6 -0
  45. package/dist/css-components/editorial-content.js +52 -0
  46. package/dist/css-components/icon.d.ts +1 -0
  47. package/dist/css-components/icon.js +75 -0
  48. package/dist/css-components/interactive-feedback.d.ts +2 -0
  49. package/dist/css-components/interactive-feedback.js +230 -0
  50. package/dist/css-components/interactive-tables.d.ts +7 -0
  51. package/dist/css-components/interactive-tables.js +164 -0
  52. package/dist/css-components/linked-logo-site-layout.d.ts +21 -0
  53. package/dist/css-components/linked-logo-site-layout.js +214 -0
  54. package/dist/css-components/list-tree.d.ts +5 -0
  55. package/dist/css-components/list-tree.js +98 -0
  56. package/dist/css-components/list.d.ts +3 -0
  57. package/dist/css-components/list.js +119 -0
  58. package/dist/css-components/logo-media.d.ts +16 -0
  59. package/dist/css-components/logo-media.js +219 -0
  60. package/dist/css-components/navigation-layout.d.ts +5 -0
  61. package/dist/css-components/navigation-layout.js +151 -0
  62. package/dist/css-components/panel.d.ts +6 -0
  63. package/dist/css-components/panel.js +148 -0
  64. package/dist/css-components/search-box-and-filter.d.ts +9 -0
  65. package/dist/css-components/search-box-and-filter.js +267 -0
  66. package/dist/css-components/sites-editorial-ports.d.ts +19 -0
  67. package/dist/css-components/sites-editorial-ports.js +264 -0
  68. package/dist/css-components/sites-foundation.d.ts +17 -0
  69. package/dist/css-components/sites-foundation.js +173 -0
  70. package/dist/css-components/sites-rich-lists.d.ts +18 -0
  71. package/dist/css-components/sites-rich-lists.js +364 -0
  72. package/dist/css-components/static-content-ports.d.ts +14 -0
  73. package/dist/css-components/static-content-ports.js +206 -0
  74. package/dist/css-components/tab-section.d.ts +19 -0
  75. package/dist/css-components/tab-section.js +103 -0
  76. package/dist/css-components/table.d.ts +7 -0
  77. package/dist/css-components/table.js +79 -0
  78. package/dist/css-components/tabs-choice-breadcrumbs.d.ts +9 -0
  79. package/dist/css-components/tabs-choice-breadcrumbs.js +307 -0
  80. package/dist/css-components/tiered-list-equal-height-row.d.ts +1 -0
  81. package/dist/css-components/tiered-list-equal-height-row.js +286 -0
  82. package/dist/css-components.d.ts +2 -0
  83. package/dist/css-components.js +2701 -0
  84. package/dist/css-grid.d.ts +1 -0
  85. package/dist/css-grid.js +179 -0
  86. package/dist/css.d.ts +6 -0
  87. package/dist/css.js +431 -0
  88. package/dist/experiments/ibm-plex-engine-smoke/styles.css +8969 -0
  89. package/dist/experiments/ibm-plex-engine-smoke/surfaces.json +604 -0
  90. package/dist/experiments/ibm-plex-engine-smoke/tokens.json +217 -0
  91. package/dist/in-page-navigation.d.ts +7 -0
  92. package/dist/in-page-navigation.js +136 -0
  93. package/dist/index.d.ts +37 -0
  94. package/dist/index.js +18 -0
  95. package/dist/interactive-feedback.d.ts +14 -0
  96. package/dist/interactive-feedback.js +96 -0
  97. package/dist/interactive-tables.d.ts +28 -0
  98. package/dist/interactive-tables.js +184 -0
  99. package/dist/list-tree.d.ts +4 -0
  100. package/dist/list-tree.js +44 -0
  101. package/dist/panel-drawer.d.ts +4 -0
  102. package/dist/panel-drawer.js +171 -0
  103. package/dist/presets/app-tier/styles.css +9459 -0
  104. package/dist/presets/app-tier/surfaces.json +1225 -0
  105. package/dist/presets/app-tier/tokens.json +221 -0
  106. package/dist/presets/prose/styles.css +9442 -0
  107. package/dist/presets/prose/surfaces.json +1225 -0
  108. package/dist/presets/prose/tokens.json +221 -0
  109. package/dist/presets.d.ts +6 -0
  110. package/dist/presets.js +27 -0
  111. package/dist/range-controls.d.ts +9 -0
  112. package/dist/range-controls.js +73 -0
  113. package/dist/resizable-aside.d.ts +4 -0
  114. package/dist/resizable-aside.js +328 -0
  115. package/dist/side-navigation.d.ts +4 -0
  116. package/dist/side-navigation.js +219 -0
  117. package/dist/styles.css +9442 -0
  118. package/dist/surfaces.json +1225 -0
  119. package/dist/tabs.d.ts +4 -0
  120. package/dist/tabs.js +88 -0
  121. package/dist/tier-registry.d.ts +10 -0
  122. package/dist/tier-registry.js +25 -0
  123. package/dist/tiers/app/styles.css +9459 -0
  124. package/dist/tiers/app/surfaces.json +1225 -0
  125. package/dist/tiers/app/tokens.json +221 -0
  126. package/dist/tiers/documentation/styles.css +9442 -0
  127. package/dist/tiers/documentation/surfaces.json +1225 -0
  128. package/dist/tiers/documentation/tokens.json +221 -0
  129. package/dist/tiers/editorial/styles.css +9442 -0
  130. package/dist/tiers/editorial/surfaces.json +1225 -0
  131. package/dist/tiers/editorial/tokens.json +221 -0
  132. package/dist/tiers/os/styles.css +9442 -0
  133. package/dist/tiers/os/surfaces.json +1225 -0
  134. package/dist/tiers/os/tokens.json +221 -0
  135. package/dist/tokens.json +221 -0
  136. package/dist/tooltip.d.ts +4 -0
  137. package/dist/tooltip.js +70 -0
  138. package/dist/top-navigation.d.ts +4 -0
  139. package/dist/top-navigation.js +354 -0
  140. package/dist/types.d.ts +155 -0
  141. package/dist/types.js +1 -0
  142. package/dist/vanilla-theme-colors.d.ts +5 -0
  143. package/dist/vanilla-theme-colors.js +208 -0
  144. package/docs/publishing.md +66 -0
  145. package/package.json +108 -0
@@ -0,0 +1,221 @@
1
+ {
2
+ "baselineUnit": "0.5rem",
3
+ "fontFiles": [
4
+ {
5
+ "family": "ubuntu-sans",
6
+ "path": "../../../assets/fonts/UbuntuSans[wdth,wght].ttf",
7
+ "cssFamily": "Ubuntu Sans",
8
+ "fontStyle": "normal",
9
+ "fontWeight": "100 800",
10
+ "fontStretch": "75% 100%",
11
+ "fontDisplay": "swap",
12
+ "emitFontFace": false
13
+ }
14
+ ],
15
+ "fontStacks": {
16
+ "ubuntu-sans": "\"Ubuntu Sans\", \"Segoe UI\", system-ui, sans-serif"
17
+ },
18
+ "roles": {
19
+ "body": {
20
+ "fontSize": "1rem",
21
+ "lineHeight": "1.5rem",
22
+ "fontFamily": "ubuntu-sans",
23
+ "fontWeight": 400,
24
+ "fontStyle": "normal",
25
+ "spaceAfter": "1rem",
26
+ "nudgeTop": "0.41rem",
27
+ "identifier": "body",
28
+ "fontStack": "\"Ubuntu Sans\", \"Segoe UI\", system-ui, sans-serif",
29
+ "marginBottom": "0.09rem"
30
+ },
31
+ "h1": {
32
+ "fontSize": "2.625rem",
33
+ "lineHeight": "3rem",
34
+ "fontFamily": "ubuntu-sans",
35
+ "fontWeight": 500,
36
+ "fontStyle": "normal",
37
+ "spaceAfter": "1rem",
38
+ "nudgeTop": "0.06881rem",
39
+ "identifier": "h1",
40
+ "fontStack": "\"Ubuntu Sans\", \"Segoe UI\", system-ui, sans-serif",
41
+ "marginBottom": "0.43119rem"
42
+ },
43
+ "h2": {
44
+ "fontSize": "2.625rem",
45
+ "lineHeight": "3rem",
46
+ "fontFamily": "ubuntu-sans",
47
+ "fontWeight": 200,
48
+ "fontStyle": "normal",
49
+ "spaceAfter": "1rem",
50
+ "nudgeTop": "0.06881rem",
51
+ "identifier": "h2",
52
+ "fontStack": "\"Ubuntu Sans\", \"Segoe UI\", system-ui, sans-serif",
53
+ "marginBottom": "0.43119rem"
54
+ },
55
+ "h3": {
56
+ "fontSize": "1.5rem",
57
+ "lineHeight": "2rem",
58
+ "fontFamily": "ubuntu-sans",
59
+ "fontWeight": 500,
60
+ "fontStyle": "normal",
61
+ "spaceAfter": "1rem",
62
+ "nudgeTop": "0.46917rem",
63
+ "identifier": "h3",
64
+ "fontStack": "\"Ubuntu Sans\", \"Segoe UI\", system-ui, sans-serif",
65
+ "marginBottom": "0.03083rem"
66
+ },
67
+ "h4": {
68
+ "fontSize": "1.5rem",
69
+ "lineHeight": "2rem",
70
+ "fontFamily": "ubuntu-sans",
71
+ "fontWeight": 300,
72
+ "fontStyle": "normal",
73
+ "spaceAfter": "1rem",
74
+ "nudgeTop": "0.46917rem",
75
+ "identifier": "h4",
76
+ "fontStack": "\"Ubuntu Sans\", \"Segoe UI\", system-ui, sans-serif",
77
+ "marginBottom": "0.03083rem"
78
+ },
79
+ "h5": {
80
+ "fontSize": "1rem",
81
+ "lineHeight": "1.5rem",
82
+ "fontFamily": "ubuntu-sans",
83
+ "fontWeight": 550,
84
+ "fontStyle": "normal",
85
+ "spaceAfter": "1rem",
86
+ "nudgeTop": "0.41rem",
87
+ "identifier": "h5",
88
+ "fontStack": "\"Ubuntu Sans\", \"Segoe UI\", system-ui, sans-serif",
89
+ "marginBottom": "0.09rem",
90
+ "fontVariantCaps": "all-small-caps",
91
+ "letterSpacing": "0.05em"
92
+ },
93
+ "h6": {
94
+ "fontSize": "1rem",
95
+ "lineHeight": "1.5rem",
96
+ "fontFamily": "ubuntu-sans",
97
+ "fontWeight": 550,
98
+ "fontStyle": "normal",
99
+ "spaceAfter": "1rem",
100
+ "nudgeTop": "0.41rem",
101
+ "identifier": "h6",
102
+ "fontStack": "\"Ubuntu Sans\", \"Segoe UI\", system-ui, sans-serif",
103
+ "marginBottom": "0.09rem"
104
+ }
105
+ },
106
+ "elements": {
107
+ "body": {
108
+ "fontSize": "1rem",
109
+ "lineHeight": "1.5rem",
110
+ "fontFamily": "ubuntu-sans",
111
+ "fontWeight": 400,
112
+ "fontStyle": "normal",
113
+ "spaceAfter": "1rem",
114
+ "nudgeTop": "0.41rem",
115
+ "identifier": "body",
116
+ "fontStack": "\"Ubuntu Sans\", \"Segoe UI\", system-ui, sans-serif",
117
+ "marginBottom": "0.09rem"
118
+ },
119
+ "h6": {
120
+ "fontSize": "1rem",
121
+ "lineHeight": "1.5rem",
122
+ "fontFamily": "ubuntu-sans",
123
+ "fontWeight": 550,
124
+ "fontStyle": "normal",
125
+ "spaceAfter": "1rem",
126
+ "nudgeTop": "0.41rem",
127
+ "identifier": "h6",
128
+ "fontStack": "\"Ubuntu Sans\", \"Segoe UI\", system-ui, sans-serif",
129
+ "marginBottom": "0.09rem"
130
+ },
131
+ "h5": {
132
+ "fontSize": "1rem",
133
+ "lineHeight": "1.5rem",
134
+ "fontFamily": "ubuntu-sans",
135
+ "fontWeight": 550,
136
+ "fontStyle": "normal",
137
+ "spaceAfter": "1rem",
138
+ "nudgeTop": "0.41rem",
139
+ "identifier": "h5",
140
+ "fontStack": "\"Ubuntu Sans\", \"Segoe UI\", system-ui, sans-serif",
141
+ "marginBottom": "0.09rem",
142
+ "fontVariantCaps": "all-small-caps",
143
+ "letterSpacing": "0.05em"
144
+ },
145
+ "h4": {
146
+ "fontSize": "1.5rem",
147
+ "lineHeight": "2rem",
148
+ "fontFamily": "ubuntu-sans",
149
+ "fontWeight": 300,
150
+ "fontStyle": "normal",
151
+ "spaceAfter": "1rem",
152
+ "nudgeTop": "0.46917rem",
153
+ "identifier": "h4",
154
+ "fontStack": "\"Ubuntu Sans\", \"Segoe UI\", system-ui, sans-serif",
155
+ "marginBottom": "0.03083rem"
156
+ },
157
+ "h3": {
158
+ "fontSize": "1.5rem",
159
+ "lineHeight": "2rem",
160
+ "fontFamily": "ubuntu-sans",
161
+ "fontWeight": 500,
162
+ "fontStyle": "normal",
163
+ "spaceAfter": "1rem",
164
+ "nudgeTop": "0.46917rem",
165
+ "identifier": "h3",
166
+ "fontStack": "\"Ubuntu Sans\", \"Segoe UI\", system-ui, sans-serif",
167
+ "marginBottom": "0.03083rem"
168
+ },
169
+ "h2": {
170
+ "fontSize": "2.625rem",
171
+ "lineHeight": "3rem",
172
+ "fontFamily": "ubuntu-sans",
173
+ "fontWeight": 200,
174
+ "fontStyle": "normal",
175
+ "spaceAfter": "1rem",
176
+ "nudgeTop": "0.06881rem",
177
+ "identifier": "h2",
178
+ "fontStack": "\"Ubuntu Sans\", \"Segoe UI\", system-ui, sans-serif",
179
+ "marginBottom": "0.43119rem"
180
+ },
181
+ "h1": {
182
+ "fontSize": "2.625rem",
183
+ "lineHeight": "3rem",
184
+ "fontFamily": "ubuntu-sans",
185
+ "fontWeight": 500,
186
+ "fontStyle": "normal",
187
+ "spaceAfter": "1rem",
188
+ "nudgeTop": "0.06881rem",
189
+ "identifier": "h1",
190
+ "fontStack": "\"Ubuntu Sans\", \"Segoe UI\", system-ui, sans-serif",
191
+ "marginBottom": "0.43119rem"
192
+ }
193
+ },
194
+ "layout": {
195
+ "contentMaxWidth": "90rem",
196
+ "contentPaddingInline": "1rem",
197
+ "measure": "40rem",
198
+ "sectionSpace": "4rem",
199
+ "sectionSpaceShallow": "1.5rem",
200
+ "sectionSpaceDeep": "8rem",
201
+ "stripSpace": "4rem",
202
+ "gridGapInline": "1rem",
203
+ "gridGapBlock": "1rem",
204
+ "pageMargin": "1rem"
205
+ },
206
+ "components": {
207
+ "borderWidth": "1px",
208
+ "barThickness": "0.1875rem",
209
+ "radius": "0rem",
210
+ "controlBlockPadding": "0.5rem",
211
+ "controlCompactBlockPadding": "0.25rem",
212
+ "controlInlinePadding": "1rem",
213
+ "controlInlinePaddingAction": "1rem",
214
+ "controlInlinePaddingField": "0.5rem",
215
+ "controlVisualSize": "1rem",
216
+ "fieldGap": "0.5rem",
217
+ "panelPaddingInline": "1rem",
218
+ "panelPaddingBlock": "1rem",
219
+ "accordionIndent": "1.5rem"
220
+ }
221
+ }
@@ -0,0 +1,6 @@
1
+ import { type BuiltInThemeName, type PresetName, type TierName } from "./tier-registry.js";
2
+ export { isPresetName, isTierName, normalizeBuiltInThemeName, presetDescriptions, presetNames, tierDescriptions, tierNames } from "./tier-registry.js";
3
+ export type { BuiltInThemeName, PresetName, TierName } from "./tier-registry.js";
4
+ export declare function resolveTierPath(name: TierName): string;
5
+ export declare function resolvePresetPath(name: PresetName): string;
6
+ export declare function resolveBuiltInThemePath(name: BuiltInThemeName): string;
@@ -0,0 +1,27 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import { isTierName } from "./tier-registry.js";
4
+ export { isPresetName, isTierName, normalizeBuiltInThemeName, presetDescriptions, presetNames, tierDescriptions, tierNames } from "./tier-registry.js";
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ const tierConfigPaths = {
7
+ editorial: path.resolve(__dirname, "..", "config", "tiers", "editorial.json"),
8
+ documentation: path.resolve(__dirname, "..", "config", "tiers", "documentation.json"),
9
+ app: path.resolve(__dirname, "..", "config", "tiers", "app.json"),
10
+ os: path.resolve(__dirname, "..", "config", "tiers", "os.json")
11
+ };
12
+ const presetConfigPaths = {
13
+ prose: tierConfigPaths.editorial,
14
+ "app-tier": tierConfigPaths.app
15
+ };
16
+ export function resolveTierPath(name) {
17
+ return tierConfigPaths[name];
18
+ }
19
+ export function resolvePresetPath(name) {
20
+ return presetConfigPaths[name];
21
+ }
22
+ export function resolveBuiltInThemePath(name) {
23
+ if (isTierName(name)) {
24
+ return resolveTierPath(name);
25
+ }
26
+ return resolvePresetPath(name);
27
+ }
@@ -0,0 +1,9 @@
1
+ export interface RangeControlsInitOptions {
2
+ root?: ParentNode;
3
+ rangeSelector?: string;
4
+ numberSelector?: string;
5
+ wrapperSelector?: string;
6
+ }
7
+ export declare function updateRangeFill(range: HTMLInputElement): void;
8
+ export declare function setupRangeControl(range: HTMLInputElement, options?: RangeControlsInitOptions): void;
9
+ export declare function initRangeControls(options?: RangeControlsInitOptions): void;
@@ -0,0 +1,73 @@
1
+ const DEFAULT_RANGE_SELECTOR = "input[type='range']";
2
+ const DEFAULT_NUMBER_SELECTOR = ".bf-slider-input";
3
+ const DEFAULT_WRAPPER_SELECTOR = ".bf-slider";
4
+ function clamp(value, min, max) {
5
+ return Math.min(Math.max(value, min), max);
6
+ }
7
+ function getBounds(input) {
8
+ const min = Number(input.min || 0);
9
+ const max = Number(input.max || 100);
10
+ if (Number.isNaN(min) || Number.isNaN(max) || min === max) {
11
+ return { min: 0, max: 100 };
12
+ }
13
+ return { min, max };
14
+ }
15
+ export function updateRangeFill(range) {
16
+ const { min, max } = getBounds(range);
17
+ const value = Number(range.value || min);
18
+ const percent = ((clamp(value, min, max) - min) / (max - min)) * 100;
19
+ range.style.setProperty("--bf-slider-fill-percent", `${percent}%`);
20
+ }
21
+ function findPairedNumberInput(range, options) {
22
+ const wrapperSelector = options.wrapperSelector ?? DEFAULT_WRAPPER_SELECTOR;
23
+ const numberSelector = options.numberSelector ?? DEFAULT_NUMBER_SELECTOR;
24
+ const wrapper = range.closest(wrapperSelector);
25
+ if (!wrapper) {
26
+ return null;
27
+ }
28
+ return wrapper.querySelector(numberSelector);
29
+ }
30
+ export function setupRangeControl(range, options = {}) {
31
+ if (range.dataset.bfRangeInitialized === "true") {
32
+ updateRangeFill(range);
33
+ return;
34
+ }
35
+ const pairedNumberInput = findPairedNumberInput(range, options);
36
+ updateRangeFill(range);
37
+ if (pairedNumberInput) {
38
+ pairedNumberInput.value = range.value;
39
+ const syncFromNumberInput = () => {
40
+ const { min, max } = getBounds(range);
41
+ const numericValue = Number(pairedNumberInput.value || range.value);
42
+ const nextValue = clamp(Number.isNaN(numericValue) ? Number(range.value) : numericValue, min, max);
43
+ range.value = String(nextValue);
44
+ pairedNumberInput.value = String(nextValue);
45
+ updateRangeFill(range);
46
+ };
47
+ pairedNumberInput.addEventListener("input", syncFromNumberInput);
48
+ pairedNumberInput.addEventListener("change", syncFromNumberInput);
49
+ }
50
+ range.addEventListener("input", () => {
51
+ updateRangeFill(range);
52
+ if (pairedNumberInput) {
53
+ pairedNumberInput.value = range.value;
54
+ }
55
+ });
56
+ range.addEventListener("change", () => {
57
+ updateRangeFill(range);
58
+ if (pairedNumberInput) {
59
+ pairedNumberInput.value = range.value;
60
+ }
61
+ });
62
+ range.dataset.bfRangeInitialized = "true";
63
+ }
64
+ export function initRangeControls(options = {}) {
65
+ const root = options.root ?? document;
66
+ const rangeSelector = options.rangeSelector ?? DEFAULT_RANGE_SELECTOR;
67
+ const ranges = root.querySelectorAll(rangeSelector);
68
+ ranges.forEach(range => {
69
+ if (range.type === "range") {
70
+ setupRangeControl(range, options);
71
+ }
72
+ });
73
+ }
@@ -0,0 +1,4 @@
1
+ export interface ResizableAsideInitOptions {
2
+ root?: ParentNode;
3
+ }
4
+ export declare function initResizableAsides(options?: ResizableAsideInitOptions): () => void;
@@ -0,0 +1,328 @@
1
+ const APPLICATION_SELECTOR = ".bf-application";
2
+ const PINNED_ASIDE_SELECTOR = ".bf-aside.is-pinned";
3
+ const HANDLE_SELECTOR = ".bf-application-aside-resize-handle";
4
+ const DEFAULT_STEP_PX = 16;
5
+ const DEFAULT_STORAGE_PREFIX = "baseline-foundry-aside-width";
6
+ const RESIZING_CLASS = "is-resizing-aside";
7
+ function queryAllWithinRoot(root, selector) {
8
+ const elements = Array.from(root.querySelectorAll(selector));
9
+ if (root instanceof Element && root.matches(selector)) {
10
+ elements.unshift(root);
11
+ }
12
+ return elements;
13
+ }
14
+ function getApplications(root) {
15
+ return queryAllWithinRoot(root, APPLICATION_SELECTOR);
16
+ }
17
+ function resolveCssLengthPx(context, cssValue, fallbackPx) {
18
+ const trimmedValue = cssValue.trim();
19
+ if (!trimmedValue) {
20
+ return fallbackPx;
21
+ }
22
+ const probe = document.createElement("div");
23
+ probe.style.border = "0";
24
+ probe.style.inlineSize = trimmedValue;
25
+ probe.style.margin = "0";
26
+ probe.style.opacity = "0";
27
+ probe.style.padding = "0";
28
+ probe.style.pointerEvents = "none";
29
+ probe.style.position = "absolute";
30
+ probe.style.visibility = "hidden";
31
+ context.appendChild(probe);
32
+ const resolvedPx = probe.getBoundingClientRect().width;
33
+ probe.remove();
34
+ return Number.isFinite(resolvedPx) && resolvedPx > 0 ? resolvedPx : fallbackPx;
35
+ }
36
+ function resolveNumericDatasetValue(...values) {
37
+ for (const value of values) {
38
+ if (!value) {
39
+ continue;
40
+ }
41
+ const parsed = Number.parseFloat(value);
42
+ if (Number.isFinite(parsed) && parsed > 0) {
43
+ return parsed;
44
+ }
45
+ }
46
+ return null;
47
+ }
48
+ function getPinnedAside(application) {
49
+ return Array.from(application.children).find((child) => {
50
+ return child instanceof HTMLElement && child.matches(PINNED_ASIDE_SELECTOR);
51
+ }) ?? application.querySelector(PINNED_ASIDE_SELECTOR);
52
+ }
53
+ function getResizeHandle(application, aside) {
54
+ return aside?.querySelector(HANDLE_SELECTOR)
55
+ ?? application.querySelector(HANDLE_SELECTOR)
56
+ ?? null;
57
+ }
58
+ function isPinnedResizableAside(aside) {
59
+ return Boolean(aside
60
+ && aside.classList.contains("is-pinned")
61
+ && !aside.classList.contains("is-overlay")
62
+ && !aside.classList.contains("is-drawer")
63
+ && !aside.classList.contains("is-collapsed"));
64
+ }
65
+ function getStorageKey(application, aside, handle) {
66
+ const configuredKey = handle.dataset.asideResizeStorageKey
67
+ ?? aside.dataset.asideResizeStorageKey
68
+ ?? application.dataset.asideResizeStorageKey;
69
+ if (configuredKey === "") {
70
+ return null;
71
+ }
72
+ if (configuredKey) {
73
+ return configuredKey;
74
+ }
75
+ const identity = aside.id || application.id;
76
+ if (!identity) {
77
+ return null;
78
+ }
79
+ return `${DEFAULT_STORAGE_PREFIX}:${location.pathname}:${identity}`;
80
+ }
81
+ function getStepPx(application, aside, handle) {
82
+ return resolveNumericDatasetValue(handle.dataset.asideResizeStep, aside.dataset.asideResizeStep, application.dataset.asideResizeStep) ?? DEFAULT_STEP_PX;
83
+ }
84
+ function getWidthBounds(application, aside, handle) {
85
+ const computedStyle = getComputedStyle(application);
86
+ const minFromDataset = handle.dataset.asideResizeMin
87
+ ?? aside.dataset.asideResizeMin
88
+ ?? application.dataset.asideResizeMin;
89
+ const maxFromDataset = handle.dataset.asideResizeMax
90
+ ?? aside.dataset.asideResizeMax
91
+ ?? application.dataset.asideResizeMax;
92
+ const minValue = minFromDataset
93
+ ?? computedStyle.getPropertyValue("--bf-app-aside-width-min")
94
+ ?? computedStyle.getPropertyValue("--bf-application-aside-width-min")
95
+ ?? computedStyle.getPropertyValue("--bf-app-drawer-width-small")
96
+ ?? computedStyle.getPropertyValue("--bf-application-drawer-width-small");
97
+ const maxValue = maxFromDataset
98
+ ?? computedStyle.getPropertyValue("--bf-app-aside-width-max")
99
+ ?? computedStyle.getPropertyValue("--bf-application-aside-width-max")
100
+ ?? computedStyle.getPropertyValue("--bf-app-drawer-width-medium-max")
101
+ ?? computedStyle.getPropertyValue("--bf-application-drawer-width-medium-max");
102
+ const minPx = resolveCssLengthPx(application, minValue, 240);
103
+ const maxPx = resolveCssLengthPx(application, maxValue, 640);
104
+ return {
105
+ minPx,
106
+ maxPx: Math.max(minPx, maxPx)
107
+ };
108
+ }
109
+ function getCurrentWidthPx(application, aside) {
110
+ const measuredWidth = aside.getBoundingClientRect().width;
111
+ if (measuredWidth > 0) {
112
+ return measuredWidth;
113
+ }
114
+ const computedStyle = getComputedStyle(application);
115
+ return resolveCssLengthPx(application, computedStyle.getPropertyValue("--bf-app-aside-width")
116
+ || computedStyle.getPropertyValue("--bf-application-aside-width"), 465);
117
+ }
118
+ function clamp(value, minPx, maxPx) {
119
+ return Math.max(minPx, Math.min(maxPx, value));
120
+ }
121
+ function setHandleInteractivity(handle, enabled) {
122
+ handle.setAttribute("aria-disabled", String(!enabled));
123
+ handle.tabIndex = enabled ? 0 : -1;
124
+ }
125
+ function updateHandleA11y(application, aside, handle, widthPx = getCurrentWidthPx(application, aside)) {
126
+ if (!handle.hasAttribute("role")) {
127
+ handle.setAttribute("role", "separator");
128
+ }
129
+ if (!handle.hasAttribute("aria-orientation")) {
130
+ handle.setAttribute("aria-orientation", "vertical");
131
+ }
132
+ if (!handle.hasAttribute("aria-label")) {
133
+ handle.setAttribute("aria-label", "Resize panel");
134
+ }
135
+ if (aside.id && !handle.hasAttribute("aria-controls")) {
136
+ handle.setAttribute("aria-controls", aside.id);
137
+ }
138
+ const enabled = isPinnedResizableAside(aside);
139
+ setHandleInteractivity(handle, enabled);
140
+ if (!enabled) {
141
+ return;
142
+ }
143
+ const { minPx, maxPx } = getWidthBounds(application, aside, handle);
144
+ handle.setAttribute("aria-valuemin", String(Math.round(minPx)));
145
+ handle.setAttribute("aria-valuemax", String(Math.round(maxPx)));
146
+ handle.setAttribute("aria-valuenow", String(Math.round(clamp(widthPx, minPx, maxPx))));
147
+ }
148
+ function readPersistedWidth(storageKey) {
149
+ if (!storageKey) {
150
+ return null;
151
+ }
152
+ try {
153
+ const rawWidth = localStorage.getItem(storageKey);
154
+ if (!rawWidth) {
155
+ return null;
156
+ }
157
+ const parsedWidth = Number.parseFloat(rawWidth);
158
+ return Number.isFinite(parsedWidth) ? parsedWidth : null;
159
+ }
160
+ catch {
161
+ return null;
162
+ }
163
+ }
164
+ function writePersistedWidth(storageKey, widthPx) {
165
+ if (!storageKey) {
166
+ return;
167
+ }
168
+ try {
169
+ localStorage.setItem(storageKey, String(Math.round(widthPx)));
170
+ }
171
+ catch {
172
+ // Ignore storage failures so the shell still works in restricted environments.
173
+ }
174
+ }
175
+ function clearPersistedWidth(storageKey) {
176
+ if (!storageKey) {
177
+ return;
178
+ }
179
+ try {
180
+ localStorage.removeItem(storageKey);
181
+ }
182
+ catch {
183
+ // Ignore storage failures so reset still works visually.
184
+ }
185
+ }
186
+ function applyWidth(application, aside, handle, storageKey, widthPx, persist) {
187
+ const { minPx, maxPx } = getWidthBounds(application, aside, handle);
188
+ const nextWidthPx = clamp(widthPx, minPx, maxPx);
189
+ application.style.setProperty("--bf-app-aside-width", `${nextWidthPx}px`);
190
+ application.style.setProperty("--bf-application-aside-width", `${nextWidthPx}px`);
191
+ updateHandleA11y(application, aside, handle, nextWidthPx);
192
+ if (persist) {
193
+ writePersistedWidth(storageKey, nextWidthPx);
194
+ }
195
+ return nextWidthPx;
196
+ }
197
+ function resetWidth(application, aside, handle, storageKey) {
198
+ clearPersistedWidth(storageKey);
199
+ application.style.removeProperty("--bf-app-aside-width");
200
+ application.style.removeProperty("--bf-application-aside-width");
201
+ updateHandleA11y(application, aside, handle);
202
+ }
203
+ function syncHandleToRenderedWidth(application, aside, handle) {
204
+ updateHandleA11y(application, aside, handle, getCurrentWidthPx(application, aside));
205
+ }
206
+ function setupApplication(application) {
207
+ const aside = getPinnedAside(application);
208
+ const handle = getResizeHandle(application, aside);
209
+ if (!aside || !handle) {
210
+ return () => { };
211
+ }
212
+ const storageKey = getStorageKey(application, aside, handle);
213
+ const stepPx = getStepPx(application, aside, handle);
214
+ const persistedWidth = readPersistedWidth(storageKey);
215
+ if (persistedWidth !== null) {
216
+ applyWidth(application, aside, handle, storageKey, persistedWidth, false);
217
+ }
218
+ else {
219
+ updateHandleA11y(application, aside, handle);
220
+ }
221
+ let initialSyncFrameId = 0;
222
+ const onWindowLoad = () => {
223
+ syncHandleToRenderedWidth(application, aside, handle);
224
+ };
225
+ initialSyncFrameId = window.requestAnimationFrame(() => {
226
+ syncHandleToRenderedWidth(application, aside, handle);
227
+ });
228
+ window.addEventListener("load", onWindowLoad, { once: true });
229
+ const onDoubleClick = () => {
230
+ resetWidth(application, aside, handle, storageKey);
231
+ };
232
+ const onKeyDown = (event) => {
233
+ if (!isPinnedResizableAside(aside)) {
234
+ return;
235
+ }
236
+ const currentWidthPx = getCurrentWidthPx(application, aside);
237
+ const { minPx, maxPx } = getWidthBounds(application, aside, handle);
238
+ const adjustedStepPx = event.shiftKey ? stepPx * 3 : stepPx;
239
+ if (event.key === "ArrowLeft") {
240
+ applyWidth(application, aside, handle, storageKey, currentWidthPx + adjustedStepPx, true);
241
+ event.preventDefault();
242
+ return;
243
+ }
244
+ if (event.key === "ArrowRight") {
245
+ applyWidth(application, aside, handle, storageKey, currentWidthPx - adjustedStepPx, true);
246
+ event.preventDefault();
247
+ return;
248
+ }
249
+ if (event.key === "Home") {
250
+ applyWidth(application, aside, handle, storageKey, minPx, true);
251
+ event.preventDefault();
252
+ return;
253
+ }
254
+ if (event.key === "End") {
255
+ applyWidth(application, aside, handle, storageKey, maxPx, true);
256
+ event.preventDefault();
257
+ }
258
+ };
259
+ const onPointerDown = (event) => {
260
+ if (event.button !== 0 || !isPinnedResizableAside(aside)) {
261
+ return;
262
+ }
263
+ event.preventDefault();
264
+ const shellRect = application.getBoundingClientRect();
265
+ application.classList.add(RESIZING_CLASS);
266
+ handle.setPointerCapture(event.pointerId);
267
+ let finished = false;
268
+ const onPointerMove = (moveEvent) => {
269
+ const nextWidthPx = shellRect.right - moveEvent.clientX;
270
+ applyWidth(application, aside, handle, storageKey, nextWidthPx, false);
271
+ };
272
+ const finishResize = () => {
273
+ if (finished) {
274
+ return;
275
+ }
276
+ finished = true;
277
+ application.classList.remove(RESIZING_CLASS);
278
+ applyWidth(application, aside, handle, storageKey, getCurrentWidthPx(application, aside), true);
279
+ handle.removeEventListener("pointermove", onPointerMove);
280
+ handle.removeEventListener("pointerup", finishResize);
281
+ handle.removeEventListener("pointercancel", finishResize);
282
+ handle.removeEventListener("lostpointercapture", finishResize);
283
+ window.removeEventListener("pointermove", onPointerMove);
284
+ window.removeEventListener("pointerup", finishResize);
285
+ window.removeEventListener("pointercancel", finishResize);
286
+ if (handle.hasPointerCapture(event.pointerId)) {
287
+ handle.releasePointerCapture(event.pointerId);
288
+ }
289
+ };
290
+ handle.addEventListener("pointermove", onPointerMove);
291
+ handle.addEventListener("pointerup", finishResize, { once: true });
292
+ handle.addEventListener("pointercancel", finishResize, { once: true });
293
+ handle.addEventListener("lostpointercapture", finishResize, { once: true });
294
+ window.addEventListener("pointermove", onPointerMove);
295
+ window.addEventListener("pointerup", finishResize, { once: true });
296
+ window.addEventListener("pointercancel", finishResize, { once: true });
297
+ };
298
+ const onWindowResize = () => {
299
+ if (isPinnedResizableAside(aside)) {
300
+ applyWidth(application, aside, handle, storageKey, getCurrentWidthPx(application, aside), false);
301
+ return;
302
+ }
303
+ updateHandleA11y(application, aside, handle);
304
+ };
305
+ handle.addEventListener("dblclick", onDoubleClick);
306
+ handle.addEventListener("keydown", onKeyDown);
307
+ handle.addEventListener("pointerdown", onPointerDown);
308
+ window.addEventListener("resize", onWindowResize);
309
+ return () => {
310
+ if (initialSyncFrameId !== 0) {
311
+ window.cancelAnimationFrame(initialSyncFrameId);
312
+ }
313
+ window.removeEventListener("load", onWindowLoad);
314
+ handle.removeEventListener("dblclick", onDoubleClick);
315
+ handle.removeEventListener("keydown", onKeyDown);
316
+ handle.removeEventListener("pointerdown", onPointerDown);
317
+ window.removeEventListener("resize", onWindowResize);
318
+ };
319
+ }
320
+ export function initResizableAsides(options = {}) {
321
+ const root = options.root ?? document;
322
+ const cleanups = getApplications(root).map(setupApplication);
323
+ return () => {
324
+ for (const cleanup of cleanups) {
325
+ cleanup();
326
+ }
327
+ };
328
+ }
@@ -0,0 +1,4 @@
1
+ export interface SideNavigationInitOptions {
2
+ root?: ParentNode;
3
+ }
4
+ export declare function initSideNavigations(options?: SideNavigationInitOptions): () => void;