@chesapeake/stack 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to
7
7
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
8
 
9
+ ## [0.1.6] - 2026-09-19
10
+
11
+ ### Added
12
+
13
+ - Neutral `FocusCard`, `FocusFeed`, `TodoCard`, and `EventCard` components for
14
+ mixed todo and calendar-style feeds
15
+ - Public focus component subpath exports
16
+
9
17
  ## [0.1.5] - 2026-08-03
10
18
 
11
19
  ### Added
@@ -83,6 +91,7 @@ and this project adheres to
83
91
  - Apache 2.0 license
84
92
 
85
93
  [0.1.4]: https://github.com/chesapeakedev/stack/releases/tag/v0.1.4
94
+ [0.1.6]: https://github.com/chesapeakedev/stack/releases/tag/v0.1.6
86
95
  [0.1.3]: https://github.com/chesapeakedev/stack/releases/tag/v0.1.3
87
96
  [0.1.2]: https://github.com/chesapeakedev/stack/releases/tag/v0.1.2
88
97
  [0.1.1]: https://github.com/chesapeakedev/stack/releases/tag/v0.1.1
@@ -0,0 +1,48 @@
1
+ // Copyright 2026 Chesapeake Computing
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { ReactNode } from "react";
5
+ import { FocusCard } from "./FocusCard.tsx";
6
+
7
+ export interface EventCardProps {
8
+ title: string;
9
+ time: ReactNode;
10
+ location?: ReactNode;
11
+ metadata?: ReactNode;
12
+ actions?: ReactNode;
13
+ allDay?: boolean;
14
+ className?: string;
15
+ }
16
+
17
+ /** Read-only calendar event presentation; consumers own calendar mutations. */
18
+ export function EventCard({
19
+ title,
20
+ time,
21
+ location,
22
+ metadata,
23
+ actions,
24
+ allDay = false,
25
+ className,
26
+ }: EventCardProps) {
27
+ return (
28
+ <FocusCard
29
+ className={className}
30
+ leading={
31
+ <div
32
+ aria-hidden="true"
33
+ className="mt-1 h-5 w-5 shrink-0 rounded-full border-2 border-primary/60"
34
+ />
35
+ }
36
+ actions={actions}
37
+ >
38
+ <div className="space-y-1">
39
+ <div className="font-medium">{title}</div>
40
+ <div className="text-sm text-muted-foreground">
41
+ {allDay ? "All day" : time}
42
+ </div>
43
+ {location}
44
+ {metadata}
45
+ </div>
46
+ </FocusCard>
47
+ );
48
+ }
@@ -0,0 +1,37 @@
1
+ // Copyright 2026 Chesapeake Computing
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { ReactNode } from "react";
5
+ import { Card } from "../../ui/card.tsx";
6
+
7
+ export interface FocusCardProps {
8
+ children: ReactNode;
9
+ leading?: ReactNode;
10
+ actions?: ReactNode;
11
+ completed?: boolean;
12
+ className?: string;
13
+ }
14
+
15
+ /** Shared surface and layout for every item in a focus feed. */
16
+ export function FocusCard({
17
+ children,
18
+ leading,
19
+ actions,
20
+ completed = false,
21
+ className,
22
+ }: FocusCardProps) {
23
+ return (
24
+ <Card
25
+ role="listitem"
26
+ className={`bg-background min-w-0 p-4 transition-shadow hover:shadow-md ${
27
+ completed ? "bg-muted/50 opacity-75" : ""
28
+ } ${className ?? ""}`}
29
+ >
30
+ <div className="flex min-w-0 items-start gap-3">
31
+ {leading}
32
+ <div className="min-w-0 flex-1">{children}</div>
33
+ {actions}
34
+ </div>
35
+ </Card>
36
+ );
37
+ }
@@ -0,0 +1,39 @@
1
+ // Copyright 2026 Chesapeake Computing
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { ReactNode } from "react";
5
+
6
+ export interface FocusFeedProps {
7
+ children: ReactNode;
8
+ loading?: boolean;
9
+ empty?: ReactNode;
10
+ className?: string;
11
+ label?: string;
12
+ }
13
+
14
+ /** A narrow, accessible feed surface for mixed todo and event cards. */
15
+ export function FocusFeed({
16
+ children,
17
+ loading = false,
18
+ empty,
19
+ className,
20
+ label = "Focus feed",
21
+ }: FocusFeedProps) {
22
+ const content = loading ? (
23
+ <div className="text-muted-foreground p-8 text-center" role="status">
24
+ Loading feed...
25
+ </div>
26
+ ) : (
27
+ (empty ?? children)
28
+ );
29
+
30
+ return (
31
+ <div
32
+ aria-label={label}
33
+ className={`flex min-h-0 flex-1 flex-col gap-3 overflow-y-auto p-4 ${className ?? ""}`}
34
+ role="list"
35
+ >
36
+ {content}
37
+ </div>
38
+ );
39
+ }
@@ -0,0 +1,88 @@
1
+ // Copyright 2026 Chesapeake Computing
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { useState } from "react";
5
+ import type { ReactNode } from "react";
6
+ import { Input } from "../../ui/input.tsx";
7
+ import { FocusCard } from "./FocusCard.tsx";
8
+
9
+ export interface TodoCardProps {
10
+ title: string;
11
+ completed: boolean;
12
+ onToggle: () => void;
13
+ onTitleChange?: (title: string) => void;
14
+ metadata?: ReactNode;
15
+ description?: ReactNode;
16
+ actions?: ReactNode;
17
+ className?: string;
18
+ }
19
+
20
+ export function TodoCard({
21
+ title,
22
+ completed,
23
+ onToggle,
24
+ onTitleChange,
25
+ metadata,
26
+ description,
27
+ actions,
28
+ className,
29
+ }: TodoCardProps) {
30
+ const [editing, setEditing] = useState(false);
31
+ const [draft, setDraft] = useState(title);
32
+
33
+ const save = () => {
34
+ const next = draft.trim();
35
+ if (next && next !== title) onTitleChange?.(next);
36
+ setEditing(false);
37
+ };
38
+
39
+ return (
40
+ <FocusCard
41
+ completed={completed}
42
+ className={className}
43
+ leading={
44
+ <input
45
+ aria-label={completed ? "Mark incomplete" : "Mark complete"}
46
+ checked={completed}
47
+ className="mt-1 h-5 w-5 shrink-0 cursor-pointer rounded text-primary focus:ring-2 focus:ring-primary"
48
+ onChange={onToggle}
49
+ type="checkbox"
50
+ />
51
+ }
52
+ actions={actions}
53
+ >
54
+ <div className="space-y-1">
55
+ {editing ? (
56
+ <Input
57
+ aria-label="Edit todo title"
58
+ autoFocus
59
+ onBlur={save}
60
+ onChange={(event) => {
61
+ setDraft(event.target.value);
62
+ }}
63
+ onKeyDown={(event) => {
64
+ if (event.key === "Enter") save();
65
+ if (event.key === "Escape") setEditing(false);
66
+ }}
67
+ value={draft}
68
+ />
69
+ ) : (
70
+ <button
71
+ className={`block w-full rounded-sm text-left font-medium hover:text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${
72
+ completed ? "text-muted-foreground line-through" : ""
73
+ }`}
74
+ onClick={() => {
75
+ setDraft(title);
76
+ if (onTitleChange) setEditing(true);
77
+ }}
78
+ type="button"
79
+ >
80
+ {title}
81
+ </button>
82
+ )}
83
+ {metadata}
84
+ {description}
85
+ </div>
86
+ </FocusCard>
87
+ );
88
+ }
@@ -0,0 +1,8 @@
1
+ export { EventCard } from "./EventCard.tsx";
2
+ export type { EventCardProps } from "./EventCard.tsx";
3
+ export { FocusCard } from "./FocusCard.tsx";
4
+ export type { FocusCardProps } from "./FocusCard.tsx";
5
+ export { FocusFeed } from "./FocusFeed.tsx";
6
+ export type { FocusFeedProps } from "./FocusFeed.tsx";
7
+ export { TodoCard } from "./TodoCard.tsx";
8
+ export type { TodoCardProps } from "./TodoCard.tsx";
@@ -46,5 +46,6 @@ export type {
46
46
  } from "./StandardLayout/index.ts";
