@exsto/ds 0.0.1-rc.1 → 0.0.1-rc.13
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/README.md +108 -27
- package/dist/chunks/date-range-picker-CWVcHgy2.js +204 -0
- package/dist/chunks/date-range-picker-CWVcHgy2.js.map +1 -0
- package/dist/chunks/input-syWxjY2U.js +172 -0
- package/dist/chunks/input-syWxjY2U.js.map +1 -0
- package/dist/chunks/toggle-Bxg8P-61.js +490 -0
- package/dist/chunks/toggle-Bxg8P-61.js.map +1 -0
- package/dist/date-picker.js +345 -0
- package/dist/date-picker.js.map +1 -0
- package/dist/ds/src/date-picker.d.ts +9 -0
- package/dist/ds/src/index.d.ts +2 -3
- package/dist/ds/src/rich-text-editor.d.ts +8 -0
- package/dist/index.js +4514 -2
- package/dist/index.js.map +1 -0
- package/dist/rich-text-editor.js +905 -0
- package/dist/rich-text-editor.js.map +1 -0
- package/dist/source-hash.txt +1 -0
- package/dist/styles.css +938 -235
- package/dist/theme.css +308 -0
- package/dist/theme.css.d.ts +2 -0
- package/dist/ui/src/components/edyn/animated-number/animated-number.d.ts +30 -0
- package/dist/ui/src/components/edyn/animated-number/animated-number.test.d.ts +9 -0
- package/dist/ui/src/components/edyn/animated-number/index.d.ts +1 -0
- package/dist/ui/src/components/edyn/audio-player/audio-player.d.ts +3 -2
- package/dist/ui/src/components/edyn/date-picker/date-picker.d.ts +76 -0
- package/dist/ui/src/components/edyn/date-picker/date-picker.state.test.d.ts +9 -0
- package/dist/ui/src/components/edyn/date-picker/date-text.d.ts +40 -0
- package/dist/ui/src/components/edyn/date-picker/index.d.ts +2 -0
- package/dist/ui/src/components/edyn/date-range-picker/date-range-picker.d.ts +28 -0
- package/dist/ui/src/components/edyn/date-range-picker/date-range-picker.test.d.ts +9 -0
- package/dist/ui/src/components/edyn/date-range-picker/index.d.ts +1 -0
- package/dist/ui/src/components/edyn/initials-avatar/index.d.ts +1 -0
- package/dist/ui/src/components/edyn/initials-avatar/initials-avatar.d.ts +54 -0
- package/dist/ui/src/components/edyn/initials-avatar/initials-avatar.test.d.ts +10 -0
- package/dist/ui/src/components/edyn/multi-select/index.d.ts +1 -0
- package/dist/ui/src/components/edyn/multi-select/multi-select.d.ts +106 -0
- package/dist/ui/src/components/edyn/multi-select/multi-select.test.d.ts +9 -0
- package/dist/ui/src/components/edyn/progress-line/index.d.ts +1 -0
- package/dist/ui/src/components/edyn/progress-line/progress-line.d.ts +23 -0
- package/dist/ui/src/components/edyn/progress-line/progress-line.test.d.ts +9 -0
- package/dist/ui/src/components/edyn/rich-text-content/allow-list.d.ts +11 -0
- package/dist/ui/src/components/edyn/rich-text-content/index.d.ts +2 -0
- package/dist/ui/src/components/edyn/rich-text-content/rich-text-content.d.ts +22 -0
- package/dist/ui/src/components/edyn/rich-text-content/rich-text-content.test.d.ts +8 -0
- package/dist/ui/src/components/edyn/rich-text-editor/extensions.d.ts +18 -0
- package/dist/ui/src/components/edyn/rich-text-editor/index.d.ts +3 -0
- package/dist/ui/src/components/edyn/rich-text-editor/mention-list.d.ts +17 -0
- package/dist/ui/src/components/edyn/rich-text-editor/rich-text-editor.d.ts +53 -0
- package/dist/ui/src/components/edyn/rich-text-editor/rich-text-editor.test.d.ts +6 -0
- package/dist/ui/src/components/edyn/rich-text-editor/serialize.d.ts +13 -0
- package/dist/ui/src/components/edyn/rich-text-editor/sync.d.ts +19 -0
- package/dist/ui/src/components/edyn/rich-text-editor/sync.test.d.ts +11 -0
- package/dist/ui/src/components/edyn/rich-text-editor/toolbar.d.ts +20 -0
- package/dist/ui/src/components/edyn/rich-text-editor/types.d.ts +68 -0
- package/dist/ui/src/components/edyn/video-player/video-player.d.ts +2 -2
- package/dist/ui/src/components/forwarded-props.test.d.ts +8 -0
- package/dist/ui/src/components/ui/accordion/accordion.d.ts +9 -8
- package/dist/ui/src/components/ui/attachment/attachment.d.ts +1 -1
- package/dist/ui/src/components/ui/badge/badge.d.ts +1 -1
- package/dist/ui/src/components/ui/button/button.behavior.test.d.ts +8 -0
- package/dist/ui/src/components/ui/button/button.d.ts +1 -1
- package/dist/ui/src/components/ui/command/command.d.ts +17 -1
- package/dist/ui/src/components/ui/dropdown-menu/dropdown-menu.d.ts +12 -1
- package/dist/ui/src/components/ui/input-group/input-group.d.ts +1 -1
- package/dist/ui/src/components/ui/item/item.d.ts +1 -1
- package/dist/ui/src/components/ui/pagination/pagination.d.ts +3 -2
- package/dist/ui/src/components/ui/popover/popover.d.ts +1 -1
- package/dist/ui/src/components/ui/progress/progress.behavior.test.d.ts +8 -0
- package/dist/ui/src/components/ui/progress/progress.d.ts +43 -1
- package/dist/ui/src/components/ui/scroll-area/scroll-area.behavior.test.d.ts +12 -0
- package/dist/ui/src/components/ui/scroll-area/scroll-area.d.ts +59 -6
- package/dist/ui/src/components/ui/separator/separator.behavior.test.d.ts +14 -0
- package/dist/ui/src/components/ui/separator/separator.d.ts +14 -0
- package/dist/ui/src/components/ui/table/table.behavior.test.d.ts +4 -5
- package/dist/ui/src/components/ui/table/table.d.ts +8 -1
- package/dist/ui/src/components/ui/tabs/tabs.d.ts +9 -8
- package/dist/ui/src/index.d.ts +6 -0
- package/dist/ui/src/lib/first-weekday.d.ts +6 -0
- package/dist/ui/src/lib/focus-ring.test.d.ts +8 -0
- package/dist/ui/src/lib/popup-list.d.ts +53 -0
- package/dist/ui/src/lib/popup-list.test.d.ts +10 -0
- package/package.json +32 -8
- package/dist/chunks/toggle-group-ByNq8g7H.js +0 -4263
- package/dist/chunks/toggle-group-ByNq8g7H.js.map +0 -1
- package/dist/dom/src/context.d.ts +0 -34
- package/dist/dom/src/define-element.d.ts +0 -11
- package/dist/dom/src/field-control.d.ts +0 -5
- package/dist/dom/src/index.d.ts +0 -6
- package/dist/dom/src/mode.d.ts +0 -8
- package/dist/dom/src/styles.d.ts +0 -6
- package/dist/dom/src/theme-state.d.ts +0 -1
- package/dist/dom/src/types.d.ts +0 -65
- package/dist/ds/src/elements.d.ts +0 -12
- package/dist/elements/src/edx-accordion.d.ts +0 -14
- package/dist/elements/src/edx-accordion.test.d.ts +0 -1
- package/dist/elements/src/edx-alert.d.ts +0 -14
- package/dist/elements/src/edx-audio-player.d.ts +0 -11
- package/dist/elements/src/edx-avatar.d.ts +0 -10
- package/dist/elements/src/edx-badge.d.ts +0 -12
- package/dist/elements/src/edx-breadcrumb.d.ts +0 -11
- package/dist/elements/src/edx-button-group.d.ts +0 -7
- package/dist/elements/src/edx-button.d.ts +0 -20
- package/dist/elements/src/edx-calendar.d.ts +0 -21
- package/dist/elements/src/edx-card.d.ts +0 -8
- package/dist/elements/src/edx-checkbox.d.ts +0 -18
- package/dist/elements/src/edx-combobox.d.ts +0 -28
- package/dist/elements/src/edx-content-components.d.ts +0 -2
- package/dist/elements/src/edx-dialog.d.ts +0 -20
- package/dist/elements/src/edx-drawer.d.ts +0 -21
- package/dist/elements/src/edx-dropdown-menu.d.ts +0 -29
- package/dist/elements/src/edx-field.d.ts +0 -9
- package/dist/elements/src/edx-field.test.d.ts +0 -1
- package/dist/elements/src/edx-indicator.d.ts +0 -14
- package/dist/elements/src/edx-input-group.d.ts +0 -32
- package/dist/elements/src/edx-input-otp.d.ts +0 -19
- package/dist/elements/src/edx-input.d.ts +0 -31
- package/dist/elements/src/edx-interactive-components.d.ts +0 -2
- package/dist/elements/src/edx-label.d.ts +0 -6
- package/dist/elements/src/edx-pagination.d.ts +0 -8
- package/dist/elements/src/edx-popover.d.ts +0 -24
- package/dist/elements/src/edx-progress.d.ts +0 -10
- package/dist/elements/src/edx-radio-group.d.ts +0 -22
- package/dist/elements/src/edx-radio-group.test.d.ts +0 -1
- package/dist/elements/src/edx-scroll-area.d.ts +0 -15
- package/dist/elements/src/edx-scroll-area.test.d.ts +0 -1
- package/dist/elements/src/edx-select.d.ts +0 -34
- package/dist/elements/src/edx-separator.d.ts +0 -10
- package/dist/elements/src/edx-skeleton.d.ts +0 -8
- package/dist/elements/src/edx-slider.d.ts +0 -17
- package/dist/elements/src/edx-slider.test.d.ts +0 -1
- package/dist/elements/src/edx-switch.d.ts +0 -18
- package/dist/elements/src/edx-table.d.ts +0 -13
- package/dist/elements/src/edx-table.test.d.ts +0 -1
- package/dist/elements/src/edx-tabs.d.ts +0 -15
- package/dist/elements/src/edx-tabs.test.d.ts +0 -1
- package/dist/elements/src/edx-textarea.d.ts +0 -25
- package/dist/elements/src/edx-toggle-group.d.ts +0 -27
- package/dist/elements/src/edx-toggle-group.test.d.ts +0 -1
- package/dist/elements/src/edx-toggle.d.ts +0 -18
- package/dist/elements/src/edx-tooltip.d.ts +0 -28
- package/dist/elements/src/edx-video-player.d.ts +0 -16
- package/dist/elements/src/field-control-bridge.d.ts +0 -7
- package/dist/elements/src/index.d.ts +0 -14
- package/dist/elements/src/projected-content.test.d.ts +0 -1
- package/dist/elements/src/registry.d.ts +0 -39
- package/dist/elements/src/registry.test.d.ts +0 -1
- package/dist/elements.js +0 -4020
- package/dist/elements.js.map +0 -1
- /package/dist/{dom/src/define-element.test.d.ts → ui/src/components/edyn/date-picker/date-text.test.d.ts} +0 -0
- /package/dist/{dom/src/native-slot.test.d.ts → ui/src/lib/first-weekday.test.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# @exsto/ds
|
|
2
2
|
|
|
3
|
-
The eDynamix design system: React components
|
|
4
|
-
components.
|
|
3
|
+
The eDynamix design system: React components and design tokens.
|
|
5
4
|
|
|
6
5
|
## Install
|
|
7
6
|
|
|
@@ -30,10 +29,40 @@ That is the whole setup. **You do not need Tailwind.** `styles.css` ships
|
|
|
30
29
|
precompiled: it carries the design tokens plus every utility the components
|
|
31
30
|
themselves use.
|
|
32
31
|
|
|
33
|
-
If you
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
### If you use Tailwind
|
|
33
|
+
|
|
34
|
+
`styles.css` is compiled output, so it only contains the utilities the components
|
|
35
|
+
themselves use. To write `bg-card` or `text-muted-foreground` in your *own*
|
|
36
|
+
markup, import the token layer as well and let your Tailwind build generate the
|
|
37
|
+
whole vocabulary:
|
|
38
|
+
|
|
39
|
+
```css
|
|
40
|
+
@import "@exsto/ds/styles.css";
|
|
41
|
+
@import "tailwindcss";
|
|
42
|
+
@import "@exsto/ds/theme.css";
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**The order matters, and `styles.css` has to come first.** It is a compiled
|
|
46
|
+
Tailwind build, so its utilities land in the same cascade layer as yours, and at
|
|
47
|
+
equal specificity the later rule wins. Import it after your Tailwind entry and
|
|
48
|
+
its unconditional `.hidden` beats your `.md:table-cell`: responsive columns
|
|
49
|
+
vanish, widths collapse. Library CSS before app CSS, as with any CSS dependency.
|
|
50
|
+
|
|
51
|
+
Nothing else is needed. `theme.css` also points your build at the components'
|
|
52
|
+
shipped code, so your build emits their classes too, each responsive variant
|
|
53
|
+
after its own base. That is what keeps a design system class such as the
|
|
54
|
+
sidebar's `hidden md:block` working when your own markup uses `hidden` as well:
|
|
55
|
+
one build decides the order for both sides. Earlier releases needed a
|
|
56
|
+
`@source` line in the app to get this, and that line can be removed.
|
|
57
|
+
|
|
58
|
+
`theme.css` is uncompiled on purpose. It carries the tokens, the `.dark` scope,
|
|
59
|
+
the brand layer (Inter, `text-2xs`) and the `@theme` mapping Tailwind needs to
|
|
60
|
+
turn them into utilities. Do not copy that block into your app, or it will drift
|
|
61
|
+
from the design system.
|
|
62
|
+
|
|
63
|
+
Without it some token utilities still work, because our components happen to use
|
|
64
|
+
them, but the coverage is incidental: roughly half of `bg-*` and nearly every
|
|
65
|
+
`border-*` is absent.
|
|
37
66
|
|
|
38
67
|
### Dark mode
|
|
39
68
|
|
|
@@ -46,39 +75,91 @@ query, so a subtree can be dark inside a light page.
|
|
|
46
75
|
</div>
|
|
47
76
|
```
|
|
48
77
|
|
|
49
|
-
##
|
|
78
|
+
## Entry points
|
|
50
79
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
80
|
+
| Import | What it is |
|
|
81
|
+
|---|---|
|
|
82
|
+
| `@exsto/ds` | React components |
|
|
83
|
+
| `@exsto/ds/date-picker` | The DatePicker (one day, typed or picked), on its own entry so the main one stays inside its size budget |
|
|
84
|
+
| `@exsto/ds/rich-text-editor` | The rich-text editor, on its own entry so its engine loads only where it is used |
|
|
85
|
+
| `@exsto/ds/styles.css` | Precompiled tokens and component styles |
|
|
86
|
+
| `@exsto/ds/theme.css` | Uncompiled tokens, brand layer, and the Tailwind `@theme` mapping |
|
|
87
|
+
|
|
88
|
+
## Rich text
|
|
54
89
|
|
|
55
|
-
|
|
56
|
-
import { registerElements } from '@exsto/ds/elements';
|
|
90
|
+
`RichTextEditor` is a small rich-text field: paragraphs, bold, italic, underline, bulleted and numbered lists, links and a horizontal line, with optional `@mentions` that your app searches. It lives on its own entry because it carries an editor engine (about 138 KB gzipped); an app that never imports it never pays for it.
|
|
57
91
|
|
|
58
|
-
|
|
92
|
+
```tsx
|
|
93
|
+
import { RichTextContent } from '@exsto/ds';
|
|
94
|
+
import { RichTextEditor } from '@exsto/ds/rich-text-editor';
|
|
95
|
+
|
|
96
|
+
<RichTextEditor
|
|
97
|
+
aria-label="Comment"
|
|
98
|
+
value={html}
|
|
99
|
+
onChange={setHtml} // "" when there is no text
|
|
100
|
+
minRows={3}
|
|
101
|
+
mention={{ search: async (query, { page, signal }) => fetchPeople(query, page, signal) }}
|
|
102
|
+
/>;
|
|
103
|
+
|
|
104
|
+
<RichTextContent html={savedHtml} />;
|
|
59
105
|
```
|
|
60
106
|
|
|
61
|
-
|
|
62
|
-
|
|
107
|
+
The value is HTML. **The editor emits only these elements and attributes**, whatever is typed or pasted (anything else is dropped on paste and on load, keeping its text), so a server allow-list can be exactly this; the same lists are exported as `RICH_TEXT_ELEMENTS`, `RICH_TEXT_ATTRIBUTES` and `RICH_TEXT_LINK_PROTOCOLS`:
|
|
108
|
+
|
|
109
|
+
| Element | Attributes |
|
|
110
|
+
|---|---|
|
|
111
|
+
| `p`, `br`, `hr`, `strong`, `em`, `u`, `ul`, `ol`, `li` | none (no `class`, no `style`) |
|
|
112
|
+
| `a` | `href`, which must be absolute `http`, `https` or `mailto` |
|
|
113
|
+
|
|
114
|
+
Strong and em, not b and i; no `target` or `rel` on links (add them when you render, as `RichTextContent` does). The editor is a convenience for the writer, not a security boundary: sanitise on the server as well.
|
|
115
|
+
|
|
116
|
+
**Mentions.** Typing `@` opens a list under the caret that calls `mention.search(query, { page, signal })` after a pause (250 ms by default), aborting the previous call. Return an array (one page) or `{ items, hasMore }` (the next page is asked for when the list is scrolled to its end). An item is `{ id, label, secondary? }`: the chip shows `label` while editing, and the HTML carries **plain text, `@${id}` followed by a space, and no element** (`mention.toText` changes that). A value loaded back into the editor shows that text as text, not as a chip. `RichTextContent` marks runs that look like `@person@example.test` and `@assignees` as pills.
|
|
117
|
+
|
|
118
|
+
Also: `ref` gives `insertText(text)` and `focus()` for buttons outside the editor; `fields` adds an "Insert a field" menu that writes `{{id}}`; `maxLength` refuses edits past a number of characters; `onPasteFiles` receives files pasted without any text; `invalid` and `aria-invalid`, `disabled`, `readOnly`, `placeholder`, `labels` (every string) and `toolbar={false}` do what they say. The box is `minRows` lines tall when empty and grows to `maxRows`, then scrolls inside.
|
|
119
|
+
|
|
120
|
+
## Dates
|
|
121
|
+
|
|
122
|
+
`DatePicker` is one calendar day in a field: typed in the locale's order (`DD/MM/YYYY` for en-GB, `MM/DD/YYYY` for en-US) or picked from a calendar, read when the field is left, with a message under it when the text is not a day. It is on its own entry. The value is a `yyyy-mm-dd` string, not a `Date`, so no time zone moves it a day.
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
import { DatePicker } from '@exsto/ds/date-picker';
|
|
126
|
+
|
|
127
|
+
<DatePicker aria-label="Go-live date" value={day} onValueChange={setDay} min="2026-01-01" locale="en-GB" />;
|
|
63
128
|
```
|
|
64
129
|
|
|
65
|
-
`
|
|
66
|
-
compiled stylesheet, because a shadow root does not inherit the page's styles.
|
|
67
|
-
Pass `{ mode: 'light' }` to render into the light DOM instead, or `{ styles }` to
|
|
68
|
-
supply your own sheet. Calling it more than once is harmless.
|
|
130
|
+
`onValueChange(day, state)` gets `undefined` as the day when the field is emptied or its text is not a usable day. The second argument says which: `{ kind: 'day' }`, `{ kind: 'empty' }`, or `{ kind: 'refused'; text; problem }`, where `text` is what was typed and `problem` the message the picker shows under the field. A caller that ignores the second argument keeps working. A required field uses it to show its own message only when the field is empty, so the person never reads two:
|
|
69
131
|
|
|
70
|
-
|
|
132
|
+
```tsx
|
|
133
|
+
import { Field, FieldError, FieldLabel } from '@exsto/ds';
|
|
134
|
+
import { DatePicker, type TDatePickerState } from '@exsto/ds/date-picker';
|
|
135
|
+
|
|
136
|
+
const [day, setDay] = useState<string>();
|
|
137
|
+
const [state, setState] = useState<TDatePickerState>({ kind: 'empty' });
|
|
138
|
+
const missing = state.kind === 'empty'; // 'refused' is already said by the picker itself
|
|
139
|
+
|
|
140
|
+
<Field invalid={missing}>
|
|
141
|
+
<FieldLabel>Go-live date</FieldLabel>
|
|
142
|
+
<DatePicker
|
|
143
|
+
value={day}
|
|
144
|
+
onValueChange={(next, nextState) => {
|
|
145
|
+
setDay(next);
|
|
146
|
+
setState(nextState);
|
|
147
|
+
}}
|
|
148
|
+
/>
|
|
149
|
+
<FieldError match={missing}>Enter the date.</FieldError>
|
|
150
|
+
</Field>;
|
|
151
|
+
```
|
|
71
152
|
|
|
72
|
-
|
|
73
|
-
|---|---|
|
|
74
|
-
| `@exsto/ds` | React components (the default) |
|
|
75
|
-
| `@exsto/ds/styles.css` | Precompiled tokens and component styles |
|
|
76
|
-
| `@exsto/ds/elements` | `<edx-*>` custom elements and `registerElements()` |
|
|
153
|
+
Refused text is reported each time it changes, also when the field held no day before, and emptying the field afterwards reports `empty`. A parent's `aria-invalid` only draws the invalid border; the picker's own message is added to `aria-describedby` once, after the parent's. `DateRangePicker`, for a range, is in the main entry.
|
|
77
154
|
|
|
78
155
|
## Status
|
|
79
156
|
|
|
80
|
-
`0.0.1-rc.
|
|
81
|
-
`0.1.0`.
|
|
157
|
+
Release candidates (`0.0.1-rc.N`). The API is not stable yet; treat minor versions
|
|
158
|
+
as breaking until `0.1.0`.
|
|
159
|
+
|
|
160
|
+
`0.0.1-rc.5` removed the `@exsto/ds/elements` entry point (the `<edx-*>` web
|
|
161
|
+
components) and with it Angular support. The design system is React only. Stay on
|
|
162
|
+
`0.0.1-rc.4` if you depend on the elements.
|
|
82
163
|
|
|
83
164
|
## Licence
|
|
84
165
|
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { c as Button, l as buttonVariants, n as Popover, r as PopoverContent, s as PopoverTrigger, u as cn } from "./input-syWxjY2U.js";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { IconCalendar, IconChevronDown, IconChevronLeft, IconChevronRight } from "@tabler/icons-react";
|
|
5
|
+
import { DayPicker, getDefaultClassNames } from "react-day-picker";
|
|
6
|
+
//#region ../ui/src/components/ui/calendar/calendar.tsx
|
|
7
|
+
var Calendar = ({ className, classNames, showOutsideDays = true, captionLayout = "label", buttonVariant = "ghost", locale, formatters, components, ...props }) => {
|
|
8
|
+
const defaultClassNames = getDefaultClassNames();
|
|
9
|
+
return /* @__PURE__ */ jsx(DayPicker, {
|
|
10
|
+
showOutsideDays,
|
|
11
|
+
className: cn("group/calendar bg-background p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(7)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent", String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`, String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`, className),
|
|
12
|
+
captionLayout,
|
|
13
|
+
locale,
|
|
14
|
+
formatters: {
|
|
15
|
+
formatMonthDropdown: (date) => date.toLocaleString(locale?.code, { month: "short" }),
|
|
16
|
+
...formatters
|
|
17
|
+
},
|
|
18
|
+
classNames: {
|
|
19
|
+
root: cn("w-fit", defaultClassNames.root),
|
|
20
|
+
months: cn("relative flex flex-col gap-4 md:flex-row", defaultClassNames.months),
|
|
21
|
+
month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
|
|
22
|
+
nav: cn("absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1", defaultClassNames.nav),
|
|
23
|
+
button_previous: cn(buttonVariants({ variant: buttonVariant }), "size-(--cell-size) p-0 select-none aria-disabled:opacity-50", defaultClassNames.button_previous),
|
|
24
|
+
button_next: cn(buttonVariants({ variant: buttonVariant }), "size-(--cell-size) p-0 select-none aria-disabled:opacity-50", defaultClassNames.button_next),
|
|
25
|
+
month_caption: cn("flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)", defaultClassNames.month_caption),
|
|
26
|
+
dropdowns: cn("flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium", defaultClassNames.dropdowns),
|
|
27
|
+
dropdown_root: cn("relative rounded-(--cell-radius)", defaultClassNames.dropdown_root),
|
|
28
|
+
dropdown: cn("absolute inset-0 bg-popover opacity-0", defaultClassNames.dropdown),
|
|
29
|
+
caption_label: cn("font-medium select-none", captionLayout === "label" ? "text-sm" : "flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground", defaultClassNames.caption_label),
|
|
30
|
+
month_grid: cn("w-full border-collapse", defaultClassNames.month_grid),
|
|
31
|
+
weekdays: cn("flex", defaultClassNames.weekdays),
|
|
32
|
+
weekday: cn("flex-1 rounded-(--cell-radius) text-[0.8rem] font-normal text-muted-foreground select-none", defaultClassNames.weekday),
|
|
33
|
+
week: cn("mt-2 flex w-full", defaultClassNames.week),
|
|
34
|
+
week_number_header: cn("w-(--cell-size) select-none", defaultClassNames.week_number_header),
|
|
35
|
+
week_number: cn("text-[0.8rem] text-muted-foreground select-none", defaultClassNames.week_number),
|
|
36
|
+
day: cn("group/day relative aspect-square h-full w-full rounded-(--cell-radius) p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)", props.showWeekNumber ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)" : "[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)", defaultClassNames.day),
|
|
37
|
+
range_start: cn("relative isolate z-0 rounded-l-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:right-0 after:w-4 after:bg-muted", defaultClassNames.range_start),
|
|
38
|
+
range_middle: cn("rounded-none", defaultClassNames.range_middle),
|
|
39
|
+
range_end: cn("relative isolate z-0 rounded-r-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:left-0 after:w-4 after:bg-muted", defaultClassNames.range_end),
|
|
40
|
+
today: cn("rounded-(--cell-radius) bg-muted text-foreground data-[selected=true]:rounded-none", defaultClassNames.today),
|
|
41
|
+
outside: cn("text-muted-foreground aria-selected:text-muted-foreground", defaultClassNames.outside),
|
|
42
|
+
disabled: cn("text-muted-foreground opacity-50", defaultClassNames.disabled),
|
|
43
|
+
hidden: cn("invisible", defaultClassNames.hidden),
|
|
44
|
+
...classNames
|
|
45
|
+
},
|
|
46
|
+
components: {
|
|
47
|
+
Root: ({ className, rootRef, ...props }) => {
|
|
48
|
+
return /* @__PURE__ */ jsx("div", {
|
|
49
|
+
"data-slot": "calendar",
|
|
50
|
+
ref: rootRef,
|
|
51
|
+
className: cn(className),
|
|
52
|
+
...props
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
Chevron: ({ className, orientation, ...props }) => {
|
|
56
|
+
if (orientation === "left") return /* @__PURE__ */ jsx(IconChevronLeft, {
|
|
57
|
+
className: cn("size-4", className),
|
|
58
|
+
...props
|
|
59
|
+
});
|
|
60
|
+
if (orientation === "right") return /* @__PURE__ */ jsx(IconChevronRight, {
|
|
61
|
+
className: cn("size-4", className),
|
|
62
|
+
...props
|
|
63
|
+
});
|
|
64
|
+
return /* @__PURE__ */ jsx(IconChevronDown, {
|
|
65
|
+
className: cn("size-4", className),
|
|
66
|
+
...props
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
DayButton: ({ ...props }) => /* @__PURE__ */ jsx(CalendarDayButton, {
|
|
70
|
+
locale,
|
|
71
|
+
...props
|
|
72
|
+
}),
|
|
73
|
+
WeekNumber: ({ children, ...props }) => {
|
|
74
|
+
return /* @__PURE__ */ jsx("td", {
|
|
75
|
+
...props,
|
|
76
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
77
|
+
className: "flex size-(--cell-size) items-center justify-center text-center",
|
|
78
|
+
children
|
|
79
|
+
})
|
|
80
|
+
});
|
|
81
|
+
},
|
|
82
|
+
...components
|
|
83
|
+
},
|
|
84
|
+
...props
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
var CalendarDayButton = ({ className, day, modifiers, locale, ...props }) => {
|
|
88
|
+
const defaultClassNames = getDefaultClassNames();
|
|
89
|
+
const refButton = React.useRef(null);
|
|
90
|
+
React.useEffect(() => {
|
|
91
|
+
if (modifiers.focused) refButton.current?.focus();
|
|
92
|
+
}, [modifiers.focused]);
|
|
93
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
94
|
+
ref: refButton,
|
|
95
|
+
variant: "ghost",
|
|
96
|
+
size: "icon",
|
|
97
|
+
"data-day": day.date.toLocaleDateString(locale?.code),
|
|
98
|
+
"data-selected-single": modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle,
|
|
99
|
+
"data-range-start": modifiers.range_start,
|
|
100
|
+
"data-range-end": modifiers.range_end,
|
|
101
|
+
"data-range-middle": modifiers.range_middle,
|
|
102
|
+
className: cn("relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-r-(--cell-radius) data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:rounded-(--cell-radius) data-[range-start=true]:rounded-l-(--cell-radius) data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-foreground [&>span]:text-xs [&>span]:opacity-70", defaultClassNames.day, className),
|
|
103
|
+
...props
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region ../ui/src/hooks/use-mobile.ts
|
|
108
|
+
var MOBILE_BREAKPOINT = 768;
|
|
109
|
+
var useIsMobile = () => {
|
|
110
|
+
const [isMobile, setIsMobile] = React.useState(void 0);
|
|
111
|
+
React.useEffect(() => {
|
|
112
|
+
const mql = window.matchMedia(`(max-width: 767px)`);
|
|
113
|
+
const onChange = () => {
|
|
114
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
115
|
+
};
|
|
116
|
+
mql.addEventListener("change", onChange);
|
|
117
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
118
|
+
return () => mql.removeEventListener("change", onChange);
|
|
119
|
+
}, []);
|
|
120
|
+
return !!isMobile;
|
|
121
|
+
};
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region ../ui/src/lib/first-weekday.ts
|
|
124
|
+
var SUNDAY_FIRST = /* @__PURE__ */ new Set([
|
|
125
|
+
"US",
|
|
126
|
+
"CA",
|
|
127
|
+
"MX",
|
|
128
|
+
"BR",
|
|
129
|
+
"JP",
|
|
130
|
+
"IL",
|
|
131
|
+
"IN",
|
|
132
|
+
"PH",
|
|
133
|
+
"ZA",
|
|
134
|
+
"KR",
|
|
135
|
+
"TW",
|
|
136
|
+
"HK",
|
|
137
|
+
"SA"
|
|
138
|
+
]);
|
|
139
|
+
/**
|
|
140
|
+
* The weekday a locale starts its week on, 0 for Sunday to 6 for Saturday, from Intl, so en-GB starts on Monday and en-US on
|
|
141
|
+
* Sunday with no locale data of our own shipped. A browser without week info gets the region's usual start.
|
|
142
|
+
*/
|
|
143
|
+
var firstWeekdayOf = (locale) => {
|
|
144
|
+
try {
|
|
145
|
+
const info = new Intl.Locale(locale);
|
|
146
|
+
const first = info.getWeekInfo?.().firstDay ?? info.weekInfo?.firstDay;
|
|
147
|
+
if (first !== void 0) return first % 7;
|
|
148
|
+
return SUNDAY_FIRST.has(info.region ?? "") ? 0 : 1;
|
|
149
|
+
} catch {
|
|
150
|
+
return 0;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region ../ui/src/components/edyn/date-range-picker/date-range-picker.tsx
|
|
155
|
+
var dayToDate = (value) => value === void 0 ? void 0 : /* @__PURE__ */ new Date(`${value}T12:00:00`);
|
|
156
|
+
var dateToDay = (date) => date === void 0 ? void 0 : `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
157
|
+
/** A range of calendar days, either end optional; two months side by side, one on a phone. */
|
|
158
|
+
var DateRangePicker = ({ id, value, onValueChange, placeholder = "Select date range", locale = "en-GB", summary = "range", "aria-label": ariaLabel, className, ...props }) => {
|
|
159
|
+
const isMobile = useIsMobile();
|
|
160
|
+
const from = dayToDate(value.from);
|
|
161
|
+
const to = dayToDate(value.to);
|
|
162
|
+
const format = new Intl.DateTimeFormat(locale, {
|
|
163
|
+
day: "2-digit",
|
|
164
|
+
month: "short",
|
|
165
|
+
year: "numeric"
|
|
166
|
+
});
|
|
167
|
+
const ends = [from, to].filter((date) => date !== void 0).map((date) => format.format(date));
|
|
168
|
+
return /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsxs(PopoverTrigger, {
|
|
169
|
+
render: /* @__PURE__ */ jsx(Button, {
|
|
170
|
+
...props,
|
|
171
|
+
id,
|
|
172
|
+
"aria-label": ariaLabel,
|
|
173
|
+
"data-slot": "date-range-picker-trigger",
|
|
174
|
+
"data-set": ends.length > 0 ? "" : void 0,
|
|
175
|
+
variant: "outline",
|
|
176
|
+
className: cn("w-full min-w-0 justify-start font-normal", ends.length === 0 && "text-muted-foreground", className)
|
|
177
|
+
}),
|
|
178
|
+
children: [/* @__PURE__ */ jsx(IconCalendar, { "aria-hidden": "true" }), /* @__PURE__ */ jsx("span", {
|
|
179
|
+
className: "truncate",
|
|
180
|
+
children: ends.length === 0 || summary === "label" ? placeholder : ends.join(" – ")
|
|
181
|
+
})]
|
|
182
|
+
}), /* @__PURE__ */ jsx(PopoverContent, {
|
|
183
|
+
align: "start",
|
|
184
|
+
className: "w-auto p-0",
|
|
185
|
+
children: /* @__PURE__ */ jsx(Calendar, {
|
|
186
|
+
mode: "range",
|
|
187
|
+
defaultMonth: from,
|
|
188
|
+
numberOfMonths: isMobile ? 1 : 2,
|
|
189
|
+
weekStartsOn: firstWeekdayOf(locale),
|
|
190
|
+
selected: {
|
|
191
|
+
from,
|
|
192
|
+
to
|
|
193
|
+
},
|
|
194
|
+
onSelect: (range) => onValueChange({
|
|
195
|
+
from: dateToDay(range?.from),
|
|
196
|
+
to: dateToDay(range?.to)
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
})] });
|
|
200
|
+
};
|
|
201
|
+
//#endregion
|
|
202
|
+
export { useIsMobile as a, firstWeekdayOf as i, dateToDay as n, Calendar as o, dayToDate as r, CalendarDayButton as s, DateRangePicker as t };
|
|
203
|
+
|
|
204
|
+
//# sourceMappingURL=date-range-picker-CWVcHgy2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date-range-picker-CWVcHgy2.js","names":[],"sources":["../../../ui/src/components/ui/calendar/calendar.tsx","../../../ui/src/hooks/use-mobile.ts","../../../ui/src/lib/first-weekday.ts","../../../ui/src/components/edyn/date-range-picker/date-range-picker.tsx"],"sourcesContent":["import { cn } from '@edyn/ui/lib/utils';\nimport { IconChevronDown, IconChevronLeft, IconChevronRight } from '@tabler/icons-react';\nimport * as React from 'react';\nimport { type DayButton, DayPicker, getDefaultClassNames, type Locale } from 'react-day-picker';\nimport { Button, buttonVariants } from '../button';\n\nexport const Calendar = ({\n className,\n classNames,\n showOutsideDays = true,\n captionLayout = 'label',\n buttonVariant = 'ghost',\n locale,\n formatters,\n components,\n ...props\n}: React.ComponentProps<typeof DayPicker> & {\n buttonVariant?: React.ComponentProps<typeof Button>['variant'];\n}) => {\n const defaultClassNames = getDefaultClassNames();\n\n return (\n <DayPicker\n showOutsideDays={showOutsideDays}\n className={cn(\n 'group/calendar bg-background p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(7)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent',\n String.raw`rtl:**:[.rdp-button\\_next>svg]:rotate-180`,\n String.raw`rtl:**:[.rdp-button\\_previous>svg]:rotate-180`,\n className,\n )}\n captionLayout={captionLayout}\n locale={locale}\n formatters={{\n formatMonthDropdown: (date) => date.toLocaleString(locale?.code, { month: 'short' }),\n ...formatters,\n }}\n classNames={{\n root: cn('w-fit', defaultClassNames.root),\n months: cn('relative flex flex-col gap-4 md:flex-row', defaultClassNames.months),\n month: cn('flex w-full flex-col gap-4', defaultClassNames.month),\n nav: cn('absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1', defaultClassNames.nav),\n button_previous: cn(\n buttonVariants({ variant: buttonVariant }),\n 'size-(--cell-size) p-0 select-none aria-disabled:opacity-50',\n defaultClassNames.button_previous,\n ),\n button_next: cn(\n buttonVariants({ variant: buttonVariant }),\n 'size-(--cell-size) p-0 select-none aria-disabled:opacity-50',\n defaultClassNames.button_next,\n ),\n month_caption: cn('flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)', defaultClassNames.month_caption),\n dropdowns: cn('flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium', defaultClassNames.dropdowns),\n dropdown_root: cn('relative rounded-(--cell-radius)', defaultClassNames.dropdown_root),\n dropdown: cn('absolute inset-0 bg-popover opacity-0', defaultClassNames.dropdown),\n caption_label: cn(\n 'font-medium select-none',\n captionLayout === 'label'\n ? 'text-sm'\n : 'flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground',\n defaultClassNames.caption_label,\n ),\n month_grid: cn('w-full border-collapse', defaultClassNames.month_grid),\n weekdays: cn('flex', defaultClassNames.weekdays),\n weekday: cn('flex-1 rounded-(--cell-radius) text-[0.8rem] font-normal text-muted-foreground select-none', defaultClassNames.weekday),\n week: cn('mt-2 flex w-full', defaultClassNames.week),\n week_number_header: cn('w-(--cell-size) select-none', defaultClassNames.week_number_header),\n week_number: cn('text-[0.8rem] text-muted-foreground select-none', defaultClassNames.week_number),\n day: cn(\n 'group/day relative aspect-square h-full w-full rounded-(--cell-radius) p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)',\n props.showWeekNumber\n ? '[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)'\n : '[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)',\n defaultClassNames.day,\n ),\n range_start: cn(\n 'relative isolate z-0 rounded-l-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:right-0 after:w-4 after:bg-muted',\n defaultClassNames.range_start,\n ),\n range_middle: cn('rounded-none', defaultClassNames.range_middle),\n range_end: cn(\n 'relative isolate z-0 rounded-r-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:left-0 after:w-4 after:bg-muted',\n defaultClassNames.range_end,\n ),\n today: cn('rounded-(--cell-radius) bg-muted text-foreground data-[selected=true]:rounded-none', defaultClassNames.today),\n outside: cn('text-muted-foreground aria-selected:text-muted-foreground', defaultClassNames.outside),\n disabled: cn('text-muted-foreground opacity-50', defaultClassNames.disabled),\n hidden: cn('invisible', defaultClassNames.hidden),\n ...classNames,\n }}\n components={{\n Root: ({ className, rootRef, ...props }) => {\n return <div data-slot=\"calendar\" ref={rootRef} className={cn(className)} {...props} />;\n },\n Chevron: ({ className, orientation, ...props }) => {\n if (orientation === 'left') {\n return <IconChevronLeft className={cn('size-4', className)} {...props} />;\n }\n\n if (orientation === 'right') {\n return <IconChevronRight className={cn('size-4', className)} {...props} />;\n }\n\n return <IconChevronDown className={cn('size-4', className)} {...props} />;\n },\n DayButton: ({ ...props }) => <CalendarDayButton locale={locale} {...props} />,\n WeekNumber: ({ children, ...props }) => {\n return (\n <td {...props}>\n <div className=\"flex size-(--cell-size) items-center justify-center text-center\">{children}</div>\n </td>\n );\n },\n ...components,\n }}\n {...props}\n />\n );\n};\n\nexport const CalendarDayButton = ({\n className,\n day,\n modifiers,\n locale,\n ...props\n}: React.ComponentProps<typeof DayButton> & { locale?: Partial<Locale> }) => {\n const defaultClassNames = getDefaultClassNames();\n\n const refButton = React.useRef<HTMLButtonElement>(null);\n React.useEffect(() => {\n if (modifiers.focused) {\n refButton.current?.focus();\n }\n }, [modifiers.focused]);\n\n return (\n <Button\n ref={refButton}\n variant=\"ghost\"\n size=\"icon\"\n data-day={day.date.toLocaleDateString(locale?.code)}\n data-selected-single={modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle}\n data-range-start={modifiers.range_start}\n data-range-end={modifiers.range_end}\n data-range-middle={modifiers.range_middle}\n className={cn(\n 'relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-r-(--cell-radius) data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:rounded-(--cell-radius) data-[range-start=true]:rounded-l-(--cell-radius) data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-foreground [&>span]:text-xs [&>span]:opacity-70',\n defaultClassNames.day,\n className,\n )}\n {...props}\n />\n );\n};\n","import * as React from 'react';\n\nconst MOBILE_BREAKPOINT = 768;\n\nexport const useIsMobile = () => {\n const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);\n\n React.useEffect(() => {\n const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);\n const onChange = () => {\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);\n };\n mql.addEventListener('change', onChange);\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);\n return () => mql.removeEventListener('change', onChange);\n }, []);\n\n return !!isMobile;\n};\n","export type TWeekday = 0 | 1 | 2 | 3 | 4 | 5 | 6;\n\n// Regions whose week starts on a Sunday, for a browser that cannot say (Intl week info is not everywhere yet).\nconst SUNDAY_FIRST = new Set(['US', 'CA', 'MX', 'BR', 'JP', 'IL', 'IN', 'PH', 'ZA', 'KR', 'TW', 'HK', 'SA']);\n\n/**\n * The weekday a locale starts its week on, 0 for Sunday to 6 for Saturday, from Intl, so en-GB starts on Monday and en-US on\n * Sunday with no locale data of our own shipped. A browser without week info gets the region's usual start.\n */\nexport const firstWeekdayOf = (locale: string): TWeekday => {\n try {\n const info = new Intl.Locale(locale) as Intl.Locale & { getWeekInfo?: () => { firstDay: number }; weekInfo?: { firstDay: number } };\n const first = info.getWeekInfo?.().firstDay ?? info.weekInfo?.firstDay;\n if (first !== undefined) {\n // Intl counts Monday as 1 to Sunday as 7; the calendar counts Sunday as 0.\n return (first % 7) as TWeekday;\n }\n return SUNDAY_FIRST.has(info.region ?? '') ? 0 : 1;\n } catch {\n return 0;\n }\n};\n","import { Button } from '@edyn/ui/components/ui/button';\nimport { Calendar } from '@edyn/ui/components/ui/calendar';\nimport { Popover, PopoverContent, PopoverTrigger } from '@edyn/ui/components/ui/popover';\nimport { useIsMobile } from '@edyn/ui/hooks/use-mobile';\nimport { firstWeekdayOf } from '@edyn/ui/lib/first-weekday';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconCalendar } from '@tabler/icons-react';\nimport type { ComponentPropsWithoutRef } from 'react';\n\n/** A range of calendar days as yyyy-mm-dd, either end optional. */\nexport type TDateRange = { from?: string; to?: string };\n\n// A calendar day is read and written at midday local time, so no time zone or daylight\n// saving change can move it onto the day before or after.\nexport const dayToDate = (value: string | undefined): Date | undefined => (value === undefined ? undefined : new Date(`${value}T12:00:00`));\n\nexport const dateToDay = (date: Date | undefined): string | undefined =>\n date === undefined ? undefined : `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;\n\n/** Anything else a button takes (data-*, aria-*, title) goes to the trigger. */\nexport interface IDateRangePickerProps\n extends Omit<ComponentPropsWithoutRef<'button'>, 'value' | 'defaultValue' | 'placeholder' | 'id' | 'className' | 'children' | 'onChange' | 'type'> {\n id?: string;\n value: TDateRange;\n onValueChange: (value: TDateRange) => void;\n placeholder?: string;\n /**\n * What the trigger says once a range is set. `range` (default) writes the ends; `label` keeps the label, so a\n * range the page shows elsewhere (as a removable chip, say) is not repeated here. In both, the trigger carries\n * `data-set` while either end is set, for a look of its own.\n */\n summary?: 'range' | 'label';\n /** How the ends are written on the trigger, and which weekday the calendar starts on (BCP 47); en-GB reads 05 Mar 2026 and starts on Monday. */\n locale?: string;\n /** An accessible name for the trigger, when there is no visible label to point at. */\n 'aria-label'?: string;\n className?: string;\n}\n\n/** A range of calendar days, either end optional; two months side by side, one on a phone. */\nexport const DateRangePicker = ({\n id,\n value,\n onValueChange,\n placeholder = 'Select date range',\n locale = 'en-GB',\n summary = 'range',\n 'aria-label': ariaLabel,\n className,\n ...props\n}: IDateRangePickerProps) => {\n const isMobile = useIsMobile();\n const from = dayToDate(value.from);\n const to = dayToDate(value.to);\n const format = new Intl.DateTimeFormat(locale, { day: '2-digit', month: 'short', year: 'numeric' });\n const ends = [from, to].filter((date) => date !== undefined).map((date) => format.format(date));\n return (\n <Popover>\n <PopoverTrigger\n render={\n <Button\n {...props}\n id={id}\n aria-label={ariaLabel}\n data-slot=\"date-range-picker-trigger\"\n data-set={ends.length > 0 ? '' : undefined}\n variant=\"outline\"\n className={cn('w-full min-w-0 justify-start font-normal', ends.length === 0 && 'text-muted-foreground', className)}\n />\n }\n >\n <IconCalendar aria-hidden=\"true\" />\n <span className=\"truncate\">{ends.length === 0 || summary === 'label' ? placeholder : ends.join(' – ')}</span>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-auto p-0\">\n <Calendar\n mode=\"range\"\n defaultMonth={from}\n numberOfMonths={isMobile ? 1 : 2}\n weekStartsOn={firstWeekdayOf(locale)}\n selected={{ from, to }}\n onSelect={(range) => onValueChange({ from: dateToDay(range?.from), to: dateToDay(range?.to) })}\n />\n </PopoverContent>\n </Popover>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { isValidElement } from "react";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Button } from "@base-ui/react/button";
|
|
4
|
+
import { clsx } from "clsx";
|
|
5
|
+
import { twMerge } from "tailwind-merge";
|
|
6
|
+
import { cva } from "class-variance-authority";
|
|
7
|
+
import { Popover } from "@base-ui/react/popover";
|
|
8
|
+
import { Input } from "@base-ui/react/input";
|
|
9
|
+
//#region ../ui/src/lib/utils.ts
|
|
10
|
+
var cn = (...inputs) => twMerge(clsx(inputs));
|
|
11
|
+
/**
|
|
12
|
+
* Normalise a size prop to a CSS length.
|
|
13
|
+
*
|
|
14
|
+
* Bare numbers become px, which is also what a bare numeric *string* means: an
|
|
15
|
+
* element attribute arrives as a string, so `width="480"` and `width={480}` have
|
|
16
|
+
* to land on the same value. Anything with a unit, a percentage or a keyword
|
|
17
|
+
* passes through untouched.
|
|
18
|
+
*/
|
|
19
|
+
var cssLength = (value) => {
|
|
20
|
+
if (value == null || value === "") return;
|
|
21
|
+
if (typeof value === "number") return Number.isFinite(value) ? `${value}px` : void 0;
|
|
22
|
+
return /^-?\d*\.?\d+$/.test(value.trim()) ? `${value.trim()}px` : value;
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region ../ui/src/components/ui/button/button.tsx
|
|
26
|
+
/**
|
|
27
|
+
* The fill of a disabled Button that has one: muted, whichever way it is disabled (a button's own disabled, Base UI's
|
|
28
|
+
* data-disabled, or aria-disabled on a link), in the dark theme too, where the variants set their own fill. Ghost and link
|
|
29
|
+
* have no fill to begin with and are given none: a grey box would turn a link into a button.
|
|
30
|
+
*/
|
|
31
|
+
var DISABLED_FILL = "disabled:bg-muted data-disabled:bg-muted aria-disabled:bg-muted dark:disabled:bg-muted dark:data-disabled:bg-muted dark:aria-disabled:bg-muted";
|
|
32
|
+
var buttonVariants = cva("group/button inline-flex shrink-0 cursor-pointer items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all motion-reduce:transition-none motion-reduce:transform-none outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:not-aria-disabled:translate-y-px disabled:pointer-events-none disabled:border-transparent disabled:text-foreground/60 disabled:shadow-none disabled:focus-visible:ring-0 dark:disabled:border-transparent data-link-disabled:pointer-events-none data-link-disabled:focus-visible:ring-0 data-disabled:border-transparent data-disabled:text-foreground/60 data-disabled:shadow-none dark:data-disabled:border-transparent aria-disabled:border-transparent aria-disabled:text-foreground/60 aria-disabled:shadow-none dark:aria-disabled:border-transparent aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
|
|
33
|
+
variants: {
|
|
34
|
+
variant: {
|
|
35
|
+
default: `bg-primary text-primary-foreground hover:bg-primary/80 ${DISABLED_FILL}`,
|
|
36
|
+
outline: `border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50 ${DISABLED_FILL}`,
|
|
37
|
+
secondary: `bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground ${DISABLED_FILL}`,
|
|
38
|
+
ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50 aria-disabled:hover:bg-transparent dark:aria-disabled:hover:bg-transparent",
|
|
39
|
+
destructive: `bg-destructive/10 text-destructive hover:bg-destructive/15 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/15 dark:hover:bg-destructive/20 dark:focus-visible:ring-destructive/40 ${DISABLED_FILL}`,
|
|
40
|
+
link: "text-primary underline-offset-4 hover:underline aria-disabled:hover:no-underline"
|
|
41
|
+
},
|
|
42
|
+
size: {
|
|
43
|
+
default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
44
|
+
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
45
|
+
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
46
|
+
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
47
|
+
icon: "size-8",
|
|
48
|
+
"icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
|
|
49
|
+
"icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
|
|
50
|
+
"icon-lg": "size-9"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
defaultVariants: {
|
|
54
|
+
variant: "default",
|
|
55
|
+
size: "default"
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
/**
|
|
59
|
+
* Whether this Button is rendering a link rather than a button, so it can keep
|
|
60
|
+
* link semantics. Covers `render={<a href="..." />}` and a routing component
|
|
61
|
+
* that takes an `href` (Next's Link). A component with a different prop for its
|
|
62
|
+
* destination (react-router's `to`) cannot be recognised from here: give those
|
|
63
|
+
* `nativeButton={false} role={undefined}` directly.
|
|
64
|
+
*/
|
|
65
|
+
var rendersALink = (render) => isValidElement(render) && (render.type === "a" || typeof render.props.href === "string");
|
|
66
|
+
/**
|
|
67
|
+
* A link that looks like a button is still a link, and has to be announced as
|
|
68
|
+
* one: it opens in a new tab, it has an address, Enter follows it and Space
|
|
69
|
+
* scrolls the page.
|
|
70
|
+
*
|
|
71
|
+
* Base UI needs telling, in two steps. Rendering an anchor while `nativeButton`
|
|
72
|
+
* is true (its default) warns in development and puts `type="button"` on the
|
|
73
|
+
* anchor; following the warning and passing `nativeButton={false}` then adds
|
|
74
|
+
* `role="button"`, which is what takes the link semantics away. So this does
|
|
75
|
+
* both: non-native, with the role and the tabindex Base UI would add cleared
|
|
76
|
+
* again. An explicit prop from the caller still wins over either.
|
|
77
|
+
*/
|
|
78
|
+
var LINK_PROPS = {
|
|
79
|
+
nativeButton: false,
|
|
80
|
+
role: void 0,
|
|
81
|
+
tabIndex: void 0
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* A disabled link is not :disabled (only a form control can be), so the markup has to say it: Base UI already marks it
|
|
85
|
+
* `aria-disabled` and `data-disabled` and stops the click, and this takes it out of the tab order, which LINK_PROPS
|
|
86
|
+
* would otherwise leave to the browser. The href is kept on purpose, so a routing component that requires one keeps
|
|
87
|
+
* working; the pointer is kept off the link by `data-link-disabled:pointer-events-none`, which also covers a middle
|
|
88
|
+
* click and the context menu, and the click and Enter are stopped by Base UI. It is a data attribute of its own, not
|
|
89
|
+
* `aria-disabled`, because an aria-disabled button (`focusableWhenDisabled`) is meant to stay hoverable and focusable so it
|
|
90
|
+
* can say why it cannot act; only a native :disabled button and this link are inert.
|
|
91
|
+
*/
|
|
92
|
+
var DISABLED_LINK_PROPS = {
|
|
93
|
+
...LINK_PROPS,
|
|
94
|
+
tabIndex: -1,
|
|
95
|
+
"data-link-disabled": ""
|
|
96
|
+
};
|
|
97
|
+
var Button$1 = ({ className, variant = "default", size = "default", ...props }) => {
|
|
98
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
99
|
+
"data-slot": "button",
|
|
100
|
+
...rendersALink(props.render) ? props.disabled ? DISABLED_LINK_PROPS : LINK_PROPS : {},
|
|
101
|
+
className: cn(buttonVariants({
|
|
102
|
+
variant,
|
|
103
|
+
size,
|
|
104
|
+
className
|
|
105
|
+
})),
|
|
106
|
+
...props
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region ../ui/src/components/ui/popover/popover.tsx
|
|
111
|
+
var Popover$1 = ({ ...props }) => {
|
|
112
|
+
return /* @__PURE__ */ jsx(Popover.Root, {
|
|
113
|
+
"data-slot": "popover",
|
|
114
|
+
...props
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
var PopoverTrigger = ({ ...props }) => {
|
|
118
|
+
return /* @__PURE__ */ jsx(Popover.Trigger, {
|
|
119
|
+
"data-slot": "popover-trigger",
|
|
120
|
+
...props
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
var PopoverContent = ({ className, align = "center", alignOffset = 0, side = "bottom", sideOffset = 4, anchor, ...props }) => {
|
|
124
|
+
return /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(Popover.Positioner, {
|
|
125
|
+
align,
|
|
126
|
+
alignOffset,
|
|
127
|
+
anchor,
|
|
128
|
+
side,
|
|
129
|
+
sideOffset,
|
|
130
|
+
className: "isolate z-50",
|
|
131
|
+
children: /* @__PURE__ */ jsx(Popover.Popup, {
|
|
132
|
+
"data-slot": "popover-content",
|
|
133
|
+
className: cn("z-50 flex w-72 origin-(--transform-origin) flex-col gap-2.5 rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 motion-reduce:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className),
|
|
134
|
+
...props
|
|
135
|
+
})
|
|
136
|
+
}) });
|
|
137
|
+
};
|
|
138
|
+
var PopoverHeader = ({ className, ...props }) => {
|
|
139
|
+
return /* @__PURE__ */ jsx("div", {
|
|
140
|
+
"data-slot": "popover-header",
|
|
141
|
+
className: cn("flex flex-col gap-0.5 text-sm", className),
|
|
142
|
+
...props
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
var PopoverTitle = ({ className, ...props }) => {
|
|
146
|
+
return /* @__PURE__ */ jsx(Popover.Title, {
|
|
147
|
+
"data-slot": "popover-title",
|
|
148
|
+
className: cn("font-medium", className),
|
|
149
|
+
...props
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
var PopoverDescription = ({ className, ...props }) => {
|
|
153
|
+
return /* @__PURE__ */ jsx(Popover.Description, {
|
|
154
|
+
"data-slot": "popover-description",
|
|
155
|
+
className: cn("text-muted-foreground", className),
|
|
156
|
+
...props
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region ../ui/src/components/ui/input/input.tsx
|
|
161
|
+
var Input$1 = ({ className, type, ...props }) => {
|
|
162
|
+
return /* @__PURE__ */ jsx(Input, {
|
|
163
|
+
type,
|
|
164
|
+
"data-slot": "input",
|
|
165
|
+
className: cn("h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className),
|
|
166
|
+
...props
|
|
167
|
+
});
|
|
168
|
+
};
|
|
169
|
+
//#endregion
|
|
170
|
+
export { PopoverHeader as a, Button$1 as c, cssLength as d, PopoverDescription as i, buttonVariants as l, Popover$1 as n, PopoverTitle as o, PopoverContent as r, PopoverTrigger as s, Input$1 as t, cn as u };
|
|
171
|
+
|
|
172
|
+
//# sourceMappingURL=input-syWxjY2U.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-syWxjY2U.js","names":[],"sources":["../../../ui/src/lib/utils.ts","../../../ui/src/components/ui/button/button.tsx","../../../ui/src/components/ui/popover/popover.tsx","../../../ui/src/components/ui/input/input.tsx"],"sourcesContent":["import type { ClassValue } from 'clsx';\nimport { clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport const cn = (...inputs: ClassValue[]): string => twMerge(clsx(inputs));\n\n/** A CSS length as a component prop: a number is px, a string is used verbatim. */\nexport type TCssLength = number | string;\n\n/**\n * Normalise a size prop to a CSS length.\n *\n * Bare numbers become px, which is also what a bare numeric *string* means: an\n * element attribute arrives as a string, so `width=\"480\"` and `width={480}` have\n * to land on the same value. Anything with a unit, a percentage or a keyword\n * passes through untouched.\n */\nexport const cssLength = (value: TCssLength | null | undefined): string | undefined => {\n if (value == null || value === '') {\n return undefined;\n }\n\n if (typeof value === 'number') {\n return Number.isFinite(value) ? `${value}px` : undefined;\n }\n\n return /^-?\\d*\\.?\\d+$/.test(value.trim()) ? `${value.trim()}px` : value;\n};\n","import { Button as ButtonPrimitive } from '@base-ui/react/button';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { isValidElement } from 'react';\n\n/**\n * The fill of a disabled Button that has one: muted, whichever way it is disabled (a button's own disabled, Base UI's\n * data-disabled, or aria-disabled on a link), in the dark theme too, where the variants set their own fill. Ghost and link\n * have no fill to begin with and are given none: a grey box would turn a link into a button.\n */\nconst DISABLED_FILL =\n 'disabled:bg-muted data-disabled:bg-muted aria-disabled:bg-muted dark:disabled:bg-muted dark:data-disabled:bg-muted dark:aria-disabled:bg-muted';\n\nexport const buttonVariants = cva(\n \"group/button inline-flex shrink-0 cursor-pointer items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all motion-reduce:transition-none motion-reduce:transform-none outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:not-aria-disabled:translate-y-px disabled:pointer-events-none disabled:border-transparent disabled:text-foreground/60 disabled:shadow-none disabled:focus-visible:ring-0 dark:disabled:border-transparent data-link-disabled:pointer-events-none data-link-disabled:focus-visible:ring-0 data-disabled:border-transparent data-disabled:text-foreground/60 data-disabled:shadow-none dark:data-disabled:border-transparent aria-disabled:border-transparent aria-disabled:text-foreground/60 aria-disabled:shadow-none dark:aria-disabled:border-transparent aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n variants: {\n variant: {\n default: `bg-primary text-primary-foreground hover:bg-primary/80 ${DISABLED_FILL}`,\n outline: `border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50 ${DISABLED_FILL}`,\n secondary: `bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground ${DISABLED_FILL}`,\n ghost:\n 'hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50 aria-disabled:hover:bg-transparent dark:aria-disabled:hover:bg-transparent',\n destructive: `bg-destructive/10 text-destructive hover:bg-destructive/15 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/15 dark:hover:bg-destructive/20 dark:focus-visible:ring-destructive/40 ${DISABLED_FILL}`,\n link: 'text-primary underline-offset-4 hover:underline aria-disabled:hover:no-underline',\n },\n size: {\n default: 'h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',\n xs: \"h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3\",\n sm: \"h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5\",\n lg: 'h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',\n icon: 'size-8',\n 'icon-xs': \"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3\",\n 'icon-sm': 'size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg',\n 'icon-lg': 'size-9',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n },\n);\n\n/**\n * Whether this Button is rendering a link rather than a button, so it can keep\n * link semantics. Covers `render={<a href=\"...\" />}` and a routing component\n * that takes an `href` (Next's Link). A component with a different prop for its\n * destination (react-router's `to`) cannot be recognised from here: give those\n * `nativeButton={false} role={undefined}` directly.\n */\nconst rendersALink = (render: ButtonPrimitive.Props['render']): boolean =>\n isValidElement(render) && (render.type === 'a' || typeof (render.props as { href?: unknown }).href === 'string');\n\n/**\n * A link that looks like a button is still a link, and has to be announced as\n * one: it opens in a new tab, it has an address, Enter follows it and Space\n * scrolls the page.\n *\n * Base UI needs telling, in two steps. Rendering an anchor while `nativeButton`\n * is true (its default) warns in development and puts `type=\"button\"` on the\n * anchor; following the warning and passing `nativeButton={false}` then adds\n * `role=\"button\"`, which is what takes the link semantics away. So this does\n * both: non-native, with the role and the tabindex Base UI would add cleared\n * again. An explicit prop from the caller still wins over either.\n */\nconst LINK_PROPS = { nativeButton: false, role: undefined, tabIndex: undefined } as const;\n\n/**\n * A disabled link is not :disabled (only a form control can be), so the markup has to say it: Base UI already marks it\n * `aria-disabled` and `data-disabled` and stops the click, and this takes it out of the tab order, which LINK_PROPS\n * would otherwise leave to the browser. The href is kept on purpose, so a routing component that requires one keeps\n * working; the pointer is kept off the link by `data-link-disabled:pointer-events-none`, which also covers a middle\n * click and the context menu, and the click and Enter are stopped by Base UI. It is a data attribute of its own, not\n * `aria-disabled`, because an aria-disabled button (`focusableWhenDisabled`) is meant to stay hoverable and focusable so it\n * can say why it cannot act; only a native :disabled button and this link are inert.\n */\nconst DISABLED_LINK_PROPS = { ...LINK_PROPS, tabIndex: -1, 'data-link-disabled': '' } as const;\n\nexport const Button = ({\n className,\n variant = 'default',\n size = 'default',\n ...props\n}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) => {\n return (\n <ButtonPrimitive\n data-slot=\"button\"\n {...(rendersALink(props.render) ? (props.disabled ? DISABLED_LINK_PROPS : LINK_PROPS) : {})}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n};\n","import { Popover as PopoverPrimitive } from '@base-ui/react/popover';\nimport { cn } from '@edyn/ui/lib/utils';\nimport type * as React from 'react';\n\nexport const Popover = ({ ...props }: PopoverPrimitive.Root.Props) => {\n return <PopoverPrimitive.Root data-slot=\"popover\" {...props} />;\n};\n\nexport const PopoverTrigger = ({ ...props }: PopoverPrimitive.Trigger.Props) => {\n return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />;\n};\n\nexport const PopoverContent = ({\n className,\n align = 'center',\n alignOffset = 0,\n side = 'bottom',\n sideOffset = 4,\n anchor,\n ...props\n}: PopoverPrimitive.Popup.Props & Pick<PopoverPrimitive.Positioner.Props, 'align' | 'alignOffset' | 'anchor' | 'side' | 'sideOffset'>) => {\n return (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Positioner\n align={align}\n alignOffset={alignOffset}\n anchor={anchor}\n side={side}\n sideOffset={sideOffset}\n className=\"isolate z-50\"\n >\n <PopoverPrimitive.Popup\n data-slot=\"popover-content\"\n className={cn(\n 'z-50 flex w-72 origin-(--transform-origin) flex-col gap-2.5 rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 motion-reduce:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',\n className,\n )}\n {...props}\n />\n </PopoverPrimitive.Positioner>\n </PopoverPrimitive.Portal>\n );\n};\n\nexport const PopoverHeader = ({ className, ...props }: React.ComponentProps<'div'>) => {\n return <div data-slot=\"popover-header\" className={cn('flex flex-col gap-0.5 text-sm', className)} {...props} />;\n};\n\nexport const PopoverTitle = ({ className, ...props }: PopoverPrimitive.Title.Props) => {\n return <PopoverPrimitive.Title data-slot=\"popover-title\" className={cn('font-medium', className)} {...props} />;\n};\n\nexport const PopoverDescription = ({ className, ...props }: PopoverPrimitive.Description.Props) => {\n return <PopoverPrimitive.Description data-slot=\"popover-description\" className={cn('text-muted-foreground', className)} {...props} />;\n};\n","import { Input as InputPrimitive } from '@base-ui/react/input';\nimport { cn } from '@edyn/ui/lib/utils';\nimport type * as React from 'react';\n\nexport const Input = ({ className, type, ...props }: React.ComponentProps<'input'>) => {\n return (\n <InputPrimitive\n type={type}\n data-slot=\"input\"\n className={cn(\n 'h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40',\n className,\n )}\n {...props}\n />\n );\n};\n"],"mappings":"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"}
|