@pine-ds/core 4.9.1 → 4.11.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/components/pds-checkbox2.js +1 -1
- package/components/pds-chip2.js +1 -1
- package/components/pds-combobox.js +2 -2
- package/components/pds-datepicker.js +1 -1
- package/components/pds-dropdown-menu.js +1 -1
- package/components/truncation-tooltip.js +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/pds-checkbox.cjs.entry.js +1 -1
- package/dist/cjs/pds-chip.cjs.entry.js +5 -3
- package/dist/cjs/pds-combobox.cjs.entry.js +101 -15
- package/dist/cjs/pds-copytext.cjs.entry.js +1 -1
- package/dist/cjs/pds-datepicker.cjs.entry.js +253 -75
- package/dist/cjs/pds-dropdown-menu.cjs.entry.js +58 -46
- package/dist/cjs/pds-table-cell.cjs.entry.js +1 -1
- package/dist/cjs/pds-text.cjs.entry.js +1 -1
- package/dist/cjs/pine-core.cjs.js +1 -1
- package/dist/cjs/{truncation-tooltip-DdB78RIe.js → truncation-tooltip-Bikp7n9I.js} +1 -1
- package/dist/collection/components/pds-checkbox/pds-checkbox.css +20 -14
- package/dist/collection/components/pds-chip/pds-chip.js +4 -2
- package/dist/collection/components/pds-combobox/grouped-options-panel.js +39 -1
- package/dist/collection/components/pds-combobox/pds-combobox.css +37 -1
- package/dist/collection/components/pds-combobox/pds-combobox.js +62 -14
- package/dist/collection/components/pds-combobox/stories/pds-combobox.stories.js +48 -0
- package/dist/collection/components/pds-datepicker/date-utils.js +42 -0
- package/dist/collection/components/pds-datepicker/pds-datepicker.js +163 -64
- package/dist/collection/components/pds-datepicker/stories/pds-datepicker.stories.js +11 -0
- package/dist/collection/components/pds-datepicker/value.js +76 -18
- package/dist/collection/components/pds-dropdown-menu/pds-dropdown-menu.js +64 -46
- package/dist/collection/utils/truncation-tooltip.js +1 -1
- package/dist/docs.json +42 -7
- package/dist/esm/loader.js +1 -1
- package/dist/esm/pds-checkbox.entry.js +1 -1
- package/dist/esm/pds-chip.entry.js +5 -3
- package/dist/esm/pds-combobox.entry.js +101 -15
- package/dist/esm/pds-copytext.entry.js +1 -1
- package/dist/esm/pds-datepicker.entry.js +253 -75
- package/dist/esm/pds-dropdown-menu.entry.js +58 -46
- package/dist/esm/pds-table-cell.entry.js +1 -1
- package/dist/esm/pds-text.entry.js +1 -1
- package/dist/esm/pine-core.js +1 -1
- package/dist/esm/{truncation-tooltip-CRSdYzpA.js → truncation-tooltip-DBcPipr9.js} +1 -1
- package/dist/esm-es5/loader.js +1 -1
- package/dist/esm-es5/pds-checkbox.entry.js +1 -1
- package/dist/esm-es5/pds-chip.entry.js +1 -1
- package/dist/esm-es5/pds-combobox.entry.js +1 -1
- package/dist/esm-es5/pds-copytext.entry.js +1 -1
- package/dist/esm-es5/pds-datepicker.entry.js +1 -1
- package/dist/esm-es5/pds-dropdown-menu.entry.js +1 -1
- package/dist/esm-es5/pds-table-cell.entry.js +1 -1
- package/dist/esm-es5/pds-text.entry.js +1 -1
- package/dist/esm-es5/pine-core.js +1 -1
- package/dist/esm-es5/truncation-tooltip-DBcPipr9.js +1 -0
- package/dist/pine-core/p-0b7b3fc2.system.entry.js +3 -0
- package/dist/pine-core/{p-9a776443.entry.js → p-124a0a24.entry.js} +1 -1
- package/dist/pine-core/p-128c04e7.entry.js +1 -0
- package/dist/pine-core/p-3359a935.entry.js +1 -0
- package/dist/pine-core/{p-3d34b967.system.entry.js → p-3a1dc752.system.entry.js} +1 -1
- package/dist/pine-core/{p-20079552.system.entry.js → p-639acef8.system.entry.js} +1 -1
- package/dist/pine-core/{p-beb7a430.entry.js → p-97356d4b.entry.js} +1 -1
- package/dist/pine-core/p-BZVUD2on.js +1 -0
- package/dist/pine-core/p-Dj-z_ugg.system.js +1 -0
- package/dist/pine-core/p-XK45dzWR.system.js +1 -1
- package/dist/pine-core/{p-4d1a8803.entry.js → p-afc2f165.entry.js} +1 -1
- package/dist/pine-core/p-c6a9e62c.system.entry.js +1 -0
- package/dist/pine-core/p-d4968f9d.system.entry.js +1 -0
- package/dist/pine-core/p-da8b864a.entry.js +2 -0
- package/dist/pine-core/p-dd7ded84.entry.js +1 -0
- package/dist/pine-core/p-de455073.system.entry.js +1 -0
- package/dist/pine-core/{p-1cc39908.system.entry.js → p-eb56af4a.system.entry.js} +1 -1
- package/dist/pine-core/p-f354dbac.entry.js +1 -0
- package/dist/pine-core/{p-93fd7dd8.system.entry.js → p-f834cb07.system.entry.js} +1 -1
- package/dist/pine-core/pine-core.esm.js +1 -1
- package/dist/types/components/pds-combobox/combobox-interface.d.ts +26 -0
- package/dist/types/components/pds-combobox/grouped-options-panel.d.ts +17 -0
- package/dist/types/components/pds-combobox/pds-combobox.d.ts +12 -2
- package/dist/types/components/pds-datepicker/date-utils.d.ts +19 -0
- package/dist/types/components/pds-datepicker/datepicker-interface.d.ts +2 -1
- package/dist/types/components/pds-datepicker/pds-datepicker.d.ts +30 -5
- package/dist/types/components/pds-datepicker/value.d.ts +14 -2
- package/dist/types/components/pds-dropdown-menu/pds-dropdown-menu.d.ts +4 -0
- package/dist/types/components.d.ts +25 -10
- package/dist/types/utils/truncation-tooltip.d.ts +1 -1
- package/dist/vscode.html-data.json +11 -4
- package/hydrate/index.js +422 -142
- package/hydrate/index.mjs +422 -142
- package/package.json +2 -2
- package/dist/esm-es5/truncation-tooltip-CRSdYzpA.js +0 -1
- package/dist/pine-core/p-37718637.entry.js +0 -1
- package/dist/pine-core/p-383d84f4.entry.js +0 -2
- package/dist/pine-core/p-3e30a761.entry.js +0 -1
- package/dist/pine-core/p-54f1e86f.system.entry.js +0 -1
- package/dist/pine-core/p-73efb1ed.system.entry.js +0 -1
- package/dist/pine-core/p-8436cbfb.entry.js +0 -1
- package/dist/pine-core/p-DQyF6v9Q.system.js +0 -1
- package/dist/pine-core/p-DwQ5Tjis.js +0 -1
- package/dist/pine-core/p-b3d4f97a.system.entry.js +0 -3
- package/dist/pine-core/p-d47b2a6a.system.entry.js +0 -1
- package/dist/pine-core/p-f018af99.entry.js +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseIsoDate, toIsoDate } from "./date-utils";
|
|
1
|
+
import { MONTH_DAY_YEAR, dateFormatter, inMonthDayYear, parseIsoDate, parseMonthDay, toIsoDate } from "./date-utils";
|
|
2
2
|
const DATE_TIME = /^(\d{4}-\d{2}-\d{2})(?:[T ](\d{2}):(\d{2}))?$/;
|
|
3
3
|
/** Parses `YYYY-MM-DD`, `YYYY-MM-DDTHH:mm` or `YYYY-MM-DD HH:mm`. */
|
|
4
4
|
export function parseDateTime(text) {
|
|
@@ -47,32 +47,50 @@ const wordsCache = new Map();
|
|
|
47
47
|
function localeWords(locale) {
|
|
48
48
|
let words = wordsCache.get(locale);
|
|
49
49
|
if (!words) {
|
|
50
|
-
const month = (style) => {
|
|
51
|
-
const format = new Intl.DateTimeFormat(locale, {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
const month = (style, withDay) => {
|
|
51
|
+
const format = new Intl.DateTimeFormat(locale, Object.assign(Object.assign({ month: style }, (withDay ? { day: 'numeric' } : {})), { timeZone: 'UTC', calendar: 'gregory' }));
|
|
52
|
+
return Array.from({ length: 12 }, (_, m) => {
|
|
53
|
+
const date = new Date(Date.UTC(2023, m, 1));
|
|
54
|
+
if (!withDay)
|
|
55
|
+
return format.format(date);
|
|
56
|
+
const parts = format.formatToParts(date);
|
|
57
|
+
const index = parts.findIndex((part) => part.type === 'month');
|
|
58
|
+
if (index === -1)
|
|
59
|
+
return format.format(date);
|
|
60
|
+
// CJK locales split a counter word, as Japanese `月`, into its own literal right after the
|
|
61
|
+
// month; a literal with no space is that word, not the separator before the day.
|
|
62
|
+
const suffix = parts[index + 1];
|
|
63
|
+
return (suffix === null || suffix === void 0 ? void 0 : suffix.type) === 'literal' && !/\s/.test(suffix.value) ? parts[index].value + suffix.value : parts[index].value;
|
|
55
64
|
});
|
|
56
|
-
return Array.from({ length: 12 }, (_, m) => format.format(new Date(Date.UTC(2023, m, 1))));
|
|
57
65
|
};
|
|
58
66
|
const clock = clockFor(locale);
|
|
59
|
-
words = {
|
|
67
|
+
words = {
|
|
68
|
+
months: month('long', false),
|
|
69
|
+
shortMonths: month('short', false),
|
|
70
|
+
monthsWithDay: month('long', true),
|
|
71
|
+
shortMonthsWithDay: month('short', true),
|
|
72
|
+
am: clock.am,
|
|
73
|
+
pm: clock.pm,
|
|
74
|
+
};
|
|
60
75
|
wordsCache.set(locale, words);
|
|
61
76
|
}
|
|
62
77
|
return words;
|
|
63
78
|
}
|
|
79
|
+
/** Whether a format's tokens (see `tokensOnly`) have a day, so a month name should inflect for one. */
|
|
80
|
+
const hasDayToken = (tokens) => /D/.test(tokens);
|
|
64
81
|
/** Writes a date in a format of tokens (see `FORMAT_PARTS`), with month names and AM/PM in the locale. */
|
|
65
82
|
export function formatWith(value, format, locale = 'en-US') {
|
|
66
83
|
var _a;
|
|
67
84
|
const words = localeWords(locale);
|
|
85
|
+
const withDay = hasDayToken(tokensOnly(format));
|
|
68
86
|
const [hours, minutes] = splitTime((_a = value.time) !== null && _a !== void 0 ? _a : '00:00');
|
|
69
87
|
const hour12 = hours % 12 || 12;
|
|
70
88
|
const month = value.date.getUTCMonth();
|
|
71
89
|
const period = hours < 12 ? words.am : words.pm;
|
|
72
90
|
const parts = {
|
|
73
91
|
YYYY: pad(value.date.getUTCFullYear(), 4),
|
|
74
|
-
MMMM: words.months[month],
|
|
75
|
-
MMM: words.shortMonths[month],
|
|
92
|
+
MMMM: withDay ? words.monthsWithDay[month] : words.months[month],
|
|
93
|
+
MMM: withDay ? words.shortMonthsWithDay[month] : words.shortMonths[month],
|
|
76
94
|
MM: pad(month + 1),
|
|
77
95
|
M: String(month + 1),
|
|
78
96
|
DD: pad(value.date.getUTCDate()),
|
|
@@ -94,17 +112,23 @@ const alternatives = (words) => `(${words.map(escapeRegExp).join('|')})`;
|
|
|
94
112
|
/**
|
|
95
113
|
* Reads typed text in a format of tokens, as written by `formatWith`. Leading zeros are optional,
|
|
96
114
|
* spaces are loose, and month names and AM/PM are matched without regard to case, in the locale or
|
|
97
|
-
* in English.
|
|
115
|
+
* in English. A format without `YYYY` reads into `year`, and without `year` it reads nothing.
|
|
116
|
+
* Returns `null` for anything that isn't a real date and time in that format.
|
|
98
117
|
*/
|
|
99
|
-
export function parseWith(text, format, locale = 'en-US') {
|
|
118
|
+
export function parseWith(text, format, locale = 'en-US', year) {
|
|
100
119
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
101
120
|
const words = localeWords(locale);
|
|
102
121
|
const english = localeWords('en-US');
|
|
122
|
+
const withDay = hasDayToken(tokensOnly(format));
|
|
123
|
+
const months = withDay ? words.monthsWithDay : words.months;
|
|
124
|
+
const shortMonths = withDay ? words.shortMonthsWithDay : words.shortMonths;
|
|
125
|
+
const englishMonths = withDay ? english.monthsWithDay : english.months;
|
|
126
|
+
const englishShortMonths = withDay ? english.shortMonthsWithDay : english.shortMonths;
|
|
103
127
|
const periods = [words.am, words.pm, english.am, english.pm];
|
|
104
128
|
const patterns = {
|
|
105
129
|
YYYY: '(\\d{4})',
|
|
106
|
-
MMMM: alternatives([...
|
|
107
|
-
MMM: alternatives([...
|
|
130
|
+
MMMM: alternatives([...months, ...englishMonths]),
|
|
131
|
+
MMM: alternatives([...shortMonths, ...englishShortMonths]),
|
|
108
132
|
MM: '(\\d{1,2})',
|
|
109
133
|
M: '(\\d{1,2})',
|
|
110
134
|
DD: '(\\d{1,2})',
|
|
@@ -151,7 +175,7 @@ export function parseWith(text, format, locale = 'en-US') {
|
|
|
151
175
|
const monthIndex = (name, long) => {
|
|
152
176
|
if (name === undefined)
|
|
153
177
|
return undefined;
|
|
154
|
-
const lists = long ? [
|
|
178
|
+
const lists = long ? [months, englishMonths] : [shortMonths, englishShortMonths];
|
|
155
179
|
for (const list of lists) {
|
|
156
180
|
const index = list.findIndex((candidate) => candidate.toLowerCase() === name.toLowerCase());
|
|
157
181
|
if (index >= 0)
|
|
@@ -159,12 +183,12 @@ export function parseWith(text, format, locale = 'en-US') {
|
|
|
159
183
|
}
|
|
160
184
|
return undefined;
|
|
161
185
|
};
|
|
162
|
-
const
|
|
186
|
+
const readYear = read.YYYY ? Number(read.YYYY) : year;
|
|
163
187
|
const month = (_b = (_a = monthIndex(read.MMMM, true)) !== null && _a !== void 0 ? _a : monthIndex(read.MMM, false)) !== null && _b !== void 0 ? _b : Number((_c = read.MM) !== null && _c !== void 0 ? _c : read.M);
|
|
164
188
|
const day = Number((_d = read.DD) !== null && _d !== void 0 ? _d : read.D);
|
|
165
|
-
if (
|
|
189
|
+
if (readYear === undefined || !month || !day)
|
|
166
190
|
return null;
|
|
167
|
-
const date = parseIsoDate(`${pad(
|
|
191
|
+
const date = parseIsoDate(`${pad(readYear, 4)}-${pad(month)}-${pad(day)}`);
|
|
168
192
|
if (!date)
|
|
169
193
|
return null;
|
|
170
194
|
const hourText = (_g = (_f = (_e = read.HH) !== null && _e !== void 0 ? _e : read.H) !== null && _f !== void 0 ? _f : read.hh) !== null && _g !== void 0 ? _g : read.h;
|
|
@@ -186,6 +210,40 @@ export function parseFieldText(text, format = DEFAULT_FORMAT, locale = 'en-US')
|
|
|
186
210
|
var _a;
|
|
187
211
|
return (_a = parseWith(text, format, locale)) !== null && _a !== void 0 ? _a : parseDateTime(text);
|
|
188
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* The locale's way of writing a month and day, as a format of tokens: `MMMM D` in English, `D MMMM`
|
|
215
|
+
* in French, `M月D日` in Japanese, or with `numeric`, `M/D` and `D/M`.
|
|
216
|
+
*/
|
|
217
|
+
export function monthDayFormat(locale = 'en-US', month = 'long') {
|
|
218
|
+
const parts = dateFormatter(locale, { month, day: 'numeric' }).formatToParts(new Date(Date.UTC(MONTH_DAY_YEAR, 11, 31)));
|
|
219
|
+
return parts
|
|
220
|
+
.map((part) => {
|
|
221
|
+
// Some locales write even the long month in digits, as Japanese `12月31日`.
|
|
222
|
+
if (part.type === 'month')
|
|
223
|
+
return /^\d+$/.test(part.value) ? 'M' : 'MMMM';
|
|
224
|
+
if (part.type === 'day')
|
|
225
|
+
return 'D';
|
|
226
|
+
// Bracketed, spaces included, only when it has letters a token could be read from, as
|
|
227
|
+
// Spanish `D[ de ]MMMM`.
|
|
228
|
+
return /[A-Za-z]/.test(part.value) ? `[${part.value}]` : part.value;
|
|
229
|
+
})
|
|
230
|
+
.join('');
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Reads a typed month and day, into month-day mode's year: in the format, then as the locale writes
|
|
234
|
+
* it with a month name or in numbers, with the English order too, then as `--MM-DD` or a full ISO
|
|
235
|
+
* date, whose year is dropped.
|
|
236
|
+
*/
|
|
237
|
+
export function parseMonthDayText(text, format, locale = 'en-US') {
|
|
238
|
+
const formats = [format, monthDayFormat(locale), monthDayFormat(locale, 'numeric'), 'MMMM D', 'D MMMM', 'MMM D', 'D MMM'];
|
|
239
|
+
for (const candidate of formats) {
|
|
240
|
+
const read = parseWith(text, candidate, locale, MONTH_DAY_YEAR);
|
|
241
|
+
if (read)
|
|
242
|
+
return { date: inMonthDayYear(read.date), time: null };
|
|
243
|
+
}
|
|
244
|
+
const date = parseMonthDay(text);
|
|
245
|
+
return date ? { date, time: null } : null;
|
|
246
|
+
}
|
|
189
247
|
/**
|
|
190
248
|
* Parses a typed range: two dates (with times, optionally) joined by a dash or `to` with spaces
|
|
191
249
|
* around it, as `2026-09-13 – 2026-10-13`, or by `/` when the format doesn't use slashes. The
|
|
@@ -107,7 +107,7 @@ export class PdsDropdownMenu {
|
|
|
107
107
|
this.currentFocusIndex = -1;
|
|
108
108
|
// Return focus to trigger
|
|
109
109
|
if (this.isFocusInMenu()) {
|
|
110
|
-
this.
|
|
110
|
+
this.focusTrigger();
|
|
111
111
|
}
|
|
112
112
|
};
|
|
113
113
|
// Handle click on the trigger element
|
|
@@ -158,6 +158,14 @@ export class PdsDropdownMenu {
|
|
|
158
158
|
const focused = this.host.getRootNode().activeElement;
|
|
159
159
|
return focused != null && this.host.contains(focused);
|
|
160
160
|
}
|
|
161
|
+
// A trigger whose host can't take focus itself, such as pds-chip, passes it to the control inside.
|
|
162
|
+
focusTrigger() {
|
|
163
|
+
var _a, _b;
|
|
164
|
+
this.triggerEl.focus();
|
|
165
|
+
if (this.host.getRootNode().activeElement === this.triggerEl)
|
|
166
|
+
return;
|
|
167
|
+
(_b = (_a = this.triggerEl.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('button, [href], [tabindex]:not([tabindex="-1"])')) === null || _b === void 0 ? void 0 : _b.focus();
|
|
168
|
+
}
|
|
161
169
|
// Check if a menu item is disabled (handles both component and raw elements)
|
|
162
170
|
isItemDisabled(item) {
|
|
163
171
|
const tagName = item.tagName.toLowerCase();
|
|
@@ -253,6 +261,23 @@ export class PdsDropdownMenu {
|
|
|
253
261
|
this.focusItemByIndex(prevIndex);
|
|
254
262
|
}
|
|
255
263
|
}
|
|
264
|
+
// Unlike the arrow keys, Tab stops at either end of the menu instead of wrapping.
|
|
265
|
+
adjacentEnabledIndex(fromIndex, step) {
|
|
266
|
+
for (let index = fromIndex + step; index >= 0 && index < this.menuItems.length; index += step) {
|
|
267
|
+
if (!this.isItemDisabled(this.menuItems[index]))
|
|
268
|
+
return index;
|
|
269
|
+
}
|
|
270
|
+
return -1;
|
|
271
|
+
}
|
|
272
|
+
// An enabled item can still refuse focus (hidden, or a link with no href), so each one is tried in turn.
|
|
273
|
+
focusAdjacentItem(fromIndex, step) {
|
|
274
|
+
for (let index = this.adjacentEnabledIndex(fromIndex, step); index !== -1; index = this.adjacentEnabledIndex(index, step)) {
|
|
275
|
+
this.focusItemByIndex(index);
|
|
276
|
+
if (this.getFocusedItemIndex() === index)
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
256
281
|
// Escape is taken in document's capture phase, before a modal around the menu hears it on
|
|
257
282
|
// document and closes too.
|
|
258
283
|
handleEscapeKeyDown(event) {
|
|
@@ -261,6 +286,28 @@ export class PdsDropdownMenu {
|
|
|
261
286
|
event.preventDefault();
|
|
262
287
|
this.closeDropdown();
|
|
263
288
|
}
|
|
289
|
+
// Taken in the capture phase, so a modal or popover around the menu that traps Tab finds the key
|
|
290
|
+
// already handled, or focus back on the trigger with the items hidden.
|
|
291
|
+
handleTabKeyDown(event) {
|
|
292
|
+
if (!this.isOpen || event.key !== 'Tab' || event.defaultPrevented)
|
|
293
|
+
return;
|
|
294
|
+
const currentIndex = this.getFocusedItemIndex();
|
|
295
|
+
if (currentIndex === -1) {
|
|
296
|
+
// The menu sits after the trigger, so Shift+Tab from the trigger leaves it behind. So does Shift+Tab
|
|
297
|
+
// from outside the menu, where focus stays after a mouse open in Safari.
|
|
298
|
+
if (event.shiftKey && (document.activeElement === this.triggerEl || !this.isFocusInMenu()))
|
|
299
|
+
this.closeDropdown();
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (this.focusAdjacentItem(currentIndex, event.shiftKey ? -1 : 1)) {
|
|
303
|
+
event.preventDefault();
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
// Past either end, closing moves focus to the trigger. Shift+Tab stays there; Tab carries on past it.
|
|
307
|
+
if (event.shiftKey)
|
|
308
|
+
event.preventDefault();
|
|
309
|
+
this.closeDropdown();
|
|
310
|
+
}
|
|
264
311
|
// Handle keyboard events for the dropdown
|
|
265
312
|
handleKeyDown(event) {
|
|
266
313
|
if (!this.isOpen)
|
|
@@ -300,54 +347,19 @@ export class PdsDropdownMenu {
|
|
|
300
347
|
}
|
|
301
348
|
}
|
|
302
349
|
break;
|
|
303
|
-
case 'Tab':
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
// If not on first item, prevent default and go to previous item
|
|
310
|
-
event.preventDefault();
|
|
311
|
-
this.focusPreviousItem(); // Use our method that skips disabled items
|
|
312
|
-
}
|
|
313
|
-
// If on first item or no item, let natural tab order move back to trigger
|
|
350
|
+
case 'Tab': {
|
|
351
|
+
// Tab from an item is handled in handleTabKeyDown; from anywhere else it moves into the menu.
|
|
352
|
+
if (event.shiftKey || this.getFocusedItemIndex() !== -1)
|
|
353
|
+
break;
|
|
354
|
+
if (this.focusAdjacentItem(-1, 1)) {
|
|
355
|
+
event.preventDefault();
|
|
314
356
|
}
|
|
315
357
|
else {
|
|
316
|
-
//
|
|
317
|
-
|
|
318
|
-
const isTriggerFocused = activeElement === this.triggerEl;
|
|
319
|
-
const currentIndex = this.getFocusedItemIndex();
|
|
320
|
-
if (isTriggerFocused && this.menuItems.length > 0) {
|
|
321
|
-
// If trigger is focused, move to first non-disabled menu item
|
|
322
|
-
event.preventDefault();
|
|
323
|
-
// Find the first non-disabled item
|
|
324
|
-
let firstFocusableIndex = 0;
|
|
325
|
-
while (firstFocusableIndex < this.menuItems.length && this.isItemDisabled(this.menuItems[firstFocusableIndex])) {
|
|
326
|
-
firstFocusableIndex++;
|
|
327
|
-
}
|
|
328
|
-
if (firstFocusableIndex < this.menuItems.length) {
|
|
329
|
-
this.focusItemByIndex(firstFocusableIndex);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
else if (currentIndex === -1 && this.menuItems.length > 0) {
|
|
333
|
-
// If no menu item is focused, focus the first non-disabled one
|
|
334
|
-
event.preventDefault();
|
|
335
|
-
// Find the first non-disabled item
|
|
336
|
-
let firstFocusableIndex = 0;
|
|
337
|
-
while (firstFocusableIndex < this.menuItems.length && this.isItemDisabled(this.menuItems[firstFocusableIndex])) {
|
|
338
|
-
firstFocusableIndex++;
|
|
339
|
-
}
|
|
340
|
-
if (firstFocusableIndex < this.menuItems.length) {
|
|
341
|
-
this.focusItemByIndex(firstFocusableIndex);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
else if (currentIndex !== -1) {
|
|
345
|
-
// Use our method that skips disabled items
|
|
346
|
-
event.preventDefault();
|
|
347
|
-
this.focusNextItem();
|
|
348
|
-
}
|
|
358
|
+
// With no item to move to, Tab carries on past the menu, as it does past the last item.
|
|
359
|
+
this.closeDropdown();
|
|
349
360
|
}
|
|
350
361
|
break;
|
|
362
|
+
}
|
|
351
363
|
}
|
|
352
364
|
}
|
|
353
365
|
// Handle clicks outside the dropdown to close it
|
|
@@ -357,7 +369,7 @@ export class PdsDropdownMenu {
|
|
|
357
369
|
}
|
|
358
370
|
}
|
|
359
371
|
render() {
|
|
360
|
-
return (h(Host, { key: '
|
|
372
|
+
return (h(Host, { key: 'aa4ce84354cf87638ee46fd7ed69520228b7e656', id: this.componentId }, h("slot", { key: '948800e67d18c8c6cc9e77ef3aeab635c5035453', name: "trigger", onSlotchange: this.handleTriggerSlotChange }), h("pds-box", { key: '3ce2a2879b14aad975cea8ae72fcbf8c3abcf20c', "border-radius": "sm", display: "flex", direction: "column", class: "pds-dropdown-menu--panel is-hidden", shadow: "100", part: "menu-panel" }, h("div", { key: 'ad25fbf4f3c7a24653c229e64fe2ce893a92e4c8', class: { 'pds-dropdown-menu__header': true, 'is-hidden': !this.hasHeader } }, h("slot", { key: '35a5903543e08577997b456bcd312bdd009fd8fc', name: "header", onSlotchange: this.handleHeaderSlotChange })), h("div", { key: 'a9593adacf047918c1d87494f8c951a2a4a5f76f', class: "pds-dropdown-menu__items", role: "menu", "aria-orientation": "vertical" }, h("slot", { key: '8d2def1cc7020608220099f4ced0f44e9838cce8', onSlotchange: this.handleSlotChange })))));
|
|
361
373
|
}
|
|
362
374
|
static get is() { return "pds-dropdown-menu"; }
|
|
363
375
|
static get encapsulation() { return "shadow"; }
|
|
@@ -435,6 +447,12 @@ export class PdsDropdownMenu {
|
|
|
435
447
|
"target": "document",
|
|
436
448
|
"capture": true,
|
|
437
449
|
"passive": false
|
|
450
|
+
}, {
|
|
451
|
+
"name": "keydown",
|
|
452
|
+
"method": "handleTabKeyDown",
|
|
453
|
+
"target": "document",
|
|
454
|
+
"capture": true,
|
|
455
|
+
"passive": false
|
|
438
456
|
}, {
|
|
439
457
|
"name": "keydown",
|
|
440
458
|
"method": "handleKeyDown",
|
|
@@ -2,7 +2,7 @@ import { computePosition, flip, offset, shift, } from "@floating-ui/dom";
|
|
|
2
2
|
import { findPortalTarget, isTopLayerInDocument, onTopLayerClose } from "./portal-target";
|
|
3
3
|
// Controls that clipped text can sit inside that already take focus, so the text adds no tab stop of its own.
|
|
4
4
|
// Not any `[tabindex]`: a focusable scroll region full of chips would show every chip's tooltip at once.
|
|
5
|
-
export const FOCUSABLE_WRAPPER = 'a[href], button, summary, pds-button, pds-link, pds-tab, pds-dropdown-menu-item, [role="option"]';
|
|
5
|
+
export const FOCUSABLE_WRAPPER = 'a[href], button, summary, pds-button, pds-link, pds-tab, pds-dropdown-menu-item, [role="option"], [role="treeitem"]';
|
|
6
6
|
/** CSS class prefix used for truncation tooltip portal elements */
|
|
7
7
|
const TOOLTIP_CLASS = 'pds-truncation-tooltip';
|
|
8
8
|
/**
|
package/dist/docs.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"timestamp": "2026-11-
|
|
2
|
+
"timestamp": "2026-11-09T22:28:16",
|
|
3
3
|
"compiler": {
|
|
4
4
|
"name": "@stencil/core",
|
|
5
5
|
"version": "4.43.5",
|
|
@@ -15253,7 +15253,7 @@
|
|
|
15253
15253
|
"encapsulation": "shadow",
|
|
15254
15254
|
"tag": "pds-datepicker",
|
|
15255
15255
|
"readme": "# pds-datepicker\n\n\n",
|
|
15256
|
-
"docs": "A field for one date, or in range mode a start and end date, with a calendar to pick from.\nThe value is `YYYY-MM-DD`, or `YYYY-MM-DD/YYYY-MM-DD` for a range; with the time picker, each\ndate carries a time, as `YYYY-MM-DDTHH:mm`.",
|
|
15256
|
+
"docs": "A field for one date, or in range mode a start and end date, with a calendar to pick from.\nThe value is `YYYY-MM-DD`, or `YYYY-MM-DD/YYYY-MM-DD` for a range; with the time picker, each\ndate carries a time, as `YYYY-MM-DDTHH:mm`. In month-day mode, for a date that comes round every\nyear such as a birthday, it's a month and day with no year, as `--MM-DD`.",
|
|
15257
15257
|
"docsTags": [
|
|
15258
15258
|
{
|
|
15259
15259
|
"name": "part",
|
|
@@ -15572,7 +15572,7 @@
|
|
|
15572
15572
|
"mutable": false,
|
|
15573
15573
|
"attr": "format",
|
|
15574
15574
|
"reflectToAttr": false,
|
|
15575
|
-
"docs": "How the field shows dates and reads typed ones, as tokens: `YYYY`, `MMMM` or `MMM` (month name,\nin the locale), `MM` or `M` (month), `DD` or `D` (day), and with the time picker `HH` or `H`\n(24-hour), `hh` or `h` (12-hour), `mm` and `A` (AM/PM, in the locale), as `MM/DD/YYYY`,\n`MMM D, YYYY` or `D.M.YYYY h:mm A`. Put literal text in brackets, as `[Due] D/M/YYYY`. The value\nand the change event stay ISO (`YYYY-MM-DD`), whatever the format
|
|
15575
|
+
"docs": "How the field shows dates and reads typed ones, as tokens: `YYYY`, `MMMM` or `MMM` (month name,\nin the locale), `MM` or `M` (month), `DD` or `D` (day), and with the time picker `HH` or `H`\n(24-hour), `hh` or `h` (12-hour), `mm` and `A` (AM/PM, in the locale), as `MM/DD/YYYY`,\n`MMM D, YYYY` or `D.M.YYYY h:mm A`. Put literal text in brackets, as `[Due] D/M/YYYY`. The value\nand the change event stay ISO (`YYYY-MM-DD`; `--MM-DD` with `month-day`), whatever the format.\nTyped ISO dates are always accepted too. With `month-day`, a format without `YYYY` is taken as a\nmonth-and-day format and used as given, rather than reordered for the locale; leave it unset to\nfollow the locale instead.",
|
|
15576
15576
|
"docsTags": [
|
|
15577
15577
|
{
|
|
15578
15578
|
"name": "default",
|
|
@@ -15948,6 +15948,35 @@
|
|
|
15948
15948
|
"getter": false,
|
|
15949
15949
|
"setter": false
|
|
15950
15950
|
},
|
|
15951
|
+
{
|
|
15952
|
+
"name": "monthDay",
|
|
15953
|
+
"type": "boolean",
|
|
15954
|
+
"complexType": {
|
|
15955
|
+
"original": "boolean",
|
|
15956
|
+
"resolved": "boolean",
|
|
15957
|
+
"references": {}
|
|
15958
|
+
},
|
|
15959
|
+
"mutable": false,
|
|
15960
|
+
"attr": "month-day",
|
|
15961
|
+
"reflectToAttr": false,
|
|
15962
|
+
"docs": "Picks a month and day with no year, for a date that comes round every year, such as a birthday.\nThe value is `--MM-DD` (ISO 8601 without a year), the field shows the date as the locale writes\na month and day, and the calendar shows each month without a year or weekdays, with February\nalways having its 29th. Turns off `range`, `time-picker`, `show-dropdowns`, `min` and `max`, and\nhas no effect with `auto-apply`, `presets`, `first-day-of-week`, `min-date-message` or\n`max-date-message`, which all depend on them.",
|
|
15963
|
+
"docsTags": [
|
|
15964
|
+
{
|
|
15965
|
+
"name": "default",
|
|
15966
|
+
"text": "false"
|
|
15967
|
+
}
|
|
15968
|
+
],
|
|
15969
|
+
"default": "false",
|
|
15970
|
+
"values": [
|
|
15971
|
+
{
|
|
15972
|
+
"type": "boolean"
|
|
15973
|
+
}
|
|
15974
|
+
],
|
|
15975
|
+
"optional": false,
|
|
15976
|
+
"required": false,
|
|
15977
|
+
"getter": false,
|
|
15978
|
+
"setter": false
|
|
15979
|
+
},
|
|
15951
15980
|
{
|
|
15952
15981
|
"name": "monthLabel",
|
|
15953
15982
|
"type": "string",
|
|
@@ -16069,7 +16098,7 @@
|
|
|
16069
16098
|
"mutable": false,
|
|
16070
16099
|
"attr": "placeholder",
|
|
16071
16100
|
"reflectToAttr": false,
|
|
16072
|
-
"docs": "Specifies a short hint that describes the expected value of the field. Defaults to the format\nthe field takes, such as `YYYY-MM-DD`.",
|
|
16101
|
+
"docs": "Specifies a short hint that describes the expected value of the field. Defaults to the format\nthe field takes, such as `YYYY-MM-DD`; with `month-day`, to an example date instead, such as\n`December 31`.",
|
|
16073
16102
|
"docsTags": [],
|
|
16074
16103
|
"values": [
|
|
16075
16104
|
{
|
|
@@ -16484,7 +16513,7 @@
|
|
|
16484
16513
|
"mutable": true,
|
|
16485
16514
|
"attr": "value",
|
|
16486
16515
|
"reflectToAttr": false,
|
|
16487
|
-
"docs": "The selected date as `YYYY-MM-DD`, or in range mode the range as `YYYY-MM-DD/YYYY-MM-DD`; with\nthe time picker, each date as `YYYY-MM-DDTHH:mm
|
|
16516
|
+
"docs": "The selected date as `YYYY-MM-DD`, or in range mode the range as `YYYY-MM-DD/YYYY-MM-DD`; with\nthe time picker, each date as `YYYY-MM-DDTHH:mm`; with `month-day`, the month and day with no\nyear, as `--MM-DD` (a full date is read too, as that month and day, and rewritten to it). An\nempty string when nothing is selected.",
|
|
16488
16517
|
"docsTags": [
|
|
16489
16518
|
{
|
|
16490
16519
|
"name": "default",
|
|
@@ -17370,6 +17399,12 @@
|
|
|
17370
17399
|
"capture": true,
|
|
17371
17400
|
"passive": false
|
|
17372
17401
|
},
|
|
17402
|
+
{
|
|
17403
|
+
"event": "keydown",
|
|
17404
|
+
"target": "document",
|
|
17405
|
+
"capture": true,
|
|
17406
|
+
"passive": false
|
|
17407
|
+
},
|
|
17373
17408
|
{
|
|
17374
17409
|
"event": "keydown",
|
|
17375
17410
|
"target": "window",
|
|
@@ -28271,7 +28306,7 @@
|
|
|
28271
28306
|
"path": "src/utils/types.ts"
|
|
28272
28307
|
},
|
|
28273
28308
|
"src/components/pds-combobox/combobox-interface.ts::ComboboxOption": {
|
|
28274
|
-
"declaration": "export interface ComboboxOption {\n id: string | number;\n text: string;\n /**\n * Label of the group this option belongs to. Options sharing a group are rendered together\n * under one group label, where the group first appears, the same as slotted `<optgroup>`\n * markup. With `group-display=\"inline\"`, options without a group are listed before every group;\n * with `flyout` and `drill-in`, options without a group and groups keep the array's order.\n */\n group?: string;\n /**\n * Pine icon name shown beside the group's label (letters, digits and hyphens, for example\n * `hashtag`); URLs and file paths are not accepted. The first option in the group that sets\n * one wins, the same as `data-icon` on a slotted `<optgroup>`.\n */\n groupIcon?: string;\n /**\n * Pine icon name shown before the option's text (letters, digits and hyphens, for example\n * `hashtag`); URLs and file paths are not accepted. The same as `data-icon` on a slotted `<option>`.\n * Once the option is chosen, the `button` trigger shows it too.\n */\n icon?: string;\n /**\n * When `true`, the option stays reachable by the arrow keys and the pointer, so assistive tech\n * announces it as unavailable, but it cannot be selected.\n */\n disabled?: boolean;\n [key: string]: unknown;\n}",
|
|
28309
|
+
"declaration": "export interface ComboboxOption {\n id: string | number;\n text: string;\n /**\n * Label of the group this option belongs to. Options sharing a group are rendered together\n * under one group label, where the group first appears, the same as slotted `<optgroup>`\n * markup. With `group-display=\"inline\"`, options without a group are listed before every group;\n * with `flyout` and `drill-in`, options without a group and groups keep the array's order.\n */\n group?: string;\n /**\n * Pine icon name shown beside the group's label (letters, digits and hyphens, for example\n * `hashtag`); URLs and file paths are not accepted. The first option in the group that sets\n * one wins, the same as `data-icon` on a slotted `<optgroup>`.\n */\n groupIcon?: string;\n /**\n * A small chip after the group's label, such as \"Premium\". `label` is its text; `sentiment` is a\n * `pds-chip` sentiment (`neutral` when unset or unknown); `icon` is a Pine icon name (letters,\n * digits and hyphens, for example `premium`); URLs and file paths are not accepted. The first\n * option in the group that sets one wins, the same as `data-badge`, `data-badge-sentiment` and\n * `data-badge-icon` on a slotted `<optgroup>`. It isn't matched by search, and screen readers\n * read it as part of the group's name. It isn't shown on a drilled-in group's row back.\n */\n groupBadge?: { label: string; sentiment?: string; icon?: string };\n /**\n * Pine icon name shown before the option's text (letters, digits and hyphens, for example\n * `hashtag`); URLs and file paths are not accepted. The same as `data-icon` on a slotted `<option>`.\n * Once the option is chosen, the `button` trigger shows it too.\n */\n icon?: string;\n /**\n * A small chip after the option's text, such as \"New\". `label` is its text; `sentiment` is a\n * `pds-chip` sentiment (`neutral` when unset or unknown); `icon` is a Pine icon name (letters,\n * digits and hyphens, for example `star`); URLs and file paths are not accepted. The same as\n * `data-badge`, `data-badge-sentiment` and `data-badge-icon` on a slotted `<option>`. It isn't\n * shown in the trigger or matched by search, and screen readers read it as part of the option's\n * name. Options that are chips or custom layouts don't show it.\n */\n badge?: { label: string; sentiment?: string; icon?: string };\n /**\n * When `true`, the option stays reachable by the arrow keys and the pointer, so assistive tech\n * announces it as unavailable, but it cannot be selected.\n */\n disabled?: boolean;\n [key: string]: unknown;\n}",
|
|
28275
28310
|
"docstring": "",
|
|
28276
28311
|
"path": "src/components/pds-combobox/combobox-interface.ts"
|
|
28277
28312
|
},
|
|
@@ -28296,7 +28331,7 @@
|
|
|
28296
28331
|
"path": "src/components/pds-datepicker/datepicker-interface.ts"
|
|
28297
28332
|
},
|
|
28298
28333
|
"src/components/pds-datepicker/datepicker-interface.ts::DatepickerChangeEventDetail": {
|
|
28299
|
-
"declaration": "export interface DatepickerChangeEventDetail {\n /**\n * The selected date as `YYYY-MM-DD`, or in range mode the range as `YYYY-MM-DD/YYYY-MM-DD`; with\n * the time picker, each date as `YYYY-MM-DDTHH:mm`. An empty string when cleared.\n */\n value: string;\n /** Range mode: the first day of the range, `YYYY-MM-DD`, or `YYYY-MM-DDTHH:mm` with the time picker. */\n start?: string;\n /** Range mode: the last day of the range, `YYYY-MM-DD`, or `YYYY-MM-DDTHH:mm` with the time picker. */\n end?: string;\n /**\n * Range mode: the key of the preset the range matches, such as `last-30-days`, so a filter can\n * keep the range relative to today.\n */\n preset?: string;\n}",
|
|
28334
|
+
"declaration": "export interface DatepickerChangeEventDetail {\n /**\n * The selected date as `YYYY-MM-DD`, or in range mode the range as `YYYY-MM-DD/YYYY-MM-DD`; with\n * the time picker, each date as `YYYY-MM-DDTHH:mm`; with `month-day`, `--MM-DD`. An empty string\n * when cleared.\n */\n value: string;\n /** Range mode: the first day of the range, `YYYY-MM-DD`, or `YYYY-MM-DDTHH:mm` with the time picker. */\n start?: string;\n /** Range mode: the last day of the range, `YYYY-MM-DD`, or `YYYY-MM-DDTHH:mm` with the time picker. */\n end?: string;\n /**\n * Range mode: the key of the preset the range matches, such as `last-30-days`, so a filter can\n * keep the range relative to today.\n */\n preset?: string;\n}",
|
|
28300
28335
|
"docstring": "",
|
|
28301
28336
|
"path": "src/components/pds-datepicker/datepicker-interface.ts"
|
|
28302
28337
|
},
|