47
47
 
48
48
  export * from "./task/index.ts";
49
+ export * from "./focus/index.ts";
49
50
  export * from "./connectivity/index.ts";
50
51
  export * from "./runner/index.ts";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chesapeake/stack",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "A simple, reusable React component library and design system built on shadcn/ui with Tailwind CSS",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.2.0",
@@ -111,6 +111,11 @@
111
111
  "./components/ux/task/TaskInput": "./components/ux/task/TaskInput.tsx",
112
112
  "./components/ux/task/TaskCard": "./components/ux/task/TaskCard.tsx",
113
113
  "./components/ux/task/TaskList": "./components/ux/task/TaskList.tsx",
114
+ "./components/ux/focus": "./components/ux/focus/index.ts",
115
+ "./components/ux/focus/FocusCard": "./components/ux/focus/FocusCard.tsx",
116
+ "./components/ux/focus/FocusFeed": "./components/ux/focus/FocusFeed.tsx",
117
+ "./components/ux/focus/TodoCard": "./components/ux/focus/TodoCard.tsx",
118
+ "./components/ux/focus/EventCard": "./components/ux/focus/EventCard.tsx",
114
119
  "./components/ux/connectivity": "./components/ux/connectivity/index.ts",
115
120
  "./components/ux/connectivity/ConnectivityBadge": "./components/ux/connectivity/ConnectivityBadge.tsx",
116
121
  "./components/ux/runner": "./components/ux/runner/index.ts",