chore: standardize web controls
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
<script>
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
export let value = "#000000";
|
||||
export let disabled = false;
|
||||
export let ariaLabel = "";
|
||||
let className = "";
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<input
|
||||
bind:value
|
||||
class={cn("ui-color-input", className)}
|
||||
type="color"
|
||||
{disabled}
|
||||
aria-label={ariaLabel}
|
||||
on:input
|
||||
on:change
|
||||
{...$$restProps}
|
||||
/>
|
||||
@@ -0,0 +1,19 @@
|
||||
<script>
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
export let element = null;
|
||||
export let accept = undefined;
|
||||
export let disabled = false;
|
||||
let className = "";
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<input
|
||||
bind:this={element}
|
||||
class={cn("ui-file-input", className)}
|
||||
type="file"
|
||||
{accept}
|
||||
{disabled}
|
||||
on:change
|
||||
{...$$restProps}
|
||||
/>
|
||||
@@ -2,11 +2,14 @@ export { default as AttentionDot } from "./attention-dot.svelte";
|
||||
export { default as Badge } from "./badge.svelte";
|
||||
export { default as Button } from "./button.svelte";
|
||||
export { default as Checkbox } from "./checkbox.svelte";
|
||||
export { default as ColorInput } from "./color-input.svelte";
|
||||
export { default as Dialog } from "./dialog.svelte";
|
||||
export { default as FileInput } from "./file-input.svelte";
|
||||
export { default as Input } from "./input.svelte";
|
||||
export { default as LegacyCard } from "./card.svelte";
|
||||
export { default as RadioGroup } from "./radio-group.svelte";
|
||||
export { default as RadioGroupItem } from "./radio-group-item.svelte";
|
||||
export { default as RangeInput } from "./range-input.svelte";
|
||||
export { default as Skeleton } from "./skeleton.svelte";
|
||||
export { default as Spinner } from "./spinner.svelte";
|
||||
export { default as ScrollArea } from "./scroll-area.svelte";
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
export { Accordion, Label, Select, Separator, Switch, Tabs, Tooltip } from "bits-ui";
|
||||
export {
|
||||
Accordion,
|
||||
Label,
|
||||
ScrollArea,
|
||||
Select,
|
||||
Separator,
|
||||
Slider,
|
||||
Switch,
|
||||
Tabs,
|
||||
Tooltip,
|
||||
} from "bits-ui";
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
<script>
|
||||
import { cn } from "$lib/utils.js";
|
||||
import { Slider } from "./primitives.js";
|
||||
|
||||
export let value = 0;
|
||||
export let min = undefined;
|
||||
export let max = undefined;
|
||||
export let step = undefined;
|
||||
export let disabled = false;
|
||||
export let ariaLabel = "";
|
||||
export let onValueChange = () => {};
|
||||
export let onValueCommit = () => {};
|
||||
let className = "";
|
||||
export { className as class };
|
||||
|
||||
$: sliderValue = Number(value ?? min ?? 0);
|
||||
$: sliderMin = min === undefined ? undefined : Number(min);
|
||||
$: sliderMax = max === undefined ? undefined : Number(max);
|
||||
$: sliderStep = step === undefined ? undefined : Number(step);
|
||||
|
||||
function handleValueChange(next) {
|
||||
value = next;
|
||||
onValueChange(next);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Slider.Root
|
||||
class={cn("ui-range-input", className)}
|
||||
type="single"
|
||||
value={sliderValue}
|
||||
min={sliderMin}
|
||||
max={sliderMax}
|
||||
step={sliderStep}
|
||||
{disabled}
|
||||
onValueChange={handleValueChange}
|
||||
{onValueCommit}
|
||||
{...$$restProps}
|
||||
>
|
||||
<Slider.Range class="ui-range-input__range" />
|
||||
<Slider.Thumb class="ui-range-input__thumb" aria-label={ariaLabel} />
|
||||
</Slider.Root>
|
||||
|
||||
<style>
|
||||
:global(.ui-range-input) {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 18px;
|
||||
touch-action: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
:global(.ui-range-input::before) {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
height: 5px;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in srgb, var(--admin-border-strong, var(--border)) 70%, transparent);
|
||||
}
|
||||
|
||||
:global(.ui-range-input__range) {
|
||||
position: absolute;
|
||||
height: 5px;
|
||||
border-radius: 999px;
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
:global(.ui-range-input__thumb) {
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid var(--accent);
|
||||
border-radius: 999px;
|
||||
background: var(--admin-surface, var(--panel));
|
||||
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.24);
|
||||
outline: none;
|
||||
transition:
|
||||
box-shadow 0.14s ease,
|
||||
transform 0.14s ease;
|
||||
}
|
||||
|
||||
:global(.ui-range-input__thumb:hover) {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
:global(.ui-range-input__thumb:focus-visible) {
|
||||
box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 24%, transparent);
|
||||
}
|
||||
|
||||
:global(.ui-range-input[data-disabled]) {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
@@ -1,15 +1,86 @@
|
||||
<script>
|
||||
import { cn } from "$lib/utils.js";
|
||||
import { ScrollArea as ScrollAreaPrimitive } from "./primitives.js";
|
||||
|
||||
export let maxHeight = "100%";
|
||||
export let element = null;
|
||||
export let type = "auto";
|
||||
let className = "";
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={element}
|
||||
class={`scroll-area ${className}`.trim()}
|
||||
<ScrollAreaPrimitive.Root
|
||||
class={cn("scroll-area scroll-area--mono", className)}
|
||||
style={`max-height:${maxHeight};`}
|
||||
{type}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
<ScrollAreaPrimitive.Viewport bind:ref={element} class="scroll-area__viewport">
|
||||
<slot />
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
<ScrollAreaPrimitive.Scrollbar class="scroll-area__scrollbar" orientation="vertical">
|
||||
<ScrollAreaPrimitive.Thumb class="scroll-area__thumb" />
|
||||
</ScrollAreaPrimitive.Scrollbar>
|
||||
<ScrollAreaPrimitive.Scrollbar
|
||||
class="scroll-area__scrollbar scroll-area__scrollbar--horizontal"
|
||||
orientation="horizontal"
|
||||
>
|
||||
<ScrollAreaPrimitive.Thumb class="scroll-area__thumb" />
|
||||
</ScrollAreaPrimitive.Scrollbar>
|
||||
<ScrollAreaPrimitive.Corner class="scroll-area__corner" />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
|
||||
<style>
|
||||
:global(.scroll-area) {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global(.scroll-area__viewport) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: inherit;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
:global(.scroll-area__scrollbar) {
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
touch-action: none;
|
||||
user-select: none;
|
||||
padding: 2px;
|
||||
transition: background 0.14s ease;
|
||||
}
|
||||
|
||||
:global(.scroll-area__scrollbar[data-orientation="vertical"]) {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
:global(.scroll-area__scrollbar[data-orientation="horizontal"]) {
|
||||
flex-direction: column;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
:global(.scroll-area__thumb) {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
border-radius: 999px;
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
var(--admin-muted, var(--muted)) 34%,
|
||||
var(--admin-border, var(--border))
|
||||
);
|
||||
}
|
||||
|
||||
:global(.scroll-area__thumb:hover) {
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
var(--admin-muted, var(--muted)) 48%,
|
||||
var(--admin-border, var(--border))
|
||||
);
|
||||
}
|
||||
|
||||
:global(.scroll-area__corner) {
|
||||
background: transparent;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user