@heyramzi/ui 2.12.0 → 2.14.0
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/dist/charts/index.js +1 -0
- package/dist/charts/smoothing.js +94 -0
- package/dist/components/chip/index.js +1 -0
- package/dist/components/work-parts/index.js +6 -0
- package/dist/components/work-parts/looks.js +53 -0
- package/package.json +1 -1
- package/src/charts/area-chart.svelte +41 -6
- package/src/charts/index.ts +8 -0
- package/src/charts/smoothing.ts +115 -0
- package/src/charts/sparkline.svelte +25 -3
- package/src/components/chip/index.ts +1 -0
- package/src/components/chip/tag-chip.svelte +63 -0
- package/src/components/work-parts/count-chip.svelte +20 -0
- package/src/components/work-parts/group-head.svelte +48 -0
- package/src/components/work-parts/index.ts +6 -0
- package/src/components/work-parts/looks.ts +55 -0
- package/src/components/work-parts/property-row.svelte +44 -0
- package/src/components/work-parts/work-card.svelte +34 -0
- package/src/components/work-parts/work-well.svelte +36 -0
- package/src/work/README.md +3 -3
- package/src/work/kanban-board.svelte +298 -126
- package/src/work/kanban-types.ts +3 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shapes the work organisms (kanban, task list, gantt) put on wrappers they don't render
|
|
3
|
+
* themselves: a held card's hole, the focus ring around a consumer's card or row. The organism
|
|
4
|
+
* picks the role, this file owns the radius, so a token edit moves every view at once.
|
|
5
|
+
*/
|
|
6
|
+
export const workLooks = {
|
|
7
|
+
/** A card-sized hole: a drop slot, the held card's own place, an empty column. */
|
|
8
|
+
hole: "rounded-xl bg-foreground/[0.03] outline-1 outline-dashed outline-border -outline-offset-1",
|
|
9
|
+
/** The wrapper around a consumer's card, so its focus ring follows the card's corners. */
|
|
10
|
+
card: "rounded-xl",
|
|
11
|
+
/** A list row or a rail entry: control radius. */
|
|
12
|
+
row: "rounded-lg",
|
|
13
|
+
/** A one-line row: 32px, 44px on touch (DESIGN.md, Patterns: list rows). The snippet stretches to fill it; a two-line row grows. */
|
|
14
|
+
rowHeight: "grid min-h-8 any-pointer-coarse:min-h-11",
|
|
15
|
+
/** A row-sized hole: an empty list group. */
|
|
16
|
+
rowHole: "rounded-lg outline-1 outline-dashed outline-border -outline-offset-1",
|
|
17
|
+
/** A link wrapped round a chip: the focus ring takes the chip's corners. */
|
|
18
|
+
chipLink: "rounded-sm outline-none focus-visible:shadow-(--ring-focus)",
|
|
19
|
+
/** A quiet grey box inside a card: a command, a quote. Chip radius, no edge. */
|
|
20
|
+
inset: "rounded-sm bg-muted/50",
|
|
21
|
+
/** A surface that holds a whole list: the card step, whose shadow draws its own hairline. */
|
|
22
|
+
panel: "rounded-xl bg-card text-card-foreground shadow-card",
|
|
23
|
+
} as const;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The gantt's marks. A bar is a control-sized shape, a mark inside a row is chip-sized, and
|
|
27
|
+
* the two round ones are a progress track and today's date number, which DESIGN rule 15
|
|
28
|
+
* allows as a count.
|
|
29
|
+
*/
|
|
30
|
+
export const ganttLooks = {
|
|
31
|
+
bar: "rounded-lg",
|
|
32
|
+
mark: "rounded-sm",
|
|
33
|
+
/** The readout that follows a dragged bar. */
|
|
34
|
+
tip: "rounded-sm bg-foreground px-2 py-1 text-[11px] font-medium whitespace-nowrap text-background tabular-nums shadow-raised",
|
|
35
|
+
/** A title that didn't fit its bar, set beside it. */
|
|
36
|
+
outsideLabel: "rounded-sm bg-card/90 px-1 text-xs text-muted-foreground",
|
|
37
|
+
track: "rounded-full",
|
|
38
|
+
day: "rounded-full",
|
|
39
|
+
} as const;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The doc pages' shapes. Their icon buttons and history button are controls, so they take the
|
|
43
|
+
* Button atom's radius; the edited chip is a chip; the page's icon tile and the demo's frame are
|
|
44
|
+
* card-sized.
|
|
45
|
+
*/
|
|
46
|
+
export const docLooks = {
|
|
47
|
+
/** A 28px icon button or a full-width row button: control radius, as `Button`. */
|
|
48
|
+
control: "rounded-lg",
|
|
49
|
+
/** The "Edited" chip that opens the page's history. */
|
|
50
|
+
chip: "rounded-sm",
|
|
51
|
+
/** The icon tile above a page title. */
|
|
52
|
+
tile: "rounded-xl",
|
|
53
|
+
/** The frame a demo draws round a whole doc page. */
|
|
54
|
+
frame: "rounded-xl",
|
|
55
|
+
} as const;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Component, Snippet } from "svelte";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
label: string;
|
|
7
|
+
/** A Phosphor icon before the label. */
|
|
8
|
+
icon?: Component<{ class?: string; "aria-hidden"?: boolean | "true" | "false" }>;
|
|
9
|
+
/**
|
|
10
|
+
* `responsive`: the label hides in a narrow container (a chip row) and shows as a column
|
|
11
|
+
* from `@3xl` up. `row`: always icon, label, value.
|
|
12
|
+
*/
|
|
13
|
+
layout?: "row" | "responsive";
|
|
14
|
+
children: Snippet;
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let { label, icon: Icon, layout = "row", children, class: className }: Props = $props();
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<!-- One property: icon and label on the left in muted text, the value on the right. -->
|
|
22
|
+
<div
|
|
23
|
+
role="group"
|
|
24
|
+
aria-label={label}
|
|
25
|
+
class={cn(
|
|
26
|
+
"min-h-8 items-center gap-2",
|
|
27
|
+
layout === "row"
|
|
28
|
+
? "grid grid-cols-[minmax(0,7rem)_minmax(0,1fr)]"
|
|
29
|
+
: "flex @3xl:grid @3xl:grid-cols-[7rem_minmax(0,1fr)]",
|
|
30
|
+
className,
|
|
31
|
+
)}
|
|
32
|
+
>
|
|
33
|
+
<span
|
|
34
|
+
class={cn(
|
|
35
|
+
"min-w-0 items-center gap-2 text-xs font-medium text-muted-foreground",
|
|
36
|
+
layout === "row" ? "flex" : "hidden @3xl:flex",
|
|
37
|
+
)}
|
|
38
|
+
aria-hidden="true"
|
|
39
|
+
>
|
|
40
|
+
{#if Icon}<Icon class="size-4 shrink-0" aria-hidden="true" />{/if}
|
|
41
|
+
<span class="truncate">{label}</span>
|
|
42
|
+
</span>
|
|
43
|
+
<div class="flex min-w-0 items-center">{@render children()}</div>
|
|
44
|
+
</div>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
import { cn } from "../../lib/utils.js";
|
|
5
|
+
import { Card } from "../../shadcn/card/index.js";
|
|
6
|
+
|
|
7
|
+
interface Props extends HTMLAttributes<HTMLElement> {
|
|
8
|
+
/** The bottom row: dates, subtasks, time, avatars. */
|
|
9
|
+
meta?: Snippet;
|
|
10
|
+
children: Snippet;
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let { meta, children, class: className, ...rest }: Props = $props();
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<!-- A white card on a grey well: the title, then a quiet meta row. Depth comes from the card
|
|
18
|
+
step and rises one step on hover, so a held card has somewhere to go. -->
|
|
19
|
+
<Card
|
|
20
|
+
{...rest}
|
|
21
|
+
class={cn(
|
|
22
|
+
"gap-2.5 px-3 py-2.5 transition-shadow duration-quick ease-smooth-out hover:shadow-card-hover",
|
|
23
|
+
className,
|
|
24
|
+
)}
|
|
25
|
+
>
|
|
26
|
+
<div class="text-sm leading-snug text-pretty">{@render children()}</div>
|
|
27
|
+
{#if meta}
|
|
28
|
+
<div
|
|
29
|
+
class="flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-xs whitespace-nowrap text-muted-foreground tabular-nums"
|
|
30
|
+
>
|
|
31
|
+
{@render meta()}
|
|
32
|
+
</div>
|
|
33
|
+
{/if}
|
|
34
|
+
</Card>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
import { cn } from "../../lib/utils.js";
|
|
5
|
+
|
|
6
|
+
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
/** Any CSS colour. Tints the well only while something is held over it. */
|
|
8
|
+
hue?: string;
|
|
9
|
+
/** Something is held over the well. */
|
|
10
|
+
active?: boolean;
|
|
11
|
+
children: Snippet;
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let { hue, active = false, children, class: className, ...rest }: Props = $props();
|
|
16
|
+
|
|
17
|
+
// A well rests grey. Its hue shows only while a card is held over it.
|
|
18
|
+
let style = $derived(
|
|
19
|
+
hue && active
|
|
20
|
+
? `background: color-mix(in oklch, ${hue} 8%, var(--color-muted)); outline-color: color-mix(in oklch, ${hue} 30%, transparent);`
|
|
21
|
+
: undefined,
|
|
22
|
+
);
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<!-- The grey well white cards sit in. Card radius plus its padding takes the next step up:
|
|
26
|
+
radius-xl cards, 6px in, a radius-2xl well (DESIGN rule 14). -->
|
|
27
|
+
<div
|
|
28
|
+
{...rest}
|
|
29
|
+
class={cn(
|
|
30
|
+
"flex min-h-0 flex-col gap-1.5 overflow-x-hidden overflow-y-auto rounded-2xl bg-muted/70 p-1.5 outline-1 outline-transparent -outline-offset-1 transition-[background-color,outline-color] duration-quick ease-smooth-out dark:bg-muted/60",
|
|
31
|
+
className,
|
|
32
|
+
)}
|
|
33
|
+
{style}
|
|
34
|
+
>
|
|
35
|
+
{@render children()}
|
|
36
|
+
</div>
|
package/src/work/README.md
CHANGED
|
@@ -81,15 +81,15 @@ isn't a drop target (an Overdue lane, say).
|
|
|
81
81
|
| `manualTiebreak` | `(a: T, b: T) => number` | Settles two cards with the same manual key, or none. |
|
|
82
82
|
| `getLabel` | `(item: T) => string` | The screen-reader name of a card. Defaults to a keyboard hint. |
|
|
83
83
|
| `onSelect` | `(id: string) => void` | Enter or Space on a focused card. |
|
|
84
|
-
| `columnFooter` | `Snippet<[Column, KanbanColumnContext]>` | Under a column's cards
|
|
84
|
+
| `columnFooter` | `Snippet<[Column, KanbanColumnContext]>` | Under a column's cards and its fold button, e.g. an add button. |
|
|
85
85
|
| `wrapColumn` | `Snippet<[Column, Snippet<[props]>]>` | Wraps a whole column, for a context menu. Render the inner snippet once with the props you are handed. |
|
|
86
|
-
| `limit` | `(columnId: string) => number \| undefined`
|
|
86
|
+
| `limit` | `number \| ((columnId: string) => number \| undefined)` | Cards a column shows before the rest fold, 5 by default; `Infinity` for none. A "Show N more" button opens the fold and "Show less" shuts it, both animated. `moreLabel`, `lessLabel`, `expanded` and `onExpandedChange` go with it. A card moved into a shut column stays in view. |
|
|
87
87
|
| `hidden` | `string[]` | Columns folded into a rail on the right. Fold and unfold animate, and a card dropped on a rail entry brings the column back. `onHiddenChange`, `hiddenLabel`, `showAllLabel` and `hiddenItem` go with it. |
|
|
88
88
|
|
|
89
89
|
`columns` can be your own type, as long as it extends `KanbanColumn`: the
|
|
90
90
|
header, footer and wrap snippets get it back as you passed it, so a column can
|
|
91
91
|
carry a status or a stage for your own markup. The header and footer context is
|
|
92
|
-
`{ count, active, shown, capped, hide, expand }`.
|
|
92
|
+
`{ count, active, shown, capped, hide, expand, collapse }`.
|
|
93
93
|
|
|
94
94
|
`onMove` gives `{ columnId, boardOrder?, beforeId?, afterId? }`. On a manual
|
|
95
95
|
drop `beforeId` and `afterId` name the cards above and below the new slot (null
|