twintrinsic 0.0.1
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/LICENSE +674 -0
- package/README.md +150 -0
- package/dist/App/App.svelte +54 -0
- package/dist/App/App.svelte.d.ts +65 -0
- package/dist/Section.svelte +25 -0
- package/dist/Section.svelte.d.ts +34 -0
- package/dist/actions/clickOutside.d.ts +9 -0
- package/dist/actions/clickOutside.js +19 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +1 -0
- package/dist/components/Accordion/Accordion.svelte +75 -0
- package/dist/components/Accordion/Accordion.svelte.d.ts +39 -0
- package/dist/components/Accordion/AccordionItem.svelte +150 -0
- package/dist/components/Accordion/AccordionItem.svelte.d.ts +30 -0
- package/dist/components/App/App.story.md +8 -0
- package/dist/components/App/App.story.svelte +170 -0
- package/dist/components/App/App.story.svelte.d.ts +22 -0
- package/dist/components/App/App.svelte +77 -0
- package/dist/components/App/App.svelte.d.ts +66 -0
- package/dist/components/App/Split.svelte +346 -0
- package/dist/components/App/Split.svelte.d.ts +54 -0
- package/dist/components/App/index.d.ts +2 -0
- package/dist/components/App/index.js +3 -0
- package/dist/components/AppHeader/AppHeader.svelte +439 -0
- package/dist/components/AppHeader/AppHeader.svelte.d.ts +24 -0
- package/dist/components/Avatar/Avatar.svelte +300 -0
- package/dist/components/Avatar/Avatar.svelte.d.ts +48 -0
- package/dist/components/Avatar/AvatarGroup.svelte +185 -0
- package/dist/components/Avatar/AvatarGroup.svelte.d.ts +46 -0
- package/dist/components/Badge/Badge.svelte +186 -0
- package/dist/components/Badge/Badge.svelte.d.ts +51 -0
- package/dist/components/BottomBar/BottomBar.svelte +146 -0
- package/dist/components/BottomBar/BottomBar.svelte.d.ts +38 -0
- package/dist/components/Breadcrumb/Breadcrumb.svelte +77 -0
- package/dist/components/Breadcrumb/Breadcrumb.svelte.d.ts +42 -0
- package/dist/components/Breadcrumb/BreadcrumbItem.svelte +171 -0
- package/dist/components/Breadcrumb/BreadcrumbItem.svelte.d.ts +38 -0
- package/dist/components/Button/Button.svelte +252 -0
- package/dist/components/Button/Button.svelte.d.ts +80 -0
- package/dist/components/Button/ButtonGroup.svelte +127 -0
- package/dist/components/Button/ButtonGroup.svelte.d.ts +44 -0
- package/dist/components/Card/Card.svelte +152 -0
- package/dist/components/Card/Card.svelte.d.ts +55 -0
- package/dist/components/Carousel/Carousel.svelte +461 -0
- package/dist/components/Carousel/Carousel.svelte.d.ts +79 -0
- package/dist/components/Carousel/CarouselItem.svelte +149 -0
- package/dist/components/Carousel/CarouselItem.svelte.d.ts +35 -0
- package/dist/components/Chip/Chip.svelte +288 -0
- package/dist/components/Chip/Chip.svelte.d.ts +71 -0
- package/dist/components/Chip/ChipGroup.svelte +190 -0
- package/dist/components/Chip/ChipGroup.svelte.d.ts +71 -0
- package/dist/components/CodeBlock/CodeBlock.svelte +356 -0
- package/dist/components/CodeBlock/CodeBlock.svelte.d.ts +44 -0
- package/dist/components/CodeBlock/index.d.ts +1 -0
- package/dist/components/CodeBlock/index.js +1 -0
- package/dist/components/CodeBlockSpeed/CodeBlockSpeed.svelte +145 -0
- package/dist/components/CodeBlockSpeed/CodeBlockSpeed.svelte.d.ts +44 -0
- package/dist/components/CodeEditor/CodeEditor.svelte +229 -0
- package/dist/components/CodeEditor/CodeEditor.svelte.d.ts +23 -0
- package/dist/components/Combobox/Combobox.svelte +279 -0
- package/dist/components/Combobox/Combobox.svelte.d.ts +34 -0
- package/dist/components/Container/Container.svelte +45 -0
- package/dist/components/Container/Container.svelte.d.ts +36 -0
- package/dist/components/DataTable/DataTable.svelte +879 -0
- package/dist/components/DataTable/DataTable.svelte.d.ts +102 -0
- package/dist/components/Form/AutoComplete.svelte +357 -0
- package/dist/components/Form/AutoComplete.svelte.d.ts +73 -0
- package/dist/components/Form/Calendar.svelte +429 -0
- package/dist/components/Form/Calendar.svelte.d.ts +53 -0
- package/dist/components/Form/Checkbox.svelte +196 -0
- package/dist/components/Form/Checkbox.svelte.d.ts +50 -0
- package/dist/components/Form/ColorPicker.svelte +396 -0
- package/dist/components/Form/ColorPicker.svelte.d.ts +43 -0
- package/dist/components/Form/Combobox.svelte +645 -0
- package/dist/components/Form/Combobox.svelte.d.ts +93 -0
- package/dist/components/Form/Dropdown.svelte +773 -0
- package/dist/components/Form/Dropdown.svelte.d.ts +81 -0
- package/dist/components/Form/FileUpload.svelte +796 -0
- package/dist/components/Form/FileUpload.svelte.d.ts +78 -0
- package/dist/components/Form/FloatLabel.svelte +245 -0
- package/dist/components/Form/FloatLabel.svelte.d.ts +44 -0
- package/dist/components/Form/Form.svelte +281 -0
- package/dist/components/Form/Form.svelte.d.ts +54 -0
- package/dist/components/Form/FormField.svelte +218 -0
- package/dist/components/Form/FormField.svelte.d.ts +47 -0
- package/dist/components/Form/Input.svelte +340 -0
- package/dist/components/Form/Input.svelte.d.ts +79 -0
- package/dist/components/Form/InputSwitch.svelte +189 -0
- package/dist/components/Form/InputSwitch.svelte.d.ts +46 -0
- package/dist/components/Form/InvalidState.svelte +97 -0
- package/dist/components/Form/InvalidState.svelte.d.ts +37 -0
- package/dist/components/Form/Knob.svelte +537 -0
- package/dist/components/Form/Knob.svelte.d.ts +78 -0
- package/dist/components/Form/ListInput.svelte +469 -0
- package/dist/components/Form/ListInput.svelte.d.ts +70 -0
- package/dist/components/Form/Listbox.svelte +513 -0
- package/dist/components/Form/Listbox.svelte.d.ts +74 -0
- package/dist/components/Form/NumberInput.svelte +452 -0
- package/dist/components/Form/NumberInput.svelte.d.ts +82 -0
- package/dist/components/Form/Radio.svelte +192 -0
- package/dist/components/Form/Radio.svelte.d.ts +53 -0
- package/dist/components/Form/RadioGroup.svelte +155 -0
- package/dist/components/Form/RadioGroup.svelte.d.ts +48 -0
- package/dist/components/Form/Rating.svelte +380 -0
- package/dist/components/Form/Rating.svelte.d.ts +64 -0
- package/dist/components/Form/Select.svelte +436 -0
- package/dist/components/Form/Select.svelte.d.ts +49 -0
- package/dist/components/Form/SelectGroup.svelte +34 -0
- package/dist/components/Form/SelectGroup.svelte.d.ts +33 -0
- package/dist/components/Form/Slider.svelte +622 -0
- package/dist/components/Form/Slider.svelte.d.ts +73 -0
- package/dist/components/Form/Switch.svelte +192 -0
- package/dist/components/Form/Switch.svelte.d.ts +46 -0
- package/dist/components/Form/TextInput.svelte +274 -0
- package/dist/components/Form/TextInput.svelte.d.ts +74 -0
- package/dist/components/Form/Textarea.svelte +207 -0
- package/dist/components/Form/Textarea.svelte.d.ts +62 -0
- package/dist/components/Icon/Icon.svelte +140 -0
- package/dist/components/Icon/Icon.svelte.d.ts +25 -0
- package/dist/components/Icon/index.d.ts +1 -0
- package/dist/components/Icon/index.js +1 -0
- package/dist/components/Lazy/Lazy.svelte +158 -0
- package/dist/components/Lazy/Lazy.svelte.d.ts +42 -0
- package/dist/components/Masonry/Masonry.svelte +299 -0
- package/dist/components/Masonry/Masonry.svelte.d.ts +55 -0
- package/dist/components/Menu/Menu/Menu.svelte +65 -0
- package/dist/components/Menu/Menu/Menu.svelte.d.ts +17 -0
- package/dist/components/Menu/Menu/MenuItem.svelte +90 -0
- package/dist/components/Menu/Menu/MenuItem.svelte.d.ts +27 -0
- package/dist/components/Modal/Modal.svelte +334 -0
- package/dist/components/Modal/Modal.svelte.d.ts +55 -0
- package/dist/components/Panel/Card.svelte +141 -0
- package/dist/components/Panel/Card.svelte.d.ts +52 -0
- package/dist/components/Panel/Hero/Hero.story.md +9 -0
- package/dist/components/Panel/Hero/Hero.story.svelte +49 -0
- package/dist/components/Panel/Hero/Hero.story.svelte.d.ts +21 -0
- package/dist/components/Panel/Hero/Hero.svelte +24 -0
- package/dist/components/Panel/Hero/Hero.svelte.d.ts +32 -0
- package/dist/components/Panel/LazyPanel.svelte +110 -0
- package/dist/components/Panel/LazyPanel.svelte.d.ts +46 -0
- package/dist/components/Panel/Panel.svelte +205 -0
- package/dist/components/Panel/Panel.svelte.d.ts +23 -0
- package/dist/components/Progress/Progress.svelte +220 -0
- package/dist/components/Progress/Progress.svelte.d.ts +61 -0
- package/dist/components/Separator/Separator.svelte +109 -0
- package/dist/components/Separator/Separator.svelte.d.ts +35 -0
- package/dist/components/Sidebar/Sidebar.svelte +213 -0
- package/dist/components/Sidebar/Sidebar.svelte.d.ts +60 -0
- package/dist/components/Skeleton/Skeleton.svelte +170 -0
- package/dist/components/Skeleton/Skeleton.svelte.d.ts +48 -0
- package/dist/components/Stepper/Stepper.svelte +111 -0
- package/dist/components/Stepper/Stepper.svelte.d.ts +54 -0
- package/dist/components/Stepper/StepperStep.svelte +369 -0
- package/dist/components/Stepper/StepperStep.svelte.d.ts +63 -0
- package/dist/components/Table/Table.svelte +167 -0
- package/dist/components/Table/Table.svelte.d.ts +56 -0
- package/dist/components/Table/TableBody.svelte +41 -0
- package/dist/components/Table/TableBody.svelte.d.ts +33 -0
- package/dist/components/Table/TableCell.svelte +76 -0
- package/dist/components/Table/TableCell.svelte.d.ts +36 -0
- package/dist/components/Table/TableHead.svelte +41 -0
- package/dist/components/Table/TableHead.svelte.d.ts +32 -0
- package/dist/components/Table/TableHeader.svelte +148 -0
- package/dist/components/Table/TableHeader.svelte.d.ts +42 -0
- package/dist/components/Table/TableRow.svelte +99 -0
- package/dist/components/Table/TableRow.svelte.d.ts +40 -0
- package/dist/components/Tabs/Tab.svelte +145 -0
- package/dist/components/Tabs/Tab.svelte.d.ts +36 -0
- package/dist/components/Tabs/TabList.svelte +60 -0
- package/dist/components/Tabs/TabList.svelte.d.ts +32 -0
- package/dist/components/Tabs/TabPanel.svelte +118 -0
- package/dist/components/Tabs/TabPanel.svelte.d.ts +38 -0
- package/dist/components/Tabs/Tabs.svelte +287 -0
- package/dist/components/Tabs/Tabs.svelte.d.ts +50 -0
- package/dist/components/Tag/Tag.svelte +260 -0
- package/dist/components/Tag/Tag.svelte.d.ts +54 -0
- package/dist/components/Tag/TagGroup.svelte +147 -0
- package/dist/components/Tag/TagGroup.svelte.d.ts +62 -0
- package/dist/components/ThemeToggle/ThemeToggle.svelte +93 -0
- package/dist/components/ThemeToggle/ThemeToggle.svelte.d.ts +12 -0
- package/dist/components/Timeline/Timeline.svelte +144 -0
- package/dist/components/Timeline/Timeline.svelte.d.ts +48 -0
- package/dist/components/Timeline/TimelineItem.svelte +391 -0
- package/dist/components/Timeline/TimelineItem.svelte.d.ts +63 -0
- package/dist/components/Toast/Toast.svelte +313 -0
- package/dist/components/Toast/Toast.svelte.d.ts +44 -0
- package/dist/components/Toast/toastStore.d.ts +40 -0
- package/dist/components/Toast/toastStore.js +293 -0
- package/dist/components/Tooltip/Tooltip.svelte +282 -0
- package/dist/components/Tooltip/Tooltip.svelte.d.ts +55 -0
- package/dist/components/Tree/Tree.svelte +129 -0
- package/dist/components/Tree/Tree.svelte.d.ts +61 -0
- package/dist/components/Tree/TreeNode.svelte +332 -0
- package/dist/components/Tree/TreeNode.svelte.d.ts +55 -0
- package/dist/components/icons/TwintrinsicLogo.svelte +73 -0
- package/dist/components/icons/TwintrinsicLogo.svelte.d.ts +17 -0
- package/dist/components/icons/twintrinsic-source.svg +73 -0
- package/dist/components/icons/twintrinsic.svg +38 -0
- package/dist/docs/EventsTable.svelte +86 -0
- package/dist/docs/EventsTable.svelte.d.ts +27 -0
- package/dist/docs/PropsTable.svelte +103 -0
- package/dist/docs/PropsTable.svelte.d.ts +28 -0
- package/dist/docs/index.d.ts +2 -0
- package/dist/docs/index.js +2 -0
- package/dist/helpers/detectLanguage.d.ts +6 -0
- package/dist/helpers/detectLanguage.js +60 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +1 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +94 -0
- package/dist/twintrinsic.css +347 -0
- package/package.json +98 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Calendar - A date picker component with month navigation, range selection,
|
|
4
|
+
and keyboard controls. Built with accessibility in mind.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
```svelte
|
|
8
|
+
<Calendar
|
|
9
|
+
value={selectedDate}
|
|
10
|
+
onselect={handleSelect}
|
|
11
|
+
/>
|
|
12
|
+
|
|
13
|
+
<Calendar
|
|
14
|
+
value={dateRange}
|
|
15
|
+
range={true}
|
|
16
|
+
minDate={minDate}
|
|
17
|
+
maxDate={maxDate}
|
|
18
|
+
onselect={handleRangeSelect}
|
|
19
|
+
/>
|
|
20
|
+
```
|
|
21
|
+
-->
|
|
22
|
+
<script>
|
|
23
|
+
import { slide } from "svelte/transition"
|
|
24
|
+
import { clickOutside } from "../../actions"
|
|
25
|
+
import Input from "./Input.svelte"
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
/** @type {Date | [Date, Date] | null} - Selected date or date range */
|
|
29
|
+
value = null,
|
|
30
|
+
/** @type {boolean} - Whether to allow range selection */
|
|
31
|
+
range = false,
|
|
32
|
+
/** @type {Date | null} - Minimum selectable date */
|
|
33
|
+
minDate = null,
|
|
34
|
+
/** @type {Date | null} - Maximum selectable date */
|
|
35
|
+
maxDate = null,
|
|
36
|
+
/** @type {boolean} - Whether to show week numbers */
|
|
37
|
+
showWeekNumbers = false,
|
|
38
|
+
/** @type {string[]} - Custom day names */
|
|
39
|
+
dayNames = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
|
|
40
|
+
/** @type {string[]} - Custom month names */
|
|
41
|
+
monthNames = [
|
|
42
|
+
"January",
|
|
43
|
+
"February",
|
|
44
|
+
"March",
|
|
45
|
+
"April",
|
|
46
|
+
"May",
|
|
47
|
+
"June",
|
|
48
|
+
"July",
|
|
49
|
+
"August",
|
|
50
|
+
"September",
|
|
51
|
+
"October",
|
|
52
|
+
"November",
|
|
53
|
+
"December",
|
|
54
|
+
],
|
|
55
|
+
/** @type {string} - Input label */
|
|
56
|
+
label = "Date",
|
|
57
|
+
/** @type {string} - Date format for display */
|
|
58
|
+
format = "MM/dd/yyyy",
|
|
59
|
+
/** @type {boolean} - Whether the calendar is disabled */
|
|
60
|
+
disabled = false,
|
|
61
|
+
/** @type {string} - Additional CSS classes */
|
|
62
|
+
class: className = "",
|
|
63
|
+
/** @type {(event: CustomEvent) => void} - Select event handler */
|
|
64
|
+
onselect,
|
|
65
|
+
} = $props()
|
|
66
|
+
|
|
67
|
+
let showCalendar = $state(false)
|
|
68
|
+
let currentMonth = $state(new Date())
|
|
69
|
+
let hoverDate = $state(null)
|
|
70
|
+
let inputValue = $state("")
|
|
71
|
+
let startDate = $state(null)
|
|
72
|
+
let endDate = $state(null)
|
|
73
|
+
let calendarElement = $state()
|
|
74
|
+
|
|
75
|
+
// Initialize dates from value prop
|
|
76
|
+
$effect(() => {
|
|
77
|
+
if (value) {
|
|
78
|
+
if (range && Array.isArray(value)) {
|
|
79
|
+
;[startDate, endDate] = value
|
|
80
|
+
currentMonth = new Date(startDate)
|
|
81
|
+
} else if (!range && value instanceof Date) {
|
|
82
|
+
startDate = value
|
|
83
|
+
currentMonth = new Date(value)
|
|
84
|
+
}
|
|
85
|
+
updateInputValue()
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// Get days in month matrix
|
|
90
|
+
function getDaysInMonth(date) {
|
|
91
|
+
const year = date.getFullYear()
|
|
92
|
+
const month = date.getMonth()
|
|
93
|
+
const firstDay = new Date(year, month, 1)
|
|
94
|
+
const lastDay = new Date(year, month + 1, 0)
|
|
95
|
+
const days = []
|
|
96
|
+
let week = []
|
|
97
|
+
|
|
98
|
+
// Fill in leading empty cells
|
|
99
|
+
for (let i = 0; i < firstDay.getDay(); i++) {
|
|
100
|
+
week.push(null)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Fill in days
|
|
104
|
+
for (let day = 1; day <= lastDay.getDate(); day++) {
|
|
105
|
+
if (week.length === 7) {
|
|
106
|
+
days.push(week)
|
|
107
|
+
week = []
|
|
108
|
+
}
|
|
109
|
+
week.push(new Date(year, month, day))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Fill in trailing empty cells
|
|
113
|
+
while (week.length < 7) {
|
|
114
|
+
week.push(null)
|
|
115
|
+
}
|
|
116
|
+
days.push(week)
|
|
117
|
+
|
|
118
|
+
return days
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Get week number
|
|
122
|
+
function getWeekNumber(date) {
|
|
123
|
+
const target = new Date(date.valueOf())
|
|
124
|
+
const dayNr = (date.getDay() + 6) % 7
|
|
125
|
+
target.setDate(target.getDate() - dayNr + 3)
|
|
126
|
+
const firstThursday = target.valueOf()
|
|
127
|
+
target.setMonth(0, 1)
|
|
128
|
+
if (target.getDay() !== 4) {
|
|
129
|
+
target.setMonth(0, 1 + ((4 - target.getDay() + 7) % 7))
|
|
130
|
+
}
|
|
131
|
+
return 1 + Math.ceil((firstThursday - target) / 604800000)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Format date for display
|
|
135
|
+
function formatDate(date) {
|
|
136
|
+
if (!date) return ""
|
|
137
|
+
|
|
138
|
+
return format
|
|
139
|
+
.replace("MM", String(date.getMonth() + 1).padStart(2, "0"))
|
|
140
|
+
.replace("dd", String(date.getDate()).padStart(2, "0"))
|
|
141
|
+
.replace("yyyy", date.getFullYear())
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Update input value based on selected dates
|
|
145
|
+
function updateInputValue() {
|
|
146
|
+
if (range) {
|
|
147
|
+
inputValue = startDate && endDate ? `${formatDate(startDate)} - ${formatDate(endDate)}` : ""
|
|
148
|
+
} else {
|
|
149
|
+
inputValue = startDate ? formatDate(startDate) : ""
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Handle date selection
|
|
154
|
+
function handleDateSelect(date) {
|
|
155
|
+
if (disabled) return
|
|
156
|
+
|
|
157
|
+
if (range) {
|
|
158
|
+
if (!startDate || (startDate && endDate) || date < startDate) {
|
|
159
|
+
startDate = date
|
|
160
|
+
endDate = null
|
|
161
|
+
} else {
|
|
162
|
+
endDate = date
|
|
163
|
+
showCalendar = false
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
onselect?.(new CustomEvent("select", { detail: { start: startDate, end: endDate } }))
|
|
167
|
+
} else {
|
|
168
|
+
startDate = date
|
|
169
|
+
showCalendar = false
|
|
170
|
+
onselect?.(new CustomEvent("select", { detail: { date } }))
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
updateInputValue()
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Handle date hover for range selection
|
|
177
|
+
function handleDateHover(date) {
|
|
178
|
+
if (range && startDate && !endDate) {
|
|
179
|
+
hoverDate = date
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Check if date is in range
|
|
184
|
+
function isInRange(date) {
|
|
185
|
+
if (!date) return false
|
|
186
|
+
if (range) {
|
|
187
|
+
if (startDate && !endDate && hoverDate) {
|
|
188
|
+
return date >= startDate && date <= hoverDate
|
|
189
|
+
}
|
|
190
|
+
return startDate && endDate && date >= startDate && date <= endDate
|
|
191
|
+
}
|
|
192
|
+
return false
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Check if date is selected
|
|
196
|
+
function isSelected(date) {
|
|
197
|
+
if (!date) return false
|
|
198
|
+
if (range) {
|
|
199
|
+
return (
|
|
200
|
+
(startDate && date.getTime() === startDate.getTime()) ||
|
|
201
|
+
(endDate && date.getTime() === endDate.getTime())
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
return startDate && date.getTime() === startDate.getTime()
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Check if date is disabled
|
|
208
|
+
function isDisabled(date) {
|
|
209
|
+
if (!date) return true
|
|
210
|
+
if (minDate && date < minDate) return true
|
|
211
|
+
if (maxDate && date > maxDate) return true
|
|
212
|
+
return false
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Navigate to previous/next month
|
|
216
|
+
function navigateMonth(delta) {
|
|
217
|
+
const newMonth = new Date(currentMonth)
|
|
218
|
+
newMonth.setMonth(newMonth.getMonth() + delta)
|
|
219
|
+
currentMonth = newMonth
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Handle keyboard navigation
|
|
223
|
+
function handleKeydown(event) {
|
|
224
|
+
if (!showCalendar) return
|
|
225
|
+
|
|
226
|
+
const key = event.key
|
|
227
|
+
const newDate = new Date(currentMonth)
|
|
228
|
+
|
|
229
|
+
switch (key) {
|
|
230
|
+
case "ArrowLeft":
|
|
231
|
+
event.preventDefault()
|
|
232
|
+
newDate.setDate(newDate.getDate() - 1)
|
|
233
|
+
break
|
|
234
|
+
case "ArrowRight":
|
|
235
|
+
event.preventDefault()
|
|
236
|
+
newDate.setDate(newDate.getDate() + 1)
|
|
237
|
+
break
|
|
238
|
+
case "ArrowUp":
|
|
239
|
+
event.preventDefault()
|
|
240
|
+
newDate.setDate(newDate.getDate() - 7)
|
|
241
|
+
break
|
|
242
|
+
case "ArrowDown":
|
|
243
|
+
event.preventDefault()
|
|
244
|
+
newDate.setDate(newDate.getDate() + 7)
|
|
245
|
+
break
|
|
246
|
+
case "Enter":
|
|
247
|
+
event.preventDefault()
|
|
248
|
+
handleDateSelect(currentMonth)
|
|
249
|
+
break
|
|
250
|
+
case "Escape":
|
|
251
|
+
event.preventDefault()
|
|
252
|
+
showCalendar = false
|
|
253
|
+
break
|
|
254
|
+
default:
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (!isDisabled(newDate)) {
|
|
259
|
+
currentMonth = newDate
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
</script>
|
|
263
|
+
|
|
264
|
+
<div
|
|
265
|
+
class="calendar-container {className}"
|
|
266
|
+
use:clickOutside
|
|
267
|
+
onclickOutside={() => showCalendar = false}
|
|
268
|
+
>
|
|
269
|
+
<Input
|
|
270
|
+
{label}
|
|
271
|
+
{disabled}
|
|
272
|
+
value={inputValue}
|
|
273
|
+
readonly
|
|
274
|
+
rightIcon="calendar"
|
|
275
|
+
onclick={() => showCalendar = !showCalendar}
|
|
276
|
+
onrightIconClick={() => showCalendar = !showCalendar}
|
|
277
|
+
/>
|
|
278
|
+
|
|
279
|
+
{#if showCalendar}
|
|
280
|
+
<div
|
|
281
|
+
class="calendar"
|
|
282
|
+
role="dialog"
|
|
283
|
+
aria-label="Calendar"
|
|
284
|
+
transition:slide={{ duration: 150 }}
|
|
285
|
+
onkeydown={handleKeydown}
|
|
286
|
+
>
|
|
287
|
+
<div class="calendar-header">
|
|
288
|
+
<button
|
|
289
|
+
type="button"
|
|
290
|
+
class="calendar-nav-btn"
|
|
291
|
+
onclick={() => navigateMonth(-1)}
|
|
292
|
+
aria-label="Previous month"
|
|
293
|
+
disabled={disabled}
|
|
294
|
+
>
|
|
295
|
+
<svg class="w-5 h-5" viewBox="0 0 20 20" fill="currentColor">
|
|
296
|
+
<path d="M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z" />
|
|
297
|
+
</svg>
|
|
298
|
+
</button>
|
|
299
|
+
|
|
300
|
+
<div class="calendar-title">
|
|
301
|
+
{monthNames[currentMonth.getMonth()]} {currentMonth.getFullYear()}
|
|
302
|
+
</div>
|
|
303
|
+
|
|
304
|
+
<button
|
|
305
|
+
type="button"
|
|
306
|
+
class="calendar-nav-btn"
|
|
307
|
+
onclick={() => navigateMonth(1)}
|
|
308
|
+
aria-label="Next month"
|
|
309
|
+
disabled={disabled}
|
|
310
|
+
>
|
|
311
|
+
<svg class="w-5 h-5" viewBox="0 0 20 20" fill="currentColor">
|
|
312
|
+
<path d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" />
|
|
313
|
+
</svg>
|
|
314
|
+
</button>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
<table class="calendar-grid" role="grid">
|
|
318
|
+
<thead>
|
|
319
|
+
<tr>
|
|
320
|
+
{#if showWeekNumbers}
|
|
321
|
+
<th scope="col">Wk</th>
|
|
322
|
+
{/if}
|
|
323
|
+
{#each dayNames as day}
|
|
324
|
+
<th scope="col">{day}</th>
|
|
325
|
+
{/each}
|
|
326
|
+
</tr>
|
|
327
|
+
</thead>
|
|
328
|
+
<tbody>
|
|
329
|
+
{#each getDaysInMonth(currentMonth) as week}
|
|
330
|
+
<tr>
|
|
331
|
+
{#if showWeekNumbers}
|
|
332
|
+
<td class="calendar-week">
|
|
333
|
+
{#if week[0]}
|
|
334
|
+
{getWeekNumber(week[0])}
|
|
335
|
+
{/if}
|
|
336
|
+
</td>
|
|
337
|
+
{/if}
|
|
338
|
+
{#each week as day}
|
|
339
|
+
<td
|
|
340
|
+
class="calendar-day"
|
|
341
|
+
class:calendar-day-selected={isSelected(day)}
|
|
342
|
+
class:calendar-day-in-range={isInRange(day)}
|
|
343
|
+
class:calendar-day-disabled={isDisabled(day)}
|
|
344
|
+
>
|
|
345
|
+
{#if day}
|
|
346
|
+
<button
|
|
347
|
+
type="button"
|
|
348
|
+
disabled={isDisabled(day)}
|
|
349
|
+
onclick={() => handleDateSelect(day)}
|
|
350
|
+
onmouseenter={() => handleDateHover(day)}
|
|
351
|
+
aria-label={formatDate(day)}
|
|
352
|
+
aria-selected={isSelected(day)}
|
|
353
|
+
>
|
|
354
|
+
{day.getDate()}
|
|
355
|
+
</button>
|
|
356
|
+
{/if}
|
|
357
|
+
</td>
|
|
358
|
+
{/each}
|
|
359
|
+
</tr>
|
|
360
|
+
{/each}
|
|
361
|
+
</tbody>
|
|
362
|
+
</table>
|
|
363
|
+
</div>
|
|
364
|
+
{/if}
|
|
365
|
+
</div>
|
|
366
|
+
|
|
367
|
+
<style>
|
|
368
|
+
@reference "../../twintrinsic.css";
|
|
369
|
+
|
|
370
|
+
.calendar-container {
|
|
371
|
+
@apply relative inline-block w-full;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.calendar {
|
|
375
|
+
@apply absolute z-50 mt-1 p-4;
|
|
376
|
+
@apply bg-surface border border-border rounded-md shadow-lg;
|
|
377
|
+
@apply min-w-[280px];
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.calendar-header {
|
|
381
|
+
@apply flex items-center justify-between mb-4;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.calendar-title {
|
|
385
|
+
@apply text-sm font-medium;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.calendar-nav-btn {
|
|
389
|
+
@apply p-1 rounded-md;
|
|
390
|
+
@apply hover:bg-hover focus:bg-hover;
|
|
391
|
+
@apply disabled:opacity-50 disabled:cursor-not-allowed;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.calendar-grid {
|
|
395
|
+
@apply w-full border-collapse;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.calendar-grid th {
|
|
399
|
+
@apply p-1 text-xs font-medium text-muted text-center;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.calendar-week {
|
|
403
|
+
@apply p-1 text-xs text-muted text-center;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.calendar-day {
|
|
407
|
+
@apply p-0 text-center;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.calendar-day button {
|
|
411
|
+
@apply w-8 h-8 rounded-md text-sm;
|
|
412
|
+
@apply hover:bg-hover focus:bg-hover;
|
|
413
|
+
@apply disabled:opacity-50 disabled:cursor-not-allowed;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.calendar-day-selected button {
|
|
417
|
+
@apply bg-primary text-primary-text;
|
|
418
|
+
@apply hover:bg-primary-hover focus:bg-primary-hover;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.calendar-day-in-range {
|
|
422
|
+
@apply bg-primary-50 dark:bg-primary-900;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.calendar-day-disabled button {
|
|
426
|
+
@apply opacity-50 cursor-not-allowed;
|
|
427
|
+
@apply hover:bg-transparent focus:bg-transparent;
|
|
428
|
+
}
|
|
429
|
+
</style>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export default Calendar;
|
|
2
|
+
type Calendar = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Calendar - A date picker component with month navigation, range selection,
|
|
8
|
+
* and keyboard controls. Built with accessibility in mind.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <Calendar
|
|
13
|
+
* value={selectedDate}
|
|
14
|
+
* onselect={handleSelect}
|
|
15
|
+
* />
|
|
16
|
+
*
|
|
17
|
+
* <Calendar
|
|
18
|
+
* value={dateRange}
|
|
19
|
+
* range={true}
|
|
20
|
+
* minDate={minDate}
|
|
21
|
+
* maxDate={maxDate}
|
|
22
|
+
* onselect={handleRangeSelect}
|
|
23
|
+
* />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
declare const Calendar: import("svelte").Component<{
|
|
27
|
+
value?: any;
|
|
28
|
+
range?: boolean;
|
|
29
|
+
minDate?: any;
|
|
30
|
+
maxDate?: any;
|
|
31
|
+
showWeekNumbers?: boolean;
|
|
32
|
+
dayNames?: any[];
|
|
33
|
+
monthNames?: any[];
|
|
34
|
+
label?: string;
|
|
35
|
+
format?: string;
|
|
36
|
+
disabled?: boolean;
|
|
37
|
+
class?: string;
|
|
38
|
+
onselect: any;
|
|
39
|
+
}, {}, "">;
|
|
40
|
+
type $$ComponentProps = {
|
|
41
|
+
value?: any;
|
|
42
|
+
range?: boolean;
|
|
43
|
+
minDate?: any;
|
|
44
|
+
maxDate?: any;
|
|
45
|
+
showWeekNumbers?: boolean;
|
|
46
|
+
dayNames?: any[];
|
|
47
|
+
monthNames?: any[];
|
|
48
|
+
label?: string;
|
|
49
|
+
format?: string;
|
|
50
|
+
disabled?: boolean;
|
|
51
|
+
class?: string;
|
|
52
|
+
onselect: any;
|
|
53
|
+
};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Checkbox - A form component for boolean or indeterminate input with customizable
|
|
4
|
+
labels, descriptions, and validation states.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
```svelte
|
|
8
|
+
<Checkbox
|
|
9
|
+
label="Accept terms"
|
|
10
|
+
onchange={handleChange}
|
|
11
|
+
/>
|
|
12
|
+
|
|
13
|
+
<Checkbox
|
|
14
|
+
label="Select all"
|
|
15
|
+
indeterminate={someSelected}
|
|
16
|
+
checked={allSelected}
|
|
17
|
+
description="Select all items in the list"
|
|
18
|
+
/>
|
|
19
|
+
```
|
|
20
|
+
-->
|
|
21
|
+
<script>
|
|
22
|
+
const {
|
|
23
|
+
/** @type {string} - Label text */
|
|
24
|
+
label,
|
|
25
|
+
/** @type {string} - Description text */
|
|
26
|
+
description = "",
|
|
27
|
+
/** @type {boolean} - Whether the checkbox is checked */
|
|
28
|
+
checked = false,
|
|
29
|
+
/** @type {boolean} - Whether the checkbox is in an indeterminate state */
|
|
30
|
+
indeterminate = false,
|
|
31
|
+
/** @type {boolean} - Whether the checkbox is disabled */
|
|
32
|
+
disabled = false,
|
|
33
|
+
/** @type {boolean} - Whether the checkbox is required */
|
|
34
|
+
required = false,
|
|
35
|
+
/** @type {string} - Error message */
|
|
36
|
+
error = "",
|
|
37
|
+
/** @type {string} - Name attribute */
|
|
38
|
+
name = "",
|
|
39
|
+
/** @type {string} - Value attribute */
|
|
40
|
+
value = "",
|
|
41
|
+
/** @type {string} - Additional CSS classes */
|
|
42
|
+
class: className = "",
|
|
43
|
+
/** @type {(event: CustomEvent) => void} - Change event handler */
|
|
44
|
+
onchange,
|
|
45
|
+
} = $props()
|
|
46
|
+
|
|
47
|
+
let checkboxEl = $state()
|
|
48
|
+
|
|
49
|
+
// Update indeterminate state
|
|
50
|
+
$effect(() => {
|
|
51
|
+
if (checkboxEl) {
|
|
52
|
+
checkboxEl.indeterminate = indeterminate
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
// Handle change event
|
|
57
|
+
function handleChange(event) {
|
|
58
|
+
const isChecked = event.target.checked
|
|
59
|
+
onchange?.(new CustomEvent("change", { detail: { checked: isChecked } }))
|
|
60
|
+
}
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<div class="checkbox-container {className}">
|
|
64
|
+
<label
|
|
65
|
+
class="checkbox"
|
|
66
|
+
class:checkbox-disabled={disabled}
|
|
67
|
+
class:checkbox-error={error}
|
|
68
|
+
>
|
|
69
|
+
<input
|
|
70
|
+
type="checkbox"
|
|
71
|
+
bind:this={checkboxEl}
|
|
72
|
+
{name}
|
|
73
|
+
{value}
|
|
74
|
+
{checked}
|
|
75
|
+
{disabled}
|
|
76
|
+
{required}
|
|
77
|
+
aria-invalid={!!error}
|
|
78
|
+
aria-describedby={error ? `${name}-error` : undefined}
|
|
79
|
+
onchange={handleChange}
|
|
80
|
+
/>
|
|
81
|
+
<span class="checkbox-control" aria-hidden="true">
|
|
82
|
+
{#if checked && !indeterminate}
|
|
83
|
+
<svg class="w-4 h-4" viewBox="0 0 20 20" fill="currentColor">
|
|
84
|
+
<path d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" />
|
|
85
|
+
</svg>
|
|
86
|
+
{:else if indeterminate}
|
|
87
|
+
<svg class="w-4 h-4" viewBox="0 0 20 20" fill="currentColor">
|
|
88
|
+
<path d="M5 10a1 1 0 011-1h8a1 1 0 110 2H6a1 1 0 01-1-1z" />
|
|
89
|
+
</svg>
|
|
90
|
+
{/if}
|
|
91
|
+
</span>
|
|
92
|
+
<span class="checkbox-label">
|
|
93
|
+
{label}
|
|
94
|
+
{#if required}
|
|
95
|
+
<span class="checkbox-required" aria-hidden="true">*</span>
|
|
96
|
+
{/if}
|
|
97
|
+
</span>
|
|
98
|
+
</label>
|
|
99
|
+
|
|
100
|
+
{#if description}
|
|
101
|
+
<div class="checkbox-description">
|
|
102
|
+
{description}
|
|
103
|
+
</div>
|
|
104
|
+
{/if}
|
|
105
|
+
|
|
106
|
+
{#if error}
|
|
107
|
+
<div
|
|
108
|
+
class="checkbox-error-text"
|
|
109
|
+
id="{name}-error"
|
|
110
|
+
role="alert"
|
|
111
|
+
>
|
|
112
|
+
{error}
|
|
113
|
+
</div>
|
|
114
|
+
{/if}
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<style>
|
|
118
|
+
@reference "../../twintrinsic.css";
|
|
119
|
+
|
|
120
|
+
.checkbox-container {
|
|
121
|
+
@apply flex flex-col gap-1;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.checkbox {
|
|
125
|
+
@apply relative flex items-start gap-2;
|
|
126
|
+
@apply cursor-pointer select-none;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.checkbox-disabled {
|
|
130
|
+
@apply cursor-not-allowed opacity-50;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* Hide default checkbox */
|
|
134
|
+
.checkbox input {
|
|
135
|
+
@apply absolute w-0 h-0 opacity-0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Custom checkbox control */
|
|
139
|
+
.checkbox-control {
|
|
140
|
+
@apply flex items-center justify-center;
|
|
141
|
+
@apply w-5 h-5 rounded;
|
|
142
|
+
@apply border-2 border-border;
|
|
143
|
+
@apply text-primary;
|
|
144
|
+
@apply transition-colors;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* Hover state */
|
|
148
|
+
.checkbox:hover .checkbox-control {
|
|
149
|
+
@apply border-primary-500;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* Focus state */
|
|
153
|
+
.checkbox input:focus + .checkbox-control {
|
|
154
|
+
@apply ring-2 ring-primary-500 ring-offset-2;
|
|
155
|
+
@apply ring-offset-background;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* Checked state */
|
|
159
|
+
.checkbox input:checked + .checkbox-control {
|
|
160
|
+
@apply bg-primary-500 border-primary-500;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* Error state */
|
|
164
|
+
.checkbox-error .checkbox-control {
|
|
165
|
+
@apply border-error;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.checkbox-error:hover .checkbox-control {
|
|
169
|
+
@apply border-error-bold;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.checkbox-error input:checked + .checkbox-control {
|
|
173
|
+
@apply bg-error border-error-bold;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* Label */
|
|
177
|
+
.checkbox-label {
|
|
178
|
+
@apply text-sm;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.checkbox-required {
|
|
182
|
+
@apply ml-1 text-error;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* Description */
|
|
186
|
+
.checkbox-description {
|
|
187
|
+
@apply text-sm text-muted;
|
|
188
|
+
@apply pl-7; /* Align with label text */
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/* Error message */
|
|
192
|
+
.checkbox-error-text {
|
|
193
|
+
@apply text-sm text-error;
|
|
194
|
+
@apply pl-7; /* Align with label text */
|
|
195
|
+
}
|
|
196
|
+
</style>
|