Skip to content
Merged
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ Versioning follows [SemVer](https://semver.org/).
- **Visual regression** — Playwright gallery screenshots (`npm run test:visual`) + CI step
- Gallery **Welcome** feature cards (Theme Builder + App shell) · README gallery map
- GETTING-STARTED refresh for v1.8 languages + gallery hashes
- **`Calendar`** — inline day/month/year grids (single · range); shared by DatePicker
- **`TimePicker`** — trigger + panel with TimeInput, presets, Apply
- Studio: `#library/calendar` · `#library/time-picker`

### Changed

- **DatePicker** panel body now composes **`Calendar`** (behavior preserved)

## [1.8.0] — 2026-07-31

Expand Down
36 changes: 36 additions & 0 deletions apps/web/public/r/calendar.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/web/public/r/data-table.json

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions apps/web/public/r/date-picker.json

Large diffs are not rendered by default.

83 changes: 81 additions & 2 deletions apps/web/public/r/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -447,13 +447,45 @@
}
]
},
{
"name": "calendar",
"type": "registry:ui",
"title": "Calendar",
"description": "Inline day/month/year grid — single or range ISO dates.",
"registryDependencies": [
"tokens"
],
"files": [
{
"path": "packages/ui/src/atoms/calendar.tsx",
"type": "registry:ui",
"target": "components/softglass/atoms/calendar.tsx"
},
{
"path": "packages/ui/src/lib/cn.ts",
"type": "registry:lib",
"target": "components/softglass/lib/cn.ts"
},
{
"path": "packages/ui/src/lib/looks.ts",
"type": "registry:lib",
"target": "components/softglass/lib/looks.ts"
},
{
"path": "packages/ui/src/lib/motion.ts",
"type": "registry:lib",
"target": "components/softglass/lib/motion.ts"
}
]
},
{
"name": "date-picker",
"type": "registry:ui",
"title": "Date Picker",
"description": "Single ISO date calendar — body portal + flip/clamp panel.",
"description": "Single or range ISO dates — portal panel over Calendar.",
"registryDependencies": [
"tokens"
"tokens",
"calendar"
],
"files": [
{
Expand Down Expand Up @@ -493,6 +525,53 @@
}
]
},
{
"name": "time-picker",
"type": "registry:ui",
"title": "Time Picker",
"description": "HH:mm trigger + panel with TimeInput and presets.",
"registryDependencies": [
"tokens",
"time-input"
],
"files": [
{
"path": "packages/ui/src/atoms/time-picker.tsx",
"type": "registry:ui",
"target": "components/softglass/atoms/time-picker.tsx"
},
{
"path": "packages/ui/src/lib/cn.ts",
"type": "registry:lib",
"target": "components/softglass/lib/cn.ts"
},
{
"path": "packages/ui/src/lib/looks.ts",
"type": "registry:lib",
"target": "components/softglass/lib/looks.ts"
},
{
"path": "packages/ui/src/lib/motion.ts",
"type": "registry:lib",
"target": "components/softglass/lib/motion.ts"
},
{
"path": "packages/ui/src/lib/presence.ts",
"type": "registry:lib",
"target": "components/softglass/lib/presence.ts"
},
{
"path": "packages/ui/src/lib/floating.ts",
"type": "registry:lib",
"target": "components/softglass/lib/floating.ts"
},
{
"path": "packages/ui/src/lib/use-floating-portal.ts",
"type": "registry:lib",
"target": "components/softglass/lib/use-floating-portal.ts"
}
]
},
{
"name": "tooltip",
"type": "registry:ui",
Expand Down
55 changes: 55 additions & 0 deletions apps/web/public/r/time-picker.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/web/public/r/tokens.json

Large diffs are not rendered by default.

62 changes: 62 additions & 0 deletions apps/web/src/components/playground/library/pages/fields.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
import {
Checkbox,
Combobox,
Calendar,
DatePicker,
FileField,
Input,
Expand All @@ -31,6 +32,7 @@ import {
Slider,
Switch,
Textarea,
TimePicker,
type DatePickerMode,
type DatePickerSize,
type FieldLook,
Expand Down Expand Up @@ -386,6 +388,37 @@ export const fieldPlaygrounds: ComponentPlayground[] = [
),
],
},
{
id: "calendar",
title: "Calendar",
controls: [
enumControl("mode", ["single", "range"], "single"),
boolControl("disabledMin", false, "min bound demo"),
],
render: (p) => (
<div style={{ width: "100%", maxWidth: 320 }}>
<Calendar
mode={str(p, "mode") as DatePickerMode}
min={bool(p, "disabledMin") ? "2020-01-01" : undefined}
/>
</div>
),
showcases: [
showcase("Single", () => (
<div style={{ maxWidth: 300 }}>
<Calendar defaultValue="2026-07-15" />
</div>
)),
showcase("Range", () => (
<div style={{ maxWidth: 300 }}>
<Calendar
mode="range"
defaultRangeValue={{ start: "2026-07-10", end: "2026-07-14" }}
/>
</div>
)),
],
},
{
id: "date-picker",
title: "DatePicker",
Expand Down Expand Up @@ -423,6 +456,35 @@ export const fieldPlaygrounds: ComponentPlayground[] = [
),
],
},
{
id: "time-picker",
title: "TimePicker",
controls: [
enumControl("size", SIZES, "md"),
enumControl("look", ["soft", "solid", "outline", "glass"], "soft"),
enumControl("hourCycle", ["24", "12"], "24"),
textControl("label", "Start time"),
boolControl("disabled", false),
],
render: (p) => (
<TimePicker
size={str(p, "size") as DatePickerSize}
look={str(p, "look") as "soft" | "solid" | "outline" | "glass"}
hourCycle={str(p, "hourCycle") === "12" ? 12 : 24}
label={str(p, "label")}
disabled={bool(p, "disabled")}
motion="scale"
/>
),
showcases: [
showcase("Presets", () => (
<TimePicker label="Meeting" defaultValue="09:00" motion="none" />
)),
showcase("12h", () => (
<TimePicker label="Alarm" hourCycle={12} defaultValue="14:30" motion="none" />
)),
],
},
{
id: "slider",
title: "Slider",
Expand Down
88 changes: 87 additions & 1 deletion apps/web/src/lib/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,12 +423,58 @@ export const COMPONENT_DOCS: ComponentDoc[] = [
},
],
},
{
id: "calendar",
name: "Calendar",
layer: "atom",
summary:
"Inline day/month/year grids. Single or range ISO dates — shared by DatePicker.",
importLine: 'import { Calendar } from "@softglass/ui";',
example: `<Calendar
value={date}
onValueChange={setDate}
/>
{/* range */}
<Calendar
mode="range"
rangeValue={range}
onRangeValueChange={setRange}
/>`,
props: [
{
name: "mode",
type: '"single" | "range"',
default: '"single"',
description: "Single day or start/end range.",
},
{
name: "value / defaultValue",
type: "string (YYYY-MM-DD)",
description: "Single mode ISO date.",
},
{
name: "rangeValue / defaultRangeValue",
type: "{ start: string; end: string }",
description: "Range mode ISO start/end.",
},
{
name: "min / max",
type: "string (YYYY-MM-DD)",
description: "Inclusive bounds.",
},
{
name: "onSelectComplete",
type: "() => void",
description: "Fires when a full selection is done (DatePicker closes).",
},
],
},
{
id: "date-picker",
name: "DatePicker",
layer: "atom",
summary:
"Single or range ISO dates. Day/month/year grids. Body portal + flip/clamp.",
"Trigger + portaled Calendar. Single or range ISO dates. Flip/clamp panel.",
importLine: 'import { DatePicker } from "@softglass/ui";',
example: `<DatePicker
label="Launch date"
Expand Down Expand Up @@ -493,6 +539,46 @@ export const COMPONENT_DOCS: ComponentDoc[] = [
},
],
},
{
id: "time-picker",
name: "TimePicker",
layer: "atom",
summary:
"HH:mm trigger + panel with TimeInput, presets, and Apply. Value always 24h.",
importLine: 'import { TimePicker } from "@softglass/ui";',
example: `<TimePicker
label="Start"
value={time}
onValueChange={setTime}
hourCycle={24}
presets={["09:00", "12:00", "17:00"]}
/>`,
props: [
{
name: "value / defaultValue",
type: "string (HH:mm)",
description: "24h clock string.",
},
{
name: "presets",
type: "string[]",
default: '["09:00","12:00","13:00","17:00","18:00"]',
description: "Quick pick chips in the panel.",
},
{
name: "hourCycle",
type: "24 | 12",
default: "24",
description: "Display cycle; stored value stays HH:mm 24h.",
},
{
name: "minuteStep",
type: "number",
default: "5",
description: "Stepper step for minutes.",
},
],
},
{
id: "switch",
name: "Switch",
Expand Down
9 changes: 5 additions & 4 deletions docs/LIMITATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ Honest scope notes for Softglass. Read this before treating the kit like Radix /
| **Select** | Custom listbox-ish menu, keyboard, placement auto, **body portal + flip/clamp**, **enter/exit** | Virtualization for 500+ options; native form autofill parity |
| **Combobox** | Type-to-filter; option-only; body portal; **`onSearch` + `loading` async skeleton** | Creatable free-form; full data layer / cancellation |
| **MultiSelect** | Multi chips; body portal; **filter-in-menu** (default on) | Select-all; paste list |
| **DatePicker** | Single **or range** ISO `YYYY-MM-DD`; day/month/year grids; **body portal + flip/clamp**; min/max; range span highlight. Range: first click is draft start; `onRangeValueChange` fires when **both** ends chosen | Dual-month calendar; partial range callback; time; locale packs / full i18n; typing into field |
| **Calendar** | Inline day/month/year; single **or range** ISO `YYYY-MM-DD`; min/max | Dual-month; locale packs / full i18n |
| **DatePicker** | Trigger + portal over **Calendar**; flip/clamp; range draft → complete | Dual-month; typing into field; partial range callback |
| **TimePicker** | Trigger + panel with TimeInput, presets, Apply (HH:mm 24h value) | Seconds; timezone; ranges |
| **DropdownMenu** | Items API, keyboard, Escape + outside, `Button` merge, **body portal + collision**, **enter/exit** | Submenus; checkbox/radio items |
| **ContextMenu** | Same `items` language; right-click + long-press; fixed at pointer; Escape + outside; **enter/exit** | Nested submenu tree; full OS parity; Floating UI-level collision |
| **Popover** | Anchored panel, Escape + outside, non-modal, **body portal + collision**, **enter/exit** | Focus trap option; arrow |
Expand Down Expand Up @@ -60,9 +62,8 @@ Honest scope notes for Softglass. Read this before treating the kit like Radix /
| **Chip** | Select / remove / filter / check | Multi-select store; avatar chips |
| **PasswordInput** | Show/hide | Strength meter; generator |
| **SearchInput** | Free text + clear | Command palette; async suggestions |
| **TimeInput** | Custom HH:mm Softglass UI | Timezone / seconds / range |
| **NativeDateInput** | Custom day/month/year steppers (YYYY-MM-DD) | Full calendar suite (use DatePicker) |
| **DatePicker** | Day/month/year grids; body portal; **range mode** | Dual-month UI; locale packs; Calendar extract |
| **TimeInput** | Custom HH:mm Softglass UI | Timezone / seconds / range (use TimePicker for popover) |
| **NativeDateInput** | Custom day/month/year steppers (YYYY-MM-DD) | Full calendar suite (use Calendar / DatePicker) |
| **Rating** | SVG stars + shell | Half-stars; custom icon sets |
| **Glass surfaces** | Frost-first (cheap); optional blur | Guaranteed WCAG on every glass+text combo |

Expand Down
9 changes: 7 additions & 2 deletions docs/claude/todos.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,17 @@

---

## v1.9 calendar (this branch)

- [x] Extract **`Calendar`** from DatePicker
- [x] **`TimePicker`** suite (presets + panel)
- [x] Studio + registry + tests

## v1.9+ backlog (kalan)

- [ ] Token JSON / Figma bridge
- [ ] i18n / RTL
- [ ] Full calendar / TimePicker suite
- [ ] DatePicker Calendar extract
- [ ] Dual-month range calendar / full suite polish
- [ ] (optional) expand visual baselines / mobile project

---
Expand Down
Loading
Loading