@taqwim/solid 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/index.js +221 -0
- package/dist/source/HijriCalendar.d.ts +47 -0
- package/dist/source/HijriCalendar.jsx +227 -0
- package/dist/source/context.d.ts +9 -0
- package/dist/source/context.js +10 -0
- package/dist/source/index.d.ts +5 -0
- package/dist/source/index.js +3 -0
- package/dist/source/types.d.ts +55 -0
- package/dist/source/types.js +28 -0
- package/dist/source/useCalendar.d.ts +14 -0
- package/dist/source/useCalendar.js +30 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 boussadjra
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# @taqwim/solid
|
|
2
|
+
|
|
3
|
+
Headless Solid components for Hijri calendars. No styles, no markup opinions — just behaviour, accessibility and the `data-*` attributes to hang a design on.
|
|
4
|
+
|
|
5
|
+
Want something that already looks like a calendar? Use [`@taqwim/solid-styled`](../solid-styled).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pnpm add @taqwim/solid
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Solid 1.8 or later.
|
|
14
|
+
|
|
15
|
+
## Use
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import {
|
|
19
|
+
HijriCalendarCell,
|
|
20
|
+
HijriCalendarCellTrigger,
|
|
21
|
+
HijriCalendarGrid,
|
|
22
|
+
HijriCalendarGridBody,
|
|
23
|
+
HijriCalendarGridHead,
|
|
24
|
+
HijriCalendarGridRow,
|
|
25
|
+
HijriCalendarHeadCell,
|
|
26
|
+
HijriCalendarHeader,
|
|
27
|
+
HijriCalendarHeading,
|
|
28
|
+
HijriCalendarNext,
|
|
29
|
+
HijriCalendarPrev,
|
|
30
|
+
HijriCalendarRoot,
|
|
31
|
+
} from '@taqwim/solid'
|
|
32
|
+
import { createSignal, For, Index } from 'solid-js'
|
|
33
|
+
|
|
34
|
+
export function Calendar() {
|
|
35
|
+
const [value, setValue] = createSignal()
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<HijriCalendarRoot value={value()} onValueChange={setValue} initialFocus>
|
|
39
|
+
{({ months, weekDays }) => (
|
|
40
|
+
<>
|
|
41
|
+
<HijriCalendarHeader>
|
|
42
|
+
<HijriCalendarPrev>←</HijriCalendarPrev>
|
|
43
|
+
<HijriCalendarHeading />
|
|
44
|
+
<HijriCalendarNext>→</HijriCalendarNext>
|
|
45
|
+
</HijriCalendarHeader>
|
|
46
|
+
|
|
47
|
+
<Index each={months}>
|
|
48
|
+
{month => (
|
|
49
|
+
<HijriCalendarGrid month={month()}>
|
|
50
|
+
<HijriCalendarGridHead>
|
|
51
|
+
<HijriCalendarGridRow>
|
|
52
|
+
<For each={weekDays}>{day => <HijriCalendarHeadCell>{day}</HijriCalendarHeadCell>}</For>
|
|
53
|
+
</HijriCalendarGridRow>
|
|
54
|
+
</HijriCalendarGridHead>
|
|
55
|
+
|
|
56
|
+
<HijriCalendarGridBody>
|
|
57
|
+
<Index each={month().weeks}>
|
|
58
|
+
{week => (
|
|
59
|
+
<HijriCalendarGridRow>
|
|
60
|
+
<Index each={week()}>
|
|
61
|
+
{day => (
|
|
62
|
+
<HijriCalendarCell day={day()}>
|
|
63
|
+
<HijriCalendarCellTrigger day={day()} />
|
|
64
|
+
</HijriCalendarCell>
|
|
65
|
+
)}
|
|
66
|
+
</Index>
|
|
67
|
+
</HijriCalendarGridRow>
|
|
68
|
+
)}
|
|
69
|
+
</Index>
|
|
70
|
+
</HijriCalendarGridBody>
|
|
71
|
+
</HijriCalendarGrid>
|
|
72
|
+
)}
|
|
73
|
+
</Index>
|
|
74
|
+
</>
|
|
75
|
+
)}
|
|
76
|
+
</HijriCalendarRoot>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Use `Index` rather than `For` for the grid. The store rebuilds its arrays on every snapshot and `For` keys by reference, so it would throw away and recreate every cell on each change.
|
|
82
|
+
|
|
83
|
+
## How it works
|
|
84
|
+
|
|
85
|
+
All behaviour lives in [`@taqwim/calendar-core`](../calendar-core), a framework-free state machine. `HijriCalendarRoot` mirrors the store's snapshot into a signal and shares it through Solid context; every part spreads attributes the store computed.
|
|
86
|
+
|
|
87
|
+
That is why the Vue, React, Svelte and Angular adapters behave identically: one implementation of grid layout, selection, paging and keyboard navigation, and one set of emitted attributes.
|
|
88
|
+
|
|
89
|
+
Drop to the store directly with `createCalendarStore`:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { createCalendarStore } from '@taqwim/solid'
|
|
93
|
+
|
|
94
|
+
const { store, state } = createCalendarStore(() => ({ locale: 'ar', dir: 'rtl' }))
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The accessor is a function so that reading props inside it registers them as dependencies — Solid props are getters, and destructuring would freeze the calendar at its initial configuration.
|
|
98
|
+
|
|
99
|
+
The package ships uncompiled JSX under the `solid` export condition so your compiler can target DOM or SSR; `dist/index.js` is a DOM-compiled fallback for bundlers that ignore the condition.
|
|
100
|
+
|
|
101
|
+
## Props
|
|
102
|
+
|
|
103
|
+
The same names every adapter uses: `defaultValue` · `value` · `onValueChange` · `defaultPlaceholder` · `placeholder` · `onPlaceholderChange` · `weekStartsOn` · `weekdayFormat` · `numberOfMonths` · `pagedNavigation` · `fixedWeeks` · `multiple` · `preventDeselect` · `minValue` · `maxValue` · `isDateDisabled` · `isDateUnavailable` · `disableDaysOutsideCurrentView` · `disabled` · `readonly` · `locale` · `dir` · `initialFocus` · `nextPage` · `prevPage` · `calendarLabel`.
|
|
104
|
+
|
|
105
|
+
`minValue`/`maxValue` disable the out-of-range days themselves, not only the paging buttons, and the matchers are enforced for keyboard selection as well as clicks.
|
|
106
|
+
|
|
107
|
+
Full reference: [Calendar options](https://boussadjra.github.io/taqwim/reference/options/).
|
|
108
|
+
|
|
109
|
+
## Keyboard
|
|
110
|
+
|
|
111
|
+
| Key | |
|
|
112
|
+
| ----------------------------- | ------------------------------------------------ |
|
|
113
|
+
| `←` `→` | Previous / next day — mirrored under `dir="rtl"` |
|
|
114
|
+
| `↑` `↓` | Previous / next week |
|
|
115
|
+
| `Home` `End` | First / last day of the week |
|
|
116
|
+
| `PageUp` `PageDown` | Previous / next month |
|
|
117
|
+
| `Shift` + `PageUp`/`PageDown` | Previous / next year |
|
|
118
|
+
| `Enter` `Space` | Select the focused day |
|
|
119
|
+
|
|
120
|
+
A roving tabindex keeps exactly one cell in the tab order, so `Tab` enters and leaves the grid rather than walking 42 buttons.
|
|
121
|
+
|
|
122
|
+
## Attributes to style against
|
|
123
|
+
|
|
124
|
+
Root `[data-taqwim-calendar]`, with `data-disabled`, `data-readonly`, `data-invalid`.
|
|
125
|
+
Cell trigger `[data-taqwim-calendar-cell-trigger]`, with `data-selected`, `data-today`, `data-outside-month`, `data-disabled`, `data-unavailable`, `data-focused`, `data-value` (`iYYYY-iMM-iDD`).
|
|
126
|
+
|
|
127
|
+
[`@taqwim/themes`](../themes) is a ready-made stylesheet over exactly these.
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { createComponent as e, insert as t, memo as n, mergeProps as r, spread as i, template as a, use as o } from "solid-js/web";
|
|
2
|
+
import { createContext as s, createEffect as c, createRenderEffect as l, createSignal as u, onCleanup as d, onMount as f, splitProps as p, useContext as m } from "solid-js";
|
|
3
|
+
import { createCalendar as h } from "@taqwim/calendar-core";
|
|
4
|
+
//#region src/context.ts
|
|
5
|
+
var g = s();
|
|
6
|
+
function _() {
|
|
7
|
+
let e = m(g);
|
|
8
|
+
if (!e) throw Error("Taqwim calendar parts must be rendered inside <HijriCalendarRoot>");
|
|
9
|
+
return e;
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/types.ts
|
|
13
|
+
var v = [
|
|
14
|
+
"defaultValue",
|
|
15
|
+
"value",
|
|
16
|
+
"onValueChange",
|
|
17
|
+
"defaultPlaceholder",
|
|
18
|
+
"placeholder",
|
|
19
|
+
"onPlaceholderChange",
|
|
20
|
+
"weekStartsOn",
|
|
21
|
+
"weekdayFormat",
|
|
22
|
+
"calendarLabel",
|
|
23
|
+
"fixedWeeks",
|
|
24
|
+
"numberOfMonths",
|
|
25
|
+
"pagedNavigation",
|
|
26
|
+
"multiple",
|
|
27
|
+
"preventDeselect",
|
|
28
|
+
"disableDaysOutsideCurrentView",
|
|
29
|
+
"disabled",
|
|
30
|
+
"readonly",
|
|
31
|
+
"minValue",
|
|
32
|
+
"maxValue",
|
|
33
|
+
"locale",
|
|
34
|
+
"dir",
|
|
35
|
+
"isDateDisabled",
|
|
36
|
+
"isDateUnavailable",
|
|
37
|
+
"nextPage",
|
|
38
|
+
"prevPage"
|
|
39
|
+
];
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/useCalendar.ts
|
|
42
|
+
function y(e) {
|
|
43
|
+
let t = h(e()), [n, r] = u(t.getSnapshot());
|
|
44
|
+
return d(t.subscribe(() => {
|
|
45
|
+
r(t.getSnapshot());
|
|
46
|
+
})), l(() => {
|
|
47
|
+
t.setOptions(e());
|
|
48
|
+
}), {
|
|
49
|
+
store: t,
|
|
50
|
+
state: n
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/HijriCalendar.tsx
|
|
55
|
+
var b = /*#__PURE__*/ a("<div><div style=position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0><div role=heading aria-level=2>"), x = /*#__PURE__*/ a("<div role=group data-taqwim-calendar-header>"), S = /*#__PURE__*/ a("<div data-taqwim-calendar-heading>"), C = /*#__PURE__*/ a("<button>"), w = /*#__PURE__*/ a("<div tabindex=-1>"), T = /*#__PURE__*/ a("<div aria-hidden=true data-taqwim-calendar-grid-head>"), E = /*#__PURE__*/ a("<div data-taqwim-calendar-grid-body>"), D = /*#__PURE__*/ a("<div role=row data-taqwim-calendar-grid-row>"), O = /*#__PURE__*/ a("<div data-taqwim-calendar-head-cell>"), k = /*#__PURE__*/ a("<div role=gridcell data-taqwim-calendar-cell>");
|
|
56
|
+
function A(e, t) {
|
|
57
|
+
return typeof e == "function" ? e(t()) : e;
|
|
58
|
+
}
|
|
59
|
+
function j(n) {
|
|
60
|
+
let [a, s, l] = p(n, v, ["children", "initialFocus"]), u, d, { store: m, state: h } = y(() => ({
|
|
61
|
+
...a,
|
|
62
|
+
onFocusedDateChange: (e) => {
|
|
63
|
+
d = e ? m.formatter.isoDate(e) : void 0;
|
|
64
|
+
}
|
|
65
|
+
}));
|
|
66
|
+
c(() => {
|
|
67
|
+
if (h(), !d) return;
|
|
68
|
+
let e = d;
|
|
69
|
+
d = void 0, u?.querySelector(`[data-taqwim-calendar-cell-trigger][data-value="${e}"]`)?.focus();
|
|
70
|
+
}), f(() => {
|
|
71
|
+
s.initialFocus && m.focusInitial();
|
|
72
|
+
});
|
|
73
|
+
let _ = (e) => {
|
|
74
|
+
m.handleKeydown(e) && e.preventDefault();
|
|
75
|
+
}, x = () => (h(), m.getRootProps());
|
|
76
|
+
return e(g.Provider, {
|
|
77
|
+
value: {
|
|
78
|
+
store: m,
|
|
79
|
+
state: h
|
|
80
|
+
},
|
|
81
|
+
get children() {
|
|
82
|
+
var e = b(), n = e.firstChild, a = n.firstChild, c = u;
|
|
83
|
+
return typeof c == "function" ? o(c, e) : u = e, i(e, r(x, l, { onKeyDown: _ }), !1, !0), t(e, () => A(s.children, () => ({
|
|
84
|
+
get months() {
|
|
85
|
+
return h().months;
|
|
86
|
+
},
|
|
87
|
+
get weekDays() {
|
|
88
|
+
return h().weekDays;
|
|
89
|
+
},
|
|
90
|
+
get state() {
|
|
91
|
+
return h();
|
|
92
|
+
},
|
|
93
|
+
store: m
|
|
94
|
+
})), n), t(a, () => h().fullCalendarLabel), e;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function M(e) {
|
|
99
|
+
let { state: n } = _(), [a, o] = p(e, ["children"]);
|
|
100
|
+
return (() => {
|
|
101
|
+
var e = x();
|
|
102
|
+
return i(e, r({ get "aria-label"() {
|
|
103
|
+
return n().fullCalendarLabel;
|
|
104
|
+
} }, o), !1, !0), t(e, () => a.children), e;
|
|
105
|
+
})();
|
|
106
|
+
}
|
|
107
|
+
function N(e) {
|
|
108
|
+
let { state: a } = _(), [o, s] = p(e, ["children"]);
|
|
109
|
+
return (() => {
|
|
110
|
+
var e = S();
|
|
111
|
+
return i(e, r({ get "data-disabled"() {
|
|
112
|
+
return a().disabled ? "" : void 0;
|
|
113
|
+
} }, s), !1, !0), t(e, (() => {
|
|
114
|
+
var e = n(() => o.children === void 0);
|
|
115
|
+
return () => e() ? a().headingValue : A(o.children, () => a().headingValue);
|
|
116
|
+
})()), e;
|
|
117
|
+
})();
|
|
118
|
+
}
|
|
119
|
+
function P(e) {
|
|
120
|
+
let { store: n, state: a } = _(), [o, s] = p(e, ["children"]), c = () => (a(), n.getPrevButtonProps());
|
|
121
|
+
return (() => {
|
|
122
|
+
var e = C();
|
|
123
|
+
return i(e, r(c, s, {
|
|
124
|
+
get disabled() {
|
|
125
|
+
return a().isPrevDisabled;
|
|
126
|
+
},
|
|
127
|
+
onClick: () => n.prevPage()
|
|
128
|
+
}), !1, !0), t(e, () => A(o.children, () => a().isPrevDisabled)), e;
|
|
129
|
+
})();
|
|
130
|
+
}
|
|
131
|
+
function F(e) {
|
|
132
|
+
let { store: n, state: a } = _(), [o, s] = p(e, ["children"]), c = () => (a(), n.getNextButtonProps());
|
|
133
|
+
return (() => {
|
|
134
|
+
var e = C();
|
|
135
|
+
return i(e, r(c, s, {
|
|
136
|
+
get disabled() {
|
|
137
|
+
return a().isNextDisabled;
|
|
138
|
+
},
|
|
139
|
+
onClick: () => n.nextPage()
|
|
140
|
+
}), !1, !0), t(e, () => A(o.children, () => a().isNextDisabled)), e;
|
|
141
|
+
})();
|
|
142
|
+
}
|
|
143
|
+
function I(e) {
|
|
144
|
+
let { store: n, state: a } = _(), [o, s] = p(e, ["month", "children"]), c = () => o.month ?? a().months[0], l = () => (a(), n.getGridProps(c()));
|
|
145
|
+
return (() => {
|
|
146
|
+
var e = w();
|
|
147
|
+
return i(e, r(l, s), !1, !0), t(e, () => A(o.children, c)), e;
|
|
148
|
+
})();
|
|
149
|
+
}
|
|
150
|
+
function L(e) {
|
|
151
|
+
let { state: n } = _(), [r, a] = p(e, ["children"]);
|
|
152
|
+
return (() => {
|
|
153
|
+
var e = T();
|
|
154
|
+
return i(e, a, !1, !0), t(e, () => A(r.children, () => n().weekDays)), e;
|
|
155
|
+
})();
|
|
156
|
+
}
|
|
157
|
+
function R(e) {
|
|
158
|
+
let [n, r] = p(e, ["children"]);
|
|
159
|
+
return (() => {
|
|
160
|
+
var e = E();
|
|
161
|
+
return i(e, r, !1, !0), t(e, () => n.children), e;
|
|
162
|
+
})();
|
|
163
|
+
}
|
|
164
|
+
function z(e) {
|
|
165
|
+
let [n, r] = p(e, ["children"]);
|
|
166
|
+
return (() => {
|
|
167
|
+
var e = D();
|
|
168
|
+
return i(e, r, !1, !0), t(e, () => n.children), e;
|
|
169
|
+
})();
|
|
170
|
+
}
|
|
171
|
+
function B(e) {
|
|
172
|
+
let [n, r] = p(e, ["children"]);
|
|
173
|
+
return (() => {
|
|
174
|
+
var e = O();
|
|
175
|
+
return i(e, r, !1, !0), t(e, () => n.children), e;
|
|
176
|
+
})();
|
|
177
|
+
}
|
|
178
|
+
function V(e) {
|
|
179
|
+
let [n, a] = p(e, ["day", "children"]);
|
|
180
|
+
return (() => {
|
|
181
|
+
var e = k();
|
|
182
|
+
return i(e, r({
|
|
183
|
+
get "aria-selected"() {
|
|
184
|
+
return n.day.isSelected || void 0;
|
|
185
|
+
},
|
|
186
|
+
get "aria-disabled"() {
|
|
187
|
+
return n.day.isDisabled || n.day.isUnavailable || void 0;
|
|
188
|
+
},
|
|
189
|
+
get "data-disabled"() {
|
|
190
|
+
return n.day.isDisabled ? "" : void 0;
|
|
191
|
+
},
|
|
192
|
+
get "data-outside-month"() {
|
|
193
|
+
return n.day.isOutsideMonth ? "" : void 0;
|
|
194
|
+
}
|
|
195
|
+
}, a), !1, !0), t(e, () => n.children), e;
|
|
196
|
+
})();
|
|
197
|
+
}
|
|
198
|
+
function H(e) {
|
|
199
|
+
let { store: a, state: o } = _(), [s, c] = p(e, ["day", "children"]), l = () => a.formatter.dayOfMonth(s.day.date), u = () => (o(), a.getCellTriggerProps(s.day));
|
|
200
|
+
function d() {
|
|
201
|
+
s.day.isDisabled || s.day.isUnavailable || a.select(s.day.date);
|
|
202
|
+
}
|
|
203
|
+
function f() {
|
|
204
|
+
s.day.isDisabled || s.day.isFocused || a.focusDate(s.day.date);
|
|
205
|
+
}
|
|
206
|
+
return (() => {
|
|
207
|
+
var e = C();
|
|
208
|
+
return i(e, r(u, c, {
|
|
209
|
+
onClick: d,
|
|
210
|
+
onFocus: f
|
|
211
|
+
}), !1, !0), t(e, (() => {
|
|
212
|
+
var e = n(() => s.children === void 0);
|
|
213
|
+
return () => e() ? l() : A(s.children, () => ({
|
|
214
|
+
dayValue: l(),
|
|
215
|
+
day: s.day
|
|
216
|
+
}));
|
|
217
|
+
})()), e;
|
|
218
|
+
})();
|
|
219
|
+
}
|
|
220
|
+
//#endregion
|
|
221
|
+
export { V as HijriCalendarCell, H as HijriCalendarCellTrigger, I as HijriCalendarGrid, R as HijriCalendarGridBody, L as HijriCalendarGridHead, z as HijriCalendarGridRow, B as HijriCalendarHeadCell, M as HijriCalendarHeader, N as HijriCalendarHeading, F as HijriCalendarNext, P as HijriCalendarPrev, j as HijriCalendarRoot, y as createCalendarStore, _ as useHijriCalendarContext };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { CalendarDay, CalendarMonth } from '@taqwim/calendar-core';
|
|
2
|
+
import { type JSX } from 'solid-js';
|
|
3
|
+
import { type HijriCalendarRootProps } from './types';
|
|
4
|
+
type DivProps = Omit<JSX.HTMLAttributes<HTMLDivElement>, 'children'>;
|
|
5
|
+
type ButtonProps = Omit<JSX.ButtonHTMLAttributes<HTMLButtonElement>, 'children' | 'onClick' | 'type'>;
|
|
6
|
+
export declare function HijriCalendarRoot(props: HijriCalendarRootProps): JSX.Element;
|
|
7
|
+
export declare function HijriCalendarHeader(props: DivProps & {
|
|
8
|
+
children?: JSX.Element;
|
|
9
|
+
}): JSX.Element;
|
|
10
|
+
export declare function HijriCalendarHeading(props: DivProps & {
|
|
11
|
+
children?: JSX.Element | ((headingValue: string) => JSX.Element);
|
|
12
|
+
}): JSX.Element;
|
|
13
|
+
export declare function HijriCalendarPrev(props: ButtonProps & {
|
|
14
|
+
children?: JSX.Element | ((disabled: boolean) => JSX.Element);
|
|
15
|
+
}): JSX.Element;
|
|
16
|
+
export declare function HijriCalendarNext(props: ButtonProps & {
|
|
17
|
+
children?: JSX.Element | ((disabled: boolean) => JSX.Element);
|
|
18
|
+
}): JSX.Element;
|
|
19
|
+
export declare function HijriCalendarGrid(props: DivProps & {
|
|
20
|
+
/** Optional for the single-month case, where it defaults to the only month. */
|
|
21
|
+
month?: CalendarMonth;
|
|
22
|
+
children?: JSX.Element | ((month: CalendarMonth) => JSX.Element);
|
|
23
|
+
}): JSX.Element;
|
|
24
|
+
export declare function HijriCalendarGridHead(props: DivProps & {
|
|
25
|
+
children?: JSX.Element | ((weekDays: string[]) => JSX.Element);
|
|
26
|
+
}): JSX.Element;
|
|
27
|
+
export declare function HijriCalendarGridBody(props: DivProps & {
|
|
28
|
+
children?: JSX.Element;
|
|
29
|
+
}): JSX.Element;
|
|
30
|
+
export declare function HijriCalendarGridRow(props: DivProps & {
|
|
31
|
+
children?: JSX.Element;
|
|
32
|
+
}): JSX.Element;
|
|
33
|
+
export declare function HijriCalendarHeadCell(props: DivProps & {
|
|
34
|
+
children?: JSX.Element;
|
|
35
|
+
}): JSX.Element;
|
|
36
|
+
export declare function HijriCalendarCell(props: DivProps & {
|
|
37
|
+
day: CalendarDay;
|
|
38
|
+
children?: JSX.Element;
|
|
39
|
+
}): JSX.Element;
|
|
40
|
+
export declare function HijriCalendarCellTrigger(props: ButtonProps & {
|
|
41
|
+
day: CalendarDay;
|
|
42
|
+
children?: JSX.Element | ((props: {
|
|
43
|
+
dayValue: string;
|
|
44
|
+
day: CalendarDay;
|
|
45
|
+
}) => JSX.Element);
|
|
46
|
+
}): JSX.Element;
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { createEffect, onMount, splitProps } from 'solid-js';
|
|
2
|
+
import { HijriCalendarContext, useHijriCalendarContext } from './context';
|
|
3
|
+
import { OPTION_KEYS } from './types';
|
|
4
|
+
import { createCalendarStore } from './useCalendar';
|
|
5
|
+
/*
|
|
6
|
+
* Every part below spreads props the store computed. Because those are the same
|
|
7
|
+
* `data-*` and `aria-*` attributes the Vue and React adapters emit,
|
|
8
|
+
* `@taqwim/themes` styles all of them and one Playwright spec covers all of
|
|
9
|
+
* them.
|
|
10
|
+
*
|
|
11
|
+
* Props are never destructured — Solid props are getters, and destructuring
|
|
12
|
+
* would read them once and lose reactivity.
|
|
13
|
+
*/
|
|
14
|
+
/*
|
|
15
|
+
* `props` is an accessor, not a value, and that is the whole point.
|
|
16
|
+
*
|
|
17
|
+
* Every call site sits inside a JSX expression, which Solid compiles into a
|
|
18
|
+
* tracked computation. Passing `month()` or `state().weekDays` evaluates the
|
|
19
|
+
* signal *to build the argument*, which subscribes the computation to it — so
|
|
20
|
+
* every store change threw the subtree away and built a new one, even for the
|
|
21
|
+
* common case where `children` is plain JSX and the argument is never used.
|
|
22
|
+
*
|
|
23
|
+
* For the grid that was not a performance detail. A browser focuses a button
|
|
24
|
+
* before it clicks it, the focus handler calls `store.focusDate`, and Solid
|
|
25
|
+
* delegates `click` to the document: the click was dispatched at a button that
|
|
26
|
+
* had already been replaced, and selection by mouse did not work at all.
|
|
27
|
+
*
|
|
28
|
+
* Read lazily, the argument is only built when a caller actually passed a
|
|
29
|
+
* function, and the JSX subtree is created once and then updated in place by
|
|
30
|
+
* the fine-grained bindings inside it.
|
|
31
|
+
*/
|
|
32
|
+
function renderChildren(children, props) {
|
|
33
|
+
return typeof children === 'function' ? children(props()) : children;
|
|
34
|
+
}
|
|
35
|
+
export function HijriCalendarRoot(props) {
|
|
36
|
+
const [options, local, domProps] = splitProps(props, OPTION_KEYS, ['children', 'initialFocus']);
|
|
37
|
+
let rootElement;
|
|
38
|
+
// Set by the store when the roving focus moves; consumed after the render.
|
|
39
|
+
let pendingFocus;
|
|
40
|
+
const { store, state } = createCalendarStore(() => ({
|
|
41
|
+
...options,
|
|
42
|
+
onFocusedDateChange: date => {
|
|
43
|
+
pendingFocus = date ? store.formatter.isoDate(date) : undefined;
|
|
44
|
+
},
|
|
45
|
+
}));
|
|
46
|
+
/*
|
|
47
|
+
* The store owns which date has focus; the adapter owns the DOM. Solid's
|
|
48
|
+
* effects run after the DOM is updated, so the target cell exists even when
|
|
49
|
+
* the move paged the calendar into a month that was not rendered a moment
|
|
50
|
+
* ago.
|
|
51
|
+
*/
|
|
52
|
+
createEffect(() => {
|
|
53
|
+
// Read the state so this re-runs on every change.
|
|
54
|
+
state();
|
|
55
|
+
if (!pendingFocus)
|
|
56
|
+
return;
|
|
57
|
+
const value = pendingFocus;
|
|
58
|
+
pendingFocus = undefined;
|
|
59
|
+
rootElement?.querySelector(`[data-taqwim-calendar-cell-trigger][data-value="${value}"]`)?.focus();
|
|
60
|
+
});
|
|
61
|
+
onMount(() => {
|
|
62
|
+
if (local.initialFocus)
|
|
63
|
+
store.focusInitial();
|
|
64
|
+
});
|
|
65
|
+
const onKeyDown = event => {
|
|
66
|
+
if (store.handleKeydown(event))
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
};
|
|
69
|
+
const rootProps = () => {
|
|
70
|
+
void state();
|
|
71
|
+
return store.getRootProps();
|
|
72
|
+
};
|
|
73
|
+
return (<HijriCalendarContext.Provider value={{ store, state }}>
|
|
74
|
+
<div ref={rootElement} {...rootProps()} {...domProps} onKeyDown={onKeyDown}>
|
|
75
|
+
{renderChildren(local.children, () => ({
|
|
76
|
+
get months() {
|
|
77
|
+
return state().months;
|
|
78
|
+
},
|
|
79
|
+
get weekDays() {
|
|
80
|
+
return state().weekDays;
|
|
81
|
+
},
|
|
82
|
+
get state() {
|
|
83
|
+
return state();
|
|
84
|
+
},
|
|
85
|
+
store,
|
|
86
|
+
}))}
|
|
87
|
+
|
|
88
|
+
{/* Inlined rather than classed: the headless package must not require a stylesheet. */}
|
|
89
|
+
<div style={{
|
|
90
|
+
position: 'absolute',
|
|
91
|
+
width: '1px',
|
|
92
|
+
height: '1px',
|
|
93
|
+
padding: '0',
|
|
94
|
+
margin: '-1px',
|
|
95
|
+
overflow: 'hidden',
|
|
96
|
+
'clip-path': 'inset(50%)',
|
|
97
|
+
'white-space': 'nowrap',
|
|
98
|
+
border: '0',
|
|
99
|
+
}}>
|
|
100
|
+
<div role="heading" aria-level={2}>
|
|
101
|
+
{state().fullCalendarLabel}
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
</HijriCalendarContext.Provider>);
|
|
106
|
+
}
|
|
107
|
+
export function HijriCalendarHeader(props) {
|
|
108
|
+
const { state } = useHijriCalendarContext();
|
|
109
|
+
const [local, rest] = splitProps(props, ['children']);
|
|
110
|
+
return (<div role="group" aria-label={state().fullCalendarLabel} data-taqwim-calendar-header="" {...rest}>
|
|
111
|
+
{local.children}
|
|
112
|
+
</div>);
|
|
113
|
+
}
|
|
114
|
+
export function HijriCalendarHeading(props) {
|
|
115
|
+
const { state } = useHijriCalendarContext();
|
|
116
|
+
const [local, rest] = splitProps(props, ['children']);
|
|
117
|
+
return (<div data-taqwim-calendar-heading="" data-disabled={state().disabled ? '' : undefined} {...rest}>
|
|
118
|
+
{local.children === undefined ? state().headingValue : renderChildren(local.children, () => state().headingValue)}
|
|
119
|
+
</div>);
|
|
120
|
+
}
|
|
121
|
+
export function HijriCalendarPrev(props) {
|
|
122
|
+
const { store, state } = useHijriCalendarContext();
|
|
123
|
+
const [local, rest] = splitProps(props, ['children']);
|
|
124
|
+
const prevProps = () => {
|
|
125
|
+
void state();
|
|
126
|
+
return store.getPrevButtonProps();
|
|
127
|
+
};
|
|
128
|
+
return (<button {...prevProps()} {...rest} disabled={state().isPrevDisabled} onClick={() => store.prevPage()}>
|
|
129
|
+
{renderChildren(local.children, () => state().isPrevDisabled)}
|
|
130
|
+
</button>);
|
|
131
|
+
}
|
|
132
|
+
export function HijriCalendarNext(props) {
|
|
133
|
+
const { store, state } = useHijriCalendarContext();
|
|
134
|
+
const [local, rest] = splitProps(props, ['children']);
|
|
135
|
+
const nextProps = () => {
|
|
136
|
+
void state();
|
|
137
|
+
return store.getNextButtonProps();
|
|
138
|
+
};
|
|
139
|
+
return (<button {...nextProps()} {...rest} disabled={state().isNextDisabled} onClick={() => store.nextPage()}>
|
|
140
|
+
{renderChildren(local.children, () => state().isNextDisabled)}
|
|
141
|
+
</button>);
|
|
142
|
+
}
|
|
143
|
+
export function HijriCalendarGrid(props) {
|
|
144
|
+
const { store, state } = useHijriCalendarContext();
|
|
145
|
+
const [local, rest] = splitProps(props, ['month', 'children']);
|
|
146
|
+
const month = () => local.month ?? state().months[0];
|
|
147
|
+
const gridProps = () => {
|
|
148
|
+
void state();
|
|
149
|
+
return store.getGridProps(month());
|
|
150
|
+
};
|
|
151
|
+
return (<div tabindex={-1} {...gridProps()} {...rest}>
|
|
152
|
+
{/*
|
|
153
|
+
`month`, not `month()`. Handing over the accessor keeps this expression
|
|
154
|
+
from reading the store, so the grid is built once and then updated in
|
|
155
|
+
place; passing the value rebuilt every cell on every state change and
|
|
156
|
+
broke selection by mouse outright. See `renderChildren` above.
|
|
157
|
+
*/}
|
|
158
|
+
{renderChildren(local.children, month)}
|
|
159
|
+
</div>);
|
|
160
|
+
}
|
|
161
|
+
export function HijriCalendarGridHead(props) {
|
|
162
|
+
const { state } = useHijriCalendarContext();
|
|
163
|
+
const [local, rest] = splitProps(props, ['children']);
|
|
164
|
+
// The weekday row duplicates each cell's accessible label, so it is hidden
|
|
165
|
+
// from assistive technology rather than read out twice.
|
|
166
|
+
return (<div aria-hidden="true" data-taqwim-calendar-grid-head="" {...rest}>
|
|
167
|
+
{renderChildren(local.children, () => state().weekDays)}
|
|
168
|
+
</div>);
|
|
169
|
+
}
|
|
170
|
+
export function HijriCalendarGridBody(props) {
|
|
171
|
+
const [local, rest] = splitProps(props, ['children']);
|
|
172
|
+
return (<div data-taqwim-calendar-grid-body="" {...rest}>
|
|
173
|
+
{local.children}
|
|
174
|
+
</div>);
|
|
175
|
+
}
|
|
176
|
+
export function HijriCalendarGridRow(props) {
|
|
177
|
+
const [local, rest] = splitProps(props, ['children']);
|
|
178
|
+
return (<div role="row" data-taqwim-calendar-grid-row="" {...rest}>
|
|
179
|
+
{local.children}
|
|
180
|
+
</div>);
|
|
181
|
+
}
|
|
182
|
+
export function HijriCalendarHeadCell(props) {
|
|
183
|
+
const [local, rest] = splitProps(props, ['children']);
|
|
184
|
+
return (<div data-taqwim-calendar-head-cell="" {...rest}>
|
|
185
|
+
{local.children}
|
|
186
|
+
</div>);
|
|
187
|
+
}
|
|
188
|
+
export function HijriCalendarCell(props) {
|
|
189
|
+
const [local, rest] = splitProps(props, ['day', 'children']);
|
|
190
|
+
return (<div role="gridcell" aria-selected={local.day.isSelected || undefined} aria-disabled={local.day.isDisabled || local.day.isUnavailable || undefined} data-taqwim-calendar-cell="" data-disabled={local.day.isDisabled ? '' : undefined} data-outside-month={local.day.isOutsideMonth ? '' : undefined} {...rest}>
|
|
191
|
+
{local.children}
|
|
192
|
+
</div>);
|
|
193
|
+
}
|
|
194
|
+
export function HijriCalendarCellTrigger(props) {
|
|
195
|
+
const { store, state } = useHijriCalendarContext();
|
|
196
|
+
const [local, rest] = splitProps(props, ['day', 'children']);
|
|
197
|
+
const dayValue = () => store.formatter.dayOfMonth(local.day.date);
|
|
198
|
+
/*
|
|
199
|
+
* The prop getters read the store's snapshot directly, which is not a signal.
|
|
200
|
+
* Touching `state()` here is what makes this a tracked dependency — without
|
|
201
|
+
* it the attributes only refresh when the day object happens to change
|
|
202
|
+
* identity, which leaves the rendered selection one interaction behind.
|
|
203
|
+
*/
|
|
204
|
+
const triggerProps = () => {
|
|
205
|
+
void state();
|
|
206
|
+
return store.getCellTriggerProps(local.day);
|
|
207
|
+
};
|
|
208
|
+
function onClick() {
|
|
209
|
+
// `select` re-checks these itself; this only avoids the pointless call.
|
|
210
|
+
if (local.day.isDisabled || local.day.isUnavailable)
|
|
211
|
+
return;
|
|
212
|
+
store.select(local.day.date);
|
|
213
|
+
}
|
|
214
|
+
function onFocus() {
|
|
215
|
+
// Tabbing or clicking into a cell makes it the roving-focus target.
|
|
216
|
+
// Re-reporting a date the store already holds would echo its own
|
|
217
|
+
// programmatic `.focus()` back at it, so that case is skipped.
|
|
218
|
+
if (local.day.isDisabled || local.day.isFocused)
|
|
219
|
+
return;
|
|
220
|
+
store.focusDate(local.day.date);
|
|
221
|
+
}
|
|
222
|
+
return (<button {...triggerProps()} {...rest} onClick={onClick} onFocus={onFocus}>
|
|
223
|
+
{local.children === undefined
|
|
224
|
+
? dayValue()
|
|
225
|
+
: renderChildren(local.children, () => ({ dayValue: dayValue(), day: local.day }))}
|
|
226
|
+
</button>);
|
|
227
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CalendarState, CalendarStore } from '@taqwim/calendar-core';
|
|
2
|
+
import { type Accessor } from 'solid-js';
|
|
3
|
+
export interface HijriCalendarContextValue {
|
|
4
|
+
store: CalendarStore;
|
|
5
|
+
state: Accessor<CalendarState>;
|
|
6
|
+
}
|
|
7
|
+
declare const HijriCalendarContext: import("solid-js").Context<HijriCalendarContextValue | undefined>;
|
|
8
|
+
export { HijriCalendarContext };
|
|
9
|
+
export declare function useHijriCalendarContext(): HijriCalendarContextValue;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createContext, useContext } from 'solid-js';
|
|
2
|
+
const HijriCalendarContext = createContext();
|
|
3
|
+
export { HijriCalendarContext };
|
|
4
|
+
export function useHijriCalendarContext() {
|
|
5
|
+
const context = useContext(HijriCalendarContext);
|
|
6
|
+
if (!context) {
|
|
7
|
+
throw new Error('Taqwim calendar parts must be rendered inside <HijriCalendarRoot>');
|
|
8
|
+
}
|
|
9
|
+
return context;
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { HijriCalendarCell, HijriCalendarCellTrigger, HijriCalendarGrid, HijriCalendarGridBody, HijriCalendarGridHead, HijriCalendarGridRow, HijriCalendarHeadCell, HijriCalendarHeader, HijriCalendarHeading, HijriCalendarNext, HijriCalendarPrev, HijriCalendarRoot, } from './HijriCalendar';
|
|
2
|
+
export { useHijriCalendarContext, type HijriCalendarContextValue } from './context';
|
|
3
|
+
export type { HijriCalendarRenderProps, HijriCalendarRootOptions, HijriCalendarRootProps } from './types';
|
|
4
|
+
export { createCalendarStore, type UseCalendarReturn } from './useCalendar';
|
|
5
|
+
export type { CalendarDay, CalendarFormatter, CalendarMonth, CalendarOptions, CalendarState, CalendarStore, Direction, Matcher, WeekDayFormat, WeekStartsOn, } from '@taqwim/calendar-core';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { HijriCalendarCell, HijriCalendarCellTrigger, HijriCalendarGrid, HijriCalendarGridBody, HijriCalendarGridHead, HijriCalendarGridRow, HijriCalendarHeadCell, HijriCalendarHeader, HijriCalendarHeading, HijriCalendarNext, HijriCalendarPrev, HijriCalendarRoot, } from './HijriCalendar';
|
|
2
|
+
export { useHijriCalendarContext } from './context';
|
|
3
|
+
export { createCalendarStore } from './useCalendar';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { CalendarMonth, CalendarState, CalendarStore, Matcher, WeekDayFormat, WeekStartsOn } from '@taqwim/calendar-core';
|
|
2
|
+
import type { HijriDateObject } from '@taqwim/core';
|
|
3
|
+
import type { JSX } from 'solid-js';
|
|
4
|
+
/**
|
|
5
|
+
* Deliberately the same names as the Vue and React adapters.
|
|
6
|
+
*
|
|
7
|
+
* Parity is a hard requirement — the shared end-to-end suite runs one spec file
|
|
8
|
+
* against every framework — so the surfaces differ only where the host
|
|
9
|
+
* framework forces it.
|
|
10
|
+
*/
|
|
11
|
+
export interface HijriCalendarRootOptions {
|
|
12
|
+
/** Initial selection, for uncontrolled use. */
|
|
13
|
+
defaultValue?: HijriDateObject | HijriDateObject[];
|
|
14
|
+
/** Controlled selection. */
|
|
15
|
+
value?: HijriDateObject | HijriDateObject[] | undefined;
|
|
16
|
+
onValueChange?: (value: HijriDateObject | HijriDateObject[] | undefined) => void;
|
|
17
|
+
/** Month to display when nothing is selected. */
|
|
18
|
+
defaultPlaceholder?: HijriDateObject;
|
|
19
|
+
/** Controlled placeholder. */
|
|
20
|
+
placeholder?: HijriDateObject;
|
|
21
|
+
onPlaceholderChange?: (placeholder: HijriDateObject) => void;
|
|
22
|
+
weekStartsOn?: WeekStartsOn;
|
|
23
|
+
weekdayFormat?: WeekDayFormat;
|
|
24
|
+
calendarLabel?: string;
|
|
25
|
+
fixedWeeks?: boolean;
|
|
26
|
+
numberOfMonths?: number;
|
|
27
|
+
pagedNavigation?: boolean;
|
|
28
|
+
multiple?: boolean;
|
|
29
|
+
preventDeselect?: boolean;
|
|
30
|
+
disableDaysOutsideCurrentView?: boolean;
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
readonly?: boolean;
|
|
33
|
+
minValue?: HijriDateObject;
|
|
34
|
+
maxValue?: HijriDateObject;
|
|
35
|
+
locale?: string;
|
|
36
|
+
dir?: 'ltr' | 'rtl';
|
|
37
|
+
isDateDisabled?: Matcher;
|
|
38
|
+
isDateUnavailable?: Matcher;
|
|
39
|
+
nextPage?: (placeholder: HijriDateObject) => HijriDateObject;
|
|
40
|
+
prevPage?: (placeholder: HijriDateObject) => HijriDateObject;
|
|
41
|
+
/** Focus the selected day, today, or the first of the month on mount. */
|
|
42
|
+
initialFocus?: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface HijriCalendarRenderProps {
|
|
45
|
+
months: CalendarMonth[];
|
|
46
|
+
weekDays: string[];
|
|
47
|
+
state: CalendarState;
|
|
48
|
+
store: CalendarStore;
|
|
49
|
+
}
|
|
50
|
+
export type HijriCalendarRootProps = HijriCalendarRootOptions & Omit<JSX.HTMLAttributes<HTMLDivElement>, 'children' | 'dir'> & {
|
|
51
|
+
/** A render function is the Solid equivalent of Vue's scoped slot. */
|
|
52
|
+
children?: JSX.Element | ((props: HijriCalendarRenderProps) => JSX.Element);
|
|
53
|
+
};
|
|
54
|
+
/** The option names, so the root can tell them from DOM attributes. */
|
|
55
|
+
export declare const OPTION_KEYS: readonly ["defaultValue", "value", "onValueChange", "defaultPlaceholder", "placeholder", "onPlaceholderChange", "weekStartsOn", "weekdayFormat", "calendarLabel", "fixedWeeks", "numberOfMonths", "pagedNavigation", "multiple", "preventDeselect", "disableDaysOutsideCurrentView", "disabled", "readonly", "minValue", "maxValue", "locale", "dir", "isDateDisabled", "isDateUnavailable", "nextPage", "prevPage"];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** The option names, so the root can tell them from DOM attributes. */
|
|
2
|
+
export const OPTION_KEYS = [
|
|
3
|
+
'defaultValue',
|
|
4
|
+
'value',
|
|
5
|
+
'onValueChange',
|
|
6
|
+
'defaultPlaceholder',
|
|
7
|
+
'placeholder',
|
|
8
|
+
'onPlaceholderChange',
|
|
9
|
+
'weekStartsOn',
|
|
10
|
+
'weekdayFormat',
|
|
11
|
+
'calendarLabel',
|
|
12
|
+
'fixedWeeks',
|
|
13
|
+
'numberOfMonths',
|
|
14
|
+
'pagedNavigation',
|
|
15
|
+
'multiple',
|
|
16
|
+
'preventDeselect',
|
|
17
|
+
'disableDaysOutsideCurrentView',
|
|
18
|
+
'disabled',
|
|
19
|
+
'readonly',
|
|
20
|
+
'minValue',
|
|
21
|
+
'maxValue',
|
|
22
|
+
'locale',
|
|
23
|
+
'dir',
|
|
24
|
+
'isDateDisabled',
|
|
25
|
+
'isDateUnavailable',
|
|
26
|
+
'nextPage',
|
|
27
|
+
'prevPage',
|
|
28
|
+
];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CalendarOptions, CalendarState, CalendarStore } from '@taqwim/calendar-core';
|
|
2
|
+
import { type Accessor } from 'solid-js';
|
|
3
|
+
export interface UseCalendarReturn {
|
|
4
|
+
store: CalendarStore;
|
|
5
|
+
state: Accessor<CalendarState>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Binds a `@taqwim/calendar-core` store to Solid's reactivity.
|
|
9
|
+
*
|
|
10
|
+
* `options` is an accessor so that reading props inside it registers them as
|
|
11
|
+
* dependencies of the effect below — Solid props are getters, so destructuring
|
|
12
|
+
* them here would freeze the calendar at its initial configuration.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createCalendarStore(options: Accessor<CalendarOptions>): UseCalendarReturn;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createCalendar } from '@taqwim/calendar-core';
|
|
2
|
+
import { createRenderEffect, createSignal, onCleanup } from 'solid-js';
|
|
3
|
+
/**
|
|
4
|
+
* Binds a `@taqwim/calendar-core` store to Solid's reactivity.
|
|
5
|
+
*
|
|
6
|
+
* `options` is an accessor so that reading props inside it registers them as
|
|
7
|
+
* dependencies of the effect below — Solid props are getters, so destructuring
|
|
8
|
+
* them here would freeze the calendar at its initial configuration.
|
|
9
|
+
*/
|
|
10
|
+
export function createCalendarStore(options) {
|
|
11
|
+
const store = createCalendar(options());
|
|
12
|
+
const [state, setState] = createSignal(store.getSnapshot());
|
|
13
|
+
onCleanup(store.subscribe(() => {
|
|
14
|
+
setState(store.getSnapshot());
|
|
15
|
+
}));
|
|
16
|
+
/*
|
|
17
|
+
* A render effect, not a plain effect: options have to reach the store
|
|
18
|
+
* before the components read the snapshot for this render. With
|
|
19
|
+
* `createEffect` the push lands after the DOM is written, so a controlled
|
|
20
|
+
* `value` handed back through `onValueChange` shows up one interaction late.
|
|
21
|
+
*
|
|
22
|
+
* Safe to run on every dependency change — the store compares the state it
|
|
23
|
+
* builds and stays quiet when nothing observable moved, so this cannot feed
|
|
24
|
+
* back into itself.
|
|
25
|
+
*/
|
|
26
|
+
createRenderEffect(() => {
|
|
27
|
+
store.setOptions(options());
|
|
28
|
+
});
|
|
29
|
+
return { store, state };
|
|
30
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@taqwim/solid",
|
|
3
|
+
"version": "0.1.0-beta.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Headless Solid components for Hijri calendars, built on @taqwim/calendar-core",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"accessibility",
|
|
8
|
+
"calendar",
|
|
9
|
+
"datepicker",
|
|
10
|
+
"headless",
|
|
11
|
+
"hijri",
|
|
12
|
+
"islamic",
|
|
13
|
+
"solid",
|
|
14
|
+
"solid-js",
|
|
15
|
+
"taqwim",
|
|
16
|
+
"typescript"
|
|
17
|
+
],
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "Brahim Boussadjra",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/boussadjra/taqwim.git",
|
|
23
|
+
"directory": "packages/solid"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist/**/*",
|
|
27
|
+
"CHANGELOG.md",
|
|
28
|
+
"LICENSE",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"type": "module",
|
|
32
|
+
"sideEffects": false,
|
|
33
|
+
"main": "./dist/index.js",
|
|
34
|
+
"module": "./dist/index.js",
|
|
35
|
+
"types": "./dist/source/index.d.ts",
|
|
36
|
+
"exports": {
|
|
37
|
+
".": {
|
|
38
|
+
"types": "./dist/source/index.d.ts",
|
|
39
|
+
"solid": "./dist/source/index.js",
|
|
40
|
+
"import": "./dist/index.js",
|
|
41
|
+
"default": "./dist/index.js"
|
|
42
|
+
},
|
|
43
|
+
"./package.json": "./package.json"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@taqwim/calendar-core": "0.1.0-beta.0",
|
|
50
|
+
"@taqwim/core": "0.1.0-beta.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@arethetypeswrong/cli": "^0.18.2",
|
|
54
|
+
"@solidjs/testing-library": "^0.8.10",
|
|
55
|
+
"@testing-library/dom": "^10.4.0",
|
|
56
|
+
"jsdom": "^26.1.0",
|
|
57
|
+
"publint": "^0.3.14",
|
|
58
|
+
"solid-js": "^1.9.14",
|
|
59
|
+
"typescript": "~5.9.3",
|
|
60
|
+
"vite": "^8.1.5",
|
|
61
|
+
"vite-plugin-solid": "^2.11.14",
|
|
62
|
+
"vitest": "^4.1.10"
|
|
63
|
+
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"solid-js": "^1.8.0"
|
|
66
|
+
},
|
|
67
|
+
"engines": {
|
|
68
|
+
"node": ">=20"
|
|
69
|
+
},
|
|
70
|
+
"scripts": {
|
|
71
|
+
"_comment": "build / test are declared as vp tasks in vite.config.ts",
|
|
72
|
+
"test:watch": "vp exec vitest"
|
|
73
|
+
}
|
|
74
|
+
}
|