bits-lab 0.0.0-stage → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +57 -2
- package/dist/Accordion.d.ts +52 -0
- package/dist/Accordion.js +192 -0
- package/dist/AchievementToast.d.ts +36 -0
- package/dist/AchievementToast.js +162 -0
- package/dist/AgentCard.d.ts +41 -0
- package/dist/AgentCard.js +168 -0
- package/dist/ArcadeButtons.d.ts +23 -0
- package/dist/ArcadeButtons.js +169 -0
- package/dist/AuroraBands.d.ts +19 -0
- package/dist/AuroraBands.js +126 -0
- package/dist/AvatarTooltips.d.ts +32 -0
- package/dist/AvatarTooltips.js +121 -0
- package/dist/BarRace.d.ts +39 -0
- package/dist/BarRace.js +227 -0
- package/dist/BentoGrid.d.ts +50 -0
- package/dist/BentoGrid.js +145 -0
- package/dist/BootSequence.d.ts +48 -0
- package/dist/BootSequence.js +234 -0
- package/dist/BreadcrumbTrail.d.ts +31 -0
- package/dist/BreadcrumbTrail.js +181 -0
- package/dist/CRTPower.d.ts +30 -0
- package/dist/CRTPower.js +142 -0
- package/dist/CardDeck.d.ts +29 -0
- package/dist/CardDeck.js +154 -0
- package/dist/ChangelogTimeline.d.ts +35 -0
- package/dist/ChangelogTimeline.js +137 -0
- package/dist/ChapterProgress.d.ts +23 -0
- package/dist/ChapterProgress.js +211 -0
- package/dist/ChatBubbles.d.ts +42 -0
- package/dist/ChatBubbles.js +198 -0
- package/dist/ChiptuneKit.d.ts +158 -0
- package/dist/ChiptuneKit.js +207 -0
- package/dist/CipherCard.d.ts +22 -0
- package/dist/CipherCard.js +91 -0
- package/dist/CloudSky.d.ts +28 -0
- package/dist/CloudSky.js +222 -0
- package/dist/CodeRain.d.ts +24 -0
- package/dist/CodeRain.js +158 -0
- package/dist/CodeShowcase.d.ts +50 -0
- package/dist/CodeShowcase.js +307 -0
- package/dist/ColorDial.d.ts +42 -0
- package/dist/ColorDial.js +288 -0
- package/dist/CommandPalette.d.ts +59 -0
- package/dist/CommandPalette.js +349 -0
- package/dist/CompareSlider.d.ts +33 -0
- package/dist/CompareSlider.js +152 -0
- package/dist/Confetti.d.ts +61 -0
- package/dist/Confetti.js +176 -0
- package/dist/ConnectBeams.d.ts +39 -0
- package/dist/ConnectBeams.js +132 -0
- package/dist/ContextOrb.d.ts +34 -0
- package/dist/ContextOrb.js +269 -0
- package/dist/ContourLines.d.ts +20 -0
- package/dist/ContourLines.js +159 -0
- package/dist/Coverflow.d.ts +27 -0
- package/dist/Coverflow.js +167 -0
- package/dist/CursorAttractor.d.ts +21 -0
- package/dist/CursorAttractor.js +136 -0
- package/dist/CutReveal.d.ts +21 -0
- package/dist/CutReveal.js +116 -0
- package/dist/DateRange.d.ts +45 -0
- package/dist/DateRange.js +327 -0
- package/dist/DeviceFrame.d.ts +30 -0
- package/dist/DeviceFrame.js +145 -0
- package/dist/DiffReveal.d.ts +36 -0
- package/dist/DiffReveal.js +247 -0
- package/dist/DirectionHover.d.ts +15 -0
- package/dist/DirectionHover.js +90 -0
- package/dist/Discard.d.ts +18 -0
- package/dist/Discard.js +61 -0
- package/dist/DitherField.d.ts +15 -0
- package/dist/DitherField.js +157 -0
- package/dist/DotGlobe.d.ts +48 -0
- package/dist/DotGlobe.js +312 -0
- package/dist/DotMap.d.ts +47 -0
- package/dist/DotMap.js +172 -0
- package/dist/DropZone.d.ts +57 -0
- package/dist/DropZone.js +304 -0
- package/dist/DynamicIsland.d.ts +32 -0
- package/dist/DynamicIsland.js +92 -0
- package/dist/EdgeFX.d.ts +27 -0
- package/dist/EdgeFX.js +98 -0
- package/dist/ElasticString.d.ts +22 -0
- package/dist/ElasticString.js +127 -0
- package/dist/ExpandCard.d.ts +44 -0
- package/dist/ExpandCard.js +182 -0
- package/dist/FeatureTabs.d.ts +34 -0
- package/dist/FeatureTabs.js +169 -0
- package/dist/FeedList.d.ts +37 -0
- package/dist/FeedList.js +163 -0
- package/dist/FlickerGrid.d.ts +29 -0
- package/dist/FlickerGrid.js +156 -0
- package/dist/FocusGrid.d.ts +20 -0
- package/dist/FocusGrid.js +48 -0
- package/dist/FocusPull.d.ts +26 -0
- package/dist/FocusPull.js +106 -0
- package/dist/Footprints.d.ts +24 -0
- package/dist/Footprints.js +126 -0
- package/dist/FunnelFlow.d.ts +34 -0
- package/dist/FunnelFlow.js +267 -0
- package/dist/GameOver.d.ts +46 -0
- package/dist/GameOver.js +191 -0
- package/dist/GaugeMeter.d.ts +38 -0
- package/dist/GaugeMeter.js +195 -0
- package/dist/GlassPanel.d.ts +27 -0
- package/dist/GlassPanel.js +80 -0
- package/dist/GrainFX.d.ts +34 -0
- package/dist/GrainFX.js +122 -0
- package/dist/GravityBox.d.ts +30 -0
- package/dist/GravityBox.js +220 -0
- package/dist/HealthBar.d.ts +33 -0
- package/dist/HealthBar.js +140 -0
- package/dist/Heatmap.d.ts +42 -0
- package/dist/Heatmap.js +232 -0
- package/dist/HexField.d.ts +29 -0
- package/dist/HexField.js +188 -0
- package/dist/HoldToConfirm.d.ts +29 -0
- package/dist/HoldToConfirm.js +163 -0
- package/dist/HorizontalTrack.d.ts +28 -0
- package/dist/HorizontalTrack.js +195 -0
- package/dist/HotbarDock.d.ts +40 -0
- package/dist/HotbarDock.js +148 -0
- package/dist/InkText.d.ts +27 -0
- package/dist/InkText.js +65 -0
- package/dist/InlineEdit.d.ts +30 -0
- package/dist/InlineEdit.js +198 -0
- package/dist/InteractiveGrid.d.ts +25 -0
- package/dist/InteractiveGrid.js +194 -0
- package/dist/InventoryGrid.d.ts +45 -0
- package/dist/InventoryGrid.js +272 -0
- package/dist/JellyBlob.d.ts +30 -0
- package/dist/JellyBlob.js +209 -0
- package/dist/KineticHeadline.d.ts +30 -0
- package/dist/KineticHeadline.js +133 -0
- package/dist/LavaLamp.d.ts +21 -0
- package/dist/LavaLamp.js +133 -0
- package/dist/LetterSwap.d.ts +20 -0
- package/dist/LetterSwap.js +78 -0
- package/dist/LevelMap.d.ts +35 -0
- package/dist/LevelMap.js +189 -0
- package/dist/LightShafts.d.ts +22 -0
- package/dist/LightShafts.js +170 -0
- package/dist/LightboxGallery.d.ts +26 -0
- package/dist/LightboxGallery.js +327 -0
- package/dist/LinkPeek.d.ts +36 -0
- package/dist/LinkPeek.js +194 -0
- package/dist/LogoOrbit.d.ts +37 -0
- package/dist/LogoOrbit.js +117 -0
- package/dist/Marquee.d.ts +33 -0
- package/dist/Marquee.js +148 -0
- package/dist/MarqueeLine.d.ts +33 -0
- package/dist/MarqueeLine.js +167 -0
- package/dist/MasonryWall.d.ts +13 -0
- package/dist/MasonryWall.js +90 -0
- package/dist/MegaMenu.d.ts +32 -0
- package/dist/MegaMenu.js +280 -0
- package/dist/MeteorShower.d.ts +24 -0
- package/dist/MeteorShower.js +155 -0
- package/dist/Minimap.d.ts +21 -0
- package/dist/Minimap.js +236 -0
- package/dist/MobileTabBar.d.ts +38 -0
- package/dist/MobileTabBar.js +148 -0
- package/dist/MorphDialog.d.ts +34 -0
- package/dist/MorphDialog.js +199 -0
- package/dist/MorphPopover.d.ts +33 -0
- package/dist/MorphPopover.js +168 -0
- package/dist/MorphWords.d.ts +18 -0
- package/dist/MorphWords.js +136 -0
- package/dist/OTPInput.d.ts +43 -0
- package/dist/OTPInput.js +174 -0
- package/dist/OverlayKit.d.ts +97 -0
- package/dist/OverlayKit.js +466 -0
- package/dist/OverlayMenu.d.ts +32 -0
- package/dist/OverlayMenu.js +154 -0
- package/dist/ParallaxCollage.d.ts +41 -0
- package/dist/ParallaxCollage.js +143 -0
- package/dist/PasswordMeter.d.ts +44 -0
- package/dist/PasswordMeter.js +165 -0
- package/dist/PegBoard.d.ts +26 -0
- package/dist/PegBoard.js +184 -0
- package/dist/PinCard.d.ts +21 -0
- package/dist/PinCard.js +84 -0
- package/dist/PixelAvatar.d.ts +33 -0
- package/dist/PixelAvatar.js +144 -0
- package/dist/PixelButton.d.ts +20 -0
- package/dist/PixelButton.js +128 -0
- package/dist/PixelExplode.d.ts +31 -0
- package/dist/PixelExplode.js +164 -0
- package/dist/PixelLoader.d.ts +32 -0
- package/dist/PixelLoader.js +142 -0
- package/dist/PixelParallax.d.ts +41 -0
- package/dist/PixelParallax.js +151 -0
- package/dist/PixelSpinners.d.ts +21 -0
- package/dist/PixelSpinners.js +110 -0
- package/dist/PixelType.d.ts +26 -0
- package/dist/PixelType.js +220 -0
- package/dist/PointerKit.d.ts +23 -0
- package/dist/PointerKit.js +222 -0
- package/dist/Polaroids.d.ts +41 -0
- package/dist/Polaroids.js +293 -0
- package/dist/PressStart.d.ts +28 -0
- package/dist/PressStart.js +99 -0
- package/dist/PricingFlip.d.ts +52 -0
- package/dist/PricingFlip.js +190 -0
- package/dist/ProximityType.d.ts +26 -0
- package/dist/ProximityType.js +135 -0
- package/dist/QuestDialog.d.ts +50 -0
- package/dist/QuestDialog.js +204 -0
- package/dist/QuestLog.d.ts +39 -0
- package/dist/QuestLog.js +214 -0
- package/dist/RangeDual.d.ts +34 -0
- package/dist/RangeDual.js +238 -0
- package/dist/RatingPips.d.ts +25 -0
- package/dist/RatingPips.js +129 -0
- package/dist/RetroHorizon.d.ts +27 -0
- package/dist/RetroHorizon.js +169 -0
- package/dist/Reveal.d.ts +30 -0
- package/dist/Reveal.js +92 -0
- package/dist/RingProgress.d.ts +45 -0
- package/dist/RingProgress.js +180 -0
- package/dist/RollingNumber.d.ts +28 -0
- package/dist/RollingNumber.js +108 -0
- package/dist/ScanScope.d.ts +24 -0
- package/dist/ScanScope.js +164 -0
- package/dist/ScoreTicker.d.ts +32 -0
- package/dist/ScoreTicker.js +176 -0
- package/dist/ScreenScroller.d.ts +24 -0
- package/dist/ScreenScroller.js +138 -0
- package/dist/Screensaver.d.ts +31 -0
- package/dist/Screensaver.js +221 -0
- package/dist/ScrollTilt.d.ts +27 -0
- package/dist/ScrollTilt.js +100 -0
- package/dist/ScrollTypography.d.ts +26 -0
- package/dist/ScrollTypography.js +124 -0
- package/dist/ScrollZoom.d.ts +31 -0
- package/dist/ScrollZoom.js +123 -0
- package/dist/ScrubNumber.d.ts +29 -0
- package/dist/ScrubNumber.js +152 -0
- package/dist/ShadowStack.d.ts +28 -0
- package/dist/ShadowStack.js +116 -0
- package/dist/SideRail.d.ts +43 -0
- package/dist/SideRail.js +253 -0
- package/dist/SignalText.d.ts +25 -0
- package/dist/SignalText.js +178 -0
- package/dist/SkeletonShimmer.d.ts +18 -0
- package/dist/SkeletonShimmer.js +111 -0
- package/dist/SketchHighlight.d.ts +27 -0
- package/dist/SketchHighlight.js +180 -0
- package/dist/SlideTabs.d.ts +40 -0
- package/dist/SlideTabs.js +190 -0
- package/dist/SmartNavbar.d.ts +48 -0
- package/dist/SmartNavbar.js +188 -0
- package/dist/SparkChart.d.ts +41 -0
- package/dist/SparkChart.js +188 -0
- package/dist/Spoiler.d.ts +22 -0
- package/dist/Spoiler.js +97 -0
- package/dist/SpotlightTour.d.ts +38 -0
- package/dist/SpotlightTour.js +231 -0
- package/dist/SpringDrawer.d.ts +39 -0
- package/dist/SpringDrawer.js +360 -0
- package/dist/SpriteAnimator.d.ts +41 -0
- package/dist/SpriteAnimator.js +124 -0
- package/dist/StatCard.d.ts +43 -0
- package/dist/StatCard.js +168 -0
- package/dist/StepLoader.d.ts +41 -0
- package/dist/StepLoader.js +191 -0
- package/dist/StepperForm.d.ts +41 -0
- package/dist/StepperForm.js +206 -0
- package/dist/StickerBoard.d.ts +39 -0
- package/dist/StickerBoard.js +209 -0
- package/dist/StickyReveal.d.ts +42 -0
- package/dist/StickyReveal.js +181 -0
- package/dist/StoryViewer.d.ts +36 -0
- package/dist/StoryViewer.js +175 -0
- package/dist/StreamingText.d.ts +29 -0
- package/dist/StreamingText.js +271 -0
- package/dist/SuggestionChips.d.ts +32 -0
- package/dist/SuggestionChips.js +96 -0
- package/dist/SwipeActions.d.ts +30 -0
- package/dist/SwipeActions.js +141 -0
- package/dist/TagInput.d.ts +30 -0
- package/dist/TagInput.js +284 -0
- package/dist/TallyUp.d.ts +38 -0
- package/dist/TallyUp.js +157 -0
- package/dist/TerminalDemo.d.ts +63 -0
- package/dist/TerminalDemo.js +214 -0
- package/dist/TestimonialWall.d.ts +35 -0
- package/dist/TestimonialWall.js +131 -0
- package/dist/TextOnPath.d.ts +36 -0
- package/dist/TextOnPath.js +130 -0
- package/dist/ThinkingShimmer.d.ts +36 -0
- package/dist/ThinkingShimmer.js +132 -0
- package/dist/ToastStack.d.ts +62 -0
- package/dist/ToastStack.js +289 -0
- package/dist/ToggleGroup.d.ts +39 -0
- package/dist/ToggleGroup.js +190 -0
- package/dist/ToggleKit.d.ts +70 -0
- package/dist/ToggleKit.js +229 -0
- package/dist/TracingLine.d.ts +24 -0
- package/dist/TracingLine.js +139 -0
- package/dist/TradingCard.d.ts +30 -0
- package/dist/TradingCard.js +147 -0
- package/dist/VanishInput.d.ts +34 -0
- package/dist/VanishInput.js +190 -0
- package/dist/VideoHero.d.ts +32 -0
- package/dist/VideoHero.js +207 -0
- package/dist/VoiceOrb.d.ts +23 -0
- package/dist/VoiceOrb.js +182 -0
- package/dist/VoxelTiles.d.ts +38 -0
- package/dist/VoxelTiles.js +201 -0
- package/dist/WarpField.d.ts +18 -0
- package/dist/WarpField.js +160 -0
- package/dist/WebcamPixels.d.ts +38 -0
- package/dist/WebcamPixels.js +292 -0
- package/dist/WheelPicker.d.ts +29 -0
- package/dist/WheelPicker.js +199 -0
- package/dist/WobbleCard.d.ts +23 -0
- package/dist/WobbleCard.js +112 -0
- package/dist/index.d.ts +158 -0
- package/dist/index.js +159 -0
- package/package.json +58 -4
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useInsertionEffect, useLayoutEffect, useRef, useState } from "react";
|
|
4
|
+
const STYLE_ID = "bl-toggle-group";
|
|
5
|
+
const CSS = `
|
|
6
|
+
.bl-tg { position: relative; display: inline-flex; max-width: 100%; overflow-x: auto; scrollbar-width: none; padding: var(--bl-pad); gap: var(--bl-gap); border-radius: var(--bl-radius); background: var(--bl-track); box-shadow: inset 0 0 0 1px var(--bl-line); font-family: var(--bl-font); isolation: isolate; }
|
|
7
|
+
.bl-tg[data-full] { display: flex; width: 100%; }
|
|
8
|
+
.bl-tg[data-full] .bl-tg__item { flex: 1 0 auto; }
|
|
9
|
+
.bl-tg__thumb { position: absolute; z-index: 0; top: var(--bl-pad); bottom: var(--bl-pad); left: 0; border-radius: var(--bl-inner-radius); background: var(--bl-thumb); box-shadow: 0 1px 2px rgba(0,0,0,0.3), 0 4px 12px -6px rgba(0,0,0,0.5); transition: transform var(--bl-dur) cubic-bezier(.3,1.25,.5,1), width var(--bl-dur) cubic-bezier(.3,1.25,.5,1); }
|
|
10
|
+
.bl-tg__thumb[data-instant] { transition: none; }
|
|
11
|
+
.bl-tg__item { position: relative; z-index: 1; display: inline-flex; flex: 0 0 auto; align-items: center; justify-content: center; gap: 7px; min-width: 0; height: var(--bl-h); padding: 0 var(--bl-item-pad); border: 0; border-radius: var(--bl-inner-radius); background: transparent; color: var(--bl-muted); font: inherit; font-size: var(--bl-size); font-weight: 600; white-space: nowrap; cursor: pointer; transition: color 180ms, background-color 180ms, border-radius 180ms; }
|
|
12
|
+
.bl-tg__item:hover:not(:disabled) { color: var(--bl-ink); }
|
|
13
|
+
.bl-tg__item:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
14
|
+
.bl-tg__item:focus-visible { outline: 2px solid var(--bl-accent); outline-offset: 1px; z-index: 2; }
|
|
15
|
+
.bl-tg[data-type="single"] .bl-tg__item[aria-checked="true"] { color: var(--bl-thumb-ink); }
|
|
16
|
+
.bl-tg[data-type="multiple"] .bl-tg__item[aria-pressed="true"] { background: var(--bl-thumb); color: var(--bl-thumb-ink); animation: bl-tg-pop 260ms cubic-bezier(.3,1.4,.5,1); }
|
|
17
|
+
.bl-tg[data-type="multiple"] .bl-tg__item[data-join-left] { border-top-left-radius: 0; border-bottom-left-radius: 0; }
|
|
18
|
+
.bl-tg[data-type="multiple"] .bl-tg__item[data-join-right] { border-top-right-radius: 0; border-bottom-right-radius: 0; }
|
|
19
|
+
.bl-tg[data-type="multiple"] .bl-tg__item[data-join-right]::after { content: ""; position: absolute; top: 0; bottom: 0; right: calc(var(--bl-gap) * -1); width: var(--bl-gap); background: var(--bl-thumb); }
|
|
20
|
+
@keyframes bl-tg-pop { 40% { transform: scale(0.94); } }
|
|
21
|
+
@media (prefers-reduced-motion: reduce) { .bl-tg__thumb, .bl-tg__item { transition: none; animation: none !important; } }
|
|
22
|
+
`;
|
|
23
|
+
function useStyles() {
|
|
24
|
+
useInsertionEffect(() => {
|
|
25
|
+
if (typeof document === "undefined") return;
|
|
26
|
+
const style = document.getElementById(STYLE_ID) ?? Object.assign(document.createElement("style"), { id: STYLE_ID });
|
|
27
|
+
style.textContent = CSS;
|
|
28
|
+
if (!style.isConnected) document.head.appendChild(style);
|
|
29
|
+
}, []);
|
|
30
|
+
}
|
|
31
|
+
function ToggleGroup({
|
|
32
|
+
items = [],
|
|
33
|
+
type = "single",
|
|
34
|
+
value,
|
|
35
|
+
defaultValue,
|
|
36
|
+
onChange,
|
|
37
|
+
label = "Options",
|
|
38
|
+
fullWidth = false,
|
|
39
|
+
size = "md",
|
|
40
|
+
duration = 340,
|
|
41
|
+
radius = 12,
|
|
42
|
+
gap = 2,
|
|
43
|
+
padding = 4,
|
|
44
|
+
trackColor = "rgba(255,255,255,0.06)",
|
|
45
|
+
lineColor = "rgba(255,255,255,0.1)",
|
|
46
|
+
thumbColor = "#f4f4f4",
|
|
47
|
+
thumbTextColor = "#1a1c2c",
|
|
48
|
+
textColor = "#f4f4f4",
|
|
49
|
+
mutedColor = "#94b0c2",
|
|
50
|
+
accentColor = "#73eff7",
|
|
51
|
+
fontFamily = "ui-sans-serif, system-ui, sans-serif",
|
|
52
|
+
className = "",
|
|
53
|
+
style
|
|
54
|
+
}) {
|
|
55
|
+
useStyles();
|
|
56
|
+
const multiple = type === "multiple";
|
|
57
|
+
const [inner, setInner] = useState(() => defaultValue ?? (multiple ? [] : items.find((item) => !item.disabled)?.value));
|
|
58
|
+
const selected = value !== void 0 ? value : inner;
|
|
59
|
+
const selectedSet = new Set(multiple ? selected ?? [] : [selected]);
|
|
60
|
+
const [focusIndex, setFocusIndex] = useState(null);
|
|
61
|
+
const refs = useRef([]);
|
|
62
|
+
const thumbRef = useRef(null);
|
|
63
|
+
const placed = useRef(false);
|
|
64
|
+
const commit = (next) => {
|
|
65
|
+
if (value === void 0) setInner(next);
|
|
66
|
+
onChange?.(next);
|
|
67
|
+
};
|
|
68
|
+
const toggle = (item) => {
|
|
69
|
+
if (item.disabled) return;
|
|
70
|
+
if (!multiple) {
|
|
71
|
+
if (item.value !== selected) commit(item.value);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const current = selected ?? [];
|
|
75
|
+
commit(current.includes(item.value) ? current.filter((v) => v !== item.value) : items.filter((i) => i.value === item.value || current.includes(i.value)).map((i) => i.value));
|
|
76
|
+
};
|
|
77
|
+
const selectedIndex = multiple ? -1 : items.findIndex((item) => item.value === selected);
|
|
78
|
+
useLayoutEffect(() => {
|
|
79
|
+
const thumb = thumbRef.current;
|
|
80
|
+
const target = refs.current[selectedIndex];
|
|
81
|
+
if (!thumb) return void 0;
|
|
82
|
+
const place = () => {
|
|
83
|
+
const node = refs.current[selectedIndex];
|
|
84
|
+
if (!node) {
|
|
85
|
+
thumb.style.opacity = "0";
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
thumb.style.opacity = "1";
|
|
89
|
+
thumb.style.width = `${node.offsetWidth}px`;
|
|
90
|
+
thumb.style.transform = `translateX(${node.offsetLeft}px)`;
|
|
91
|
+
};
|
|
92
|
+
if (!placed.current) thumb.setAttribute("data-instant", "");
|
|
93
|
+
place();
|
|
94
|
+
if (!placed.current) {
|
|
95
|
+
placed.current = true;
|
|
96
|
+
requestAnimationFrame(() => thumb.removeAttribute("data-instant"));
|
|
97
|
+
}
|
|
98
|
+
const observer = new ResizeObserver(() => {
|
|
99
|
+
thumb.setAttribute("data-instant", "");
|
|
100
|
+
place();
|
|
101
|
+
requestAnimationFrame(() => thumb.removeAttribute("data-instant"));
|
|
102
|
+
});
|
|
103
|
+
if (target) observer.observe(target);
|
|
104
|
+
if (thumb.parentElement) observer.observe(thumb.parentElement);
|
|
105
|
+
return () => observer.disconnect();
|
|
106
|
+
}, [selectedIndex, items.length, size]);
|
|
107
|
+
const enabled = items.map((item, i) => item.disabled ? -1 : i).filter((i) => i >= 0);
|
|
108
|
+
const tabStop = focusIndex ?? (multiple ? enabled[0] : selectedIndex >= 0 ? selectedIndex : enabled[0]);
|
|
109
|
+
const onKeyDown = (event, index) => {
|
|
110
|
+
const position = enabled.indexOf(index);
|
|
111
|
+
const keys = { ArrowRight: position + 1, ArrowDown: position + 1, ArrowLeft: position - 1, ArrowUp: position - 1, Home: 0, End: enabled.length - 1 };
|
|
112
|
+
if (!(event.key in keys)) return;
|
|
113
|
+
event.preventDefault();
|
|
114
|
+
const next = enabled[(keys[event.key] + enabled.length) % enabled.length];
|
|
115
|
+
setFocusIndex(next);
|
|
116
|
+
refs.current[next]?.focus();
|
|
117
|
+
if (!multiple) toggle(items[next]);
|
|
118
|
+
};
|
|
119
|
+
const heights = { sm: 30, md: 38, lg: 46 };
|
|
120
|
+
const fontSizes = { sm: 13, md: 14, lg: 15 };
|
|
121
|
+
return /* @__PURE__ */ jsxs(
|
|
122
|
+
"div",
|
|
123
|
+
{
|
|
124
|
+
className: `bl-tg ${className}`,
|
|
125
|
+
role: multiple ? "group" : "radiogroup",
|
|
126
|
+
"aria-label": label,
|
|
127
|
+
"data-type": type,
|
|
128
|
+
"data-full": fullWidth || void 0,
|
|
129
|
+
style: {
|
|
130
|
+
"--bl-h": `${heights[size] ?? heights.md}px`,
|
|
131
|
+
"--bl-size": `${fontSizes[size] ?? fontSizes.md}px`,
|
|
132
|
+
"--bl-item-pad": size === "sm" ? "10px" : "14px",
|
|
133
|
+
"--bl-pad": `${padding}px`,
|
|
134
|
+
"--bl-gap": `${gap}px`,
|
|
135
|
+
"--bl-radius": `${radius}px`,
|
|
136
|
+
"--bl-inner-radius": `${Math.max(0, radius - padding)}px`,
|
|
137
|
+
"--bl-dur": `${duration}ms`,
|
|
138
|
+
"--bl-track": trackColor,
|
|
139
|
+
"--bl-line": lineColor,
|
|
140
|
+
"--bl-thumb": thumbColor,
|
|
141
|
+
"--bl-thumb-ink": thumbTextColor,
|
|
142
|
+
"--bl-ink": textColor,
|
|
143
|
+
"--bl-muted": mutedColor,
|
|
144
|
+
"--bl-accent": accentColor,
|
|
145
|
+
"--bl-font": fontFamily,
|
|
146
|
+
...style
|
|
147
|
+
},
|
|
148
|
+
children: [
|
|
149
|
+
!multiple && /* @__PURE__ */ jsx("span", { ref: thumbRef, className: "bl-tg__thumb", "aria-hidden": "true" }),
|
|
150
|
+
items.map((item, index) => {
|
|
151
|
+
const on = selectedSet.has(item.value);
|
|
152
|
+
const joinLeft = multiple && on && index > 0 && selectedSet.has(items[index - 1].value);
|
|
153
|
+
const joinRight = multiple && on && index < items.length - 1 && selectedSet.has(items[index + 1].value);
|
|
154
|
+
return /* @__PURE__ */ jsxs(
|
|
155
|
+
"button",
|
|
156
|
+
{
|
|
157
|
+
ref: (node) => {
|
|
158
|
+
refs.current[index] = node;
|
|
159
|
+
},
|
|
160
|
+
type: "button",
|
|
161
|
+
className: "bl-tg__item",
|
|
162
|
+
role: multiple ? void 0 : "radio",
|
|
163
|
+
"aria-checked": multiple ? void 0 : on,
|
|
164
|
+
"aria-pressed": multiple ? on : void 0,
|
|
165
|
+
"aria-label": item.ariaLabel,
|
|
166
|
+
disabled: item.disabled,
|
|
167
|
+
tabIndex: index === tabStop ? 0 : -1,
|
|
168
|
+
"data-join-left": joinLeft || void 0,
|
|
169
|
+
"data-join-right": joinRight || void 0,
|
|
170
|
+
onClick: () => {
|
|
171
|
+
setFocusIndex(index);
|
|
172
|
+
toggle(item);
|
|
173
|
+
},
|
|
174
|
+
onKeyDown: (event) => onKeyDown(event, index),
|
|
175
|
+
onBlur: (event) => !event.currentTarget.parentElement?.contains(event.relatedTarget) && setFocusIndex(null),
|
|
176
|
+
children: [
|
|
177
|
+
item.icon,
|
|
178
|
+
item.label
|
|
179
|
+
]
|
|
180
|
+
},
|
|
181
|
+
item.value
|
|
182
|
+
);
|
|
183
|
+
})
|
|
184
|
+
]
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
export {
|
|
189
|
+
ToggleGroup as default
|
|
190
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactNode, type InputHTMLAttributes, type ChangeEvent, type ButtonHTMLAttributes } from 'react';
|
|
2
|
+
export type ToggleSize = 'sm' | 'md' | 'lg';
|
|
3
|
+
type InputRest = Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'size' | 'color' | 'checked' | 'defaultChecked' | 'onChange' | 'value' | 'name' | 'disabled' | 'className' | 'style'>;
|
|
4
|
+
interface ToggleBase {
|
|
5
|
+
label?: ReactNode;
|
|
6
|
+
description?: ReactNode;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
size?: ToggleSize;
|
|
9
|
+
/** Accent color for this control (sets --bl-tk-accent). */
|
|
10
|
+
color?: string;
|
|
11
|
+
/** Submitted with the form, like a plain input. */
|
|
12
|
+
name?: string;
|
|
13
|
+
value?: string;
|
|
14
|
+
className?: string;
|
|
15
|
+
style?: CSSProperties;
|
|
16
|
+
}
|
|
17
|
+
export interface SwitchProps extends ToggleBase, InputRest {
|
|
18
|
+
checked?: boolean;
|
|
19
|
+
defaultChecked?: boolean;
|
|
20
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
21
|
+
}
|
|
22
|
+
export interface CheckboxProps extends SwitchProps {
|
|
23
|
+
/** Shows a dash; set for "some selected" parents. */
|
|
24
|
+
indeterminate?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface RadioProps extends ToggleBase, InputRest {
|
|
27
|
+
checked?: boolean;
|
|
28
|
+
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
29
|
+
}
|
|
30
|
+
export interface RadioOption {
|
|
31
|
+
value: string;
|
|
32
|
+
label?: ReactNode;
|
|
33
|
+
description?: ReactNode;
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
}
|
|
36
|
+
export interface RadioGroupProps {
|
|
37
|
+
options?: RadioOption[];
|
|
38
|
+
value?: string | null;
|
|
39
|
+
defaultValue?: string | null;
|
|
40
|
+
onValueChange?: (value: string) => void;
|
|
41
|
+
/** Rendered as the fieldset legend. */
|
|
42
|
+
label?: ReactNode;
|
|
43
|
+
name?: string;
|
|
44
|
+
orientation?: 'vertical' | 'horizontal';
|
|
45
|
+
size?: ToggleSize;
|
|
46
|
+
color?: string;
|
|
47
|
+
disabled?: boolean;
|
|
48
|
+
className?: string;
|
|
49
|
+
style?: CSSProperties;
|
|
50
|
+
}
|
|
51
|
+
export declare function Switch({ checked, defaultChecked, onCheckedChange, label, description, disabled, size, color, name, value, className, style, ...rest }: SwitchProps): import("react").JSX.Element;
|
|
52
|
+
export declare function Checkbox({ checked, defaultChecked, onCheckedChange, indeterminate, label, description, disabled, size, color, name, value, className, style, ...rest }: CheckboxProps): import("react").JSX.Element;
|
|
53
|
+
export declare function Radio({ checked, onChange, label, description, disabled, size, color, name, value, className, style, ...rest }: RadioProps): import("react").JSX.Element;
|
|
54
|
+
export declare function RadioGroup({ options, value, defaultValue, onValueChange, label, name, orientation, size, color, disabled, className, style }: RadioGroupProps): import("react").JSX.Element;
|
|
55
|
+
export interface NotifyBellProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'type' | 'style' | 'color'> {
|
|
56
|
+
pressed?: boolean;
|
|
57
|
+
defaultPressed?: boolean;
|
|
58
|
+
onPressedChange?: (pressed: boolean) => void;
|
|
59
|
+
/** The button's name; shown beside the bell unless showLabel is false. */
|
|
60
|
+
label?: string;
|
|
61
|
+
showLabel?: boolean;
|
|
62
|
+
disabled?: boolean;
|
|
63
|
+
size?: ToggleSize;
|
|
64
|
+
/** Accent colour when on. */
|
|
65
|
+
color?: string;
|
|
66
|
+
className?: string;
|
|
67
|
+
style?: CSSProperties;
|
|
68
|
+
}
|
|
69
|
+
export declare function NotifyBell({ pressed, defaultPressed, onPressedChange, label, showLabel, disabled, size, color, className, style, ...rest }: NotifyBellProps): import("react").JSX.Element;
|
|
70
|
+
export default Switch;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useId, useInsertionEffect, useRef, useState } from "react";
|
|
4
|
+
const STYLE_ID = "bl-toggle-kit";
|
|
5
|
+
const CSS = `
|
|
6
|
+
.bl-tk { --tk-accent: var(--bl-tk-accent, #73eff7); --tk-ink: var(--bl-tk-ink, #f4f4f4); --tk-muted: var(--bl-tk-muted, #94b0c2); --tk-track: var(--bl-tk-track, #333c57); --tk-on-ink: var(--bl-tk-on-ink, #1a1c2c); --tk-size: 22px; position: relative; display: inline-flex; align-items: flex-start; gap: 10px; color: var(--tk-ink); font-family: var(--bl-tk-font, ui-sans-serif, system-ui, sans-serif); font-size: 14px; line-height: 1.4; cursor: pointer; -webkit-tap-highlight-color: transparent; }
|
|
7
|
+
.bl-tk[data-size="sm"] { --tk-size: 18px; font-size: 13px; }
|
|
8
|
+
.bl-tk[data-size="lg"] { --tk-size: 26px; font-size: 15px; }
|
|
9
|
+
.bl-tk[data-disabled] { opacity: 0.45; cursor: not-allowed; }
|
|
10
|
+
.bl-tk__input { position: absolute; width: 1px; height: 1px; margin: 0; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; opacity: 0; }
|
|
11
|
+
.bl-tk__text { display: grid; gap: 2px; padding-top: calc((var(--tk-size) - 1.4em) / 2); }
|
|
12
|
+
.bl-tk__desc { color: var(--tk-muted); font-size: 0.92em; }
|
|
13
|
+
.bl-tk__input:focus-visible + .bl-tk__control { outline: 2px solid var(--tk-accent); outline-offset: 3px; }
|
|
14
|
+
|
|
15
|
+
/* Switch */
|
|
16
|
+
.bl-tk__switch { position: relative; flex: 0 0 auto; width: calc(var(--tk-size) * 1.8); height: var(--tk-size); border-radius: 999px; background: var(--tk-track); transition: background-color 220ms cubic-bezier(.2,.9,.25,1); }
|
|
17
|
+
.bl-tk__thumb { position: absolute; top: 3px; left: 3px; width: calc(var(--tk-size) - 6px); height: calc(var(--tk-size) - 6px); border-radius: 999px; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,0.35); transition: left 320ms cubic-bezier(.3,1.35,.5,1), width 160ms cubic-bezier(.2,.9,.25,1); }
|
|
18
|
+
.bl-tk__input:checked + .bl-tk__switch { background: var(--tk-accent); }
|
|
19
|
+
.bl-tk__input:checked + .bl-tk__switch .bl-tk__thumb { left: calc(100% - var(--tk-size) + 3px); }
|
|
20
|
+
.bl-tk:active .bl-tk__input:not(:disabled) + .bl-tk__switch .bl-tk__thumb { width: calc(var(--tk-size) * 1.12); }
|
|
21
|
+
.bl-tk:active .bl-tk__input:checked:not(:disabled) + .bl-tk__switch .bl-tk__thumb { left: calc(100% - var(--tk-size) * 1.12 - 3px); }
|
|
22
|
+
|
|
23
|
+
/* Checkbox */
|
|
24
|
+
.bl-tk__box { position: relative; flex: 0 0 auto; display: grid; place-items: center; width: var(--tk-size); height: var(--tk-size); border-radius: calc(var(--tk-size) * 0.28); background: transparent; box-shadow: inset 0 0 0 2px var(--tk-track); color: var(--tk-on-ink); transition: background-color 160ms, box-shadow 160ms; animation: bl-tk-pop-off 260ms cubic-bezier(.3,1.35,.5,1); }
|
|
25
|
+
.bl-tk__box svg { width: 70%; height: 70%; overflow: visible; }
|
|
26
|
+
.bl-tk__check, .bl-tk__dash { fill: none; stroke: currentColor; stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset 220ms cubic-bezier(.2,.9,.25,1); }
|
|
27
|
+
.bl-tk__input:checked + .bl-tk__box, .bl-tk__input:indeterminate + .bl-tk__box { background: var(--tk-accent); box-shadow: inset 0 0 0 2px var(--tk-accent); animation: bl-tk-pop-on 320ms cubic-bezier(.3,1.35,.5,1); }
|
|
28
|
+
.bl-tk__input:checked:not(:indeterminate) + .bl-tk__box .bl-tk__check { stroke-dashoffset: 0; transition-delay: 60ms; }
|
|
29
|
+
.bl-tk__input:indeterminate + .bl-tk__box .bl-tk__dash { stroke-dashoffset: 0; }
|
|
30
|
+
|
|
31
|
+
/* Radio */
|
|
32
|
+
.bl-tk__radio { position: relative; flex: 0 0 auto; display: grid; place-items: center; width: var(--tk-size); height: var(--tk-size); border-radius: 50%; box-shadow: inset 0 0 0 2px var(--tk-track); transition: box-shadow 160ms; }
|
|
33
|
+
.bl-tk__dot { width: 45%; height: 45%; border-radius: 50%; background: var(--tk-accent); transform: scale(0); transition: transform 320ms cubic-bezier(.3,1.5,.5,1); }
|
|
34
|
+
.bl-tk__input:checked + .bl-tk__radio { box-shadow: inset 0 0 0 2px var(--tk-accent); }
|
|
35
|
+
.bl-tk__input:checked + .bl-tk__radio .bl-tk__dot { transform: scale(1); }
|
|
36
|
+
.bl-tk:active .bl-tk__input:not(:disabled) + .bl-tk__radio { transform: scale(0.9); }
|
|
37
|
+
|
|
38
|
+
.bl-tk-group { margin: 0; padding: 0; border: 0; font-family: var(--bl-tk-font, ui-sans-serif, system-ui, sans-serif); }
|
|
39
|
+
.bl-tk-group legend { margin-bottom: 10px; padding: 0; color: var(--bl-tk-muted, #94b0c2); font-size: 12px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; }
|
|
40
|
+
.bl-tk-group__list { display: flex; flex-direction: column; gap: 12px; }
|
|
41
|
+
.bl-tk-group[data-orientation="horizontal"] .bl-tk-group__list { flex-direction: row; flex-wrap: wrap; gap: 18px; }
|
|
42
|
+
|
|
43
|
+
/* Bell */
|
|
44
|
+
.bl-tk__bell { margin: 0; padding: 4px 10px 4px 4px; border: 0; border-radius: 10px; background: transparent; text-align: left; }
|
|
45
|
+
.bl-tk__bell:focus-visible { outline: 2px solid var(--tk-accent); outline-offset: 2px; }
|
|
46
|
+
.bl-tk__bell-icon { display: grid; place-items: center; flex: 0 0 auto; width: var(--tk-size); height: var(--tk-size); color: var(--tk-muted); transition: color 160ms; }
|
|
47
|
+
.bl-tk__bell[aria-pressed="true"] .bl-tk__bell-icon { color: var(--tk-accent); }
|
|
48
|
+
.bl-tk__bell-icon svg { width: 100%; height: 100%; overflow: visible; fill: currentColor; shape-rendering: crispEdges; transform-origin: 50% 8%; }
|
|
49
|
+
.bl-tk__bell[aria-pressed="true"] .bl-tk__bell-icon svg { animation: bl-tk-ring 720ms steps(8); }
|
|
50
|
+
.bl-tk__bell-waves { opacity: 0; }
|
|
51
|
+
.bl-tk__bell[aria-pressed="true"] .bl-tk__bell-waves { animation: bl-tk-waves 720ms steps(4); }
|
|
52
|
+
.bl-tk__bell[aria-pressed="true"] .bl-tk__bell-slash { display: none; }
|
|
53
|
+
|
|
54
|
+
@keyframes bl-tk-pop-on { 40% { transform: scale(0.82); } }
|
|
55
|
+
@keyframes bl-tk-pop-off { 40% { transform: scale(0.88); } }
|
|
56
|
+
@keyframes bl-tk-ring { 12% { transform: rotate(18deg); } 25% { transform: rotate(-16deg); } 38% { transform: rotate(12deg); } 50% { transform: rotate(-8deg); } 62% { transform: rotate(4deg); } 75% { transform: none; } }
|
|
57
|
+
@keyframes bl-tk-waves { 15%, 60% { opacity: 1; } }
|
|
58
|
+
@media (prefers-reduced-motion: reduce) {
|
|
59
|
+
.bl-tk__switch, .bl-tk__thumb, .bl-tk__check, .bl-tk__dash, .bl-tk__dot, .bl-tk__box, .bl-tk__radio, .bl-tk__bell-icon, .bl-tk__bell-icon svg, .bl-tk__bell-waves { transition: none; animation: none !important; }
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
function useStyles() {
|
|
63
|
+
useInsertionEffect(() => {
|
|
64
|
+
if (typeof document === "undefined") return;
|
|
65
|
+
const style = document.getElementById(STYLE_ID) ?? Object.assign(document.createElement("style"), { id: STYLE_ID });
|
|
66
|
+
style.textContent = CSS;
|
|
67
|
+
if (!style.isConnected) document.head.appendChild(style);
|
|
68
|
+
}, []);
|
|
69
|
+
}
|
|
70
|
+
function useChecked(checked, defaultChecked, onCheckedChange) {
|
|
71
|
+
const [inner, setInner] = useState(defaultChecked ?? false);
|
|
72
|
+
const value = checked ?? inner;
|
|
73
|
+
const set = (next) => {
|
|
74
|
+
if (checked === void 0) setInner(next);
|
|
75
|
+
onCheckedChange?.(next);
|
|
76
|
+
};
|
|
77
|
+
return [value, set];
|
|
78
|
+
}
|
|
79
|
+
const accentVars = (color) => color ? { "--bl-tk-accent": color } : null;
|
|
80
|
+
function Label({ label, description, id }) {
|
|
81
|
+
if (!label && !description) return null;
|
|
82
|
+
return /* @__PURE__ */ jsxs("span", { className: "bl-tk__text", children: [
|
|
83
|
+
label && /* @__PURE__ */ jsx("span", { children: label }),
|
|
84
|
+
description && /* @__PURE__ */ jsx("span", { id, className: "bl-tk__desc", children: description })
|
|
85
|
+
] });
|
|
86
|
+
}
|
|
87
|
+
function Switch({ checked, defaultChecked, onCheckedChange, label, description, disabled = false, size = "md", color, name, value, className = "", style, ...rest }) {
|
|
88
|
+
useStyles();
|
|
89
|
+
const uid = useId().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
90
|
+
const [on, setOn] = useChecked(checked, defaultChecked, onCheckedChange);
|
|
91
|
+
return /* @__PURE__ */ jsxs("label", { className: `bl-tk ${className}`, "data-size": size, "data-disabled": disabled || void 0, style: { ...accentVars(color), ...style }, children: [
|
|
92
|
+
/* @__PURE__ */ jsx(
|
|
93
|
+
"input",
|
|
94
|
+
{
|
|
95
|
+
...rest,
|
|
96
|
+
type: "checkbox",
|
|
97
|
+
role: "switch",
|
|
98
|
+
className: "bl-tk__input",
|
|
99
|
+
checked: on,
|
|
100
|
+
disabled,
|
|
101
|
+
name,
|
|
102
|
+
value,
|
|
103
|
+
"aria-describedby": description ? `${uid}-desc` : void 0,
|
|
104
|
+
onChange: (event) => setOn(event.target.checked)
|
|
105
|
+
}
|
|
106
|
+
),
|
|
107
|
+
/* @__PURE__ */ jsx("span", { className: "bl-tk__switch bl-tk__control", "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", { className: "bl-tk__thumb" }) }),
|
|
108
|
+
/* @__PURE__ */ jsx(Label, { label, description, id: `${uid}-desc` })
|
|
109
|
+
] });
|
|
110
|
+
}
|
|
111
|
+
function Checkbox({ checked, defaultChecked, onCheckedChange, indeterminate = false, label, description, disabled = false, size = "md", color, name, value, className = "", style, ...rest }) {
|
|
112
|
+
useStyles();
|
|
113
|
+
const uid = useId().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
114
|
+
const ref = useRef(null);
|
|
115
|
+
const [on, setOn] = useChecked(checked, defaultChecked, onCheckedChange);
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (ref.current) ref.current.indeterminate = indeterminate;
|
|
118
|
+
}, [indeterminate]);
|
|
119
|
+
return /* @__PURE__ */ jsxs("label", { className: `bl-tk ${className}`, "data-size": size, "data-disabled": disabled || void 0, style: { ...accentVars(color), ...style }, children: [
|
|
120
|
+
/* @__PURE__ */ jsx(
|
|
121
|
+
"input",
|
|
122
|
+
{
|
|
123
|
+
...rest,
|
|
124
|
+
ref,
|
|
125
|
+
type: "checkbox",
|
|
126
|
+
className: "bl-tk__input",
|
|
127
|
+
checked: on,
|
|
128
|
+
disabled,
|
|
129
|
+
name,
|
|
130
|
+
value,
|
|
131
|
+
"aria-describedby": description ? `${uid}-desc` : void 0,
|
|
132
|
+
onChange: (event) => setOn(event.target.checked)
|
|
133
|
+
}
|
|
134
|
+
),
|
|
135
|
+
/* @__PURE__ */ jsx("span", { className: "bl-tk__box bl-tk__control", "aria-hidden": "true", children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", children: [
|
|
136
|
+
/* @__PURE__ */ jsx("path", { className: "bl-tk__check", pathLength: "1", d: "M3 8.5 6.5 12 13 4.5" }),
|
|
137
|
+
/* @__PURE__ */ jsx("path", { className: "bl-tk__dash", pathLength: "1", d: "M4 8h8" })
|
|
138
|
+
] }) }),
|
|
139
|
+
/* @__PURE__ */ jsx(Label, { label, description, id: `${uid}-desc` })
|
|
140
|
+
] });
|
|
141
|
+
}
|
|
142
|
+
function Radio({ checked, onChange, label, description, disabled = false, size = "md", color, name, value, className = "", style, ...rest }) {
|
|
143
|
+
useStyles();
|
|
144
|
+
const uid = useId().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
145
|
+
return /* @__PURE__ */ jsxs("label", { className: `bl-tk ${className}`, "data-size": size, "data-disabled": disabled || void 0, style: { ...accentVars(color), ...style }, children: [
|
|
146
|
+
/* @__PURE__ */ jsx(
|
|
147
|
+
"input",
|
|
148
|
+
{
|
|
149
|
+
...rest,
|
|
150
|
+
type: "radio",
|
|
151
|
+
className: "bl-tk__input",
|
|
152
|
+
checked,
|
|
153
|
+
disabled,
|
|
154
|
+
name,
|
|
155
|
+
value,
|
|
156
|
+
"aria-describedby": description ? `${uid}-desc` : void 0,
|
|
157
|
+
onChange
|
|
158
|
+
}
|
|
159
|
+
),
|
|
160
|
+
/* @__PURE__ */ jsx("span", { className: "bl-tk__radio bl-tk__control", "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", { className: "bl-tk__dot" }) }),
|
|
161
|
+
/* @__PURE__ */ jsx(Label, { label, description, id: `${uid}-desc` })
|
|
162
|
+
] });
|
|
163
|
+
}
|
|
164
|
+
function RadioGroup({ options = [], value, defaultValue, onValueChange, label, name, orientation = "vertical", size = "md", color, disabled = false, className = "", style }) {
|
|
165
|
+
useStyles();
|
|
166
|
+
const uid = useId().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
167
|
+
const [inner, setInner] = useState(defaultValue ?? null);
|
|
168
|
+
const selected = value !== void 0 ? value : inner;
|
|
169
|
+
return /* @__PURE__ */ jsxs("fieldset", { className: `bl-tk-group ${className}`, "data-orientation": orientation, disabled, style: { ...accentVars(color), ...style }, children: [
|
|
170
|
+
label && /* @__PURE__ */ jsx("legend", { children: label }),
|
|
171
|
+
/* @__PURE__ */ jsx("div", { className: "bl-tk-group__list", children: options.map((option) => /* @__PURE__ */ jsx(
|
|
172
|
+
Radio,
|
|
173
|
+
{
|
|
174
|
+
name: name ?? `${uid}-radio`,
|
|
175
|
+
value: option.value,
|
|
176
|
+
label: option.label,
|
|
177
|
+
description: option.description,
|
|
178
|
+
disabled: option.disabled,
|
|
179
|
+
size,
|
|
180
|
+
checked: selected === option.value,
|
|
181
|
+
onChange: () => {
|
|
182
|
+
if (value === void 0) setInner(option.value);
|
|
183
|
+
onValueChange?.(option.value);
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
option.value
|
|
187
|
+
)) })
|
|
188
|
+
] });
|
|
189
|
+
}
|
|
190
|
+
const SLASH = Array.from({ length: 10 }, (_, i) => i + 1);
|
|
191
|
+
function NotifyBell({ pressed, defaultPressed, onPressedChange, label = "Notifications", showLabel = true, disabled = false, size = "md", color, className = "", style, ...rest }) {
|
|
192
|
+
useStyles();
|
|
193
|
+
const [on, setOn] = useChecked(pressed, defaultPressed, onPressedChange);
|
|
194
|
+
return /* @__PURE__ */ jsxs(
|
|
195
|
+
"button",
|
|
196
|
+
{
|
|
197
|
+
...rest,
|
|
198
|
+
type: "button",
|
|
199
|
+
className: `bl-tk bl-tk__bell ${className}`,
|
|
200
|
+
"aria-pressed": on,
|
|
201
|
+
"aria-label": showLabel ? void 0 : label,
|
|
202
|
+
disabled,
|
|
203
|
+
"data-size": size,
|
|
204
|
+
"data-disabled": disabled || void 0,
|
|
205
|
+
onClick: () => setOn(!on),
|
|
206
|
+
style: { ...accentVars(color), ...style },
|
|
207
|
+
children: [
|
|
208
|
+
/* @__PURE__ */ jsx("span", { className: "bl-tk__bell-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 12 12", children: [
|
|
209
|
+
/* @__PURE__ */ jsx("path", { d: "M5 1h2v1h1v1h1v4h1v1h1v1H1V8h1V7h1V3h1V2h1zM5 10h2v1H5z" }),
|
|
210
|
+
/* @__PURE__ */ jsxs("g", { className: "bl-tk__bell-waves", children: [
|
|
211
|
+
/* @__PURE__ */ jsx("rect", { x: "0", y: "3", width: "1", height: "2" }),
|
|
212
|
+
/* @__PURE__ */ jsx("rect", { x: "11", y: "3", width: "1", height: "2" })
|
|
213
|
+
] }),
|
|
214
|
+
/* @__PURE__ */ jsx("g", { className: "bl-tk__bell-slash", children: SLASH.map((i) => /* @__PURE__ */ jsx("rect", { x: i, y: i, width: "1", height: "1" }, i)) })
|
|
215
|
+
] }) }),
|
|
216
|
+
showLabel && /* @__PURE__ */ jsx("span", { className: "bl-tk__text", children: /* @__PURE__ */ jsx("span", { children: label }) })
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
var stdin_default = Switch;
|
|
222
|
+
export {
|
|
223
|
+
Checkbox,
|
|
224
|
+
NotifyBell,
|
|
225
|
+
Radio,
|
|
226
|
+
RadioGroup,
|
|
227
|
+
Switch,
|
|
228
|
+
stdin_default as default
|
|
229
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactNode } from 'react';
|
|
2
|
+
export interface TracingLineProps {
|
|
3
|
+
/** Entries. Each direct child gets a marker. */
|
|
4
|
+
children?: ReactNode;
|
|
5
|
+
/** Width of the left gutter holding the line, in px. */
|
|
6
|
+
gutter?: number;
|
|
7
|
+
lineWidth?: number;
|
|
8
|
+
markerSize?: number;
|
|
9
|
+
/** Marker position below each child's top, in px. */
|
|
10
|
+
markerOffset?: number;
|
|
11
|
+
/** Square markers and head, for a pixel look. */
|
|
12
|
+
square?: boolean;
|
|
13
|
+
/** Viewport position (0 top, 1 bottom) the line draws down to. */
|
|
14
|
+
activeLine?: number;
|
|
15
|
+
fromColor?: string;
|
|
16
|
+
toColor?: string;
|
|
17
|
+
glowColor?: string;
|
|
18
|
+
trackColor?: string;
|
|
19
|
+
/** Fill of markers the line hasn't reached yet. */
|
|
20
|
+
backgroundColor?: string;
|
|
21
|
+
className?: string;
|
|
22
|
+
style?: CSSProperties;
|
|
23
|
+
}
|
|
24
|
+
export default function TracingLine({ children, gutter, lineWidth, markerSize, markerOffset, square, activeLine, fromColor, toColor, glowColor, trackColor, backgroundColor, className, style }: TracingLineProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useInsertionEffect, useRef, useState } from "react";
|
|
4
|
+
const STYLE_ID = "bl-tracing-line";
|
|
5
|
+
const CSS = `
|
|
6
|
+
.bl-trace { position: relative; padding-left: var(--bl-gutter); }
|
|
7
|
+
.bl-trace__track, .bl-trace__fill { position: absolute; left: calc(var(--bl-gutter) / 2 - var(--bl-w) / 2); top: var(--bl-start); width: var(--bl-w); border-radius: var(--bl-w); }
|
|
8
|
+
.bl-trace__track { bottom: var(--bl-end); background: var(--bl-track); }
|
|
9
|
+
.bl-trace__fill { height: var(--bl-fill); background: linear-gradient(to bottom, var(--bl-from), var(--bl-to)); box-shadow: 0 0 12px var(--bl-glow); }
|
|
10
|
+
.bl-trace__head { position: absolute; left: calc(var(--bl-gutter) / 2); top: calc(var(--bl-start) + var(--bl-fill)); width: calc(var(--bl-w) * 4); height: calc(var(--bl-w) * 4); border-radius: var(--bl-dot-radius); background: var(--bl-to); box-shadow: 0 0 0 4px var(--bl-glow), 0 0 22px 4px var(--bl-glow); transform: translate(-50%, -50%); opacity: var(--bl-head-o); }
|
|
11
|
+
.bl-trace__marker { position: absolute; left: calc(var(--bl-gutter) / 2); width: var(--bl-marker); height: var(--bl-marker); border-radius: var(--bl-dot-radius); background: var(--bl-bg); box-shadow: inset 0 0 0 2px var(--bl-track); transform: translate(-50%, -50%) scale(0.85); transition: box-shadow 250ms, background-color 250ms, transform 250ms; }
|
|
12
|
+
.bl-trace__marker[data-passed] { background: var(--bl-to); box-shadow: inset 0 0 0 2px var(--bl-to), 0 0 0 4px var(--bl-glow); transform: translate(-50%, -50%) scale(1); }
|
|
13
|
+
.bl-trace__content > * { scroll-margin-top: 24px; }
|
|
14
|
+
@media (prefers-reduced-motion: reduce) { .bl-trace__marker { transition: none; } }
|
|
15
|
+
`;
|
|
16
|
+
function useStyles() {
|
|
17
|
+
useInsertionEffect(() => {
|
|
18
|
+
if (typeof document === "undefined") return;
|
|
19
|
+
const style = document.getElementById(STYLE_ID) ?? Object.assign(document.createElement("style"), { id: STYLE_ID });
|
|
20
|
+
style.textContent = CSS;
|
|
21
|
+
if (!style.isConnected) document.head.appendChild(style);
|
|
22
|
+
}, []);
|
|
23
|
+
}
|
|
24
|
+
function scrollParent(node) {
|
|
25
|
+
for (let el = node?.parentElement; el; el = el.parentElement) {
|
|
26
|
+
if (/(auto|scroll|overlay)/.test(getComputedStyle(el).overflowY) && el.scrollHeight > el.clientHeight) return el;
|
|
27
|
+
}
|
|
28
|
+
return window;
|
|
29
|
+
}
|
|
30
|
+
function TracingLine({
|
|
31
|
+
children,
|
|
32
|
+
gutter = 56,
|
|
33
|
+
lineWidth = 3,
|
|
34
|
+
markerSize = 14,
|
|
35
|
+
markerOffset = 14,
|
|
36
|
+
square = false,
|
|
37
|
+
activeLine = 0.55,
|
|
38
|
+
fromColor = "#41a6f6",
|
|
39
|
+
toColor = "#73eff7",
|
|
40
|
+
glowColor = "rgba(115,239,247,0.35)",
|
|
41
|
+
trackColor = "rgba(148,176,194,0.22)",
|
|
42
|
+
backgroundColor = "#1a1c2c",
|
|
43
|
+
className = "",
|
|
44
|
+
style
|
|
45
|
+
}) {
|
|
46
|
+
useStyles();
|
|
47
|
+
const rootRef = useRef(null);
|
|
48
|
+
const contentRef = useRef(null);
|
|
49
|
+
const [markers, setMarkers] = useState([]);
|
|
50
|
+
const [passed, setPassed] = useState(-1);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
const root = rootRef.current;
|
|
53
|
+
const content = contentRef.current;
|
|
54
|
+
const scroller = scrollParent(root);
|
|
55
|
+
let frame = 0;
|
|
56
|
+
let positions = [];
|
|
57
|
+
let lastPassed = -2;
|
|
58
|
+
const measure = () => {
|
|
59
|
+
positions = [...content.children].map((child) => child.offsetTop + markerOffset);
|
|
60
|
+
setMarkers((prev) => prev.length === positions.length && prev.every((y, i) => y === positions[i]) ? prev : positions);
|
|
61
|
+
const first = positions[0] ?? 0;
|
|
62
|
+
const last = positions[positions.length - 1] ?? root.offsetHeight;
|
|
63
|
+
root.style.setProperty("--bl-start", `${first}px`);
|
|
64
|
+
root.style.setProperty("--bl-end", `${root.offsetHeight - last}px`);
|
|
65
|
+
};
|
|
66
|
+
const update = () => {
|
|
67
|
+
frame = 0;
|
|
68
|
+
const rect = root.getBoundingClientRect();
|
|
69
|
+
const viewTop = scroller === window ? 0 : scroller.getBoundingClientRect().top;
|
|
70
|
+
const viewHeight = scroller === window ? window.innerHeight : scroller.clientHeight;
|
|
71
|
+
const line = viewTop + viewHeight * activeLine - rect.top;
|
|
72
|
+
const first = positions[0] ?? 0;
|
|
73
|
+
const last = positions[positions.length - 1] ?? rect.height;
|
|
74
|
+
const fill = Math.min(last - first, Math.max(0, line - first));
|
|
75
|
+
root.style.setProperty("--bl-fill", `${fill}px`);
|
|
76
|
+
root.style.setProperty("--bl-head-o", fill > 0 && fill < last - first ? "1" : "0");
|
|
77
|
+
let count = -1;
|
|
78
|
+
positions.forEach((y, i) => {
|
|
79
|
+
if (y <= first + fill + 0.5) count = i;
|
|
80
|
+
});
|
|
81
|
+
if (count !== lastPassed) {
|
|
82
|
+
lastPassed = count;
|
|
83
|
+
setPassed(count);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const schedule = () => {
|
|
87
|
+
if (!frame) frame = requestAnimationFrame(update);
|
|
88
|
+
};
|
|
89
|
+
const observer = new ResizeObserver(() => {
|
|
90
|
+
measure();
|
|
91
|
+
schedule();
|
|
92
|
+
});
|
|
93
|
+
observer.observe(content);
|
|
94
|
+
[...content.children].forEach((child) => observer.observe(child));
|
|
95
|
+
measure();
|
|
96
|
+
update();
|
|
97
|
+
scroller.addEventListener("scroll", schedule, { passive: true });
|
|
98
|
+
window.addEventListener("resize", schedule);
|
|
99
|
+
return () => {
|
|
100
|
+
cancelAnimationFrame(frame);
|
|
101
|
+
observer.disconnect();
|
|
102
|
+
scroller.removeEventListener("scroll", schedule);
|
|
103
|
+
window.removeEventListener("resize", schedule);
|
|
104
|
+
};
|
|
105
|
+
}, [activeLine, markerOffset, children]);
|
|
106
|
+
return /* @__PURE__ */ jsxs(
|
|
107
|
+
"div",
|
|
108
|
+
{
|
|
109
|
+
ref: rootRef,
|
|
110
|
+
className: `bl-trace ${className}`,
|
|
111
|
+
style: {
|
|
112
|
+
"--bl-gutter": `${gutter}px`,
|
|
113
|
+
"--bl-w": `${lineWidth}px`,
|
|
114
|
+
"--bl-marker": `${markerSize}px`,
|
|
115
|
+
"--bl-dot-radius": square ? "0" : "50%",
|
|
116
|
+
"--bl-from": fromColor,
|
|
117
|
+
"--bl-to": toColor,
|
|
118
|
+
"--bl-glow": glowColor,
|
|
119
|
+
"--bl-track": trackColor,
|
|
120
|
+
"--bl-bg": backgroundColor,
|
|
121
|
+
"--bl-start": "0px",
|
|
122
|
+
"--bl-end": "0px",
|
|
123
|
+
"--bl-fill": "0px",
|
|
124
|
+
"--bl-head-o": 0,
|
|
125
|
+
...style
|
|
126
|
+
},
|
|
127
|
+
children: [
|
|
128
|
+
/* @__PURE__ */ jsx("div", { className: "bl-trace__track", "aria-hidden": "true" }),
|
|
129
|
+
/* @__PURE__ */ jsx("div", { className: "bl-trace__fill", "aria-hidden": "true" }),
|
|
130
|
+
markers.map((y, index) => /* @__PURE__ */ jsx("span", { className: "bl-trace__marker", style: { top: y }, "data-passed": index <= passed ? "" : void 0, "aria-hidden": "true" }, index)),
|
|
131
|
+
/* @__PURE__ */ jsx("div", { className: "bl-trace__head", "aria-hidden": "true" }),
|
|
132
|
+
/* @__PURE__ */ jsx("div", { ref: contentRef, className: "bl-trace__content", children })
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
export {
|
|
138
|
+
TracingLine as default
|
|
139
|
+
};
|