@boostdev/design-system-components 2.0.0 → 2.2.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.
- package/AGENTS.md +2 -1
- package/README.md +42 -1
- package/dist/client.cjs +305 -60
- package/dist/client.css +577 -523
- package/dist/client.d.cts +42 -2
- package/dist/client.d.ts +42 -2
- package/dist/client.js +311 -60
- package/dist/index.cjs +305 -60
- package/dist/index.css +577 -523
- package/dist/index.d.cts +42 -2
- package/dist/index.d.ts +42 -2
- package/dist/index.js +311 -60
- package/dist/native/index.cjs +5 -2
- package/dist/native/index.d.cts +3 -1
- package/dist/native/index.d.ts +3 -1
- package/dist/native/index.js +5 -2
- package/dist/web-components/{chunk-3REOIRDW.js → chunk-N3TN6WCH.js} +26 -1
- package/dist/web-components/index.d.ts +129 -1
- package/dist/web-components/index.js +312 -1
- package/dist/web-components/interaction/bds-button.d.ts +7 -0
- package/dist/web-components/interaction/bds-button.js +1 -1
- package/package.json +1 -1
- package/src/components/interaction/Button/Button.mdx +81 -36
- package/src/components/interaction/Button/Button.module.css +24 -0
- package/src/components/interaction/Button/Button.native.mdx +31 -12
- package/src/components/interaction/Button/Button.native.spec.tsx +20 -0
- package/src/components/interaction/Button/Button.native.stories.tsx +110 -9
- package/src/components/interaction/Button/Button.native.tsx +13 -4
- package/src/components/interaction/Button/Button.spec.tsx +16 -0
- package/src/components/interaction/Button/Button.stories.tsx +134 -16
- package/src/components/interaction/Button/Button.tsx +4 -0
- package/src/components/layout/Grid/Grid.mdx +244 -0
- package/src/components/layout/Grid/Grid.module.css +59 -0
- package/src/components/layout/Grid/Grid.spec.tsx +401 -0
- package/src/components/layout/Grid/Grid.stories.tsx +160 -0
- package/src/components/layout/Grid/Grid.tsx +85 -0
- package/src/components/layout/Grid/GridItem.tsx +150 -0
- package/src/components/layout/Grid/autoSpan.ts +77 -0
- package/src/components/layout/Grid/index.ts +4 -0
- package/src/components/layout/Grid/masonry.ts +134 -0
- package/src/components/layout/IconWrapper/IconWrapper.stories.tsx +46 -14
- package/src/index.ts +2 -0
- package/src/web-components/index.ts +4 -0
- package/src/web-components/interaction/BdsButton.mdx +46 -14
- package/src/web-components/interaction/BdsButton.stories.tsx +171 -19
- package/src/web-components/interaction/bds-button.spec.ts +35 -0
- package/src/web-components/interaction/bds-button.ts +28 -1
- package/src/web-components/layout/BdsGrid.mdx +210 -0
- package/src/web-components/layout/BdsGrid.stories.tsx +209 -0
- package/src/web-components/layout/BdsGridItem.mdx +52 -0
- package/src/web-components/layout/BdsGridItem.stories.tsx +72 -0
- package/src/web-components/layout/bds-grid-item.spec.ts +102 -0
- package/src/web-components/layout/bds-grid-item.ts +177 -0
- package/src/web-components/layout/bds-grid.spec.ts +62 -0
- package/src/web-components/layout/bds-grid.ts +184 -0
package/dist/native/index.cjs
CHANGED
|
@@ -2139,6 +2139,7 @@ function Button({
|
|
|
2139
2139
|
iconStart,
|
|
2140
2140
|
iconEnd,
|
|
2141
2141
|
disabled = false,
|
|
2142
|
+
isIconOnly = false,
|
|
2142
2143
|
onPress,
|
|
2143
2144
|
accessibilityLabel,
|
|
2144
2145
|
style
|
|
@@ -2151,9 +2152,11 @@ function Button({
|
|
|
2151
2152
|
const isOutline = variant === "outline" || variant === "ghost";
|
|
2152
2153
|
const containerStyle = {
|
|
2153
2154
|
height,
|
|
2154
|
-
paddingHorizontal: px,
|
|
2155
|
+
paddingHorizontal: isIconOnly ? import_native.spacing.xs : px,
|
|
2155
2156
|
borderRadius: radius,
|
|
2156
2157
|
opacity: disabled ? 0.4 : 1,
|
|
2158
|
+
// Icon-only: square with equal xs padding on both axes.
|
|
2159
|
+
...isIconOnly && { aspectRatio: 1, paddingVertical: import_native.spacing.xs },
|
|
2157
2160
|
...variant === "default" && { backgroundColor: primaryColor },
|
|
2158
2161
|
...isOutline && {
|
|
2159
2162
|
backgroundColor: "transparent",
|
|
@@ -2173,7 +2176,7 @@ function Button({
|
|
|
2173
2176
|
style: ({ pressed }) => [styles24.pressable, containerStyle, pressed && { opacity: 0.75 }, style],
|
|
2174
2177
|
children: [
|
|
2175
2178
|
iconStart && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_native27.View, { importantForAccessibility: "no-hide-descendants", accessibilityElementsHidden: true, children: iconStart }),
|
|
2176
|
-
children && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_native27.Text, { style: [styles24.label, { fontSize, color: textColor }], children }),
|
|
2179
|
+
children && (isIconOnly ? children : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_native27.Text, { style: [styles24.label, { fontSize, color: textColor }], children })),
|
|
2177
2180
|
iconEnd && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_native27.View, { importantForAccessibility: "no-hide-descendants", children: iconEnd })
|
|
2178
2181
|
]
|
|
2179
2182
|
}
|
package/dist/native/index.d.cts
CHANGED
|
@@ -293,11 +293,13 @@ interface ButtonProps {
|
|
|
293
293
|
iconStart?: ReactNode;
|
|
294
294
|
iconEnd?: ReactNode;
|
|
295
295
|
disabled?: boolean;
|
|
296
|
+
/** Forces a 1:1 aspect ratio with minimal padding for icon-only buttons. Requires `accessibilityLabel`. */
|
|
297
|
+
isIconOnly?: boolean;
|
|
296
298
|
onPress?: () => void;
|
|
297
299
|
accessibilityLabel?: string;
|
|
298
300
|
style?: StyleProp<ViewStyle>;
|
|
299
301
|
}
|
|
300
|
-
declare function Button({ children, variant, size, iconStart, iconEnd, disabled, onPress, accessibilityLabel, style, }: Readonly<ButtonProps>): react_jsx_runtime.JSX.Element;
|
|
302
|
+
declare function Button({ children, variant, size, iconStart, iconEnd, disabled, isIconOnly, onPress, accessibilityLabel, style, }: Readonly<ButtonProps>): react_jsx_runtime.JSX.Element;
|
|
301
303
|
|
|
302
304
|
interface RatingProps {
|
|
303
305
|
value: number;
|
package/dist/native/index.d.ts
CHANGED
|
@@ -293,11 +293,13 @@ interface ButtonProps {
|
|
|
293
293
|
iconStart?: ReactNode;
|
|
294
294
|
iconEnd?: ReactNode;
|
|
295
295
|
disabled?: boolean;
|
|
296
|
+
/** Forces a 1:1 aspect ratio with minimal padding for icon-only buttons. Requires `accessibilityLabel`. */
|
|
297
|
+
isIconOnly?: boolean;
|
|
296
298
|
onPress?: () => void;
|
|
297
299
|
accessibilityLabel?: string;
|
|
298
300
|
style?: StyleProp<ViewStyle>;
|
|
299
301
|
}
|
|
300
|
-
declare function Button({ children, variant, size, iconStart, iconEnd, disabled, onPress, accessibilityLabel, style, }: Readonly<ButtonProps>): react_jsx_runtime.JSX.Element;
|
|
302
|
+
declare function Button({ children, variant, size, iconStart, iconEnd, disabled, isIconOnly, onPress, accessibilityLabel, style, }: Readonly<ButtonProps>): react_jsx_runtime.JSX.Element;
|
|
301
303
|
|
|
302
304
|
interface RatingProps {
|
|
303
305
|
value: number;
|
package/dist/native/index.js
CHANGED
|
@@ -2087,6 +2087,7 @@ function Button({
|
|
|
2087
2087
|
iconStart,
|
|
2088
2088
|
iconEnd,
|
|
2089
2089
|
disabled = false,
|
|
2090
|
+
isIconOnly = false,
|
|
2090
2091
|
onPress,
|
|
2091
2092
|
accessibilityLabel,
|
|
2092
2093
|
style
|
|
@@ -2099,9 +2100,11 @@ function Button({
|
|
|
2099
2100
|
const isOutline = variant === "outline" || variant === "ghost";
|
|
2100
2101
|
const containerStyle = {
|
|
2101
2102
|
height,
|
|
2102
|
-
paddingHorizontal: px,
|
|
2103
|
+
paddingHorizontal: isIconOnly ? spacing.xs : px,
|
|
2103
2104
|
borderRadius: radius,
|
|
2104
2105
|
opacity: disabled ? 0.4 : 1,
|
|
2106
|
+
// Icon-only: square with equal xs padding on both axes.
|
|
2107
|
+
...isIconOnly && { aspectRatio: 1, paddingVertical: spacing.xs },
|
|
2105
2108
|
...variant === "default" && { backgroundColor: primaryColor },
|
|
2106
2109
|
...isOutline && {
|
|
2107
2110
|
backgroundColor: "transparent",
|
|
@@ -2121,7 +2124,7 @@ function Button({
|
|
|
2121
2124
|
style: ({ pressed }) => [styles24.pressable, containerStyle, pressed && { opacity: 0.75 }, style],
|
|
2122
2125
|
children: [
|
|
2123
2126
|
iconStart && /* @__PURE__ */ jsx27(View22, { importantForAccessibility: "no-hide-descendants", accessibilityElementsHidden: true, children: iconStart }),
|
|
2124
|
-
children && /* @__PURE__ */ jsx27(Text20, { style: [styles24.label, { fontSize, color: textColor }], children }),
|
|
2127
|
+
children && (isIconOnly ? children : /* @__PURE__ */ jsx27(Text20, { style: [styles24.label, { fontSize, color: textColor }], children })),
|
|
2125
2128
|
iconEnd && /* @__PURE__ */ jsx27(View22, { importantForAccessibility: "no-hide-descendants", children: iconEnd })
|
|
2126
2129
|
]
|
|
2127
2130
|
}
|
|
@@ -90,6 +90,23 @@ var BdsButton = class extends i2 {
|
|
|
90
90
|
animation: pulse 3s infinite;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/* Icon-only: square (1:1), equal xs padding on both axes — composes with
|
|
94
|
+
any variant/size. box-sizing: border-box keeps the outer box at
|
|
95
|
+
--button_height; without it, all: unset reverts to content-box and
|
|
96
|
+
padding-block would stretch the button past its declared height.
|
|
97
|
+
Slotted child fills the content area (button height minus the two
|
|
98
|
+
padding sides) so SVG icons scale with button size. */
|
|
99
|
+
.button.is-icon-only {
|
|
100
|
+
aspect-ratio: 1;
|
|
101
|
+
box-sizing: border-box;
|
|
102
|
+
padding: var(--bds-space_xs);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
:host([is-icon-only]) ::slotted(*) {
|
|
106
|
+
inline-size: 100%;
|
|
107
|
+
block-size: 100%;
|
|
108
|
+
}
|
|
109
|
+
|
|
93
110
|
/* SVG icon colour */
|
|
94
111
|
.button svg {
|
|
95
112
|
--icon__stroke: currentcolor;
|
|
@@ -117,6 +134,12 @@ var BdsButton = class extends i2 {
|
|
|
117
134
|
transition: var(--bds-animation_transition);
|
|
118
135
|
}
|
|
119
136
|
|
|
137
|
+
/* Icon-only: zero out icon slot margins so icon centres */
|
|
138
|
+
.button.is-icon-only .icon-start.has-content,
|
|
139
|
+
.button.is-icon-only .icon-end.has-content {
|
|
140
|
+
margin-inline: 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
120
143
|
/* Hover icon animation */
|
|
121
144
|
@media (hover: hover) and (pointer: fine) {
|
|
122
145
|
.button:hover .icon-start svg,
|
|
@@ -164,6 +187,7 @@ var BdsButton = class extends i2 {
|
|
|
164
187
|
target: { type: String },
|
|
165
188
|
rel: { type: String },
|
|
166
189
|
hasPulse: { type: Boolean, attribute: "has-pulse", reflect: true },
|
|
190
|
+
isIconOnly: { type: Boolean, attribute: "is-icon-only", reflect: true },
|
|
167
191
|
type: { type: String },
|
|
168
192
|
ariaLabel: { type: String, attribute: "aria-label" },
|
|
169
193
|
// Internal state
|
|
@@ -176,6 +200,7 @@ var BdsButton = class extends i2 {
|
|
|
176
200
|
this.size = "medium";
|
|
177
201
|
this.disabled = false;
|
|
178
202
|
this.hasPulse = false;
|
|
203
|
+
this.isIconOnly = false;
|
|
179
204
|
this.type = "button";
|
|
180
205
|
this.ariaLabel = null;
|
|
181
206
|
this._iconStartFilled = false;
|
|
@@ -191,7 +216,7 @@ var BdsButton = class extends i2 {
|
|
|
191
216
|
}
|
|
192
217
|
}
|
|
193
218
|
get _classes() {
|
|
194
|
-
return `button ${this.variant} ${this.size}${this.hasPulse ? " has-pulse" : ""}`;
|
|
219
|
+
return `button ${this.variant} ${this.size}${this.hasPulse ? " has-pulse" : ""}${this.isIconOnly ? " is-icon-only" : ""}`;
|
|
195
220
|
}
|
|
196
221
|
_handleAnchorClick(e) {
|
|
197
222
|
if (this.disabled) {
|
|
@@ -41,6 +41,134 @@ export { BdsLink } from './ui/bds-link.js';
|
|
|
41
41
|
import * as lit from 'lit';
|
|
42
42
|
import { LitElement } from 'lit';
|
|
43
43
|
|
|
44
|
+
type GridVariant = 'main' | 'page' | 'funnel' | 'custom';
|
|
45
|
+
/**
|
|
46
|
+
* `<bds-grid>` — framework-agnostic Grid layout custom element.
|
|
47
|
+
*
|
|
48
|
+
* Attributes:
|
|
49
|
+
* variant — "main" (default) | "page" | "funnel" | "custom"
|
|
50
|
+
* columns — number; column count when variant="custom"
|
|
51
|
+
* is-centered — boolean; centers the grid and caps width via --bds-container_max-width
|
|
52
|
+
* is-masonry — boolean; enables masonry layout per CSS Grid Level 3.
|
|
53
|
+
* Uses native `display: grid-lanes` / `grid-template-rows: masonry`
|
|
54
|
+
* when supported, otherwise runs a JS polyfill.
|
|
55
|
+
* auto-span-media — boolean; child `<bds-grid-item>`s without a `column-span`
|
|
56
|
+
* attribute resolve their span from the intrinsic aspect
|
|
57
|
+
* ratio of their first `<img>`/`<video>` child.
|
|
58
|
+
*
|
|
59
|
+
* Slots:
|
|
60
|
+
* (default) — grid content; typically `<bds-grid-item>` children
|
|
61
|
+
*/
|
|
62
|
+
declare class BdsGrid extends LitElement {
|
|
63
|
+
static styles: lit.CSSResult;
|
|
64
|
+
static properties: {
|
|
65
|
+
variant: {
|
|
66
|
+
type: StringConstructor;
|
|
67
|
+
reflect: boolean;
|
|
68
|
+
};
|
|
69
|
+
columns: {
|
|
70
|
+
type: NumberConstructor;
|
|
71
|
+
reflect: boolean;
|
|
72
|
+
};
|
|
73
|
+
isCentered: {
|
|
74
|
+
type: BooleanConstructor;
|
|
75
|
+
attribute: string;
|
|
76
|
+
reflect: boolean;
|
|
77
|
+
};
|
|
78
|
+
isMasonry: {
|
|
79
|
+
type: BooleanConstructor;
|
|
80
|
+
attribute: string;
|
|
81
|
+
reflect: boolean;
|
|
82
|
+
};
|
|
83
|
+
autoSpanMedia: {
|
|
84
|
+
type: BooleanConstructor;
|
|
85
|
+
attribute: string;
|
|
86
|
+
reflect: boolean;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
variant: GridVariant;
|
|
90
|
+
columns: number | undefined;
|
|
91
|
+
isCentered: boolean;
|
|
92
|
+
isMasonry: boolean;
|
|
93
|
+
autoSpanMedia: boolean;
|
|
94
|
+
private _ro?;
|
|
95
|
+
private _mo?;
|
|
96
|
+
private _frame;
|
|
97
|
+
constructor();
|
|
98
|
+
disconnectedCallback(): void;
|
|
99
|
+
updated(): void;
|
|
100
|
+
private _gridEl;
|
|
101
|
+
private _items;
|
|
102
|
+
private _setupMasonry;
|
|
103
|
+
private _scheduleMasonry;
|
|
104
|
+
private _teardownMasonry;
|
|
105
|
+
render(): lit.TemplateResult<1>;
|
|
106
|
+
}
|
|
107
|
+
declare global {
|
|
108
|
+
interface HTMLElementTagNameMap {
|
|
109
|
+
'bds-grid': BdsGrid;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
type GridItemSpanValue = 'full' | 'three-quarters' | 'two-thirds' | 'half' | 'one-third' | 'one-quarter' | 'auto' | number;
|
|
114
|
+
/**
|
|
115
|
+
* `<bds-grid-item>` — grid cell for `<bds-grid>`.
|
|
116
|
+
*
|
|
117
|
+
* Attributes:
|
|
118
|
+
* column-span — 'full' | 'three-quarters' | 'two-thirds' | 'half' | 'one-third' | 'one-quarter' | 'auto' | number
|
|
119
|
+
* `'auto'` derives the span from the intrinsic aspect ratio of the first
|
|
120
|
+
* `<img>` or `<video>` child (see Grid.mdx for thresholds).
|
|
121
|
+
* If the parent `<bds-grid>` sets `auto-span-media` and this element has
|
|
122
|
+
* no `column-span`, the item behaves as if `column-span="auto"`.
|
|
123
|
+
* row-span — number
|
|
124
|
+
* start-column — number (escape hatch; overrides column-span)
|
|
125
|
+
* end-column — number (escape hatch; overrides column-span)
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* <bds-grid-item column-span="three-quarters">Content</bds-grid-item>
|
|
129
|
+
*/
|
|
130
|
+
declare class BdsGridItem extends LitElement {
|
|
131
|
+
static styles: lit.CSSResult;
|
|
132
|
+
static properties: {
|
|
133
|
+
columnSpan: {
|
|
134
|
+
attribute: string;
|
|
135
|
+
};
|
|
136
|
+
rowSpan: {
|
|
137
|
+
attribute: string;
|
|
138
|
+
type: NumberConstructor;
|
|
139
|
+
};
|
|
140
|
+
startColumn: {
|
|
141
|
+
attribute: string;
|
|
142
|
+
type: NumberConstructor;
|
|
143
|
+
};
|
|
144
|
+
endColumn: {
|
|
145
|
+
attribute: string;
|
|
146
|
+
type: NumberConstructor;
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
columnSpan: GridItemSpanValue | undefined;
|
|
150
|
+
rowSpan: number | undefined;
|
|
151
|
+
startColumn: number | undefined;
|
|
152
|
+
endColumn: number | undefined;
|
|
153
|
+
private _autoSpan;
|
|
154
|
+
private _mediaCleanup?;
|
|
155
|
+
private _slotObserver?;
|
|
156
|
+
constructor();
|
|
157
|
+
connectedCallback(): void;
|
|
158
|
+
disconnectedCallback(): void;
|
|
159
|
+
private _effectiveColumnSpan;
|
|
160
|
+
private _teardownMedia;
|
|
161
|
+
private _resolveAutoSpan;
|
|
162
|
+
private _setAutoSpan;
|
|
163
|
+
updated(): void;
|
|
164
|
+
render(): lit.TemplateResult<1>;
|
|
165
|
+
}
|
|
166
|
+
declare global {
|
|
167
|
+
interface HTMLElementTagNameMap {
|
|
168
|
+
'bds-grid-item': BdsGridItem;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
44
172
|
type ButtonGroupOrientation = 'horizontal' | 'vertical';
|
|
45
173
|
/**
|
|
46
174
|
* `<bds-button-group>` — groups related buttons into a flex container.
|
|
@@ -608,4 +736,4 @@ declare global {
|
|
|
608
736
|
}
|
|
609
737
|
}
|
|
610
738
|
|
|
611
|
-
export { BdsButtonGroup, BdsCalendar, BdsCarousel, BdsCheckboxGroup, BdsDropdownMenu, BdsDropdownMenuItem, BdsFormInput, BdsPagination, BdsRadioGroup, BdsTable };
|
|
739
|
+
export { BdsButtonGroup, BdsCalendar, BdsCarousel, BdsCheckboxGroup, BdsDropdownMenu, BdsDropdownMenuItem, BdsFormInput, BdsGrid, BdsGridItem, BdsPagination, BdsRadioGroup, BdsTable };
|
|
@@ -69,7 +69,7 @@ import {
|
|
|
69
69
|
} from "./chunk-UFFCKVZE.js";
|
|
70
70
|
import {
|
|
71
71
|
BdsButton
|
|
72
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-N3TN6WCH.js";
|
|
73
73
|
import {
|
|
74
74
|
BdsCollapsible
|
|
75
75
|
} from "./chunk-6I2DBFQ7.js";
|
|
@@ -126,6 +126,315 @@ import {
|
|
|
126
126
|
i2
|
|
127
127
|
} from "./chunk-LGEZYFUU.js";
|
|
128
128
|
|
|
129
|
+
// src/components/layout/Grid/masonry.ts
|
|
130
|
+
import { useLayoutEffect } from "react";
|
|
131
|
+
var ROW_UNIT_PX = 1;
|
|
132
|
+
function supportsNativeMasonry() {
|
|
133
|
+
if (typeof CSS === "undefined" || typeof CSS.supports !== "function") return false;
|
|
134
|
+
return CSS.supports("display", "grid-lanes") || CSS.supports("grid-template-rows", "masonry");
|
|
135
|
+
}
|
|
136
|
+
function applyMasonryLayout(container, rawChildren) {
|
|
137
|
+
const cs = window.getComputedStyle(container);
|
|
138
|
+
const targetGap = parseFloat(cs.columnGap) || 0;
|
|
139
|
+
container.style.gridAutoRows = `${ROW_UNIT_PX}px`;
|
|
140
|
+
container.style.rowGap = "0px";
|
|
141
|
+
container.style.gridAutoFlow = "dense";
|
|
142
|
+
const children = rawChildren.filter(
|
|
143
|
+
(el) => el.nodeType === 1 && window.getComputedStyle(el).display !== "none"
|
|
144
|
+
);
|
|
145
|
+
for (const child of children) {
|
|
146
|
+
child.style.gridRow = "";
|
|
147
|
+
child.style.gridRowStart = "";
|
|
148
|
+
child.style.gridRowEnd = "";
|
|
149
|
+
}
|
|
150
|
+
const heights = children.map((child) => child.offsetHeight);
|
|
151
|
+
for (let i3 = 0; i3 < children.length; i3++) {
|
|
152
|
+
const span = Math.max(1, Math.ceil(heights[i3] + targetGap));
|
|
153
|
+
children[i3].style.gridRowEnd = `span ${span}`;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function clearMasonryLayout(container, rawChildren) {
|
|
157
|
+
container.style.gridAutoRows = "";
|
|
158
|
+
container.style.gridAutoFlow = "";
|
|
159
|
+
container.style.rowGap = "";
|
|
160
|
+
for (const child of rawChildren) {
|
|
161
|
+
if (child.nodeType !== 1) continue;
|
|
162
|
+
child.style.gridRow = "";
|
|
163
|
+
child.style.gridRowStart = "";
|
|
164
|
+
child.style.gridRowEnd = "";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/web-components/layout/bds-grid.ts
|
|
169
|
+
var BdsGrid = class extends i2 {
|
|
170
|
+
static styles = i`
|
|
171
|
+
:host {
|
|
172
|
+
display: block;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.grid {
|
|
176
|
+
display: grid;
|
|
177
|
+
gap: var(--grid_gap, var(--bds-grid_gap));
|
|
178
|
+
box-sizing: border-box;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.grid.--main {
|
|
182
|
+
grid-template-columns: var(--bds-grid_template-columns);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.grid.--page {
|
|
186
|
+
grid-template-columns: 1fr;
|
|
187
|
+
grid-column: 1 / -1;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.grid.--funnel {
|
|
191
|
+
grid-template-columns: var(--bds-grid_template-columns);
|
|
192
|
+
max-inline-size: var(--bds-container_max-width--narrow);
|
|
193
|
+
margin-inline: auto;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.grid.--custom {
|
|
197
|
+
grid-template-columns: repeat(var(--grid_columns, 4), minmax(0, 1fr));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.grid.--centered {
|
|
201
|
+
margin-inline: auto;
|
|
202
|
+
max-inline-size: var(--bds-container_max-width);
|
|
203
|
+
padding-inline: var(--bds-container_spacing-inline);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
@supports (grid-template-rows: masonry) {
|
|
207
|
+
.grid.--masonry {
|
|
208
|
+
grid-template-rows: masonry;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
@supports (display: grid-lanes) {
|
|
213
|
+
.grid.--masonry {
|
|
214
|
+
display: grid-lanes;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
`;
|
|
218
|
+
static properties = {
|
|
219
|
+
variant: { type: String, reflect: true },
|
|
220
|
+
columns: { type: Number, reflect: true },
|
|
221
|
+
isCentered: { type: Boolean, attribute: "is-centered", reflect: true },
|
|
222
|
+
isMasonry: { type: Boolean, attribute: "is-masonry", reflect: true },
|
|
223
|
+
autoSpanMedia: { type: Boolean, attribute: "auto-span-media", reflect: true }
|
|
224
|
+
};
|
|
225
|
+
_ro;
|
|
226
|
+
_mo;
|
|
227
|
+
_frame = 0;
|
|
228
|
+
constructor() {
|
|
229
|
+
super();
|
|
230
|
+
this.variant = "main";
|
|
231
|
+
this.columns = void 0;
|
|
232
|
+
this.isCentered = false;
|
|
233
|
+
this.isMasonry = false;
|
|
234
|
+
this.autoSpanMedia = false;
|
|
235
|
+
}
|
|
236
|
+
disconnectedCallback() {
|
|
237
|
+
super.disconnectedCallback();
|
|
238
|
+
this._teardownMasonry();
|
|
239
|
+
}
|
|
240
|
+
updated() {
|
|
241
|
+
if (this.isMasonry && !supportsNativeMasonry()) {
|
|
242
|
+
this._setupMasonry();
|
|
243
|
+
this._scheduleMasonry();
|
|
244
|
+
} else {
|
|
245
|
+
this._teardownMasonry();
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
_gridEl() {
|
|
249
|
+
return this.renderRoot.querySelector(".grid");
|
|
250
|
+
}
|
|
251
|
+
_items() {
|
|
252
|
+
return Array.from(this.children).filter((el) => el.nodeType === 1);
|
|
253
|
+
}
|
|
254
|
+
_setupMasonry() {
|
|
255
|
+
if (this._ro || this._mo) return;
|
|
256
|
+
const container = this._gridEl();
|
|
257
|
+
if (!container) return;
|
|
258
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
259
|
+
this._ro = new ResizeObserver(() => this._scheduleMasonry());
|
|
260
|
+
for (const child of this._items()) this._ro.observe(child);
|
|
261
|
+
}
|
|
262
|
+
if (typeof MutationObserver !== "undefined") {
|
|
263
|
+
this._mo = new MutationObserver(() => {
|
|
264
|
+
if (this._ro) for (const child of this._items()) this._ro.observe(child);
|
|
265
|
+
this._scheduleMasonry();
|
|
266
|
+
});
|
|
267
|
+
this._mo.observe(this, { childList: true });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
_scheduleMasonry() {
|
|
271
|
+
cancelAnimationFrame(this._frame);
|
|
272
|
+
this._frame = requestAnimationFrame(() => {
|
|
273
|
+
const container = this._gridEl();
|
|
274
|
+
if (container) applyMasonryLayout(container, this._items());
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
_teardownMasonry() {
|
|
278
|
+
cancelAnimationFrame(this._frame);
|
|
279
|
+
this._ro?.disconnect();
|
|
280
|
+
this._mo?.disconnect();
|
|
281
|
+
this._ro = void 0;
|
|
282
|
+
this._mo = void 0;
|
|
283
|
+
const container = this._gridEl();
|
|
284
|
+
if (container) clearMasonryLayout(container, this._items());
|
|
285
|
+
}
|
|
286
|
+
render() {
|
|
287
|
+
const classes = [
|
|
288
|
+
"grid",
|
|
289
|
+
`--${this.variant}`,
|
|
290
|
+
this.isCentered ? "--centered" : "",
|
|
291
|
+
this.isMasonry ? "--masonry" : ""
|
|
292
|
+
].filter(Boolean).join(" ");
|
|
293
|
+
const styleAttr = this.variant === "custom" && this.columns !== void 0 ? `--grid_columns: ${this.columns}` : "";
|
|
294
|
+
return T`<div class=${classes} style=${styleAttr}><slot></slot></div>`;
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
customElements.define("bds-grid", BdsGrid);
|
|
298
|
+
|
|
299
|
+
// src/components/layout/Grid/autoSpan.ts
|
|
300
|
+
function aspectToColumnSpan(ratio) {
|
|
301
|
+
if (!Number.isFinite(ratio) || ratio <= 0) return "one-quarter";
|
|
302
|
+
if (ratio > 3.5) return "full";
|
|
303
|
+
if (ratio > 2.2) return "three-quarters";
|
|
304
|
+
if (ratio > 1.5) return "half";
|
|
305
|
+
return "one-quarter";
|
|
306
|
+
}
|
|
307
|
+
function findMediaChild(root) {
|
|
308
|
+
return root.querySelector("img, video");
|
|
309
|
+
}
|
|
310
|
+
function readMediaAspect(media) {
|
|
311
|
+
if (media instanceof HTMLImageElement) {
|
|
312
|
+
if (media.naturalWidth > 0 && media.naturalHeight > 0) {
|
|
313
|
+
return media.naturalWidth / media.naturalHeight;
|
|
314
|
+
}
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
if (media.videoWidth > 0 && media.videoHeight > 0) {
|
|
318
|
+
return media.videoWidth / media.videoHeight;
|
|
319
|
+
}
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
function mediaReadyEvent(media) {
|
|
323
|
+
if (media instanceof HTMLImageElement) return media.complete ? null : "load";
|
|
324
|
+
if (media.readyState < 1) return "loadedmetadata";
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// src/web-components/layout/bds-grid-item.ts
|
|
329
|
+
var SPAN_VAR = {
|
|
330
|
+
full: "var(--bds-grid_span-100)",
|
|
331
|
+
"three-quarters": "span var(--bds-grid_columns-75)",
|
|
332
|
+
"two-thirds": "span var(--bds-grid_columns-66)",
|
|
333
|
+
half: "span var(--bds-grid_columns-50)",
|
|
334
|
+
"one-third": "span var(--bds-grid_columns-33)",
|
|
335
|
+
"one-quarter": "span var(--bds-grid_columns-25)"
|
|
336
|
+
};
|
|
337
|
+
function resolveSpan(value) {
|
|
338
|
+
if (value === void 0 || value === null) return SPAN_VAR.full;
|
|
339
|
+
if (typeof value === "number") return `span ${value}`;
|
|
340
|
+
if (!(value in SPAN_VAR)) {
|
|
341
|
+
const n = Number(value);
|
|
342
|
+
if (Number.isFinite(n)) return `span ${n}`;
|
|
343
|
+
return SPAN_VAR.full;
|
|
344
|
+
}
|
|
345
|
+
return SPAN_VAR[value];
|
|
346
|
+
}
|
|
347
|
+
var BdsGridItem = class extends i2 {
|
|
348
|
+
static styles = i`
|
|
349
|
+
:host {
|
|
350
|
+
display: block;
|
|
351
|
+
min-inline-size: 0;
|
|
352
|
+
}
|
|
353
|
+
`;
|
|
354
|
+
static properties = {
|
|
355
|
+
columnSpan: { attribute: "column-span" },
|
|
356
|
+
rowSpan: { attribute: "row-span", type: Number },
|
|
357
|
+
startColumn: { attribute: "start-column", type: Number },
|
|
358
|
+
endColumn: { attribute: "end-column", type: Number }
|
|
359
|
+
};
|
|
360
|
+
_autoSpan = "one-quarter";
|
|
361
|
+
_mediaCleanup;
|
|
362
|
+
_slotObserver;
|
|
363
|
+
constructor() {
|
|
364
|
+
super();
|
|
365
|
+
this.columnSpan = void 0;
|
|
366
|
+
this.rowSpan = void 0;
|
|
367
|
+
this.startColumn = void 0;
|
|
368
|
+
this.endColumn = void 0;
|
|
369
|
+
}
|
|
370
|
+
connectedCallback() {
|
|
371
|
+
super.connectedCallback();
|
|
372
|
+
if (typeof MutationObserver !== "undefined") {
|
|
373
|
+
this._slotObserver = new MutationObserver(() => this._resolveAutoSpan());
|
|
374
|
+
this._slotObserver.observe(this, { childList: true, subtree: true });
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
disconnectedCallback() {
|
|
378
|
+
super.disconnectedCallback();
|
|
379
|
+
this._teardownMedia();
|
|
380
|
+
this._slotObserver?.disconnect();
|
|
381
|
+
this._slotObserver = void 0;
|
|
382
|
+
}
|
|
383
|
+
_effectiveColumnSpan() {
|
|
384
|
+
if (this.columnSpan !== void 0 && this.columnSpan !== null) return this.columnSpan;
|
|
385
|
+
const parent = this.closest("bds-grid");
|
|
386
|
+
if (parent?.autoSpanMedia) return "auto";
|
|
387
|
+
return "full";
|
|
388
|
+
}
|
|
389
|
+
_teardownMedia() {
|
|
390
|
+
this._mediaCleanup?.();
|
|
391
|
+
this._mediaCleanup = void 0;
|
|
392
|
+
}
|
|
393
|
+
_resolveAutoSpan() {
|
|
394
|
+
this._teardownMedia();
|
|
395
|
+
if (this._effectiveColumnSpan() !== "auto") return;
|
|
396
|
+
const media = findMediaChild(this);
|
|
397
|
+
if (!media) {
|
|
398
|
+
this._setAutoSpan("one-quarter");
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
const aspect = readMediaAspect(media);
|
|
402
|
+
if (aspect !== null) {
|
|
403
|
+
this._setAutoSpan(aspectToColumnSpan(aspect));
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
const event = mediaReadyEvent(media);
|
|
407
|
+
if (!event) {
|
|
408
|
+
this._setAutoSpan("one-quarter");
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
const handler = () => {
|
|
412
|
+
const a = readMediaAspect(media);
|
|
413
|
+
if (a !== null) this._setAutoSpan(aspectToColumnSpan(a));
|
|
414
|
+
};
|
|
415
|
+
media.addEventListener(event, handler, { once: true });
|
|
416
|
+
this._mediaCleanup = () => media.removeEventListener(event, handler);
|
|
417
|
+
}
|
|
418
|
+
_setAutoSpan(next) {
|
|
419
|
+
if (this._autoSpan === next) return;
|
|
420
|
+
this._autoSpan = next;
|
|
421
|
+
this.requestUpdate();
|
|
422
|
+
}
|
|
423
|
+
updated() {
|
|
424
|
+
const effective = this._effectiveColumnSpan();
|
|
425
|
+
if (effective === "auto") this._resolveAutoSpan();
|
|
426
|
+
const hasExplicitRange = this.startColumn !== void 0 || this.endColumn !== void 0;
|
|
427
|
+
const resolved = effective === "auto" ? this._autoSpan : effective;
|
|
428
|
+
const gridColumn = hasExplicitRange ? `${this.startColumn ?? "auto"} / ${this.endColumn ?? "auto"}` : resolveSpan(resolved);
|
|
429
|
+
this.style.gridColumn = gridColumn;
|
|
430
|
+
this.style.gridRow = this.rowSpan !== void 0 ? `span ${this.rowSpan}` : "";
|
|
431
|
+
}
|
|
432
|
+
render() {
|
|
433
|
+
return T`<slot></slot>`;
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
customElements.define("bds-grid-item", BdsGridItem);
|
|
437
|
+
|
|
129
438
|
// src/web-components/ui/bds-button-group.ts
|
|
130
439
|
var BdsButtonGroup = class extends i2 {
|
|
131
440
|
static styles = i`
|
|
@@ -1631,6 +1940,8 @@ export {
|
|
|
1631
1940
|
BdsDropdownMenuItem,
|
|
1632
1941
|
BdsFileInput,
|
|
1633
1942
|
BdsFormInput,
|
|
1943
|
+
BdsGrid,
|
|
1944
|
+
BdsGridItem,
|
|
1634
1945
|
BdsIconWrapper,
|
|
1635
1946
|
BdsLink,
|
|
1636
1947
|
BdsLoading,
|
|
@@ -14,6 +14,7 @@ type ButtonSize = 'small' | 'medium' | 'large';
|
|
|
14
14
|
* target — forwarded to <a> when href is set
|
|
15
15
|
* rel — forwarded to <a> when href is set
|
|
16
16
|
* has-pulse — boolean, enables the pulse ring animation
|
|
17
|
+
* is-icon-only — boolean, forces a 1:1 aspect ratio with minimal xs padding (requires aria-label)
|
|
17
18
|
* type — "button" (default) | "submit" | "reset"
|
|
18
19
|
*
|
|
19
20
|
* Slots:
|
|
@@ -59,6 +60,11 @@ declare class BdsButton extends LitElement {
|
|
|
59
60
|
attribute: string;
|
|
60
61
|
reflect: boolean;
|
|
61
62
|
};
|
|
63
|
+
isIconOnly: {
|
|
64
|
+
type: BooleanConstructor;
|
|
65
|
+
attribute: string;
|
|
66
|
+
reflect: boolean;
|
|
67
|
+
};
|
|
62
68
|
type: {
|
|
63
69
|
type: StringConstructor;
|
|
64
70
|
};
|
|
@@ -80,6 +86,7 @@ declare class BdsButton extends LitElement {
|
|
|
80
86
|
target: string | undefined;
|
|
81
87
|
rel: string | undefined;
|
|
82
88
|
hasPulse: boolean;
|
|
89
|
+
isIconOnly: boolean;
|
|
83
90
|
type: 'button' | 'submit' | 'reset';
|
|
84
91
|
ariaLabel: string | null;
|
|
85
92
|
private _iconStartFilled;
|
package/package.json
CHANGED