@torpor/ui 0.3.4 → 0.3.6
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/Form/Form.torp +1 -0
- package/dist/metafile-esm.json +1 -1
- package/package.json +12 -9
- package/CHANGELOG.md +0 -338
- package/scripts/postbuild.ts +0 -127
- package/src/Accordion/Accordion.torp +0 -230
- package/src/Accordion/AccordionContent.torp +0 -46
- package/src/Accordion/AccordionHeader.torp +0 -45
- package/src/Accordion/AccordionItem.torp +0 -101
- package/src/Accordion/AccordionTrigger.torp +0 -70
- package/src/Accordion/AccordionTypes.ts +0 -36
- package/src/Accordion/index.ts +0 -7
- package/src/Breadcrumb/Breadcrumb.torp +0 -45
- package/src/Breadcrumb/BreadcrumbItem.torp +0 -30
- package/src/Breadcrumb/BreadcrumbSeparator.torp +0 -27
- package/src/Breadcrumb/index.ts +0 -5
- package/src/Calendar/Calendar.torp +0 -277
- package/src/Calendar/CalendarDay.torp +0 -78
- package/src/Calendar/CalendarTypes.ts +0 -22
- package/src/Calendar/index.ts +0 -4
- package/src/Contextual/Contextual.torp +0 -129
- package/src/Contextual/ContextualContent.torp +0 -112
- package/src/Contextual/ContextualTrigger.torp +0 -70
- package/src/Contextual/ContextualTypes.ts +0 -30
- package/src/Contextual/index.ts +0 -5
- package/src/Dialog/Dialog.torp +0 -136
- package/src/Dialog/DialogBody.torp +0 -44
- package/src/Dialog/DialogButton.torp +0 -66
- package/src/Dialog/DialogFooter.torp +0 -25
- package/src/Dialog/DialogHeader.torp +0 -45
- package/src/Dialog/DialogTypes.ts +0 -17
- package/src/Dialog/index.ts +0 -7
- package/src/Disclosure/Disclosure.torp +0 -82
- package/src/Disclosure/DisclosureContent.torp +0 -46
- package/src/Disclosure/DisclosureHeader.torp +0 -45
- package/src/Disclosure/DisclosureTrigger.torp +0 -47
- package/src/Disclosure/DisclosureTypes.ts +0 -13
- package/src/Disclosure/index.ts +0 -6
- package/src/Drawer/Drawer.torp +0 -127
- package/src/Drawer/DrawerContent.torp +0 -83
- package/src/Drawer/DrawerOverlay.torp +0 -42
- package/src/Drawer/DrawerTrigger.torp +0 -39
- package/src/Drawer/DrawerTypes.ts +0 -24
- package/src/Drawer/index.ts +0 -6
- package/src/Form/CheckBox.torp +0 -94
- package/src/Form/Field.torp +0 -95
- package/src/Form/File.torp +0 -102
- package/src/Form/Form.torp +0 -146
- package/src/Form/FormTypes.ts +0 -35
- package/src/Form/Hidden.torp +0 -25
- package/src/Form/Input.torp +0 -112
- package/src/Form/Label.torp +0 -35
- package/src/Form/Message.torp +0 -39
- package/src/Form/Option.torp +0 -28
- package/src/Form/Select.torp +0 -101
- package/src/Form/StandardSchemaV1.ts +0 -70
- package/src/Form/TextArea.torp +0 -105
- package/src/Form/index.ts +0 -13
- package/src/Menu/Menu.torp +0 -174
- package/src/Menu/MenuButton.torp +0 -74
- package/src/Menu/MenuCheck.torp +0 -89
- package/src/Menu/MenuGroup.torp +0 -31
- package/src/Menu/MenuIndicator.torp +0 -40
- package/src/Menu/MenuPopout.torp +0 -83
- package/src/Menu/MenuPopoutContent.torp +0 -84
- package/src/Menu/MenuPopoutTrigger.torp +0 -128
- package/src/Menu/MenuRadio.torp +0 -103
- package/src/Menu/MenuRadioGroup.torp +0 -109
- package/src/Menu/MenuSeparator.torp +0 -26
- package/src/Menu/MenuTypes.ts +0 -60
- package/src/Menu/index.ts +0 -25
- package/src/MenuBar/MenuBar.torp +0 -162
- package/src/MenuBar/MenuBarItem.torp +0 -116
- package/src/MenuBar/MenuBarItemContent.torp +0 -119
- package/src/MenuBar/MenuBarItemTrigger.torp +0 -99
- package/src/MenuBar/MenuBarTypes.ts +0 -36
- package/src/MenuBar/index.ts +0 -6
- package/src/Modal/Modal.torp +0 -122
- package/src/Modal/ModalContent.torp +0 -81
- package/src/Modal/ModalOverlay.torp +0 -42
- package/src/Modal/ModalTrigger.torp +0 -54
- package/src/Modal/ModalTypes.ts +0 -22
- package/src/Modal/index.ts +0 -10
- package/src/Modal/showModal.ts +0 -19
- package/src/Modal/showPrompt.ts +0 -23
- package/src/NavMenu/NavMenu.torp +0 -190
- package/src/NavMenu/NavMenuButton.torp +0 -55
- package/src/NavMenu/NavMenuElement.torp +0 -55
- package/src/NavMenu/NavMenuGroup.torp +0 -41
- package/src/NavMenu/NavMenuGroupHeader.torp +0 -41
- package/src/NavMenu/NavMenuLink.torp +0 -60
- package/src/NavMenu/NavMenuPopout.torp +0 -89
- package/src/NavMenu/NavMenuPopoutButton.torp +0 -129
- package/src/NavMenu/NavMenuPopoutContent.torp +0 -92
- package/src/NavMenu/NavMenuSeparator.torp +0 -24
- package/src/NavMenu/NavMenuTypes.ts +0 -32
- package/src/NavMenu/index.ts +0 -23
- package/src/Notification/Notification.torp +0 -71
- package/src/Notification/NotificationClose.torp +0 -39
- package/src/Notification/NotificationContainer.torp +0 -26
- package/src/Notification/NotificationContent.torp +0 -25
- package/src/Notification/NotificationTypes.ts +0 -5
- package/src/Notification/index.ts +0 -13
- package/src/Notification/showNotification.ts +0 -30
- package/src/Popover/Popover.torp +0 -129
- package/src/Popover/PopoverContent.torp +0 -117
- package/src/Popover/PopoverTrigger.torp +0 -62
- package/src/Popover/PopoverTypes.ts +0 -30
- package/src/Popover/index.ts +0 -6
- package/src/Popover/showPopover.ts +0 -24
- package/src/Slider/Slider.torp +0 -84
- package/src/Slider/index.ts +0 -3
- package/src/TabGroup/TabContent.torp +0 -49
- package/src/TabGroup/TabGroup.torp +0 -250
- package/src/TabGroup/TabGroupTypes.ts +0 -44
- package/src/TabGroup/TabHeader.torp +0 -81
- package/src/TabGroup/TabList.torp +0 -61
- package/src/TabGroup/TabTrigger.torp +0 -76
- package/src/TabGroup/index.ts +0 -7
- package/src/ambient.d.ts +0 -7
- package/src/icons/ArrowDown.torp +0 -18
- package/src/icons/ArrowLeft.torp +0 -18
- package/src/icons/ArrowRight.torp +0 -18
- package/src/icons/ArrowUp.torp +0 -18
- package/src/icons/Bars.torp +0 -20
- package/src/icons/Check.torp +0 -18
- package/src/icons/ChevronDown.torp +0 -18
- package/src/icons/ChevronLeft.torp +0 -18
- package/src/icons/ChevronRight.torp +0 -18
- package/src/icons/ChevronUp.torp +0 -18
- package/src/icons/Cross.torp +0 -19
- package/src/icons/IconTypes.ts +0 -10
- package/src/icons/Loading.torp +0 -19
- package/src/icons/Minus.torp +0 -18
- package/src/icons/Plus.torp +0 -19
- package/src/icons/index.ts +0 -31
- package/src/motion/fade.ts +0 -16
- package/src/motion/grow.ts +0 -23
- package/src/motion/index.ts +0 -6
- package/src/motion/measure.ts +0 -28
- package/src/motion/slide.ts +0 -45
- package/src/mount/clickPopover.ts +0 -51
- package/src/mount/contextPopover.ts +0 -51
- package/src/mount/disableScroll.ts +0 -28
- package/src/mount/hoverPopover.ts +0 -58
- package/src/mount/hoverTrigger.ts +0 -59
- package/src/mount/trapFocus.ts +0 -58
- package/src/utils/dateUtils.ts +0 -244
- package/src/utils/documentEvents.ts +0 -17
- package/src/utils/getId.ts +0 -6
- package/src/utils/getScrollParent.ts +0 -22
- package/src/utils/setContextPosition.ts +0 -31
- package/src/utils/setPopoverPosition.ts +0 -142
- package/test/Accordion/accessibility.test.ts +0 -59
- package/test/Accordion/components/AccordionAccessibility.torp +0 -44
- package/test/Accordion/components/AccordionKeyboard.torp +0 -32
- package/test/Accordion/components/AccordionList.torp +0 -23
- package/test/Accordion/components/AccordionMultiple.torp +0 -32
- package/test/Accordion/components/AccordionSingle.torp +0 -32
- package/test/Accordion/for-items.test.ts +0 -52
- package/test/Accordion/keyboard.test.ts +0 -64
- package/test/Accordion/multiple-item-toggle.test.ts +0 -32
- package/test/Accordion/multiple-item-value.test.ts +0 -17
- package/test/Accordion/single-item-toggle.test.ts +0 -31
- package/test/Accordion/single-item-value.test.ts +0 -17
- package/test/Breadcrumb/accessibility.test.ts +0 -29
- package/test/Breadcrumb/basic.test.ts +0 -17
- package/test/Breadcrumb/components/BreadcrumbTest.torp +0 -15
- package/test/Calendar/components/CalendarTest.torp +0 -9
- package/test/Calendar/default.test.ts +0 -18
- package/test/Dialog/accessibility-alert.test.ts +0 -40
- package/test/Dialog/accessibility-modal.test.ts +0 -52
- package/test/Dialog/components/DialogAccessibility.torp +0 -25
- package/test/Dialog/components/DialogFunction.torp +0 -36
- package/test/Dialog/components/DialogModal.torp +0 -30
- package/test/Dialog/components/DialogVariable.torp +0 -33
- package/test/Dialog/function.test.ts +0 -26
- package/test/Dialog/variable.test.ts +0 -27
- package/test/Disclosure/accessibility.test.ts +0 -31
- package/test/Disclosure/components/DisclosureTest.torp +0 -17
- package/test/Disclosure/default.test.ts +0 -15
- package/test/Disclosure/toggle.test.ts +0 -20
- package/test/Form/components/FieldTest.torp +0 -25
- package/test/Form/components/FormTest.torp +0 -31
- package/test/Form/components/InputTest.torp +0 -9
- package/test/Form/field.test.ts +0 -34
- package/test/Form/form.test.ts +0 -34
- package/test/Form/input.test.ts +0 -19
- package/test/Menu/accessibility.test.ts +0 -83
- package/test/Menu/basic.test.ts +0 -58
- package/test/Menu/components/MenuAccessibility.torp +0 -59
- package/test/Menu/components/MenuBasic.torp +0 -58
- package/test/MenuBar/accessibility.test.ts +0 -87
- package/test/MenuBar/basic.test.ts +0 -22
- package/test/MenuBar/components/MenuBarAccessibility.torp +0 -34
- package/test/MenuBar/components/MenuBarBasic.torp +0 -25
- package/test/NavMenu/accessibility.test.ts +0 -97
- package/test/NavMenu/basic.test.ts +0 -22
- package/test/NavMenu/components/NavMenuAccessibility.torp +0 -35
- package/test/NavMenu/components/NavMenuBasic.torp +0 -28
- package/test/Notification/accessibility.test.ts +0 -21
- package/test/Notification/components/CustomNotification.torp +0 -14
- package/test/Notification/components/NotificationAccessibility.torp +0 -14
- package/test/Notification/components/NotificationFunction.torp +0 -17
- package/test/Notification/components/NotificationVariable.torp +0 -22
- package/test/Notification/function.test.ts +0 -25
- package/test/Notification/variable.test.ts +0 -25
- package/test/Popover/components/CustomDialog.torp +0 -25
- package/test/Popover/components/PopoverFunction.torp +0 -37
- package/test/Popover/components/PopoverTrigger.torp +0 -36
- package/test/Popover/components/PopoverVariable.torp +0 -40
- package/test/Popover/function.test.ts +0 -21
- package/test/Popover/trigger.test.ts +0 -27
- package/test/Popover/variable.test.ts +0 -27
- package/test/TabGroup/accessibility.test.ts +0 -67
- package/test/TabGroup/components/TabGroupAccessibility.torp +0 -28
- package/test/TabGroup/components/TabGroupKeyboard.torp +0 -28
- package/test/TabGroup/components/TabGroupList.torp +0 -26
- package/test/TabGroup/components/TabGroupTrigger.torp +0 -28
- package/test/TabGroup/for-items.test.ts +0 -52
- package/test/TabGroup/item-trigger.test.ts +0 -33
- package/test/TabGroup/item-value.test.ts +0 -17
- package/test/TabGroup/keyboard.test.ts +0 -72
- package/tsconfig.json +0 -4
- package/tsup.config.ts +0 -37
- package/vitest.config.ts +0 -12
package/src/utils/dateUtils.ts
DELETED
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
// TODO: Can we get these from the locale?
|
|
2
|
-
const monthNames: string[] = [
|
|
3
|
-
"January",
|
|
4
|
-
"February",
|
|
5
|
-
"March",
|
|
6
|
-
"April",
|
|
7
|
-
"May",
|
|
8
|
-
"June",
|
|
9
|
-
"July",
|
|
10
|
-
"August",
|
|
11
|
-
"September",
|
|
12
|
-
"October",
|
|
13
|
-
"November",
|
|
14
|
-
"December",
|
|
15
|
-
];
|
|
16
|
-
const shortMonthNames: string[] = monthNames.map((n) => n.substring(0, 3).toLowerCase());
|
|
17
|
-
|
|
18
|
-
const dayNames: string[] = [
|
|
19
|
-
"Sunday",
|
|
20
|
-
"Monday",
|
|
21
|
-
"Tuesday",
|
|
22
|
-
"Wednesday",
|
|
23
|
-
"Thursday",
|
|
24
|
-
"Friday",
|
|
25
|
-
"Saturday",
|
|
26
|
-
"Sunday",
|
|
27
|
-
];
|
|
28
|
-
const shortDayNames: string[] = dayNames.map((n) => n.substring(0, 3).toLowerCase());
|
|
29
|
-
|
|
30
|
-
export { monthNames, shortMonthNames, dayNames, shortDayNames };
|
|
31
|
-
|
|
32
|
-
export function areDatesEqual(date1: Date, date2: Date): boolean {
|
|
33
|
-
if ((date1 && !date2) || (date2 && !date1)) {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
return (
|
|
37
|
-
date1.getFullYear() === date2.getFullYear() &&
|
|
38
|
-
date1.getMonth() === date2.getMonth() &&
|
|
39
|
-
date1.getDate() === date2.getDate()
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function parseDateTime(text: string, dateOrder: string): Date {
|
|
44
|
-
const parts = text
|
|
45
|
-
.toString()
|
|
46
|
-
.toLowerCase()
|
|
47
|
-
.split(/[.,/ :-]/);
|
|
48
|
-
|
|
49
|
-
const today = new Date();
|
|
50
|
-
let day = today.getDate();
|
|
51
|
-
let month = today.getMonth();
|
|
52
|
-
let year = today.getFullYear();
|
|
53
|
-
let hour = 9;
|
|
54
|
-
let minute = 0;
|
|
55
|
-
//let ampm = "";
|
|
56
|
-
|
|
57
|
-
let dayPart = -1;
|
|
58
|
-
let monthPart = -1;
|
|
59
|
-
let yearPart = -1;
|
|
60
|
-
//let hourPart = -1;
|
|
61
|
-
|
|
62
|
-
// Try to get the month if it's been supplied as a word
|
|
63
|
-
for (let i = 0; i < parts.length; i++) {
|
|
64
|
-
const index = shortMonthNames.indexOf(parts[i].substring(0, 3));
|
|
65
|
-
if (index !== -1) {
|
|
66
|
-
month = index;
|
|
67
|
-
monthPart = i;
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Get each date part based on the dateOrder supplied
|
|
73
|
-
const dateOrderLower = dateOrder.toLowerCase();
|
|
74
|
-
for (let i = 0; i < dateOrder.length; i++) {
|
|
75
|
-
if (dateOrderLower[i] === "d") {
|
|
76
|
-
const dayResult = getDayPart(parts, monthPart, yearPart);
|
|
77
|
-
if (dayResult) {
|
|
78
|
-
day = dayResult.value;
|
|
79
|
-
dayPart = dayResult.part;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
if (dateOrderLower[i] === "m" && monthPart === -1) {
|
|
83
|
-
const monthResult = getMonthPart(parts, dayPart, yearPart);
|
|
84
|
-
if (monthResult) {
|
|
85
|
-
month = monthResult.value;
|
|
86
|
-
monthPart = monthResult.part;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
if (dateOrderLower[i] === "y") {
|
|
90
|
-
const yearResult = getYearPart(parts, dayPart, monthPart);
|
|
91
|
-
if (yearResult) {
|
|
92
|
-
year = yearResult.value;
|
|
93
|
-
yearPart = yearResult.part;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// TODO: Time...
|
|
99
|
-
//if (includeTime) {
|
|
100
|
-
// // Hour part is the first part that isn't a date part
|
|
101
|
-
// for (let i = 0; i < parts.length; i++) {
|
|
102
|
-
// if (i !== yearPart && i !== monthPart && i !== dayPart) {
|
|
103
|
-
// const maybehour = parseInt(parts[i])
|
|
104
|
-
// if (!isNaN(maybehour)) {
|
|
105
|
-
// hour = maybehour
|
|
106
|
-
// hourPart = i
|
|
107
|
-
// break
|
|
108
|
-
// }
|
|
109
|
-
// }
|
|
110
|
-
// }
|
|
111
|
-
//
|
|
112
|
-
// // Minute part is the first part that isn't a date part or the hour part
|
|
113
|
-
// for (let i = 0; i < parts.length; i++) {
|
|
114
|
-
// if (i !== yearPart && i !== monthPart && i !== dayPart && i !== hourPart) {
|
|
115
|
-
// const maybeminute = parseInt(parts[i])
|
|
116
|
-
// if (!isNaN(maybeminute)) {
|
|
117
|
-
// minute = maybeminute
|
|
118
|
-
// break
|
|
119
|
-
// }
|
|
120
|
-
// }
|
|
121
|
-
// }
|
|
122
|
-
//
|
|
123
|
-
// // Look for AM or PM
|
|
124
|
-
// for (let i = 0; i < parts.length; i++) {
|
|
125
|
-
// if (parts[i] === 'am' || parts[i] === 'pm') {
|
|
126
|
-
// ampm = parts[i].toString().toUpperCase()
|
|
127
|
-
// if (ampm === 'PM') {
|
|
128
|
-
// hour = hour + 12
|
|
129
|
-
// }
|
|
130
|
-
// break
|
|
131
|
-
// }
|
|
132
|
-
// }
|
|
133
|
-
//}
|
|
134
|
-
|
|
135
|
-
return new Date(year, month, day, hour, minute);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
interface DatePart {
|
|
139
|
-
value: number;
|
|
140
|
-
part: number;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function getDayPart(parts: string[], monthPart: number, yearPart: number): DatePart | undefined {
|
|
144
|
-
for (let i = 0; i < parts.length; i++) {
|
|
145
|
-
if (i !== monthPart && i !== yearPart) {
|
|
146
|
-
const maybeday = parseInt(parts[i]);
|
|
147
|
-
if (!isNaN(maybeday)) {
|
|
148
|
-
const value = maybeday;
|
|
149
|
-
const part = i;
|
|
150
|
-
return { value, part };
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function getMonthPart(parts: string[], dayPart: number, yearPart: number): DatePart | undefined {
|
|
157
|
-
for (let i = 0; i < parts.length; i++) {
|
|
158
|
-
if (i !== dayPart && i !== yearPart) {
|
|
159
|
-
const maybemonth = parseInt(parts[i]);
|
|
160
|
-
if (!isNaN(maybemonth)) {
|
|
161
|
-
const value = maybemonth - 1;
|
|
162
|
-
const part = i;
|
|
163
|
-
return { value, part };
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function getYearPart(parts: string[], dayPart: number, monthPart: number): DatePart | undefined {
|
|
170
|
-
for (let i = 0; i < parts.length; i++) {
|
|
171
|
-
if (i !== monthPart && i !== dayPart) {
|
|
172
|
-
const maybeyear = parseInt(parts[i]);
|
|
173
|
-
if (!isNaN(maybeyear)) {
|
|
174
|
-
const value = maybeyear < 100 ? maybeyear + 2000 : maybeyear;
|
|
175
|
-
const part = i;
|
|
176
|
-
return { value, part };
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export function formatDate(value: Date, dateFormat: string): string {
|
|
183
|
-
if (!value) {
|
|
184
|
-
return "";
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const date = new Date(value);
|
|
188
|
-
|
|
189
|
-
// Build the text that will be displayed
|
|
190
|
-
return dateFormat.replace(/(d+)|(M+)|(y+)/g, (m) => {
|
|
191
|
-
if (m.indexOf("d") >= 0) {
|
|
192
|
-
return formatDay(date, m);
|
|
193
|
-
} else if (m.indexOf("M") >= 0) {
|
|
194
|
-
return formatMonth(date, m);
|
|
195
|
-
} else if (m.indexOf("y") >= 0) {
|
|
196
|
-
return formatYear(date, m);
|
|
197
|
-
} else {
|
|
198
|
-
throw new Error("Invalid date format string");
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
// TODO: Time
|
|
203
|
-
//if (includeTime) {
|
|
204
|
-
// const ampm = hour >= 12 ? 'PM' : 'AM'
|
|
205
|
-
// hour = hour % 12
|
|
206
|
-
// hour = hour || 12 // the hour '0' should be '12'
|
|
207
|
-
//
|
|
208
|
-
// minute = minute < 10 ? '0' + minute : minute
|
|
209
|
-
// text = text + ' ' + hour + ':' + minute + ' ' + ampm
|
|
210
|
-
//}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function formatDay(date: Date, d: string): string {
|
|
214
|
-
if (d.length >= 4) {
|
|
215
|
-
return dayNames[date.getDay()];
|
|
216
|
-
} else if (d.length === 3) {
|
|
217
|
-
return dayNames[date.getDay()].substring(0, 3);
|
|
218
|
-
} else if (d.length == 2) {
|
|
219
|
-
let text = date.getDate().toString();
|
|
220
|
-
return text.length < 2 ? "0" + text : text;
|
|
221
|
-
} else {
|
|
222
|
-
return date.getDate().toString();
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function formatMonth(date: Date, m: string): string {
|
|
227
|
-
if (m.length >= 4) {
|
|
228
|
-
return monthNames[date.getMonth()];
|
|
229
|
-
} else if (m.length === 3) {
|
|
230
|
-
return monthNames[date.getMonth()].substring(0, 3);
|
|
231
|
-
} else if (m.length == 2) {
|
|
232
|
-
let text = date.getMonth().toString();
|
|
233
|
-
return text.length < 2 ? "0" + text : text;
|
|
234
|
-
} else {
|
|
235
|
-
return date.getMonth().toString();
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function formatYear(date: Date, y: string): string {
|
|
240
|
-
return date
|
|
241
|
-
.getFullYear()
|
|
242
|
-
.toString()
|
|
243
|
-
.substring(4 - y.length, y.length);
|
|
244
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export function addDocumentEvent<K extends keyof DocumentEventMap>(
|
|
2
|
-
type: K,
|
|
3
|
-
listener: (this: Document, ev: DocumentEventMap[K]) => any
|
|
4
|
-
): void {
|
|
5
|
-
if (typeof document !== 'undefined') {
|
|
6
|
-
document.addEventListener(type, listener);
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function removeDocumentEvent<K extends keyof DocumentEventMap>(
|
|
11
|
-
type: K,
|
|
12
|
-
listener: (this: Document, ev: DocumentEventMap[K]) => any
|
|
13
|
-
): void {
|
|
14
|
-
if (typeof document !== 'undefined') {
|
|
15
|
-
document.removeEventListener(type, listener);
|
|
16
|
-
}
|
|
17
|
-
}
|
package/src/utils/getId.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// From https://stackoverflow.com/a/76535387
|
|
2
|
-
export default function getScrollParent(node: Element): Element {
|
|
3
|
-
let currentParent = node.parentElement;
|
|
4
|
-
while (currentParent) {
|
|
5
|
-
if (isScrollable(currentParent)) {
|
|
6
|
-
return currentParent;
|
|
7
|
-
}
|
|
8
|
-
currentParent = currentParent.parentElement;
|
|
9
|
-
}
|
|
10
|
-
return document.scrollingElement || document.documentElement;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function isScrollable(node: Element): boolean {
|
|
14
|
-
if (!(node instanceof HTMLElement || node instanceof SVGElement)) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
const style = getComputedStyle(node);
|
|
18
|
-
return ["overflow", "overflow-x", "overflow-y"].some((propertyName) => {
|
|
19
|
-
const value = style.getPropertyValue(propertyName);
|
|
20
|
-
return value === "auto" || value === "scroll";
|
|
21
|
-
});
|
|
22
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
interface Point {
|
|
2
|
-
x: number;
|
|
3
|
-
y: number;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export default function setContextPosition(
|
|
7
|
-
anchor: HTMLElement,
|
|
8
|
-
content: HTMLElement,
|
|
9
|
-
position: Point,
|
|
10
|
-
): void {
|
|
11
|
-
var anchorRect = anchor.getBoundingClientRect();
|
|
12
|
-
var contentRect = content.getBoundingClientRect();
|
|
13
|
-
|
|
14
|
-
content.style.position = "fixed";
|
|
15
|
-
|
|
16
|
-
// Try to make sure the content is entirely on the screen horizontally
|
|
17
|
-
// Just move it left or right -- the pointer is outside the content vertically so moving it left
|
|
18
|
-
// or right won't make the content appear over the cursor
|
|
19
|
-
let left = anchorRect.left + position.x;
|
|
20
|
-
left = Math.min(left, window.innerWidth - contentRect.width);
|
|
21
|
-
left = Math.max(left, 0);
|
|
22
|
-
content.style.left = left + "px";
|
|
23
|
-
|
|
24
|
-
// Try to make sure the content is entirely on the screen vertically
|
|
25
|
-
// Show it above the position if it wouldn't fit
|
|
26
|
-
let top = anchorRect.top + position.y;
|
|
27
|
-
if (top + contentRect.height > window.innerHeight) {
|
|
28
|
-
top -= contentRect.height;
|
|
29
|
-
}
|
|
30
|
-
content.style.top = top + "px";
|
|
31
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
export default function setPopoverPosition(
|
|
2
|
-
anchor: HTMLElement,
|
|
3
|
-
content: HTMLElement,
|
|
4
|
-
side: "top" | "right" | "bottom" | "left",
|
|
5
|
-
alignment: "start" | "center" | "end",
|
|
6
|
-
): void {
|
|
7
|
-
var anchorRect = anchor.getBoundingClientRect();
|
|
8
|
-
var contentRect = content.getBoundingClientRect();
|
|
9
|
-
|
|
10
|
-
content.style.position = "fixed";
|
|
11
|
-
|
|
12
|
-
// Check that we there is enough room to show the content on the side requested
|
|
13
|
-
// Otherwise, if there is enough room on the other side, show it there instead
|
|
14
|
-
let calculatedSide = side;
|
|
15
|
-
const canShowOnTop = anchorRect.top - contentRect.height >= 0;
|
|
16
|
-
const canShowAtRight = anchorRect.right + contentRect.width <= window.innerWidth;
|
|
17
|
-
const canShowOnBottom = anchorRect.bottom + contentRect.height <= window.innerHeight;
|
|
18
|
-
const canShowAtLeft = anchorRect.left - contentRect.width >= 0;
|
|
19
|
-
switch (side) {
|
|
20
|
-
case "top": {
|
|
21
|
-
if (!canShowOnTop && canShowOnBottom) {
|
|
22
|
-
calculatedSide = "bottom";
|
|
23
|
-
}
|
|
24
|
-
break;
|
|
25
|
-
}
|
|
26
|
-
case "right": {
|
|
27
|
-
if (!canShowAtRight && canShowAtLeft) {
|
|
28
|
-
calculatedSide = "left";
|
|
29
|
-
}
|
|
30
|
-
break;
|
|
31
|
-
}
|
|
32
|
-
case "bottom": {
|
|
33
|
-
if (!canShowOnBottom && canShowOnTop) {
|
|
34
|
-
calculatedSide = "top";
|
|
35
|
-
}
|
|
36
|
-
break;
|
|
37
|
-
}
|
|
38
|
-
case "left": {
|
|
39
|
-
if (!canShowAtLeft && canShowAtRight) {
|
|
40
|
-
calculatedSide = "right";
|
|
41
|
-
}
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
default: {
|
|
45
|
-
// eslint-disable-next-line restrict-template-expressions
|
|
46
|
-
throw new Error(`Invalid MenuPopout side: ${side}`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
switch (calculatedSide) {
|
|
51
|
-
case "top": {
|
|
52
|
-
content.style.top = anchorRect.top - contentRect.height + "px";
|
|
53
|
-
setVerticalAlignment(content, anchorRect, contentRect, alignment);
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
56
|
-
case "right": {
|
|
57
|
-
content.style.left = anchorRect.right + "px";
|
|
58
|
-
setHorizontalAlignment(content, anchorRect, contentRect, alignment);
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
61
|
-
case "bottom": {
|
|
62
|
-
content.style.top = anchorRect.bottom + "px";
|
|
63
|
-
setVerticalAlignment(content, anchorRect, contentRect, alignment);
|
|
64
|
-
break;
|
|
65
|
-
}
|
|
66
|
-
case "left": {
|
|
67
|
-
content.style.left = anchorRect.left - contentRect.width + "px";
|
|
68
|
-
setHorizontalAlignment(content, anchorRect, contentRect, alignment);
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
default: {
|
|
72
|
-
// eslint-disable-next-line restrict-template-expressions
|
|
73
|
-
throw new Error(`Invalid MenuPopout side: ${side}`);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function setVerticalAlignment(
|
|
79
|
-
content: HTMLElement,
|
|
80
|
-
anchorRect: DOMRect,
|
|
81
|
-
contentRect: DOMRect,
|
|
82
|
-
alignment: "start" | "center" | "end",
|
|
83
|
-
) {
|
|
84
|
-
let left = 0;
|
|
85
|
-
switch (alignment) {
|
|
86
|
-
case "start": {
|
|
87
|
-
left = anchorRect.left;
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
case "center": {
|
|
91
|
-
left = anchorRect.left + anchorRect.width / 2 - contentRect.width / 2;
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
case "end": {
|
|
95
|
-
left = anchorRect.right - contentRect.width;
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
default: {
|
|
99
|
-
// eslint-disable-next-line restrict-template-expressions
|
|
100
|
-
throw new Error(`Invalid Popout alignment: ${alignment}`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Try to make sure the content is entirely on the screen horizontally
|
|
105
|
-
left = Math.min(left, window.innerWidth - contentRect.width);
|
|
106
|
-
left = Math.max(left, 0);
|
|
107
|
-
|
|
108
|
-
content.style.left = left + "px";
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function setHorizontalAlignment(
|
|
112
|
-
content: HTMLElement,
|
|
113
|
-
anchorRect: DOMRect,
|
|
114
|
-
contentRect: DOMRect,
|
|
115
|
-
alignment: "start" | "center" | "end",
|
|
116
|
-
) {
|
|
117
|
-
let top = 0;
|
|
118
|
-
switch (alignment) {
|
|
119
|
-
case "start": {
|
|
120
|
-
top = anchorRect.top;
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
case "center": {
|
|
124
|
-
top = anchorRect.top + anchorRect.height / 2 - contentRect.height / 2;
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
127
|
-
case "end": {
|
|
128
|
-
top = anchorRect.bottom - contentRect.height;
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
default: {
|
|
132
|
-
// eslint-disable-next-line restrict-template-expressions
|
|
133
|
-
throw new Error(`Invalid Popout alignment: ${alignment}`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Try to make sure the content is entirely on the screen vertically
|
|
138
|
-
top = Math.min(top, window.innerHeight - contentRect.height);
|
|
139
|
-
top = Math.max(top, 0);
|
|
140
|
-
|
|
141
|
-
content.style.top = top + "px";
|
|
142
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { queryByText } from "@testing-library/dom";
|
|
2
|
-
import "@testing-library/jest-dom/vitest";
|
|
3
|
-
import { mount } from "@torpor/view";
|
|
4
|
-
import { describe, expect, it } from "vitest";
|
|
5
|
-
import AccordionAccessibility from "./components/AccordionAccessibility.torp";
|
|
6
|
-
|
|
7
|
-
describe("Accordion", () => {
|
|
8
|
-
it("Accessibility", async () => {
|
|
9
|
-
const container = document.createElement("div");
|
|
10
|
-
document.body.appendChild(container);
|
|
11
|
-
mount(container, AccordionAccessibility, { value: ["1", "3"] });
|
|
12
|
-
|
|
13
|
-
// Tests from https://www.w3.org/WAI/ARIA/apg/patterns/accordion/
|
|
14
|
-
|
|
15
|
-
// The title of each accordion header is contained in an element with role button
|
|
16
|
-
expect(queryByText(container, "Role Header")?.tagName.toLowerCase()).toMatch("button");
|
|
17
|
-
|
|
18
|
-
// Each accordion header button is wrapped in an element with role heading that has a value set
|
|
19
|
-
// for aria-level that is appropriate for the information architecture of the page
|
|
20
|
-
// * If the native host language has an element with an implicit heading and aria-level, such
|
|
21
|
-
// as an HTML heading tag, a native host language element may be used
|
|
22
|
-
// * The button element is the only element inside the heading element. That is, if there are
|
|
23
|
-
// other visually persistent elements, they are not included inside the heading element
|
|
24
|
-
// NOTE: This is kind of up to the user -- should we check this at runtime?
|
|
25
|
-
expect(queryByText(container, "Role Header")?.parentElement?.tagName.toLowerCase()).toMatch(
|
|
26
|
-
"h4",
|
|
27
|
-
);
|
|
28
|
-
expect(
|
|
29
|
-
queryByText(container, "Role Header")?.parentElement?.childElementCount.toString(),
|
|
30
|
-
).toMatch("1");
|
|
31
|
-
|
|
32
|
-
// If the accordion panel associated with an accordion header is visible, the header button
|
|
33
|
-
// element has aria-expanded set to true. If the panel is not visible, aria-expanded is set
|
|
34
|
-
// to false
|
|
35
|
-
expect(queryByText(container, "Open Header")).toHaveAttribute("aria-expanded", "true");
|
|
36
|
-
expect(queryByText(container, "Closed Header")).toHaveAttribute("aria-expanded", "false");
|
|
37
|
-
|
|
38
|
-
// The accordion header button element has aria-controls set to the ID of the element containing
|
|
39
|
-
// the accordion panel content
|
|
40
|
-
const headerId = queryByText(container, "Open Header")?.id;
|
|
41
|
-
const contentId = queryByText(container, "Open Content")?.id;
|
|
42
|
-
expect(queryByText(container, "Open Header")).toHaveAttribute("aria-controls", contentId);
|
|
43
|
-
|
|
44
|
-
// If the accordion panel associated with an accordion header is visible, and if the accordion
|
|
45
|
-
// does not permit the panel to be collapsed, the header button element has aria-disabled set
|
|
46
|
-
// to true
|
|
47
|
-
expect(queryByText(container, "Disabled Header")).toHaveAttribute("aria-disabled", "true");
|
|
48
|
-
|
|
49
|
-
// Optionally, each element that serves as a container for panel content has role region and
|
|
50
|
-
// aria-labelledby with a value that refers to the button that controls display of the panel
|
|
51
|
-
// * Avoid using the region role in circumstances that create landmark region proliferation,
|
|
52
|
-
// e.g., in an accordion that contains more than approximately 6 panels that can be expanded
|
|
53
|
-
// at the same time
|
|
54
|
-
// * Role region is especially helpful to the perception of structure by screen reader users
|
|
55
|
-
// when panels contain heading elements or a nested accordion
|
|
56
|
-
expect(queryByText(container, "Open Content")).toHaveAttribute("aria-labelledby", headerId);
|
|
57
|
-
expect(queryByText(container, "Region Content")).toHaveAttribute("role", "region");
|
|
58
|
-
});
|
|
59
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Accordion,
|
|
3
|
-
AccordionItem,
|
|
4
|
-
AccordionHeader,
|
|
5
|
-
AccordionTrigger,
|
|
6
|
-
AccordionContent,
|
|
7
|
-
} from "../../../src/Accordion/index";
|
|
8
|
-
|
|
9
|
-
export default function AccordionAccessibility($props: any) {
|
|
10
|
-
@render {
|
|
11
|
-
<Accordion value={$props.value} type="multiple">
|
|
12
|
-
<AccordionItem>
|
|
13
|
-
<AccordionHeader as="h4">
|
|
14
|
-
<AccordionTrigger>Role Header</AccordionTrigger>
|
|
15
|
-
</AccordionHeader>
|
|
16
|
-
<AccordionContent>Role Content</AccordionContent>
|
|
17
|
-
</AccordionItem>
|
|
18
|
-
<AccordionItem>
|
|
19
|
-
<AccordionHeader as="h4">
|
|
20
|
-
<AccordionTrigger>Open Header</AccordionTrigger>
|
|
21
|
-
</AccordionHeader>
|
|
22
|
-
<AccordionContent>Open Content</AccordionContent>
|
|
23
|
-
</AccordionItem>
|
|
24
|
-
<AccordionItem>
|
|
25
|
-
<AccordionHeader as="h4">
|
|
26
|
-
<AccordionTrigger>Closed Header</AccordionTrigger>
|
|
27
|
-
</AccordionHeader>
|
|
28
|
-
<AccordionContent>Closed Content</AccordionContent>
|
|
29
|
-
</AccordionItem>
|
|
30
|
-
<AccordionItem>
|
|
31
|
-
<AccordionHeader as="h4">
|
|
32
|
-
<AccordionTrigger>Region Header</AccordionTrigger>
|
|
33
|
-
</AccordionHeader>
|
|
34
|
-
<AccordionContent ariaRegion>Region Content</AccordionContent>
|
|
35
|
-
</AccordionItem>
|
|
36
|
-
<AccordionItem disabled>
|
|
37
|
-
<AccordionHeader as="h4">
|
|
38
|
-
<AccordionTrigger>Disabled Header</AccordionTrigger>
|
|
39
|
-
</AccordionHeader>
|
|
40
|
-
<AccordionContent>Disabled Content</AccordionContent>
|
|
41
|
-
</AccordionItem>
|
|
42
|
-
</Accordion>
|
|
43
|
-
}
|
|
44
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Accordion,
|
|
3
|
-
AccordionItem,
|
|
4
|
-
AccordionHeader,
|
|
5
|
-
AccordionTrigger,
|
|
6
|
-
AccordionContent,
|
|
7
|
-
} from "../../../src/Accordion/index";
|
|
8
|
-
|
|
9
|
-
export default function AccordionKeyboard($props: any) {
|
|
10
|
-
@render {
|
|
11
|
-
<Accordion value={$props.value} type="multiple">
|
|
12
|
-
<AccordionItem>
|
|
13
|
-
<AccordionHeader as="h4">
|
|
14
|
-
<AccordionTrigger>Header 1</AccordionTrigger>
|
|
15
|
-
</AccordionHeader>
|
|
16
|
-
<AccordionContent>Content 1</AccordionContent>
|
|
17
|
-
</AccordionItem>
|
|
18
|
-
<AccordionItem>
|
|
19
|
-
<AccordionHeader as="h4">
|
|
20
|
-
<AccordionTrigger>Header 2</AccordionTrigger>
|
|
21
|
-
</AccordionHeader>
|
|
22
|
-
<AccordionContent>Content 2</AccordionContent>
|
|
23
|
-
</AccordionItem>
|
|
24
|
-
<AccordionItem>
|
|
25
|
-
<AccordionHeader as="h4">
|
|
26
|
-
<AccordionTrigger>Header 3</AccordionTrigger>
|
|
27
|
-
</AccordionHeader>
|
|
28
|
-
<AccordionContent>Content 3</AccordionContent>
|
|
29
|
-
</AccordionItem>
|
|
30
|
-
</Accordion>
|
|
31
|
-
}
|
|
32
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Accordion,
|
|
3
|
-
AccordionItem,
|
|
4
|
-
AccordionHeader,
|
|
5
|
-
AccordionTrigger,
|
|
6
|
-
AccordionContent,
|
|
7
|
-
} from "../../../src/Accordion/index";
|
|
8
|
-
|
|
9
|
-
export default function AccordionList($props: any) {
|
|
10
|
-
@render {
|
|
11
|
-
<Accordion value={$props.value}>
|
|
12
|
-
@for (let item of $props.items) {
|
|
13
|
-
@key = item.id
|
|
14
|
-
<AccordionItem>
|
|
15
|
-
<AccordionHeader as="h4">
|
|
16
|
-
<AccordionTrigger>{item.header}</AccordionTrigger>
|
|
17
|
-
</AccordionHeader>
|
|
18
|
-
<AccordionContent>{item.text}</AccordionContent>
|
|
19
|
-
</AccordionItem>
|
|
20
|
-
}
|
|
21
|
-
</Accordion>
|
|
22
|
-
}
|
|
23
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Accordion,
|
|
3
|
-
AccordionItem,
|
|
4
|
-
AccordionHeader,
|
|
5
|
-
AccordionTrigger,
|
|
6
|
-
AccordionContent,
|
|
7
|
-
} from "../../../src/Accordion/index";
|
|
8
|
-
|
|
9
|
-
export default function AccordionMultiple($props: any) {
|
|
10
|
-
@render {
|
|
11
|
-
<Accordion value={$props?.value} type="multiple">
|
|
12
|
-
<AccordionItem>
|
|
13
|
-
<AccordionHeader as="h4">
|
|
14
|
-
<AccordionTrigger>Header 1</AccordionTrigger>
|
|
15
|
-
</AccordionHeader>
|
|
16
|
-
<AccordionContent>Content 1</AccordionContent>
|
|
17
|
-
</AccordionItem>
|
|
18
|
-
<AccordionItem>
|
|
19
|
-
<AccordionHeader as="h4">
|
|
20
|
-
<AccordionTrigger>Header 2</AccordionTrigger>
|
|
21
|
-
</AccordionHeader>
|
|
22
|
-
<AccordionContent>Content 2</AccordionContent>
|
|
23
|
-
</AccordionItem>
|
|
24
|
-
<AccordionItem>
|
|
25
|
-
<AccordionHeader as="h4">
|
|
26
|
-
<AccordionTrigger>Header 3</AccordionTrigger>
|
|
27
|
-
</AccordionHeader>
|
|
28
|
-
<AccordionContent>Content 3</AccordionContent>
|
|
29
|
-
</AccordionItem>
|
|
30
|
-
</Accordion>
|
|
31
|
-
}
|
|
32
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Accordion,
|
|
3
|
-
AccordionItem,
|
|
4
|
-
AccordionHeader,
|
|
5
|
-
AccordionTrigger,
|
|
6
|
-
AccordionContent,
|
|
7
|
-
} from "../../../src/Accordion/index";
|
|
8
|
-
|
|
9
|
-
export default function AccordionSingle($props: any) {
|
|
10
|
-
@render {
|
|
11
|
-
<Accordion value={$props?.value}>
|
|
12
|
-
<AccordionItem>
|
|
13
|
-
<AccordionHeader as="h4">
|
|
14
|
-
<AccordionTrigger>Header 1</AccordionTrigger>
|
|
15
|
-
</AccordionHeader>
|
|
16
|
-
<AccordionContent>Content 1</AccordionContent>
|
|
17
|
-
</AccordionItem>
|
|
18
|
-
<AccordionItem>
|
|
19
|
-
<AccordionHeader as="h4">
|
|
20
|
-
<AccordionTrigger>Header 2</AccordionTrigger>
|
|
21
|
-
</AccordionHeader>
|
|
22
|
-
<AccordionContent>Content 2</AccordionContent>
|
|
23
|
-
</AccordionItem>
|
|
24
|
-
<AccordionItem>
|
|
25
|
-
<AccordionHeader as="h4">
|
|
26
|
-
<AccordionTrigger>Header 3</AccordionTrigger>
|
|
27
|
-
</AccordionHeader>
|
|
28
|
-
<AccordionContent>Content 3</AccordionContent>
|
|
29
|
-
</AccordionItem>
|
|
30
|
-
</Accordion>
|
|
31
|
-
}
|
|
32
|
-
}
|