Skip to content
Draft
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
2 changes: 1 addition & 1 deletion docs/src/routes/components/otp/examples/disabled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default component$(() => {
<Otp.Item
key={slot}
class={
"h-9 w-10 border-2 text-center data-[highlighted]:border-blue-600 rounded data-[highlighted]:ring-blue-100 data-[highlighted]:ring-[3px] data-[highlighted]:pl-1 data-[highlighted]:pr-1 caret-blue-600 data-[disabled]:opacity-50"
"h-9 w-10 border-2 text-center ui-highlighted:border-blue-600 rounded ui-highlighted:ring-blue-100 ui-highlighted:ring-[3px] ui-highlighted:pl-1 ui-highlighted:pr-1 caret-blue-600 ui-disabled:opacity-50"
}
>
<Otp.ItemIndicator class="text-blue-500 text-xl animate-blink-caret">
Expand Down
4 changes: 2 additions & 2 deletions docs/src/routes/components/otp/examples/hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default component$(() => {
<Otp.Item
key={`otp-item-${slot}`}
class={
"h-9 w-10 border-2 text-center rounded data-[highlighted]:ring-qwik-blue-800 data-[highlighted]:ring-[3px] caret-blue-600"
"h-9 w-10 border-2 text-center rounded ui-highlighted:ring-red-800 ui-highlighted:ring-[3px] caret-blue-600"
}
>
<Otp.ItemIndicator class="text-blue-500 text-xl animate-blink-caret">
Expand All @@ -58,7 +58,7 @@ export default component$(() => {
<Otp.Item
key={`otp-item-${slot}`}
class={
"h-9 w-10 border-2 text-center rounded data-[highlighted]:ring-qwik-blue-800 data-[highlighted]:ring-[3px] caret-blue-600"
"h-9 w-10 border-2 text-center rounded ui-highlighted:ring-red-800 ui-highlighted:ring-[3px] caret-blue-600"
}
>
<Otp.ItemIndicator class="text-blue-500 text-xl animate-blink-caret">
Expand Down
16 changes: 16 additions & 0 deletions docs/src/routes/components/otp/index.mdx
Original file line number Diff line number Diff line change
@@ -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

<Hero />

### Disabled

<Disabled />
23 changes: 13 additions & 10 deletions docs/src/routes/components/radio-group/examples/disabled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,30 @@ export default component$(() => {
{isGroupDisabled.value ? "Enable" : "Disable"} group
</button>

<RadioGroup.Root class="radio-group-root" disabled={isGroupDisabled.value}>
<RadioGroup.Root disabled={isGroupDisabled.value}>
<RadioGroup.Label>Choose option</RadioGroup.Label>

<RadioGroup.Item value="option1" class="radio-group-item">
<RadioGroup.Item value="option1" class="flex items-center gap-2">
<RadioGroup.ItemLabel>Option 1</RadioGroup.ItemLabel>
<RadioGroup.ItemTrigger class="radio-group-trigger">
<RadioGroup.ItemIndicator class="radio-group-indicator" />
<RadioGroup.ItemTrigger class="flex items-center justify-center w-5 h-5 border-2 border-current ui-disabled:opacity-50 ui-disabled:cursor-not-allowed rounded-full cursor-pointer p-0 bg-none ui-checked:bg-red-500">
<RadioGroup.ItemIndicator class="flex text-current text-xs items-center justify-center w-full h-full" />
</RadioGroup.ItemTrigger>
</RadioGroup.Item>

<RadioGroup.Item value="option2" class="radio-group-item">
<RadioGroup.Item value="option2" class="flex items-center gap-2">
<RadioGroup.ItemLabel>Option 2 (Disabled)</RadioGroup.ItemLabel>
<RadioGroup.ItemTrigger class="radio-group-trigger" disabled>
<RadioGroup.ItemIndicator class="radio-group-indicator" />
<RadioGroup.ItemTrigger
class="flex items-center ui-disabled:opacity-50 ui-disabled:cursor-not-allowed justify-center w-5 h-5 border-2 border-current rounded-full cursor-pointer p-0 bg-none ui-checked:bg-red-500"
disabled
>
<RadioGroup.ItemIndicator class="flex text-current text-xs items-center justify-center w-full h-full" />
</RadioGroup.ItemTrigger>
</RadioGroup.Item>

<RadioGroup.Item value="option3" class="radio-group-item">
<RadioGroup.Item value="option3" class="flex items-center gap-2">
<RadioGroup.ItemLabel>Option 3</RadioGroup.ItemLabel>
<RadioGroup.ItemTrigger class="radio-group-trigger">
<RadioGroup.ItemIndicator class="radio-group-indicator" />
<RadioGroup.ItemTrigger class="flex items-center ui-disabled:opacity-50 ui-disabled:cursor-not-allowed justify-center w-5 h-5 border-2 border-current rounded-full cursor-pointer p-0 bg-none ui-checked:bg-red-500">
<RadioGroup.ItemIndicator class="flex text-current text-xs items-center justify-center w-full h-full" />
</RadioGroup.ItemTrigger>
</RadioGroup.Item>
</RadioGroup.Root>
Expand Down
8 changes: 4 additions & 4 deletions docs/src/routes/components/radio-group/examples/hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import styles from "./radio-group-custom.css?inline";
export default component$(() => {
useStyles$(styles);
return (
<RadioGroup.Root class="radio-group-root">
<RadioGroup.Root>
<RadioGroup.Label>Choose option</RadioGroup.Label>

{["Option 1", "Option 2"].map((value) => (
<RadioGroup.Item value={value} key={value} class="radio-group-item">
<RadioGroup.Item value={value} key={value} class="flex items-center gap-2">
<RadioGroup.ItemLabel>{value}</RadioGroup.ItemLabel>
<RadioGroup.ItemTrigger class="radio-group-trigger">
<RadioGroup.ItemIndicator class="radio-group-indicator" />
<RadioGroup.ItemTrigger class="flex items-center justify-center w-5 h-5 border-2 border-current rounded-full cursor-pointer p-0 bg-none ui-checked:bg-red-500">
<RadioGroup.ItemIndicator class="flex text-current text-xs items-center justify-center w-full h-full" />
</RadioGroup.ItemTrigger>
</RadioGroup.Item>
))}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
12 changes: 4 additions & 8 deletions docs/src/routes/components/radio-group/examples/value-based.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,13 @@ export default component$(() => {
});

return (
<RadioGroup.Root
value={currentValue.value}
onChange$={valueSelected$}
class="radio-group-root"
>
<RadioGroup.Root value={currentValue.value} onChange$={valueSelected$}>
<RadioGroup.Label>Choose option</RadioGroup.Label>

{["Option 1", "Option 2"].map((value) => (
<RadioGroup.Item value={value} key={value} class="radio-group-item">
<RadioGroup.ItemTrigger class="radio-group-trigger">
<RadioGroup.ItemIndicator class="radio-group-indicator" />
<RadioGroup.Item value={value} key={value} class="flex items-center gap-2">
<RadioGroup.ItemTrigger class="flex items-center justify-center w-5 h-5 border-2 border-current ui-disabled:opacity-50 ui-disabled:cursor-not-allowed rounded-full cursor-pointer p-0 bg-none ui-checked:bg-red-500">
<RadioGroup.ItemIndicator class="flex text-current text-xs items-center justify-center w-full h-full" />
</RadioGroup.ItemTrigger>
<RadioGroup.ItemLabel>{value}</RadioGroup.ItemLabel>
</RadioGroup.Item>
Expand Down
17 changes: 17 additions & 0 deletions docs/src/routes/components/radio-group/index.mdx
Original file line number Diff line number Diff line change
@@ -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

<Hero />

## Value Based

<ValueBased />

## Disabled

<Disabled />
6 changes: 3 additions & 3 deletions docs/src/routes/components/switch/examples/disabled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import styles from "./switch-custom.css?inline";
export default component$(() => {
useStyles$(styles);
return (
<Switch.Root class="switch-root" disabled>
<Switch.Trigger class="switch-trigger">
<Switch.Thumb class="switch-thumb" />
<Switch.Root class="ml-4 gap-2 ui-disabled:opacity-50" disabled>
<Switch.Trigger class="w-10 h-6 rounded-full bg-gray-300 transition-colors duration-200 border-none p-0 relative cursor-pointer ui-checked:bg-[#045c88] focus-visible:outline focus-visible:outline-offset-2 focus-visible:outline-[#045c88]">
<Switch.Thumb class="w-5 h-5 rounded-full bg-white top-0.5 left-0.5 transition-transform duration-200 absolute transform translate-x-0 ui-checked:translate-x-full ui-checked:left-[-2px]" />
</Switch.Trigger>
<Switch.Label>Disabled switch</Switch.Label>
</Switch.Root>
Expand Down
6 changes: 3 additions & 3 deletions docs/src/routes/components/switch/examples/hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import styles from "./switch-custom.css?inline";
export default component$(() => {
useStyles$(styles);
return (
<Switch.Root class="switch-root">
<Switch.Trigger class="switch-trigger">
<Switch.Thumb class="switch-thumb" />
<Switch.Root class="ml-4 gap-2 ui-disabled:opacity-50">
<Switch.Trigger class="w-10 h-6 rounded-full bg-gray-300 transition-colors duration-200 border-none p-0 relative cursor-pointer ui-checked:bg-[#045c88] focus-visible:outline focus-visible:outline-offset-2 focus-visible:outline-[#045c88]">
<Switch.Thumb class="w-5 h-5 rounded-full bg-white top-0.5 left-0.5 transition-transform duration-200 absolute transform translate-x-0 ui-checked:translate-x-full ui-checked:left-[-2px]" />
</Switch.Trigger>
<Switch.Label>Enable notifications</Switch.Label>
</Switch.Root>
Expand Down
17 changes: 17 additions & 0 deletions docs/src/routes/components/switch/index.mdx
Original file line number Diff line number Diff line change
@@ -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

<Hero />

## Disabled

<Disabled />
8 changes: 4 additions & 4 deletions docs/src/routes/components/tree/examples/hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default component$(() => {
];

return (
<Tree.Root class="tree-root">
<Tree.Root class="ml-4 p-4 min-w-60 border border-[#0f1317]">
{treeData.map((item) => renderTreeItem(item))}
</Tree.Root>
);
Expand All @@ -56,11 +56,11 @@ function renderTreeItem(item: TreeItemType) {
if (item.children && item.children.length > 0) {
return (
<Tree.Item key={item.id}>
<Tree.ItemTrigger class="tree-item-trigger">
<Tree.ItemTrigger class="flex gap-2 items-center cursor-pointer w-full">
<Tree.ItemLabel>{item.label}</Tree.ItemLabel>
<LuChevronRight />
<LuChevronRight class="not-ui-open:rotate-none ui-open:rotate-90 transition-transform duration-200" />
</Tree.ItemTrigger>
<Tree.ItemContent class="tree-item-content">
<Tree.ItemContent class="pl-4">
{item.children.map((child) => renderTreeItem(child))}
</Tree.ItemContent>
</Tree.Item>
Expand Down
4 changes: 2 additions & 2 deletions libs/components/src/radio-group/radio-group-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ type TriggerRef = {
};

export interface RadioGroupContext {
selectedValueSig: Signal<string | undefined>;
isDisabledSig: Signal<boolean> | ReadonlySignal<boolean>;
selectedValue: Signal<string>;
isDisabled: Signal<boolean> | ReadonlySignal<boolean>;
isErrorSig: Signal<boolean>;
localId: string;
required?: boolean;
Expand Down
23 changes: 14 additions & 9 deletions libs/components/src/radio-group/radio-group-hidden-input.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
// 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 PublicHiddenInputProps = Omit<PropsOf<"input">, "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 (
<VisuallyHidden>
<input
{...restProps}
type="radio"
tabIndex={-1}
value={context.selectedValueSig.value}
value={context.selectedValue.value}
data-qds-radio-group-hidden-input
required={context.required ?? required ?? undefined}
checked={context.selectedValueSig.value !== undefined}
name={context.localId}
disabled={context.isDisabledSig.value}
name={context.name ?? context.localId}
disabled={context.isDisabled.value}
onChange$={[handleChange$, props.onChange$]}
/>
</VisuallyHidden>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
>
<Slot />
</Render>
Expand Down
2 changes: 1 addition & 1 deletion libs/components/src/radio-group/radio-group-item-label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
Expand Down
12 changes: 6 additions & 6 deletions libs/components/src/radio-group/radio-group-item-trigger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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}
Expand Down
8 changes: 3 additions & 5 deletions libs/components/src/radio-group/radio-group-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const radioGroupItemContextId = createContextId<RadioGroupItemContext>(
);

type RadioGroupItemContext = {
isSelectedSig: Signal<boolean>;
isSelected: Signal<boolean>;
itemValue: string;
itemId: string;
itemIndex: number;
Expand All @@ -38,12 +38,10 @@ 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
Expand Down
Loading