fokus-styles 2.6.2 → 2.7.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/README.md +5 -0
- package/dist/css/components.css +534 -27
- package/dist/css/components.min.css +1 -1
- package/dist/css/fokus-components.css +534 -27
- package/dist/css/fokus-components.min.css +1 -1
- package/dist/css/fokus-core.css +42 -0
- package/dist/css/fokus-core.min.css +1 -1
- package/dist/css/fokus-dark.css +42 -0
- package/dist/css/fokus-dark.min.css +1 -1
- package/dist/css/fokus-helpers.css +646 -0
- package/dist/css/fokus-helpers.min.css +2 -0
- package/dist/css/fokus-rtl.css +9667 -2716
- package/dist/css/fokus-rtl.min.css +1 -1
- package/dist/css/fokus-utilities.css +9175 -2682
- package/dist/css/fokus-utilities.min.css +1 -1
- package/dist/css/fokus.css +9893 -2799
- package/dist/css/fokus.min.css +1 -1
- package/dist/css/forms.css +206 -28
- package/dist/css/forms.min.css +1 -1
- package/dist/css/helpers.css +9175 -2682
- package/dist/css/helpers.min.css +1 -1
- package/dist/css/layout.css +42 -0
- package/dist/css/layout.min.css +1 -1
- package/dist/js/fokus.js +91 -69
- package/dist/js/fokus.min.js +19 -19
- package/package.json +2 -1
- package/packages/fokus-components/scss/components/_button-group.scss +12 -0
- package/packages/fokus-components/scss/components/_buttons.scss +39 -6
- package/packages/fokus-components/scss/components/_datepicker.scss +47 -1
- package/packages/fokus-components/scss/components/_dropdown.scss +212 -6
- package/packages/fokus-components/scss/components/_index.scss +1 -0
- package/packages/fokus-components/scss/components/_menu.scss +95 -0
- package/packages/fokus-components/scss/components/_pagination.scss +43 -16
- package/packages/fokus-components/scss/components/_segmented-control.scss +16 -1
- package/packages/fokus-components/scss/components/_tabs.scss +58 -0
- package/packages/fokus-components/scss/components/_tag.scss +32 -0
- package/packages/fokus-components/scss/forms/_forms.scss +155 -28
- package/packages/fokus-components/scss/forms/_range.scss +29 -0
- package/packages/fokus-core/scss/base/_motion.scss +3 -1
- package/packages/fokus-core/scss/settings/_config.scss +39 -1
- package/packages/fokus-core/scss/settings/_index.scss +1 -0
- package/packages/fokus-core/scss/settings/_utilities.scss +86 -0
- package/packages/fokus-core/scss/themes/_dark.scss +9 -0
- package/packages/fokus-core/scss/tokens/_root.scss +23 -0
- package/packages/fokus-js/js/combobox.js +2 -6
- package/packages/fokus-js/js/command-palette.js +2 -6
- package/packages/fokus-js/js/core/positioning.d.ts +2 -0
- package/packages/fokus-js/js/core/positioning.js +2 -2
- package/packages/fokus-js/js/core/theme.js +8 -0
- package/packages/fokus-js/js/datatable.js +6 -2
- package/packages/fokus-js/js/datepicker.js +13 -5
- package/packages/fokus-js/js/dropdown.js +37 -5
- package/packages/fokus-js/js/nested-menu.js +2 -6
- package/packages/fokus-js/js/notification-center.js +2 -6
- package/packages/fokus-js/js/pagination.js +15 -9
- package/packages/fokus-js/js/popover.js +2 -6
- package/packages/fokus-js/js/select.js +4 -0
- package/packages/fokus-js/js/tabs.js +4 -4
- package/packages/fokus-js/js/tooltip.js +2 -6
- package/packages/fokus-utilities/scss/utilities/_api.scss +62 -69
- package/packages/fokus-utilities/scss/utilities/_borders.scss +39 -0
- package/packages/fokus-utilities/scss/utilities/_colors.scss +27 -0
- package/packages/fokus-utilities/scss/utilities/_display.scss +20 -16
- package/packages/fokus-utilities/scss/utilities/_effects.scss +53 -0
- package/packages/fokus-utilities/scss/utilities/_flex.scss +50 -35
- package/packages/fokus-utilities/scss/utilities/_generated.scss +21 -0
- package/packages/fokus-utilities/scss/utilities/_grid.scss +25 -0
- package/packages/fokus-utilities/scss/utilities/_helpers.scss +35 -0
- package/packages/fokus-utilities/scss/utilities/_index.scss +11 -0
- package/packages/fokus-utilities/scss/utilities/_interactivity.scss +51 -0
- package/packages/fokus-utilities/scss/utilities/_layout.scss +61 -0
- package/packages/fokus-utilities/scss/utilities/_motion.scss +51 -0
- package/packages/fokus-utilities/scss/utilities/_position.scss +6 -6
- package/packages/fokus-utilities/scss/utilities/_print.scss +22 -18
- package/packages/fokus-utilities/scss/utilities/_shadow.scss +7 -3
- package/packages/fokus-utilities/scss/utilities/_sizing.scss +46 -0
- package/packages/fokus-utilities/scss/utilities/_spacing.scss +40 -113
- package/packages/fokus-utilities/scss/utilities/_states.scss +26 -10
- package/packages/fokus-utilities/scss/utilities/_transforms.scss +67 -0
- package/packages/fokus-utilities/scss/utilities/_typography.scss +72 -94
- package/scss/entries/helpers-entry.scss +5 -0
- package/scss/entries/rtl-entry.scss +6 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { computePosition, applyPosition } from "./core/positioning.js";
|
|
2
2
|
import { onClickOutside } from "./core/overlay.js";
|
|
3
3
|
import { onEscapeKey } from "./core/focus.js";
|
|
4
|
+
import { applyThemeContext } from "./core/theme.js";
|
|
4
5
|
import { autoInit, createInstanceRegistry } from "./core/register.js";
|
|
5
6
|
|
|
6
7
|
const instances = createInstanceRegistry();
|
|
@@ -175,12 +176,7 @@ export class Combobox {
|
|
|
175
176
|
if (this.isOpen) return;
|
|
176
177
|
this.isOpen = true;
|
|
177
178
|
|
|
178
|
-
|
|
179
|
-
if (theme) {
|
|
180
|
-
this.listboxEl.setAttribute("data-theme", theme);
|
|
181
|
-
} else {
|
|
182
|
-
this.listboxEl.removeAttribute("data-theme");
|
|
183
|
-
}
|
|
179
|
+
applyThemeContext(this.listboxEl, this.inputEl);
|
|
184
180
|
|
|
185
181
|
this.listboxEl.classList.add("is-open");
|
|
186
182
|
this.listboxEl.style.width = `${this.inputEl.offsetWidth}px`;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { lockScroll, unlockScroll, onClickOutside } from "./core/overlay.js";
|
|
2
2
|
import { createFocusTrap, onEscapeKey } from "./core/focus.js";
|
|
3
|
+
import { applyThemeContext } from "./core/theme.js";
|
|
3
4
|
import { autoInit, createInstanceRegistry } from "./core/register.js";
|
|
4
5
|
|
|
5
6
|
const instances = createInstanceRegistry();
|
|
@@ -196,12 +197,7 @@ export class CommandPalette {
|
|
|
196
197
|
if (this.isOpen) return;
|
|
197
198
|
this.isOpen = true;
|
|
198
199
|
|
|
199
|
-
|
|
200
|
-
if (theme) {
|
|
201
|
-
this.paletteEl.setAttribute("data-theme", theme);
|
|
202
|
-
} else {
|
|
203
|
-
this.paletteEl.removeAttribute("data-theme");
|
|
204
|
-
}
|
|
200
|
+
applyThemeContext(this.paletteEl, this.triggerEl);
|
|
205
201
|
|
|
206
202
|
this.paletteEl.classList.add("is-open");
|
|
207
203
|
lockScroll();
|
|
@@ -6,12 +6,14 @@ export interface ComputePositionOptions {
|
|
|
6
6
|
align?: Align;
|
|
7
7
|
offset?: number;
|
|
8
8
|
padding?: number;
|
|
9
|
+
fallbackPlacements?: Placement[];
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export interface Position {
|
|
12
13
|
top: number;
|
|
13
14
|
left: number;
|
|
14
15
|
placement: Placement;
|
|
16
|
+
arrowOffset?: string;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export function computePosition(
|
|
@@ -41,8 +41,8 @@ export function computePosition(referenceEl, floatingEl, options = {}) {
|
|
|
41
41
|
|
|
42
42
|
const candidates = [...new Set(fallbackPlacements ?? [placement, OPPOSITE_PLACEMENT[placement], "bottom", "top", "right", "left"])]
|
|
43
43
|
.filter(Boolean);
|
|
44
|
-
const
|
|
45
|
-
const finalPlacement =
|
|
44
|
+
const placementCandidates = fallbackPlacements ? candidates : candidates.slice(0, 2);
|
|
45
|
+
const finalPlacement = placementCandidates.find((candidate) => fitsPlacement(candidate, referenceRect, floatingRect, offset, viewportWidth, viewportHeight))
|
|
46
46
|
?? placement;
|
|
47
47
|
|
|
48
48
|
let top;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function applyThemeContext(targetEl, sourceEl) {
|
|
2
|
+
targetEl.removeAttribute("data-theme");
|
|
3
|
+
targetEl.removeAttribute("data-fs-theme");
|
|
4
|
+
|
|
5
|
+
const themeAttr = sourceEl.hasAttribute("data-fs-theme") ? "data-fs-theme" : "data-theme";
|
|
6
|
+
const theme = sourceEl.closest(`[${themeAttr}]`)?.getAttribute(themeAttr);
|
|
7
|
+
if (theme) targetEl.setAttribute(themeAttr, theme);
|
|
8
|
+
}
|
|
@@ -51,7 +51,10 @@ export class DataTable {
|
|
|
51
51
|
this.tableEl = tableEl;
|
|
52
52
|
this.theadEl = tableEl.querySelector("thead");
|
|
53
53
|
this.tbodyEl = tbodyEl;
|
|
54
|
-
|
|
54
|
+
const configuredPageSize = Number(rootEl.getAttribute("data-fs-page-size") ?? options.pageSize ?? 10);
|
|
55
|
+
this.pageSize = Number.isFinite(configuredPageSize) && configuredPageSize > 0
|
|
56
|
+
? Math.floor(configuredPageSize)
|
|
57
|
+
: 10;
|
|
55
58
|
|
|
56
59
|
this.filterInput = rootEl.querySelector("[data-fs-datatable-filter]");
|
|
57
60
|
this.emptyEl = rootEl.querySelector("[data-fs-datatable-empty]");
|
|
@@ -191,7 +194,8 @@ export class DataTable {
|
|
|
191
194
|
|
|
192
195
|
goToPage(page) {
|
|
193
196
|
const pageCount = this.pageCount;
|
|
194
|
-
const
|
|
197
|
+
const requested = Number(page);
|
|
198
|
+
const next = Number.isFinite(requested) ? Math.min(Math.max(1, Math.floor(requested)), pageCount) : 1;
|
|
195
199
|
if (next === this.currentPage) return;
|
|
196
200
|
|
|
197
201
|
this.currentPage = next;
|
|
@@ -43,6 +43,12 @@ function isSameDay(a, b) {
|
|
|
43
43
|
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
function addMonthsClamped(date, months) {
|
|
47
|
+
const target = new Date(date.getFullYear(), date.getMonth() + months, 1);
|
|
48
|
+
const lastDay = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate();
|
|
49
|
+
return new Date(target.getFullYear(), target.getMonth(), Math.min(date.getDate(), lastDay));
|
|
50
|
+
}
|
|
51
|
+
|
|
46
52
|
function buildMonthCells(viewDate) {
|
|
47
53
|
const year = viewDate.getFullYear();
|
|
48
54
|
const month = viewDate.getMonth();
|
|
@@ -246,13 +252,13 @@ export class Datepicker {
|
|
|
246
252
|
break;
|
|
247
253
|
case "PageUp":
|
|
248
254
|
nextDate = event.shiftKey
|
|
249
|
-
?
|
|
250
|
-
:
|
|
255
|
+
? addMonthsClamped(currentDate, -12)
|
|
256
|
+
: addMonthsClamped(currentDate, -1);
|
|
251
257
|
break;
|
|
252
258
|
case "PageDown":
|
|
253
259
|
nextDate = event.shiftKey
|
|
254
|
-
?
|
|
255
|
-
:
|
|
260
|
+
? addMonthsClamped(currentDate, 12)
|
|
261
|
+
: addMonthsClamped(currentDate, 1);
|
|
256
262
|
break;
|
|
257
263
|
case "Enter":
|
|
258
264
|
case " ":
|
|
@@ -328,6 +334,7 @@ export class Datepicker {
|
|
|
328
334
|
return (
|
|
329
335
|
`<button type="button" class="${classes.join(" ")}" role="gridcell" ` +
|
|
330
336
|
`tabindex="${isTabbable ? "0" : "-1"}" aria-selected="${isSelected}" ` +
|
|
337
|
+
`${isToday ? 'aria-current="date" ' : ""}` +
|
|
331
338
|
`data-year="${cell.year}" data-month="${cell.month}" data-day="${cell.day}">${cell.day}</button>`
|
|
332
339
|
);
|
|
333
340
|
})
|
|
@@ -344,7 +351,8 @@ export class Datepicker {
|
|
|
344
351
|
if (this.isOpen) return;
|
|
345
352
|
this.isOpen = true;
|
|
346
353
|
|
|
347
|
-
const
|
|
354
|
+
const themeRoot = this.inputEl.closest("[data-theme], [data-fs-theme]");
|
|
355
|
+
const theme = themeRoot?.getAttribute("data-theme") ?? themeRoot?.getAttribute("data-fs-theme");
|
|
348
356
|
if (theme) {
|
|
349
357
|
this.panelEl.setAttribute("data-theme", theme);
|
|
350
358
|
} else {
|
|
@@ -26,20 +26,28 @@ export class Dropdown {
|
|
|
26
26
|
|
|
27
27
|
document.body.appendChild(this.menuEl);
|
|
28
28
|
|
|
29
|
+
if (!this.toggleEl.id) this.toggleEl.id = `fs-dropdown-toggle-${Math.random().toString(36).slice(2)}`;
|
|
29
30
|
this.toggleEl.setAttribute("aria-haspopup", "menu");
|
|
30
31
|
this.toggleEl.setAttribute("aria-expanded", "false");
|
|
31
32
|
this.toggleEl.setAttribute("aria-controls", this.menuEl.id || `fs-dropdown-${Math.random().toString(36).slice(2)}`);
|
|
32
33
|
if (!this.menuEl.id) this.menuEl.id = this.toggleEl.getAttribute("aria-controls");
|
|
33
|
-
this.menuEl.setAttribute("role", "menu");
|
|
34
|
+
if (!this.menuEl.hasAttribute("role")) this.menuEl.setAttribute("role", "menu");
|
|
35
|
+
this.menuEl.setAttribute("aria-orientation", "vertical");
|
|
36
|
+
this.menuEl.setAttribute("aria-labelledby", this.toggleEl.id || this.toggleEl.getAttribute("aria-controls"));
|
|
34
37
|
this.menuEl.querySelectorAll(".fs-dropdown-item").forEach((item) => {
|
|
35
38
|
if (!item.hasAttribute("role")) item.setAttribute("role", "menuitem");
|
|
39
|
+
if (this._isDisabled(item)) item.setAttribute("aria-disabled", "true");
|
|
36
40
|
});
|
|
41
|
+
this.menuEl.querySelectorAll(".fs-dropdown-divider").forEach((divider) => divider.setAttribute("role", "separator"));
|
|
42
|
+
this.menuEl.querySelectorAll(".fs-dropdown-header").forEach((header) => header.setAttribute("role", "presentation"));
|
|
37
43
|
|
|
38
44
|
this._handleToggleClick = this._handleToggleClick.bind(this);
|
|
45
|
+
this._handleToggleKeydown = this._handleToggleKeydown.bind(this);
|
|
39
46
|
this._handleMenuKeydown = this._handleMenuKeydown.bind(this);
|
|
40
47
|
this._handleMenuClick = this._handleMenuClick.bind(this);
|
|
41
48
|
|
|
42
49
|
this.toggleEl.addEventListener("click", this._handleToggleClick);
|
|
50
|
+
this.toggleEl.addEventListener("keydown", this._handleToggleKeydown);
|
|
43
51
|
this.menuEl.addEventListener("keydown", this._handleMenuKeydown);
|
|
44
52
|
this.menuEl.addEventListener("click", this._handleMenuClick);
|
|
45
53
|
|
|
@@ -67,15 +75,25 @@ export class Dropdown {
|
|
|
67
75
|
this.toggle();
|
|
68
76
|
}
|
|
69
77
|
|
|
78
|
+
_handleToggleKeydown(event) {
|
|
79
|
+
if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
|
|
80
|
+
|
|
81
|
+
event.preventDefault();
|
|
82
|
+
if (!this.isOpen) this.show();
|
|
83
|
+
|
|
84
|
+
const items = this._items();
|
|
85
|
+
(event.key === "ArrowUp" ? items.at(-1) : items[0])?.focus();
|
|
86
|
+
}
|
|
87
|
+
|
|
70
88
|
_handleMenuClick(event) {
|
|
71
|
-
if (event.target.closest(".fs-dropdown-item")) {
|
|
89
|
+
if (event.target.closest(".fs-dropdown-item, [data-fs-dropdown-dismiss]")) {
|
|
72
90
|
this.hide();
|
|
73
91
|
this.toggleEl.focus();
|
|
74
92
|
}
|
|
75
93
|
}
|
|
76
94
|
|
|
77
95
|
_handleMenuKeydown(event) {
|
|
78
|
-
const items =
|
|
96
|
+
const items = this._items();
|
|
79
97
|
const currentIndex = items.indexOf(document.activeElement);
|
|
80
98
|
|
|
81
99
|
if (event.key === "ArrowDown") {
|
|
@@ -101,7 +119,8 @@ export class Dropdown {
|
|
|
101
119
|
if (this.isOpen) return;
|
|
102
120
|
this.isOpen = true;
|
|
103
121
|
|
|
104
|
-
const
|
|
122
|
+
const themeRoot = this.toggleEl.closest("[data-theme], [data-fs-theme]");
|
|
123
|
+
const theme = themeRoot?.getAttribute("data-theme") ?? themeRoot?.getAttribute("data-fs-theme");
|
|
105
124
|
if (theme) {
|
|
106
125
|
this.menuEl.setAttribute("data-theme", theme);
|
|
107
126
|
} else {
|
|
@@ -116,6 +135,8 @@ export class Dropdown {
|
|
|
116
135
|
offset: 4,
|
|
117
136
|
});
|
|
118
137
|
applyPosition(this.menuEl, position);
|
|
138
|
+
this.menuEl.dataset.placement = position.placement;
|
|
139
|
+
this.menuEl.dataset.align = this.align;
|
|
119
140
|
this._positionCleanup = watchPosition(this.toggleEl, this.menuEl, {
|
|
120
141
|
placement: this.placement,
|
|
121
142
|
align: this.align,
|
|
@@ -124,7 +145,7 @@ export class Dropdown {
|
|
|
124
145
|
|
|
125
146
|
this.toggleEl.setAttribute("aria-expanded", "true");
|
|
126
147
|
|
|
127
|
-
const firstItem = this.
|
|
148
|
+
const firstItem = this._items()[0];
|
|
128
149
|
firstItem?.focus();
|
|
129
150
|
|
|
130
151
|
this._outsideClickCleanup = onClickOutside(this.menuEl, (event) => {
|
|
@@ -144,6 +165,8 @@ export class Dropdown {
|
|
|
144
165
|
this.menuEl.style.removeProperty("position");
|
|
145
166
|
this.menuEl.style.removeProperty("top");
|
|
146
167
|
this.menuEl.style.removeProperty("left");
|
|
168
|
+
delete this.menuEl.dataset.placement;
|
|
169
|
+
delete this.menuEl.dataset.align;
|
|
147
170
|
this.toggleEl.setAttribute("aria-expanded", "false");
|
|
148
171
|
|
|
149
172
|
this._outsideClickCleanup?.();
|
|
@@ -163,10 +186,19 @@ export class Dropdown {
|
|
|
163
186
|
}
|
|
164
187
|
}
|
|
165
188
|
|
|
189
|
+
_isDisabled(item) {
|
|
190
|
+
return item.classList.contains("is-disabled") || item.disabled || item.getAttribute("aria-disabled") === "true";
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
_items() {
|
|
194
|
+
return Array.from(this.menuEl.querySelectorAll(".fs-dropdown-item")).filter((item) => !this._isDisabled(item));
|
|
195
|
+
}
|
|
196
|
+
|
|
166
197
|
dispose() {
|
|
167
198
|
this.hide();
|
|
168
199
|
this._removeEscapeListener();
|
|
169
200
|
this.toggleEl.removeEventListener("click", this._handleToggleClick);
|
|
201
|
+
this.toggleEl.removeEventListener("keydown", this._handleToggleKeydown);
|
|
170
202
|
this.menuEl.removeEventListener("keydown", this._handleMenuKeydown);
|
|
171
203
|
this.menuEl.removeEventListener("click", this._handleMenuClick);
|
|
172
204
|
if (this._originalParent) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { computePosition, applyPosition } from "./core/positioning.js";
|
|
2
2
|
import { onClickOutside } from "./core/overlay.js";
|
|
3
3
|
import { onEscapeKey } from "./core/focus.js";
|
|
4
|
+
import { applyThemeContext } from "./core/theme.js";
|
|
4
5
|
import { autoInit, createInstanceRegistry } from "./core/register.js";
|
|
5
6
|
|
|
6
7
|
const instances = createInstanceRegistry();
|
|
@@ -243,12 +244,7 @@ export class NestedMenu {
|
|
|
243
244
|
if (this.isOpen) return;
|
|
244
245
|
this.isOpen = true;
|
|
245
246
|
|
|
246
|
-
|
|
247
|
-
if (theme) {
|
|
248
|
-
this.rootMenu.setAttribute("data-theme", theme);
|
|
249
|
-
} else {
|
|
250
|
-
this.rootMenu.removeAttribute("data-theme");
|
|
251
|
-
}
|
|
247
|
+
applyThemeContext(this.rootMenu, this.toggleEl);
|
|
252
248
|
|
|
253
249
|
this.rootMenu.classList.add("is-open");
|
|
254
250
|
|
|
@@ -2,6 +2,7 @@ import { Toast } from "./toast.js";
|
|
|
2
2
|
import { computePosition, applyPosition } from "./core/positioning.js";
|
|
3
3
|
import { onClickOutside } from "./core/overlay.js";
|
|
4
4
|
import { onEscapeKey } from "./core/focus.js";
|
|
5
|
+
import { applyThemeContext } from "./core/theme.js";
|
|
5
6
|
import { autoInit, createInstanceRegistry } from "./core/register.js";
|
|
6
7
|
|
|
7
8
|
const instances = createInstanceRegistry();
|
|
@@ -217,12 +218,7 @@ export class NotificationCenter {
|
|
|
217
218
|
if (this.isOpen) return;
|
|
218
219
|
this.isOpen = true;
|
|
219
220
|
|
|
220
|
-
|
|
221
|
-
if (theme) {
|
|
222
|
-
this.panelEl.setAttribute("data-theme", theme);
|
|
223
|
-
} else {
|
|
224
|
-
this.panelEl.removeAttribute("data-theme");
|
|
225
|
-
}
|
|
221
|
+
applyThemeContext(this.panelEl, this.triggerEl);
|
|
226
222
|
|
|
227
223
|
this.panelEl.classList.add("is-open");
|
|
228
224
|
|
|
@@ -4,13 +4,13 @@ const instances = createInstanceRegistry();
|
|
|
4
4
|
export class Pagination {
|
|
5
5
|
constructor(element) {
|
|
6
6
|
this.element = element;
|
|
7
|
-
this.
|
|
8
|
-
this.
|
|
7
|
+
this.total = Math.max(1, this._normalizePage(element.getAttribute("data-fs-pages"), element.querySelectorAll("[data-fs-page]").length || 1));
|
|
8
|
+
this.current = Math.min(this.total, this._normalizePage(element.getAttribute("data-fs-page"), 1));
|
|
9
9
|
this._click = (event) => {
|
|
10
10
|
const button = event.target.closest("[data-fs-page]");
|
|
11
11
|
if (!button || !this.element.contains(button)) return;
|
|
12
12
|
event.preventDefault();
|
|
13
|
-
this.goTo(
|
|
13
|
+
this.goTo(button.getAttribute("data-fs-page"));
|
|
14
14
|
};
|
|
15
15
|
element.addEventListener("click", this._click);
|
|
16
16
|
instances.set(element, this);
|
|
@@ -19,20 +19,26 @@ export class Pagination {
|
|
|
19
19
|
static getInstance(el) { return instances.get(el); }
|
|
20
20
|
static getOrCreateInstance(el) { return this.getInstance(el) ?? new Pagination(el); }
|
|
21
21
|
goTo(page) {
|
|
22
|
-
const next =
|
|
23
|
-
|
|
24
|
-
this.current
|
|
25
|
-
this.
|
|
22
|
+
const next = this._normalizePage(page, this.current);
|
|
23
|
+
const bounded = Math.max(1, Math.min(this.total, next));
|
|
24
|
+
if (bounded === this.current) return;
|
|
25
|
+
this.current = bounded;
|
|
26
|
+
this.element.setAttribute("data-fs-page", String(bounded));
|
|
26
27
|
this._render();
|
|
27
|
-
this.element.dispatchEvent(new CustomEvent("fs:pagination:change", { bubbles: true, detail: { page:
|
|
28
|
+
this.element.dispatchEvent(new CustomEvent("fs:pagination:change", { bubbles: true, detail: { page: bounded, total: this.total } }));
|
|
28
29
|
}
|
|
29
30
|
_render() {
|
|
30
31
|
this.element.querySelectorAll("[data-fs-page]").forEach((item) => {
|
|
31
32
|
const active = Number(item.getAttribute("data-fs-page")) === this.current;
|
|
32
33
|
item.classList.toggle("is-active", active);
|
|
33
|
-
item.setAttribute("aria-current",
|
|
34
|
+
if (active) item.setAttribute("aria-current", "page");
|
|
35
|
+
else item.removeAttribute("aria-current");
|
|
34
36
|
});
|
|
35
37
|
}
|
|
38
|
+
_normalizePage(value, fallback) {
|
|
39
|
+
const parsed = Number(value);
|
|
40
|
+
return Number.isFinite(parsed) && parsed >= 1 ? Math.floor(parsed) : fallback;
|
|
41
|
+
}
|
|
36
42
|
dispose() { this.element.removeEventListener("click", this._click); instances.delete(this.element); }
|
|
37
43
|
}
|
|
38
44
|
autoInit("pagination", Pagination);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { computePosition, applyPosition, watchPosition } from "./core/positioning.js";
|
|
2
2
|
import { onClickOutside } from "./core/overlay.js";
|
|
3
3
|
import { onEscapeKey } from "./core/focus.js";
|
|
4
|
+
import { applyThemeContext } from "./core/theme.js";
|
|
4
5
|
import { autoInit, createInstanceRegistry } from "./core/register.js";
|
|
5
6
|
|
|
6
7
|
const instances = createInstanceRegistry();
|
|
@@ -132,12 +133,7 @@ export class Popover {
|
|
|
132
133
|
if (this.isOpen) return;
|
|
133
134
|
this.isOpen = true;
|
|
134
135
|
|
|
135
|
-
|
|
136
|
-
if (theme) {
|
|
137
|
-
this.popoverEl.setAttribute("data-theme", theme);
|
|
138
|
-
} else {
|
|
139
|
-
this.popoverEl.removeAttribute("data-theme");
|
|
140
|
-
}
|
|
136
|
+
applyThemeContext(this.popoverEl, this.triggerEl);
|
|
141
137
|
|
|
142
138
|
this.popoverEl.classList.add("is-open");
|
|
143
139
|
|
|
@@ -37,6 +37,10 @@ export class Select {
|
|
|
37
37
|
return instances.get(el);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
get isOpen() {
|
|
41
|
+
return this.dropdown?.isOpen ?? false;
|
|
42
|
+
}
|
|
43
|
+
|
|
40
44
|
_buildMarkup() {
|
|
41
45
|
const wrapper = document.createElement("div");
|
|
42
46
|
wrapper.className = "fs-dropdown fs-form-select-dropdown";
|
|
@@ -6,7 +6,7 @@ let idCounter = 0;
|
|
|
6
6
|
export class Tabs {
|
|
7
7
|
constructor(tablistEl) {
|
|
8
8
|
this.tablistEl = tablistEl;
|
|
9
|
-
this.tabs = Array.from(tablistEl.querySelectorAll(".fs-nav-link"));
|
|
9
|
+
this.tabs = Array.from(tablistEl.querySelectorAll(".fs-nav-link, [role=tab], [data-fs-tab-target]"));
|
|
10
10
|
this.manualActivation = tablistEl.getAttribute("data-tabs-activation") === "manual";
|
|
11
11
|
|
|
12
12
|
tablistEl.setAttribute("role", "tablist");
|
|
@@ -21,7 +21,7 @@ export class Tabs {
|
|
|
21
21
|
tab.setAttribute("aria-selected", String(isActive));
|
|
22
22
|
tab.setAttribute("tabindex", isActive ? "0" : "-1");
|
|
23
23
|
|
|
24
|
-
const paneSelector = tab.getAttribute("data-fs-target");
|
|
24
|
+
const paneSelector = tab.getAttribute("data-fs-target") || tab.getAttribute("data-fs-tab-target");
|
|
25
25
|
const pane = paneSelector ? document.querySelector(paneSelector) : null;
|
|
26
26
|
|
|
27
27
|
if (pane) {
|
|
@@ -50,7 +50,7 @@ export class Tabs {
|
|
|
50
50
|
static dispose(el) { return this.getInstance(el)?.dispose(); }
|
|
51
51
|
|
|
52
52
|
_handleClick(event) {
|
|
53
|
-
const tab = event.target.closest(".fs-nav-link:not(.is-disabled)");
|
|
53
|
+
const tab = event.target.closest(".fs-nav-link:not(.is-disabled), [role=tab]:not(.is-disabled), [data-fs-tab-target]:not(.is-disabled)");
|
|
54
54
|
if (!tab || !this.tabs.includes(tab)) return;
|
|
55
55
|
this.show(tab);
|
|
56
56
|
}
|
|
@@ -84,7 +84,7 @@ export class Tabs {
|
|
|
84
84
|
otherTab.setAttribute("aria-selected", String(isActive));
|
|
85
85
|
otherTab.setAttribute("tabindex", isActive ? "0" : "-1");
|
|
86
86
|
|
|
87
|
-
const paneSelector = otherTab.getAttribute("data-fs-target");
|
|
87
|
+
const paneSelector = otherTab.getAttribute("data-fs-target") || otherTab.getAttribute("data-fs-tab-target");
|
|
88
88
|
const pane = paneSelector ? document.querySelector(paneSelector) : null;
|
|
89
89
|
pane?.classList.toggle("is-active", isActive);
|
|
90
90
|
if (pane) pane.hidden = !isActive;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { computePosition, applyPosition, watchPosition } from "./core/positioning.js";
|
|
2
2
|
import { onEscapeKey } from "./core/focus.js";
|
|
3
|
+
import { applyThemeContext } from "./core/theme.js";
|
|
3
4
|
import { autoInit, createInstanceRegistry } from "./core/register.js";
|
|
4
5
|
|
|
5
6
|
const instances = createInstanceRegistry();
|
|
@@ -137,12 +138,7 @@ export class Tooltip {
|
|
|
137
138
|
openInstance = this;
|
|
138
139
|
this.isOpen = true;
|
|
139
140
|
|
|
140
|
-
|
|
141
|
-
if (theme) {
|
|
142
|
-
this.tooltipEl.setAttribute("data-theme", theme);
|
|
143
|
-
} else {
|
|
144
|
-
this.tooltipEl.removeAttribute("data-theme");
|
|
145
|
-
}
|
|
141
|
+
applyThemeContext(this.tooltipEl, this.referenceEl);
|
|
146
142
|
|
|
147
143
|
this.tooltipEl.classList.add("is-open");
|
|
148
144
|
|
|
@@ -1,96 +1,89 @@
|
|
|
1
|
-
|
|
1
|
+
@forward "../../../fokus-core/scss/settings/utilities";
|
|
2
2
|
@use "sass:map";
|
|
3
|
-
@use "
|
|
4
|
-
@use "
|
|
3
|
+
@use "../../../fokus-core/scss/settings" as *;
|
|
4
|
+
@use "../../../fokus-core/scss/tools" as *;
|
|
5
5
|
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
$
|
|
12
|
-
$
|
|
6
|
+
// API declarativa inspirada na Utility API do Bootstrap. A API aceita um
|
|
7
|
+
// subconjunto estático e previsível: valores oficiais, sem JIT e sem classes
|
|
8
|
+
// arbitrárias. Categorias históricas permanecem em seus módulos próprios para
|
|
9
|
+
// preservar aliases e a ordem do CSS público.
|
|
10
|
+
@mixin fs-generate-utility($name, $definition) {
|
|
11
|
+
$property: map.get($definition, property);
|
|
12
|
+
$class: map.get($definition, class);
|
|
13
|
+
$values: map.get($definition, values);
|
|
14
|
+
$category: map.get($definition, category);
|
|
15
|
+
$important: map.get($definition, important);
|
|
16
|
+
$responsive: false;
|
|
17
|
+
$print: false;
|
|
18
|
+
$states: ();
|
|
19
|
+
$declaration-property: $property;
|
|
13
20
|
|
|
14
|
-
@
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
@if map.has-key($definition, responsive) { $responsive: map.get($definition, responsive); }
|
|
22
|
+
|
|
23
|
+
@if map.has-key($definition, print) { $print: map.get($definition, print); }
|
|
24
|
+
|
|
25
|
+
@if map.has-key($definition, states) { $states: map.get($definition, states); }
|
|
26
|
+
|
|
27
|
+
@if map.has-key($definition, css-var) and map.get($definition, css-var) {
|
|
28
|
+
$declaration-property: --#{map.get($definition, css-variable-name)};
|
|
20
29
|
}
|
|
30
|
+
$suffix: "";
|
|
21
31
|
|
|
22
|
-
@if
|
|
23
|
-
|
|
24
|
-
.fs-u-m-#{$name} { margin: $value !important; }
|
|
25
|
-
.fs-u-mx-#{$name} { margin-inline: $value !important; }
|
|
26
|
-
.fs-u-my-#{$name} { margin-block: $value !important; }
|
|
27
|
-
.fs-u-p-#{$name} { padding: $value !important; }
|
|
28
|
-
.fs-u-px-#{$name} { padding-inline: $value !important; }
|
|
29
|
-
.fs-u-py-#{$name} { padding-block: $value !important; }
|
|
30
|
-
}
|
|
32
|
+
@if $important and fs-feature-enabled(important-utilities, true) {
|
|
33
|
+
$suffix: " !important";
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
@
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
.fs-u-h-#{$name} { height: $value !important; }
|
|
36
|
+
@each $key, $value in $values {
|
|
37
|
+
.fs-u-#{$class}-#{$key} {
|
|
38
|
+
#{$declaration-property}: #{$value}#{$suffix};
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
@if fs-
|
|
42
|
+
@if $responsive and fs-feature-enabled(responsive, true) {
|
|
41
43
|
@each $breakpoint, $min in $breakpoints {
|
|
44
|
+
$infix: breakpoint-infix($breakpoint);
|
|
45
|
+
|
|
42
46
|
@if $min != 0 {
|
|
43
|
-
@media
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
@include media-breakpoint-up($breakpoint) {
|
|
48
|
+
@each $key, $value in $values {
|
|
49
|
+
.fs-u-#{$class}#{$infix}-#{$key} {
|
|
50
|
+
#{$declaration-property}: #{$value}#{$suffix};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
47
53
|
}
|
|
48
54
|
}
|
|
49
55
|
}
|
|
50
56
|
}
|
|
51
57
|
|
|
52
|
-
@
|
|
53
|
-
@each $
|
|
54
|
-
.fs-u
|
|
58
|
+
@each $state, $pseudo in $states {
|
|
59
|
+
@each $key, $value in $values {
|
|
60
|
+
.fs-u-#{$class}-#{$key}-#{$state}:#{$pseudo} {
|
|
61
|
+
#{$declaration-property}: #{$value}#{$suffix};
|
|
62
|
+
}
|
|
55
63
|
}
|
|
56
64
|
}
|
|
57
65
|
|
|
58
|
-
@if fs-
|
|
59
|
-
@
|
|
60
|
-
|
|
66
|
+
@if $print and fs-feature-enabled(print, true) {
|
|
67
|
+
@media print {
|
|
68
|
+
@each $key, $value in $values {
|
|
69
|
+
.fs-u-#{$class}-#{$key}-print {
|
|
70
|
+
#{$declaration-property}: #{$value}#{$suffix};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
61
73
|
}
|
|
62
|
-
.fs-u-flex-wrap { flex-wrap: wrap !important; }
|
|
63
|
-
.fs-u-flex-nowrap { flex-wrap: nowrap !important; }
|
|
64
|
-
.fs-u-justify-start { justify-content: flex-start !important; }
|
|
65
|
-
.fs-u-justify-center { justify-content: center !important; }
|
|
66
|
-
.fs-u-justify-end { justify-content: flex-end !important; }
|
|
67
|
-
.fs-u-justify-between { justify-content: space-between !important; }
|
|
68
|
-
.fs-u-align-start { align-items: flex-start !important; }
|
|
69
|
-
.fs-u-align-center { align-items: center !important; }
|
|
70
|
-
.fs-u-align-end { align-items: flex-end !important; }
|
|
71
74
|
}
|
|
75
|
+
}
|
|
72
76
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
.fs-u-row-gap-#{$name} { row-gap: $value !important; }
|
|
77
|
-
.fs-u-column-gap-#{$name} { column-gap: $value !important; }
|
|
78
|
-
}
|
|
79
|
-
}
|
|
77
|
+
@mixin fs-generate-utilities($utilities: $fs-utilities) {
|
|
78
|
+
@each $name, $definition in $utilities {
|
|
79
|
+
$category: utilities;
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
.fs-u-text-end { text-align: end !important; }
|
|
85
|
-
.fs-u-text-wrap { white-space: normal !important; }
|
|
86
|
-
.fs-u-text-nowrap { white-space: nowrap !important; }
|
|
87
|
-
.fs-u-fw-normal { font-weight: 400 !important; }
|
|
88
|
-
.fs-u-fw-semibold { font-weight: 600 !important; }
|
|
89
|
-
.fs-u-fw-bold { font-weight: 700 !important; }
|
|
90
|
-
}
|
|
81
|
+
@if map.has-key($definition, category) {
|
|
82
|
+
$category: map.get($definition, category);
|
|
83
|
+
}
|
|
91
84
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
85
|
+
@if fs-config-list-enabled($category, $name) {
|
|
86
|
+
@include fs-generate-utility($name, $definition);
|
|
87
|
+
}
|
|
95
88
|
}
|
|
96
89
|
}
|