Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ColorField and ColorWheel #510

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions apps/docs/src/examples/color-field.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
.color-field {
display: flex;
flex-direction: column;
gap: 4px;
}

.color-field__label {
color: hsl(240 6% 10%);
font-size: 14px;
font-weight: 500;
user-select: none;
}

.color-field__input {
display: inline-flex;
width: 200px;
border-radius: 6px;
padding: 6px 12px;
font-size: 16px;
outline: none;
background-color: white;
border: 1px solid hsl(240 6% 90%);
color: hsl(240 4% 16%);
transition:
border-color 250ms,
color 250ms;
}

.color-field__input:hover {
border-color: hsl(240 5% 65%);
}

.color-field__input:focus-visible {
outline: 2px solid hsl(200 98% 39%);
outline-offset: 2px;
}

.color-field__input[data-invalid] {
border-color: hsl(0 72% 51%);
color: hsl(0 72% 51%);
}

.color-field__input::placeholder {
color: hsl(240 4% 46%);
}

.color-field__description {
color: hsl(240 5% 26%);
font-size: 12px;
user-select: none;
}

.color-field__error-message {
color: hsl(0 72% 51%);
font-size: 12px;
user-select: none;
}

[data-kb-theme="dark"] .color-field__input {
background-color: hsl(240 4% 16%);
border: 1px solid hsl(240 5% 34%);
color: hsl(0 100% 100% / 0.9);
}

[data-kb-theme="dark"] .color-field__input:hover {
border-color: hsl(240 4% 46%);
}

[data-kb-theme="dark"] .color-field__input[data-invalid] {
border-color: hsl(0 72% 51%);
color: hsl(0 72% 51%);
}

[data-kb-theme="dark"] .color-field__input::placeholder {
color: hsl(0 100% 100% / 0.5);
}

[data-kb-theme="dark"] .color-field__label {
color: hsl(240 5% 84%);
}

[data-kb-theme="dark"] .color-field__description {
color: hsl(240 5% 65%);
}
117 changes: 117 additions & 0 deletions apps/docs/src/examples/color-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { createSignal } from "solid-js";
import { ColorField } from "../../../../packages/core/src/colors/color-field";

import style from "./color-field.module.css";

export function BasicExample() {
return (
<ColorField class={style["color-field"]}>
<ColorField.Label class={style["color-field__label"]}>
Favorite color
</ColorField.Label>
<ColorField.Input class={style["color-field__input"]} />
</ColorField>
);
}

export function DefaultValueExample() {
return (
<ColorField class={style["color-field"]} defaultValue="#7f007f">
<ColorField.Label class={style["color-field__label"]}>
Favorite color
</ColorField.Label>
<ColorField.Input class={style["color-field__input"]} />
</ColorField>
);
}

export function ControlledExample() {
const [value, setValue] = createSignal("#7f007f");

return (
<>
<ColorField
class={style["color-field"]}
value={value()}
onChange={setValue}
>
<ColorField.Label class={style["color-field__label"]}>
Favorite color
</ColorField.Label>
<ColorField.Input class={style["color-field__input"]} />
</ColorField>
<p class="not-prose text-sm mt-4">Your favorite color is: {value()}</p>
</>
);
}

export function DescriptionExample() {
return (
<ColorField class={style["color-field"]}>
<ColorField.Label class={style["color-field__label"]}>
Favorite color
</ColorField.Label>
<ColorField.Input class={style["color-field__input"]} />
<ColorField.Description class={style["color-field__description"]}>
Choose the color you like the most.
</ColorField.Description>
</ColorField>
);
}

export function ErrorMessageExample() {
const [value, setValue] = createSignal("#7f007f");

return (
<ColorField
class={style["color-field"]}
value={value()}
onChange={setValue}
validationState={value() !== "#000000" ? "invalid" : "valid"}
>
<ColorField.Label class={style["color-field__label"]}>
Favorite color
</ColorField.Label>
<ColorField.Input class={style["color-field__input"]} />
<ColorField.ErrorMessage class={style["color-field__error-message"]}>
Hmm, I prefer black.
</ColorField.ErrorMessage>
</ColorField>
);
}

export function HTMLFormExample() {
let formRef: HTMLFormElement | undefined;

const onSubmit = (e: SubmitEvent) => {
e.preventDefault();
e.stopPropagation();

const formData = new FormData(formRef);

alert(JSON.stringify(Object.fromEntries(formData), null, 2));
};

return (
<form
ref={formRef}
onSubmit={onSubmit}
class="flex flex-col items-center space-y-6"
>
<ColorField class={style["color-field"]} name="favorite-color">
<ColorField.Label class={style["color-field__label"]}>
Favorite color
</ColorField.Label>
<ColorField.Input class={style["color-field__input"]} />
</ColorField>
<div class="flex space-x-2">
<button type="reset" class="kb-button">
Reset
</button>
<button type="submit" class="kb-button-primary">
Submit
</button>
</div>
</form>
);
}
33 changes: 33 additions & 0 deletions apps/docs/src/examples/color-wheel.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.ColorWheelRoot {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
user-select: none;
touch-action: none;
}

