From dbe9fc4353a561ae6cbfb19a320576197bf28104 Mon Sep 17 00:00:00 2001 From: Jerry_wu <409187100@qq.com> Date: Fri, 13 Sep 2024 13:29:50 +0800 Subject: [PATCH 01/25] feat: add a new switch component --- packages/kit-headless/src/components/switch/index.ts | 1 + packages/kit-headless/src/components/switch/switch.test.ts | 0 2 files changed, 1 insertion(+) create mode 100644 packages/kit-headless/src/components/switch/index.ts create mode 100644 packages/kit-headless/src/components/switch/switch.test.ts diff --git a/packages/kit-headless/src/components/switch/index.ts b/packages/kit-headless/src/components/switch/index.ts new file mode 100644 index 000000000..336ce12bb --- /dev/null +++ b/packages/kit-headless/src/components/switch/index.ts @@ -0,0 +1 @@ +export {} diff --git a/packages/kit-headless/src/components/switch/switch.test.ts b/packages/kit-headless/src/components/switch/switch.test.ts new file mode 100644 index 000000000..e69de29bb From a219d8fe55d4d6d2c60882e6d97e53a7f7dda0e8 Mon Sep 17 00:00:00 2001 From: Jerry_wu <409187100@qq.com> Date: Thu, 19 Sep 2024 19:24:23 +0800 Subject: [PATCH 02/25] init debug --- .changeset/poor-spiders-clap.md | 6 ++++++ .../src/routes/docs/styled/switch/examples/hero.tsx | 12 ++++++++++++ .../website/src/routes/docs/styled/switch/index.mdx | 5 +++++ .../kit-styled/src/components/switch/switch.tsx | 13 +++++++++++++ packages/kit-styled/src/index.ts | 1 + 5 files changed, 37 insertions(+) create mode 100644 .changeset/poor-spiders-clap.md create mode 100644 apps/website/src/routes/docs/styled/switch/examples/hero.tsx create mode 100644 apps/website/src/routes/docs/styled/switch/index.mdx create mode 100644 packages/kit-styled/src/components/switch/switch.tsx diff --git a/.changeset/poor-spiders-clap.md b/.changeset/poor-spiders-clap.md new file mode 100644 index 000000000..72193b11f --- /dev/null +++ b/.changeset/poor-spiders-clap.md @@ -0,0 +1,6 @@ +--- +'@qwik-ui/headless': major +'@qwik-ui/styled': major +--- + +add a new switch component diff --git a/apps/website/src/routes/docs/styled/switch/examples/hero.tsx b/apps/website/src/routes/docs/styled/switch/examples/hero.tsx new file mode 100644 index 000000000..d83b69617 --- /dev/null +++ b/apps/website/src/routes/docs/styled/switch/examples/hero.tsx @@ -0,0 +1,12 @@ +import { component$ } from '@builder.io/qwik'; +import { Switch } from '~/components/ui'; + + +export default component$(() => { + return ( + <> + sdsdsd + + + ); +}); diff --git a/apps/website/src/routes/docs/styled/switch/index.mdx b/apps/website/src/routes/docs/styled/switch/index.mdx new file mode 100644 index 000000000..22cf1caa3 --- /dev/null +++ b/apps/website/src/routes/docs/styled/switch/index.mdx @@ -0,0 +1,5 @@ +--- +title: Qwik UI | Styled Radio Group Component +--- + +sdsds diff --git a/packages/kit-styled/src/components/switch/switch.tsx b/packages/kit-styled/src/components/switch/switch.tsx new file mode 100644 index 000000000..212ba16ef --- /dev/null +++ b/packages/kit-styled/src/components/switch/switch.tsx @@ -0,0 +1,13 @@ +import { type PropsOf, component$ } from '@builder.io/qwik'; +import { cn } from '@qwik-ui/utils'; + + +export const Switch = component$>(({ ...props }) => { + return ( + + ); +}); diff --git a/packages/kit-styled/src/index.ts b/packages/kit-styled/src/index.ts index 6c2b24bf7..1746808e8 100644 --- a/packages/kit-styled/src/index.ts +++ b/packages/kit-styled/src/index.ts @@ -21,3 +21,4 @@ export * from './components/textarea/textarea'; export * from './components/toggle/toggle'; export * from './components/toggle-group/toggle-group'; export * from './components/dropdown/dropdown'; +export * from './components/switch/switch'; From 7a5f6272498a4e327f8c046a061e796d2eeafb89 Mon Sep 17 00:00:00 2001 From: Jerry_wu <409187100@qq.com> Date: Tue, 24 Sep 2024 09:26:20 +0800 Subject: [PATCH 03/25] switch is developing --- .../docs/headless/switch/examples/hero.tsx | 31 ++ .../src/routes/docs/headless/switch/index.mdx | 274 ++++++++++++++++++ .../docs/headless/switch/snippets/switch.css | 0 .../src/components/switch/switch-context.tsx | 12 + .../src/components/switch/switch-root.tsx | 27 ++ packages/kit-headless/src/index.ts | 1 + .../src/components/switch/switch.tsx | 14 +- 7 files changed, 354 insertions(+), 5 deletions(-) create mode 100644 apps/website/src/routes/docs/headless/switch/examples/hero.tsx create mode 100644 apps/website/src/routes/docs/headless/switch/index.mdx create mode 100644 apps/website/src/routes/docs/headless/switch/snippets/switch.css create mode 100644 packages/kit-headless/src/components/switch/switch-context.tsx create mode 100644 packages/kit-headless/src/components/switch/switch-root.tsx diff --git a/apps/website/src/routes/docs/headless/switch/examples/hero.tsx b/apps/website/src/routes/docs/headless/switch/examples/hero.tsx new file mode 100644 index 000000000..e02b83356 --- /dev/null +++ b/apps/website/src/routes/docs/headless/switch/examples/hero.tsx @@ -0,0 +1,31 @@ +import { component$, useStyles$ } from '@builder.io/qwik'; +import { Select } from '@qwik-ui/headless'; + +export default component$(() => { + useStyles$(styles); + const users = ['Tim', 'Ryan', 'Jim', 'Jessie', 'Abby']; + + return ( + + Logged in users + + + + + {users.map((user) => ( + + {user} + + + + + ))} + + + ); +}); + +// internal +import styles from '../snippets/select.css?inline'; +import { LuCheck } from '@qwikest/icons/lucide'; + diff --git a/apps/website/src/routes/docs/headless/switch/index.mdx b/apps/website/src/routes/docs/headless/switch/index.mdx new file mode 100644 index 000000000..4c12c72d1 --- /dev/null +++ b/apps/website/src/routes/docs/headless/switch/index.mdx @@ -0,0 +1,274 @@ +--- +title: Qwik UI | Tabs +--- + +import { statusByComponent } from '~/_state/component-statuses'; + + + +# Tabs + +Explore and switch between different views using tabs + + + +## Building blocks + +### The full version + + + +### The short version + + + +> The "short version" πŸ‘† will be automatically transformed into the full ARIA compliant version + +## Vertical + +by default, the tabs are horizontal, but you can adjust the underlying behavior to be vertical by setting the `vertical` prop to true. + + + +## Disabled + +You can disable a tab by setting the `disabled` prop to true. + + + +## Behavior + +### Automatic + +The default behavior of the tabs is manual, which means that the tabs will automatically switch to the next tab when the user hovers over the tab. You can change this behavior by setting the `behavior` prop to `automatic`. + + + +### Manual + +You can set the behavior to manual, which means that the tabs will not automatically switch to the next tab when the user presses the right arrow key, and to the previous tab when the user presses the left arrow key. + + + +## Dynamic + + + +## onSelectedIndexChange$ + +You can listen to changes in the selected index by subscribing to the `onSelectedIndexChange$` store. + + + +## bind:selectedIndex + +You can provide a signal for the selected index with the `bind:selectedIndex={yourSignal}` and it will be used directly. This is a more efficient version of `onSelectedIndexChange$`. + +## bind:selectedTabId + +You can provide a signal for the selected tab id with the `bind:selectedTabId={yourSignal}` +and it will be used directly. + +πŸ’‘ You can manually set the `tabId` prop on each tab to be able to select any tab by its ID. + + + +## Add a "selected" prop + +You can add a "selected" prop to the Tab component to make it selected by default. + + + +## onClick$ + +You can pass a custom `onClick$` handler. + + + +## Creating reusable Tabs + +In order to remove the need for a linking `value` prop for each Tab and Tabs.Panel pair, we have chosen to create the Tabs component as an [inline component](https://qwik.builder.io/docs/components/overview/#inline-components). + +By consequence, the Tabs component needs to be aware of its children. If you want to create your custom reusable Tabs.List/Tab/Tabs.Panel components, you will have to pass them to the Tabs component through its `Tabs.List`, `Tab`, and `Tabs.Panel` props: + +```tsx +const CustomTabs = (props: PropsOf) => ( + +); +``` + +
+ + + If you don't do the above, the Tabs component cannot know if your CustomTab component is + a Tab component. + + + + +## Accessibility + +### Keyboard interaction + + + +## API + +### Tabs + + void', + description: 'An event hook that gets notified whenever the selected index changes', + }, + { + name: 'onSelectedTabIdChange$', + type: 'function', + info: '(index: string) => void', + description: 'An event hook that gets notified whenever the selected tabId changes', + }, + { + name: 'bind:selectedIndex', + type: 'signal', + info: 'Signal', + description: + 'A signal that binds the selected index. This is a more efficient version of `selectedIndex` + `onSelectedIndexChange$`', + }, + { + name: 'bind:selectedTabId', + type: 'signal', + info: 'Signal', + description: + 'A signal that binds the selected tabId. This is a more efficient version of `selectedTabId` + `onSelectedTabIdChange$`', + }, + { + name: 'tabClass', + type: 'string', + description: 'The class name to apply to tabs', + }, + { + name: 'panelClass', + type: 'string', + description: 'The class name to apply to panels', + }, + ]} +/> + +### Tab + + + +### Tabs.Panel + +{label}`', + }, + { + name: 'selected', + type: 'boolean', + description: 'Select this tab by default', + }, + ]} +/> diff --git a/apps/website/src/routes/docs/headless/switch/snippets/switch.css b/apps/website/src/routes/docs/headless/switch/snippets/switch.css new file mode 100644 index 000000000..e69de29bb diff --git a/packages/kit-headless/src/components/switch/switch-context.tsx b/packages/kit-headless/src/components/switch/switch-context.tsx new file mode 100644 index 000000000..17a7db5dd --- /dev/null +++ b/packages/kit-headless/src/components/switch/switch-context.tsx @@ -0,0 +1,12 @@ +import { createContextId, type Signal } from '@builder.io/qwik'; + +export interface SwitchState { + 'bind:checked': Signal; + defaultChecked: boolean; + disabled: boolean; + label: string; +} +// +export type SwitchContextState = Omit & { bindChecked: Signal } + +export const SwitchContext = createContextId('SwitchContext'); diff --git a/packages/kit-headless/src/components/switch/switch-root.tsx b/packages/kit-headless/src/components/switch/switch-root.tsx new file mode 100644 index 000000000..1788a05ed --- /dev/null +++ b/packages/kit-headless/src/components/switch/switch-root.tsx @@ -0,0 +1,27 @@ +import { + component$, + Slot, + useContextProvider, + type PropsOf +} from '@builder.io/qwik'; +import { type SwitchContextState, type SwitchState, SwitchContext } from './switch-context'; +export type SwitchProps = PropsOf<'div'> & SwitchState; + + + +export const SwitchRoot = component$(({defaultChecked, disabled, label, ...rest}: SwitchProps) => { + const context: SwitchContextState = { + defaultChecked, + disabled, + label, + bindChecked: rest['bind:checked'] + } + + useContextProvider(SwitchContext, context) + + return ( +
+ +
+ ); +}) diff --git a/packages/kit-headless/src/index.ts b/packages/kit-headless/src/index.ts index 77c2e051a..08ad64599 100644 --- a/packages/kit-headless/src/index.ts +++ b/packages/kit-headless/src/index.ts @@ -19,3 +19,4 @@ export * as Tooltip from './components/tooltip'; export * as Dropdown from './components/dropdown'; export * as Combobox from './components/combobox'; export { Polymorphic } from './components/polymorphic'; +export * as Switch from './components/switch'; diff --git a/packages/kit-styled/src/components/switch/switch.tsx b/packages/kit-styled/src/components/switch/switch.tsx index 212ba16ef..15a6649b8 100644 --- a/packages/kit-styled/src/components/switch/switch.tsx +++ b/packages/kit-styled/src/components/switch/switch.tsx @@ -4,10 +4,14 @@ import { cn } from '@qwik-ui/utils'; export const Switch = component$>(({ ...props }) => { return ( - +
+ sdsd + +
+ ); }); From 5850f72027c7f45ac1a05c3b0cb6767b9b3f6bdd Mon Sep 17 00:00:00 2001 From: Jerry_wu <409187100@qq.com> Date: Tue, 24 Sep 2024 15:36:18 +0800 Subject: [PATCH 04/25] headless Switch --- .../docs/headless/switch/examples/hero.tsx | 29 ++++--------------- .../src/components/switch/index.ts | 4 ++- .../src/components/switch/switch-context.tsx | 12 ++++---- .../src/components/switch/switch-input.tsx | 14 +++++++++ .../src/components/switch/switch-lable.tsx | 10 +++++++ .../src/components/switch/switch-root.tsx | 5 ++-- 6 files changed, 42 insertions(+), 32 deletions(-) create mode 100644 packages/kit-headless/src/components/switch/switch-input.tsx create mode 100644 packages/kit-headless/src/components/switch/switch-lable.tsx diff --git a/apps/website/src/routes/docs/headless/switch/examples/hero.tsx b/apps/website/src/routes/docs/headless/switch/examples/hero.tsx index e02b83356..f5d8cfe43 100644 --- a/apps/website/src/routes/docs/headless/switch/examples/hero.tsx +++ b/apps/website/src/routes/docs/headless/switch/examples/hero.tsx @@ -1,31 +1,14 @@ -import { component$, useStyles$ } from '@builder.io/qwik'; -import { Select } from '@qwik-ui/headless'; +import { component$ } from '@builder.io/qwik'; +import { Switch } from '@qwik-ui/headless'; export default component$(() => { - useStyles$(styles); - const users = ['Tim', 'Ryan', 'Jim', 'Jessie', 'Abby']; return ( - - Logged in users - - - - - {users.map((user) => ( - - {user} - - - - - ))} - - + + sdsds + + ); }); -// internal -import styles from '../snippets/select.css?inline'; -import { LuCheck } from '@qwikest/icons/lucide'; diff --git a/packages/kit-headless/src/components/switch/index.ts b/packages/kit-headless/src/components/switch/index.ts index 336ce12bb..234a55a00 100644 --- a/packages/kit-headless/src/components/switch/index.ts +++ b/packages/kit-headless/src/components/switch/index.ts @@ -1 +1,3 @@ -export {} +export { SwitchRoot as Root } from './switch-root' +export { SwitchInput as Input } from './switch-input' +export { SwitchLable as Label } from './switch-lable' diff --git a/packages/kit-headless/src/components/switch/switch-context.tsx b/packages/kit-headless/src/components/switch/switch-context.tsx index 17a7db5dd..f55d083a1 100644 --- a/packages/kit-headless/src/components/switch/switch-context.tsx +++ b/packages/kit-headless/src/components/switch/switch-context.tsx @@ -1,12 +1,12 @@ -import { createContextId, type Signal } from '@builder.io/qwik'; +import { createContextId, QRL, type Signal } from '@builder.io/qwik'; export interface SwitchState { - 'bind:checked': Signal; - defaultChecked: boolean; - disabled: boolean; - label: string; + 'bind:checked'?: Signal; + defaultChecked?: boolean; + disabled?: boolean; + onChange$?: QRL<() => void> } // -export type SwitchContextState = Omit & { bindChecked: Signal } +export type SwitchContextState = Omit & { bindChecked?: Signal } export const SwitchContext = createContextId('SwitchContext'); diff --git a/packages/kit-headless/src/components/switch/switch-input.tsx b/packages/kit-headless/src/components/switch/switch-input.tsx new file mode 100644 index 000000000..ae116452f --- /dev/null +++ b/packages/kit-headless/src/components/switch/switch-input.tsx @@ -0,0 +1,14 @@ + +import { component$, PropsOf, useContext } from '@builder.io/qwik'; +import { SwitchContext } from './switch-context'; +export const SwitchInput = component$>(() => { + const context = useContext(SwitchContext) + return ( + + ); + }, +); diff --git a/packages/kit-headless/src/components/switch/switch-lable.tsx b/packages/kit-headless/src/components/switch/switch-lable.tsx new file mode 100644 index 000000000..b336b2266 --- /dev/null +++ b/packages/kit-headless/src/components/switch/switch-lable.tsx @@ -0,0 +1,10 @@ + +import { component$, PropsOf, Slot } from '@builder.io/qwik'; +export const SwitchLable = component$>(() => { + return ( + + ); + }, +); diff --git a/packages/kit-headless/src/components/switch/switch-root.tsx b/packages/kit-headless/src/components/switch/switch-root.tsx index 1788a05ed..92e6757ba 100644 --- a/packages/kit-headless/src/components/switch/switch-root.tsx +++ b/packages/kit-headless/src/components/switch/switch-root.tsx @@ -9,12 +9,13 @@ export type SwitchProps = PropsOf<'div'> & SwitchState; -export const SwitchRoot = component$(({defaultChecked, disabled, label, ...rest}: SwitchProps) => { +export const SwitchRoot = component$(({defaultChecked, disabled, label, onChange$, ...rest}: SwitchProps) => { const context: SwitchContextState = { defaultChecked, disabled, label, - bindChecked: rest['bind:checked'] + bindChecked: rest['bind:checked'], + onChange$: onChange$ } useContextProvider(SwitchContext, context) From 61e5eb448cb2c458748eef39502e09197c200892 Mon Sep 17 00:00:00 2001 From: Jerry_wu <409187100@qq.com> Date: Wed, 25 Sep 2024 18:54:51 +0800 Subject: [PATCH 05/25] add css style --- .../docs/headless/switch/examples/hero.tsx | 7 +- .../docs/headless/switch/snippets/switch.css | 110 ++++++++++++++++++ 2 files changed, 114 insertions(+), 3 deletions(-) diff --git a/apps/website/src/routes/docs/headless/switch/examples/hero.tsx b/apps/website/src/routes/docs/headless/switch/examples/hero.tsx index f5d8cfe43..a3d6bba2c 100644 --- a/apps/website/src/routes/docs/headless/switch/examples/hero.tsx +++ b/apps/website/src/routes/docs/headless/switch/examples/hero.tsx @@ -1,10 +1,10 @@ -import { component$ } from '@builder.io/qwik'; +import { component$, useStyles$ } from '@builder.io/qwik'; import { Switch } from '@qwik-ui/headless'; export default component$(() => { - + useStyles$(styles); return ( - + sdsds @@ -12,3 +12,4 @@ export default component$(() => { }); +import styles from '../snippets/switch.css?inline'; diff --git a/apps/website/src/routes/docs/headless/switch/snippets/switch.css b/apps/website/src/routes/docs/headless/switch/snippets/switch.css index e69de29bb..84e128782 100644 --- a/apps/website/src/routes/docs/headless/switch/snippets/switch.css +++ b/apps/website/src/routes/docs/headless/switch/snippets/switch.css @@ -0,0 +1,110 @@ +.switch { + --brand-hue: 200; + --brand-saturation: 100%; + --brand-lightness: 50%; + + --brand-light: hsl(var(--brand-hue) var(--brand-saturation) var(--brand-lightness)); + --text1-light: hsl(var(--brand-hue) var(--brand-saturation) 10%); + --surface1-light: hsl(var(--brand-hue) 25% 100%); + + --brand-dark: hsl(var(--brand-hue) calc(var(--brand-saturation) / 2) calc(var(--brand-lightness) / 1.5)); + --text1-dark: hsl(var(--brand-hue) 15% 85%); + --surface1-dark: hsl(var(--brand-hue) 10% 10%); + --thumb-size: 2rem; + --thumb: hsl(0 0% 100%); + --thumb-highlight: hsl(0 0% 0% / 25%); + + --track-size: calc(var(--thumb-size) * 2); + --track-padding: 2px; + --track-inactive: hsl(80 0% 80%); + --track-active: hsl(80 60% 45%); + + --thumb-color: var(--thumb); + --thumb-color-highlight: var(--thumb-highlight); + --track-color-inactive: var(--track-inactive); + --track-color-active: var(--track-active); + + --isLTR: 1; + + display: flex; + align-items: center; + gap: 2ch; + justify-content: space-between; + + cursor: pointer; + user-select: none; + -webkit-tap-highlight-color: transparent; + + + &>input { + --thumb-position: 0%; + --thumb-transition-duration: .25s; + + padding: var(--track-padding); + background: var(--track-color-inactive); + inline-size: var(--track-size); + block-size: var(--thumb-size); + border-radius: var(--track-size); + + appearance: none; + pointer-events: none; + touch-action: pan-y; + border: none; + outline-offset: 5px; + box-sizing: content-box; + + flex-shrink: 0; + display: grid; + align-items: center; + grid: [track] 1fr / [track] 1fr; + + transition: background-color .25s ease; + + &::before { + --highlight-size: 0; + + content: ""; + cursor: pointer; + pointer-events: auto; + grid-area: track; + inline-size: var(--thumb-size); + block-size: var(--thumb-size); + background: var(--thumb-color); + box-shadow: 0 0 0 var(--highlight-size) var(--thumb-color-highlight); + border-radius: 50%; + transform: translateX(var(--thumb-position)); + + @media (--motionOK) { + & { + transition: + transform var(--thumb-transition-duration) ease, + box-shadow .25s ease; + } + } + } + + &:not(:disabled):hover::before { + --highlight-size: .5rem; + } + + &:checked { + background: var(--track-color-active); + --thumb-position: calc((var(--track-size) - 100%) * var(--isLTR)); + } + + &:indeterminate { + --thumb-position: calc(calc(calc(var(--track-size) / 2) - calc(var(--thumb-size) / 2)) * var(--isLTR)); + } + + &:disabled { + cursor: not-allowed; + --thumb-color: transparent; + + &::before { + cursor: not-allowed; + box-shadow: inset 0 0 0 2px hsl(0 0% 100% / 50%); + + } + } + } +} From 75b226e2f80b3217c670c420e6ab8254ef16699c Mon Sep 17 00:00:00 2001 From: Jerry_wu <409187100@qq.com> Date: Tue, 15 Oct 2024 17:13:55 +0800 Subject: [PATCH 06/25] switch function --- apps/component-tests/src/global.css | 6 +- .../[kit]/[component]/[example]/index.tsx | 1 - .../docs/headless/switch/examples/checked.tsx | 18 +++++ .../switch/examples/defaultChecked.tsx | 16 ++++ .../headless/switch/examples/disabled.tsx | 18 +++++ .../docs/headless/switch/examples/hero.tsx | 13 ++-- .../docs/headless/switch/snippets/switch.css | 77 +++++++------------ .../src/components/switch/switch-context.tsx | 2 +- .../src/components/switch/switch-input.tsx | 14 +++- .../src/components/switch/switch-lable.tsx | 5 +- .../src/components/switch/switch-root.tsx | 3 +- 11 files changed, 109 insertions(+), 64 deletions(-) create mode 100644 apps/website/src/routes/docs/headless/switch/examples/checked.tsx create mode 100644 apps/website/src/routes/docs/headless/switch/examples/defaultChecked.tsx create mode 100644 apps/website/src/routes/docs/headless/switch/examples/disabled.tsx diff --git a/apps/component-tests/src/global.css b/apps/component-tests/src/global.css index 62a2111e0..40118f8dd 100644 --- a/apps/component-tests/src/global.css +++ b/apps/component-tests/src/global.css @@ -36,9 +36,13 @@ --alert: 0 84.2% 60.2%; --alert-foreground: 210 40% 98%; --ring: 222.2 47.4% 11.2%; + --switch-thumb-color: 0 0% 100%; + --thumb-color-highlight: 0, 0%, 72%, 0.25; } .dark { + --thumb-color-highlight: 0, 0%, 100%, 0.25; + --switch-thumb-color: 0 0% 100%; --background: 222.2 84% 4.9%; --foreground: 210 40% 98%; --muted: 217.2 32.6% 17.5%; @@ -1342,7 +1346,7 @@ body { min-height: 100%; } -/* Utilities layer for animations. The current arbitrary & docs tailwind animation guidelines are not maintainable long term. +/* Utilities layer for animations. The current arbitrary & docs tailwind animation guidelines are not maintainable long term. It would make more sense to supply the user with the animation declaration in the docs. */ @layer utilities { diff --git a/apps/component-tests/src/routes/[kit]/[component]/[example]/index.tsx b/apps/component-tests/src/routes/[kit]/[component]/[example]/index.tsx index 05cdec8cf..e0d821320 100644 --- a/apps/component-tests/src/routes/[kit]/[component]/[example]/index.tsx +++ b/apps/component-tests/src/routes/[kit]/[component]/[example]/index.tsx @@ -1,6 +1,5 @@ import { component$ } from '@builder.io/qwik'; import { ShowcaseTest } from '../../../../components/showcase-test/showcase-test'; - export default component$(() => { // Need to center the content in the screen // so that tests like popover placement can diff --git a/apps/website/src/routes/docs/headless/switch/examples/checked.tsx b/apps/website/src/routes/docs/headless/switch/examples/checked.tsx new file mode 100644 index 000000000..2057e77ec --- /dev/null +++ b/apps/website/src/routes/docs/headless/switch/examples/checked.tsx @@ -0,0 +1,18 @@ +import { component$, useStyles$, useSignal } from '@builder.io/qwik'; +import { Switch } from '@qwik-ui/headless'; + + +export default component$(() => { + const checked = useSignal(true) + useStyles$(styles); + return ( + + test + + + ); +}); + +import styles from '../snippets/switch.css?inline'; + + diff --git a/apps/website/src/routes/docs/headless/switch/examples/defaultChecked.tsx b/apps/website/src/routes/docs/headless/switch/examples/defaultChecked.tsx new file mode 100644 index 000000000..7baaf8bc9 --- /dev/null +++ b/apps/website/src/routes/docs/headless/switch/examples/defaultChecked.tsx @@ -0,0 +1,16 @@ +import { component$, useSignal } from '@builder.io/qwik'; +import { Switch } from '@qwik-ui/headless'; + + +export default component$(() => { + const checked = useSignal(false) + return ( + + test + + + ); +}); + + + diff --git a/apps/website/src/routes/docs/headless/switch/examples/disabled.tsx b/apps/website/src/routes/docs/headless/switch/examples/disabled.tsx new file mode 100644 index 000000000..7754d549f --- /dev/null +++ b/apps/website/src/routes/docs/headless/switch/examples/disabled.tsx @@ -0,0 +1,18 @@ +import { component$, useSignal, useStyles$ } from '@builder.io/qwik'; +import { Switch } from '@qwik-ui/headless'; + + +export default component$(() => { + const checked = useSignal(false) + useStyles$(styles); + return ( + + test + + + ); +}); + +import styles from '../snippets/switch.css?inline'; + + diff --git a/apps/website/src/routes/docs/headless/switch/examples/hero.tsx b/apps/website/src/routes/docs/headless/switch/examples/hero.tsx index a3d6bba2c..312e4aec0 100644 --- a/apps/website/src/routes/docs/headless/switch/examples/hero.tsx +++ b/apps/website/src/routes/docs/headless/switch/examples/hero.tsx @@ -1,15 +1,18 @@ -import { component$, useStyles$ } from '@builder.io/qwik'; +import { component$, useSignal, useStyles$ } from '@builder.io/qwik'; import { Switch } from '@qwik-ui/headless'; + export default component$(() => { + const checked = useSignal(true) useStyles$(styles); return ( - - sdsds - + + test + ); }); - import styles from '../snippets/switch.css?inline'; + + diff --git a/apps/website/src/routes/docs/headless/switch/snippets/switch.css b/apps/website/src/routes/docs/headless/switch/snippets/switch.css index 84e128782..7ee7540f1 100644 --- a/apps/website/src/routes/docs/headless/switch/snippets/switch.css +++ b/apps/website/src/routes/docs/headless/switch/snippets/switch.css @@ -1,85 +1,55 @@ +/* Define default light theme colors */ .switch { - --brand-hue: 200; - --brand-saturation: 100%; - --brand-lightness: 50%; - - --brand-light: hsl(var(--brand-hue) var(--brand-saturation) var(--brand-lightness)); - --text1-light: hsl(var(--brand-hue) var(--brand-saturation) 10%); - --surface1-light: hsl(var(--brand-hue) 25% 100%); - - --brand-dark: hsl(var(--brand-hue) calc(var(--brand-saturation) / 2) calc(var(--brand-lightness) / 1.5)); - --text1-dark: hsl(var(--brand-hue) 15% 85%); - --surface1-dark: hsl(var(--brand-hue) 10% 10%); - --thumb-size: 2rem; - --thumb: hsl(0 0% 100%); - --thumb-highlight: hsl(0 0% 0% / 25%); - - --track-size: calc(var(--thumb-size) * 2); - --track-padding: 2px; - --track-inactive: hsl(80 0% 80%); - --track-active: hsl(80 60% 45%); - - --thumb-color: var(--thumb); - --thumb-color-highlight: var(--thumb-highlight); - --track-color-inactive: var(--track-inactive); - --track-color-active: var(--track-active); - + --thumb-color: hsla(var(--switch-thumb-color)); + --track-color-inactive: hsla(80 0% 80%); + --track-color-active: hsla(var(--primary)); --isLTR: 1; display: flex; align-items: center; gap: 2ch; justify-content: space-between; - cursor: pointer; user-select: none; -webkit-tap-highlight-color: transparent; - &>input { --thumb-position: 0%; --thumb-transition-duration: .25s; - - padding: var(--track-padding); + padding: 2px; background: var(--track-color-inactive); - inline-size: var(--track-size); - block-size: var(--thumb-size); - border-radius: var(--track-size); - + inline-size: 4rem; + block-size: 2rem; + border-radius: 4rem; appearance: none; pointer-events: none; touch-action: pan-y; border: none; outline-offset: 5px; box-sizing: content-box; - flex-shrink: 0; display: grid; align-items: center; grid: [track] 1fr / [track] 1fr; - transition: background-color .25s ease; &::before { --highlight-size: 0; - content: ""; cursor: pointer; pointer-events: auto; grid-area: track; - inline-size: var(--thumb-size); - block-size: var(--thumb-size); + inline-size: 2rem; + block-size: 2rem; background: var(--thumb-color); - box-shadow: 0 0 0 var(--highlight-size) var(--thumb-color-highlight); + box-shadow: 0 0 0 var(--highlight-size) hsla(var(--thumb-color-highlight)); border-radius: 50%; transform: translateX(var(--thumb-position)); @media (--motionOK) { - & { - transition: - transform var(--thumb-transition-duration) ease, - box-shadow .25s ease; - } + transition: + transform var(--thumb-transition-duration) ease, + box-shadow .25s ease; } } @@ -89,22 +59,29 @@ &:checked { background: var(--track-color-active); - --thumb-position: calc((var(--track-size) - 100%) * var(--isLTR)); + --thumb-position: calc((4rem - 100%) * var(--isLTR)); } &:indeterminate { - --thumb-position: calc(calc(calc(var(--track-size) / 2) - calc(var(--thumb-size) / 2)) * var(--isLTR)); + --thumb-position: calc(1rem * var(--isLTR)); } &:disabled { cursor: not-allowed; - --thumb-color: transparent; - + opacity: 0.35; &::before { cursor: not-allowed; - box-shadow: inset 0 0 0 2px hsl(0 0% 100% / 50%); - + box-shadow: inset 0 0 0 2px hsl(0 0% 100% / 10%); } } } } + +/* Dark theme overrides */ +.theme-dark .switch { + --background: 210, 50%, 20%; /* Adjusted for dark theme */ + --primary: 210, 80%, 60%; /* Adjusted for dark theme */ + --thumb-color: hsla(var(--background)); + --track-color-inactive: hsla(240, 10%, 50%); + --track-color-active: hsla(var(--primary)); +} diff --git a/packages/kit-headless/src/components/switch/switch-context.tsx b/packages/kit-headless/src/components/switch/switch-context.tsx index f55d083a1..dde013299 100644 --- a/packages/kit-headless/src/components/switch/switch-context.tsx +++ b/packages/kit-headless/src/components/switch/switch-context.tsx @@ -1,7 +1,7 @@ import { createContextId, QRL, type Signal } from '@builder.io/qwik'; export interface SwitchState { - 'bind:checked'?: Signal; + 'bind:checked': Signal; defaultChecked?: boolean; disabled?: boolean; onChange$?: QRL<() => void> diff --git a/packages/kit-headless/src/components/switch/switch-input.tsx b/packages/kit-headless/src/components/switch/switch-input.tsx index ae116452f..8cab071af 100644 --- a/packages/kit-headless/src/components/switch/switch-input.tsx +++ b/packages/kit-headless/src/components/switch/switch-input.tsx @@ -1,12 +1,22 @@ -import { component$, PropsOf, useContext } from '@builder.io/qwik'; +import { component$, PropsOf, useContext, useId } from '@builder.io/qwik'; import { SwitchContext } from './switch-context'; export const SwitchInput = component$>(() => { const context = useContext(SwitchContext) + const id = useId() + + if(context.defaultChecked && context.bindChecked && !context.bindChecked.value){ + context.bindChecked.value = true + } + return ( ); diff --git a/packages/kit-headless/src/components/switch/switch-lable.tsx b/packages/kit-headless/src/components/switch/switch-lable.tsx index b336b2266..349d38650 100644 --- a/packages/kit-headless/src/components/switch/switch-lable.tsx +++ b/packages/kit-headless/src/components/switch/switch-lable.tsx @@ -1,8 +1,9 @@ -import { component$, PropsOf, Slot } from '@builder.io/qwik'; +import { component$, PropsOf, Slot, useId } from '@builder.io/qwik'; export const SwitchLable = component$>(() => { + const id = useId() return ( -