From 1b2911d7b4170a2f39ae29ec33335f83c155b41d Mon Sep 17 00:00:00 2001 From: toan-kunaico <122486240+toan-kunaico@users.noreply.github.com> Date: Wed, 29 Oct 2025 16:08:13 -0700 Subject: [PATCH 1/5] check otp data attr --- .../routes/components/otp/examples/disabled.tsx | 2 +- docs/src/routes/components/otp/examples/hero.tsx | 4 ++-- docs/src/routes/components/otp/index.mdx | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/docs/src/routes/components/otp/examples/disabled.tsx b/docs/src/routes/components/otp/examples/disabled.tsx index e696d9128..1e405ab27 100644 --- a/docs/src/routes/components/otp/examples/disabled.tsx +++ b/docs/src/routes/components/otp/examples/disabled.tsx @@ -30,7 +30,7 @@ export default component$(() => { diff --git a/docs/src/routes/components/otp/examples/hero.tsx b/docs/src/routes/components/otp/examples/hero.tsx index 37fed71d2..35588a9de 100644 --- a/docs/src/routes/components/otp/examples/hero.tsx +++ b/docs/src/routes/components/otp/examples/hero.tsx @@ -38,7 +38,7 @@ export default component$(() => { @@ -58,7 +58,7 @@ export default component$(() => { diff --git a/docs/src/routes/components/otp/index.mdx b/docs/src/routes/components/otp/index.mdx index e69de29bb..b45f50fc0 100644 --- a/docs/src/routes/components/otp/index.mdx +++ b/docs/src/routes/components/otp/index.mdx @@ -0,0 +1,16 @@ +import Hero from "./examples/hero.tsx"; +import Disabled from "./examples/disabled.tsx"; + +# OTP + +The OTP component is a Qwik component that allows you to create a one-time password input. + +## Examples + +### Hero + + + +### Disabled + + \ No newline at end of file From a17ec4dc20eb7cd016675aaf6fb73a3ea1becf9f Mon Sep 17 00:00:00 2001 From: toan-kunaico <122486240+toan-kunaico@users.noreply.github.com> Date: Wed, 29 Oct 2025 17:33:25 -0700 Subject: [PATCH 2/5] radio group attrs --- .../radio-group/examples/disabled.tsx | 20 +++---- .../components/radio-group/examples/hero.tsx | 8 +-- .../examples/radio-group-custom.css | 44 --------------- .../radio-group/examples/value-based.tsx | 7 ++- .../routes/components/radio-group/index.mdx | 17 ++++++ .../src/radio-group/radio-group-context.tsx | 4 +- .../radio-group/radio-group-hidden-input.tsx | 20 ++++--- .../radio-group-item-indicator.tsx | 6 +-- .../radio-group/radio-group-item-label.tsx | 2 +- .../radio-group/radio-group-item-trigger.tsx | 12 ++--- .../src/radio-group/radio-group-item.tsx | 8 +-- .../src/radio-group/radio-group-root.tsx | 54 ++++++++++--------- .../src/radio-group/radio-group.browser.tsx | 3 +- 13 files changed, 94 insertions(+), 111 deletions(-) diff --git a/docs/src/routes/components/radio-group/examples/disabled.tsx b/docs/src/routes/components/radio-group/examples/disabled.tsx index 16c605fde..03a99cb03 100644 --- a/docs/src/routes/components/radio-group/examples/disabled.tsx +++ b/docs/src/routes/components/radio-group/examples/disabled.tsx @@ -14,27 +14,27 @@ export default component$(() => { {isGroupDisabled.value ? "Enable" : "Disable"} group - + Choose option - + Option 1 - - + + - + Option 2 (Disabled) - - + + - + Option 3 - - + + diff --git a/docs/src/routes/components/radio-group/examples/hero.tsx b/docs/src/routes/components/radio-group/examples/hero.tsx index 824f50849..7c9717e04 100644 --- a/docs/src/routes/components/radio-group/examples/hero.tsx +++ b/docs/src/routes/components/radio-group/examples/hero.tsx @@ -5,14 +5,14 @@ import styles from "./radio-group-custom.css?inline"; export default component$(() => { useStyles$(styles); return ( - + Choose option {["Option 1", "Option 2"].map((value) => ( - + {value} - - + + ))} diff --git a/docs/src/routes/components/radio-group/examples/radio-group-custom.css b/docs/src/routes/components/radio-group/examples/radio-group-custom.css index 8e36c7b8a..ff7ec0924 100644 --- a/docs/src/routes/components/radio-group/examples/radio-group-custom.css +++ b/docs/src/routes/components/radio-group/examples/radio-group-custom.css @@ -1,47 +1,3 @@ .radio-group-root[data-orientation="horizontal"] { gap: 1rem; } - -.radio-group-item { - display: flex; - align-items: center; - gap: 0.5rem; -} - -.radio-group-trigger { - display: flex; - align-items: center; - justify-content: center; - width: 20px; - height: 20px; - border: 2px solid currentColor; - border-radius: 50%; - cursor: pointer; - padding: 0; - background: none; -} - -.radio-group-trigger[data-checked] { - background-color: #e5e5e5; -} - -.radio-group-trigger[data-disabled] { - opacity: 0.5; - cursor: not-allowed; -} - -.radio-group-indicator { - display: flex; - color: currentColor; - font-size: 12px; - align-items: center; - justify-content: center; - width: 100%; - height: 100%; -} - -.radio-group-error { - color: red; - font-size: 0.875rem; - margin-top: 0.25rem; -} diff --git a/docs/src/routes/components/radio-group/examples/value-based.tsx b/docs/src/routes/components/radio-group/examples/value-based.tsx index 5fc0faa3b..e8a184ce8 100644 --- a/docs/src/routes/components/radio-group/examples/value-based.tsx +++ b/docs/src/routes/components/radio-group/examples/value-based.tsx @@ -14,14 +14,13 @@ export default component$(() => { Choose option {["Option 1", "Option 2"].map((value) => ( - - - + + + {value} diff --git a/docs/src/routes/components/radio-group/index.mdx b/docs/src/routes/components/radio-group/index.mdx index e69de29bb..0d8822189 100644 --- a/docs/src/routes/components/radio-group/index.mdx +++ b/docs/src/routes/components/radio-group/index.mdx @@ -0,0 +1,17 @@ +import Hero from "./examples/hero.tsx"; +import ValueBased from "./examples/value-based.tsx"; +import Disabled from "./examples/disabled.tsx"; + +# Radio Group + +## Hero + + + +## Value Based + + + +## Disabled + + \ No newline at end of file diff --git a/libs/components/src/radio-group/radio-group-context.tsx b/libs/components/src/radio-group/radio-group-context.tsx index 9ce44cc2d..873aa8007 100644 --- a/libs/components/src/radio-group/radio-group-context.tsx +++ b/libs/components/src/radio-group/radio-group-context.tsx @@ -6,8 +6,8 @@ type TriggerRef = { }; export interface RadioGroupContext { - selectedValueSig: Signal; - isDisabledSig: Signal | ReadonlySignal; + selectedValue: Signal; + isDisabled: Signal | ReadonlySignal; isErrorSig: Signal; localId: string; required?: boolean; diff --git a/libs/components/src/radio-group/radio-group-hidden-input.tsx b/libs/components/src/radio-group/radio-group-hidden-input.tsx index c26340c3a..e7d9097c1 100644 --- a/libs/components/src/radio-group/radio-group-hidden-input.tsx +++ b/libs/components/src/radio-group/radio-group-hidden-input.tsx @@ -1,29 +1,37 @@ // no-as-child -import { type PropsOf, component$, useContext } from "@qwik.dev/core"; +import { $, type PropsOf, component$, useContext } from "@qwik.dev/core"; import { VisuallyHidden } from "../visually-hidden/visually-hidden"; import { radioGroupContextId } from "./radio-group-context"; type PublicHiddenInputProps = Omit< PropsOf<"input">, - "type" | "checked" | "form" | "style" + "type" | "value" | "form" | "style" >; export const RadioGroupHiddenInput = component$((props: PublicHiddenInputProps) => { const context = useContext(radioGroupContextId); const { required, ...restProps } = props; + const handleChange$ = $((e: InputEvent) => { + const target = e.target as HTMLInputElement; + if (target.value === context.selectedValue.value) { + return; + } + context.selectedValue.value = target.value; + }); + return ( ); diff --git a/libs/components/src/radio-group/radio-group-item-indicator.tsx b/libs/components/src/radio-group/radio-group-item-indicator.tsx index 78973f9b2..32ff31ccf 100644 --- a/libs/components/src/radio-group/radio-group-item-indicator.tsx +++ b/libs/components/src/radio-group/radio-group-item-indicator.tsx @@ -12,9 +12,9 @@ export const RadioGroupItemIndicator = component$((props: PublicIndicatorProps) {...props} fallback="span" data-qds-indicator - data-checked={itemContext.isSelectedSig.value} - data-hidden={!itemContext.isSelectedSig.value} - aria-hidden={!itemContext.isSelectedSig.value ? "true" : "false"} + data-checked={itemContext.isSelected.value} + data-hidden={!itemContext.isSelected.value} + aria-hidden={!itemContext.isSelected.value ? "true" : "false"} > diff --git a/libs/components/src/radio-group/radio-group-item-label.tsx b/libs/components/src/radio-group/radio-group-item-label.tsx index 83ba6ab35..999a33bb8 100644 --- a/libs/components/src/radio-group/radio-group-item-label.tsx +++ b/libs/components/src/radio-group/radio-group-item-label.tsx @@ -15,7 +15,7 @@ export const RadioGroupItemLabel = component$((props: PublicLabelProps) => { if (!currItem || currItem.disabled) return; - context.selectedValueSig.value = itemContext.itemValue; + context.selectedValue.value = itemContext.itemValue; currItem.focus(); }); diff --git a/libs/components/src/radio-group/radio-group-item-trigger.tsx b/libs/components/src/radio-group/radio-group-item-trigger.tsx index 6617167dd..96c66063c 100644 --- a/libs/components/src/radio-group/radio-group-item-trigger.tsx +++ b/libs/components/src/radio-group/radio-group-item-trigger.tsx @@ -43,19 +43,19 @@ export const RadioGroupItemTrigger = component$((props: PublicTriggerProps) => { }; }); - const isDisabledSig = useComputed$(() => context.isDisabledSig.value || props.disabled); + const isDisabledSig = useComputed$(() => context.isDisabled.value || props.disabled); const tabIndexSig = useComputed$(() => { - const isSelected = itemContext.isSelectedSig.value; + const isSelected = itemContext.isSelected.value; const isFirstItem = itemIndex === 0; - const noSelection = !context.selectedValueSig.value; + const noSelection = !context.selectedValue.value; return isSelected || (noSelection && isFirstItem) ? 0 : -1; }); const handleSelection$ = $(() => { if (isDisabledSig.value) return; - context.selectedValueSig.value = value; + context.selectedValue.value = value; }); const handleKeyDown$ = $(async (event: KeyboardEvent) => { @@ -87,8 +87,8 @@ export const RadioGroupItemTrigger = component$((props: PublicTriggerProps) => { type="button" {...restProps} internalRef={triggerRef} - aria-checked={itemContext.isSelectedSig.value} - data-checked={itemContext.isSelectedSig.value} + aria-checked={itemContext.isSelected.value} + data-checked={itemContext.isSelected.value} data-qds-radio-group-trigger data-disabled={isDisabledSig.value || undefined} value={value} diff --git a/libs/components/src/radio-group/radio-group-item.tsx b/libs/components/src/radio-group/radio-group-item.tsx index 26bde1f94..52f5e2c6e 100644 --- a/libs/components/src/radio-group/radio-group-item.tsx +++ b/libs/components/src/radio-group/radio-group-item.tsx @@ -22,7 +22,7 @@ export const radioGroupItemContextId = createContextId( ); type RadioGroupItemContext = { - isSelectedSig: Signal; + isSelected: Signal; itemValue: string; itemId: string; itemIndex: number; @@ -38,12 +38,12 @@ export const RadioGroupItem = component$((props: PublicItemProps) => { return currItemIndex; }); - const isSelectedSig = useComputed$( - () => context.selectedValueSig.value === props.value + const isSelected = useComputed$( + () => context.selectedValue.value === props.value ); const itemContext: RadioGroupItemContext = { - isSelectedSig, + isSelected, itemValue: props.value, itemId, itemIndex diff --git a/libs/components/src/radio-group/radio-group-root.tsx b/libs/components/src/radio-group/radio-group-root.tsx index ac3f676cf..795ab66f5 100644 --- a/libs/components/src/radio-group/radio-group-root.tsx +++ b/libs/components/src/radio-group/radio-group-root.tsx @@ -1,4 +1,4 @@ -import { useBoundSignal } from "@qds.dev/utils"; +import { type BindableProps, useBindings } from "@qds.dev/utils"; import { $, component$, @@ -17,15 +17,18 @@ import styles from "./radio-group.css?inline"; import { radioGroupContextId } from "./radio-group-context"; type PublicRootProps = { - value?: string; onChange$?: (value: string) => void; disabled?: boolean; name?: string; required?: boolean; orientation?: "horizontal" | "vertical"; isError?: boolean; - "bind:value"?: Signal; -} & Omit, "onChange$">; +} & Omit, "onChange$"> & BindableProps; + +type RadioGroupBinds = { + value: string; + disabled: boolean; +}; interface TriggerRef { ref: Signal; @@ -36,7 +39,6 @@ export const RadioGroupRoot = component$((props: PublicRootProps) => { useStyles$(styles); const { - "bind:value": givenValueSig, onChange$: _onChange$, disabled: _disabled, name: _name, @@ -48,14 +50,13 @@ export const RadioGroupRoot = component$((props: PublicRootProps) => { } = props; const rootRef = useSignal(); - const valuePropSig = useComputed$(() => props.value); - const selectedValueSig = useBoundSignal( - givenValueSig, - givenValueSig?.value ?? valuePropSig.value, - valuePropSig - ); - const isInitialLoadSig = useSignal(true); - const isDisabledSig = useComputed$(() => !!props.disabled); + + const { valueSig: selectedValue, disabledSig: isDisabled } = useBindings(props, { + value: "", + disabled: false + }); + + const isInitialRender = useSignal(true); const localId = useId(); const computedIsError = useComputed$(() => !!props.isError); const triggerRefsArray = useSignal([]); @@ -64,17 +65,17 @@ export const RadioGroupRoot = component$((props: PublicRootProps) => { const labelId = `${localId}-label`; useTask$(function handleChange({ track }) { - track(() => selectedValueSig.value); - if (isInitialLoadSig.value) { + track(() => selectedValue.value); + if (isInitialRender.value) { return; } - if (selectedValueSig.value) { - props.onChange$?.(selectedValueSig.value); + if (selectedValue.value) { + props.onChange$?.(selectedValue.value); } }); useTask$(() => { - isInitialLoadSig.value = false; + isInitialRender.value = false; }); const getEnabledTriggerIndexes = $((triggerRefs: TriggerRef[]) => { @@ -90,13 +91,13 @@ export const RadioGroupRoot = component$((props: PublicRootProps) => { const value = triggerData.value; if (value && trigger) { - selectedValueSig.value = value; + selectedValue.value = value; trigger.focus(); } }); const handleKeyDown$ = $(async (e: KeyboardEvent) => { - if (isDisabledSig.value || !rootRef.value) return; + if (isDisabled.value || !rootRef.value) return; const isHorizontal = props.orientation === "horizontal"; const nextKey = isHorizontal ? "ArrowRight" : "ArrowDown"; @@ -108,9 +109,9 @@ export const RadioGroupRoot = component$((props: PublicRootProps) => { let currentIndex = 0; - if (selectedValueSig.value) { + if (selectedValue.value) { currentIndex = enabledTriggerIndexes.findIndex( - (index) => triggerRefsArray.value[index].value === selectedValueSig.value + (index) => triggerRefsArray.value[index].value === selectedValue.value ); } @@ -139,8 +140,8 @@ export const RadioGroupRoot = component$((props: PublicRootProps) => { }); useContextProvider(radioGroupContextId, { - selectedValueSig, - isDisabledSig, + selectedValue, + isDisabled, isErrorSig: computedIsError, localId, required: props.required, @@ -157,9 +158,10 @@ export const RadioGroupRoot = component$((props: PublicRootProps) => { internalRef={rootRef} role="radiogroup" data-qds-radio-group-root + data-qds-scope data-orientation={props.orientation || "vertical"} - data-disabled={isDisabledSig.value ? "" : undefined} - aria-disabled={isDisabledSig.value} + data-disabled={isDisabled.value ? "" : undefined} + aria-disabled={isDisabled.value} aria-required={props.required} aria-invalid={computedIsError.value} aria-labelledby={labelId} diff --git a/libs/components/src/radio-group/radio-group.browser.tsx b/libs/components/src/radio-group/radio-group.browser.tsx index 9a6eb5170..4118611d4 100644 --- a/libs/components/src/radio-group/radio-group.browser.tsx +++ b/libs/components/src/radio-group/radio-group.browser.tsx @@ -186,10 +186,11 @@ const FormBasic = component$(() => { Indicator - ))} + + {isError.value && ( Please select a subscription plan From 080ad158f87cae75d996f2e2f7cf2b8d3a291304 Mon Sep 17 00:00:00 2001 From: toan-kunaico <122486240+toan-kunaico@users.noreply.github.com> Date: Thu, 30 Oct 2025 12:29:55 -0700 Subject: [PATCH 3/5] refactor: clean up radio group component code and improve formatting --- docs/src/routes/components/radio-group/examples/disabled.tsx | 5 ++++- .../routes/components/radio-group/examples/value-based.tsx | 5 +---- libs/components/src/radio-group/radio-group-hidden-input.tsx | 5 +---- libs/components/src/radio-group/radio-group-item.tsx | 4 +--- libs/components/src/radio-group/radio-group-root.tsx | 3 ++- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/docs/src/routes/components/radio-group/examples/disabled.tsx b/docs/src/routes/components/radio-group/examples/disabled.tsx index 03a99cb03..391d0c1ef 100644 --- a/docs/src/routes/components/radio-group/examples/disabled.tsx +++ b/docs/src/routes/components/radio-group/examples/disabled.tsx @@ -26,7 +26,10 @@ export default component$(() => { Option 2 (Disabled) - + diff --git a/docs/src/routes/components/radio-group/examples/value-based.tsx b/docs/src/routes/components/radio-group/examples/value-based.tsx index e8a184ce8..48fffae75 100644 --- a/docs/src/routes/components/radio-group/examples/value-based.tsx +++ b/docs/src/routes/components/radio-group/examples/value-based.tsx @@ -11,10 +11,7 @@ export default component$(() => { }); return ( - + Choose option {["Option 1", "Option 2"].map((value) => ( diff --git a/libs/components/src/radio-group/radio-group-hidden-input.tsx b/libs/components/src/radio-group/radio-group-hidden-input.tsx index e7d9097c1..59705020f 100644 --- a/libs/components/src/radio-group/radio-group-hidden-input.tsx +++ b/libs/components/src/radio-group/radio-group-hidden-input.tsx @@ -3,10 +3,7 @@ import { $, type PropsOf, component$, useContext } from "@qwik.dev/core"; import { VisuallyHidden } from "../visually-hidden/visually-hidden"; import { radioGroupContextId } from "./radio-group-context"; -type PublicHiddenInputProps = Omit< - PropsOf<"input">, - "type" | "value" | "form" | "style" ->; +type PublicHiddenInputProps = Omit, "type" | "value" | "form" | "style">; export const RadioGroupHiddenInput = component$((props: PublicHiddenInputProps) => { const context = useContext(radioGroupContextId); diff --git a/libs/components/src/radio-group/radio-group-item.tsx b/libs/components/src/radio-group/radio-group-item.tsx index 52f5e2c6e..2c3803a7d 100644 --- a/libs/components/src/radio-group/radio-group-item.tsx +++ b/libs/components/src/radio-group/radio-group-item.tsx @@ -38,9 +38,7 @@ export const RadioGroupItem = component$((props: PublicItemProps) => { return currItemIndex; }); - const isSelected = useComputed$( - () => context.selectedValue.value === props.value - ); + const isSelected = useComputed$(() => context.selectedValue.value === props.value); const itemContext: RadioGroupItemContext = { isSelected, diff --git a/libs/components/src/radio-group/radio-group-root.tsx b/libs/components/src/radio-group/radio-group-root.tsx index 795ab66f5..c0c63724c 100644 --- a/libs/components/src/radio-group/radio-group-root.tsx +++ b/libs/components/src/radio-group/radio-group-root.tsx @@ -23,7 +23,8 @@ type PublicRootProps = { required?: boolean; orientation?: "horizontal" | "vertical"; isError?: boolean; -} & Omit, "onChange$"> & BindableProps; +} & Omit, "onChange$"> & + BindableProps; type RadioGroupBinds = { value: string; From e30f7b952c2634a7c42d41761fdc10f5eb822825 Mon Sep 17 00:00:00 2001 From: toan-kunaico <122486240+toan-kunaico@users.noreply.github.com> Date: Thu, 30 Oct 2025 12:38:32 -0700 Subject: [PATCH 4/5] switch data attrs --- .../components/switch/examples/disabled.tsx | 6 +++--- .../routes/components/switch/examples/hero.tsx | 6 +++--- docs/src/routes/components/switch/index.mdx | 17 +++++++++++++++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/docs/src/routes/components/switch/examples/disabled.tsx b/docs/src/routes/components/switch/examples/disabled.tsx index 6a42f7549..727471aec 100644 --- a/docs/src/routes/components/switch/examples/disabled.tsx +++ b/docs/src/routes/components/switch/examples/disabled.tsx @@ -5,9 +5,9 @@ import styles from "./switch-custom.css?inline"; export default component$(() => { useStyles$(styles); return ( - - - + + + Disabled switch diff --git a/docs/src/routes/components/switch/examples/hero.tsx b/docs/src/routes/components/switch/examples/hero.tsx index 2b02af36c..be1fa60cf 100644 --- a/docs/src/routes/components/switch/examples/hero.tsx +++ b/docs/src/routes/components/switch/examples/hero.tsx @@ -5,9 +5,9 @@ import styles from "./switch-custom.css?inline"; export default component$(() => { useStyles$(styles); return ( - - - + + + Enable notifications diff --git a/docs/src/routes/components/switch/index.mdx b/docs/src/routes/components/switch/index.mdx index e69de29bb..a3c40f018 100644 --- a/docs/src/routes/components/switch/index.mdx +++ b/docs/src/routes/components/switch/index.mdx @@ -0,0 +1,17 @@ +import Hero from "./examples/hero"; +import Disabled from "./examples/disabled"; + +# Switch + +A Switch is an interactive button that works like a modern light switch - it smoothly slides between two states: ON and OFF. +When interacting with it, the switch's thumb (the moving part) slides from one side to the other, giving clear +visual feedback about whether something is enabled or disabled. It provides a visual representation similar to a +physical switch and is commonly used for enabling/disabling settings or features. + +## Basic Usage + + + +## Disabled + + \ No newline at end of file From 8635cc7a28f29483a4dd38255c1b79b7c0168b55 Mon Sep 17 00:00:00 2001 From: toan-kunaico <122486240+toan-kunaico@users.noreply.github.com> Date: Thu, 30 Oct 2025 16:27:51 -0700 Subject: [PATCH 5/5] refactor: update tree components for improved styling and functionality --- .../routes/components/tree/examples/hero.tsx | 8 ++-- libs/components/src/switch/switch-root.tsx | 1 + .../components/src/tree/tree-item-content.tsx | 2 +- .../src/tree/tree-item-indicator.tsx | 2 +- libs/components/src/tree/tree-item-label.tsx | 2 +- .../components/src/tree/tree-item-trigger.tsx | 2 +- libs/components/src/tree/tree-item.tsx | 43 ++++++++++++------- libs/components/src/tree/tree-root.tsx | 4 +- 8 files changed, 38 insertions(+), 26 deletions(-) diff --git a/docs/src/routes/components/tree/examples/hero.tsx b/docs/src/routes/components/tree/examples/hero.tsx index 5bbd79eea..49deb4bbb 100644 --- a/docs/src/routes/components/tree/examples/hero.tsx +++ b/docs/src/routes/components/tree/examples/hero.tsx @@ -46,7 +46,7 @@ export default component$(() => { ]; return ( - + {treeData.map((item) => renderTreeItem(item))} ); @@ -56,11 +56,11 @@ function renderTreeItem(item: TreeItemType) { if (item.children && item.children.length > 0) { return ( - + {item.label} - + - + {item.children.map((child) => renderTreeItem(child))} diff --git a/libs/components/src/switch/switch-root.tsx b/libs/components/src/switch/switch-root.tsx index 39235c211..f5029e5a8 100644 --- a/libs/components/src/switch/switch-root.tsx +++ b/libs/components/src/switch/switch-root.tsx @@ -92,6 +92,7 @@ export const SwitchRoot = component$((props) => { aria-describedby={descriptionId} aria-errormessage={hasErrorMessageSig.value ? errorId : undefined} data-qds-switch-root + data-qds-scope // Indicates whether the switch is currently checked data-checked={checkedSig.value} // Indicates whether the switch is currently disabled diff --git a/libs/components/src/tree/tree-item-content.tsx b/libs/components/src/tree/tree-item-content.tsx index 3f981e556..8fd5901da 100644 --- a/libs/components/src/tree/tree-item-content.tsx +++ b/libs/components/src/tree/tree-item-content.tsx @@ -1,4 +1,4 @@ -import { type PropsOf, Slot, component$ } from "@qwik.dev/core"; +import { component$, type PropsOf, Slot } from "@qwik.dev/core"; import { CollapsibleContent } from "../collapsible/collapsible-content"; export const TreeItemContent = component$((props: PropsOf) => { diff --git a/libs/components/src/tree/tree-item-indicator.tsx b/libs/components/src/tree/tree-item-indicator.tsx index d32f2e951..5acdd3b29 100644 --- a/libs/components/src/tree/tree-item-indicator.tsx +++ b/libs/components/src/tree/tree-item-indicator.tsx @@ -1,4 +1,4 @@ -import { type PropsOf, Slot, component$ } from "@qwik.dev/core"; +import { component$, type PropsOf, Slot } from "@qwik.dev/core"; import { Render } from "../render/render"; export const TreeItemIndicator = component$((props: PropsOf<"span">) => { diff --git a/libs/components/src/tree/tree-item-label.tsx b/libs/components/src/tree/tree-item-label.tsx index 0dcda4182..042785b7c 100644 --- a/libs/components/src/tree/tree-item-label.tsx +++ b/libs/components/src/tree/tree-item-label.tsx @@ -1,4 +1,4 @@ -import { type PropsOf, Slot, component$ } from "@qwik.dev/core"; +import { component$, type PropsOf, Slot } from "@qwik.dev/core"; import { Render } from "../render/render"; export const TreeItemLabel = component$((props: PropsOf<"span">) => { diff --git a/libs/components/src/tree/tree-item-trigger.tsx b/libs/components/src/tree/tree-item-trigger.tsx index 14c002781..dac3bff7b 100644 --- a/libs/components/src/tree/tree-item-trigger.tsx +++ b/libs/components/src/tree/tree-item-trigger.tsx @@ -1,8 +1,8 @@ import { type Component, + component$, type PropsOf, Slot, - component$, useContext } from "@qwik.dev/core"; import { CollapsibleTrigger } from "../collapsible/collapsible-trigger"; diff --git a/libs/components/src/tree/tree-item.tsx b/libs/components/src/tree/tree-item.tsx index 11283d6a1..37cd70e4c 100644 --- a/libs/components/src/tree/tree-item.tsx +++ b/libs/components/src/tree/tree-item.tsx @@ -1,12 +1,12 @@ -import { useBoundSignal } from "@qds.dev/utils"; +import { type BindableProps, useBindings } from "@qds.dev/utils"; import { $, - type PropsOf, - type Signal, - Slot, component$, createContextId, isServer, + type PropsOf, + type Signal, + Slot, sync$, useContext, useContextProvider, @@ -18,26 +18,35 @@ import { CollapsibleRoot } from "../collapsible/collapsible-root"; import { TreeRootContextId } from "./tree-root"; import { useTree } from "./use-tree"; -type TreeItemContext = { +export const itemContextId = createContextId("tree-item"); + +export interface TreeItemContext { id: string; level: number; - isOpenSig: Signal; -}; - -export const itemContextId = createContextId("tree-item"); + isOpen: Signal; + isDisabled: Signal; +} -interface TreeItemProps extends PropsOf { +export type TreeItemProps = Omit, "open" | "disabled"> & { _index?: number; groupTrigger?: boolean; groupId?: string; -} +} & BindableProps; + +type TreeItemBinds = { + open: boolean; + disabled: boolean; +}; export const TreeItem = component$((props: TreeItemProps) => { const context = useContext(TreeRootContextId); const parentContext = useContext(itemContextId, null); const id = useId(); const itemRef = useSignal(); - const isOpenSig = useBoundSignal(props["bind:open"], false); + const { openSig: isOpen, disabledSig: isDisabled } = useBindings(props, { + open: false, + disabled: false + }); const { getCurrentLevel } = useTree(); const isHighlightedSig = useSignal(false); const level = getCurrentLevel(parentContext?.level); @@ -45,7 +54,8 @@ export const TreeItem = component$((props: TreeItemProps) => { const itemContext: TreeItemContext = { id, level, - isOpenSig + isOpen, + isDisabled }; useContextProvider(itemContextId, itemContext); @@ -92,14 +102,14 @@ export const TreeItem = component$((props: TreeItemProps) => { case "ArrowRight": { const isCollapsed = currentItem.hasAttribute("data-closed"); if (isCollapsed) { - isOpenSig.value = true; + isOpen.value = true; } return; } case "ArrowLeft": { const isExpanded = !currentItem.hasAttribute("data-closed"); if (isExpanded) { - isOpenSig.value = false; + isOpen.value = false; } return; } @@ -136,7 +146,8 @@ export const TreeItem = component$((props: TreeItemProps) => { id={id} ref={itemRef} role="row" - bind:open={isOpenSig} + open={isOpen.value} + disabled={isDisabled.value} tabIndex={ context.currentFocusEl.value === itemRef.value || context.currentFocusEl.value === null diff --git a/libs/components/src/tree/tree-root.tsx b/libs/components/src/tree/tree-root.tsx index c07a116f1..2c1746123 100644 --- a/libs/components/src/tree/tree-root.tsx +++ b/libs/components/src/tree/tree-root.tsx @@ -1,11 +1,11 @@ // no-bound-signal import { + component$, + createContextId, type PropsOf, type Signal, Slot, - component$, - createContextId, useContextProvider, useSignal } from "@qwik.dev/core";