.ColorWheelTrack {
position: relative;
height: 160px;
width: 160px;
}

.ColorWheelThumb {
display: block;
width: 16px;
height: 16px;
border-radius: 9999px;
border: 2px solid #fff;
box-shadow: 0 0 0 1px #0000006b;
}

.ColorWheelThumb:focus {
outline: none;
}

.ColorWheelLabel {
display: flex;
justify-content: space-between;
width: 100%;
}
131 changes: 131 additions & 0 deletions apps/docs/src/examples/color-wheel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { createSignal } from "solid-js";
import { ColorWheel } from "../../../../packages/core/src/colors/color-wheel";
import { parseColor } from "../../../../packages/core/src/colors/utils";
import style from "./color-wheel.module.css";

export function BasicExample() {
return (
<ColorWheel class={style.ColorWheelRoot} thickness={24}>
<ColorWheel.Track class={style.ColorWheelTrack}>
<ColorWheel.Thumb class={style.ColorWheelThumb}>
<ColorWheel.Input />
</ColorWheel.Thumb>
</ColorWheel.Track>
</ColorWheel>
);
}

export function DefaultValueExample() {
return (
<ColorWheel
class={style.ColorWheelRoot}
defaultValue={parseColor("hsl(80, 100%, 50%)")}
thickness={24}
>
<ColorWheel.Track class={style.ColorWheelTrack}>
<ColorWheel.Thumb class={style.ColorWheelThumb}>
<ColorWheel.Input />
</ColorWheel.Thumb>
</ColorWheel.Track>
</ColorWheel>
);
}

export function ThicknessExample() {
return (
<ColorWheel class={style.ColorWheelRoot} thickness={56}>
<ColorWheel.Track class={style.ColorWheelTrack}>
<ColorWheel.Thumb class={style.ColorWheelThumb}>
<ColorWheel.Input />
</ColorWheel.Thumb>
</ColorWheel.Track>
</ColorWheel>
);
}

export function ControlledValueExample() {
const [value, setValue] = createSignal(parseColor("hsl(0, 100%, 50%)"));

return (
<>
<ColorWheel
class={style.ColorWheelRoot}
value={value()}
onChange={setValue}
thickness={24}
>
<ColorWheel.Track class={style.ColorWheelTrack}>
<ColorWheel.Thumb class={style.ColorWheelThumb}>
<ColorWheel.Input />
</ColorWheel.Thumb>
</ColorWheel.Track>
</ColorWheel>
<p class="not-prose text-sm mt-4">
Current color value: {value().toString("hsl")}
</p>
</>
);
}

export function CustomValueLabelExample() {
return (
<ColorWheel
class={style.ColorWheelRoot}
thickness={24}
getValueLabel={(color) =>
color
.toFormat("hsl")
.withChannelValue("saturation", 100)
.withChannelValue("lightness", 50)
.withChannelValue("alpha", 1)
.toString()
}
>
<div class={style.ColorWheelLabel}>
<ColorWheel.ValueLabel />
</div>
<ColorWheel.Track class={style.ColorWheelTrack}>
<ColorWheel.Thumb class={style.ColorWheelThumb}>
<ColorWheel.Input />
</ColorWheel.Thumb>
</ColorWheel.Track>
</ColorWheel>
);
}

export function HTMLFormExample() {
let formRef: HTMLFormElement | undefined;

const onSubmit = (e: SubmitEvent) => {
e.preventDefault();
e.stopPropagation();

const formData = new FormData(formRef);

alert(JSON.stringify(Object.fromEntries(formData), null, 2));
};

return (
<form
ref={formRef}
onSubmit={onSubmit}
class="flex flex-col items-center space-y-6"
>
<ColorWheel class={style.ColorWheelRoot} name="hue" thickness={24}>
<ColorWheel.Track class={style.ColorWheelTrack}>
<ColorWheel.Thumb class={style.ColorWheelThumb}>
<ColorWheel.Input />
</ColorWheel.Thumb>
</ColorWheel.Track>
</ColorWheel>
<div class="flex space-x-2">
<button type="reset" class="kb-button">
Reset
</button>
<button type="submit" class="kb-button-primary">
Submit
</button>
</div>
</form>
);
}
10 changes: 10 additions & 0 deletions apps/docs/src/routes/docs/core.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ const CORE_NAV_SECTIONS: NavSection[] = [
title: "Collapsible",
href: "/docs/core/components/collapsible",
},
{
title: "Color Field",
href: "/docs/core/components/color-field",
status: "new",
},
{
title: "Color Wheel",
href: "/docs/core/components/color-wheel",
status: "new",
},
{
title: "Combobox",
href: "/docs/core/components/combobox",
Expand Down
Loading