@dryui/ui 1.1.1 → 1.1.3

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.
@@ -5,7 +5,7 @@
5
5
 
6
6
  export type AlertVariant = 'info' | 'success' | 'warning' | 'error';
7
7
 
8
- interface Props extends HTMLAttributes<HTMLDivElement> {
8
+ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'title'> {
9
9
  variant?: AlertVariant;
10
10
  dismissible?: boolean;
11
11
  onDismiss?: () => void;
@@ -1,7 +1,7 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
3
  export type AlertVariant = 'info' | 'success' | 'warning' | 'error';
4
- interface Props extends HTMLAttributes<HTMLDivElement> {
4
+ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'title'> {
5
5
  variant?: AlertVariant;
6
6
  dismissible?: boolean;
7
7
  onDismiss?: () => void;
@@ -2,7 +2,7 @@ import type { Snippet } from 'svelte';
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
3
  import Alert from './alert.svelte';
4
4
  export type AlertVariant = 'info' | 'success' | 'warning' | 'error';
5
- export interface AlertProps extends HTMLAttributes<HTMLDivElement> {
5
+ export interface AlertProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'title'> {
6
6
  variant?: AlertVariant;
7
7
  dismissible?: boolean;
8
8
  onDismiss?: () => void;
@@ -1,151 +1,51 @@
1
1
  <script lang="ts">
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
- import Button from '../button/button.svelte';
3
+ import { isSameDay } from '@dryui/primitives';
4
4
  import { getCalendarCtx } from './context.svelte.js';
5
- import {
6
- getCalendarDays,
7
- isSameDay,
8
- isToday,
9
- isDateInRange,
10
- formatDate
11
- } from '@dryui/primitives';
12
- import {
13
- generateWeekdayLabels,
14
- splitIntoWeeks,
15
- getDayISOString,
16
- handleCalendarKeydown,
17
- focusCalendarDay
18
- } from '../internal/calendar-grid-utils.js';
5
+ import CalendarGridButton, {
6
+ type CalendarGridAdapter
7
+ } from '../internal/calendar-grid-button.svelte';
19
8
 
20
9
  interface Props extends HTMLAttributes<HTMLDivElement> {}
21
10
 
22
- let { class: className, ...rest }: Props = $props();
11
+ let props: Props = $props();
23
12
 
24
13
  const ctx = getCalendarCtx();
25
14
 
26
- let containerEl = $state<HTMLDivElement>();
27
-
28
- const weekdayLabels = $derived(generateWeekdayLabels(ctx.locale, ctx.weekStartDay));
29
-
30
- const calendarDays = $derived(getCalendarDays(ctx.viewYear, ctx.viewMonth, ctx.weekStartDay));
31
-
32
- const monthYearLabel = $derived(
33
- formatDate(new Date(ctx.viewYear, ctx.viewMonth, 1), ctx.locale, {
34
- month: 'long',
35
- year: 'numeric'
36
- })
37
- );
38
-
39
- const weeks = $derived(splitIntoWeeks(calendarDays));
40
-
41
- function handleDayClick(day: Date) {
42
- if (ctx.disabled) return;
43
- if (!isDateInRange(day, ctx.min, ctx.max)) return;
44
- ctx.select(day);
45
- }
46
-
47
- function handleDayKeydown(e: KeyboardEvent, day: Date) {
48
- if (ctx.disabled) return;
49
- const result = handleCalendarKeydown(e, day, {
50
- onSelect: (d) => {
51
- if (isDateInRange(d, ctx.min, ctx.max)) ctx.select(d);
52
- },
53
- onEscape: () => {},
54
- setFocusedDate: (d) => ctx.setFocusedDate(d),
55
- weekStartDay: ctx.weekStartDay,
56
- min: ctx.min,
57
- max: ctx.max
58
- });
59
- if (result?.type === 'navigate') {
60
- requestAnimationFrame(() => focusCalendarDay(containerEl, result.newDate));
61
- }
62
- }
63
-
64
- function isDayDisabled(day: Date): boolean {
65
- return !isDateInRange(day, ctx.min, ctx.max);
66
- }
15
+ const adapter: CalendarGridAdapter = {
16
+ get viewYear() {
17
+ return ctx.viewYear;
18
+ },
19
+ get viewMonth() {
20
+ return ctx.viewMonth;
21
+ },
22
+ get locale() {
23
+ return ctx.locale;
24
+ },
25
+ get weekStartDay() {
26
+ return ctx.weekStartDay;
27
+ },
28
+ get focusedDate() {
29
+ return ctx.focusedDate;
30
+ },
31
+ get min() {
32
+ return ctx.min;
33
+ },
34
+ get max() {
35
+ return ctx.max;
36
+ },
37
+ get disabled() {
38
+ return ctx.disabled;
39
+ },
40
+ isSelected: (day) => (ctx.value ? isSameDay(day, ctx.value) : false),
41
+ isInRange: () => false,
42
+ isRangeStart: () => false,
43
+ isRangeEnd: () => false,
44
+ selectDate: (day) => ctx.select(day),
45
+ setFocusedDate: (day) => ctx.setFocusedDate(day),
46
+ prevMonth: () => ctx.prevMonth(),
47
+ nextMonth: () => ctx.nextMonth()
48
+ };
67
49
  </script>
68
50
 
69
- <div bind:this={containerEl} data-calendar-grid class={className} {...rest}>
70
- <div role="grid" aria-label={monthYearLabel}>
71
- <div data-calendar-row role="row">
72
- {#each weekdayLabels as label (label)}
73
- <div data-calendar-columnheader role="columnheader" aria-label={label}>
74
- <span aria-hidden="true">{label}</span>
75
- </div>
76
- {/each}
77
- </div>
78
-
79
- {#each weeks as week, wi (wi)}
80
- <div data-calendar-row role="row">
81
- {#each week as day (day.getTime())}
82
- {@const isCurrent = day.getMonth() === ctx.viewMonth}
83
- {@const selected = ctx.value ? isSameDay(day, ctx.value) : false}
84
- {@const today = isToday(day)}
85
- {@const disabled = isDayDisabled(day)}
86
- {@const focused = isSameDay(day, ctx.focusedDate)}
87
- {@const isoStr = getDayISOString(day)}
88
- <div data-calendar-cell>
89
- <Button
90
- variant="trigger"
91
- size="icon-sm"
92
- type="button"
93
- role="gridcell"
94
- tabindex={focused ? 0 : -1}
95
- aria-label={formatDate(day, ctx.locale, {
96
- year: 'numeric',
97
- month: 'long',
98
- day: 'numeric'
99
- })}
100
- aria-selected={selected}
101
- aria-disabled={disabled}
102
- data-calendar-day={isoStr}
103
- data-today={today ? '' : undefined}
104
- data-selected={selected ? '' : undefined}
105
- data-outside-month={!isCurrent ? '' : undefined}
106
- {disabled}
107
- onclick={() => handleDayClick(day)}
108
- onkeydown={(e) => handleDayKeydown(e, day)}
109
- >
110
- {day.getDate()}
111
- </Button>
112
- </div>
113
- {/each}
114
- </div>
115
- {/each}
116
- </div>
117
- </div>
118
-
119
- <style>
120
- [data-calendar-grid] {
121
- --dry-calendar-grid-gap: 1px;
122
- display: grid;
123
- gap: var(--dry-space-2);
124
- }
125
-
126
- [role='grid'] {
127
- display: grid;
128
- gap: var(--dry-calendar-grid-gap);
129
- }
130
-
131
- [data-calendar-row] {
132
- display: grid;
133
- grid-template-columns: repeat(7, minmax(0, 1fr));
134
- gap: var(--dry-calendar-grid-gap);
135
- }
136
-
137
- [data-calendar-columnheader] {
138
- display: grid;
139
- place-items: center;
140
- min-height: var(--dry-space-8);
141
- font-size: var(--dry-type-tiny-size);
142
- color: var(--dry-calendar-muted-color, var(--dry-color-text-weak));
143
- text-transform: uppercase;
144
- letter-spacing: 0.04em;
145
- }
146
-
147
- [data-calendar-cell] {
148
- display: grid;
149
- place-items: center;
150
- }
151
- </style>
51
+ <CalendarGridButton {adapter} {...props} />
@@ -1,214 +1,58 @@
1
1
  <script lang="ts">
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
- import Button from '../button/button.svelte';
3
+ import { isSameDay } from '@dryui/primitives';
4
4
  import { getDatePickerCtx } from './context.svelte.js';
5
- import {
6
- getCalendarDays,
7
- isSameDay,
8
- isToday,
9
- isDateInRange,
10
- formatDate
11
- } from '@dryui/primitives';
12
- import {
13
- generateWeekdayLabels,
14
- splitIntoWeeks,
15
- getDayISOString,
16
- handleCalendarKeydown,
17
- focusCalendarDay
18
- } from '../internal/calendar-grid-utils.js';
5
+ import CalendarGridButton, {
6
+ type CalendarGridAdapter
7
+ } from '../internal/calendar-grid-button.svelte';
19
8
 
20
9
  interface Props extends HTMLAttributes<HTMLDivElement> {}
21
10
 
22
- let { class: className, ...rest }: Props = $props();
11
+ let props: Props = $props();
23
12
 
24
13
  const ctx = getDatePickerCtx();
25
14
 
26
- let containerEl = $state<HTMLDivElement>();
27
-
28
- function bindContainer(node: HTMLDivElement) {
29
- containerEl = node;
30
-
31
- return () => {
32
- if (containerEl === node) {
33
- containerEl = undefined;
34
- }
35
- };
36
- }
37
-
38
- const weekdayLabels = $derived(generateWeekdayLabels(ctx.locale, ctx.weekStartDay));
39
-
40
- const calendarDays = $derived(getCalendarDays(ctx.viewYear, ctx.viewMonth, ctx.weekStartDay));
41
-
42
- const monthYearLabel = $derived(
43
- formatDate(new Date(ctx.viewYear, ctx.viewMonth, 1), ctx.locale, {
44
- month: 'long',
45
- year: 'numeric'
46
- })
47
- );
48
-
49
- const weeks = $derived(splitIntoWeeks(calendarDays));
50
-
51
- function handleDayClick(day: Date) {
52
- if (!isDateInRange(day, ctx.min, ctx.max)) return;
53
- ctx.select(day);
54
- ctx.triggerEl?.focus();
55
- }
56
-
57
- function handleDayKeydown(e: KeyboardEvent, day: Date) {
58
- const result = handleCalendarKeydown(e, day, {
59
- onSelect: (d) => {
60
- if (isDateInRange(d, ctx.min, ctx.max)) {
61
- ctx.select(d);
62
- ctx.triggerEl?.focus();
63
- }
64
- },
65
- onEscape: () => {
66
- ctx.close();
67
- ctx.triggerEl?.focus();
68
- },
69
- setFocusedDate: (d) => ctx.setFocusedDate(d),
70
- weekStartDay: ctx.weekStartDay,
71
- min: ctx.min,
72
- max: ctx.max
73
- });
74
- if (result?.type === 'navigate') {
75
- requestAnimationFrame(() => focusCalendarDay(containerEl, result.newDate));
15
+ const adapter: CalendarGridAdapter = {
16
+ get viewYear() {
17
+ return ctx.viewYear;
18
+ },
19
+ get viewMonth() {
20
+ return ctx.viewMonth;
21
+ },
22
+ get locale() {
23
+ return ctx.locale;
24
+ },
25
+ get weekStartDay() {
26
+ return ctx.weekStartDay;
27
+ },
28
+ get focusedDate() {
29
+ return ctx.focusedDate;
30
+ },
31
+ get min() {
32
+ return ctx.min;
33
+ },
34
+ get max() {
35
+ return ctx.max;
36
+ },
37
+ get disabled() {
38
+ return ctx.disabled;
39
+ },
40
+ isSelected: (day) => (ctx.value ? isSameDay(day, ctx.value) : false),
41
+ isInRange: () => false,
42
+ isRangeStart: () => false,
43
+ isRangeEnd: () => false,
44
+ selectDate: (day) => {
45
+ ctx.select(day);
46
+ ctx.triggerEl?.focus();
47
+ },
48
+ setFocusedDate: (day) => ctx.setFocusedDate(day),
49
+ prevMonth: () => ctx.prevMonth(),
50
+ nextMonth: () => ctx.nextMonth(),
51
+ onEscape: () => {
52
+ ctx.close();
53
+ ctx.triggerEl?.focus();
76
54
  }
77
- }
78
-
79
- function isDayDisabled(day: Date): boolean {
80
- return !isDateInRange(day, ctx.min, ctx.max);
81
- }
55
+ };
82
56
  </script>
83
57
 
84
- <div {@attach bindContainer} class={className} {...rest} data-dp-calendar>
85
- <div role="group" aria-label={monthYearLabel} data-calendar-panel>
86
- <div class="dp-header" data-calendar-header>
87
- <Button
88
- variant="trigger"
89
- size="icon-sm"
90
- type="button"
91
- aria-label="Previous month"
92
- disabled={ctx.disabled}
93
- onclick={() => ctx.prevMonth()}
94
- >
95
-
96
- </Button>
97
- <span aria-live="polite" aria-atomic="true" data-calendar-heading>
98
- {monthYearLabel}
99
- </span>
100
- <Button
101
- variant="trigger"
102
- size="icon-sm"
103
- type="button"
104
- aria-label="Next month"
105
- disabled={ctx.disabled}
106
- onclick={() => ctx.nextMonth()}
107
- >
108
-
109
- </Button>
110
- </div>
111
-
112
- <div role="grid" aria-label={monthYearLabel}>
113
- <div role="row" data-calendar-row>
114
- {#each weekdayLabels as label (label)}
115
- <div role="columnheader" aria-label={label} data-calendar-columnheader>
116
- <span aria-hidden="true">{label}</span>
117
- </div>
118
- {/each}
119
- </div>
120
-
121
- {#each weeks as week, weekIndex (weekIndex)}
122
- <div role="row" data-calendar-row>
123
- {#each week as day (getDayISOString(day))}
124
- {@const isCurrent = day.getMonth() === ctx.viewMonth}
125
- {@const selected = ctx.value ? isSameDay(day, ctx.value) : false}
126
- {@const today = isToday(day)}
127
- {@const disabled = isDayDisabled(day)}
128
- {@const focused = isSameDay(day, ctx.focusedDate)}
129
- {@const isoStr = getDayISOString(day)}
130
- <div role="gridcell" data-calendar-cell>
131
- <Button
132
- variant="trigger"
133
- size="icon-sm"
134
- type="button"
135
- tabindex={focused ? 0 : -1}
136
- aria-label={formatDate(day, ctx.locale, {
137
- year: 'numeric',
138
- month: 'long',
139
- day: 'numeric'
140
- })}
141
- aria-pressed={selected}
142
- aria-disabled={disabled}
143
- data-calendar-day={isoStr}
144
- data-today={today ? '' : undefined}
145
- data-selected={selected ? '' : undefined}
146
- data-outside-month={!isCurrent ? '' : undefined}
147
- {disabled}
148
- onclick={() => handleDayClick(day)}
149
- onkeydown={(e) => handleDayKeydown(e, day)}
150
- >
151
- {day.getDate()}
152
- </Button>
153
- </div>
154
- {/each}
155
- </div>
156
- {/each}
157
- </div>
158
- </div>
159
- </div>
160
-
161
- <style>
162
- [data-dp-calendar] {
163
- display: grid;
164
- gap: var(--dry-space-2);
165
- user-select: none;
166
- color: var(--dry-calendar-header-color, var(--dry-color-text-strong));
167
- font-family: var(--dry-font-sans);
168
- }
169
-
170
- [data-dp-calendar] [data-calendar-panel] {
171
- display: grid;
172
- gap: var(--dry-space-2);
173
- }
174
-
175
- [data-dp-calendar] [data-calendar-header] {
176
- display: grid;
177
- grid-template-columns: auto 1fr auto;
178
- align-items: center;
179
- gap: var(--dry-space-2);
180
- }
181
-
182
- [data-dp-calendar] [data-calendar-heading] {
183
- font-size: var(--dry-type-small-size);
184
- font-weight: 600;
185
- letter-spacing: -0.01em;
186
- text-align: center;
187
- }
188
-
189
- [data-dp-calendar] [role='grid'] {
190
- display: grid;
191
- gap: 1px;
192
- }
193
-
194
- [data-dp-calendar] [data-calendar-row] {
195
- display: grid;
196
- grid-template-columns: repeat(7, minmax(0, 1fr));
197
- gap: 1px;
198
- }
199
-
200
- [data-dp-calendar] [data-calendar-columnheader] {
201
- display: grid;
202
- place-items: center;
203
- min-height: var(--dry-space-8);
204
- font-size: var(--dry-type-tiny-size);
205
- color: var(--dry-calendar-muted-color, var(--dry-color-text-weak));
206
- text-transform: uppercase;
207
- letter-spacing: 0.04em;
208
- }
209
-
210
- [data-dp-calendar] [data-calendar-cell] {
211
- display: grid;
212
- place-items: center;
213
- }
214
- </style>
58
+ <CalendarGridButton {adapter} {...props} />