tempest-react-sdk 0.35.0 → 0.36.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/README.md +72 -70
- package/dist/audio/audio-output.cjs +2 -0
- package/dist/audio/audio-output.cjs.map +1 -0
- package/dist/audio/audio-output.js +17 -0
- package/dist/audio/audio-output.js.map +1 -0
- package/dist/audio/audio-player.cjs +1 -1
- package/dist/audio/audio-player.cjs.map +1 -1
- package/dist/audio/audio-player.js +21 -20
- package/dist/audio/audio-player.js.map +1 -1
- package/dist/audio/audio-recorder.cjs +2 -0
- package/dist/audio/audio-recorder.cjs.map +1 -0
- package/dist/audio/audio-recorder.js +30 -0
- package/dist/audio/audio-recorder.js.map +1 -0
- package/dist/audio/duration.cjs +2 -0
- package/dist/audio/duration.cjs.map +1 -0
- package/dist/audio/duration.js +10 -0
- package/dist/audio/duration.js.map +1 -0
- package/dist/audio/level-meter.cjs +2 -0
- package/dist/audio/level-meter.cjs.map +1 -0
- package/dist/audio/level-meter.js +34 -0
- package/dist/audio/level-meter.js.map +1 -0
- package/dist/audio/media-access.cjs +2 -0
- package/dist/audio/media-access.cjs.map +1 -0
- package/dist/audio/media-access.js +51 -0
- package/dist/audio/media-access.js.map +1 -0
- package/dist/audio/use-audio-recorder.cjs +2 -0
- package/dist/audio/use-audio-recorder.cjs.map +1 -0
- package/dist/audio/use-audio-recorder.js +94 -0
- package/dist/audio/use-audio-recorder.js.map +1 -0
- package/dist/audio/use-audio.cjs.map +1 -1
- package/dist/audio/use-audio.js.map +1 -1
- package/dist/audio/use-microphone.cjs +2 -0
- package/dist/audio/use-microphone.cjs.map +1 -0
- package/dist/audio/use-microphone.js +54 -0
- package/dist/audio/use-microphone.js.map +1 -0
- package/dist/audio/wav.cjs +2 -0
- package/dist/audio/wav.cjs.map +1 -0
- package/dist/audio/wav.js +49 -0
- package/dist/audio/wav.js.map +1 -0
- package/dist/auth/passkey.cjs +2 -0
- package/dist/auth/passkey.cjs.map +1 -0
- package/dist/auth/passkey.js +171 -0
- package/dist/auth/passkey.js.map +1 -0
- package/dist/auth/use-passkey.cjs +2 -0
- package/dist/auth/use-passkey.cjs.map +1 -0
- package/dist/auth/use-passkey.js +120 -0
- package/dist/auth/use-passkey.js.map +1 -0
- package/dist/br/PixQRCode.cjs +2 -0
- package/dist/br/PixQRCode.cjs.map +1 -0
- package/dist/br/PixQRCode.js +70 -0
- package/dist/br/PixQRCode.js.map +1 -0
- package/dist/br/PixQRCode.module.cjs +2 -0
- package/dist/br/PixQRCode.module.cjs.map +1 -0
- package/dist/br/PixQRCode.module.js +15 -0
- package/dist/br/PixQRCode.module.js.map +1 -0
- package/dist/br/boleto.cjs +2 -0
- package/dist/br/boleto.cjs.map +1 -0
- package/dist/br/boleto.js +225 -0
- package/dist/br/boleto.js.map +1 -0
- package/dist/br/holidays.cjs +2 -0
- package/dist/br/holidays.cjs.map +1 -0
- package/dist/br/holidays.js +148 -0
- package/dist/br/holidays.js.map +1 -0
- package/dist/br/nfe.cjs +2 -0
- package/dist/br/nfe.cjs.map +1 -0
- package/dist/br/nfe.js +104 -0
- package/dist/br/nfe.js.map +1 -0
- package/dist/br/pix.cjs +2 -0
- package/dist/br/pix.cjs.map +1 -0
- package/dist/br/pix.js +148 -0
- package/dist/br/pix.js.map +1 -0
- package/dist/br.cjs +1 -1
- package/dist/br.cjs.map +1 -1
- package/dist/br.d.ts +878 -0
- package/dist/br.js +7 -2
- package/dist/br.js.map +1 -1
- package/dist/capture/barcode.cjs +2 -0
- package/dist/capture/barcode.cjs.map +1 -0
- package/dist/capture/barcode.js +59 -0
- package/dist/capture/barcode.js.map +1 -0
- package/dist/capture/media-recorder.cjs +2 -0
- package/dist/capture/media-recorder.cjs.map +1 -0
- package/dist/capture/media-recorder.js +64 -0
- package/dist/capture/media-recorder.js.map +1 -0
- package/dist/capture/use-barcode-scanner.cjs +2 -0
- package/dist/capture/use-barcode-scanner.cjs.map +1 -0
- package/dist/capture/use-barcode-scanner.js +86 -0
- package/dist/capture/use-barcode-scanner.js.map +1 -0
- package/dist/capture/use-screen-capture.cjs +2 -0
- package/dist/capture/use-screen-capture.cjs.map +1 -0
- package/dist/capture/use-screen-capture.js +79 -0
- package/dist/capture/use-screen-capture.js.map +1 -0
- package/dist/capture/use-speech-recognition.cjs +2 -0
- package/dist/capture/use-speech-recognition.cjs.map +1 -0
- package/dist/capture/use-speech-recognition.js +118 -0
- package/dist/capture/use-speech-recognition.js.map +1 -0
- package/dist/capture/use-torch.cjs +2 -0
- package/dist/capture/use-torch.cjs.map +1 -0
- package/dist/capture/use-torch.js +33 -0
- package/dist/capture/use-torch.js.map +1 -0
- package/dist/capture/use-video-recorder.cjs +2 -0
- package/dist/capture/use-video-recorder.cjs.map +1 -0
- package/dist/capture/use-video-recorder.js +90 -0
- package/dist/capture/use-video-recorder.js.map +1 -0
- package/dist/capture/video-recorder.cjs +2 -0
- package/dist/capture/video-recorder.cjs.map +1 -0
- package/dist/capture/video-recorder.js +31 -0
- package/dist/capture/video-recorder.js.map +1 -0
- package/dist/components/AIChat/AIChat.cjs +1 -1
- package/dist/components/AIChat/AIChat.cjs.map +1 -1
- package/dist/components/AIChat/AIChat.js +93 -97
- package/dist/components/AIChat/AIChat.js.map +1 -1
- package/dist/components/AIChat/AIChatComposer.cjs +1 -1
- package/dist/components/AIChat/AIChatComposer.cjs.map +1 -1
- package/dist/components/AIChat/AIChatComposer.js +2 -1
- package/dist/components/AIChat/AIChatComposer.js.map +1 -1
- package/dist/components/AIChat/AIChatTurn.cjs +2 -2
- package/dist/components/AIChat/AIChatTurn.js +6 -6
- package/dist/components/AIChat/AIChatTurn.js.map +1 -1
- package/dist/components/Alert/Alert.module.cjs.map +1 -1
- package/dist/components/Alert/Alert.module.js.map +1 -1
- package/dist/components/AudioPlayer/AudioPlayer.cjs +2 -0
- package/dist/components/AudioPlayer/AudioPlayer.cjs.map +1 -0
- package/dist/components/AudioPlayer/AudioPlayer.js +123 -0
- package/dist/components/AudioPlayer/AudioPlayer.js.map +1 -0
- package/dist/components/AudioPlayer/AudioPlayer.module.cjs +2 -0
- package/dist/components/AudioPlayer/AudioPlayer.module.cjs.map +1 -0
- package/dist/components/AudioPlayer/AudioPlayer.module.js +12 -0
- package/dist/components/AudioPlayer/AudioPlayer.module.js.map +1 -0
- package/dist/components/AudioRecorder/AudioRecorder.cjs +2 -0
- package/dist/components/AudioRecorder/AudioRecorder.cjs.map +1 -0
- package/dist/components/AudioRecorder/AudioRecorder.js +179 -0
- package/dist/components/AudioRecorder/AudioRecorder.js.map +1 -0
- package/dist/components/AudioRecorder/AudioRecorder.module.cjs +2 -0
- package/dist/components/AudioRecorder/AudioRecorder.module.cjs.map +1 -0
- package/dist/components/AudioRecorder/AudioRecorder.module.js +19 -0
- package/dist/components/AudioRecorder/AudioRecorder.module.js.map +1 -0
- package/dist/components/Badge/Badge.module.cjs.map +1 -1
- package/dist/components/Badge/Badge.module.js.map +1 -1
- package/dist/components/BarcodeScanner/BarcodeScanner.cjs +2 -0
- package/dist/components/BarcodeScanner/BarcodeScanner.cjs.map +1 -0
- package/dist/components/BarcodeScanner/BarcodeScanner.js +123 -0
- package/dist/components/BarcodeScanner/BarcodeScanner.js.map +1 -0
- package/dist/components/BarcodeScanner/BarcodeScanner.module.cjs +2 -0
- package/dist/components/BarcodeScanner/BarcodeScanner.module.cjs.map +1 -0
- package/dist/components/BarcodeScanner/BarcodeScanner.module.js +22 -0
- package/dist/components/BarcodeScanner/BarcodeScanner.module.js.map +1 -0
- package/dist/components/BottomNavigation/BottomNavigation.module.cjs.map +1 -1
- package/dist/components/BottomNavigation/BottomNavigation.module.js.map +1 -1
- package/dist/components/Button/Button.module.cjs.map +1 -1
- package/dist/components/Button/Button.module.js.map +1 -1
- package/dist/components/Combobox/Combobox.module.cjs.map +1 -1
- package/dist/components/Combobox/Combobox.module.js.map +1 -1
- package/dist/components/DataTable/DataTable.cjs +1 -1
- package/dist/components/DataTable/DataTable.cjs.map +1 -1
- package/dist/components/DataTable/DataTable.js +203 -66
- package/dist/components/DataTable/DataTable.js.map +1 -1
- package/dist/components/DataTable/DataTable.module.cjs +1 -1
- package/dist/components/DataTable/DataTable.module.cjs.map +1 -1
- package/dist/components/DataTable/DataTable.module.js +9 -3
- package/dist/components/DataTable/DataTable.module.js.map +1 -1
- package/dist/components/DataTable/EditableCell.cjs +2 -0
- package/dist/components/DataTable/EditableCell.cjs.map +1 -0
- package/dist/components/DataTable/EditableCell.js +77 -0
- package/dist/components/DataTable/EditableCell.js.map +1 -0
- package/dist/components/DataTable/edit-labels.cjs +2 -0
- package/dist/components/DataTable/edit-labels.cjs.map +1 -0
- package/dist/components/DataTable/edit-labels.js +11 -0
- package/dist/components/DataTable/edit-labels.js.map +1 -0
- package/dist/components/DateRangePicker/DateRangePicker.module.cjs.map +1 -1
- package/dist/components/DateRangePicker/DateRangePicker.module.js.map +1 -1
- package/dist/components/MultiSelect/MultiSelect.module.cjs.map +1 -1
- package/dist/components/MultiSelect/MultiSelect.module.js.map +1 -1
- package/dist/components/NavigationRail/NavigationRail.module.cjs.map +1 -1
- package/dist/components/NavigationRail/NavigationRail.module.js.map +1 -1
- package/dist/components/SyncStatusBadge/SyncStatusBadge.cjs +1 -1
- package/dist/components/SyncStatusBadge/SyncStatusBadge.cjs.map +1 -1
- package/dist/components/SyncStatusBadge/SyncStatusBadge.js +33 -32
- package/dist/components/SyncStatusBadge/SyncStatusBadge.js.map +1 -1
- package/dist/components/Toast/ToastProvider.cjs +1 -1
- package/dist/components/Toast/ToastProvider.cjs.map +1 -1
- package/dist/components/Toast/ToastProvider.js +1 -2
- package/dist/components/Toast/ToastProvider.js.map +1 -1
- package/dist/hooks/use-announce.cjs +2 -0
- package/dist/hooks/use-announce.cjs.map +1 -0
- package/dist/hooks/use-announce.js +50 -0
- package/dist/hooks/use-announce.js.map +1 -0
- package/dist/hooks/use-media-devices.cjs +2 -0
- package/dist/hooks/use-media-devices.cjs.map +1 -0
- package/dist/hooks/use-media-devices.js +46 -0
- package/dist/hooks/use-media-devices.js.map +1 -0
- package/dist/hooks/use-media-permission.cjs +2 -0
- package/dist/hooks/use-media-permission.cjs.map +1 -0
- package/dist/hooks/use-media-permission.js +33 -0
- package/dist/hooks/use-media-permission.js.map +1 -0
- package/dist/http/resumable-upload.cjs +2 -0
- package/dist/http/resumable-upload.cjs.map +1 -0
- package/dist/http/resumable-upload.js +249 -0
- package/dist/http/resumable-upload.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +2485 -29
- package/dist/tempest-react-sdk.js +268 -244
- package/dist/vision/use-camera-stream.cjs +1 -1
- package/dist/vision/use-camera-stream.cjs.map +1 -1
- package/dist/vision/use-camera-stream.js +40 -57
- package/dist/vision/use-camera-stream.js.map +1 -1
- package/dist/vision.cjs +1 -1
- package/dist/vision.d.ts +25 -3
- package/dist/vision.js +19 -19
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AudioRecorder.js","names":[],"sources":["../../../src/components/AudioRecorder/AudioRecorder.tsx"],"sourcesContent":["import { Mic, Pause, Play, RotateCcw, Square } from \"lucide-react\";\nimport { useEffect, useState, type HTMLAttributes, type ReactNode } from \"react\";\n\nimport { formatDuration } from \"@/audio/duration\";\nimport { isAudioRecordingSupported } from \"@/audio/audio-recorder\";\nimport { useAudioRecorder } from \"@/audio/use-audio-recorder\";\nimport { useMicrophone } from \"@/audio/use-microphone\";\nimport { blobToWav, type WavOptions } from \"@/audio/wav\";\nimport type { AudioRecording } from \"@/audio/audio-recorder\";\nimport { useMediaPermission } from \"@/hooks/use-media-permission\";\nimport { cn } from \"@/utils/cn\";\n\nimport { AudioPlayer } from \"../AudioPlayer\";\nimport styles from \"./AudioRecorder.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\" | \"onError\";\n\nexport interface AudioRecorderProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n OverriddenDomProps\n> {\n /**\n * Called once a recording is finished and (when asked) converted.\n *\n * Fires on stop and on hitting `maxDurationMs`, never on cancel.\n */\n onRecorded?: (recording: AudioRecording) => void;\n /** Stop automatically after this long. Strongly recommended on anything public. */\n maxDurationMs?: number;\n /** Specific microphone, from `useMediaDevices().audioInputs`. */\n deviceId?: string;\n /**\n * Output container.\n *\n * `\"native\"` (default) keeps what the browser produced — Opus on Chromium and\n * Firefox, AAC on Safari — which is the smallest and needs no work. `\"wav\"`\n * converts on stop, for a backend that only accepts WAV; see `wavOptions`, and\n * expect roughly ten times the bytes.\n */\n format?: \"native\" | \"wav\";\n /** Passed to `blobToWav` when `format=\"wav\"`. `{ mono: true, sampleRate: 16000 }` suits speech. */\n wavOptions?: WavOptions;\n /** Target bitrate. 32000–64000 is plenty for speech in Opus. */\n audioBitsPerSecond?: number;\n /** Offer playback of the recording before handing it over. Default `true`. */\n review?: boolean;\n /** Locale for the labels. Default `\"pt-BR\"`. */\n locale?: \"pt-BR\" | \"en\";\n /** Rendered under the controls — a hint, a character count, a legal notice. */\n footer?: ReactNode;\n /** Recorder-level failure, or a failed WAV conversion. */\n onError?: (error: unknown) => void;\n disabled?: boolean;\n}\n\nconst STRINGS = {\n \"pt-BR\": {\n record: \"Gravar\",\n stop: \"Parar\",\n pause: \"Pausar\",\n resume: \"Continuar\",\n retake: \"Gravar de novo\",\n level: \"Nível do microfone\",\n asking: \"Pedindo acesso ao microfone…\",\n denied: \"Acesso ao microfone bloqueado. Libere nas configurações do site e recarregue.\",\n unsupported: \"Este navegador não grava áudio.\",\n idleHint: \"Toque em Gravar para começar.\",\n recording: \"Gravando\",\n paused: \"Pausado\",\n converting: \"Convertendo…\",\n },\n en: {\n record: \"Record\",\n stop: \"Stop\",\n pause: \"Pause\",\n resume: \"Resume\",\n retake: \"Record again\",\n level: \"Microphone level\",\n asking: \"Requesting microphone access…\",\n denied: \"Microphone access blocked. Allow it in site settings and reload.\",\n unsupported: \"This browser cannot record audio.\",\n idleHint: \"Press Record to start.\",\n recording: \"Recording\",\n paused: \"Paused\",\n converting: \"Converting…\",\n },\n} as const;\n\n/**\n * Record a voice note: one button to start, a live level meter, a clock, and a\n * playback review before the audio leaves the component.\n *\n * The permission flow is the part worth reading. The microphone is **not** opened on\n * mount — the prompt fires on the first press of Record, because a prompt the user did\n * not provoke is the most reliable way to earn a permanent block, after which\n * `getUserMedia` rejects without ever asking again. When the permission is already\n * `\"denied\"`, the component says so and how to fix it instead of offering a button\n * that cannot work.\n *\n * The level meter is not decoration either: a muted OS input or a headset with its mic\n * arm folded up produces a perfectly successful recording of silence, and without a\n * visible level the user only finds out after they finish talking.\n *\n * @example\n * <AudioRecorder\n * maxDurationMs={120_000}\n * format=\"wav\"\n * wavOptions={{ mono: true, sampleRate: 16000 }}\n * onRecorded={({ blob }) => upload(blob)}\n * />\n */\nexport function AudioRecorder({\n onRecorded,\n maxDurationMs,\n deviceId,\n format = \"native\",\n wavOptions,\n audioBitsPerSecond,\n review = true,\n locale = \"pt-BR\",\n footer,\n onError,\n disabled = false,\n className,\n ...rest\n}: AudioRecorderProps) {\n const strings = STRINGS[locale];\n const permission = useMediaPermission(\"microphone\");\n const mic = useMicrophone({ deviceId });\n const [converting, setConverting] = useState(false);\n const [result, setResult] = useState<AudioRecording | null>(null);\n const [supported, setSupported] = useState(true);\n\n useEffect(() => setSupported(isAudioRecordingSupported()), []);\n\n /**\n * Convert if asked, then publish.\n *\n * The conversion is awaited here rather than left to the caller so that\n * `onRecorded` only ever fires with the format the caller asked for — a callback\n * that sometimes hands over WebM and sometimes WAV is a bug waiting in the upload\n * path.\n */\n const finish = async (recording: AudioRecording): Promise<void> => {\n if (format !== \"wav\") {\n setResult(recording);\n onRecorded?.(recording);\n return;\n }\n setConverting(true);\n try {\n const blob = await blobToWav(recording.blob, wavOptions);\n const converted: AudioRecording = { ...recording, blob, mimeType: \"audio/wav\" };\n setResult(converted);\n onRecorded?.(converted);\n } catch (error) {\n onError?.(error);\n setResult(recording);\n onRecorded?.(recording);\n } finally {\n setConverting(false);\n }\n };\n\n const recorder = useAudioRecorder(mic.stream, {\n maxDurationMs,\n audioBitsPerSecond,\n onRecorded: (recording) => void finish(recording),\n onError,\n });\n\n /**\n * Start recording as soon as the stream lands.\n *\n * The user pressed Record before the permission prompt existed, so the press has\n * to survive the round-trip: `mic.start()` opens the device, the stream arrives a\n * few hundred milliseconds later, and recording begins then. Waiting for a second\n * press would make the first one look broken.\n */\n const [armed, setArmed] = useState(false);\n useEffect(() => {\n if (!armed || !recorder.ready || recorder.status !== \"idle\") return;\n setArmed(false);\n recorder.start();\n }, [armed, recorder.ready, recorder.status, recorder]);\n\n const begin = (): void => {\n setResult(null);\n if (mic.stream && recorder.ready) {\n recorder.start();\n return;\n }\n setArmed(true);\n mic.start();\n };\n\n const retake = (): void => {\n setResult(null);\n recorder.cancel();\n begin();\n };\n\n const busy = recorder.status === \"recording\" || recorder.status === \"paused\";\n const blocked = permission.state === \"denied\";\n\n if (!supported) {\n return (\n <div className={cn(styles.recorder, className)} {...rest}>\n <p className={styles.notice}>{strings.unsupported}</p>\n </div>\n );\n }\n\n return (\n <div className={cn(styles.recorder, className)} {...rest}>\n <div className={styles.row}>\n {!busy && (\n <button\n type=\"button\"\n className={styles.record}\n onClick={result ? retake : begin}\n disabled={disabled || blocked || converting}\n aria-label={result ? strings.retake : strings.record}\n >\n {result ? (\n <RotateCcw size={16} aria-hidden />\n ) : (\n <Mic size={16} aria-hidden />\n )}\n <span>{result ? strings.retake : strings.record}</span>\n </button>\n )}\n\n {busy && (\n <>\n <button\n type=\"button\"\n className={styles.stop}\n onClick={() => void recorder.stop()}\n aria-label={strings.stop}\n >\n <Square size={14} aria-hidden />\n <span>{strings.stop}</span>\n </button>\n <button\n type=\"button\"\n className={styles.secondary}\n onClick={\n recorder.status === \"paused\" ? recorder.resume : recorder.pause\n }\n aria-label={\n recorder.status === \"paused\" ? strings.resume : strings.pause\n }\n >\n {recorder.status === \"paused\" ? (\n <Play size={14} aria-hidden />\n ) : (\n <Pause size={14} aria-hidden />\n )}\n </button>\n </>\n )}\n\n {busy && (\n <div\n className={styles.meter}\n role=\"meter\"\n aria-label={strings.level}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={Math.round(recorder.level * 100)}\n >\n <span\n className={styles.meterFill}\n style={{ transform: `scaleX(${recorder.level})` }}\n />\n </div>\n )}\n\n <span className={styles.clock}>\n {formatDuration(busy ? recorder.durationMs : (result?.durationMs ?? 0))}\n {maxDurationMs !== undefined && (\n <span className={styles.max}> / {formatDuration(maxDurationMs)}</span>\n )}\n </span>\n </div>\n\n <p className={styles.status} role=\"status\">\n {converting\n ? strings.converting\n : mic.status === \"requesting\"\n ? strings.asking\n : blocked\n ? strings.denied\n : mic.error\n ? mic.error.message\n : recorder.status === \"recording\"\n ? strings.recording\n : recorder.status === \"paused\"\n ? strings.paused\n : result\n ? \"\"\n : strings.idleHint}\n </p>\n\n {review && result && (\n <AudioPlayer\n src={result.blob}\n durationMs={result.durationMs}\n locale={locale}\n onError={onError}\n />\n )}\n\n {footer && <div className={styles.footer}>{footer}</div>}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAwDA,IAAM,IAAU;CACZ,SAAS;EACL,QAAQ;EACR,MAAM;EACN,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,aAAa;EACb,UAAU;EACV,WAAW;EACX,QAAQ;EACR,YAAY;CAChB;CACA,IAAI;EACA,QAAQ;EACR,MAAM;EACN,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,aAAa;EACb,UAAU;EACV,WAAW;EACX,QAAQ;EACR,YAAY;CAChB;AACJ;AAyBA,SAAgB,EAAc,EAC1B,eACA,kBACA,aACA,YAAS,UACT,eACA,uBACA,YAAS,IACT,YAAS,SACT,WACA,YACA,cAAW,IACX,cACA,GAAG,KACgB;CACnB,IAAM,IAAU,EAAQ,IAClB,IAAa,EAAmB,YAAY,GAC5C,IAAM,EAAc,EAAE,YAAS,CAAC,GAChC,CAAC,GAAY,KAAiB,EAAS,EAAK,GAC5C,CAAC,GAAQ,KAAa,EAAgC,IAAI,GAC1D,CAAC,GAAW,KAAgB,EAAS,EAAI;CAE/C,QAAgB,EAAa,EAA0B,CAAC,GAAG,CAAC,CAAC;CAU7D,IAAM,IAAS,OAAO,MAA6C;EAC/D,IAAI,MAAW,OAAO;GAElB,AADA,EAAU,CAAS,GACnB,IAAa,CAAS;GACtB;EACJ;EACA,EAAc,EAAI;EAClB,IAAI;GACA,IAAM,IAAO,MAAM,EAAU,EAAU,MAAM,CAAU,GACjD,IAA4B;IAAE,GAAG;IAAW;IAAM,UAAU;GAAY;GAE9E,AADA,EAAU,CAAS,GACnB,IAAa,CAAS;EAC1B,SAAS,GAAO;GAGZ,AAFA,IAAU,CAAK,GACf,EAAU,CAAS,GACnB,IAAa,CAAS;EAC1B,UAAU;GACN,EAAc,EAAK;EACvB;CACJ,GAEM,IAAW,EAAiB,EAAI,QAAQ;EAC1C;EACA;EACA,aAAa,MAAc,KAAK,EAAO,CAAS;EAChD;CACJ,CAAC,GAUK,CAAC,GAAO,KAAY,EAAS,EAAK;CACxC,QAAgB;EACR,CAAC,KAAS,CAAC,EAAS,SAAS,EAAS,WAAW,WACrD,EAAS,EAAK,GACd,EAAS,MAAM;CACnB,GAAG;EAAC;EAAO,EAAS;EAAO,EAAS;EAAQ;CAAQ,CAAC;CAErD,IAAM,UAAoB;EAEtB,IADA,EAAU,IAAI,GACV,EAAI,UAAU,EAAS,OAAO;GAC9B,EAAS,MAAM;GACf;EACJ;EAEA,AADA,EAAS,EAAI,GACb,EAAI,MAAM;CACd,GAEM,UAAqB;EAGvB,AAFA,EAAU,IAAI,GACd,EAAS,OAAO,GAChB,EAAM;CACV,GAEM,IAAO,EAAS,WAAW,eAAe,EAAS,WAAW,UAC9D,IAAU,EAAW,UAAU;CAUrC,OARK,IASD,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,UAAU,CAAS;EAAG,GAAI;YAApD;GACI,kBAAC,OAAD;IAAK,WAAW,EAAO;cAAvB;KACK,CAAC,KACE,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,SAAS,IAAS,IAAS;MAC3B,UAAU,KAAY,KAAW;MACjC,cAAY,IAAS,EAAQ,SAAS,EAAQ;gBALlD,CAQQ,EADH,IACI,IAEA,GAFD;OAAW,MAAM;OAAI,eAAA;MAAa,CAEN,GAEhC,kBAAC,QAAD,EAAA,UAAO,IAAS,EAAQ,SAAS,EAAQ,OAAa,CAAA,CAClD;;KAGX,KACG,kBAAA,GAAA,EAAA,UAAA,CACI,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,eAAe,KAAK,EAAS,KAAK;MAClC,cAAY,EAAQ;gBAJxB,CAMI,kBAAC,GAAD;OAAQ,MAAM;OAAI,eAAA;MAAa,CAAA,GAC/B,kBAAC,QAAD,EAAA,UAAO,EAAQ,KAAW,CAAA,CACtB;SACR,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,SACI,EAAS,WAAW,WAAW,EAAS,SAAS,EAAS;MAE9D,cACI,EAAS,WAAW,WAAW,EAAQ,SAAS,EAAQ;gBAG3D,EAAS,WAAW,WACjB,kBAAC,GAAD;OAAM,MAAM;OAAI,eAAA;MAAa,CAAA,IAE7B,kBAAC,GAAD;OAAO,MAAM;OAAI,eAAA;MAAa,CAAA;KAE9B,CAAA,CACV,EAAA,CAAA;KAGL,KACG,kBAAC,OAAD;MACI,WAAW,EAAO;MAClB,MAAK;MACL,cAAY,EAAQ;MACpB,iBAAe;MACf,iBAAe;MACf,iBAAe,KAAK,MAAM,EAAS,QAAQ,GAAG;gBAE9C,kBAAC,QAAD;OACI,WAAW,EAAO;OAClB,OAAO,EAAE,WAAW,UAAU,EAAS,MAAM,GAAG;MACnD,CAAA;KACA,CAAA;KAGT,kBAAC,QAAD;MAAM,WAAW,EAAO;gBAAxB,CACK,EAAe,IAAO,EAAS,aAAc,GAAQ,cAAc,CAAE,GACrE,MAAkB,KAAA,KACf,kBAAC,QAAD;OAAM,WAAW,EAAO;iBAAxB,CAA6B,OAAI,EAAe,CAAa,CAAQ;QAEvE;;IACL;;GAEL,kBAAC,KAAD;IAAG,WAAW,EAAO;IAAQ,MAAK;cAC7B,IACK,EAAQ,aACR,EAAI,WAAW,eACb,EAAQ,SACR,IACE,EAAQ,SACR,EAAI,QACF,EAAI,MAAM,UACV,EAAS,WAAW,cAClB,EAAQ,YACR,EAAS,WAAW,WAClB,EAAQ,SACR,IACE,KACA,EAAQ;GAC3B,CAAA;GAEF,KAAU,KACP,kBAAC,GAAD;IACI,KAAK,EAAO;IACZ,YAAY,EAAO;IACX;IACC;GACZ,CAAA;GAGJ,KAAU,kBAAC,OAAD;IAAK,WAAW,EAAO;cAAS;GAAY,CAAA;EACtD;MA5GD,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,UAAU,CAAS;EAAG,GAAI;YAChD,kBAAC,KAAD;GAAG,WAAW,EAAO;aAAS,EAAQ;EAAe,CAAA;CACpD,CAAA;AA4GjB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=`tempest_recorder_4p8pT`,t=`tempest_row_gNlbi`,n=`tempest_record_dhkt8`,r=`tempest_stop_yMMLh`,i=`tempest_secondary_C13wq`,a=`tempest_meter_6QDc9`,o=`tempest_meterFill_oUoGY`,s=`tempest_clock_VGvvJ`,c=`tempest_max_kx9VL`,l=`tempest_status_v2kHH`,u=`tempest_notice_MtPVY`,d=`tempest_footer_tfueB`,f={recorder:e,row:t,record:n,stop:r,secondary:i,meter:a,meterFill:o,clock:s,max:c,status:l,notice:u,footer:d};exports.clock=s,exports.default=f,exports.footer=d,exports.max=c,exports.meter=a,exports.meterFill=o,exports.notice=u,exports.record=n,exports.recorder=e,exports.row=t,exports.secondary=i,exports.status=l,exports.stop=r;
|
|
2
|
+
//# sourceMappingURL=AudioRecorder.module.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AudioRecorder.module.cjs","names":[],"sources":["../../../src/components/AudioRecorder/AudioRecorder.module.css"],"sourcesContent":[".recorder {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-2);\n min-width: 0;\n padding: var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n}\n\n.row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-2);\n min-width: 0;\n}\n\n.record,\n.stop,\n.secondary {\n position: relative;\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n gap: var(--tempest-space-2);\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border: 1px solid transparent;\n border-radius: var(--tempest-radius-md);\n font: inherit;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-medium);\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n.record {\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.record:disabled {\n background-color: var(--tempest-surface-3);\n color: var(--tempest-text-subtle);\n cursor: not-allowed;\n}\n\n/*\n * Stop reads as the danger control — while recording it is the one\n * irreversible-feeling thing on screen and has to be what the eye lands on without\n * reading labels — but it uses the **soft** danger pair, not solid red with white.\n *\n * Measured in a real browser: white on `--tempest-danger-solid` is 4.83:1 in the light\n * theme and **3.76:1** in dark, under the 4.5:1 floor for text. Every solid variant\n * fails there (`--tempest-danger` is 2.77:1 against white in dark, `-hover` 3.76:1);\n * only `--tempest-danger-fg` on `--tempest-danger-bg` clears it in both — 8.2:1 light,\n * 6.57:1 dark. The border keeps the control unmistakably red at a glance.\n */\n.stop {\n border-color: var(--tempest-danger);\n background-color: var(--tempest-danger-bg);\n color: var(--tempest-danger-fg);\n}\n\n.secondary {\n border-color: var(--tempest-border);\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n padding-inline: var(--tempest-space-2);\n}\n\n.record:focus-visible,\n.stop:focus-visible,\n.secondary:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: 2px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .record,\n .stop,\n .secondary {\n transition: none;\n }\n}\n\n/* Touch hit-slop — 44×44 without changing the visual footprint, as `Button` does. */\n@media (pointer: coarse) {\n .secondary::after {\n content: \"\";\n position: absolute;\n inset: -6px;\n }\n}\n\n/*\n * The meter is transform-driven, not width-driven: `transform` is composited, so a\n * value arriving 10 times a second does not force layout on the whole row.\n */\n.meter {\n flex: 1 1 6rem;\n min-width: 3rem;\n height: 0.5rem;\n overflow: hidden;\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface-2);\n}\n\n.meterFill {\n display: block;\n width: 100%;\n height: 100%;\n background-color: var(--tempest-success);\n transform-origin: left center;\n transform: scaleX(0);\n transition: transform 80ms linear;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .meterFill {\n transition: none;\n }\n}\n\n.clock {\n flex: 0 0 auto;\n color: var(--tempest-text);\n font-size: var(--tempest-text-sm);\n font-variant-numeric: tabular-nums;\n}\n\n.max {\n color: var(--tempest-text-muted);\n}\n\n.status {\n margin: 0;\n min-height: 1.25em;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n\n.notice {\n margin: 0;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n}\n\n.footer {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n\n@media (max-width: 480px) {\n .recorder {\n padding: var(--tempest-space-2);\n }\n\n .meter {\n flex-basis: 100%;\n order: 1;\n }\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/components/AudioRecorder/AudioRecorder.module.css
|
|
2
|
+
var e = "tempest_recorder_4p8pT", t = "tempest_row_gNlbi", n = "tempest_record_dhkt8", r = "tempest_stop_yMMLh", i = "tempest_secondary_C13wq", a = "tempest_meter_6QDc9", o = "tempest_meterFill_oUoGY", s = "tempest_clock_VGvvJ", c = "tempest_max_kx9VL", l = "tempest_status_v2kHH", u = "tempest_notice_MtPVY", d = "tempest_footer_tfueB", f = {
|
|
3
|
+
recorder: e,
|
|
4
|
+
row: t,
|
|
5
|
+
record: n,
|
|
6
|
+
stop: r,
|
|
7
|
+
secondary: i,
|
|
8
|
+
meter: a,
|
|
9
|
+
meterFill: o,
|
|
10
|
+
clock: s,
|
|
11
|
+
max: c,
|
|
12
|
+
status: l,
|
|
13
|
+
notice: u,
|
|
14
|
+
footer: d
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { s as clock, f as default, d as footer, c as max, a as meter, o as meterFill, u as notice, n as record, e as recorder, t as row, i as secondary, l as status, r as stop };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=AudioRecorder.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AudioRecorder.module.js","names":[],"sources":["../../../src/components/AudioRecorder/AudioRecorder.module.css"],"sourcesContent":[".recorder {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-2);\n min-width: 0;\n padding: var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n}\n\n.row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--tempest-space-2);\n min-width: 0;\n}\n\n.record,\n.stop,\n.secondary {\n position: relative;\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n gap: var(--tempest-space-2);\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border: 1px solid transparent;\n border-radius: var(--tempest-radius-md);\n font: inherit;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-medium);\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n.record {\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n\n.record:disabled {\n background-color: var(--tempest-surface-3);\n color: var(--tempest-text-subtle);\n cursor: not-allowed;\n}\n\n/*\n * Stop reads as the danger control — while recording it is the one\n * irreversible-feeling thing on screen and has to be what the eye lands on without\n * reading labels — but it uses the **soft** danger pair, not solid red with white.\n *\n * Measured in a real browser: white on `--tempest-danger-solid` is 4.83:1 in the light\n * theme and **3.76:1** in dark, under the 4.5:1 floor for text. Every solid variant\n * fails there (`--tempest-danger` is 2.77:1 against white in dark, `-hover` 3.76:1);\n * only `--tempest-danger-fg` on `--tempest-danger-bg` clears it in both — 8.2:1 light,\n * 6.57:1 dark. The border keeps the control unmistakably red at a glance.\n */\n.stop {\n border-color: var(--tempest-danger);\n background-color: var(--tempest-danger-bg);\n color: var(--tempest-danger-fg);\n}\n\n.secondary {\n border-color: var(--tempest-border);\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n padding-inline: var(--tempest-space-2);\n}\n\n.record:focus-visible,\n.stop:focus-visible,\n.secondary:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: 2px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .record,\n .stop,\n .secondary {\n transition: none;\n }\n}\n\n/* Touch hit-slop — 44×44 without changing the visual footprint, as `Button` does. */\n@media (pointer: coarse) {\n .secondary::after {\n content: \"\";\n position: absolute;\n inset: -6px;\n }\n}\n\n/*\n * The meter is transform-driven, not width-driven: `transform` is composited, so a\n * value arriving 10 times a second does not force layout on the whole row.\n */\n.meter {\n flex: 1 1 6rem;\n min-width: 3rem;\n height: 0.5rem;\n overflow: hidden;\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface-2);\n}\n\n.meterFill {\n display: block;\n width: 100%;\n height: 100%;\n background-color: var(--tempest-success);\n transform-origin: left center;\n transform: scaleX(0);\n transition: transform 80ms linear;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .meterFill {\n transition: none;\n }\n}\n\n.clock {\n flex: 0 0 auto;\n color: var(--tempest-text);\n font-size: var(--tempest-text-sm);\n font-variant-numeric: tabular-nums;\n}\n\n.max {\n color: var(--tempest-text-muted);\n}\n\n.status {\n margin: 0;\n min-height: 1.25em;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n\n.notice {\n margin: 0;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n}\n\n.footer {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n\n@media (max-width: 480px) {\n .recorder {\n padding: var(--tempest-space-2);\n }\n\n .meter {\n flex-basis: 100%;\n order: 1;\n }\n}\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Badge.module.cjs","names":[],"sources":["../../../src/components/Badge/Badge.module.css"],"sourcesContent":[".badge {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: 2px var(--tempest-space-2);\n border: 1px solid transparent;\n border-radius: var(--tempest-radius-full);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-snug);\n letter-spacing: var(--tempest-tracking-normal);\n white-space: nowrap;\n}\n\n/* ============================================================\n * Sizes\n * ============================================================ */\n.sm {\n padding: 1px var(--tempest-space-2);\n font-size: var(--tempest-text-2xs);\n}\n.md {\n padding: 2px var(--tempest-space-2);\n font-size: var(--tempest-text-xs);\n}\n.lg {\n padding: 3px var(--tempest-space-3);\n font-size: var(--tempest-text-sm);\n}\n\n/* ============================================================\n * Shapes — pill is the default (border-radius-full).\n * `square` flips to a small radius for chip-style badges.\n * ============================================================ */\n.square {\n border-radius: var(--tempest-radius-sm);\n}\n\n/* ============================================================\n * Tone × Style matrix.\n * Soft (default) = tinted bg + colored fg.\n * Solid = filled colored bg + white fg.\n * Outline = transparent bg + colored fg + colored border.\n * ============================================================ */\n\n/* Neutral */\n.neutral {\n background-color: var(--tempest-surface-2);\n color: var(--tempest-text);\n}\n.neutral.solid {\n background-color: var(--tempest-gray-700);\n color: #ffffff;\n}\n.neutral.outline {\n background-color: transparent;\n color: var(--tempest-text);\n border-color: var(--tempest-border-strong);\n}\n\n/* Success */\n.success {\n background-color: var(--tempest-success-bg);\n color: var(--tempest-success-fg);\n}\n.success.solid {\n background-color: var(--tempest-success-solid);\n color:
|
|
1
|
+
{"version":3,"file":"Badge.module.cjs","names":[],"sources":["../../../src/components/Badge/Badge.module.css"],"sourcesContent":[".badge {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: 2px var(--tempest-space-2);\n border: 1px solid transparent;\n border-radius: var(--tempest-radius-full);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-snug);\n letter-spacing: var(--tempest-tracking-normal);\n white-space: nowrap;\n}\n\n/* ============================================================\n * Sizes\n * ============================================================ */\n.sm {\n padding: 1px var(--tempest-space-2);\n font-size: var(--tempest-text-2xs);\n}\n.md {\n padding: 2px var(--tempest-space-2);\n font-size: var(--tempest-text-xs);\n}\n.lg {\n padding: 3px var(--tempest-space-3);\n font-size: var(--tempest-text-sm);\n}\n\n/* ============================================================\n * Shapes — pill is the default (border-radius-full).\n * `square` flips to a small radius for chip-style badges.\n * ============================================================ */\n.square {\n border-radius: var(--tempest-radius-sm);\n}\n\n/* ============================================================\n * Tone × Style matrix.\n * Soft (default) = tinted bg + colored fg.\n * Solid = filled colored bg + white fg.\n * Outline = transparent bg + colored fg + colored border.\n * ============================================================ */\n\n/* Neutral */\n.neutral {\n background-color: var(--tempest-surface-2);\n color: var(--tempest-text);\n}\n.neutral.solid {\n background-color: var(--tempest-gray-700);\n color: #ffffff;\n}\n.neutral.outline {\n background-color: transparent;\n color: var(--tempest-text);\n border-color: var(--tempest-border-strong);\n}\n\n/* Success */\n.success {\n background-color: var(--tempest-success-bg);\n color: var(--tempest-success-fg);\n}\n.success.solid {\n background-color: var(--tempest-success-solid);\n color: var(--tempest-success-on-solid);\n}\n.success.outline {\n background-color: transparent;\n color: var(--tempest-success);\n border-color: var(--tempest-success-border);\n}\n\n/* Warning */\n.warning {\n background-color: var(--tempest-warning-bg);\n color: var(--tempest-warning-fg);\n}\n.warning.solid {\n background-color: var(--tempest-warning-solid);\n color: var(--tempest-warning-on-solid);\n}\n.warning.outline {\n background-color: transparent;\n color: var(--tempest-warning);\n border-color: var(--tempest-warning-border);\n}\n\n/* Danger */\n.danger {\n background-color: var(--tempest-danger-bg);\n color: var(--tempest-danger-fg);\n}\n.danger.solid {\n background-color: var(--tempest-danger-solid);\n color: var(--tempest-danger-on-solid);\n}\n.danger.outline {\n background-color: transparent;\n color: var(--tempest-danger);\n border-color: var(--tempest-danger-border);\n}\n\n/* Info */\n.info {\n background-color: var(--tempest-info-bg);\n color: var(--tempest-info-fg);\n}\n.info.solid {\n background-color: var(--tempest-info-solid);\n color: var(--tempest-info-on-solid);\n}\n.info.outline {\n background-color: transparent;\n color: var(--tempest-info);\n border-color: var(--tempest-info-border);\n}\n\n/* Primary */\n.primary {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n.primary.solid {\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n.primary.outline {\n background-color: transparent;\n color: var(--tempest-primary);\n border-color: var(--tempest-primary);\n}\n\n/* Status dot — small leading dot, color inherits from text. */\n.dot {\n width: 6px;\n height: 6px;\n border-radius: var(--tempest-radius-full);\n background-color: currentColor;\n display: inline-block;\n flex-shrink: 0;\n}\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Badge.module.js","names":[],"sources":["../../../src/components/Badge/Badge.module.css"],"sourcesContent":[".badge {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: 2px var(--tempest-space-2);\n border: 1px solid transparent;\n border-radius: var(--tempest-radius-full);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-snug);\n letter-spacing: var(--tempest-tracking-normal);\n white-space: nowrap;\n}\n\n/* ============================================================\n * Sizes\n * ============================================================ */\n.sm {\n padding: 1px var(--tempest-space-2);\n font-size: var(--tempest-text-2xs);\n}\n.md {\n padding: 2px var(--tempest-space-2);\n font-size: var(--tempest-text-xs);\n}\n.lg {\n padding: 3px var(--tempest-space-3);\n font-size: var(--tempest-text-sm);\n}\n\n/* ============================================================\n * Shapes — pill is the default (border-radius-full).\n * `square` flips to a small radius for chip-style badges.\n * ============================================================ */\n.square {\n border-radius: var(--tempest-radius-sm);\n}\n\n/* ============================================================\n * Tone × Style matrix.\n * Soft (default) = tinted bg + colored fg.\n * Solid = filled colored bg + white fg.\n * Outline = transparent bg + colored fg + colored border.\n * ============================================================ */\n\n/* Neutral */\n.neutral {\n background-color: var(--tempest-surface-2);\n color: var(--tempest-text);\n}\n.neutral.solid {\n background-color: var(--tempest-gray-700);\n color: #ffffff;\n}\n.neutral.outline {\n background-color: transparent;\n color: var(--tempest-text);\n border-color: var(--tempest-border-strong);\n}\n\n/* Success */\n.success {\n background-color: var(--tempest-success-bg);\n color: var(--tempest-success-fg);\n}\n.success.solid {\n background-color: var(--tempest-success-solid);\n color:
|
|
1
|
+
{"version":3,"file":"Badge.module.js","names":[],"sources":["../../../src/components/Badge/Badge.module.css"],"sourcesContent":[".badge {\n display: inline-flex;\n align-items: center;\n gap: var(--tempest-space-1);\n padding: 2px var(--tempest-space-2);\n border: 1px solid transparent;\n border-radius: var(--tempest-radius-full);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-snug);\n letter-spacing: var(--tempest-tracking-normal);\n white-space: nowrap;\n}\n\n/* ============================================================\n * Sizes\n * ============================================================ */\n.sm {\n padding: 1px var(--tempest-space-2);\n font-size: var(--tempest-text-2xs);\n}\n.md {\n padding: 2px var(--tempest-space-2);\n font-size: var(--tempest-text-xs);\n}\n.lg {\n padding: 3px var(--tempest-space-3);\n font-size: var(--tempest-text-sm);\n}\n\n/* ============================================================\n * Shapes — pill is the default (border-radius-full).\n * `square` flips to a small radius for chip-style badges.\n * ============================================================ */\n.square {\n border-radius: var(--tempest-radius-sm);\n}\n\n/* ============================================================\n * Tone × Style matrix.\n * Soft (default) = tinted bg + colored fg.\n * Solid = filled colored bg + white fg.\n * Outline = transparent bg + colored fg + colored border.\n * ============================================================ */\n\n/* Neutral */\n.neutral {\n background-color: var(--tempest-surface-2);\n color: var(--tempest-text);\n}\n.neutral.solid {\n background-color: var(--tempest-gray-700);\n color: #ffffff;\n}\n.neutral.outline {\n background-color: transparent;\n color: var(--tempest-text);\n border-color: var(--tempest-border-strong);\n}\n\n/* Success */\n.success {\n background-color: var(--tempest-success-bg);\n color: var(--tempest-success-fg);\n}\n.success.solid {\n background-color: var(--tempest-success-solid);\n color: var(--tempest-success-on-solid);\n}\n.success.outline {\n background-color: transparent;\n color: var(--tempest-success);\n border-color: var(--tempest-success-border);\n}\n\n/* Warning */\n.warning {\n background-color: var(--tempest-warning-bg);\n color: var(--tempest-warning-fg);\n}\n.warning.solid {\n background-color: var(--tempest-warning-solid);\n color: var(--tempest-warning-on-solid);\n}\n.warning.outline {\n background-color: transparent;\n color: var(--tempest-warning);\n border-color: var(--tempest-warning-border);\n}\n\n/* Danger */\n.danger {\n background-color: var(--tempest-danger-bg);\n color: var(--tempest-danger-fg);\n}\n.danger.solid {\n background-color: var(--tempest-danger-solid);\n color: var(--tempest-danger-on-solid);\n}\n.danger.outline {\n background-color: transparent;\n color: var(--tempest-danger);\n border-color: var(--tempest-danger-border);\n}\n\n/* Info */\n.info {\n background-color: var(--tempest-info-bg);\n color: var(--tempest-info-fg);\n}\n.info.solid {\n background-color: var(--tempest-info-solid);\n color: var(--tempest-info-on-solid);\n}\n.info.outline {\n background-color: transparent;\n color: var(--tempest-info);\n border-color: var(--tempest-info-border);\n}\n\n/* Primary */\n.primary {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n.primary.solid {\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n.primary.outline {\n background-color: transparent;\n color: var(--tempest-primary);\n border-color: var(--tempest-primary);\n}\n\n/* Status dot — small leading dot, color inherits from text. */\n.dot {\n width: 6px;\n height: 6px;\n border-radius: var(--tempest-radius-full);\n background-color: currentColor;\n display: inline-block;\n flex-shrink: 0;\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("../../capture/use-barcode-scanner.cjs"),n=require("./BarcodeScanner.module.cjs");require("react");let r=require("react/jsx-runtime"),i=require("lucide-react");var a={"pt-BR":{viewport:`Visor da câmera`,scanning:`Procurando código…`,paused:`Leitura pausada`,opening:`Abrindo a câmera…`,hint:`Aponte a câmera para o código.`,found:e=>`Código lido: ${e}`,torchOn:`Desligar lanterna`,torchOff:`Ligar lanterna`,unsupported:`Este navegador não decodifica códigos de barras.`,retry:`Tentar de novo`},en:{viewport:`Camera viewport`,scanning:`Looking for a code…`,paused:`Scanning paused`,opening:`Opening the camera…`,hint:`Point the camera at the code.`,found:e=>`Code read: ${e}`,torchOn:`Turn torch off`,torchOff:`Turn torch on`,unsupported:`This browser cannot decode barcodes.`,retry:`Try again`}};function o({onScan:o,formats:s,paused:c=!1,detector:l,intervalMs:u,repeatDelayMs:d,torch:f=!0,aspectRatio:p=4/3,locale:m=`pt-BR`,footer:h,unsupported:g,onError:_,className:v,...y}){let b=a[m],{videoRef:x,status:S,error:C,supported:w,scanning:T,result:E,torch:D,retry:O}=t.useBarcodeScanner({formats:s,paused:c,detector:l,intervalMs:u,repeatDelayMs:d,onScan:o,onError:_});if(!w)return(0,r.jsxs)(`div`,{className:e.cn(n.default.scanner,v),...y,children:[(0,r.jsx)(`p`,{className:n.default.notice,children:b.unsupported}),g]});let k=E?b.found(E.rawValue):c?b.paused:T?b.scanning:b.opening;return(0,r.jsxs)(`div`,{className:e.cn(n.default.scanner,v),...y,children:[(0,r.jsxs)(`div`,{className:n.default.viewport,style:{aspectRatio:String(p)},"aria-label":b.viewport,role:`group`,children:[(0,r.jsx)(`video`,{ref:x,className:n.default.video,muted:!0,playsInline:!0,"aria-hidden":`true`}),S===`ready`&&(0,r.jsx)(`div`,{className:n.default.frame,"aria-hidden":`true`,children:(0,r.jsx)(`span`,{className:e.cn(n.default.laser,T&&n.default.laserActive)})}),f&&D.supported&&(0,r.jsx)(`button`,{type:`button`,className:n.default.torch,onClick:()=>void D.toggle(),"aria-pressed":D.on,"aria-label":D.on?b.torchOn:b.torchOff,children:D.on?(0,r.jsx)(i.FlashlightOff,{size:18,"aria-hidden":!0}):(0,r.jsx)(i.Flashlight,{size:18,"aria-hidden":!0})}),C&&(0,r.jsxs)(`div`,{className:n.default.overlay,role:`alert`,children:[(0,r.jsx)(`p`,{className:n.default.overlayText,children:C.message}),(0,r.jsx)(`button`,{type:`button`,className:n.default.retry,onClick:O,children:b.retry})]})]}),!C&&(0,r.jsxs)(`p`,{className:n.default.status,role:`status`,children:[(0,r.jsx)(i.ScanLine,{size:14,"aria-hidden":!0,className:n.default.statusIcon}),k]}),h?(0,r.jsx)(`div`,{className:n.default.footer,children:h}):null]})}exports.BarcodeScanner=o;
|
|
2
|
+
//# sourceMappingURL=BarcodeScanner.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BarcodeScanner.cjs","names":[],"sources":["../../../src/components/BarcodeScanner/BarcodeScanner.tsx"],"sourcesContent":["import { Flashlight, FlashlightOff, ScanLine } from \"lucide-react\";\nimport { type HTMLAttributes, type ReactNode } from \"react\";\n\nimport { useBarcodeScanner } from \"@/capture/use-barcode-scanner\";\nimport type { BarcodeDetectorLike, BarcodeFormat, BarcodeScanResult } from \"@/capture/barcode\";\nimport { cn } from \"@/utils/cn\";\n\nimport styles from \"./BarcodeScanner.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\" | \"onError\";\n\nexport interface BarcodeScannerProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n OverriddenDomProps\n> {\n /** Called for every accepted read — repeats of the same value are suppressed. */\n onScan: (result: BarcodeScanResult) => void;\n /** Symbologies to look for. Defaults to QR + EAN-13 + Code 128. */\n formats?: readonly BarcodeFormat[];\n /** Stop looking without releasing the camera — set it while a confirmation is open. */\n paused?: boolean;\n /** A decoder to use instead of the native one, for Safari and Firefox. */\n detector?: BarcodeDetectorLike;\n /** How often a frame is examined, in ms. Default 200. */\n intervalMs?: number;\n /** Ignore the same value again for this long, in ms. Default 2500. */\n repeatDelayMs?: number;\n /** Offer the torch toggle when the camera has a lamp. Default `true`. */\n torch?: boolean;\n /** Viewport aspect ratio, `width / height`. Default `4 / 3`. */\n aspectRatio?: number;\n /** Locale for the labels. Default `\"pt-BR\"`. */\n locale?: \"pt-BR\" | \"en\";\n /** Under the viewport — an instruction, the code just read, a manual-entry link. */\n footer?: ReactNode;\n /**\n * Rendered instead of the camera when there is no decoder.\n *\n * Worth filling in: on iOS and Firefox this is the **only** thing the user sees, so\n * the fallback is usually a plain text field for typing the code.\n */\n unsupported?: ReactNode;\n /** A frame the engine refused to decode. Routine and usually transient. */\n onError?: (error: unknown) => void;\n}\n\nconst STRINGS = {\n \"pt-BR\": {\n viewport: \"Visor da câmera\",\n scanning: \"Procurando código…\",\n paused: \"Leitura pausada\",\n opening: \"Abrindo a câmera…\",\n hint: \"Aponte a câmera para o código.\",\n found: (value: string) => `Código lido: ${value}`,\n torchOn: \"Desligar lanterna\",\n torchOff: \"Ligar lanterna\",\n unsupported: \"Este navegador não decodifica códigos de barras.\",\n retry: \"Tentar de novo\",\n },\n en: {\n viewport: \"Camera viewport\",\n scanning: \"Looking for a code…\",\n paused: \"Scanning paused\",\n opening: \"Opening the camera…\",\n hint: \"Point the camera at the code.\",\n found: (value: string) => `Code read: ${value}`,\n torchOn: \"Turn torch off\",\n torchOff: \"Turn torch on\",\n unsupported: \"This browser cannot decode barcodes.\",\n retry: \"Try again\",\n },\n} as const;\n\n/**\n * Point the camera at a barcode and get its value.\n *\n * The counterpart to {@link QRCode}, which only encodes. Under it are\n * `useBarcodeScanner` (the detect loop and repeat suppression), `useCameraStream` (the\n * stream and its classified errors) and `useTorch`.\n *\n * **Mounting this opens the camera**, so mount it when the user asks to scan rather\n * than on a page that happens to contain a scanner: a permission prompt nobody\n * provoked is the surest way to earn a permanent block, and after that `getUserMedia`\n * rejects without ever prompting again. The usual shape is a button that reveals it.\n *\n * The `unsupported` slot is not a nicety. `BarcodeDetector` is Chromium-only — absent\n * on Firefox, on every browser on iOS, and on Chromium for Windows and Linux — so on a\n * large share of real devices the fallback *is* the feature. Give it a text field, or\n * inject a polyfill through `detector`.\n *\n * The preview itself is `aria-hidden`: a live camera frame has nothing to announce and\n * no audio to caption, so what a screen reader gets is the `role=\"status\"` line, which\n * says whether scanning is running and reads out each accepted code.\n *\n * @example\n * <BarcodeScanner\n * formats={[\"ean_13\"]}\n * onScan={({ rawValue }) => addToCart(rawValue)}\n * footer={<small>Aponte para o código de barras da embalagem.</small>}\n * unsupported={<ManualCodeInput onSubmit={addToCart} />}\n * />\n */\nexport function BarcodeScanner({\n onScan,\n formats,\n paused = false,\n detector,\n intervalMs,\n repeatDelayMs,\n torch = true,\n aspectRatio = 4 / 3,\n locale = \"pt-BR\",\n footer,\n unsupported,\n onError,\n className,\n ...rest\n}: BarcodeScannerProps) {\n const strings = STRINGS[locale];\n const {\n videoRef,\n status,\n error,\n supported,\n scanning,\n result,\n torch: lamp,\n retry,\n } = useBarcodeScanner({\n formats,\n paused,\n detector,\n intervalMs,\n repeatDelayMs,\n onScan,\n onError,\n });\n\n if (!supported) {\n return (\n <div className={cn(styles.scanner, className)} {...rest}>\n <p className={styles.notice}>{strings.unsupported}</p>\n {unsupported}\n </div>\n );\n }\n\n const message = result\n ? strings.found(result.rawValue)\n : paused\n ? strings.paused\n : scanning\n ? strings.scanning\n : strings.opening;\n\n return (\n <div className={cn(styles.scanner, className)} {...rest}>\n <div\n className={styles.viewport}\n style={{ aspectRatio: String(aspectRatio) }}\n aria-label={strings.viewport}\n role=\"group\"\n >\n <video\n ref={videoRef}\n className={styles.video}\n muted\n playsInline\n aria-hidden=\"true\"\n />\n\n {status === \"ready\" && (\n <div className={styles.frame} aria-hidden=\"true\">\n <span className={cn(styles.laser, scanning && styles.laserActive)} />\n </div>\n )}\n\n {torch && lamp.supported && (\n <button\n type=\"button\"\n className={styles.torch}\n onClick={() => void lamp.toggle()}\n aria-pressed={lamp.on}\n aria-label={lamp.on ? strings.torchOn : strings.torchOff}\n >\n {lamp.on ? (\n <FlashlightOff size={18} aria-hidden />\n ) : (\n <Flashlight size={18} aria-hidden />\n )}\n </button>\n )}\n\n {error && (\n <div className={styles.overlay} role=\"alert\">\n <p className={styles.overlayText}>{error.message}</p>\n <button type=\"button\" className={styles.retry} onClick={retry}>\n {strings.retry}\n </button>\n </div>\n )}\n </div>\n\n {!error && (\n <p className={styles.status} role=\"status\">\n <ScanLine size={14} aria-hidden className={styles.statusIcon} />\n {message}\n </p>\n )}\n\n {footer ? <div className={styles.footer}>{footer}</div> : null}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { cn as e } from "../../utils/cn.js";
|
|
2
|
+
import { useBarcodeScanner as t } from "../../capture/use-barcode-scanner.js";
|
|
3
|
+
import n from "./BarcodeScanner.module.js";
|
|
4
|
+
import "react";
|
|
5
|
+
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
6
|
+
import { Flashlight as a, FlashlightOff as o, ScanLine as s } from "lucide-react";
|
|
7
|
+
//#region src/components/BarcodeScanner/BarcodeScanner.tsx
|
|
8
|
+
var c = {
|
|
9
|
+
"pt-BR": {
|
|
10
|
+
viewport: "Visor da câmera",
|
|
11
|
+
scanning: "Procurando código…",
|
|
12
|
+
paused: "Leitura pausada",
|
|
13
|
+
opening: "Abrindo a câmera…",
|
|
14
|
+
hint: "Aponte a câmera para o código.",
|
|
15
|
+
found: (e) => `Código lido: ${e}`,
|
|
16
|
+
torchOn: "Desligar lanterna",
|
|
17
|
+
torchOff: "Ligar lanterna",
|
|
18
|
+
unsupported: "Este navegador não decodifica códigos de barras.",
|
|
19
|
+
retry: "Tentar de novo"
|
|
20
|
+
},
|
|
21
|
+
en: {
|
|
22
|
+
viewport: "Camera viewport",
|
|
23
|
+
scanning: "Looking for a code…",
|
|
24
|
+
paused: "Scanning paused",
|
|
25
|
+
opening: "Opening the camera…",
|
|
26
|
+
hint: "Point the camera at the code.",
|
|
27
|
+
found: (e) => `Code read: ${e}`,
|
|
28
|
+
torchOn: "Turn torch off",
|
|
29
|
+
torchOff: "Turn torch on",
|
|
30
|
+
unsupported: "This browser cannot decode barcodes.",
|
|
31
|
+
retry: "Try again"
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
function l({ onScan: l, formats: u, paused: d = !1, detector: f, intervalMs: p, repeatDelayMs: m, torch: h = !0, aspectRatio: g = 4 / 3, locale: _ = "pt-BR", footer: v, unsupported: y, onError: b, className: x, ...S }) {
|
|
35
|
+
let C = c[_], { videoRef: w, status: T, error: E, supported: D, scanning: O, result: k, torch: A, retry: j } = t({
|
|
36
|
+
formats: u,
|
|
37
|
+
paused: d,
|
|
38
|
+
detector: f,
|
|
39
|
+
intervalMs: p,
|
|
40
|
+
repeatDelayMs: m,
|
|
41
|
+
onScan: l,
|
|
42
|
+
onError: b
|
|
43
|
+
});
|
|
44
|
+
if (!D) return /* @__PURE__ */ i("div", {
|
|
45
|
+
className: e(n.scanner, x),
|
|
46
|
+
...S,
|
|
47
|
+
children: [/* @__PURE__ */ r("p", {
|
|
48
|
+
className: n.notice,
|
|
49
|
+
children: C.unsupported
|
|
50
|
+
}), y]
|
|
51
|
+
});
|
|
52
|
+
let M = k ? C.found(k.rawValue) : d ? C.paused : O ? C.scanning : C.opening;
|
|
53
|
+
return /* @__PURE__ */ i("div", {
|
|
54
|
+
className: e(n.scanner, x),
|
|
55
|
+
...S,
|
|
56
|
+
children: [
|
|
57
|
+
/* @__PURE__ */ i("div", {
|
|
58
|
+
className: n.viewport,
|
|
59
|
+
style: { aspectRatio: String(g) },
|
|
60
|
+
"aria-label": C.viewport,
|
|
61
|
+
role: "group",
|
|
62
|
+
children: [
|
|
63
|
+
/* @__PURE__ */ r("video", {
|
|
64
|
+
ref: w,
|
|
65
|
+
className: n.video,
|
|
66
|
+
muted: !0,
|
|
67
|
+
playsInline: !0,
|
|
68
|
+
"aria-hidden": "true"
|
|
69
|
+
}),
|
|
70
|
+
T === "ready" && /* @__PURE__ */ r("div", {
|
|
71
|
+
className: n.frame,
|
|
72
|
+
"aria-hidden": "true",
|
|
73
|
+
children: /* @__PURE__ */ r("span", { className: e(n.laser, O && n.laserActive) })
|
|
74
|
+
}),
|
|
75
|
+
h && A.supported && /* @__PURE__ */ r("button", {
|
|
76
|
+
type: "button",
|
|
77
|
+
className: n.torch,
|
|
78
|
+
onClick: () => void A.toggle(),
|
|
79
|
+
"aria-pressed": A.on,
|
|
80
|
+
"aria-label": A.on ? C.torchOn : C.torchOff,
|
|
81
|
+
children: A.on ? /* @__PURE__ */ r(o, {
|
|
82
|
+
size: 18,
|
|
83
|
+
"aria-hidden": !0
|
|
84
|
+
}) : /* @__PURE__ */ r(a, {
|
|
85
|
+
size: 18,
|
|
86
|
+
"aria-hidden": !0
|
|
87
|
+
})
|
|
88
|
+
}),
|
|
89
|
+
E && /* @__PURE__ */ i("div", {
|
|
90
|
+
className: n.overlay,
|
|
91
|
+
role: "alert",
|
|
92
|
+
children: [/* @__PURE__ */ r("p", {
|
|
93
|
+
className: n.overlayText,
|
|
94
|
+
children: E.message
|
|
95
|
+
}), /* @__PURE__ */ r("button", {
|
|
96
|
+
type: "button",
|
|
97
|
+
className: n.retry,
|
|
98
|
+
onClick: j,
|
|
99
|
+
children: C.retry
|
|
100
|
+
})]
|
|
101
|
+
})
|
|
102
|
+
]
|
|
103
|
+
}),
|
|
104
|
+
!E && /* @__PURE__ */ i("p", {
|
|
105
|
+
className: n.status,
|
|
106
|
+
role: "status",
|
|
107
|
+
children: [/* @__PURE__ */ r(s, {
|
|
108
|
+
size: 14,
|
|
109
|
+
"aria-hidden": !0,
|
|
110
|
+
className: n.statusIcon
|
|
111
|
+
}), M]
|
|
112
|
+
}),
|
|
113
|
+
v ? /* @__PURE__ */ r("div", {
|
|
114
|
+
className: n.footer,
|
|
115
|
+
children: v
|
|
116
|
+
}) : null
|
|
117
|
+
]
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
export { l as BarcodeScanner };
|
|
122
|
+
|
|
123
|
+
//# sourceMappingURL=BarcodeScanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BarcodeScanner.js","names":[],"sources":["../../../src/components/BarcodeScanner/BarcodeScanner.tsx"],"sourcesContent":["import { Flashlight, FlashlightOff, ScanLine } from \"lucide-react\";\nimport { type HTMLAttributes, type ReactNode } from \"react\";\n\nimport { useBarcodeScanner } from \"@/capture/use-barcode-scanner\";\nimport type { BarcodeDetectorLike, BarcodeFormat, BarcodeScanResult } from \"@/capture/barcode\";\nimport { cn } from \"@/utils/cn\";\n\nimport styles from \"./BarcodeScanner.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\" | \"onError\";\n\nexport interface BarcodeScannerProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n OverriddenDomProps\n> {\n /** Called for every accepted read — repeats of the same value are suppressed. */\n onScan: (result: BarcodeScanResult) => void;\n /** Symbologies to look for. Defaults to QR + EAN-13 + Code 128. */\n formats?: readonly BarcodeFormat[];\n /** Stop looking without releasing the camera — set it while a confirmation is open. */\n paused?: boolean;\n /** A decoder to use instead of the native one, for Safari and Firefox. */\n detector?: BarcodeDetectorLike;\n /** How often a frame is examined, in ms. Default 200. */\n intervalMs?: number;\n /** Ignore the same value again for this long, in ms. Default 2500. */\n repeatDelayMs?: number;\n /** Offer the torch toggle when the camera has a lamp. Default `true`. */\n torch?: boolean;\n /** Viewport aspect ratio, `width / height`. Default `4 / 3`. */\n aspectRatio?: number;\n /** Locale for the labels. Default `\"pt-BR\"`. */\n locale?: \"pt-BR\" | \"en\";\n /** Under the viewport — an instruction, the code just read, a manual-entry link. */\n footer?: ReactNode;\n /**\n * Rendered instead of the camera when there is no decoder.\n *\n * Worth filling in: on iOS and Firefox this is the **only** thing the user sees, so\n * the fallback is usually a plain text field for typing the code.\n */\n unsupported?: ReactNode;\n /** A frame the engine refused to decode. Routine and usually transient. */\n onError?: (error: unknown) => void;\n}\n\nconst STRINGS = {\n \"pt-BR\": {\n viewport: \"Visor da câmera\",\n scanning: \"Procurando código…\",\n paused: \"Leitura pausada\",\n opening: \"Abrindo a câmera…\",\n hint: \"Aponte a câmera para o código.\",\n found: (value: string) => `Código lido: ${value}`,\n torchOn: \"Desligar lanterna\",\n torchOff: \"Ligar lanterna\",\n unsupported: \"Este navegador não decodifica códigos de barras.\",\n retry: \"Tentar de novo\",\n },\n en: {\n viewport: \"Camera viewport\",\n scanning: \"Looking for a code…\",\n paused: \"Scanning paused\",\n opening: \"Opening the camera…\",\n hint: \"Point the camera at the code.\",\n found: (value: string) => `Code read: ${value}`,\n torchOn: \"Turn torch off\",\n torchOff: \"Turn torch on\",\n unsupported: \"This browser cannot decode barcodes.\",\n retry: \"Try again\",\n },\n} as const;\n\n/**\n * Point the camera at a barcode and get its value.\n *\n * The counterpart to {@link QRCode}, which only encodes. Under it are\n * `useBarcodeScanner` (the detect loop and repeat suppression), `useCameraStream` (the\n * stream and its classified errors) and `useTorch`.\n *\n * **Mounting this opens the camera**, so mount it when the user asks to scan rather\n * than on a page that happens to contain a scanner: a permission prompt nobody\n * provoked is the surest way to earn a permanent block, and after that `getUserMedia`\n * rejects without ever prompting again. The usual shape is a button that reveals it.\n *\n * The `unsupported` slot is not a nicety. `BarcodeDetector` is Chromium-only — absent\n * on Firefox, on every browser on iOS, and on Chromium for Windows and Linux — so on a\n * large share of real devices the fallback *is* the feature. Give it a text field, or\n * inject a polyfill through `detector`.\n *\n * The preview itself is `aria-hidden`: a live camera frame has nothing to announce and\n * no audio to caption, so what a screen reader gets is the `role=\"status\"` line, which\n * says whether scanning is running and reads out each accepted code.\n *\n * @example\n * <BarcodeScanner\n * formats={[\"ean_13\"]}\n * onScan={({ rawValue }) => addToCart(rawValue)}\n * footer={<small>Aponte para o código de barras da embalagem.</small>}\n * unsupported={<ManualCodeInput onSubmit={addToCart} />}\n * />\n */\nexport function BarcodeScanner({\n onScan,\n formats,\n paused = false,\n detector,\n intervalMs,\n repeatDelayMs,\n torch = true,\n aspectRatio = 4 / 3,\n locale = \"pt-BR\",\n footer,\n unsupported,\n onError,\n className,\n ...rest\n}: BarcodeScannerProps) {\n const strings = STRINGS[locale];\n const {\n videoRef,\n status,\n error,\n supported,\n scanning,\n result,\n torch: lamp,\n retry,\n } = useBarcodeScanner({\n formats,\n paused,\n detector,\n intervalMs,\n repeatDelayMs,\n onScan,\n onError,\n });\n\n if (!supported) {\n return (\n <div className={cn(styles.scanner, className)} {...rest}>\n <p className={styles.notice}>{strings.unsupported}</p>\n {unsupported}\n </div>\n );\n }\n\n const message = result\n ? strings.found(result.rawValue)\n : paused\n ? strings.paused\n : scanning\n ? strings.scanning\n : strings.opening;\n\n return (\n <div className={cn(styles.scanner, className)} {...rest}>\n <div\n className={styles.viewport}\n style={{ aspectRatio: String(aspectRatio) }}\n aria-label={strings.viewport}\n role=\"group\"\n >\n <video\n ref={videoRef}\n className={styles.video}\n muted\n playsInline\n aria-hidden=\"true\"\n />\n\n {status === \"ready\" && (\n <div className={styles.frame} aria-hidden=\"true\">\n <span className={cn(styles.laser, scanning && styles.laserActive)} />\n </div>\n )}\n\n {torch && lamp.supported && (\n <button\n type=\"button\"\n className={styles.torch}\n onClick={() => void lamp.toggle()}\n aria-pressed={lamp.on}\n aria-label={lamp.on ? strings.torchOn : strings.torchOff}\n >\n {lamp.on ? (\n <FlashlightOff size={18} aria-hidden />\n ) : (\n <Flashlight size={18} aria-hidden />\n )}\n </button>\n )}\n\n {error && (\n <div className={styles.overlay} role=\"alert\">\n <p className={styles.overlayText}>{error.message}</p>\n <button type=\"button\" className={styles.retry} onClick={retry}>\n {strings.retry}\n </button>\n </div>\n )}\n </div>\n\n {!error && (\n <p className={styles.status} role=\"status\">\n <ScanLine size={14} aria-hidden className={styles.statusIcon} />\n {message}\n </p>\n )}\n\n {footer ? <div className={styles.footer}>{footer}</div> : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;AA+CA,IAAM,IAAU;CACZ,SAAS;EACL,UAAU;EACV,UAAU;EACV,QAAQ;EACR,SAAS;EACT,MAAM;EACN,QAAQ,MAAkB,gBAAgB;EAC1C,SAAS;EACT,UAAU;EACV,aAAa;EACb,OAAO;CACX;CACA,IAAI;EACA,UAAU;EACV,UAAU;EACV,QAAQ;EACR,SAAS;EACT,MAAM;EACN,QAAQ,MAAkB,cAAc;EACxC,SAAS;EACT,UAAU;EACV,aAAa;EACb,OAAO;CACX;AACJ;AA+BA,SAAgB,EAAe,EAC3B,WACA,YACA,YAAS,IACT,aACA,eACA,kBACA,WAAQ,IACR,iBAAc,IAAI,GAClB,YAAS,SACT,WACA,gBACA,YACA,cACA,GAAG,KACiB;CACpB,IAAM,IAAU,EAAQ,IAClB,EACF,aACA,WACA,UACA,cACA,aACA,WACA,OAAO,GACP,aACA,EAAkB;EAClB;EACA;EACA;EACA;EACA;EACA;EACA;CACJ,CAAC;CAED,IAAI,CAAC,GACD,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,SAAS,CAAS;EAAG,GAAI;YAAnD,CACI,kBAAC,KAAD;GAAG,WAAW,EAAO;aAAS,EAAQ;EAAe,CAAA,GACpD,CACA;;CAIb,IAAM,IAAU,IACV,EAAQ,MAAM,EAAO,QAAQ,IAC7B,IACE,EAAQ,SACR,IACE,EAAQ,WACR,EAAQ;CAElB,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,SAAS,CAAS;EAAG,GAAI;YAAnD;GACI,kBAAC,OAAD;IACI,WAAW,EAAO;IAClB,OAAO,EAAE,aAAa,OAAO,CAAW,EAAE;IAC1C,cAAY,EAAQ;IACpB,MAAK;cAJT;KAMI,kBAAC,SAAD;MACI,KAAK;MACL,WAAW,EAAO;MAClB,OAAA;MACA,aAAA;MACA,eAAY;KACf,CAAA;KAEA,MAAW,WACR,kBAAC,OAAD;MAAK,WAAW,EAAO;MAAO,eAAY;gBACtC,kBAAC,QAAD,EAAM,WAAW,EAAG,EAAO,OAAO,KAAY,EAAO,WAAW,EAAI,CAAA;KACnE,CAAA;KAGR,KAAS,EAAK,aACX,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,eAAe,KAAK,EAAK,OAAO;MAChC,gBAAc,EAAK;MACnB,cAAY,EAAK,KAAK,EAAQ,UAAU,EAAQ;gBAE/C,EAAK,KACF,kBAAC,GAAD;OAAe,MAAM;OAAI,eAAA;MAAa,CAAA,IAEtC,kBAAC,GAAD;OAAY,MAAM;OAAI,eAAA;MAAa,CAAA;KAEnC,CAAA;KAGX,KACG,kBAAC,OAAD;MAAK,WAAW,EAAO;MAAS,MAAK;gBAArC,CACI,kBAAC,KAAD;OAAG,WAAW,EAAO;iBAAc,EAAM;MAAW,CAAA,GACpD,kBAAC,UAAD;OAAQ,MAAK;OAAS,WAAW,EAAO;OAAO,SAAS;iBACnD,EAAQ;MACL,CAAA,CACP;;IAER;;GAEJ,CAAC,KACE,kBAAC,KAAD;IAAG,WAAW,EAAO;IAAQ,MAAK;cAAlC,CACI,kBAAC,GAAD;KAAU,MAAM;KAAI,eAAA;KAAY,WAAW,EAAO;IAAa,CAAA,GAC9D,CACF;;GAGN,IAAS,kBAAC,OAAD;IAAK,WAAW,EAAO;cAAS;GAAY,CAAA,IAAI;EACzD;;AAEb"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=`tempest_scanner_pIsey`,t=`tempest_viewport_PA5gg`,n=`tempest_video_vxiea`,r=`tempest_frame_sWB0M`,i=`tempest_laser_wloBH`,a=`tempest_laserActive_cCUwf`,o=`tempest_tempest-barcode-sweep_UmIe5`,s=`tempest_torch_aw7tt`,c=`tempest_retry_3K88V`,l=`tempest_overlay_ZWMCa`,u=`tempest_overlayText_jBln2`,d=`tempest_status_9eKPi`,f=`tempest_statusIcon_k2Hmu`,p=`tempest_notice_ELics`,m=`tempest_footer_6SO7U`,h={scanner:e,viewport:t,video:n,frame:r,laser:i,laserActive:a,tempestBarcodeSweep:o,torch:s,retry:c,overlay:l,overlayText:u,status:d,statusIcon:f,notice:p,footer:m};exports.default=h,exports.footer=m,exports.frame=r,exports.laser=i,exports.laserActive=a,exports.notice=p,exports.overlay=l,exports.overlayText=u,exports.retry=c,exports.scanner=e,exports.status=d,exports.statusIcon=f,exports.tempestBarcodeSweep=o,exports.torch=s,exports.video=n,exports.viewport=t;
|
|
2
|
+
//# sourceMappingURL=BarcodeScanner.module.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BarcodeScanner.module.cjs","names":[],"sources":["../../../src/components/BarcodeScanner/BarcodeScanner.module.css"],"sourcesContent":[".scanner {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-2);\n min-width: 0;\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n}\n\n/*\n * The viewport is a fixed-ratio box and the video fills it with `object-fit: cover`,\n * because a camera hands back whatever aspect ratio the device felt like: letting the\n * element size itself makes the layout jump the moment the stream arrives, which on a\n * phone pushes the button the user was aiming at off the screen.\n */\n.viewport {\n position: relative;\n overflow: hidden;\n width: 100%;\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n background-color: var(--tempest-surface-3);\n}\n\n.video {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n/*\n * Corner brackets: they tell the user where to aim without hiding the frame, which a\n * full border or a dimming mask would. Eight background layers — a horizontal and a\n * vertical bar per corner — because a `linear-gradient` cannot draw an L.\n */\n.frame {\n position: absolute;\n inset: 12%;\n background-image:\n linear-gradient(var(--tempest-primary) 0 0), linear-gradient(var(--tempest-primary) 0 0),\n linear-gradient(var(--tempest-primary) 0 0), linear-gradient(var(--tempest-primary) 0 0),\n linear-gradient(var(--tempest-primary) 0 0), linear-gradient(var(--tempest-primary) 0 0),\n linear-gradient(var(--tempest-primary) 0 0), linear-gradient(var(--tempest-primary) 0 0);\n background-repeat: no-repeat;\n background-size:\n 22% 3px,\n 3px 22%,\n 22% 3px,\n 3px 22%,\n 22% 3px,\n 3px 22%,\n 22% 3px,\n 3px 22%;\n background-position:\n left top,\n left top,\n right top,\n right top,\n left bottom,\n left bottom,\n right bottom,\n right bottom;\n pointer-events: none;\n}\n\n.laser {\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n height: 2px;\n background-color: var(--tempest-primary);\n opacity: 0.35;\n}\n\n.laserActive {\n opacity: 0.9;\n animation: tempest-barcode-sweep 1.6s ease-in-out infinite alternate;\n}\n\n@keyframes tempest-barcode-sweep {\n from {\n top: 0;\n }\n to {\n top: calc(100% - 2px);\n }\n}\n\n/*\n * A sweeping line is the one animation here, and it is the first thing to drop: the\n * indicator that matters is the `role=\"status\"` text, so with reduced motion the line\n * simply sits still at the top of the frame.\n */\n@media (prefers-reduced-motion: reduce) {\n .laserActive {\n animation: none;\n }\n}\n\n.torch {\n position: absolute;\n top: var(--tempest-space-2);\n right: var(--tempest-space-2);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n.torch[aria-pressed=\"true\"] {\n border-color: var(--tempest-warning);\n background-color: var(--tempest-warning-bg);\n color: var(--tempest-warning-fg);\n}\n\n.torch:focus-visible,\n.retry:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: 2px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .torch {\n transition: none;\n }\n}\n\n/* Touch hit-slop — 44×44 without changing the visual footprint, as `Button` does. */\n@media (pointer: coarse) {\n .torch::after {\n content: \"\";\n position: absolute;\n inset: -8px;\n }\n}\n\n.overlay {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-2);\n padding: var(--tempest-space-3);\n background-color: var(--tempest-surface);\n text-align: center;\n}\n\n.overlayText {\n margin: 0;\n color: var(--tempest-text);\n font-size: var(--tempest-text-sm);\n}\n\n.retry {\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border: 1px solid transparent;\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n font: inherit;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-medium);\n cursor: pointer;\n}\n\n.status {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n margin: 0;\n min-height: 1.25em;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n\n.statusIcon {\n flex: 0 0 auto;\n}\n\n.notice {\n margin: 0;\n padding: var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n background-color: var(--tempest-surface);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n}\n\n.footer {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/components/BarcodeScanner/BarcodeScanner.module.css
|
|
2
|
+
var e = "tempest_scanner_pIsey", t = "tempest_viewport_PA5gg", n = "tempest_video_vxiea", r = "tempest_frame_sWB0M", i = "tempest_laser_wloBH", a = "tempest_laserActive_cCUwf", o = "tempest_tempest-barcode-sweep_UmIe5", s = "tempest_torch_aw7tt", c = "tempest_retry_3K88V", l = "tempest_overlay_ZWMCa", u = "tempest_overlayText_jBln2", d = "tempest_status_9eKPi", f = "tempest_statusIcon_k2Hmu", p = "tempest_notice_ELics", m = "tempest_footer_6SO7U", h = {
|
|
3
|
+
scanner: e,
|
|
4
|
+
viewport: t,
|
|
5
|
+
video: n,
|
|
6
|
+
frame: r,
|
|
7
|
+
laser: i,
|
|
8
|
+
laserActive: a,
|
|
9
|
+
tempestBarcodeSweep: o,
|
|
10
|
+
torch: s,
|
|
11
|
+
retry: c,
|
|
12
|
+
overlay: l,
|
|
13
|
+
overlayText: u,
|
|
14
|
+
status: d,
|
|
15
|
+
statusIcon: f,
|
|
16
|
+
notice: p,
|
|
17
|
+
footer: m
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
export { h as default, m as footer, r as frame, i as laser, a as laserActive, p as notice, l as overlay, u as overlayText, c as retry, e as scanner, d as status, f as statusIcon, o as tempestBarcodeSweep, s as torch, n as video, t as viewport };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=BarcodeScanner.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BarcodeScanner.module.js","names":[],"sources":["../../../src/components/BarcodeScanner/BarcodeScanner.module.css"],"sourcesContent":[".scanner {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-2);\n min-width: 0;\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n}\n\n/*\n * The viewport is a fixed-ratio box and the video fills it with `object-fit: cover`,\n * because a camera hands back whatever aspect ratio the device felt like: letting the\n * element size itself makes the layout jump the moment the stream arrives, which on a\n * phone pushes the button the user was aiming at off the screen.\n */\n.viewport {\n position: relative;\n overflow: hidden;\n width: 100%;\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n background-color: var(--tempest-surface-3);\n}\n\n.video {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n/*\n * Corner brackets: they tell the user where to aim without hiding the frame, which a\n * full border or a dimming mask would. Eight background layers — a horizontal and a\n * vertical bar per corner — because a `linear-gradient` cannot draw an L.\n */\n.frame {\n position: absolute;\n inset: 12%;\n background-image:\n linear-gradient(var(--tempest-primary) 0 0), linear-gradient(var(--tempest-primary) 0 0),\n linear-gradient(var(--tempest-primary) 0 0), linear-gradient(var(--tempest-primary) 0 0),\n linear-gradient(var(--tempest-primary) 0 0), linear-gradient(var(--tempest-primary) 0 0),\n linear-gradient(var(--tempest-primary) 0 0), linear-gradient(var(--tempest-primary) 0 0);\n background-repeat: no-repeat;\n background-size:\n 22% 3px,\n 3px 22%,\n 22% 3px,\n 3px 22%,\n 22% 3px,\n 3px 22%,\n 22% 3px,\n 3px 22%;\n background-position:\n left top,\n left top,\n right top,\n right top,\n left bottom,\n left bottom,\n right bottom,\n right bottom;\n pointer-events: none;\n}\n\n.laser {\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n height: 2px;\n background-color: var(--tempest-primary);\n opacity: 0.35;\n}\n\n.laserActive {\n opacity: 0.9;\n animation: tempest-barcode-sweep 1.6s ease-in-out infinite alternate;\n}\n\n@keyframes tempest-barcode-sweep {\n from {\n top: 0;\n }\n to {\n top: calc(100% - 2px);\n }\n}\n\n/*\n * A sweeping line is the one animation here, and it is the first thing to drop: the\n * indicator that matters is the `role=\"status\"` text, so with reduced motion the line\n * simply sits still at the top of the frame.\n */\n@media (prefers-reduced-motion: reduce) {\n .laserActive {\n animation: none;\n }\n}\n\n.torch {\n position: absolute;\n top: var(--tempest-space-2);\n right: var(--tempest-space-2);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: var(--tempest-space-2);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n.torch[aria-pressed=\"true\"] {\n border-color: var(--tempest-warning);\n background-color: var(--tempest-warning-bg);\n color: var(--tempest-warning-fg);\n}\n\n.torch:focus-visible,\n.retry:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: 2px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .torch {\n transition: none;\n }\n}\n\n/* Touch hit-slop — 44×44 without changing the visual footprint, as `Button` does. */\n@media (pointer: coarse) {\n .torch::after {\n content: \"\";\n position: absolute;\n inset: -8px;\n }\n}\n\n.overlay {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-2);\n padding: var(--tempest-space-3);\n background-color: var(--tempest-surface);\n text-align: center;\n}\n\n.overlayText {\n margin: 0;\n color: var(--tempest-text);\n font-size: var(--tempest-text-sm);\n}\n\n.retry {\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border: 1px solid transparent;\n border-radius: var(--tempest-radius-md);\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n font: inherit;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-medium);\n cursor: pointer;\n}\n\n.status {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-1);\n margin: 0;\n min-height: 1.25em;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n\n.statusIcon {\n flex: 0 0 auto;\n}\n\n.notice {\n margin: 0;\n padding: var(--tempest-space-3);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n background-color: var(--tempest-surface);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n}\n\n.footer {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BottomNavigation.module.cjs","names":[],"sources":["../../../src/components/BottomNavigation/BottomNavigation.module.css"],"sourcesContent":[".bar {\n display: flex;\n width: 100%;\n background: var(--tempest-surface);\n border-top: 1px solid var(--tempest-border);\n padding-bottom: env(safe-area-inset-bottom, 0);\n font-family: var(--tempest-font-sans);\n}\n\n.item {\n flex: 1 1 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 2px;\n padding: 8px 4px;\n background: none;\n border: 0;\n cursor: pointer;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n transition: color var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n min-height: 56px;\n}\n\n.item:hover {\n color: var(--tempest-text);\n}\n\n.item.active {\n color: var(--tempest-primary);\n}\n\n.item:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.iconWrap {\n position: relative;\n display: inline-flex;\n}\n\n.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n font-size: 20px;\n}\n\n.badge {\n position: absolute;\n top: -4px;\n right: -8px;\n min-width: 14px;\n height: 14px;\n padding: 0 4px;\n border-radius: var(--tempest-radius-full);\n background: var(--tempest-danger-solid);\n color: var(--tempest-danger-on
|
|
1
|
+
{"version":3,"file":"BottomNavigation.module.cjs","names":[],"sources":["../../../src/components/BottomNavigation/BottomNavigation.module.css"],"sourcesContent":[".bar {\n display: flex;\n width: 100%;\n background: var(--tempest-surface);\n border-top: 1px solid var(--tempest-border);\n padding-bottom: env(safe-area-inset-bottom, 0);\n font-family: var(--tempest-font-sans);\n}\n\n.item {\n flex: 1 1 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 2px;\n padding: 8px 4px;\n background: none;\n border: 0;\n cursor: pointer;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n transition: color var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n min-height: 56px;\n}\n\n.item:hover {\n color: var(--tempest-text);\n}\n\n.item.active {\n color: var(--tempest-primary);\n}\n\n.item:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.iconWrap {\n position: relative;\n display: inline-flex;\n}\n\n.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n font-size: 20px;\n}\n\n.badge {\n position: absolute;\n top: -4px;\n right: -8px;\n min-width: 14px;\n height: 14px;\n padding: 0 4px;\n border-radius: var(--tempest-radius-full);\n background: var(--tempest-danger-solid);\n color: var(--tempest-danger-on-solid);\n font-size: 10px;\n line-height: 14px;\n text-align: center;\n font-weight: var(--tempest-weight-semibold);\n}\n\n.label {\n line-height: 1;\n}\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BottomNavigation.module.js","names":[],"sources":["../../../src/components/BottomNavigation/BottomNavigation.module.css"],"sourcesContent":[".bar {\n display: flex;\n width: 100%;\n background: var(--tempest-surface);\n border-top: 1px solid var(--tempest-border);\n padding-bottom: env(safe-area-inset-bottom, 0);\n font-family: var(--tempest-font-sans);\n}\n\n.item {\n flex: 1 1 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 2px;\n padding: 8px 4px;\n background: none;\n border: 0;\n cursor: pointer;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n transition: color var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n min-height: 56px;\n}\n\n.item:hover {\n color: var(--tempest-text);\n}\n\n.item.active {\n color: var(--tempest-primary);\n}\n\n.item:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.iconWrap {\n position: relative;\n display: inline-flex;\n}\n\n.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n font-size: 20px;\n}\n\n.badge {\n position: absolute;\n top: -4px;\n right: -8px;\n min-width: 14px;\n height: 14px;\n padding: 0 4px;\n border-radius: var(--tempest-radius-full);\n background: var(--tempest-danger-solid);\n color: var(--tempest-danger-on
|
|
1
|
+
{"version":3,"file":"BottomNavigation.module.js","names":[],"sources":["../../../src/components/BottomNavigation/BottomNavigation.module.css"],"sourcesContent":[".bar {\n display: flex;\n width: 100%;\n background: var(--tempest-surface);\n border-top: 1px solid var(--tempest-border);\n padding-bottom: env(safe-area-inset-bottom, 0);\n font-family: var(--tempest-font-sans);\n}\n\n.item {\n flex: 1 1 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 2px;\n padding: 8px 4px;\n background: none;\n border: 0;\n cursor: pointer;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-2xs);\n transition: color var(--tempest-duration-fast, 120ms) var(--tempest-ease-out, ease-out);\n min-height: 56px;\n}\n\n.item:hover {\n color: var(--tempest-text);\n}\n\n.item.active {\n color: var(--tempest-primary);\n}\n\n.item:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.iconWrap {\n position: relative;\n display: inline-flex;\n}\n\n.icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n line-height: 1;\n font-size: 20px;\n}\n\n.badge {\n position: absolute;\n top: -4px;\n right: -8px;\n min-width: 14px;\n height: 14px;\n padding: 0 4px;\n border-radius: var(--tempest-radius-full);\n background: var(--tempest-danger-solid);\n color: var(--tempest-danger-on-solid);\n font-size: 10px;\n line-height: 14px;\n text-align: center;\n font-weight: var(--tempest-weight-semibold);\n}\n\n.label {\n line-height: 1;\n}\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.module.cjs","names":[],"sources":["../../../src/components/Button/Button.module.css"],"sourcesContent":[".button {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n border: 1px solid transparent;\n border-radius: var(--tempest-control-radius, var(--tempest-radius-md));\n font-family: var(--tempest-font-sans);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-none);\n letter-spacing: var(--tempest-tracking-normal);\n text-decoration: none;\n white-space: nowrap;\n user-select: none;\n transition: var(--tempest-transition-base);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .button:hover:not(:disabled) {\n box-shadow: var(--tempest-shadow-sm);\n }\n}\n\n.button:active:not(:disabled) {\n transform: translateY(1px);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:disabled {\n opacity: 0.55;\n box-shadow: none;\n}\n\n/* ============================================================\n * Variants — solid (default), soft, outline, ghost\n * Status colors: primary, secondary (neutral), danger, success\n * ============================================================ */\n\n/* primary — solid filled */\n.primary {\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n.primary:hover:not(:disabled) {\n background-color: var(--tempest-primary-hover);\n}\n.primary:active:not(:disabled) {\n background-color: var(--tempest-primary-active);\n}\n\n/* secondary — neutral surface */\n.secondary {\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n border-color: var(--tempest-border);\n}\n.secondary:hover:not(:disabled) {\n background-color: var(--tempest-surface-2);\n border-color: var(--tempest-border-strong);\n}\n\n/* danger — solid red */\n.danger {\n background-color: var(--tempest-danger-solid);\n color:
|
|
1
|
+
{"version":3,"file":"Button.module.cjs","names":[],"sources":["../../../src/components/Button/Button.module.css"],"sourcesContent":[".button {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n border: 1px solid transparent;\n border-radius: var(--tempest-control-radius, var(--tempest-radius-md));\n font-family: var(--tempest-font-sans);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-none);\n letter-spacing: var(--tempest-tracking-normal);\n text-decoration: none;\n white-space: nowrap;\n user-select: none;\n transition: var(--tempest-transition-base);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .button:hover:not(:disabled) {\n box-shadow: var(--tempest-shadow-sm);\n }\n}\n\n.button:active:not(:disabled) {\n transform: translateY(1px);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:disabled {\n opacity: 0.55;\n box-shadow: none;\n}\n\n/* ============================================================\n * Variants — solid (default), soft, outline, ghost\n * Status colors: primary, secondary (neutral), danger, success\n * ============================================================ */\n\n/* primary — solid filled */\n.primary {\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n.primary:hover:not(:disabled) {\n background-color: var(--tempest-primary-hover);\n}\n.primary:active:not(:disabled) {\n background-color: var(--tempest-primary-active);\n}\n\n/* secondary — neutral surface */\n.secondary {\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n border-color: var(--tempest-border);\n}\n.secondary:hover:not(:disabled) {\n background-color: var(--tempest-surface-2);\n border-color: var(--tempest-border-strong);\n}\n\n/* danger — solid red */\n.danger {\n background-color: var(--tempest-danger-solid);\n color: var(--tempest-danger-on-solid);\n}\n.danger:hover:not(:disabled) {\n background-color: var(--tempest-danger-hover);\n}\n\n/* success — solid green */\n.success {\n background-color: var(--tempest-success-solid);\n color: var(--tempest-success-on-solid);\n}\n.success:hover:not(:disabled) {\n filter: brightness(0.92);\n}\n\n/* ghost — transparent, hover surface */\n.ghost {\n background-color: transparent;\n color: var(--tempest-text);\n box-shadow: none;\n}\n.ghost:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n box-shadow: none;\n}\n.ghost:active:not(:disabled) {\n background-color: var(--tempest-surface-2);\n box-shadow: none;\n}\n\n/* soft — tinted background, primary text */\n.soft {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n box-shadow: none;\n}\n.soft:hover:not(:disabled) {\n background-color: var(--tempest-primary-soft-hover);\n}\n\n/* outline — bordered, transparent fill */\n.outline {\n background-color: transparent;\n color: var(--tempest-primary);\n border-color: var(--tempest-primary);\n box-shadow: none;\n}\n/*\n * The text colour has to move with the background.\n *\n * `.outline` sets `color: var(--tempest-primary)` over a transparent background,\n * where it is fine. This rule tints the background to `--tempest-primary-soft` and,\n * without restating the colour, leaves the fill token sitting on the tint: **4.38:1 in\n * the light theme and 4.28:1 in dark**, both under the 4.5:1 floor for text — so the\n * label got harder to read at the exact moment the pointer was on it.\n *\n * `--tempest-primary-on-soft` is the token for this ground (6.18:1 / 8.72:1) and\n * `docs/styles.md` has documented the rule all along. A rule that changes only the\n * background of a coloured element is the shape of this bug; the other eight places in\n * the SDK that tint a background inherit `--tempest-text`, `--tempest-text-muted` or\n * `--tempest-primary-on-soft` and are fine.\n */\n.outline:hover:not(:disabled) {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n\n/* link — text-only, underline on hover */\n.link {\n background-color: transparent;\n color: var(--tempest-primary);\n box-shadow: none;\n padding-left: 0;\n padding-right: 0;\n border-radius: var(--tempest-radius-sm);\n}\n.link:hover:not(:disabled) {\n color: var(--tempest-primary-hover);\n text-decoration: underline;\n background: none;\n box-shadow: none;\n}\n\n/* ============================================================\n * Sizes — token-driven, density-aware\n * ============================================================ */\n.xs {\n height: var(--tempest-control-height-xs);\n padding: 0 var(--tempest-control-padding-xs);\n font-size: var(--tempest-control-font-xs);\n}\n.sm {\n height: var(--tempest-control-height-sm);\n padding: 0 var(--tempest-control-padding-sm);\n font-size: var(--tempest-control-font-sm);\n}\n.md {\n height: var(--tempest-control-height-md);\n padding: 0 var(--tempest-control-padding-md);\n font-size: var(--tempest-control-font-md);\n}\n.lg {\n height: var(--tempest-control-height-lg);\n padding: 0 var(--tempest-control-padding-lg);\n font-size: var(--tempest-control-font-lg);\n}\n.xl {\n height: var(--tempest-control-height-xl);\n padding: 0 var(--tempest-control-padding-xl);\n font-size: var(--tempest-control-font-xl);\n}\n\n/* Square (icon-only) — width matches height */\n.iconOnly {\n position: relative;\n}\n.iconOnly.xs {\n width: var(--tempest-control-height-xs);\n padding: 0;\n}\n.iconOnly.sm {\n width: var(--tempest-control-height-sm);\n padding: 0;\n}\n.iconOnly.md {\n width: var(--tempest-control-height-md);\n padding: 0;\n}\n.iconOnly.lg {\n width: var(--tempest-control-height-lg);\n padding: 0;\n}\n.iconOnly.xl {\n width: var(--tempest-control-height-xl);\n padding: 0;\n}\n\n/* Touch hit-slop — extend tap target to a min 44×44 on coarse pointers\n * for the smallest icon-only sizes, without changing visual footprint. */\n@media (pointer: coarse) {\n .iconOnly.xs::after,\n .iconOnly.sm::after {\n content: \"\";\n position: absolute;\n inset: -8px;\n }\n}\n\n/* Pill shape */\n.pill {\n border-radius: var(--tempest-radius-full);\n}\n\n.fullWidth {\n width: 100%;\n}\n\n/*\n * Lay out leftIcon + label + rightIcon as one centered, gapped row. Without\n * this the three are inline inside a single span (no gap, icon baseline-\n * aligned instead of centered), which reads as off-center — most visible on\n * fullWidth buttons where the label has room to drift.\n */\n.content {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n min-width: 0;\n}\n\n.loading .hiddenText {\n visibility: hidden;\n}\n\n.spinner {\n position: absolute;\n animation: tempest-spin 0.8s linear infinite;\n}\n\n@keyframes tempest-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.module.js","names":[],"sources":["../../../src/components/Button/Button.module.css"],"sourcesContent":[".button {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n border: 1px solid transparent;\n border-radius: var(--tempest-control-radius, var(--tempest-radius-md));\n font-family: var(--tempest-font-sans);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-none);\n letter-spacing: var(--tempest-tracking-normal);\n text-decoration: none;\n white-space: nowrap;\n user-select: none;\n transition: var(--tempest-transition-base);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .button:hover:not(:disabled) {\n box-shadow: var(--tempest-shadow-sm);\n }\n}\n\n.button:active:not(:disabled) {\n transform: translateY(1px);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:disabled {\n opacity: 0.55;\n box-shadow: none;\n}\n\n/* ============================================================\n * Variants — solid (default), soft, outline, ghost\n * Status colors: primary, secondary (neutral), danger, success\n * ============================================================ */\n\n/* primary — solid filled */\n.primary {\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n.primary:hover:not(:disabled) {\n background-color: var(--tempest-primary-hover);\n}\n.primary:active:not(:disabled) {\n background-color: var(--tempest-primary-active);\n}\n\n/* secondary — neutral surface */\n.secondary {\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n border-color: var(--tempest-border);\n}\n.secondary:hover:not(:disabled) {\n background-color: var(--tempest-surface-2);\n border-color: var(--tempest-border-strong);\n}\n\n/* danger — solid red */\n.danger {\n background-color: var(--tempest-danger-solid);\n color:
|
|
1
|
+
{"version":3,"file":"Button.module.js","names":[],"sources":["../../../src/components/Button/Button.module.css"],"sourcesContent":[".button {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n border: 1px solid transparent;\n border-radius: var(--tempest-control-radius, var(--tempest-radius-md));\n font-family: var(--tempest-font-sans);\n font-weight: var(--tempest-weight-semibold);\n line-height: var(--tempest-leading-none);\n letter-spacing: var(--tempest-tracking-normal);\n text-decoration: none;\n white-space: nowrap;\n user-select: none;\n transition: var(--tempest-transition-base);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:focus-visible {\n outline: var(--tempest-focus-ring-width) solid var(--tempest-focus-ring-color);\n outline-offset: var(--tempest-focus-ring-offset);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .button:hover:not(:disabled) {\n box-shadow: var(--tempest-shadow-sm);\n }\n}\n\n.button:active:not(:disabled) {\n transform: translateY(1px);\n box-shadow: var(--tempest-shadow-xs);\n}\n\n.button:disabled {\n opacity: 0.55;\n box-shadow: none;\n}\n\n/* ============================================================\n * Variants — solid (default), soft, outline, ghost\n * Status colors: primary, secondary (neutral), danger, success\n * ============================================================ */\n\n/* primary — solid filled */\n.primary {\n background-color: var(--tempest-primary);\n color: var(--tempest-primary-foreground);\n}\n.primary:hover:not(:disabled) {\n background-color: var(--tempest-primary-hover);\n}\n.primary:active:not(:disabled) {\n background-color: var(--tempest-primary-active);\n}\n\n/* secondary — neutral surface */\n.secondary {\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n border-color: var(--tempest-border);\n}\n.secondary:hover:not(:disabled) {\n background-color: var(--tempest-surface-2);\n border-color: var(--tempest-border-strong);\n}\n\n/* danger — solid red */\n.danger {\n background-color: var(--tempest-danger-solid);\n color: var(--tempest-danger-on-solid);\n}\n.danger:hover:not(:disabled) {\n background-color: var(--tempest-danger-hover);\n}\n\n/* success — solid green */\n.success {\n background-color: var(--tempest-success-solid);\n color: var(--tempest-success-on-solid);\n}\n.success:hover:not(:disabled) {\n filter: brightness(0.92);\n}\n\n/* ghost — transparent, hover surface */\n.ghost {\n background-color: transparent;\n color: var(--tempest-text);\n box-shadow: none;\n}\n.ghost:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n box-shadow: none;\n}\n.ghost:active:not(:disabled) {\n background-color: var(--tempest-surface-2);\n box-shadow: none;\n}\n\n/* soft — tinted background, primary text */\n.soft {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n box-shadow: none;\n}\n.soft:hover:not(:disabled) {\n background-color: var(--tempest-primary-soft-hover);\n}\n\n/* outline — bordered, transparent fill */\n.outline {\n background-color: transparent;\n color: var(--tempest-primary);\n border-color: var(--tempest-primary);\n box-shadow: none;\n}\n/*\n * The text colour has to move with the background.\n *\n * `.outline` sets `color: var(--tempest-primary)` over a transparent background,\n * where it is fine. This rule tints the background to `--tempest-primary-soft` and,\n * without restating the colour, leaves the fill token sitting on the tint: **4.38:1 in\n * the light theme and 4.28:1 in dark**, both under the 4.5:1 floor for text — so the\n * label got harder to read at the exact moment the pointer was on it.\n *\n * `--tempest-primary-on-soft` is the token for this ground (6.18:1 / 8.72:1) and\n * `docs/styles.md` has documented the rule all along. A rule that changes only the\n * background of a coloured element is the shape of this bug; the other eight places in\n * the SDK that tint a background inherit `--tempest-text`, `--tempest-text-muted` or\n * `--tempest-primary-on-soft` and are fine.\n */\n.outline:hover:not(:disabled) {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n\n/* link — text-only, underline on hover */\n.link {\n background-color: transparent;\n color: var(--tempest-primary);\n box-shadow: none;\n padding-left: 0;\n padding-right: 0;\n border-radius: var(--tempest-radius-sm);\n}\n.link:hover:not(:disabled) {\n color: var(--tempest-primary-hover);\n text-decoration: underline;\n background: none;\n box-shadow: none;\n}\n\n/* ============================================================\n * Sizes — token-driven, density-aware\n * ============================================================ */\n.xs {\n height: var(--tempest-control-height-xs);\n padding: 0 var(--tempest-control-padding-xs);\n font-size: var(--tempest-control-font-xs);\n}\n.sm {\n height: var(--tempest-control-height-sm);\n padding: 0 var(--tempest-control-padding-sm);\n font-size: var(--tempest-control-font-sm);\n}\n.md {\n height: var(--tempest-control-height-md);\n padding: 0 var(--tempest-control-padding-md);\n font-size: var(--tempest-control-font-md);\n}\n.lg {\n height: var(--tempest-control-height-lg);\n padding: 0 var(--tempest-control-padding-lg);\n font-size: var(--tempest-control-font-lg);\n}\n.xl {\n height: var(--tempest-control-height-xl);\n padding: 0 var(--tempest-control-padding-xl);\n font-size: var(--tempest-control-font-xl);\n}\n\n/* Square (icon-only) — width matches height */\n.iconOnly {\n position: relative;\n}\n.iconOnly.xs {\n width: var(--tempest-control-height-xs);\n padding: 0;\n}\n.iconOnly.sm {\n width: var(--tempest-control-height-sm);\n padding: 0;\n}\n.iconOnly.md {\n width: var(--tempest-control-height-md);\n padding: 0;\n}\n.iconOnly.lg {\n width: var(--tempest-control-height-lg);\n padding: 0;\n}\n.iconOnly.xl {\n width: var(--tempest-control-height-xl);\n padding: 0;\n}\n\n/* Touch hit-slop — extend tap target to a min 44×44 on coarse pointers\n * for the smallest icon-only sizes, without changing visual footprint. */\n@media (pointer: coarse) {\n .iconOnly.xs::after,\n .iconOnly.sm::after {\n content: \"\";\n position: absolute;\n inset: -8px;\n }\n}\n\n/* Pill shape */\n.pill {\n border-radius: var(--tempest-radius-full);\n}\n\n.fullWidth {\n width: 100%;\n}\n\n/*\n * Lay out leftIcon + label + rightIcon as one centered, gapped row. Without\n * this the three are inline inside a single span (no gap, icon baseline-\n * aligned instead of centered), which reads as off-center — most visible on\n * fullWidth buttons where the label has room to drift.\n */\n.content {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-control-gap, var(--tempest-space-2));\n min-width: 0;\n}\n\n.loading .hiddenText {\n visibility: hidden;\n}\n\n.spinner {\n position: absolute;\n animation: tempest-spin 0.8s linear infinite;\n}\n\n@keyframes tempest-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Combobox.module.cjs","names":[],"sources":["../../../src/components/Combobox/Combobox.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n width: 100%;\n font-family: var(--tempest-font-sans);\n position: relative;\n}\n\n.label {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n line-height: var(--tempest-leading-snug);\n}\n\n.field {\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.input {\n width: 100%;\n height: var(--tempest-control-height-md);\n padding: 0 32px 0 var(--tempest-control-padding-md);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-control-radius);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: var(--tempest-control-font-md);\n box-shadow: var(--tempest-shadow-xs);\n transition: var(--tempest-transition-color), var(--tempest-transition-shadow);\n}\n\n.input::placeholder {\n color: var(--tempest-text-subtle);\n}\n\n.input:focus {\n outline: none;\n border-color: var(--tempest-primary);\n box-shadow: 0 0 0 var(--tempest-focus-ring-width) var(--tempest-focus-ring-color);\n}\n\n.input:disabled {\n background-color: var(--tempest-surface);\n cursor: not-allowed;\n}\n\n.caret {\n position: absolute;\n right: 12px;\n pointer-events: none;\n color: var(--tempest-text-muted);\n}\n\n.menu {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n right: 0;\n z-index: var(--tempest-z-dropdown);\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n max-height: 280px;\n overflow-y: auto;\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n}\n\n.option {\n display: flex;\n align-items: center;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border-radius: var(--tempest-radius-sm);\n cursor: pointer;\n color: var(--tempest-text);\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .option:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.option.active {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-
|
|
1
|
+
{"version":3,"file":"Combobox.module.cjs","names":[],"sources":["../../../src/components/Combobox/Combobox.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n width: 100%;\n font-family: var(--tempest-font-sans);\n position: relative;\n}\n\n.label {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n line-height: var(--tempest-leading-snug);\n}\n\n.field {\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.input {\n width: 100%;\n height: var(--tempest-control-height-md);\n padding: 0 32px 0 var(--tempest-control-padding-md);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-control-radius);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: var(--tempest-control-font-md);\n box-shadow: var(--tempest-shadow-xs);\n transition: var(--tempest-transition-color), var(--tempest-transition-shadow);\n}\n\n.input::placeholder {\n color: var(--tempest-text-subtle);\n}\n\n.input:focus {\n outline: none;\n border-color: var(--tempest-primary);\n box-shadow: 0 0 0 var(--tempest-focus-ring-width) var(--tempest-focus-ring-color);\n}\n\n.input:disabled {\n background-color: var(--tempest-surface);\n cursor: not-allowed;\n}\n\n.caret {\n position: absolute;\n right: 12px;\n pointer-events: none;\n color: var(--tempest-text-muted);\n}\n\n.menu {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n right: 0;\n z-index: var(--tempest-z-dropdown);\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n max-height: 280px;\n overflow-y: auto;\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n}\n\n.option {\n display: flex;\n align-items: center;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border-radius: var(--tempest-radius-sm);\n cursor: pointer;\n color: var(--tempest-text);\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .option:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.option.active {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n\n.option.selected {\n font-weight: var(--tempest-weight-semibold);\n}\n\n.empty {\n padding: var(--tempest-space-3);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n text-align: center;\n}\n\n.helper {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-text-muted);\n}\n\n.errorText {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-danger);\n}\n\n.wrapper.error .input {\n border-color: var(--tempest-danger);\n}\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Combobox.module.js","names":[],"sources":["../../../src/components/Combobox/Combobox.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n width: 100%;\n font-family: var(--tempest-font-sans);\n position: relative;\n}\n\n.label {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n line-height: var(--tempest-leading-snug);\n}\n\n.field {\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.input {\n width: 100%;\n height: var(--tempest-control-height-md);\n padding: 0 32px 0 var(--tempest-control-padding-md);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-control-radius);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: var(--tempest-control-font-md);\n box-shadow: var(--tempest-shadow-xs);\n transition: var(--tempest-transition-color), var(--tempest-transition-shadow);\n}\n\n.input::placeholder {\n color: var(--tempest-text-subtle);\n}\n\n.input:focus {\n outline: none;\n border-color: var(--tempest-primary);\n box-shadow: 0 0 0 var(--tempest-focus-ring-width) var(--tempest-focus-ring-color);\n}\n\n.input:disabled {\n background-color: var(--tempest-surface);\n cursor: not-allowed;\n}\n\n.caret {\n position: absolute;\n right: 12px;\n pointer-events: none;\n color: var(--tempest-text-muted);\n}\n\n.menu {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n right: 0;\n z-index: var(--tempest-z-dropdown);\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n max-height: 280px;\n overflow-y: auto;\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n}\n\n.option {\n display: flex;\n align-items: center;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border-radius: var(--tempest-radius-sm);\n cursor: pointer;\n color: var(--tempest-text);\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .option:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.option.active {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-
|
|
1
|
+
{"version":3,"file":"Combobox.module.js","names":[],"sources":["../../../src/components/Combobox/Combobox.module.css"],"sourcesContent":[".wrapper {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n width: 100%;\n font-family: var(--tempest-font-sans);\n position: relative;\n}\n\n.label {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n line-height: var(--tempest-leading-snug);\n}\n\n.field {\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.input {\n width: 100%;\n height: var(--tempest-control-height-md);\n padding: 0 32px 0 var(--tempest-control-padding-md);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-control-radius);\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: var(--tempest-control-font-md);\n box-shadow: var(--tempest-shadow-xs);\n transition: var(--tempest-transition-color), var(--tempest-transition-shadow);\n}\n\n.input::placeholder {\n color: var(--tempest-text-subtle);\n}\n\n.input:focus {\n outline: none;\n border-color: var(--tempest-primary);\n box-shadow: 0 0 0 var(--tempest-focus-ring-width) var(--tempest-focus-ring-color);\n}\n\n.input:disabled {\n background-color: var(--tempest-surface);\n cursor: not-allowed;\n}\n\n.caret {\n position: absolute;\n right: 12px;\n pointer-events: none;\n color: var(--tempest-text-muted);\n}\n\n.menu {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n right: 0;\n z-index: var(--tempest-z-dropdown);\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n max-height: 280px;\n overflow-y: auto;\n padding: var(--tempest-space-1);\n list-style: none;\n margin: 0;\n}\n\n.option {\n display: flex;\n align-items: center;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n border-radius: var(--tempest-radius-sm);\n cursor: pointer;\n color: var(--tempest-text);\n font-size: var(--tempest-text-base);\n line-height: var(--tempest-leading-snug);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .option:hover {\n background-color: var(--tempest-surface);\n }\n}\n\n.option.active {\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n}\n\n.option.selected {\n font-weight: var(--tempest-weight-semibold);\n}\n\n.empty {\n padding: var(--tempest-space-3);\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n text-align: center;\n}\n\n.helper {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-text-muted);\n}\n\n.errorText {\n font-size: var(--tempest-text-xs);\n color: var(--tempest-danger);\n}\n\n.wrapper.error .input {\n border-color: var(--tempest-danger);\n}\n"],"mappings":""}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../utils/cn.cjs"),t=require("../../hooks/use-pagination.cjs"),n=require("../Pagination/Pagination.cjs"),
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("../../hooks/use-pagination.cjs"),n=require("../../hooks/use-announce.cjs"),r=require("../Pagination/Pagination.cjs"),i=require("../SearchBar/SearchBar.cjs"),a=require("../Table/Table.cjs"),o=require("../../utils/compare-values.cjs"),s=require("./DataTable.module.cjs"),c=require("./EditableCell.cjs"),l=require("./edit-labels.cjs");let u=require("react"),d=require("react/jsx-runtime");function f(e,t){return`${String(e)}::${String(t)}`}function p(e){return typeof e.header==`string`?e.header:String(e.key)}function m({data:m,columns:h,pageSize:g=10,searchable:_=!1,searchKeys:v,initialSort:y,rowKey:b=(e,t)=>t,emptyMessage:ee,onCellChange:x,editLabels:S,className:te,...ne}){let[C,w]=(0,u.useState)(``),[T,E]=(0,u.useState)(y??null),{page:D,setPage:O}=t.usePagination(1,g),k=n.useAnnounce(),[A,j]=(0,u.useState)(null),[M,N]=(0,u.useState)(null),[P,F]=(0,u.useState)({}),[I,L]=(0,u.useState)({}),[R,z]=(0,u.useState)({}),B=(0,u.useMemo)(()=>({...l.DEFAULT_EDIT_LABELS,...S}),[S]),V=x!==void 0&&h.some(e=>e.editable),H=(0,u.useMemo)(()=>v&&v.length>0?v:h.filter(e=>{let t=m.find(t=>t[e.key]!=null),n=t?t[e.key]:void 0;return typeof n==`string`||typeof n==`number`}).map(e=>e.key),[v,h,m]),U=(0,u.useMemo)(()=>{let e=C.trim().toLowerCase();return!e||!_?m:m.filter(t=>H.some(n=>{let r=t[n];return r!=null&&String(r).toLowerCase().includes(e)}))},[m,C,_,H]),W=(0,u.useMemo)(()=>{if(!T)return U;let e=T.direction===`asc`?1:-1;return[...U].sort((t,n)=>o.compareValues(t[T.key],n[T.key])*e)},[U,T]),G=Math.max(1,Math.ceil(W.length/g));(0,u.useEffect)(()=>{D>G&&O(G)},[D,G,O]);let K=Math.min(D,G),q=(0,u.useMemo)(()=>{let e=(K-1)*g;return W.slice(e,e+g)},[W,K,g]);function re(e){E(t=>!t||t.key!==e?{key:e,direction:`asc`}:t.direction===`asc`?{key:e,direction:`desc`}:null)}let J=(0,u.useCallback)(e=>(K-1)*g+e,[K,g]),Y=(0,u.useMemo)(()=>{if(!V)return[];let e=[];return q.forEach((t,n)=>{let r=b(t,J(n));for(let t of h)t.editable&&e.push(f(r,t.key))}),e},[V,q,h,J]),X=(0,u.useCallback)((e,t,n)=>n in P?P[n]:e[t.key],[P]),Z=(0,u.useCallback)(async(e,t,n)=>{z(t=>({...t,[e]:!0}));try{await x?.(n),k(B.saved(p(t)))}catch(n){F(t=>{let n={...t};return delete n[e],n});let r=n instanceof Error&&n.message?n.message:B.saveFailed(p(t));L(t=>({...t,[e]:r}))}finally{z(t=>{let n={...t};return delete n[e],n})}},[x,k,B]),Q=(0,u.useCallback)((e,t)=>{if(t===`none`){j(null),N(e);return}let n=Y.indexOf(e),r=Y[n+(t===`next`?1:-1)];if(r===void 0){j(null),N(e);return}j(r),N(null)},[Y]),$=(0,u.useCallback)((e,t,n,r,i,a)=>{let o=X(e,t,n);if(i===(t.formatEdit?t.formatEdit(e):String(o??``))){Q(n,a);return}let s=t.parse?t.parse(i,e):t.editorType===`number`?Number(i):i.trim(),c=t.validate?.(s,e)??null;if(c){L(e=>({...e,[n]:c}));return}L(e=>{let t={...e};return delete t[n],t}),F(e=>({...e,[n]:s})),Q(n,a),Z(n,t,{row:e,key:t.key,value:s,previous:o,rowIndex:J(r)})},[X,Q,Z,J]),ie=(0,u.useMemo)(()=>h.map(e=>{let t=T?.key===e.key?T?.direction===`asc`?` ▲`:` ▼`:``,n=e.sortable?(0,d.jsxs)(`button`,{type:`button`,className:s.default.sortButton,onClick:()=>re(e.key),"aria-label":`Ordenar por ${p(e)}`,children:[e.header,(0,d.jsx)(`span`,{className:s.default.sortIndicator,"aria-hidden":!0,children:t})]}):e.header,r=t=>e.render?e.render(t):t[e.key]??null,i=(t,n)=>{let r={...t,[e.key]:n};return e.render?e.render(r):r[e.key]??null},a=V&&e.editable===!0;return{key:String(e.key),header:n,align:e.align,priority:e.priority,width:e.width,render:a?(t,n)=>{let r=f(b(t,J(n)),e.key),a=X(t,e,r);return(0,d.jsx)(c.EditableCell,{text:e.formatEdit?e.formatEdit(t):String(a??``),columnLabel:p(e),rowNumber:n+1,inputType:e.editorType??`text`,editing:A===r,refocus:M===r,saving:R[r]===!0,error:I[r]??null,errorId:`tempest-cell-error-${r.replace(/[^\w-]/g,`_`)}`,labels:B,onOpen:()=>{j(r),N(null)},onCommit:(i,a)=>$(t,e,r,n,i,a),onCancel:()=>{j(null),N(r),L(e=>{let t={...e};return delete t[r],t})},children:i(t,a)})}:r}}),[h,T,V,A,M,R,I,B,P,J,$]);return(0,d.jsxs)(`div`,{className:e.cn(s.default.wrapper,te),...ne,children:[_&&(0,d.jsx)(i.SearchBar,{value:C,onChange:e=>{w(e),O(1)},wrapperClassName:s.default.search}),(0,d.jsx)(a.Table,{columns:ie,data:q,rowKey:(e,t)=>b(e,J(t)),emptyMessage:ee}),G>1&&(0,d.jsx)(r.Pagination,{page:K,totalPages:G,onPageChange:O,totalItems:W.length})]})}exports.DataTable=m;
|
|
2
2
|
//# sourceMappingURL=DataTable.cjs.map
|