From c11929dd69c475df96408ca00298fb7cabdd9608 Mon Sep 17 00:00:00 2001 From: ross-kunaico Date: Fri, 8 Aug 2025 09:42:07 -0600 Subject: [PATCH 01/11] Use Claude Sonnet 4 for CodeNotate Co-authored-by: Ross Acheson --- apps/docs/code-notate.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/code-notate.config.js b/apps/docs/code-notate.config.js index 00457b2d1..6f5e0fd8a 100644 --- a/apps/docs/code-notate.config.js +++ b/apps/docs/code-notate.config.js @@ -1,6 +1,6 @@ /** @type {import('@kunai-consulting/code-notate').CodeNotateConfig} */ export default { - aiModel: "claude-3-7-sonnet-20250219", + aiModel: "claude-sonnet-4-20250514", aiProvider: "anthropic", documentationFolder: "./src/routes/base", formatCommand: "pnpm format", From 6d52d5e77d87dcda182cb9696d663fb537acaee1 Mon Sep 17 00:00:00 2001 From: ross-kunaico Date: Tue, 12 Aug 2025 17:39:55 -0600 Subject: [PATCH 02/11] Set showWeekNumber, showDaysOfWeek, and fullWeeks as bound props Co-authored-by: Ross Acheson --- .../src/calendar/calendar-context.tsx | 4 +-- .../src/calendar/calendar-grid-day.tsx | 2 +- .../components/src/calendar/calendar-grid.tsx | 6 ++-- .../components/src/calendar/calendar-root.tsx | 28 +++++++++---------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/libs/components/src/calendar/calendar-context.tsx b/libs/components/src/calendar/calendar-context.tsx index 9d77570a1..85e80df8b 100644 --- a/libs/components/src/calendar/calendar-context.tsx +++ b/libs/components/src/calendar/calendar-context.tsx @@ -7,8 +7,8 @@ export type CalendarContext = { locale: Locale; dateSig: Signal; dateToFocus: Signal; - showWeekNumber: boolean; - showDaysOfWeek: boolean; + showWeekNumber: Signal; + showDaysOfWeek: Signal; daysOfWeek: [ "Sunday", "Monday", diff --git a/libs/components/src/calendar/calendar-grid-day.tsx b/libs/components/src/calendar/calendar-grid-day.tsx index aad9b7f61..d69b694a5 100644 --- a/libs/components/src/calendar/calendar-grid-day.tsx +++ b/libs/components/src/calendar/calendar-grid-day.tsx @@ -33,7 +33,7 @@ export const CalendarGridDay = component$( data-qds-calendar-grid-body-row role="row" > - {context.showWeekNumber && ( + {context.showWeekNumber.value && ( // Displays the week number in the calendar grid
diff --git a/libs/components/src/calendar/calendar-grid.tsx b/libs/components/src/calendar/calendar-grid.tsx index 4ac43a4e8..c0e80ef0b 100644 --- a/libs/components/src/calendar/calendar-grid.tsx +++ b/libs/components/src/calendar/calendar-grid.tsx @@ -167,13 +167,13 @@ export const CalendarGrid = component$((props) => { data-qds-calendar-grid role="grid" {...divProps} - data-show-week-numbers={context.showWeekNumber ? "true" : "false"} + data-show-week-numbers={context.showWeekNumber.value ? "true" : "false"} > - {context.showDaysOfWeek && ( + {context.showDaysOfWeek.value && ( // The header section of the calendar grid // biome-ignore lint/a11y/useFocusableInteractive: The header section contains no elements that a user needs to interact with or focus on.
- {context.showWeekNumber && ( + {context.showWeekNumber.value && (
Wk
diff --git a/libs/components/src/calendar/calendar-root.tsx b/libs/components/src/calendar/calendar-root.tsx index e32436710..2cdd8df81 100644 --- a/libs/components/src/calendar/calendar-root.tsx +++ b/libs/components/src/calendar/calendar-root.tsx @@ -23,12 +23,6 @@ import { daysArrGenerator } from "./utils"; export type PublicCalendarRootProps = Omit & { /** The locale used for formatting dates and text */ locale?: Locale; - /** Whether to show week numbers in the calendar */ - showWeekNumber?: boolean; - /** Whether to show complete weeks by including days from adjacent months */ - fullWeeks?: boolean; - /** Whether to show the days of the week header */ - showDaysOfWeek?: boolean; /** Event handler called when a date is selected */ onChange$?: QRL<(date: ISODate | null) => void>; /** The display mode of the calendar */ @@ -42,6 +36,12 @@ export type CalendarRootBoundProps = { disabled: boolean; /** The open state of the calendar popover */ open: boolean; + /** Whether to show week numbers in the calendar */ + showWeekNumber: boolean; + /** Whether to show complete weeks by including days from adjacent months */ + fullWeeks: boolean; + /** Whether to show the days of the week header */ + showDaysOfWeek: boolean; }; // Regular expression for validating ISO date format (yyyy-mm-dd) @@ -51,18 +51,18 @@ const isoDateRegex = /^\d{1,4}-(0[1-9]|1[0-2])-\d{2}$/; export const CalendarRootBase = component$((props) => { useStyles$(styles); const { - fullWeeks = false, locale = "en", - showWeekNumber = false, - showDaysOfWeek = true, onChange$, mode = "inline", ...otherProps } = props; - const { dateSig, disabledSig, openSig } = useBindings(props, { + const { dateSig, disabledSig, openSig, fullWeeksSig, showWeekNumberSig, showDaysOfWeekSig } = useBindings(props, { date: props.date ?? null, disabled: false, - open: false + open: false, + fullWeeks: false, + showWeekNumber: false, + showDaysOfWeek: true }); const labelStr = props["aria-label"] ?? ARIA_LABELS[locale].root; const daysOfWeek = WEEKDAYS[locale]; @@ -79,14 +79,14 @@ export const CalendarRootBase = component$((props) => { const dates = daysArrGenerator({ month: monthToRender.value, year: yearToRender.value.toString(), - fullWeeks + fullWeeks: fullWeeksSig.value, }); return dates; }); const context: CalendarContext = { - showWeekNumber, - showDaysOfWeek, + showWeekNumber: showWeekNumberSig, + showDaysOfWeek: showDaysOfWeekSig, daysOfWeek, datesArray, locale, From 079db0e1a739930af937de73c1d6ab13c0954313 Mon Sep 17 00:00:00 2001 From: ross-kunaico Date: Tue, 12 Aug 2025 17:40:23 -0600 Subject: [PATCH 03/11] Remove unused icon Co-authored-by: Ross Acheson --- libs/components/src/calendar/icons.tsx | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 libs/components/src/calendar/icons.tsx diff --git a/libs/components/src/calendar/icons.tsx b/libs/components/src/calendar/icons.tsx deleted file mode 100644 index 761222170..000000000 --- a/libs/components/src/calendar/icons.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { type PropsOf, component$ } from "@builder.io/qwik"; - -export const Calendar = component$((props: PropsOf<"svg">) => ( - - - -)); From 43249a2302e31b14301889d7de0761965fe64943 Mon Sep 17 00:00:00 2001 From: ross-kunaico Date: Tue, 12 Aug 2025 17:42:11 -0600 Subject: [PATCH 04/11] Change script from cn to code-notate (more explicit) Co-authored-by: Ross Acheson --- apps/docs/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/package.json b/apps/docs/package.json index 3f84bf97c..e353745f1 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -21,7 +21,7 @@ "serve": "wrangler pages dev ./dist --compatibility-flags=nodejs_als", "start": "vite --open --mode ssr", "qwik": "qwik", - "cn": "cn" + "code-notate": "code-notate" }, "devDependencies": { "@builder.io/qwik": "^1.14.1", From 311a1226c36648d4e3db945cae5a2aa5352b6238 Mon Sep 17 00:00:00 2001 From: ross-kunaico Date: Wed, 13 Aug 2025 10:01:50 -0600 Subject: [PATCH 05/11] Manual progress on interactive example Co-authored-by: Ross Acheson --- .../base/calendar/examples/interactive.tsx | 62 +++++++ apps/docs/src/routes/base/calendar/index.mdx | 161 +----------------- 2 files changed, 68 insertions(+), 155 deletions(-) create mode 100644 apps/docs/src/routes/base/calendar/examples/interactive.tsx diff --git a/apps/docs/src/routes/base/calendar/examples/interactive.tsx b/apps/docs/src/routes/base/calendar/examples/interactive.tsx new file mode 100644 index 000000000..30add1128 --- /dev/null +++ b/apps/docs/src/routes/base/calendar/examples/interactive.tsx @@ -0,0 +1,62 @@ +import { $, component$, useSignal, useStyles$ } from "@builder.io/qwik"; +import { Calendar } from "@kunai-consulting/qwik"; + +import { NextIcon } from "../shared/next-icon"; +import { PreviousIcon } from "../shared/previous-icon"; +import styles from "./calendar.css?inline"; +import { DocsSwitch } from "../../../../docs-widgets/switch/switch"; + +export default component$(() => { + useStyles$(styles); + const selectedDate = useSignal(null); + + // Prop values + const disabled = useSignal(false); + const fullWeeks = useSignal(false); + const showDaysOfWeek = useSignal(true); + const showWeekNumbers = useSignal(true); + + return ( +
+
+ + + + + + + + + + + + + { + console.log("Date changed:", date); + selectedDate.value = date; + })} + /> + + + + +
+

Selected date: {selectedDate.value}

+
+
+
+ + + + +
+
+ ); +}); diff --git a/apps/docs/src/routes/base/calendar/index.mdx b/apps/docs/src/routes/base/calendar/index.mdx index 4b5a3aeb7..4c36b3b96 100644 --- a/apps/docs/src/routes/base/calendar/index.mdx +++ b/apps/docs/src/routes/base/calendar/index.mdx @@ -1,165 +1,16 @@ import api from "./code-notate/api.json"; # Calendar -A customizable date picker that helps users select dates through an intuitive grid interface. - -## Features - -## Anatomy - -## Examples -### Basic Usage -The calendar component provides a simple way to implement date selection functionality. The example below shows the basic setup with core components and date change handling. - -This example demonstrates: -- Using `Calendar.Root` as the main container with `locale` set to "en" by default -- `Calendar.Header` containing navigation buttons and month/year display -- `Calendar.Grid` for the calendar layout with `Calendar.GridDay` for individual date cells -- Date selection handling through `onDateChange$` prop -- Visual feedback for selected dates using `data-selected` and current date with `data-current` -Key props used: -- `onDateChange$`: Callback fired when a date is selected -- `data-selected`: Indicates the currently selected date -- `data-current`: Highlights the current date -- `class`: Custom styling applied to various components -The calendar supports full keyboard navigation and follows WAI-ARIA calendar design patterns for accessibility. - -### Date picker -The Calendar component can be used to create a -[date picker](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/examples/datepicker-dialog/) by including -the `Calendar.Field` component and setting the `mode` prop to "popover". -The `Calendar.Field` component is a wrapper around the `DateInput` component providing a text input that works in -conjunction with the calendar. - - -### Date picker with readonly date field -You can prevent users from manually entering a date by setting the `Calendar.Field` component's `openCalendarOnClick` prop. -In this mode, the date field is read-only and the calendar is opened when the user clicks on the field. - +A date picker that shows a monthly grid view for selecting dates -### Showing leading zeros -You can control the display of leading zeros in the calendar grid by setting the `showLeadingZeros` prop on the `Calendar.GridDay` component. - + -### Week numbers -You can control the display of week numbers in the calendar grid by setting the `showWeekNumber` prop on the `Calendar.Root` component. - - -### Full weeks -You can control the display of full weeks in the calendar grid by setting the `fullWeeks` prop on the `Calendar.Root` component. - - -## Component State -### Using Component State -The Calendar component's state can be controlled through several props on the `Calendar.Root` component: -1. **Selected Date** -Track the currently selected date using the `date` prop: -```tsx - - {/* Calendar content */} - -``` -2. **Display Options** -Control what information is shown in the calendar: -```tsx - - {/* Calendar content */} - -``` -### State Interactions -The Calendar provides ways to respond to date selection through event handlers: -1. **Date Selection** -Listen for date changes using the `onDateChange$` prop on either the `Calendar.Root` or `Calendar.GridDay` components: -```tsx - { - console.log('Selected date:', date); - }} -> - {/* Calendar content */} - -``` -As shown in the hero example, you can track the selected date in your application state: -```tsx -const selectedDate = useSignal(); - { - selectedDate.value = date; - })} -/> -``` -2. **Popover State** -Control the calendar popover's open state using the `bind:open` prop: -```tsx -const isOpen = useSignal(false); - - {/* Calendar content */} - -``` -The calendar component maintains its own internal state for: -- Current month/year being displayed -- Date currently in focus -- Navigation between months -- Keyboard navigation within the calendar grid -These aspects are handled automatically by the component and don't require manual management. +## Features -## Core Configuration -### Locale and Date Format -The Calendar component supports localization through the `locale` prop on `Calendar.Root`. Currently, it supports: -- Default locale: `"en"` -- Date format: `YYYY-MM-DD` -As shown in the hero example above, the calendar uses the default English locale for month names, weekday labels, and date formatting. -### Initial Date Selection -The calendar can be configured with: -- `defaultDate`: Sets the initial focused date (defaults to current date) -- `date`: Controls the currently selected date -- `showDaysOfWeek`: Controls visibility of weekday headers (defaults to `true`) -- `showWeekNumber`: Enables week number display (defaults to `false`) -### Week Display -The calendar provides two week display modes controlled by the `fullWeeks` prop: -```typescript -type WeekDisplayProps = { - // When true, shows complete weeks including days from adjacent months - fullWeeks?: boolean; // default: false -} -``` -## Advanced Configuration -### Date Change Handling -The calendar supports date selection handling through: -```typescript -type DateChangeProps = { - onDateChange$?: (date: LocalDate) => void; -} -``` -Where `LocalDate` is typed as: -```typescript -type LocalDate = `${number}-${number}-${number}`; -``` -### Grid Customization -The calendar grid can be customized through: -- `buttonProps`: Props to be spread onto each date button -- Custom day cell rendering through `Calendar.GridDay` -### Technical Constraints -1. Date Format Validation - - Dates must follow the `YYYY-MM-DD` format - - Invalid formats will throw an error during initialization -2. Month Navigation - - Month navigation is handled internally - - Year boundaries are automatically managed when navigating between December and January -3. Focus Management - - Focus is automatically managed within the calendar grid - - Only one date cell is focusable at a time (tabIndex management) - - Focus is preserved during month navigation -The calendar maintains internal state for: -- Current month/year being displayed -- Selected date -- Focused date -- Week number calculations (when enabled) + +## Anatomy + \ No newline at end of file From 4792e1b287e1f37de7854aa5d8c933344445263d Mon Sep 17 00:00:00 2001 From: ross-kunaico Date: Thu, 14 Aug 2025 10:33:49 -0600 Subject: [PATCH 06/11] Make mode a signal as well Co-authored-by: Ross Acheson --- libs/components/src/calendar/calendar-content.tsx | 3 +-- libs/components/src/calendar/calendar-context.tsx | 2 +- libs/components/src/calendar/calendar-root.tsx | 12 ++++++------ 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/libs/components/src/calendar/calendar-content.tsx b/libs/components/src/calendar/calendar-content.tsx index 06bc36313..f1e6b11fa 100644 --- a/libs/components/src/calendar/calendar-content.tsx +++ b/libs/components/src/calendar/calendar-content.tsx @@ -9,9 +9,8 @@ import { calendarContextId } from "./calendar-context"; export const CalendarContentBase = component$(() => { useStyles$(styles); const context = useContext(calendarContextId); - const mode = context.mode; - if (mode === "popover") { + if (context.mode.value === "popover") { return ( diff --git a/libs/components/src/calendar/calendar-context.tsx b/libs/components/src/calendar/calendar-context.tsx index 85e80df8b..3ba220dab 100644 --- a/libs/components/src/calendar/calendar-context.tsx +++ b/libs/components/src/calendar/calendar-context.tsx @@ -24,6 +24,6 @@ export type CalendarContext = { currentDate: ISODate; localId: string; isPopoverOpenSig: Signal; - mode: "inline" | "popover"; + mode: Signal<"inline" | "popover">; disabledSig: Signal; }; diff --git a/libs/components/src/calendar/calendar-root.tsx b/libs/components/src/calendar/calendar-root.tsx index 2cdd8df81..2b2363292 100644 --- a/libs/components/src/calendar/calendar-root.tsx +++ b/libs/components/src/calendar/calendar-root.tsx @@ -25,8 +25,6 @@ export type PublicCalendarRootProps = Omit & { locale?: Locale; /** Event handler called when a date is selected */ onChange$?: QRL<(date: ISODate | null) => void>; - /** The display mode of the calendar */ - mode?: "inline" | "popover"; } & BindableProps; export type CalendarRootBoundProps = { @@ -34,6 +32,8 @@ export type CalendarRootBoundProps = { date: ISODate | null; /** When enabled prevents the user from interacting with the controls */ disabled: boolean; + /** The display mode of the calendar */ + mode: "inline" | "popover"; /** The open state of the calendar popover */ open: boolean; /** Whether to show week numbers in the calendar */ @@ -53,16 +53,16 @@ export const CalendarRootBase = component$((props) => { const { locale = "en", onChange$, - mode = "inline", ...otherProps } = props; - const { dateSig, disabledSig, openSig, fullWeeksSig, showWeekNumberSig, showDaysOfWeekSig } = useBindings(props, { + const { dateSig, disabledSig, openSig, fullWeeksSig, showWeekNumberSig, showDaysOfWeekSig, modeSig } = useBindings(props, { date: props.date ?? null, disabled: false, open: false, fullWeeks: false, showWeekNumber: false, - showDaysOfWeek: true + showDaysOfWeek: true, + mode: "inline" }); const labelStr = props["aria-label"] ?? ARIA_LABELS[locale].root; const daysOfWeek = WEEKDAYS[locale]; @@ -97,7 +97,7 @@ export const CalendarRootBase = component$((props) => { currentDate, localId, isPopoverOpenSig: openSig, - mode, + mode: modeSig, disabledSig }; From 9205c2137616fe0c89f06563bf57d8db2f7b58a6 Mon Sep 17 00:00:00 2001 From: ross-kunaico Date: Thu, 14 Aug 2025 10:35:41 -0600 Subject: [PATCH 07/11] Fix typo Co-authored-by: Ross Acheson --- apps/docs/src/routes/base/calendar/examples/reactive.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/src/routes/base/calendar/examples/reactive.tsx b/apps/docs/src/routes/base/calendar/examples/reactive.tsx index 787b3ce8f..d9b4ef9bf 100644 --- a/apps/docs/src/routes/base/calendar/examples/reactive.tsx +++ b/apps/docs/src/routes/base/calendar/examples/reactive.tsx @@ -11,7 +11,7 @@ export default component$(() => { return (
- + / From 33f62a4df196444d02712e9da8f523200fe04cda Mon Sep 17 00:00:00 2001 From: ross-kunaico Date: Thu, 14 Aug 2025 10:56:34 -0600 Subject: [PATCH 08/11] Progress on interactive example Co-authored-by: Ross Acheson --- .../example-container/example-container.tsx | 14 ++++ apps/docs/src/docs-widgets/switch/switch.css | 0 apps/docs/src/docs-widgets/switch/switch.tsx | 24 +++++++ .../toggle-group/toggle-group.tsx | 67 +++++++++++++++++++ .../base/calendar/examples/interactive.tsx | 59 ++++++++++------ 5 files changed, 145 insertions(+), 19 deletions(-) create mode 100644 apps/docs/src/docs-widgets/example-container/example-container.tsx create mode 100644 apps/docs/src/docs-widgets/switch/switch.css create mode 100644 apps/docs/src/docs-widgets/switch/switch.tsx create mode 100644 apps/docs/src/docs-widgets/toggle-group/toggle-group.tsx diff --git a/apps/docs/src/docs-widgets/example-container/example-container.tsx b/apps/docs/src/docs-widgets/example-container/example-container.tsx new file mode 100644 index 000000000..0122e839f --- /dev/null +++ b/apps/docs/src/docs-widgets/example-container/example-container.tsx @@ -0,0 +1,14 @@ +import { component$, Slot } from "@builder.io/qwik"; + +export const ExampleContainer = component$(() => { + return ( +
+
+ +
+
+ +
+
+ ); +}); diff --git a/apps/docs/src/docs-widgets/switch/switch.css b/apps/docs/src/docs-widgets/switch/switch.css new file mode 100644 index 000000000..e69de29bb diff --git a/apps/docs/src/docs-widgets/switch/switch.tsx b/apps/docs/src/docs-widgets/switch/switch.tsx new file mode 100644 index 000000000..374cea0b0 --- /dev/null +++ b/apps/docs/src/docs-widgets/switch/switch.tsx @@ -0,0 +1,24 @@ +import { + component$, + useStyles$, + Signal +} from "@builder.io/qwik"; +import { Switch } from "@kunai-consulting/qwik"; +import styles from "../../routes/base/switch/examples/switch-custom.css?inline"; +//import styles from "./switch.css?inline"; +type Props = { + value: Signal; + label: string; +} +export const DocsSwitch = component$((props: Props) => { + useStyles$(styles); + + return ( + + + + + {props.label} + + ); +}); diff --git a/apps/docs/src/docs-widgets/toggle-group/toggle-group.tsx b/apps/docs/src/docs-widgets/toggle-group/toggle-group.tsx new file mode 100644 index 000000000..7ace3ca36 --- /dev/null +++ b/apps/docs/src/docs-widgets/toggle-group/toggle-group.tsx @@ -0,0 +1,67 @@ +import { component$, type Signal } from "@builder.io/qwik"; + +export type ToggleItem = { + value: string; + label?: string; +}; + +export type DocsToggleGroupProps = { + // Current selected value + value: Signal; + // Items to render in the toggle group + items: ToggleItem[]; + // Optional accessible label for the group + ariaLabel?: string; + // Optional extra classes for the container + class?: string; +}; + +export const DocsToggleGroup = component$((props) => { + const { value, items, ariaLabel = "Toggle group", class: className } = props; + + return ( +
+ {items.map((item, idx) => { + const selected = value.value === item.value; + return ( + + ); + })} +
+ ); +}); \ No newline at end of file diff --git a/apps/docs/src/routes/base/calendar/examples/interactive.tsx b/apps/docs/src/routes/base/calendar/examples/interactive.tsx index 30add1128..c27a5ab34 100644 --- a/apps/docs/src/routes/base/calendar/examples/interactive.tsx +++ b/apps/docs/src/routes/base/calendar/examples/interactive.tsx @@ -5,43 +5,61 @@ import { NextIcon } from "../shared/next-icon"; import { PreviousIcon } from "../shared/previous-icon"; import styles from "./calendar.css?inline"; import { DocsSwitch } from "../../../../docs-widgets/switch/switch"; +import { DocsToggleGroup } from "../../../../docs-widgets/toggle-group/toggle-group"; +import { CalendarIcon } from "../shared/calendar-icon"; +import { ExampleContainer } from "../../../../docs-widgets/example-container/example-container"; export default component$(() => { useStyles$(styles); + + // Primary data const selectedDate = useSignal(null); - // Prop values + // Props + const mode = useSignal<"inline" | "popover">("inline"); const disabled = useSignal(false); const fullWeeks = useSignal(false); - const showDaysOfWeek = useSignal(true); - const showWeekNumbers = useSignal(true); + const showDaysOfWeek = useSignal(false); + const showWeekNumbers = useSignal(false); + const open = useSignal(false); return ( -
-
+ +
+ bind:date={selectedDate} + // props + bind:mode={mode} + bind:disabled={disabled} + bind:fullWeeks={fullWeeks} + bind:showDaysOfWeek={showDaysOfWeek} + bind:showWeekNumber={showWeekNumbers} + bind:open={open} + > + {mode.value === "popover" &&
+ + + + + + + + +
} - + - + - + { - console.log("Date changed:", date); - selectedDate.value = date; - })} /> @@ -51,12 +69,15 @@ export default component$(() => {

Selected date: {selectedDate.value}

-
- + +
+ + {mode.value === "popover" && } +
-
+ ); }); From 2ef69ca37a6de412df186eb0040f1f2c017120c2 Mon Sep 17 00:00:00 2001 From: ross-kunaico Date: Thu, 14 Aug 2025 11:12:26 -0600 Subject: [PATCH 09/11] Formatting Co-authored-by: Ross Acheson --- .../example-container/example-container.tsx | 2 +- apps/docs/src/docs-widgets/switch/switch.tsx | 8 +-- .../toggle-group/toggle-group.tsx | 12 ++--- .../base/calendar/examples/interactive.tsx | 51 ++++++++++--------- .../components/src/calendar/calendar-root.tsx | 16 +++--- 5 files changed, 45 insertions(+), 44 deletions(-) diff --git a/apps/docs/src/docs-widgets/example-container/example-container.tsx b/apps/docs/src/docs-widgets/example-container/example-container.tsx index 0122e839f..bb1e3c575 100644 --- a/apps/docs/src/docs-widgets/example-container/example-container.tsx +++ b/apps/docs/src/docs-widgets/example-container/example-container.tsx @@ -1,4 +1,4 @@ -import { component$, Slot } from "@builder.io/qwik"; +import { Slot, component$ } from "@builder.io/qwik"; export const ExampleContainer = component$(() => { return ( diff --git a/apps/docs/src/docs-widgets/switch/switch.tsx b/apps/docs/src/docs-widgets/switch/switch.tsx index 374cea0b0..2a02c2aff 100644 --- a/apps/docs/src/docs-widgets/switch/switch.tsx +++ b/apps/docs/src/docs-widgets/switch/switch.tsx @@ -1,15 +1,11 @@ -import { - component$, - useStyles$, - Signal -} from "@builder.io/qwik"; +import { type Signal, component$, useStyles$ } from "@builder.io/qwik"; import { Switch } from "@kunai-consulting/qwik"; import styles from "../../routes/base/switch/examples/switch-custom.css?inline"; //import styles from "./switch.css?inline"; type Props = { value: Signal; label: string; -} +}; export const DocsSwitch = component$((props: Props) => { useStyles$(styles); diff --git a/apps/docs/src/docs-widgets/toggle-group/toggle-group.tsx b/apps/docs/src/docs-widgets/toggle-group/toggle-group.tsx index 7ace3ca36..a02247326 100644 --- a/apps/docs/src/docs-widgets/toggle-group/toggle-group.tsx +++ b/apps/docs/src/docs-widgets/toggle-group/toggle-group.tsx @@ -1,4 +1,4 @@ -import { component$, type Signal } from "@builder.io/qwik"; +import { type Signal, component$ } from "@builder.io/qwik"; export type ToggleItem = { value: string; @@ -26,7 +26,7 @@ export const DocsToggleGroup = component$((props) => { class={[ "inline-flex overflow-hidden rounded-md border shadow-sm", "border-neutral-300 bg-white dark:border-neutral-700 dark:bg-neutral-900", - className, + className ] .filter(Boolean) .join(" ")} @@ -47,13 +47,11 @@ export const DocsToggleGroup = component$((props) => { "focus-visible:ring-2 focus-visible:ring-sky-500 ring-offset-2", "ring-offset-white dark:ring-offset-neutral-900", // borders between items - idx !== 0 - ? "border-l border-neutral-300 dark:border-neutral-700" - : "", + idx !== 0 ? "border-l border-neutral-300 dark:border-neutral-700" : "", // selected vs idle selected ? "bg-neutral-200 text-neutral-900 dark:bg-neutral-800 dark:text-neutral-50" - : "text-neutral-700 hover:bg-neutral-100 dark:text-neutral-200 dark:hover:bg-neutral-800", + : "text-neutral-700 hover:bg-neutral-100 dark:text-neutral-200 dark:hover:bg-neutral-800" ] .filter(Boolean) .join(" ")} @@ -64,4 +62,4 @@ export const DocsToggleGroup = component$((props) => { })}
); -}); \ No newline at end of file +}); diff --git a/apps/docs/src/routes/base/calendar/examples/interactive.tsx b/apps/docs/src/routes/base/calendar/examples/interactive.tsx index c27a5ab34..96dc17e7e 100644 --- a/apps/docs/src/routes/base/calendar/examples/interactive.tsx +++ b/apps/docs/src/routes/base/calendar/examples/interactive.tsx @@ -1,13 +1,13 @@ -import { $, component$, useSignal, useStyles$ } from "@builder.io/qwik"; +import { component$, useSignal, useStyles$ } from "@builder.io/qwik"; import { Calendar } from "@kunai-consulting/qwik"; -import { NextIcon } from "../shared/next-icon"; -import { PreviousIcon } from "../shared/previous-icon"; -import styles from "./calendar.css?inline"; +import { ExampleContainer } from "../../../../docs-widgets/example-container/example-container"; import { DocsSwitch } from "../../../../docs-widgets/switch/switch"; import { DocsToggleGroup } from "../../../../docs-widgets/toggle-group/toggle-group"; import { CalendarIcon } from "../shared/calendar-icon"; -import { ExampleContainer } from "../../../../docs-widgets/example-container/example-container"; +import { NextIcon } from "../shared/next-icon"; +import { PreviousIcon } from "../shared/previous-icon"; +import styles from "./calendar.css?inline"; export default component$(() => { useStyles$(styles); @@ -37,30 +37,30 @@ export default component$(() => { bind:showWeekNumber={showWeekNumbers} bind:open={open} > - {mode.value === "popover" &&
- - - - - - - - -
} + {mode.value === "popover" && ( +
+ + + + + + + + +
+ )} - + - + - + - +
@@ -71,9 +71,12 @@ export default component$(() => {
- - {mode.value === "popover" && } - + + {mode.value === "popover" && } + diff --git a/libs/components/src/calendar/calendar-root.tsx b/libs/components/src/calendar/calendar-root.tsx index 2b2363292..6d9b34579 100644 --- a/libs/components/src/calendar/calendar-root.tsx +++ b/libs/components/src/calendar/calendar-root.tsx @@ -50,12 +50,16 @@ const isoDateRegex = /^\d{1,4}-(0[1-9]|1[0-2])-\d{2}$/; /** The root calendar component that manages state and provides context */ export const CalendarRootBase = component$((props) => { useStyles$(styles); + const { locale = "en", onChange$, ...otherProps } = props; const { - locale = "en", - onChange$, - ...otherProps - } = props; - const { dateSig, disabledSig, openSig, fullWeeksSig, showWeekNumberSig, showDaysOfWeekSig, modeSig } = useBindings(props, { + dateSig, + disabledSig, + openSig, + fullWeeksSig, + showWeekNumberSig, + showDaysOfWeekSig, + modeSig + } = useBindings(props, { date: props.date ?? null, disabled: false, open: false, @@ -79,7 +83,7 @@ export const CalendarRootBase = component$((props) => { const dates = daysArrGenerator({ month: monthToRender.value, year: yearToRender.value.toString(), - fullWeeks: fullWeeksSig.value, + fullWeeks: fullWeeksSig.value }); return dates; }); From 643eb7b87ab38d286909ac4364deb2d6fea02356 Mon Sep 17 00:00:00 2001 From: ross-kunaico Date: Fri, 15 Aug 2025 11:43:06 -0600 Subject: [PATCH 10/11] Some checklist updates from CodeNotate Co-authored-by: Ross Acheson --- .../base/checklist/code-notate/api.json | 4 +++- .../src/checklist/checklist-error.tsx | 1 + .../src/checklist/checklist-hidden-input.tsx | 3 +++ .../checklist/checklist-item-description.tsx | 1 + .../checklist/checklist-item-indicator.tsx | 1 + .../src/checklist/checklist-item-label.tsx | 1 + .../src/checklist/checklist-item-trigger.tsx | 1 + .../src/checklist/checklist-item.tsx | 1 + .../src/checklist/checklist-label.tsx | 1 + .../src/checklist/checklist-root.tsx | 1 + .../checklist-select-all-indicator.tsx | 1 + .../src/checklist/checklist-select-all.tsx | 1 + libs/components/src/checklist/metadata.json | 24 +++++++++---------- 13 files changed, 28 insertions(+), 13 deletions(-) diff --git a/apps/docs/src/routes/base/checklist/code-notate/api.json b/apps/docs/src/routes/base/checklist/code-notate/api.json index 941407d83..ed21ab1b2 100644 --- a/apps/docs/src/routes/base/checklist/code-notate/api.json +++ b/apps/docs/src/routes/base/checklist/code-notate/api.json @@ -98,5 +98,7 @@ "Independent item state management", "Automated item indexing", "Context-based state propagation" - ] + ], + "name": "Checklist", + "summary": "A group of checkboxes with a select all option for choosing multiple items." } diff --git a/libs/components/src/checklist/checklist-error.tsx b/libs/components/src/checklist/checklist-error.tsx index 0a18d0316..6f4644033 100644 --- a/libs/components/src/checklist/checklist-error.tsx +++ b/libs/components/src/checklist/checklist-error.tsx @@ -1,6 +1,7 @@ import { type PropsOf, Slot, component$ } from "@builder.io/qwik"; import { CheckboxErrorBase } from "../checkbox/checkbox-error"; +/** Error message component for the checklist */ export const ChecklistError = component$((props: PropsOf) => { return ( diff --git a/libs/components/src/checklist/checklist-hidden-input.tsx b/libs/components/src/checklist/checklist-hidden-input.tsx index 0b15652ad..bc54b45b1 100644 --- a/libs/components/src/checklist/checklist-hidden-input.tsx +++ b/libs/components/src/checklist/checklist-hidden-input.tsx @@ -2,8 +2,11 @@ import { component$ } from "@builder.io/qwik"; import { CheckboxHiddenInput } from "../checkbox/checkbox-hidden-input"; type PublicCheckboxHiddenInputProps = { + /** The name attribute for the hidden input element used in form submission */ name: string; + /** The value attribute for the hidden input element when checked */ value?: string; + /** Whether the checklist input is required for form validation */ required?: boolean; }; diff --git a/libs/components/src/checklist/checklist-item-description.tsx b/libs/components/src/checklist/checklist-item-description.tsx index ffb81081f..9b65d2bb6 100644 --- a/libs/components/src/checklist/checklist-item-description.tsx +++ b/libs/components/src/checklist/checklist-item-description.tsx @@ -1,6 +1,7 @@ import { type PropsOf, Slot, component$ } from "@builder.io/qwik"; import { CheckboxDescriptionBase } from "../checkbox/checkbox-description"; +/** Description component for individual checklist items */ export const ChecklistItemDescription = component$( (props: PropsOf) => { return ( diff --git a/libs/components/src/checklist/checklist-item-indicator.tsx b/libs/components/src/checklist/checklist-item-indicator.tsx index 3be7e8858..ce33ac1f2 100644 --- a/libs/components/src/checklist/checklist-item-indicator.tsx +++ b/libs/components/src/checklist/checklist-item-indicator.tsx @@ -1,6 +1,7 @@ import { type PropsOf, Slot, component$ } from "@builder.io/qwik"; import { CheckboxIndicatorBase } from "../checkbox/checkbox-indicator"; +/** Visual indicator component for individual checklist items */ export const ChecklistItemIndicator = component$( (props: PropsOf) => { return ( diff --git a/libs/components/src/checklist/checklist-item-label.tsx b/libs/components/src/checklist/checklist-item-label.tsx index d0febfb71..d0bf3945f 100644 --- a/libs/components/src/checklist/checklist-item-label.tsx +++ b/libs/components/src/checklist/checklist-item-label.tsx @@ -1,6 +1,7 @@ import { type PropsOf, Slot, component$ } from "@builder.io/qwik"; import { CheckboxLabelBase } from "../checkbox/checkbox-label"; +/** Label component for individual checklist items */ export const ChecklistItemLabel = component$( (props: PropsOf) => { return ( diff --git a/libs/components/src/checklist/checklist-item-trigger.tsx b/libs/components/src/checklist/checklist-item-trigger.tsx index af8ec9c58..06eb2f26b 100644 --- a/libs/components/src/checklist/checklist-item-trigger.tsx +++ b/libs/components/src/checklist/checklist-item-trigger.tsx @@ -1,6 +1,7 @@ import { type PropsOf, Slot, component$ } from "@builder.io/qwik"; import { CheckboxTriggerBase } from "../checkbox/checkbox-trigger"; +/** Clickable trigger component for individual checklist items */ export const ChecklistItemTrigger = component$( (props: PropsOf) => { return ( diff --git a/libs/components/src/checklist/checklist-item.tsx b/libs/components/src/checklist/checklist-item.tsx index 5cee5f3a5..1aa71cf8c 100644 --- a/libs/components/src/checklist/checklist-item.tsx +++ b/libs/components/src/checklist/checklist-item.tsx @@ -58,6 +58,7 @@ export const ChecklistItemBase = component$((props: PublicChecklistItemProps) => ); }); +/** Individual item component within a checklist with asChild functionality */ export const ChecklistItem = withAsChild(ChecklistItemBase, (props) => { props._index = getNextIndex("qds-checklist"); diff --git a/libs/components/src/checklist/checklist-label.tsx b/libs/components/src/checklist/checklist-label.tsx index a23a3fe87..bbd9377ff 100644 --- a/libs/components/src/checklist/checklist-label.tsx +++ b/libs/components/src/checklist/checklist-label.tsx @@ -3,6 +3,7 @@ import { CheckboxLabelBase } from "../checkbox/checkbox-label"; // no-as-child TODO: remove this comment +/** Label component for the checklist */ export const ChecklistLabel = component$((props: PropsOf) => { return ( // Identifies the label element for the select all checkbox in the checklist diff --git a/libs/components/src/checklist/checklist-root.tsx b/libs/components/src/checklist/checklist-root.tsx index d001f6e5c..fd76fafe9 100644 --- a/libs/components/src/checklist/checklist-root.tsx +++ b/libs/components/src/checklist/checklist-root.tsx @@ -12,6 +12,7 @@ import { type ChecklistContext, checklistContextId } from "./checklist-context"; type PublicChecklistRootProps = Omit, "onChange$">; +/** Root container component for the checklist */ export const ChecklistRootBase = component$((props: PublicChecklistRootProps) => { const isAllCheckedSig = useSignal(false); const checkedStatesSig = useSignal<(boolean | "mixed")[]>([]); diff --git a/libs/components/src/checklist/checklist-select-all-indicator.tsx b/libs/components/src/checklist/checklist-select-all-indicator.tsx index 59acc30c7..4a2527b9e 100644 --- a/libs/components/src/checklist/checklist-select-all-indicator.tsx +++ b/libs/components/src/checklist/checklist-select-all-indicator.tsx @@ -1,6 +1,7 @@ import { type PropsOf, Slot, component$ } from "@builder.io/qwik"; import { CheckboxIndicatorBase } from "../checkbox/checkbox-indicator"; +/** Visual indicator component for the select all checkbox */ export const ChecklistSelectAllIndicator = component$( (props: PropsOf) => { return ( diff --git a/libs/components/src/checklist/checklist-select-all.tsx b/libs/components/src/checklist/checklist-select-all.tsx index 9f97f2484..3f8c44324 100644 --- a/libs/components/src/checklist/checklist-select-all.tsx +++ b/libs/components/src/checklist/checklist-select-all.tsx @@ -1,6 +1,7 @@ import { type PropsOf, Slot, component$ } from "@builder.io/qwik"; import { CheckboxTriggerBase } from "../checkbox/checkbox-trigger"; +/** Select all trigger component for the checklist */ export const ChecklistSelectAll = component$( (props: PropsOf) => { return ( diff --git a/libs/components/src/checklist/metadata.json b/libs/components/src/checklist/metadata.json index 382a859c6..ed3c8bbe5 100644 --- a/libs/components/src/checklist/metadata.json +++ b/libs/components/src/checklist/metadata.json @@ -1,4 +1,16 @@ { + "features": [ + "Select all/unselect all functionality", + "Mixed state handling for partial selections", + "Individual item selection tracking", + "WAI-ARIA checkbox list pattern", + "Form input integration", + "Dynamic item state synchronization", + "Nested accessibility descriptions per item", + "Independent item state management", + "Automated item indexing", + "Context-based state propagation" + ], "keyboard": [ { "key": "Space", @@ -16,17 +28,5 @@ "key": "Shift+Tab", "comment": "Moves focus to the previous checkbox trigger" } - ], - "features": [ - "Select all/unselect all functionality", - "Mixed state handling for partial selections", - "Individual item selection tracking", - "WAI-ARIA checkbox list pattern", - "Form input integration", - "Dynamic item state synchronization", - "Nested accessibility descriptions per item", - "Independent item state management", - "Automated item indexing", - "Context-based state propagation" ] } From 4b7c1433c0e8cb17d734439fb8cd0c6afe9e0490 Mon Sep 17 00:00:00 2001 From: ross-kunaico Date: Thu, 21 Aug 2025 17:49:59 -0600 Subject: [PATCH 11/11] Checkbox summary from CodeNotate Co-authored-by: Ross Acheson --- .../routes/base/checkbox/code-notate/api.json | 4 +- apps/docs/src/routes/base/checklist/index.mdx | 111 +----------------- 2 files changed, 9 insertions(+), 106 deletions(-) diff --git a/apps/docs/src/routes/base/checkbox/code-notate/api.json b/apps/docs/src/routes/base/checkbox/code-notate/api.json index a801d80ec..a0c0f2bc4 100644 --- a/apps/docs/src/routes/base/checkbox/code-notate/api.json +++ b/apps/docs/src/routes/base/checkbox/code-notate/api.json @@ -208,5 +208,7 @@ "Focus management with ref tracking", "Disabled state handling", "Enter key prevention for form submissions" - ] + ], + "name": "Checkbox", + "summary": "A clickable box that can be checked or unchecked to select options." } diff --git a/apps/docs/src/routes/base/checklist/index.mdx b/apps/docs/src/routes/base/checklist/index.mdx index decafcde0..2fefb9366 100644 --- a/apps/docs/src/routes/base/checklist/index.mdx +++ b/apps/docs/src/routes/base/checklist/index.mdx @@ -1,115 +1,16 @@ import api from "./code-notate/api.json"; # Checklist -A group of selectable items that can be toggled individually or all at once. - -## Features - -## Anatomy - -## Examples -### Basic Usage -The basic checklist setup allows users to select multiple items independently. - -This example demonstrates: -- `Checklist.Root` as the container for all checklist items -- `Checklist.Item` for individual selectable items -- `Checklist.ItemTrigger` and `Checklist.ItemIndicator` for the checkbox interaction -- `Checklist.ItemLabel` for item text -- `Checklist.SelectAll` for the parent checkbox that controls all items -- `Checklist.SelectAllIndicator` showing the mixed state with both check and minus icons -- `Checklist.Label` for the select all text -The select-all functionality automatically manages three states: -- Unchecked when no items are selected -- Mixed state when some items are selected -- Checked when all items are selected -Each item maintains its own state while staying synchronized with the select-all checkbox. -Note: The example uses Lucide icons (`LuCheck` and `LuMinus`) to display the checkbox states, but you can customize these with your own icons or indicators. - -### Form Integration -Integrate with native HTML forms by adding the `Checklist.HiddenInput` component under each item. -The selected items are then automatically submitted with the form. The `name` input is required and should be unique -within the form. - +A group of checkboxes with a select all option for choosing multiple items. -## Component State -### Using Component State -The Checklist component provides a powerful way to manage multiple checkbox selections with a "select all" capability. Let's look at how to implement and control the checklist state. -The basic checklist state is demonstrated in the hero example: - -For more advanced state management, including a "select all" feature: - -This example demonstrates: -- Individual item selection state -- Select all functionality -- Mixed state when only some items are selected -### State Interactions -The checklist maintains three main states: -- Unchecked: No items selected -- Mixed: Some items selected -- Checked: All items selected -The select-all checkbox automatically updates based on the state of individual items: -- Shows unchecked when no items are selected -- Shows a mixed state when some items are selected -- Shows checked when all items are selected -To respond to state changes, the checklist items and select-all checkbox are automatically synchronized: -1. When clicking the select-all checkbox: - - If unchecked or mixed: All items become checked - - If checked: All items become unchecked -2. When clicking individual items: - - Updates the select-all checkbox state based on overall selection - - Maintains the mixed state when appropriate -The state management is handled automatically by the component, requiring no additional configuration from the user. Simply structure your checklist with the appropriate components and the state synchronization works out of the box. + -Based on the provided implementation and examples, I'll document the configuration options for the Checklist component. -## Core Configuration -### Select All Behavior -The Checklist component supports a "select all" functionality that manages the state of all child checkboxes. As shown in the `select-all` example above, this requires configuring both the select all trigger and individual items. -The select all state automatically manages three possible values: -- `false` - No items checked -- `true` - All items checked -- `"mixed"` - Some items checked -### Item Management -Items must be direct children of `Checklist.Root` to be properly tracked. The component internally manages indices for state synchronization. -> Each `Checklist.Item` requires a unique key when mapping over items to maintain proper state tracking. -### Group Configuration -The Checklist is configured as a checkbox group by default with the following characteristics: -```typescript -type ChecklistContext = { - isAllCheckedSig: Signal; - checkedStatesSig: Signal<(boolean | "mixed")[]>; -}; -``` -The context manages: -- Overall checked state (`isAllCheckedSig`) -- Individual item states (`checkedStatesSig`) -### Form Integration -The Checklist can be integrated with forms through the `HiddenInput` component. As shown in the `hero` example above, this manages the form submission state for all checkboxes in the group. -## Advanced Configuration -### State Synchronization -The Checklist implements a bi-directional state synchronization: -1. Select All → Items: -- When select all is toggled, all items update to match -- Mixed state is preserved when partially selected -2. Items → Select All: -- Select all updates based on collective item state -- Automatically switches to mixed state when appropriate -### Custom Layouts -While the component handles state management, the layout is fully customizable. As shown in the `select-all` example above, items can be nested and grouped with custom spacing and hierarchies. -> Note: The internal state tracking works regardless of DOM structure, but items must remain direct children of `Checklist.Root` in the component tree. +## Features -Based on the provided implementation and examples, I'll document the form-specific features of the Checklist component. -## Forms -The Checklist component provides form integration through the `` component, which manages multiple checkbox form inputs. -The component follows a group pattern where the select-all functionality can control multiple checkbox states simultaneously. - -In this example, the select-all checkbox controls the state of all child checkboxes, maintaining form state synchronization. When some items are selected, the select-all checkbox displays a mixed state, indicated by the minus icon. -The form state is managed through the `ChecklistContext`, which tracks: -- The overall checked state (`isAllCheckedSig`) -- Individual item states (`checkedStatesSig`) -The `` component acts as a form group container with the appropriate `role="group"` attribute, ensuring proper form semantics and accessibility. -Note: The current implementation doesn't show explicit form validation examples, but the component structure includes `` for handling validation states when needed. + +## Anatomy + \ No newline at end of file