tyrell-components 1.0.0-TC54 → 1.0.0-TC55
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/dist/tyrell.js +1 -1
- package/lib/components/calendar-month.js +17 -4
- package/lib/components/calendar.d.ts +5 -3
- package/lib/components/calendar.js +20 -14
- package/lib/components/copy.js +5 -4
- package/lib/components/date-picker.d.ts +7 -0
- package/lib/components/date-picker.js +25 -0
- package/lib/components/option.js +1 -1
- package/lib/components/scroll-container.js +9 -11
- package/lib/components/select.d.ts +38 -10
- package/lib/components/select.js +112 -51
- package/lib/components/tabs.js +15 -8
- package/lib/styles/calendar-month.js +4 -4
- package/lib/styles/calendar-navigation.d.ts +1 -1
- package/lib/styles/calendar-navigation.js +8 -4
- package/lib/styles/calendar.d.ts +1 -1
- package/lib/styles/calendar.js +1 -0
- package/lib/styles/select-base.js +81 -14
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
package/lib/components/select.js
CHANGED
|
@@ -20,10 +20,10 @@ elementIds.set(element, id);
|
|
|
20
20
|
return id;
|
|
21
21
|
}
|
|
22
22
|
import { REQUIRED_ICON_SVG } from "../utils/icons.js";
|
|
23
|
+
const SEARCH_AUTO_THRESHOLD = 7;
|
|
23
24
|
const CHEVRON_DOWN_SVG = `<svg viewBox="0 0 20 20" fill="currentColor">
|
|
24
25
|
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
|
25
26
|
</svg>`;
|
|
26
|
-
const SEARCH_AUTO_THRESHOLD = 7;
|
|
27
27
|
function slugify(text) {
|
|
28
28
|
return text
|
|
29
29
|
.trim()
|
|
@@ -55,11 +55,12 @@ this._clearable = true;
|
|
|
55
55
|
this._externalSearch = false;
|
|
56
56
|
this._allowCreate = false;
|
|
57
57
|
this._createTransform = "none";
|
|
58
|
-
this.
|
|
58
|
+
this._notSearchable = false;
|
|
59
59
|
this._loading = false;
|
|
60
60
|
this._scrollLockId = null;
|
|
61
61
|
this._size = DEFAULT_SIZE;
|
|
62
62
|
this._availableLabel = "Available";
|
|
63
|
+
this._selectedLabel = "Selected";
|
|
63
64
|
this._noOptionsMessage = "No options available";
|
|
64
65
|
this._state = {
|
|
65
66
|
open: false,
|
|
@@ -185,8 +186,8 @@ break;
|
|
|
185
186
|
case "externalSearch":
|
|
186
187
|
this._externalSearch = newValue;
|
|
187
188
|
break;
|
|
188
|
-
case "
|
|
189
|
-
this.
|
|
189
|
+
case "notSearchable":
|
|
190
|
+
this._notSearchable = newValue;
|
|
190
191
|
if (this.isConnected && this.shadowRoot)
|
|
191
192
|
this.updateSearchVisibility();
|
|
192
193
|
break;
|
|
@@ -207,6 +208,9 @@ break;
|
|
|
207
208
|
case "available-label":
|
|
208
209
|
this._availableLabel = newValue || "Available";
|
|
209
210
|
break;
|
|
211
|
+
case "selected-label":
|
|
212
|
+
this._selectedLabel = newValue || "Selected";
|
|
213
|
+
break;
|
|
210
214
|
case "no-options-message":
|
|
211
215
|
this._noOptionsMessage = newValue || "No options available";
|
|
212
216
|
break;
|
|
@@ -698,6 +702,8 @@ e.stopPropagation();
|
|
|
698
702
|
e.preventDefault();
|
|
699
703
|
}
|
|
700
704
|
handleSearchInput(e) {
|
|
705
|
+
if (this._state.mode === "mobile")
|
|
706
|
+
this.expandMobileSection("available");
|
|
701
707
|
const target = e.target;
|
|
702
708
|
const query = target.value;
|
|
703
709
|
this._state.search = query;
|
|
@@ -730,7 +736,12 @@ switch (e.key) {
|
|
|
730
736
|
case "Escape":
|
|
731
737
|
e.preventDefault();
|
|
732
738
|
e.stopPropagation();
|
|
739
|
+
if (this._state.mode === "mobile") {
|
|
740
|
+
this.closeMobileModal();
|
|
741
|
+
}
|
|
742
|
+
else {
|
|
733
743
|
this.closeDropdown();
|
|
744
|
+
}
|
|
734
745
|
break;
|
|
735
746
|
case "Enter":
|
|
736
747
|
e.preventDefault();
|
|
@@ -746,43 +757,33 @@ break;
|
|
|
746
757
|
case "ArrowUp":
|
|
747
758
|
e.preventDefault();
|
|
748
759
|
e.stopPropagation();
|
|
749
|
-
|
|
750
|
-
if (tagsCount === 0) {
|
|
751
|
-
newIndexUp = -1;
|
|
752
|
-
}
|
|
753
|
-
else if (currentHighlightedIndex === -1) {
|
|
754
|
-
newIndexUp = tagsCount - 1;
|
|
755
|
-
}
|
|
756
|
-
else if (currentHighlightedIndex === 0) {
|
|
757
|
-
newIndexUp = tagsCount - 1;
|
|
758
|
-
}
|
|
759
|
-
else {
|
|
760
|
-
newIndexUp = currentHighlightedIndex - 1;
|
|
761
|
-
}
|
|
760
|
+
const newIndexUp = this.nextEnabledTagIndex(filteredTags, currentHighlightedIndex, -1);
|
|
762
761
|
this._state.highlightedIndex = newIndexUp;
|
|
763
762
|
this.highlightTag(filteredTags, newIndexUp);
|
|
764
763
|
break;
|
|
765
764
|
case "ArrowDown":
|
|
766
765
|
e.preventDefault();
|
|
767
766
|
e.stopPropagation();
|
|
768
|
-
|
|
769
|
-
if (tagsCount === 0) {
|
|
770
|
-
newIndexDown = -1;
|
|
771
|
-
}
|
|
772
|
-
else if (currentHighlightedIndex === -1) {
|
|
773
|
-
newIndexDown = 0;
|
|
774
|
-
}
|
|
775
|
-
else if (currentHighlightedIndex === tagsCount - 1) {
|
|
776
|
-
newIndexDown = 0;
|
|
777
|
-
}
|
|
778
|
-
else {
|
|
779
|
-
newIndexDown = currentHighlightedIndex + 1;
|
|
780
|
-
}
|
|
767
|
+
const newIndexDown = this.nextEnabledTagIndex(filteredTags, currentHighlightedIndex, 1);
|
|
781
768
|
this._state.highlightedIndex = newIndexDown;
|
|
782
769
|
this.highlightTag(filteredTags, newIndexDown);
|
|
783
770
|
break;
|
|
784
771
|
}
|
|
785
772
|
}
|
|
773
|
+
nextEnabledTagIndex(tags, fromIndex, direction) {
|
|
774
|
+
const count = tags.length;
|
|
775
|
+
if (count === 0)
|
|
776
|
+
return -1;
|
|
777
|
+
let index = fromIndex === -1
|
|
778
|
+
? (direction === 1 ? 0 : count - 1)
|
|
779
|
+
: (fromIndex + direction + count) % count;
|
|
780
|
+
for (let i = 0; i < count; i++) {
|
|
781
|
+
if (!tags[index].element.hasAttribute("disabled"))
|
|
782
|
+
return index;
|
|
783
|
+
index = (index + direction + count) % count;
|
|
784
|
+
}
|
|
785
|
+
return -1;
|
|
786
|
+
}
|
|
786
787
|
filterTags(tags, query) {
|
|
787
788
|
if (!query || query.trim() === "") {
|
|
788
789
|
return tags;
|
|
@@ -1098,12 +1099,21 @@ ${CHEVRON_DOWN_SVG}
|
|
|
1098
1099
|
<!-- HEADER (matches dropdown.ts) -->
|
|
1099
1100
|
${searchHeaderHtml}
|
|
1100
1101
|
<!-- BODY: options list with toggleable selected state -->
|
|
1101
|
-
<div class="mobile-body">
|
|
1102
|
+
<div class="mobile-body" data-expanded="available">
|
|
1103
|
+
<!-- SELECTED STRIP (pinned above the list, collapses when empty) -->
|
|
1104
|
+
<div class="mobile-selected-section" data-empty="true">
|
|
1105
|
+
<button class="section-header" type="button" data-section="selected" aria-expanded="false">
|
|
1106
|
+
<span class="section-title">${this._selectedLabel} <span class="section-count">(0)</span></span>
|
|
1107
|
+
${CHEVRON_DOWN_SVG}
|
|
1108
|
+
</button>
|
|
1109
|
+
<div class="section-content mobile-selected-chips"></div>
|
|
1110
|
+
</div>
|
|
1102
1111
|
<!-- AVAILABLE LIST (always visible, takes remaining space) -->
|
|
1103
1112
|
<div class="mobile-available-section" data-empty="false">
|
|
1104
|
-
<
|
|
1113
|
+
<button class="section-header" type="button" data-section="available" aria-expanded="true">
|
|
1105
1114
|
<span class="section-title">${this._availableLabel}</span>
|
|
1106
|
-
|
|
1115
|
+
${CHEVRON_DOWN_SVG}
|
|
1116
|
+
</button>
|
|
1107
1117
|
<div class="section-content dropdown-options-wrapper" id="${listboxId}" role="listbox" ${this._multiple ? 'aria-multiselectable="true"' : ""}>
|
|
1108
1118
|
<slot id="options-slot"></slot>
|
|
1109
1119
|
<div class="empty-state">${this._noOptionsMessage}</div>
|
|
@@ -1126,6 +1136,10 @@ this.setupMobileEventListeners();
|
|
|
1126
1136
|
this.updateSelectionDisplay();
|
|
1127
1137
|
}
|
|
1128
1138
|
setupMobileEventListeners() {
|
|
1139
|
+
if (!this._keyboardHandler) {
|
|
1140
|
+
this._keyboardHandler = this.handleKeyboard.bind(this);
|
|
1141
|
+
document.addEventListener("keydown", this._keyboardHandler);
|
|
1142
|
+
}
|
|
1129
1143
|
const shadow = this.shadowRoot;
|
|
1130
1144
|
const stub = shadow.querySelector(".select-stub");
|
|
1131
1145
|
const optionsSlot = shadow.querySelector("#options-slot");
|
|
@@ -1145,6 +1159,9 @@ optionsSlot.addEventListener("click", (e) => this.handleMobileTagClick(e));
|
|
|
1145
1159
|
if (searchInput) {
|
|
1146
1160
|
searchInput.addEventListener("input", (e) => this.handleSearchInput(e));
|
|
1147
1161
|
}
|
|
1162
|
+
shadow.querySelectorAll(".mobile-body .section-header").forEach((header) => {
|
|
1163
|
+
header.addEventListener("click", () => this.expandMobileSection(header.dataset.section === "selected" ? "selected" : "available"));
|
|
1164
|
+
});
|
|
1148
1165
|
if (closeButton) {
|
|
1149
1166
|
closeButton.addEventListener("click", () => this.closeMobileModal());
|
|
1150
1167
|
}
|
|
@@ -1183,9 +1200,10 @@ updateMobileSelectedState() {
|
|
|
1183
1200
|
if (this._state.mode !== "mobile")
|
|
1184
1201
|
return;
|
|
1185
1202
|
const shadow = this.shadowRoot;
|
|
1203
|
+
this.renderMobileSelectedChips();
|
|
1186
1204
|
const availableSection = shadow.querySelector(".mobile-available-section");
|
|
1187
1205
|
if (availableSection) {
|
|
1188
|
-
const visibleAvailable = this.getTagElements().filter((tag) => !tag.hasAttribute("hidden")).length;
|
|
1206
|
+
const visibleAvailable = this.getTagElements().filter((tag) => !tag.hasAttribute("hidden") && !tag.hasAttribute("selected")).length;
|
|
1189
1207
|
availableSection.setAttribute("data-empty", String(visibleAvailable === 0));
|
|
1190
1208
|
const availableTitleSpan = shadow.querySelector(".mobile-available-section .section-title");
|
|
1191
1209
|
if (availableTitleSpan) {
|
|
@@ -1193,14 +1211,59 @@ availableTitleSpan.textContent = `${this._availableLabel} (${visibleAvailable})`
|
|
|
1193
1211
|
}
|
|
1194
1212
|
}
|
|
1195
1213
|
}
|
|
1214
|
+
expandMobileSection(section) {
|
|
1215
|
+
const shadow = this.shadowRoot;
|
|
1216
|
+
const body = shadow?.querySelector(".mobile-body");
|
|
1217
|
+
if (!body)
|
|
1218
|
+
return;
|
|
1219
|
+
if (section === "selected" && this._state.selectedValues.length === 0)
|
|
1220
|
+
return;
|
|
1221
|
+
body.dataset.expanded = section;
|
|
1222
|
+
shadow.querySelectorAll(".mobile-body .section-header").forEach((header) => {
|
|
1223
|
+
const owns = header.dataset.section === section;
|
|
1224
|
+
header.setAttribute("aria-expanded", String(owns));
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
renderMobileSelectedChips() {
|
|
1228
|
+
const shadow = this.shadowRoot;
|
|
1229
|
+
const section = shadow.querySelector(".mobile-selected-section");
|
|
1230
|
+
const host = shadow.querySelector(".mobile-selected-chips");
|
|
1231
|
+
if (!section || !host)
|
|
1232
|
+
return;
|
|
1233
|
+
const selected = this._state.selectedValues;
|
|
1234
|
+
section.setAttribute("data-empty", String(selected.length === 0));
|
|
1235
|
+
const count = section.querySelector(".section-count");
|
|
1236
|
+
if (count)
|
|
1237
|
+
count.textContent = `(${selected.length})`;
|
|
1238
|
+
if (selected.length === 0)
|
|
1239
|
+
this.expandMobileSection("available");
|
|
1240
|
+
const labelFor = (value) => {
|
|
1241
|
+
const option = this.getTagElements().find((tag) => this.getTagData(tag).value === value);
|
|
1242
|
+
return option ? this.getTagData(option).text : value;
|
|
1243
|
+
};
|
|
1244
|
+
host.textContent = "";
|
|
1245
|
+
for (const value of selected) {
|
|
1246
|
+
const chip = document.createElement("button");
|
|
1247
|
+
chip.type = "button";
|
|
1248
|
+
chip.className = "mobile-selected-chip";
|
|
1249
|
+
chip.dataset.value = value;
|
|
1250
|
+
chip.setAttribute("aria-label", `Remove ${labelFor(value)}`);
|
|
1251
|
+
chip.innerHTML = `<span class="chip-label"></span>${CLEAR_ICON_SVG}`;
|
|
1252
|
+
chip.querySelector(".chip-label").textContent = labelFor(value);
|
|
1253
|
+
chip.addEventListener("click", (e) => {
|
|
1254
|
+
e.preventDefault();
|
|
1255
|
+
e.stopPropagation();
|
|
1256
|
+
this.deselectValue(value);
|
|
1257
|
+
});
|
|
1258
|
+
host.appendChild(chip);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1196
1261
|
shouldShowSearch() {
|
|
1197
1262
|
if (this._externalSearch)
|
|
1198
1263
|
return true;
|
|
1199
1264
|
if (this._allowCreate)
|
|
1200
1265
|
return true;
|
|
1201
|
-
if (this.
|
|
1202
|
-
return true;
|
|
1203
|
-
if (this._searchable === "never")
|
|
1266
|
+
if (this._notSearchable)
|
|
1204
1267
|
return false;
|
|
1205
1268
|
return this.getTagElements().length > SEARCH_AUTO_THRESHOLD;
|
|
1206
1269
|
}
|
|
@@ -1371,11 +1434,11 @@ return this.getProperty("required");
|
|
|
1371
1434
|
set required(value) {
|
|
1372
1435
|
this.setProperty("required", value);
|
|
1373
1436
|
}
|
|
1374
|
-
get
|
|
1375
|
-
return this.getProperty("
|
|
1437
|
+
get notSearchable() {
|
|
1438
|
+
return this.getProperty("notSearchable");
|
|
1376
1439
|
}
|
|
1377
|
-
set
|
|
1378
|
-
this.setProperty("
|
|
1440
|
+
set notSearchable(value) {
|
|
1441
|
+
this.setProperty("notSearchable", value);
|
|
1379
1442
|
}
|
|
1380
1443
|
get externalSearch() {
|
|
1381
1444
|
return this.getProperty("externalSearch");
|
|
@@ -1499,17 +1562,10 @@ default: "none",
|
|
|
1499
1562
|
aliases: { "create-transform": true },
|
|
1500
1563
|
coerce: (v) => (v === "slug" ? "slug" : "none"),
|
|
1501
1564
|
},
|
|
1502
|
-
|
|
1503
|
-
type: "
|
|
1565
|
+
notSearchable: {
|
|
1566
|
+
type: "boolean",
|
|
1504
1567
|
visual: true,
|
|
1505
|
-
default:
|
|
1506
|
-
coerce: (v) => {
|
|
1507
|
-
if (v === true || v === "" || v === "true")
|
|
1508
|
-
return "always";
|
|
1509
|
-
if (v === false || v === "false")
|
|
1510
|
-
return "never";
|
|
1511
|
-
return "auto";
|
|
1512
|
-
},
|
|
1568
|
+
default: false,
|
|
1513
1569
|
},
|
|
1514
1570
|
align: {
|
|
1515
1571
|
type: "string",
|
|
@@ -1552,6 +1608,11 @@ type: "string",
|
|
|
1552
1608
|
visual: true,
|
|
1553
1609
|
default: "Available",
|
|
1554
1610
|
},
|
|
1611
|
+
"selected-label": {
|
|
1612
|
+
type: "string",
|
|
1613
|
+
visual: true,
|
|
1614
|
+
default: "Selected",
|
|
1615
|
+
},
|
|
1555
1616
|
"no-options-message": {
|
|
1556
1617
|
type: "string",
|
|
1557
1618
|
visual: true,
|
package/lib/components/tabs.js
CHANGED
|
@@ -41,6 +41,11 @@ function findTabIndex(tabs, tabId) {
|
|
|
41
41
|
const index = tabs.findIndex(tab => getTabId(tab) === tabId);
|
|
42
42
|
return index >= 0 ? index : undefined;
|
|
43
43
|
}
|
|
44
|
+
function currentActive(el) {
|
|
45
|
+
const tabs = getChildTabs(el);
|
|
46
|
+
const id = getActiveTabId(el, tabs);
|
|
47
|
+
return { id, index: (id ? findTabIndex(tabs, id) : undefined) ?? 0 };
|
|
48
|
+
}
|
|
44
49
|
function getActiveTabId(el, tabs) {
|
|
45
50
|
const activeAttr = el.getAttribute('active');
|
|
46
51
|
if (activeAttr && findTabIndex(tabs, activeAttr) !== undefined) {
|
|
@@ -467,11 +472,12 @@ if (buttons) {
|
|
|
467
472
|
const buttonsHeight = buttons.offsetHeight;
|
|
468
473
|
el.style.setProperty('--buttons-height', `${buttonsHeight}px`);
|
|
469
474
|
}
|
|
470
|
-
|
|
475
|
+
const current = currentActive(el);
|
|
476
|
+
updateTransform(el, current.index);
|
|
471
477
|
updateOverflow(el);
|
|
472
|
-
if (
|
|
473
|
-
updateMarker(el,
|
|
474
|
-
scrollActiveIntoView(el,
|
|
478
|
+
if (current.id) {
|
|
479
|
+
updateMarker(el, current.id);
|
|
480
|
+
scrollActiveIntoView(el, current.id, true);
|
|
475
481
|
}
|
|
476
482
|
});
|
|
477
483
|
if (activeId) {
|
|
@@ -496,11 +502,12 @@ if (buttons) {
|
|
|
496
502
|
const buttonsHeight = buttons.offsetHeight;
|
|
497
503
|
el.style.setProperty('--buttons-height', `${buttonsHeight}px`);
|
|
498
504
|
}
|
|
499
|
-
|
|
505
|
+
const current = currentActive(el);
|
|
506
|
+
updateTransform(el, current.index);
|
|
500
507
|
updateOverflow(el);
|
|
501
|
-
if (
|
|
502
|
-
updateMarker(el,
|
|
503
|
-
scrollActiveIntoView(el,
|
|
508
|
+
if (current.id) {
|
|
509
|
+
updateMarker(el, current.id);
|
|
510
|
+
scrollActiveIntoView(el, current.id, false);
|
|
504
511
|
}
|
|
505
512
|
});
|
|
506
513
|
setupEventListeners(el, shadowRoot, tabs);
|
|
@@ -39,7 +39,7 @@ border-radius: var(--ty-radius-md);
|
|
|
39
39
|
font-family: inherit;
|
|
40
40
|
user-select: none;
|
|
41
41
|
width: var(--calendar-width, fit-content);
|
|
42
|
-
min-width: 280px;
|
|
42
|
+
min-width: var(--ty-calendar-width, 280px);
|
|
43
43
|
max-width: var(--calendar-max-width, none);
|
|
44
44
|
}
|
|
45
45
|
.calendar-row {
|
|
@@ -135,7 +135,7 @@ opacity: 0.7;
|
|
|
135
135
|
}
|
|
136
136
|
.calendar-size-sm {
|
|
137
137
|
padding: 0.5rem;
|
|
138
|
-
min-width: 240px;
|
|
138
|
+
min-width: var(--ty-calendar-width, 240px);
|
|
139
139
|
}
|
|
140
140
|
.calendar-size-sm .calendar-header-cell {
|
|
141
141
|
font-size: 0.625rem;
|
|
@@ -150,7 +150,7 @@ min-height: 1.5rem;
|
|
|
150
150
|
}
|
|
151
151
|
.calendar-size-md {
|
|
152
152
|
padding: 0.75rem;
|
|
153
|
-
min-width: 280px;
|
|
153
|
+
min-width: var(--ty-calendar-width, 280px);
|
|
154
154
|
}
|
|
155
155
|
.calendar-size-md .calendar-header-cell {
|
|
156
156
|
font-size: 0.6875rem;
|
|
@@ -165,7 +165,7 @@ min-height: 2rem;
|
|
|
165
165
|
}
|
|
166
166
|
.calendar-size-lg {
|
|
167
167
|
padding: 1rem;
|
|
168
|
-
min-width: 360px;
|
|
168
|
+
min-width: var(--ty-calendar-width, 360px);
|
|
169
169
|
}
|
|
170
170
|
.calendar-size-lg .calendar-header-cell {
|
|
171
171
|
font-size: 0.75rem;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Calendar Navigation Styles */
|
|
2
|
-
export declare const calendarNavigationStyles = "\n/* Theming tokens applied as var(--ty-calendar-nav-*, <default>) at point of\n use (NOT on :host) so consumers can override them from outside \u2014 this\n component is nested inside ty-calendar's shadow, so a :host default would\n block inherited overrides. --ty-calendar-accent still cascades in. */\n\n:host([data-size=\"md\"]) {\n --nav-btn-size: 2rem;\n --nav-btn-icon-size: 1.25rem;\n --nav-font-size:
|
|
2
|
+
export declare const calendarNavigationStyles = "\n/* Theming tokens applied as var(--ty-calendar-nav-*, <default>) at point of\n use (NOT on :host) so consumers can override them from outside \u2014 this\n component is nested inside ty-calendar's shadow, so a :host default would\n block inherited overrides. --ty-calendar-accent still cascades in. */\n\n:host([data-size=\"md\"]) {\n --nav-btn-size: 2rem;\n --nav-btn-icon-size: 1.25rem;\n --nav-font-size: 0.875rem;\n --nav-padding: 0.5rem 0.75rem;\n --nav-default-width: 280px;\n}\n\n:host([data-size=\"sm\"]) {\n --nav-btn-size: 1.5rem;\n --nav-btn-icon-size: 1rem;\n --nav-font-size: 0.875rem;\n --nav-padding: 0.375rem 0.5rem;\n --nav-default-width: 240px;\n}\n\n:host([data-size=\"lg\"]) {\n --nav-btn-size: 2.5rem;\n --nav-btn-icon-size: 1.5rem;\n --nav-font-size: 0.875rem;\n --nav-padding: 0.625rem 1rem;\n --nav-default-width: 360px;\n}\n\n.calendar-navigation-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--nav-padding);\n font-family: inherit;\n user-select: none;\n /* --ty-calendar-width is the public knob: set it on ty-calendar (or any\n ancestor) and BOTH the nav and the day grid follow it, because custom\n properties inherit through shadow boundaries. --nav-default-width can't\n serve that role \u2014 it's declared on :host([data-size]) inside this shadow\n root, and a declaration on the element always beats a value inherited\n into it, so an outside override of it is silently dead. --nav-width\n (set inline by the width attribute) still wins over both. */\n width: var(--nav-width, var(--ty-calendar-width, var(--nav-default-width)));\n box-sizing: border-box;\n}\n\n/* Navigation groups: left, center, right */\n.nav-group {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.nav-group-left,\n.nav-group-right {\n flex: 0 0 auto;\n}\n\n.nav-group-center {\n flex: 1;\n justify-content: center;\n /* Flex items default to min-width:auto, i.e. they refuse to shrink below\n their content. A long month name (\"September 2026\") therefore widened\n this group and shoved the arrow groups outward \u2014 the nav visibly\n re-laid-out as you paged month to month. min-width:0 lets the centre\n take exactly the leftover space, so the arrows stay pinned whatever the\n label says, and an over-long label ellipsizes instead of pushing. */\n min-width: 0;\n}\n\n.nav-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--nav-btn-size);\n height: var(--nav-btn-size);\n padding: 0;\n border: none;\n border-radius: var(--ty-radius-base);\n background-color: transparent;\n color: var(--ty-calendar-nav-color, var(--ty-color-neutral));\n cursor: pointer;\n transition: var(--ty-local-transition, all 0.15s ease);\n outline: none;\n}\n\n.nav-btn:hover {\n background-color: var(--ty-calendar-nav-hover-bg, var(--ty-bg-neutral-soft));\n color: var(--ty-calendar-nav-hover-color, var(--ty-color-neutral-strong));\n}\n\n.nav-btn:active {\n background-color: var(--ty-calendar-nav-active-bg, var(--ty-bg-neutral));\n transform: scale(0.95);\n}\n\n.nav-btn:disabled {\n opacity: 0.35;\n cursor: default;\n pointer-events: none;\n}\n\n.nav-btn:focus-visible {\n outline: 2px solid var(--ty-calendar-nav-focus-outline, var(--ty-calendar-accent, var(--ty-color-primary)));\n outline-offset: 2px;\n}\n\n.nav-btn svg {\n width: var(--nav-btn-icon-size);\n height: var(--nav-btn-icon-size);\n display: block;\n}\n\n/* 14px uppercase at every size, not a per-size type scale: the label is the\n widest thing in the bar and the only one whose width depends on the data\n (\"May\" vs \"September\"), so scaling it up on lg was what made the arrows\n shuffle around. Small caps read as chrome, stay legible, and leave the\n centre column comfortably wider than the longest localized month name.\n Ellipsis is a backstop for a narrow --nav-width, not the sizing strategy. */\n.month-year-display {\n font-size: var(--nav-font-size);\n font-weight: 600;\n color: var(--ty-calendar-nav-title-color, var(--ty-color-neutral-strong));\n text-align: center;\n white-space: nowrap;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n";
|
|
@@ -2,7 +2,7 @@ export const calendarNavigationStyles = `
|
|
|
2
2
|
:host([data-size="md"]) {
|
|
3
3
|
--nav-btn-size: 2rem;
|
|
4
4
|
--nav-btn-icon-size: 1.25rem;
|
|
5
|
-
--nav-font-size:
|
|
5
|
+
--nav-font-size: 0.875rem;
|
|
6
6
|
--nav-padding: 0.5rem 0.75rem;
|
|
7
7
|
--nav-default-width: 280px;
|
|
8
8
|
}
|
|
@@ -16,7 +16,7 @@ export const calendarNavigationStyles = `
|
|
|
16
16
|
:host([data-size="lg"]) {
|
|
17
17
|
--nav-btn-size: 2.5rem;
|
|
18
18
|
--nav-btn-icon-size: 1.5rem;
|
|
19
|
-
--nav-font-size:
|
|
19
|
+
--nav-font-size: 0.875rem;
|
|
20
20
|
--nav-padding: 0.625rem 1rem;
|
|
21
21
|
--nav-default-width: 360px;
|
|
22
22
|
}
|
|
@@ -27,7 +27,7 @@ justify-content: space-between;
|
|
|
27
27
|
padding: var(--nav-padding);
|
|
28
28
|
font-family: inherit;
|
|
29
29
|
user-select: none;
|
|
30
|
-
width: var(--nav-width, var(--nav-default-width));
|
|
30
|
+
width: var(--nav-width, var(--ty-calendar-width, var(--nav-default-width)));
|
|
31
31
|
box-sizing: border-box;
|
|
32
32
|
}
|
|
33
33
|
.nav-group {
|
|
@@ -42,6 +42,7 @@ flex: 0 0 auto;
|
|
|
42
42
|
.nav-group-center {
|
|
43
43
|
flex: 1;
|
|
44
44
|
justify-content: center;
|
|
45
|
+
min-width: 0;
|
|
45
46
|
}
|
|
46
47
|
.nav-btn {
|
|
47
48
|
display: inline-flex;
|
|
@@ -86,6 +87,9 @@ font-weight: 600;
|
|
|
86
87
|
color: var(--ty-calendar-nav-title-color, var(--ty-color-neutral-strong));
|
|
87
88
|
text-align: center;
|
|
88
89
|
white-space: nowrap;
|
|
89
|
-
|
|
90
|
+
text-transform: uppercase;
|
|
91
|
+
letter-spacing: 0.04em;
|
|
92
|
+
overflow: hidden;
|
|
93
|
+
text-overflow: ellipsis;
|
|
90
94
|
}
|
|
91
95
|
`;
|
package/lib/styles/calendar.d.ts
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
* Calendar Orchestration Styles
|
|
3
3
|
* Simple container for navigation + month display
|
|
4
4
|
*/
|
|
5
|
-
export declare const calendarStyles = "\n.calendar-container {\n display: flex;\n flex-direction: column;\n gap: 0.125rem;\n box-sizing: border-box;\n font-family: inherit;\n}\n\n@media (max-width: 320px) {\n .calendar-container {\n gap: 0.375rem;\n }\n}\n";
|
|
5
|
+
export declare const calendarStyles = "\n.calendar-container {\n display: flex;\n flex-direction: column;\n gap: 0.125rem;\n box-sizing: border-box;\n font-family: inherit;\n /* Nav is a fixed width and the grid is fit-content, so in any host wider\n than them (a popup, a stretched cell) the default stretch alignment\n left-pinned both. Centring keeps the block centred in whatever space it\n gets \u2014 without widening the calendar itself to fill it. */\n align-items: center;\n}\n\n@media (max-width: 320px) {\n .calendar-container {\n gap: 0.375rem;\n }\n}\n";
|
package/lib/styles/calendar.js
CHANGED
|
@@ -555,18 +555,26 @@ font-weight: 500;
|
|
|
555
555
|
color: var(--ty-text-faint);
|
|
556
556
|
margin-left: 0.25rem;
|
|
557
557
|
}
|
|
558
|
-
.dropdown-mode-mobile .mobile-
|
|
558
|
+
.dropdown-mode-mobile .mobile-body .section-header {
|
|
559
|
+
height: 36px;
|
|
560
|
+
flex: 0 0 36px;
|
|
561
|
+
}
|
|
562
|
+
.dropdown-mode-mobile .mobile-selected-section,
|
|
563
|
+
.dropdown-mode-mobile .mobile-available-section {
|
|
559
564
|
display: flex;
|
|
560
565
|
flex-direction: column;
|
|
561
566
|
background: var(--ty-input-bg);
|
|
562
567
|
overflow: hidden;
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
transition:
|
|
568
|
+
min-height: 36px;
|
|
569
|
+
flex: 0 1 36px;
|
|
570
|
+
transition: flex-grow 220ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
571
|
+
}
|
|
572
|
+
.dropdown-mode-mobile .mobile-body[data-expanded="selected"] .mobile-selected-section,
|
|
573
|
+
.dropdown-mode-mobile .mobile-body[data-expanded="available"] .mobile-available-section {
|
|
574
|
+
flex-grow: 1;
|
|
566
575
|
}
|
|
567
576
|
.dropdown-mode-mobile .mobile-selected-section[data-empty="true"] {
|
|
568
|
-
|
|
569
|
-
flex: 0 0 36px;
|
|
577
|
+
flex-grow: 0;
|
|
570
578
|
}
|
|
571
579
|
.dropdown-mode-mobile .mobile-selected-section .section-content {
|
|
572
580
|
flex: 1;
|
|
@@ -581,14 +589,6 @@ align-content: flex-start;
|
|
|
581
589
|
-webkit-mask-image: linear-gradient(to bottom, black calc(100% - 12px), transparent);
|
|
582
590
|
mask-image: linear-gradient(to bottom, black calc(100% - 12px), transparent);
|
|
583
591
|
}
|
|
584
|
-
.dropdown-mode-mobile .mobile-available-section {
|
|
585
|
-
display: flex;
|
|
586
|
-
flex-direction: column;
|
|
587
|
-
background: var(--ty-input-bg);
|
|
588
|
-
overflow: hidden;
|
|
589
|
-
flex: 1 1 auto;
|
|
590
|
-
min-height: 0;
|
|
591
|
-
}
|
|
592
592
|
.dropdown-mode-mobile .mobile-available-section .section-content {
|
|
593
593
|
flex: 1;
|
|
594
594
|
min-height: 0;
|
|
@@ -600,6 +600,73 @@ flex-wrap: wrap;
|
|
|
600
600
|
gap: 0.5rem;
|
|
601
601
|
align-content: flex-start;
|
|
602
602
|
}
|
|
603
|
+
@media (prefers-reduced-motion: reduce) {
|
|
604
|
+
.dropdown-mode-mobile .mobile-selected-section,
|
|
605
|
+
.dropdown-mode-mobile .mobile-available-section {
|
|
606
|
+
transition: none;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
.dropdown-mode-mobile .mobile-body .section-header {
|
|
610
|
+
display: flex;
|
|
611
|
+
align-items: center;
|
|
612
|
+
justify-content: space-between;
|
|
613
|
+
width: 100%;
|
|
614
|
+
border: none;
|
|
615
|
+
background: transparent;
|
|
616
|
+
font: inherit;
|
|
617
|
+
text-align: left;
|
|
618
|
+
cursor: pointer;
|
|
619
|
+
-webkit-tap-highlight-color: transparent;
|
|
620
|
+
}
|
|
621
|
+
.dropdown-mode-mobile .mobile-body .section-header svg {
|
|
622
|
+
width: 1rem;
|
|
623
|
+
height: 1rem;
|
|
624
|
+
flex-shrink: 0;
|
|
625
|
+
opacity: 0.5;
|
|
626
|
+
transition: transform 180ms ease;
|
|
627
|
+
}
|
|
628
|
+
.dropdown-mode-mobile .mobile-body .section-header[aria-expanded="true"] svg {
|
|
629
|
+
transform: rotate(180deg);
|
|
630
|
+
}
|
|
631
|
+
.dropdown-mode-mobile .mobile-selected-section[data-empty="true"] .section-header {
|
|
632
|
+
cursor: default;
|
|
633
|
+
}
|
|
634
|
+
.dropdown-mode-mobile .mobile-selected-section[data-empty="true"] .section-header svg {
|
|
635
|
+
display: none;
|
|
636
|
+
}
|
|
637
|
+
.dropdown-mode-mobile .mobile-selected-chip {
|
|
638
|
+
display: inline-flex;
|
|
639
|
+
align-items: center;
|
|
640
|
+
gap: 0.375rem;
|
|
641
|
+
padding: 0.25rem 0.5rem 0.25rem 0.625rem;
|
|
642
|
+
border: 1px solid var(--ty-border);
|
|
643
|
+
border-radius: 9999px;
|
|
644
|
+
background: var(--ty-surface-content);
|
|
645
|
+
color: var(--ty-text);
|
|
646
|
+
font: inherit;
|
|
647
|
+
font-size: var(--ty-font-sm);
|
|
648
|
+
line-height: var(--ty-leading-sm);
|
|
649
|
+
cursor: pointer;
|
|
650
|
+
transition: transform 0.1s ease, border-color 0.15s ease;
|
|
651
|
+
animation: ty-select-tag-enter 180ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
652
|
+
}
|
|
653
|
+
.dropdown-mode-mobile .mobile-selected-chip svg {
|
|
654
|
+
width: 0.875rem;
|
|
655
|
+
height: 0.875rem;
|
|
656
|
+
flex-shrink: 0;
|
|
657
|
+
opacity: 0.6;
|
|
658
|
+
}
|
|
659
|
+
@media (hover: hover) {
|
|
660
|
+
.dropdown-mode-mobile .mobile-selected-chip:hover {
|
|
661
|
+
border-color: var(--ty-text-soft);
|
|
662
|
+
}
|
|
663
|
+
.dropdown-mode-mobile .mobile-selected-chip:hover svg {
|
|
664
|
+
opacity: 1;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
.dropdown-mode-mobile .mobile-selected-chip:active {
|
|
668
|
+
transform: scale(0.96);
|
|
669
|
+
}
|
|
603
670
|
.dropdown-mode-mobile .empty-state {
|
|
604
671
|
display: none;
|
|
605
672
|
width: 100%;
|
package/lib/version.d.ts
CHANGED
package/lib/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '1.0.0-
|
|
1
|
+
export const VERSION = '1.0.0-TC55';
|