@refraction-ui/react 0.19.1 → 0.20.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/dist/chunk-533MCMSU.js +2355 -0
- package/dist/chunk-533MCMSU.js.map +7 -0
- package/dist/chunk-AMK4VCBG.js +250 -0
- package/dist/chunk-AMK4VCBG.js.map +7 -0
- package/dist/chunk-BOLFYVI6.js +90 -0
- package/dist/chunk-BOLFYVI6.js.map +7 -0
- package/dist/chunk-F7B64KT3.js +187 -0
- package/dist/chunk-F7B64KT3.js.map +7 -0
- package/dist/chunk-FD37DFCC.js +77 -0
- package/dist/chunk-FD37DFCC.js.map +7 -0
- package/dist/chunk-H54FLNAF.js +132 -0
- package/dist/chunk-H54FLNAF.js.map +7 -0
- package/dist/chunk-INP46354.js +664 -0
- package/dist/chunk-INP46354.js.map +7 -0
- package/dist/chunk-ONACRMG4.js +116 -0
- package/dist/chunk-ONACRMG4.js.map +7 -0
- package/dist/chunk-SDZ3LROZ.js +7 -0
- package/dist/chunk-SDZ3LROZ.js.map +7 -0
- package/dist/chunk-TOZBR4GL.js +154 -0
- package/dist/chunk-TOZBR4GL.js.map +7 -0
- package/dist/chunk-UNEM2TRI.js +225 -0
- package/dist/chunk-UNEM2TRI.js.map +7 -0
- package/dist/chunk-VK3EQ34F.js +92 -0
- package/dist/chunk-VK3EQ34F.js.map +7 -0
- package/dist/chunk-WK2IIUFO.js +316 -0
- package/dist/chunk-WK2IIUFO.js.map +7 -0
- package/dist/chunk-WSPJTQAV.js +59 -0
- package/dist/chunk-WSPJTQAV.js.map +7 -0
- package/dist/chunk-XWP763SH.js +3 -2
- package/dist/chunk-XWP763SH.js.map +7 -1
- package/dist/chunk-Y2FNNAIL.js +80 -0
- package/dist/chunk-Y2FNNAIL.js.map +1 -0
- package/dist/{chunk-4A4BW3GR.js → chunk-ZF3OHCGV.js} +28 -5
- package/dist/chunk-ZF3OHCGV.js.map +7 -0
- package/dist/form.cjs +5 -11
- package/dist/form.cjs.map +1 -1
- package/dist/form.js +6 -12
- package/dist/form.js.map +1 -1
- package/dist/index.cjs +2879 -33936
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +126 -28780
- package/dist/internal/react-app-shell/index.d.cts +2 -22
- package/dist/internal/react-app-shell/index.d.ts +2 -22
- package/dist/internal/react-badge/index.d.cts +5 -0
- package/dist/internal/react-badge/index.d.ts +5 -0
- package/dist/internal/react-callout/index.d.cts +4 -0
- package/dist/internal/react-callout/index.d.ts +4 -0
- package/dist/internal/react-card/index.d.cts +8 -1
- package/dist/internal/react-card/index.d.ts +8 -1
- package/dist/internal/react-collapsible/index.d.cts +0 -3
- package/dist/internal/react-collapsible/index.d.ts +0 -3
- package/dist/internal/react-combobox/index.d.cts +0 -3
- package/dist/internal/react-combobox/index.d.ts +0 -3
- package/dist/internal/react-command/index.d.cts +0 -3
- package/dist/internal/react-command/index.d.ts +0 -3
- package/dist/internal/react-data-table/index.d.cts +0 -3
- package/dist/internal/react-data-table/index.d.ts +0 -3
- package/dist/internal/react-dialog/index.d.cts +0 -3
- package/dist/internal/react-dialog/index.d.ts +0 -3
- package/dist/internal/react-dropdown-menu/index.d.cts +0 -3
- package/dist/internal/react-dropdown-menu/index.d.ts +0 -3
- package/dist/internal/react-feedback-dialog/index.d.cts +0 -3
- package/dist/internal/react-feedback-dialog/index.d.ts +0 -3
- package/dist/internal/react-form/index.d.cts +2 -21
- package/dist/internal/react-form/index.d.ts +2 -21
- package/dist/internal/react-keyboard-shortcut/index.d.cts +0 -6
- package/dist/internal/react-keyboard-shortcut/index.d.ts +0 -6
- package/dist/internal/react-link-card/index.d.cts +8 -2
- package/dist/internal/react-link-card/index.d.ts +8 -2
- package/dist/internal/react-popover/index.d.cts +0 -3
- package/dist/internal/react-popover/index.d.ts +0 -3
- package/dist/internal/react-sheet/index.d.cts +0 -3
- package/dist/internal/react-sheet/index.d.ts +0 -3
- package/dist/internal/react-slot/index.d.cts +23 -0
- package/dist/internal/react-slot/index.d.ts +23 -0
- package/dist/internal/react-toast/index.d.cts +0 -3
- package/dist/internal/react-toast/index.d.ts +0 -3
- package/dist/internal/react-tooltip/index.d.cts +0 -3
- package/dist/internal/react-tooltip/index.d.ts +0 -3
- package/dist/react-accordion.js +125 -0
- package/dist/react-accordion.js.map +7 -0
- package/dist/react-ai.js +257 -0
- package/dist/react-ai.js.map +7 -0
- package/dist/react-analytics.js +1698 -0
- package/dist/react-analytics.js.map +7 -0
- package/dist/react-animated-text.js +164 -0
- package/dist/react-animated-text.js.map +7 -0
- package/dist/react-app-shell.js +631 -0
- package/dist/react-app-shell.js.map +7 -0
- package/dist/react-audience-feature-card.js +53 -0
- package/dist/react-audience-feature-card.js.map +7 -0
- package/dist/react-audio-room.js +166 -0
- package/dist/react-audio-room.js.map +7 -0
- package/dist/react-auth.js +211 -0
- package/dist/react-auth.js.map +7 -0
- package/dist/react-avatar-group.js +154 -0
- package/dist/react-avatar-group.js.map +7 -0
- package/dist/react-avatar.js +168 -0
- package/dist/react-avatar.js.map +7 -0
- package/dist/react-badge.js +86 -0
- package/dist/react-badge.js.map +7 -0
- package/dist/react-bottom-nav.js +67 -0
- package/dist/react-bottom-nav.js.map +7 -0
- package/dist/react-brand-network-cell.js +92 -0
- package/dist/react-brand-network-cell.js.map +7 -0
- package/dist/react-breadcrumbs.js +104 -0
- package/dist/react-breadcrumbs.js.map +7 -0
- package/dist/react-browser-chrome-mock.js +89 -0
- package/dist/react-browser-chrome-mock.js.map +7 -0
- package/dist/react-button.js +13 -0
- package/dist/react-button.js.map +7 -0
- package/dist/react-calendar.js +301 -0
- package/dist/react-calendar.js.map +7 -0
- package/dist/react-call-controls.js +113 -0
- package/dist/react-call-controls.js.map +7 -0
- package/dist/react-callout.js +152 -0
- package/dist/react-callout.js.map +7 -0
- package/dist/react-card-grid.js +34 -0
- package/dist/react-card-grid.js.map +7 -0
- package/dist/react-card.js +200 -0
- package/dist/react-card.js.map +7 -0
- package/dist/react-carousel.js +132 -0
- package/dist/react-carousel.js.map +7 -0
- package/dist/react-charts.js +509 -0
- package/dist/react-charts.js.map +7 -0
- package/dist/react-checkbox.js +156 -0
- package/dist/react-checkbox.js.map +7 -0
- package/dist/react-checklist.js +168 -0
- package/dist/react-checklist.js.map +7 -0
- package/dist/react-code-block.js +78 -0
- package/dist/react-code-block.js.map +7 -0
- package/dist/react-code-editor.js +184 -0
- package/dist/react-code-editor.js.map +7 -0
- package/dist/react-collapsible.js +192 -0
- package/dist/react-collapsible.js.map +7 -0
- package/dist/react-combobox.js +609 -0
- package/dist/react-combobox.js.map +7 -0
- package/dist/react-command-input.js +202 -0
- package/dist/react-command-input.js.map +7 -0
- package/dist/react-command.js +364 -0
- package/dist/react-command.js.map +7 -0
- package/dist/react-composer.js +2036 -0
- package/dist/react-composer.js.map +7 -0
- package/dist/react-content-protection.js +133 -0
- package/dist/react-content-protection.js.map +7 -0
- package/dist/react-conversation.js +1226 -0
- package/dist/react-conversation.js.map +7 -0
- package/dist/react-cookie-consent.js +291 -0
- package/dist/react-cookie-consent.js.map +7 -0
- package/dist/react-data-table.js +310 -0
- package/dist/react-data-table.js.map +7 -0
- package/dist/react-date-picker.js +95 -0
- package/dist/react-date-picker.js.map +7 -0
- package/dist/react-device-frame.js +137 -0
- package/dist/react-device-frame.js.map +7 -0
- package/dist/react-dialog.js +218 -0
- package/dist/react-dialog.js.map +7 -0
- package/dist/react-diff-viewer.js +460 -0
- package/dist/react-diff-viewer.js.map +7 -0
- package/dist/react-dropdown-menu.js +292 -0
- package/dist/react-dropdown-menu.js.map +7 -0
- package/dist/react-editor-status-bar.js +148 -0
- package/dist/react-editor-status-bar.js.map +7 -0
- package/dist/react-editor-tabs.js +159 -0
- package/dist/react-editor-tabs.js.map +7 -0
- package/dist/react-emoji-picker.js +238 -0
- package/dist/react-emoji-picker.js.map +7 -0
- package/dist/react-empty-state.js +90 -0
- package/dist/react-empty-state.js.map +7 -0
- package/dist/react-feedback-dialog.js +275 -0
- package/dist/react-feedback-dialog.js.map +7 -0
- package/dist/react-file-tree.js +376 -0
- package/dist/react-file-tree.js.map +7 -0
- package/dist/react-file-upload.js +360 -0
- package/dist/react-file-upload.js.map +7 -0
- package/dist/react-floating-reactions.js +124 -0
- package/dist/react-floating-reactions.js.map +7 -0
- package/dist/react-flow-editor.js +198 -0
- package/dist/react-flow-editor.js.map +7 -0
- package/dist/react-footer.js +67 -0
- package/dist/react-footer.js.map +7 -0
- package/dist/react-graph-view.js +197 -0
- package/dist/react-graph-view.js.map +7 -0
- package/dist/react-icon-system.js +9 -0
- package/dist/react-icon-system.js.map +7 -0
- package/dist/react-infinite-canvas.js +8 -0
- package/dist/react-infinite-canvas.js.map +7 -0
- package/dist/react-inline-editor.js +216 -0
- package/dist/react-inline-editor.js.map +7 -0
- package/dist/react-input-group.js +150 -0
- package/dist/react-input-group.js.map +7 -0
- package/dist/react-input.js +10 -0
- package/dist/react-input.js.map +7 -0
- package/dist/react-install-prompt.js +132 -0
- package/dist/react-install-prompt.js.map +7 -0
- package/dist/react-kanban-board.js +198 -0
- package/dist/react-kanban-board.js.map +7 -0
- package/dist/react-keyboard-shortcut.js +36 -0
- package/dist/react-keyboard-shortcut.js.map +7 -0
- package/dist/react-language-selector.js +311 -0
- package/dist/react-language-selector.js.map +7 -0
- package/dist/react-link-card.js +48 -0
- package/dist/react-link-card.js.map +7 -0
- package/dist/react-live-captions.js +92 -0
- package/dist/react-live-captions.js.map +7 -0
- package/dist/react-live-cursors.js +145 -0
- package/dist/react-live-cursors.js.map +7 -0
- package/dist/react-live-transcript.js +117 -0
- package/dist/react-live-transcript.js.map +7 -0
- package/dist/react-location-selector.js +252 -0
- package/dist/react-location-selector.js.map +7 -0
- package/dist/react-logger.js +415 -0
- package/dist/react-logger.js.map +7 -0
- package/dist/react-markdown-renderer.js +12 -0
- package/dist/react-markdown-renderer.js.map +7 -0
- package/dist/react-marquee-strip.js +62 -0
- package/dist/react-marquee-strip.js.map +7 -0
- package/dist/react-mascot.js +367 -0
- package/dist/react-mascot.js.map +7 -0
- package/dist/react-mastery-bar.js +97 -0
- package/dist/react-mastery-bar.js.map +7 -0
- package/dist/react-mini-map.js +207 -0
- package/dist/react-mini-map.js.map +7 -0
- package/dist/react-mobile-nav.js +216 -0
- package/dist/react-mobile-nav.js.map +7 -0
- package/dist/react-navbar.js +96 -0
- package/dist/react-navbar.js.map +7 -0
- package/dist/react-numbered-steps.js +67 -0
- package/dist/react-numbered-steps.js.map +7 -0
- package/dist/react-otp-input.js +318 -0
- package/dist/react-otp-input.js.map +7 -0
- package/dist/react-pagination.js +199 -0
- package/dist/react-pagination.js.map +7 -0
- package/dist/react-password-input.js +104 -0
- package/dist/react-password-input.js.map +7 -0
- package/dist/react-payment.js +81 -0
- package/dist/react-payment.js.map +7 -0
- package/dist/react-popover.js +176 -0
- package/dist/react-popover.js.map +7 -0
- package/dist/react-pre-call-lobby.js +309 -0
- package/dist/react-pre-call-lobby.js.map +7 -0
- package/dist/react-presence-indicator.js +86 -0
- package/dist/react-presence-indicator.js.map +7 -0
- package/dist/react-pricing-card.js +142 -0
- package/dist/react-pricing-card.js.map +7 -0
- package/dist/react-progress-display.js +188 -0
- package/dist/react-progress-display.js.map +7 -0
- package/dist/react-radial-gauge.js +193 -0
- package/dist/react-radial-gauge.js.map +7 -0
- package/dist/react-radio.js +169 -0
- package/dist/react-radio.js.map +7 -0
- package/dist/react-rating-scale.js +171 -0
- package/dist/react-rating-scale.js.map +7 -0
- package/dist/react-reaction-bar.js +111 -0
- package/dist/react-reaction-bar.js.map +7 -0
- package/dist/react-resizable-layout.js +298 -0
- package/dist/react-resizable-layout.js.map +7 -0
- package/dist/react-rich-editor.js +1 -0
- package/dist/react-rich-editor.js.map +7 -0
- package/dist/react-search-bar.js +285 -0
- package/dist/react-search-bar.js.map +7 -0
- package/dist/react-section-head.js +65 -0
- package/dist/react-section-head.js.map +7 -0
- package/dist/react-segmented-control.js +215 -0
- package/dist/react-segmented-control.js.map +7 -0
- package/dist/react-select.js +389 -0
- package/dist/react-select.js.map +7 -0
- package/dist/react-separator.js +86 -0
- package/dist/react-separator.js.map +7 -0
- package/dist/react-sheet.js +310 -0
- package/dist/react-sheet.js.map +7 -0
- package/dist/react-sidebar.js +100 -0
- package/dist/react-sidebar.js.map +7 -0
- package/dist/react-skeleton.js +91 -0
- package/dist/react-skeleton.js.map +7 -0
- package/dist/react-skip-to-content.js +38 -0
- package/dist/react-skip-to-content.js.map +7 -0
- package/dist/react-slide-viewer.js +264 -0
- package/dist/react-slide-viewer.js.map +7 -0
- package/dist/react-slider.js +105 -0
- package/dist/react-slider.js.map +7 -0
- package/dist/react-slot-picker.js +184 -0
- package/dist/react-slot-picker.js.map +7 -0
- package/dist/react-slot.js +7 -0
- package/dist/react-slot.js.map +7 -0
- package/dist/react-social-auth-button.js +169 -0
- package/dist/react-social-auth-button.js.map +7 -0
- package/dist/react-sortable-list.js +204 -0
- package/dist/react-sortable-list.js.map +7 -0
- package/dist/react-stat-grid.js +69 -0
- package/dist/react-stat-grid.js.map +7 -0
- package/dist/react-status-indicator.js +117 -0
- package/dist/react-status-indicator.js.map +7 -0
- package/dist/react-steps.js +167 -0
- package/dist/react-steps.js.map +7 -0
- package/dist/react-sticky-note.js +131 -0
- package/dist/react-sticky-note.js.map +7 -0
- package/dist/react-switch.js +139 -0
- package/dist/react-switch.js.map +7 -0
- package/dist/react-table-of-contents.js +80 -0
- package/dist/react-table-of-contents.js.map +7 -0
- package/dist/react-tabs.js +230 -0
- package/dist/react-tabs.js.map +7 -0
- package/dist/react-terminal.js +101 -0
- package/dist/react-terminal.js.map +7 -0
- package/dist/react-test-results.js +158 -0
- package/dist/react-test-results.js.map +7 -0
- package/dist/react-textarea.js +89 -0
- package/dist/react-textarea.js.map +7 -0
- package/dist/react-thread-view.js +139 -0
- package/dist/react-thread-view.js.map +7 -0
- package/dist/react-timeline.js +153 -0
- package/dist/react-timeline.js.map +7 -0
- package/dist/react-toast.js +253 -0
- package/dist/react-toast.js.map +7 -0
- package/dist/react-tooltip.js +221 -0
- package/dist/react-tooltip.js.map +7 -0
- package/dist/react-version-selector.js +270 -0
- package/dist/react-version-selector.js.map +7 -0
- package/dist/react-video-grid.js +129 -0
- package/dist/react-video-grid.js.map +7 -0
- package/dist/react-video-player.js +206 -0
- package/dist/react-video-player.js.map +7 -0
- package/dist/react-video-tile.js +8 -0
- package/dist/react-video-tile.js.map +7 -0
- package/dist/react-voice-pill.js +314 -0
- package/dist/react-voice-pill.js.map +7 -0
- package/dist/react-waveform.js +558 -0
- package/dist/react-waveform.js.map +7 -0
- package/dist/react-wizard.js +337 -0
- package/dist/react-wizard.js.map +7 -0
- package/dist/styles.css +7 -0
- package/dist/theme.js +1 -1
- package/package.json +3 -2
- package/dist/applicationinsights-web-H7OBLIVJ.js +0 -23393
- package/dist/applicationinsights-web-H7OBLIVJ.js.map +0 -1
- package/dist/chunk-4A4BW3GR.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/module-X3HTTRQK.js +0 -6438
- package/dist/module-X3HTTRQK.js.map +0 -1
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Keys,
|
|
3
|
+
cn,
|
|
4
|
+
cva
|
|
5
|
+
} from "./chunk-ZF3OHCGV.js";
|
|
6
|
+
|
|
7
|
+
// ../react-slide-viewer/dist/index.js
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
|
|
10
|
+
// ../slide-viewer/dist/index.js
|
|
11
|
+
function createSlideViewer(props) {
|
|
12
|
+
const { slides, initialSlide = 0, onSlideChange, onComplete } = props;
|
|
13
|
+
const totalSlides = slides.length;
|
|
14
|
+
const bookmarks = /* @__PURE__ */ new Map();
|
|
15
|
+
const state = {
|
|
16
|
+
currentSlide: Math.max(0, Math.min(initialSlide, totalSlides - 1)),
|
|
17
|
+
totalSlides,
|
|
18
|
+
bookmarks
|
|
19
|
+
};
|
|
20
|
+
function updateProgress() {
|
|
21
|
+
if (totalSlides <= 1) return 1;
|
|
22
|
+
return state.currentSlide / (totalSlides - 1);
|
|
23
|
+
}
|
|
24
|
+
function next() {
|
|
25
|
+
if (state.currentSlide < totalSlides - 1) {
|
|
26
|
+
state.currentSlide++;
|
|
27
|
+
api.progress = updateProgress();
|
|
28
|
+
onSlideChange?.(state.currentSlide);
|
|
29
|
+
} else {
|
|
30
|
+
onComplete?.();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function prev() {
|
|
34
|
+
if (state.currentSlide > 0) {
|
|
35
|
+
state.currentSlide--;
|
|
36
|
+
api.progress = updateProgress();
|
|
37
|
+
onSlideChange?.(state.currentSlide);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function goTo(index) {
|
|
41
|
+
if (index >= 0 && index < totalSlides) {
|
|
42
|
+
state.currentSlide = index;
|
|
43
|
+
api.progress = updateProgress();
|
|
44
|
+
onSlideChange?.(state.currentSlide);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function toggleBookmark(type) {
|
|
48
|
+
const current = state.currentSlide;
|
|
49
|
+
if (bookmarks.has(current) && bookmarks.get(current) === type) {
|
|
50
|
+
bookmarks.delete(current);
|
|
51
|
+
} else {
|
|
52
|
+
bookmarks.set(current, type);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function getBookmarks() {
|
|
56
|
+
const result = [];
|
|
57
|
+
for (const [slide, type] of bookmarks.entries()) {
|
|
58
|
+
result.push({ slide, type });
|
|
59
|
+
}
|
|
60
|
+
return result.sort((a, b) => a.slide - b.slide);
|
|
61
|
+
}
|
|
62
|
+
const keyboardHandlers = {
|
|
63
|
+
[Keys.ArrowRight]: (e) => {
|
|
64
|
+
e.preventDefault();
|
|
65
|
+
next();
|
|
66
|
+
},
|
|
67
|
+
[Keys.ArrowLeft]: (e) => {
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
prev();
|
|
70
|
+
},
|
|
71
|
+
[Keys.Escape]: (e) => {
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
function getSlideAriaProps() {
|
|
76
|
+
const slide = slides[state.currentSlide];
|
|
77
|
+
return {
|
|
78
|
+
role: "region",
|
|
79
|
+
"aria-roledescription": "slide",
|
|
80
|
+
"aria-label": `Slide ${state.currentSlide + 1} of ${totalSlides}: ${slide?.type ?? "unknown"} slide`,
|
|
81
|
+
"aria-live": "polite"
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const api = {
|
|
85
|
+
state,
|
|
86
|
+
next,
|
|
87
|
+
prev,
|
|
88
|
+
goTo,
|
|
89
|
+
toggleBookmark,
|
|
90
|
+
getBookmarks,
|
|
91
|
+
progress: updateProgress(),
|
|
92
|
+
keyboardHandlers,
|
|
93
|
+
getSlideAriaProps
|
|
94
|
+
};
|
|
95
|
+
return api;
|
|
96
|
+
}
|
|
97
|
+
var slideViewerTokens = {
|
|
98
|
+
name: "slide-viewer",
|
|
99
|
+
tokens: {
|
|
100
|
+
bg: { variable: "--rfr-slide-bg", fallback: "hsl(var(--background))" },
|
|
101
|
+
fg: { variable: "--rfr-slide-fg", fallback: "hsl(var(--foreground))" },
|
|
102
|
+
progressBg: { variable: "--rfr-slide-progress-bg", fallback: "hsl(var(--primary))" },
|
|
103
|
+
progressTrack: { variable: "--rfr-slide-progress-track", fallback: "hsl(var(--muted))" },
|
|
104
|
+
badgeBg: { variable: "--rfr-slide-badge-bg", fallback: "hsl(var(--secondary))" }
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
var slideViewerVariants = cva({
|
|
108
|
+
base: "flex flex-col h-full w-full relative overflow-hidden",
|
|
109
|
+
variants: {
|
|
110
|
+
size: {
|
|
111
|
+
compact: "max-w-2xl",
|
|
112
|
+
default: "max-w-4xl",
|
|
113
|
+
full: "max-w-none"
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
defaultVariants: {
|
|
117
|
+
size: "default"
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
var slideViewerProgressBarVariants = cva({
|
|
121
|
+
base: "h-1 w-full bg-muted rounded-full overflow-hidden",
|
|
122
|
+
variants: {
|
|
123
|
+
position: {
|
|
124
|
+
top: "rounded-none",
|
|
125
|
+
bottom: "rounded-none",
|
|
126
|
+
inline: "rounded-full"
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
defaultVariants: {
|
|
130
|
+
position: "top"
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
var slideTypeBadgeVariants = cva({
|
|
134
|
+
base: "inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium",
|
|
135
|
+
variants: {
|
|
136
|
+
type: {
|
|
137
|
+
lesson: "bg-blue-100 text-blue-800",
|
|
138
|
+
quiz: "bg-purple-100 text-purple-800",
|
|
139
|
+
exercise: "bg-green-100 text-green-800",
|
|
140
|
+
intro: "bg-gray-100 text-gray-800",
|
|
141
|
+
summary: "bg-orange-100 text-orange-800"
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
defaultVariants: {
|
|
145
|
+
type: "lesson"
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// ../react-slide-viewer/dist/index.js
|
|
150
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
151
|
+
var SlideViewer = React.forwardRef(
|
|
152
|
+
function SlideViewer2({
|
|
153
|
+
slides,
|
|
154
|
+
initialSlide,
|
|
155
|
+
onSlideChange,
|
|
156
|
+
onComplete,
|
|
157
|
+
className,
|
|
158
|
+
size,
|
|
159
|
+
renderSlide
|
|
160
|
+
}, ref) {
|
|
161
|
+
const [, setTick] = React.useState(0);
|
|
162
|
+
const rerender = () => setTick((t) => t + 1);
|
|
163
|
+
const apiRef = React.useRef(
|
|
164
|
+
createSlideViewer({ slides, initialSlide, onSlideChange, onComplete })
|
|
165
|
+
);
|
|
166
|
+
const api = apiRef.current;
|
|
167
|
+
const containerClasses = cn(slideViewerVariants({ size }), className);
|
|
168
|
+
const currentSlide = slides[api.state.currentSlide];
|
|
169
|
+
const slideAriaProps = api.getSlideAriaProps();
|
|
170
|
+
function handleKeyDown(event) {
|
|
171
|
+
const handler = api.keyboardHandlers[event.key];
|
|
172
|
+
if (!handler) return;
|
|
173
|
+
handler(event);
|
|
174
|
+
rerender();
|
|
175
|
+
}
|
|
176
|
+
function handlePrev() {
|
|
177
|
+
api.prev();
|
|
178
|
+
rerender();
|
|
179
|
+
}
|
|
180
|
+
function handleNext() {
|
|
181
|
+
api.next();
|
|
182
|
+
rerender();
|
|
183
|
+
}
|
|
184
|
+
function handleBookmark(type) {
|
|
185
|
+
api.toggleBookmark(type);
|
|
186
|
+
rerender();
|
|
187
|
+
}
|
|
188
|
+
const isBookmarked = api.state.bookmarks.has(api.state.currentSlide);
|
|
189
|
+
return /* @__PURE__ */ jsxs(
|
|
190
|
+
"div",
|
|
191
|
+
{
|
|
192
|
+
ref,
|
|
193
|
+
className: containerClasses,
|
|
194
|
+
onKeyDown: handleKeyDown,
|
|
195
|
+
tabIndex: 0,
|
|
196
|
+
children: [
|
|
197
|
+
/* @__PURE__ */ jsx("div", { className: slideViewerProgressBarVariants({ position: "top" }), children: /* @__PURE__ */ jsx(
|
|
198
|
+
"div",
|
|
199
|
+
{
|
|
200
|
+
className: "h-full bg-primary transition-all duration-300",
|
|
201
|
+
style: { width: `${api.progress * 100}%` }
|
|
202
|
+
}
|
|
203
|
+
) }),
|
|
204
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-2 border-b", children: [
|
|
205
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
206
|
+
currentSlide && /* @__PURE__ */ jsx("span", { className: slideTypeBadgeVariants({ type: currentSlide.type }), children: currentSlide.type }),
|
|
207
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
|
|
208
|
+
api.state.currentSlide + 1,
|
|
209
|
+
" / ",
|
|
210
|
+
api.state.totalSlides
|
|
211
|
+
] })
|
|
212
|
+
] }),
|
|
213
|
+
/* @__PURE__ */ jsx(
|
|
214
|
+
"button",
|
|
215
|
+
{
|
|
216
|
+
type: "button",
|
|
217
|
+
onClick: () => handleBookmark("important"),
|
|
218
|
+
className: cn(
|
|
219
|
+
"px-2 py-1 rounded text-xs transition-colors",
|
|
220
|
+
isBookmarked ? "bg-yellow-100 text-yellow-800" : "hover:bg-muted text-muted-foreground"
|
|
221
|
+
),
|
|
222
|
+
"aria-pressed": isBookmarked,
|
|
223
|
+
"aria-label": "Toggle bookmark",
|
|
224
|
+
children: isBookmarked ? "Bookmarked" : "Bookmark"
|
|
225
|
+
}
|
|
226
|
+
)
|
|
227
|
+
] }),
|
|
228
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto p-6", ...slideAriaProps, children: currentSlide && (renderSlide ? renderSlide(currentSlide, api.state.currentSlide) : /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: currentSlide.content } })) }),
|
|
229
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-t", children: [
|
|
230
|
+
/* @__PURE__ */ jsx(
|
|
231
|
+
"button",
|
|
232
|
+
{
|
|
233
|
+
type: "button",
|
|
234
|
+
onClick: handlePrev,
|
|
235
|
+
disabled: api.state.currentSlide === 0,
|
|
236
|
+
className: "px-4 py-2 rounded text-sm font-medium disabled:opacity-50 disabled:cursor-not-allowed hover:bg-muted transition-colors",
|
|
237
|
+
"aria-label": "Previous slide",
|
|
238
|
+
children: "Previous"
|
|
239
|
+
}
|
|
240
|
+
),
|
|
241
|
+
/* @__PURE__ */ jsx(
|
|
242
|
+
"button",
|
|
243
|
+
{
|
|
244
|
+
type: "button",
|
|
245
|
+
onClick: handleNext,
|
|
246
|
+
className: "px-4 py-2 rounded text-sm font-medium bg-primary text-primary-foreground hover:bg-primary/90 transition-colors",
|
|
247
|
+
"aria-label": api.state.currentSlide === api.state.totalSlides - 1 ? "Complete" : "Next slide",
|
|
248
|
+
children: api.state.currentSlide === api.state.totalSlides - 1 ? "Complete" : "Next"
|
|
249
|
+
}
|
|
250
|
+
)
|
|
251
|
+
] })
|
|
252
|
+
]
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
);
|
|
257
|
+
export {
|
|
258
|
+
SlideViewer,
|
|
259
|
+
slideViewerProgressBarVariants as progressBarVariants,
|
|
260
|
+
slideTypeBadgeVariants,
|
|
261
|
+
slideViewerTokens,
|
|
262
|
+
slideViewerVariants
|
|
263
|
+
};
|
|
264
|
+
//# sourceMappingURL=react-slide-viewer.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../slide-viewer/src/slide-viewer.ts", "../../slide-viewer/src/slide-viewer.styles.ts", "../../react-slide-viewer/src/SlideViewer.tsx"],
|
|
4
|
+
"sourcesContent": ["import type { AccessibilityProps, KeyboardHandlerMap } from '@refraction-ui/shared'\nimport { Keys } from '@refraction-ui/shared'\n\nexport type SlideType = 'lesson' | 'quiz' | 'exercise' | 'intro' | 'summary'\n\nexport type BookmarkType = 'important' | 'difficult' | 'to-revise'\n\nexport interface SlideData {\n id: string\n type: SlideType\n content: string\n}\n\nexport interface SlideViewerProps {\n slides: SlideData[]\n initialSlide?: number\n onSlideChange?: (index: number) => void\n onComplete?: () => void\n}\n\nexport interface SlideViewerState {\n currentSlide: number\n totalSlides: number\n bookmarks: Map<number, BookmarkType>\n}\n\nexport interface SlideViewerAPI {\n /** Current viewer state */\n state: SlideViewerState\n /** Navigate to the next slide */\n next: () => void\n /** Navigate to the previous slide */\n prev: () => void\n /** Navigate to a specific slide index */\n goTo: (index: number) => void\n /** Toggle a bookmark on the current slide */\n toggleBookmark: (type: BookmarkType) => void\n /** Get all bookmarks as an array of entries */\n getBookmarks: () => Array<{ slide: number; type: BookmarkType }>\n /** Current progress as a number between 0 and 1 */\n progress: number\n /** Keyboard handlers for slide navigation */\n keyboardHandlers: KeyboardHandlerMap\n /** ARIA props for the current slide */\n getSlideAriaProps: () => Partial<AccessibilityProps> & { 'aria-roledescription'?: string }\n}\n\n/**\n * Create a headless slide viewer.\n *\n * Manages slide navigation state, bookmarks, progress tracking,\n * and keyboard navigation. Framework wrappers handle rendering.\n */\nexport function createSlideViewer(props: SlideViewerProps): SlideViewerAPI {\n const { slides, initialSlide = 0, onSlideChange, onComplete } = props\n\n const totalSlides = slides.length\n const bookmarks = new Map<number, BookmarkType>()\n\n const state: SlideViewerState = {\n currentSlide: Math.max(0, Math.min(initialSlide, totalSlides - 1)),\n totalSlides,\n bookmarks,\n }\n\n function updateProgress(): number {\n if (totalSlides <= 1) return 1\n return state.currentSlide / (totalSlides - 1)\n }\n\n function next() {\n if (state.currentSlide < totalSlides - 1) {\n state.currentSlide++\n api.progress = updateProgress()\n onSlideChange?.(state.currentSlide)\n } else {\n // Already on the last slide — trigger complete\n onComplete?.()\n }\n }\n\n function prev() {\n if (state.currentSlide > 0) {\n state.currentSlide--\n api.progress = updateProgress()\n onSlideChange?.(state.currentSlide)\n }\n }\n\n function goTo(index: number) {\n if (index >= 0 && index < totalSlides) {\n state.currentSlide = index\n api.progress = updateProgress()\n onSlideChange?.(state.currentSlide)\n }\n }\n\n function toggleBookmark(type: BookmarkType) {\n const current = state.currentSlide\n if (bookmarks.has(current) && bookmarks.get(current) === type) {\n bookmarks.delete(current)\n } else {\n bookmarks.set(current, type)\n }\n }\n\n function getBookmarks(): Array<{ slide: number; type: BookmarkType }> {\n const result: Array<{ slide: number; type: BookmarkType }> = []\n for (const [slide, type] of bookmarks.entries()) {\n result.push({ slide, type })\n }\n return result.sort((a, b) => a.slide - b.slide)\n }\n\n const keyboardHandlers: KeyboardHandlerMap = {\n [Keys.ArrowRight]: (e) => {\n e.preventDefault()\n next()\n },\n [Keys.ArrowLeft]: (e) => {\n e.preventDefault()\n prev()\n },\n [Keys.Escape]: (e) => {\n e.preventDefault()\n },\n }\n\n function getSlideAriaProps(): Partial<AccessibilityProps> & { 'aria-roledescription'?: string } {\n const slide = slides[state.currentSlide]\n return {\n role: 'region',\n 'aria-roledescription': 'slide',\n 'aria-label': `Slide ${state.currentSlide + 1} of ${totalSlides}: ${slide?.type ?? 'unknown'} slide`,\n 'aria-live': 'polite',\n }\n }\n\n const api: SlideViewerAPI = {\n state,\n next,\n prev,\n goTo,\n toggleBookmark,\n getBookmarks,\n progress: updateProgress(),\n keyboardHandlers,\n getSlideAriaProps,\n }\n\n return api\n}\n", "import { cva } from '@refraction-ui/shared'\nimport type { TokenContract } from '@refraction-ui/shared'\n\nexport const slideViewerTokens: TokenContract = {\n name: 'slide-viewer',\n tokens: {\n bg: { variable: '--rfr-slide-bg', fallback: 'hsl(var(--background))' },\n fg: { variable: '--rfr-slide-fg', fallback: 'hsl(var(--foreground))' },\n progressBg: { variable: '--rfr-slide-progress-bg', fallback: 'hsl(var(--primary))' },\n progressTrack: { variable: '--rfr-slide-progress-track', fallback: 'hsl(var(--muted))' },\n badgeBg: { variable: '--rfr-slide-badge-bg', fallback: 'hsl(var(--secondary))' },\n },\n}\n\nexport const slideViewerVariants = cva({\n base: 'flex flex-col h-full w-full relative overflow-hidden',\n variants: {\n size: {\n compact: 'max-w-2xl',\n default: 'max-w-4xl',\n full: 'max-w-none',\n },\n },\n defaultVariants: {\n size: 'default',\n },\n})\n\nconst slideViewerProgressBarVariants = cva({\n base: 'h-1 w-full bg-muted rounded-full overflow-hidden',\n variants: {\n position: {\n top: 'rounded-none',\n bottom: 'rounded-none',\n inline: 'rounded-full',\n },\n },\n defaultVariants: {\n position: 'top',\n },\n})\n\nexport { slideViewerProgressBarVariants as progressBarVariants }\n\nexport const slideTypeBadgeVariants = cva({\n base: 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium',\n variants: {\n type: {\n lesson: 'bg-blue-100 text-blue-800',\n quiz: 'bg-purple-100 text-purple-800',\n exercise: 'bg-green-100 text-green-800',\n intro: 'bg-gray-100 text-gray-800',\n summary: 'bg-orange-100 text-orange-800',\n },\n },\n defaultVariants: {\n type: 'lesson',\n },\n})\n", "import * as React from 'react'\nimport {\n createSlideViewer,\n slideViewerVariants,\n progressBarVariants,\n slideTypeBadgeVariants,\n type SlideData,\n type BookmarkType,\n type SlideType,\n} from '@refraction-ui/slide-viewer'\nimport { cn } from '@refraction-ui/shared'\n\nexport type { SlideData, BookmarkType, SlideType }\n\nexport interface SlideViewerProps {\n slides: SlideData[]\n initialSlide?: number\n onSlideChange?: (index: number) => void\n onComplete?: () => void\n className?: string\n size?: 'compact' | 'default' | 'full'\n /** Render function for custom slide content */\n renderSlide?: (slide: SlideData, index: number) => React.ReactNode\n}\n\n/**\n * SlideViewer component — slide-based content viewer with navigation,\n * progress bar, type badges, and bookmark support.\n *\n * Uses the headless @refraction-ui/slide-viewer core for state and navigation.\n */\nexport const SlideViewer = React.forwardRef<HTMLDivElement, SlideViewerProps>(\n function SlideViewer(\n {\n slides,\n initialSlide,\n onSlideChange,\n onComplete,\n className,\n size,\n renderSlide,\n },\n ref,\n ) {\n const [, setTick] = React.useState(0)\n const rerender = () => setTick((t) => t + 1)\n\n const apiRef = React.useRef(\n createSlideViewer({ slides, initialSlide, onSlideChange, onComplete }),\n )\n const api = apiRef.current\n\n const containerClasses = cn(slideViewerVariants({ size }), className)\n const currentSlide = slides[api.state.currentSlide]\n const slideAriaProps = api.getSlideAriaProps()\n // The core's keyboard handlers mutate state in place; re-render after a\n // handled key so the counter, progress bar, and buttons stay in sync.\n function handleKeyDown(event: KeyboardEvent) {\n const handler =\n api.keyboardHandlers[event.key as keyof typeof api.keyboardHandlers]\n if (!handler) return\n handler(event)\n rerender()\n }\n\n function handlePrev() {\n api.prev()\n rerender()\n }\n\n function handleNext() {\n api.next()\n rerender()\n }\n\n function handleBookmark(type: BookmarkType) {\n api.toggleBookmark(type)\n rerender()\n }\n\n const isBookmarked = api.state.bookmarks.has(api.state.currentSlide)\n\n return (\n <div\n ref={ref}\n className={containerClasses}\n onKeyDown={handleKeyDown as unknown as React.KeyboardEventHandler}\n tabIndex={0}\n >\n {/* Progress bar */}\n <div className={progressBarVariants({ position: 'top' })}>\n <div\n className=\"h-full bg-primary transition-all duration-300\"\n style={{ width: `${api.progress * 100}%` }}\n />\n </div>\n\n {/* Header with type badge and bookmark */}\n <div className=\"flex items-center justify-between px-4 py-2 border-b\">\n <div className=\"flex items-center gap-2\">\n {currentSlide && (\n <span className={slideTypeBadgeVariants({ type: currentSlide.type })}>\n {currentSlide.type}\n </span>\n )}\n <span className=\"text-sm text-muted-foreground\">\n {api.state.currentSlide + 1} / {api.state.totalSlides}\n </span>\n </div>\n <button\n type=\"button\"\n onClick={() => handleBookmark('important')}\n className={cn(\n 'px-2 py-1 rounded text-xs transition-colors',\n isBookmarked\n ? 'bg-yellow-100 text-yellow-800'\n : 'hover:bg-muted text-muted-foreground',\n )}\n aria-pressed={isBookmarked}\n aria-label=\"Toggle bookmark\"\n >\n {isBookmarked ? 'Bookmarked' : 'Bookmark'}\n </button>\n </div>\n\n {/* Slide content */}\n <div className=\"flex-1 overflow-auto p-6\" {...slideAriaProps}>\n {currentSlide &&\n (renderSlide\n ? renderSlide(currentSlide, api.state.currentSlide)\n : <div dangerouslySetInnerHTML={{ __html: currentSlide.content }} />)}\n </div>\n\n {/* Navigation buttons */}\n <div className=\"flex items-center justify-between px-4 py-3 border-t\">\n <button\n type=\"button\"\n onClick={handlePrev}\n disabled={api.state.currentSlide === 0}\n className=\"px-4 py-2 rounded text-sm font-medium disabled:opacity-50 disabled:cursor-not-allowed hover:bg-muted transition-colors\"\n aria-label=\"Previous slide\"\n >\n Previous\n </button>\n <button\n type=\"button\"\n onClick={handleNext}\n className=\"px-4 py-2 rounded text-sm font-medium bg-primary text-primary-foreground hover:bg-primary/90 transition-colors\"\n aria-label={\n api.state.currentSlide === api.state.totalSlides - 1\n ? 'Complete'\n : 'Next slide'\n }\n >\n {api.state.currentSlide === api.state.totalSlides - 1 ? 'Complete' : 'Next'}\n </button>\n </div>\n </div>\n )\n },\n)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["SlideViewer"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cn
|
|
3
|
+
} from "./chunk-ZF3OHCGV.js";
|
|
4
|
+
|
|
5
|
+
// ../react-slider/dist/index.js
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
|
|
8
|
+
// ../slider/dist/index.js
|
|
9
|
+
function clampSliderValue(value, min = 0, max = 100) {
|
|
10
|
+
if (Number.isNaN(value)) return min;
|
|
11
|
+
return Math.min(Math.max(value, min), max);
|
|
12
|
+
}
|
|
13
|
+
function roundToStep(value, min = 0, step = 1) {
|
|
14
|
+
if (!step || step <= 0 || Number.isNaN(value)) return value;
|
|
15
|
+
const snapped = min + Math.round((value - min) / step) * step;
|
|
16
|
+
const decimals = (String(step).split(".")[1] ?? "").length;
|
|
17
|
+
return Number(snapped.toFixed(decimals));
|
|
18
|
+
}
|
|
19
|
+
function createSlider(props = {}) {
|
|
20
|
+
const min = props.min ?? 0;
|
|
21
|
+
const max = props.max ?? 100;
|
|
22
|
+
const step = props.step ?? 1;
|
|
23
|
+
const value = clampSliderValue(roundToStep(props.value ?? min, min, step), min, max);
|
|
24
|
+
const percentage = max === min ? 0 : (value - min) / (max - min) * 100;
|
|
25
|
+
const ariaProps = {
|
|
26
|
+
role: "slider",
|
|
27
|
+
"aria-valuemin": min,
|
|
28
|
+
"aria-valuemax": max,
|
|
29
|
+
"aria-valuenow": value,
|
|
30
|
+
"aria-orientation": "horizontal"
|
|
31
|
+
};
|
|
32
|
+
if (props.disabled) ariaProps["aria-disabled"] = true;
|
|
33
|
+
if (props["aria-label"]) ariaProps["aria-label"] = props["aria-label"];
|
|
34
|
+
return {
|
|
35
|
+
value,
|
|
36
|
+
min,
|
|
37
|
+
max,
|
|
38
|
+
step,
|
|
39
|
+
percentage,
|
|
40
|
+
ariaProps,
|
|
41
|
+
dataAttributes: {
|
|
42
|
+
"data-value": String(value),
|
|
43
|
+
"data-min": String(min),
|
|
44
|
+
"data-max": String(max)
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ../react-slider/dist/index.js
|
|
50
|
+
import { jsx } from "react/jsx-runtime";
|
|
51
|
+
var Slider = React.forwardRef(
|
|
52
|
+
function Slider2({
|
|
53
|
+
className,
|
|
54
|
+
variant: _variant,
|
|
55
|
+
value: controlledValue,
|
|
56
|
+
defaultValue,
|
|
57
|
+
min = 0,
|
|
58
|
+
max = 100,
|
|
59
|
+
step = 1,
|
|
60
|
+
disabled,
|
|
61
|
+
onChange,
|
|
62
|
+
onValueChange,
|
|
63
|
+
...props
|
|
64
|
+
}, ref) {
|
|
65
|
+
const isControlled = controlledValue !== void 0;
|
|
66
|
+
const [internalValue, setInternalValue] = React.useState(
|
|
67
|
+
() => clampSliderValue(roundToStep(defaultValue ?? min, min, step), min, max)
|
|
68
|
+
);
|
|
69
|
+
const api = createSlider({
|
|
70
|
+
value: isControlled ? controlledValue : internalValue,
|
|
71
|
+
min,
|
|
72
|
+
max,
|
|
73
|
+
step,
|
|
74
|
+
disabled,
|
|
75
|
+
"aria-label": props["aria-label"]
|
|
76
|
+
});
|
|
77
|
+
const handleChange = (event) => {
|
|
78
|
+
const next = clampSliderValue(roundToStep(Number(event.target.value), min, step), min, max);
|
|
79
|
+
if (!isControlled) setInternalValue(next);
|
|
80
|
+
onChange?.(next);
|
|
81
|
+
onValueChange?.(next);
|
|
82
|
+
};
|
|
83
|
+
return /* @__PURE__ */ jsx(
|
|
84
|
+
"input",
|
|
85
|
+
{
|
|
86
|
+
type: "range",
|
|
87
|
+
ref,
|
|
88
|
+
min,
|
|
89
|
+
max,
|
|
90
|
+
step,
|
|
91
|
+
value: api.value,
|
|
92
|
+
disabled,
|
|
93
|
+
className: cn("w-full", className),
|
|
94
|
+
...api.ariaProps,
|
|
95
|
+
...api.dataAttributes,
|
|
96
|
+
onChange: handleChange,
|
|
97
|
+
...props
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
export {
|
|
103
|
+
Slider
|
|
104
|
+
};
|
|
105
|
+
//# sourceMappingURL=react-slider.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../slider/src/slider.ts", "../../react-slider/src/slider.tsx"],
|
|
4
|
+
"sourcesContent": ["/**\n * @refraction-ui/slider — headless slider.\n *\n * Owns the *behavior* of a single-value range slider: min/max clamping,\n * step rounding, and keyboard increment/decrement semantics. It has NO UI\n * opinion — adapters render the control (a native `<input type=\"range\">` in\n * React, which already applies these same keyboard rules natively) and spread\n * the ARIA object.\n */\n\n/** Options for {@link createSlider}. */\nexport interface SliderProps {\n /** Current value. Clamped into `[min, max]` and rounded to `step`. */\n value?: number\n /** Minimum selectable value (default 0). */\n min?: number\n /** Maximum selectable value (default 100). */\n max?: number\n /** Granularity between selectable values (default 1). */\n step?: number\n /** Disables the control. */\n disabled?: boolean\n /** Accessible label attached to the slider, when provided. */\n 'aria-label'?: string\n}\n\n/** The computed view of a slider control. */\nexport interface SliderAPI {\n /** Normalized value (clamped + step-rounded). */\n value: number\n min: number\n max: number\n step: number\n /** Value position within the range, 0–100. */\n percentage: number\n /** ARIA attributes for the slider element (`role=\"slider\"` + value attrs). */\n ariaProps: Record<string, string | number | boolean>\n /** Data attributes for styling hooks. */\n dataAttributes: Record<string, string>\n}\n\n/** Clamp a value into `[min, max]`. */\nexport function clampSliderValue(value: number, min = 0, max = 100): number {\n if (Number.isNaN(value)) return min\n return Math.min(Math.max(value, min), max)\n}\n\n/**\n * Round a value to the nearest multiple of `step`, offset from `min`.\n * Non-positive steps leave the value unchanged. The result is re-rounded to\n * the step's decimal precision to avoid float noise (e.g. 0.30000000000000004).\n */\nexport function roundToStep(value: number, min = 0, step = 1): number {\n if (!step || step <= 0 || Number.isNaN(value)) return value\n const snapped = min + Math.round((value - min) / step) * step\n const decimals = (String(step).split('.')[1] ?? '').length\n return Number(snapped.toFixed(decimals))\n}\n\n/**\n * Pure keyboard rule for a slider (WAI-ARIA slider pattern), shared by every\n * adapter that does NOT render a native range input (native inputs already\n * implement these keys):\n *\n * - ArrowRight / ArrowUp → +step\n * - ArrowLeft / ArrowDown → -step\n * - PageUp / PageDown → ±10×step\n * - Home / End → min / max\n *\n * The result is clamped; unhandled keys return `current` unchanged.\n */\nexport function getSliderValueFromKey(\n current: number,\n key: string,\n options: { min?: number; max?: number; step?: number } = {},\n): number {\n const { min = 0, max = 100, step = 1 } = options\n switch (key) {\n case 'ArrowRight':\n case 'ArrowUp':\n return clampSliderValue(current + step, min, max)\n case 'ArrowLeft':\n case 'ArrowDown':\n return clampSliderValue(current - step, min, max)\n case 'PageUp':\n return clampSliderValue(current + step * 10, min, max)\n case 'PageDown':\n return clampSliderValue(current - step * 10, min, max)\n case 'Home':\n return min\n case 'End':\n return max\n default:\n return current\n }\n}\n\n/**\n * createSlider — the framework-agnostic view of a slider: normalized value,\n * percentage position, and the ARIA object (`role=\"slider\"`,\n * `aria-valuemin/max/now`). Adapters own the value state\n * (controlled/uncontrolled) and call this per render.\n */\nexport function createSlider(props: SliderProps = {}): SliderAPI {\n const min = props.min ?? 0\n const max = props.max ?? 100\n const step = props.step ?? 1\n const value = clampSliderValue(roundToStep(props.value ?? min, min, step), min, max)\n const percentage = max === min ? 0 : ((value - min) / (max - min)) * 100\n\n const ariaProps: Record<string, string | number | boolean> = {\n role: 'slider',\n 'aria-valuemin': min,\n 'aria-valuemax': max,\n 'aria-valuenow': value,\n 'aria-orientation': 'horizontal',\n }\n if (props.disabled) ariaProps['aria-disabled'] = true\n if (props['aria-label']) ariaProps['aria-label'] = props['aria-label']\n\n return {\n value,\n min,\n max,\n step,\n percentage,\n ariaProps,\n dataAttributes: {\n 'data-value': String(value),\n 'data-min': String(min),\n 'data-max': String(max),\n },\n }\n}\n", "import * as React from 'react'\nimport { cn } from '@refraction-ui/shared'\nimport { createSlider, clampSliderValue, roundToStep } from '@refraction-ui/slider'\n\nexport interface SliderProps\n extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n 'value' | 'defaultValue' | 'onChange' | 'min' | 'max' | 'step'\n > {\n /** Current value (controlled). Pair with `onChange`/`onValueChange`. */\n value?: number\n /** Initial value for uncontrolled usage (defaults to `min`). */\n defaultValue?: number\n /** Minimum selectable value (default 0). */\n min?: number\n /** Maximum selectable value (default 100). */\n max?: number\n /** Granularity between selectable values (default 1). */\n step?: number\n /** Called with the new numeric value as the user moves the thumb. */\n onChange?: (value: number) => void\n /** Alias of `onChange`, matching the library-wide naming convention. */\n onValueChange?: (value: number) => void\n variant?: 'default'\n}\n\n/**\n * Slider — a single-value range control over a native\n * `<input type=\"range\">`. Value clamping, step rounding, and ARIA come from\n * the headless `@refraction-ui/slider` core; keyboard interaction is native\n * to the input (the core's `getSliderValueFromKey` pins the same rules for\n * non-native adapters).\n */\nexport const Slider = React.forwardRef<HTMLInputElement, SliderProps>(\n function Slider(\n {\n className,\n variant: _variant,\n value: controlledValue,\n defaultValue,\n min = 0,\n max = 100,\n step = 1,\n disabled,\n onChange,\n onValueChange,\n ...props\n },\n ref,\n ) {\n const isControlled = controlledValue !== undefined\n const [internalValue, setInternalValue] = React.useState(() =>\n clampSliderValue(roundToStep(defaultValue ?? min, min, step), min, max),\n )\n\n const api = createSlider({\n value: isControlled ? controlledValue : internalValue,\n min,\n max,\n step,\n disabled,\n 'aria-label': props['aria-label'],\n })\n\n const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const next = clampSliderValue(roundToStep(Number(event.target.value), min, step), min, max)\n if (!isControlled) setInternalValue(next)\n onChange?.(next)\n onValueChange?.(next)\n }\n\n return (\n <input\n type=\"range\"\n ref={ref}\n min={min}\n max={max}\n step={step}\n value={api.value}\n disabled={disabled}\n className={cn('w-full', className)}\n {...api.ariaProps}\n {...api.dataAttributes}\n onChange={handleChange}\n {...props}\n />\n )\n },\n)\n"],
|
|
5
|
+
"mappings": ";;;;;;;;AA0CO,SAAS,iBAAiB,OAAe,MAAM,GAAG,MAAM,KAAa;AAC1E,MAAI,OAAO,MAAM,KAAK,EAAG,QAAO;AAChC,SAAO,KAAK,IAAI,KAAK,IAAI,OAAO,GAAG,GAAG,GAAG;AAC3C;AAOO,SAAS,YAAY,OAAe,MAAM,GAAG,OAAO,GAAW;AACpE,MAAI,CAAC,QAAQ,QAAQ,KAAK,OAAO,MAAM,KAAK,EAAG,QAAO;AACtD,QAAM,UAAU,MAAM,KAAK,OAAO,QAAQ,OAAO,IAAI,IAAI;AACzD,QAAM,YAAY,OAAO,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI;AACpD,SAAO,OAAO,QAAQ,QAAQ,QAAQ,CAAC;AACzC;AA8CO,SAAS,aAAa,QAAqB,CAAA,GAAe;AAC/D,QAAM,MAAM,MAAM,OAAO;AACzB,QAAM,MAAM,MAAM,OAAO;AACzB,QAAM,OAAO,MAAM,QAAQ;AAC3B,QAAM,QAAQ,iBAAiB,YAAY,MAAM,SAAS,KAAK,KAAK,IAAI,GAAG,KAAK,GAAG;AACnF,QAAM,aAAa,QAAQ,MAAM,KAAM,QAAQ,QAAQ,MAAM,OAAQ;AAErE,QAAM,YAAuD;IAC3D,MAAM;IACN,iBAAiB;IACjB,iBAAiB;IACjB,iBAAiB;IACjB,oBAAoB;EAAA;AAEtB,MAAI,MAAM,SAAU,WAAU,eAAe,IAAI;AACjD,MAAI,MAAM,YAAY,EAAA,WAAa,YAAY,IAAI,MAAM,YAAY;AAErE,SAAO;IACL;IACA;IACA;IACA;IACA;IACA;IACA,gBAAgB;MACd,cAAc,OAAO,KAAK;MAC1B,YAAY,OAAO,GAAG;MACtB,YAAY,OAAO,GAAG;IAAA;EACxB;AAEJ;;;;ACpGO,IAAM,SAAe;EAC1B,SAASA,QACP;IACE;IACA,SAAS;IACT,OAAO;IACP;IACA,MAAM;IACN,MAAM;IACN,OAAO;IACP;IACA;IACA;IACA,GAAG;EAAA,GAEL,KACA;AACA,UAAM,eAAe,oBAAoB;AACzC,UAAM,CAAC,eAAe,gBAAgB,IAAU;MAAS,MACvD,iBAAiB,YAAY,gBAAgB,KAAK,KAAK,IAAI,GAAG,KAAK,GAAG;IAAA;AAGxE,UAAM,MAAM,aAAa;MACvB,OAAO,eAAe,kBAAkB;MACxC;MACA;MACA;MACA;MACA,cAAc,MAAM,YAAY;IAAA,CACjC;AAED,UAAM,eAAe,CAAC,UAA+C;AACnE,YAAM,OAAO,iBAAiB,YAAY,OAAO,MAAM,OAAO,KAAK,GAAG,KAAK,IAAI,GAAG,KAAK,GAAG;AAC1F,UAAI,CAAC,aAAc,kBAAiB,IAAI;AACxC,iBAAW,IAAI;AACf,sBAAgB,IAAI;IACtB;AAEA,WACE;MAAC;MAAA;QACC,MAAK;QACL;QACA;QACA;QACA;QACA,OAAO,IAAI;QACX;QACA,WAAW,GAAG,UAAU,SAAS;QAChC,GAAG,IAAI;QACP,GAAG,IAAI;QACR,UAAU;QACT,GAAG;MAAA;IAAA;EAGV;AACF;",
|
|
6
|
+
"names": ["Slider"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cn,
|
|
3
|
+
cva
|
|
4
|
+
} from "./chunk-ZF3OHCGV.js";
|
|
5
|
+
|
|
6
|
+
// ../react-slot-picker/dist/index.js
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
|
|
9
|
+
// ../slot-picker/dist/index.js
|
|
10
|
+
function slotsForDay(slotsByDay, dayId) {
|
|
11
|
+
return slotsByDay[dayId] ?? [];
|
|
12
|
+
}
|
|
13
|
+
function isSlotSelected(value, dayId, slot) {
|
|
14
|
+
return value?.dayId === dayId && value?.slot === slot;
|
|
15
|
+
}
|
|
16
|
+
function createSlotPicker() {
|
|
17
|
+
return {
|
|
18
|
+
ariaProps: {
|
|
19
|
+
role: "group",
|
|
20
|
+
"aria-label": "Pick a time"
|
|
21
|
+
},
|
|
22
|
+
dataAttributes: {
|
|
23
|
+
"data-component": "slot-picker"
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
var slotPickerVariants = cva({
|
|
28
|
+
base: "flex flex-col gap-4",
|
|
29
|
+
variants: {},
|
|
30
|
+
defaultVariants: {}
|
|
31
|
+
});
|
|
32
|
+
var slotPickerDayRowClass = "flex items-center gap-2 overflow-x-auto pb-1";
|
|
33
|
+
var slotPickerDayVariants = cva({
|
|
34
|
+
base: [
|
|
35
|
+
"inline-flex flex-col items-center justify-center rounded-lg px-3 py-2 min-w-[3.5rem]",
|
|
36
|
+
"text-sm font-medium cursor-pointer transition-colors",
|
|
37
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
38
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
39
|
+
].join(" "),
|
|
40
|
+
variants: {
|
|
41
|
+
selected: {
|
|
42
|
+
true: "bg-primary text-primary-foreground",
|
|
43
|
+
false: "bg-card border border-input text-foreground hover:bg-muted"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
defaultVariants: {
|
|
47
|
+
selected: "false"
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
var slotPickerGridClass = "grid grid-cols-3 gap-2 sm:grid-cols-4";
|
|
51
|
+
var slotPickerSlotVariants = cva({
|
|
52
|
+
base: [
|
|
53
|
+
"inline-flex items-center justify-center rounded-md border px-3 py-2 text-sm font-medium",
|
|
54
|
+
"transition-colors cursor-pointer",
|
|
55
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
56
|
+
].join(" "),
|
|
57
|
+
variants: {
|
|
58
|
+
selected: {
|
|
59
|
+
true: "bg-primary text-primary-foreground border-primary",
|
|
60
|
+
false: "bg-card border-input text-foreground hover:bg-muted"
|
|
61
|
+
},
|
|
62
|
+
disabled: {
|
|
63
|
+
true: "opacity-40 pointer-events-none",
|
|
64
|
+
false: ""
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
defaultVariants: {
|
|
68
|
+
selected: "false",
|
|
69
|
+
disabled: "false"
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
var slotPickerSectionLabelClass = "text-sm font-semibold text-foreground";
|
|
73
|
+
var slotPickerTimezoneClass = "text-xs text-muted-foreground ml-2";
|
|
74
|
+
|
|
75
|
+
// ../react-slot-picker/dist/index.js
|
|
76
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
77
|
+
var SlotPicker = React.forwardRef(
|
|
78
|
+
function SlotPicker2({
|
|
79
|
+
days,
|
|
80
|
+
slotsByDay,
|
|
81
|
+
value: valueProp,
|
|
82
|
+
defaultValue,
|
|
83
|
+
onChange,
|
|
84
|
+
timezoneLabel,
|
|
85
|
+
disabledSlots = [],
|
|
86
|
+
className,
|
|
87
|
+
...props
|
|
88
|
+
}, ref) {
|
|
89
|
+
const isControlled = valueProp !== void 0;
|
|
90
|
+
const [internal, setInternal] = React.useState(
|
|
91
|
+
defaultValue
|
|
92
|
+
);
|
|
93
|
+
const value = isControlled ? valueProp : internal;
|
|
94
|
+
const selectedDayId = value?.dayId;
|
|
95
|
+
const handleDaySelect = React.useCallback(
|
|
96
|
+
(dayId) => {
|
|
97
|
+
const slots = slotsForDay(slotsByDay, dayId);
|
|
98
|
+
const keepSlot = value?.slot && slots.includes(value.slot) ? value.slot : void 0;
|
|
99
|
+
const next = keepSlot ? { dayId, slot: keepSlot } : void 0;
|
|
100
|
+
if (!isControlled) setInternal(next);
|
|
101
|
+
if (next) onChange?.(next);
|
|
102
|
+
},
|
|
103
|
+
[isControlled, onChange, slotsByDay, value]
|
|
104
|
+
);
|
|
105
|
+
const handleSlotSelect = React.useCallback(
|
|
106
|
+
(slot) => {
|
|
107
|
+
if (!selectedDayId) return;
|
|
108
|
+
const next = { dayId: selectedDayId, slot };
|
|
109
|
+
if (!isControlled) setInternal(next);
|
|
110
|
+
onChange?.(next);
|
|
111
|
+
},
|
|
112
|
+
[isControlled, onChange, selectedDayId]
|
|
113
|
+
);
|
|
114
|
+
const api = createSlotPicker();
|
|
115
|
+
const currentSlots = selectedDayId ? slotsForDay(slotsByDay, selectedDayId) : [];
|
|
116
|
+
return /* @__PURE__ */ jsxs(
|
|
117
|
+
"div",
|
|
118
|
+
{
|
|
119
|
+
ref,
|
|
120
|
+
className: cn(slotPickerVariants(), className),
|
|
121
|
+
...api.ariaProps,
|
|
122
|
+
...api.dataAttributes,
|
|
123
|
+
...props,
|
|
124
|
+
children: [
|
|
125
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
126
|
+
/* @__PURE__ */ jsx("p", { className: slotPickerSectionLabelClass, children: "Pick a day" }),
|
|
127
|
+
/* @__PURE__ */ jsx("div", { role: "radiogroup", "aria-label": "Pick a day", className: cn(slotPickerDayRowClass, "mt-2"), children: days.map((day) => {
|
|
128
|
+
const isSelected = selectedDayId === day.id;
|
|
129
|
+
return /* @__PURE__ */ jsxs(
|
|
130
|
+
"button",
|
|
131
|
+
{
|
|
132
|
+
type: "button",
|
|
133
|
+
role: "radio",
|
|
134
|
+
"aria-checked": isSelected,
|
|
135
|
+
"data-state": isSelected ? "checked" : "unchecked",
|
|
136
|
+
className: slotPickerDayVariants({
|
|
137
|
+
selected: isSelected ? "true" : "false"
|
|
138
|
+
}),
|
|
139
|
+
onClick: () => handleDaySelect(day.id),
|
|
140
|
+
children: [
|
|
141
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs", children: day.weekday }),
|
|
142
|
+
/* @__PURE__ */ jsx("span", { className: "text-base font-bold leading-none", children: day.dayNum })
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
day.id
|
|
146
|
+
);
|
|
147
|
+
}) })
|
|
148
|
+
] }),
|
|
149
|
+
selectedDayId && /* @__PURE__ */ jsxs("div", { children: [
|
|
150
|
+
/* @__PURE__ */ jsxs("p", { className: slotPickerSectionLabelClass, children: [
|
|
151
|
+
"Pick a time",
|
|
152
|
+
timezoneLabel && /* @__PURE__ */ jsx("span", { className: slotPickerTimezoneClass, children: timezoneLabel })
|
|
153
|
+
] }),
|
|
154
|
+
/* @__PURE__ */ jsx("div", { role: "radiogroup", "aria-label": "Pick a time", className: cn(slotPickerGridClass, "mt-2"), children: currentSlots.map((slot) => {
|
|
155
|
+
const isSelected = isSlotSelected(value, selectedDayId, slot);
|
|
156
|
+
const isDisabled = disabledSlots.includes(slot);
|
|
157
|
+
return /* @__PURE__ */ jsx(
|
|
158
|
+
"button",
|
|
159
|
+
{
|
|
160
|
+
type: "button",
|
|
161
|
+
role: "radio",
|
|
162
|
+
"aria-checked": isSelected,
|
|
163
|
+
"aria-disabled": isDisabled || void 0,
|
|
164
|
+
"data-state": isSelected ? "checked" : "unchecked",
|
|
165
|
+
className: slotPickerSlotVariants({
|
|
166
|
+
selected: isSelected ? "true" : "false",
|
|
167
|
+
disabled: isDisabled ? "true" : "false"
|
|
168
|
+
}),
|
|
169
|
+
onClick: () => !isDisabled && handleSlotSelect(slot),
|
|
170
|
+
children: slot
|
|
171
|
+
},
|
|
172
|
+
slot
|
|
173
|
+
);
|
|
174
|
+
}) })
|
|
175
|
+
] })
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
export {
|
|
182
|
+
SlotPicker
|
|
183
|
+
};
|
|
184
|
+
//# sourceMappingURL=react-slot-picker.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../slot-picker/src/slot-picker.ts", "../../slot-picker/src/slot-picker.styles.ts", "../../react-slot-picker/src/slot-picker.tsx"],
|
|
4
|
+
"sourcesContent": ["/** A single day chip shown in the day-selector row. */\nexport type SlotDay = {\n /** Unique identifier for the day (e.g. \"2024-06-14\"). */\n id: string\n /** Short weekday label (e.g. \"Fri\"). */\n weekday: string\n /** Day-of-month label (e.g. \"14\" or 14). */\n dayNum: string | number\n}\n\n/** The currently-selected day + time slot pair. */\nexport type SlotSelection = {\n /** The `id` of the selected day. */\n dayId: string\n /** The selected time slot string (e.g. \"10:00 AM\"). */\n slot: string\n}\n\n/**\n * Return the list of available time slots for a given day.\n * Returns an empty array when `dayId` has no entry in `slotsByDay`.\n */\nexport function slotsForDay(\n slotsByDay: Record<string, string[]>,\n dayId: string,\n): string[] {\n return slotsByDay[dayId] ?? []\n}\n\n/**\n * Check whether a specific slot in a specific day is currently selected.\n */\nexport function isSlotSelected(\n value: SlotSelection | undefined,\n dayId: string,\n slot: string,\n): boolean {\n return value?.dayId === dayId && value?.slot === slot\n}\n\nexport interface SlotPickerAPI {\n /** ARIA attributes to spread on the root group element. */\n ariaProps: { role: 'group'; 'aria-label': string }\n /** Data attributes for styling hooks. */\n dataAttributes: Record<string, string>\n}\n\n/**\n * Build the framework-agnostic root props for a slot picker.\n *\n * Returns `role=\"group\"` with an accessible label and data attributes.\n * Adapters spread these onto their container element.\n */\nexport function createSlotPicker(): SlotPickerAPI {\n return {\n ariaProps: {\n role: 'group',\n 'aria-label': 'Pick a time',\n },\n dataAttributes: {\n 'data-component': 'slot-picker',\n },\n }\n}\n", "import { cva } from '@refraction-ui/shared'\n\n/** Root wrapper for the entire slot picker (day row + slots grid). */\nexport const slotPickerVariants = cva({\n base: 'flex flex-col gap-4',\n variants: {},\n defaultVariants: {},\n})\n\n/** Scrollable row of day chips. */\nexport const slotPickerDayRowClass = 'flex items-center gap-2 overflow-x-auto pb-1'\n\n/**\n * Individual day chip.\n *\n * `selected` → primary treatment; `unselected` → card with border.\n */\nexport const slotPickerDayVariants = cva({\n base: [\n 'inline-flex flex-col items-center justify-center rounded-lg px-3 py-2 min-w-[3.5rem]',\n 'text-sm font-medium cursor-pointer transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n 'disabled:pointer-events-none disabled:opacity-50',\n ].join(' '),\n variants: {\n selected: {\n true: 'bg-primary text-primary-foreground',\n false: 'bg-card border border-input text-foreground hover:bg-muted',\n },\n },\n defaultVariants: {\n selected: 'false',\n },\n})\n\n/** Grid that holds the time-slot buttons. */\nexport const slotPickerGridClass = 'grid grid-cols-3 gap-2 sm:grid-cols-4'\n\n/**\n * Individual time-slot button.\n *\n * `selected` → primary; `disabled` → dimmed regardless of selection.\n */\nexport const slotPickerSlotVariants = cva({\n base: [\n 'inline-flex items-center justify-center rounded-md border px-3 py-2 text-sm font-medium',\n 'transition-colors cursor-pointer',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n ].join(' '),\n variants: {\n selected: {\n true: 'bg-primary text-primary-foreground border-primary',\n false: 'bg-card border-input text-foreground hover:bg-muted',\n },\n disabled: {\n true: 'opacity-40 pointer-events-none',\n false: '',\n },\n },\n defaultVariants: {\n selected: 'false',\n disabled: 'false',\n },\n})\n\n/** Section label above the day row or slot grid (e.g. \"Pick a day\"). */\nexport const slotPickerSectionLabelClass = 'text-sm font-semibold text-foreground'\n\n/** Timezone label rendered beside the \"Pick a time\" heading. */\nexport const slotPickerTimezoneClass = 'text-xs text-muted-foreground ml-2'\n", "import * as React from 'react'\nimport {\n createSlotPicker,\n slotsForDay,\n isSlotSelected,\n slotPickerVariants,\n slotPickerDayRowClass,\n slotPickerDayVariants,\n slotPickerGridClass,\n slotPickerSlotVariants,\n slotPickerSectionLabelClass,\n slotPickerTimezoneClass,\n type SlotDay,\n type SlotSelection,\n} from '@refraction-ui/slot-picker'\nimport { cn } from '@refraction-ui/shared'\n\nexport type { SlotDay, SlotSelection }\n\nexport interface SlotPickerProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSelect' | 'results' | 'color' | 'content' | 'defaultValue' | 'onChange'> {\n /** The list of days to show as chips. */\n days: SlotDay[]\n /** Map of dayId → available time slot strings. */\n slotsByDay: Record<string, string[]>\n /** Controlled selection value. */\n value?: SlotSelection\n /** Initial selection for uncontrolled usage. */\n defaultValue?: SlotSelection\n /** Called when the user picks a day + slot. */\n onChange?: (selection: SlotSelection) => void\n /** Label shown beside the \"Pick a time\" heading (e.g. \"Eastern Time\"). */\n timezoneLabel?: string\n /** Slot strings that should be rendered as disabled. */\n disabledSlots?: string[]\n}\n\n/**\n * SlotPicker — a Calendly-style day + time-slot booking picker.\n *\n * Renders a row of day chips (radiogroup) and a grid of time-slot buttons\n * (radiogroup) for the currently-selected day. Supports controlled (`value`)\n * and uncontrolled (`defaultValue`) usage. Selecting a day clears the slot\n * when the previously-chosen slot is not available on the new day.\n */\nexport const SlotPicker = React.forwardRef<HTMLDivElement, SlotPickerProps>(\n function SlotPicker(\n {\n days,\n slotsByDay,\n value: valueProp,\n defaultValue,\n onChange,\n timezoneLabel,\n disabledSlots = [],\n className,\n ...props\n },\n ref,\n ) {\n const isControlled = valueProp !== undefined\n const [internal, setInternal] = React.useState<SlotSelection | undefined>(\n defaultValue,\n )\n const value = isControlled ? valueProp : internal\n\n const selectedDayId = value?.dayId\n\n const handleDaySelect = React.useCallback(\n (dayId: string) => {\n const slots = slotsForDay(slotsByDay, dayId)\n // Keep the slot only if it's still available on the new day.\n const keepSlot =\n value?.slot && slots.includes(value.slot) ? value.slot : undefined\n const next: SlotSelection | undefined = keepSlot\n ? { dayId, slot: keepSlot }\n : undefined\n\n if (!isControlled) setInternal(next)\n if (next) onChange?.(next)\n },\n [isControlled, onChange, slotsByDay, value],\n )\n\n const handleSlotSelect = React.useCallback(\n (slot: string) => {\n if (!selectedDayId) return\n const next: SlotSelection = { dayId: selectedDayId, slot }\n if (!isControlled) setInternal(next)\n onChange?.(next)\n },\n [isControlled, onChange, selectedDayId],\n )\n\n const api = createSlotPicker()\n const currentSlots = selectedDayId ? slotsForDay(slotsByDay, selectedDayId) : []\n\n return (\n <div\n ref={ref}\n className={cn(slotPickerVariants(), className)}\n {...api.ariaProps}\n {...api.dataAttributes}\n {...props}\n >\n {/* Day row */}\n <div>\n <p className={slotPickerSectionLabelClass}>Pick a day</p>\n <div role=\"radiogroup\" aria-label=\"Pick a day\" className={cn(slotPickerDayRowClass, 'mt-2')}>\n {days.map((day) => {\n const isSelected = selectedDayId === day.id\n return (\n <button\n key={day.id}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n data-state={isSelected ? 'checked' : 'unchecked'}\n className={slotPickerDayVariants({\n selected: isSelected ? 'true' : 'false',\n })}\n onClick={() => handleDaySelect(day.id)}\n >\n <span className=\"text-xs\">{day.weekday}</span>\n <span className=\"text-base font-bold leading-none\">{day.dayNum}</span>\n </button>\n )\n })}\n </div>\n </div>\n\n {/* Slot grid */}\n {selectedDayId && (\n <div>\n <p className={slotPickerSectionLabelClass}>\n Pick a time\n {timezoneLabel && (\n <span className={slotPickerTimezoneClass}>{timezoneLabel}</span>\n )}\n </p>\n <div role=\"radiogroup\" aria-label=\"Pick a time\" className={cn(slotPickerGridClass, 'mt-2')}>\n {currentSlots.map((slot) => {\n const isSelected = isSlotSelected(value, selectedDayId, slot)\n const isDisabled = disabledSlots.includes(slot)\n return (\n <button\n key={slot}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n aria-disabled={isDisabled || undefined}\n data-state={isSelected ? 'checked' : 'unchecked'}\n className={slotPickerSlotVariants({\n selected: isSelected ? 'true' : 'false',\n disabled: isDisabled ? 'true' : 'false',\n })}\n onClick={() => !isDisabled && handleSlotSelect(slot)}\n >\n {slot}\n </button>\n )\n })}\n </div>\n </div>\n )}\n </div>\n )\n },\n)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["SlotPicker"]
|
|
7
|
+
}
|