@sableui/react 0.17.0 → 0.19.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/AGENTS.md +10 -3
- package/CHANGELOG.md +235 -0
- package/README.md +11 -4
- package/bin/sable.mjs +266 -30
- package/dist/{_contract-CDa9-NdB.d.ts → _contract-DlklV0Im.d.ts} +2 -2
- package/dist/activity-DT8VK9fx.d.ts +25 -0
- package/dist/adapters/github.d.ts +41 -0
- package/dist/adapters/github.js +24 -0
- package/dist/blocks/index.d.ts +1 -1
- package/dist/components/activity-heatmap/activity-heatmap.js +420 -0
- package/dist/components/activity-heatmap/activity-heatmap.meta.js +142 -0
- package/dist/components/activity-heatmap/index.js +9 -0
- package/dist/components/avatar/avatar.js +16 -7
- package/dist/components/avatar/avatar.meta.js +43 -10
- package/dist/components/avatar/avatar.variants.js +26 -2
- package/dist/components/code-block/code-block.js +5 -3
- package/dist/components/code-block/code-block.meta.js +6 -0
- package/dist/components/collapsible/collapsible.js +6 -5
- package/dist/components/collapsible/collapsible.meta.js +14 -5
- package/dist/components/data-table/data-table.js +132 -45
- package/dist/components/data-table/data-table.meta.js +63 -4
- package/dist/components/date-picker/date-picker.js +13 -6
- package/dist/components/date-picker/date-picker.meta.js +22 -0
- package/dist/components/dialog/dialog.js +25 -5
- package/dist/components/dialog/dialog.meta.js +26 -5
- package/dist/components/media-image/media-image.js +3 -1
- package/dist/components/media-image/media-image.meta.js +15 -1
- package/dist/components/media-tile/media-tile.js +13 -1
- package/dist/components/media-tile/media-tile.meta.js +10 -0
- package/dist/components/menu/menu.js +2 -0
- package/dist/components/menu/menu.meta.js +2 -2
- package/dist/components/page-header/page-header.js +9 -4
- package/dist/components/page-header/page-header.meta.js +26 -1
- package/dist/components/panel-group/panel-group.js +17 -1
- package/dist/components/panel-group/panel-group.meta.js +43 -3
- package/dist/components/pill/pill.meta.js +15 -6
- package/dist/components/pill/pill.variants.js +1 -0
- package/dist/components/search-input/search-input.js +30 -5
- package/dist/components/search-input/search-input.meta.js +29 -4
- package/dist/components/section-header/section-header.js +27 -17
- package/dist/components/section-header/section-header.meta.js +27 -5
- package/dist/components/sheet/sheet.js +15 -3
- package/dist/components/sheet/sheet.meta.js +2 -2
- package/dist/components/side-nav/side-nav.variants.js +8 -3
- package/dist/components/sidebar/index.js +4 -0
- package/dist/components/sidebar/sidebar.js +90 -11
- package/dist/components/sidebar/sidebar.meta.js +38 -4
- package/dist/components/table/table.js +7 -3
- package/dist/components/tabs/tabs.js +10 -2
- package/dist/components/tabs/tabs.meta.js +4 -0
- package/dist/components/workspace/workspace.js +13 -3
- package/dist/components/workspace/workspace.meta.js +109 -1
- package/dist/file-hashes.json +94 -80
- package/dist/foundations/typography.js +13 -0
- package/dist/icon/Icon.js +21 -2
- package/dist/icon/index.d.ts +36 -1
- package/dist/icon/index.js +5 -0
- package/dist/icon/names.js +240 -0
- package/dist/icon-sets/reicon.js +41 -1
- package/dist/index.d.ts +540 -75
- package/dist/index.js +18 -1
- package/dist/layout/AspectBox.js +2 -5
- package/dist/layout/Bleed.js +10 -6
- package/dist/layout/Center.js +11 -3
- package/dist/layout/Columns.js +6 -3
- package/dist/layout/Grid.js +14 -6
- package/dist/layout/GridOverlay.js +7 -7
- package/dist/layout/Region.js +18 -3
- package/dist/layout/Row.js +27 -20
- package/dist/layout/Spacer.js +13 -2
- package/dist/layout/SplitPane.js +1 -1
- package/dist/layout/Stack.js +25 -18
- package/dist/layout/index.d.ts +57 -6
- package/dist/lib/chart/activity.js +32 -0
- package/dist/lib/chart/calendar.js +67 -0
- package/dist/lib/chart/chart-tooltip.js +2 -2
- package/dist/lib/chart/heat.js +55 -0
- package/dist/lib/cn.js +1 -0
- package/dist/lib/focus-return.js +53 -0
- package/dist/lib/taxonomy.js +1 -0
- package/dist/lib/use-media-query.js +41 -0
- package/dist/{meta-DX5r5wQL.d.ts → meta-Dfm9AExe.d.ts} +6 -0
- package/dist/patterns/dashboard-page.js +23 -17
- package/dist/patterns/index.d.ts +7 -2
- package/dist/patterns/index.js +5 -1
- package/dist/patterns/tracker-page.js +392 -0
- package/dist/patterns/tracker-page.meta.js +40 -0
- package/dist/presets/index.d.ts +1 -1
- package/dist/styles.css +301 -188
- package/dist/{theme-provider-BydfM5Z0.d.ts → theme-provider-BUBealk_.d.ts} +1 -1
- package/dist/themes/_contract.js +9 -1
- package/dist/themes/atelier/index.d.ts +1 -1
- package/dist/themes/ember/index.d.ts +1 -1
- package/dist/themes/index.d.ts +2 -2
- package/dist/themes/meridian/index.d.ts +1 -1
- package/dist/themes/poppy/index.d.ts +1 -1
- package/dist/themes/prism/index.d.ts +1 -1
- package/dist/themes/sable/index.d.ts +1 -1
- package/dist/themes/verdant/index.d.ts +1 -1
- package/llms/components/activity-heatmap.md +133 -0
- package/llms/components/avatar.md +32 -9
- package/llms/components/code-block.md +1 -0
- package/llms/components/collapsible.md +12 -4
- package/llms/components/data-table.md +40 -3
- package/llms/components/date-picker.md +15 -1
- package/llms/components/dialog.md +34 -3
- package/llms/components/media-image.md +12 -0
- package/llms/components/media-tile.md +21 -0
- package/llms/components/menu.md +1 -1
- package/llms/components/page-header.md +16 -1
- package/llms/components/panel-group.md +34 -2
- package/llms/components/pill.md +24 -5
- package/llms/components/search-input.md +37 -4
- package/llms/components/section-header.md +28 -4
- package/llms/components/sheet.md +2 -2
- package/llms/components/sidebar.md +17 -4
- package/llms/components/tabs.md +1 -0
- package/llms/components/workspace.md +81 -1
- package/llms/foundations/typography.md +4 -1
- package/llms/layout/aspect-box.md +1 -1
- package/llms/layout/bleed.md +1 -1
- package/llms/layout/center.md +1 -1
- package/llms/layout/columns.md +1 -1
- package/llms/layout/grid.md +1 -1
- package/llms/layout/region.md +1 -1
- package/llms/layout/row.md +9 -1
- package/llms/layout/spacer.md +1 -1
- package/llms/layout/split-pane.md +1 -1
- package/llms/layout/stack.md +1 -1
- package/llms/patterns/dashboard-page.md +18 -15
- package/llms/patterns/tracker-page.md +450 -0
- package/llms/reference/token-intent.md +3 -1
- package/llms/reference/utilities.md +4 -1
- package/llms/tasks/auditing-an-app.md +35 -1
- package/llms/tasks/building-a-page.md +3 -2
- package/llms/tasks/building-a-tool.md +101 -5
- package/llms/tasks/building-with-sable.md +11 -3
- package/llms/tasks/creating-a-preset.md +9 -0
- package/llms/tasks/getting-started.md +10 -3
- package/llms/tasks/using-presets.md +1 -2
- package/llms-full.txt +1215 -89
- package/llms.txt +11 -9
- package/package.json +7 -1
- package/registry.json +1021 -70
- package/scripts/adoption.mjs +252 -0
- package/scripts/agent-setup.mjs +110 -18
- package/scripts/app-css.mjs +129 -0
- package/scripts/grade.mjs +178 -2
- package/scripts/lint-rules.mjs +126 -0
- package/scripts/wiring.mjs +2 -1
- package/skills/entry/SKILL.md +1 -1
- package/skills/sable/SKILL.md +64 -6
- package/skills/sable/agents/openai.yaml +1 -1
- package/targets/ios.json +9 -0
package/dist/index.js
CHANGED
|
@@ -82,6 +82,10 @@ export * from "./components/pie-chart/index.js";
|
|
|
82
82
|
export * from "./components/radar-chart/index.js";
|
|
83
83
|
export * from "./components/radial-chart/index.js";
|
|
84
84
|
export * from "./components/sparkline/index.js";
|
|
85
|
+
export * from "./components/activity-heatmap/index.js";
|
|
86
|
+
import { mergeActivity } from "./lib/chart/activity.js";
|
|
87
|
+
import { activityGrid } from "./lib/chart/calendar.js";
|
|
88
|
+
import { activityLevels } from "./lib/chart/heat.js";
|
|
85
89
|
import {
|
|
86
90
|
CHART_SERIES_COUNT,
|
|
87
91
|
chartColor,
|
|
@@ -174,6 +178,12 @@ export * from "./components/marker/index.js";
|
|
|
174
178
|
import { Density, DensityProvider, useDensity, useAnchoredDensity, useTypeTier } from "./lib/density.js";
|
|
175
179
|
import { PresentationProvider, usePresentation } from "./lib/presentation.js";
|
|
176
180
|
import { useScrollActivity } from "./lib/use-scroll-activity.js";
|
|
181
|
+
import {
|
|
182
|
+
belowBreakpointQuery,
|
|
183
|
+
useBelowBreakpoint,
|
|
184
|
+
useMediaQuery,
|
|
185
|
+
VIEWPORT_BREAKPOINTS
|
|
186
|
+
} from "./lib/use-media-query.js";
|
|
177
187
|
import { useMatchHighlight } from "./lib/use-match-highlight.js";
|
|
178
188
|
import { useCopy } from "./lib/use-copy.js";
|
|
179
189
|
import { useSmoothWheel } from "./lib/use-smooth-wheel.js";
|
|
@@ -183,7 +193,7 @@ import {
|
|
|
183
193
|
} from "./lib/use-caret-trigger.js";
|
|
184
194
|
import { STATUS_TONES, STATUS_TONE_ICON } from "./lib/status-tone.js";
|
|
185
195
|
import { PALETTE_COLORS, PALETTE_FAMILIES, PALETTE_SCALES, PALETTE_STEPS, PALETTE_TONES, isPaletteColor } from "./lib/palette.js";
|
|
186
|
-
var SABLE_UI_VERSION = "0.
|
|
196
|
+
var SABLE_UI_VERSION = "0.19.0";
|
|
187
197
|
export {
|
|
188
198
|
APP_SHELL_TOKENS,
|
|
189
199
|
ART_TOKENS,
|
|
@@ -219,9 +229,13 @@ export {
|
|
|
219
229
|
TYPE_REFINEMENT_TOKENS,
|
|
220
230
|
TYPE_SCALE_TOKENS,
|
|
221
231
|
TYPE_UI_TOKENS,
|
|
232
|
+
VIEWPORT_BREAKPOINTS,
|
|
222
233
|
WORKSPACE_SURFACE_TOKENS,
|
|
234
|
+
activityGrid,
|
|
235
|
+
activityLevels,
|
|
223
236
|
applySableColorMode,
|
|
224
237
|
applySableTheme,
|
|
238
|
+
belowBreakpointQuery,
|
|
225
239
|
chartColor,
|
|
226
240
|
toneColor as chartToneColor,
|
|
227
241
|
componentGuidance,
|
|
@@ -232,16 +246,19 @@ export {
|
|
|
232
246
|
getThemeManifest,
|
|
233
247
|
isPaletteColor,
|
|
234
248
|
isThemeId,
|
|
249
|
+
mergeActivity,
|
|
235
250
|
switchMeta,
|
|
236
251
|
themeIds,
|
|
237
252
|
themeManifestById,
|
|
238
253
|
themeManifests,
|
|
239
254
|
useActiveSection,
|
|
240
255
|
useAnchoredDensity,
|
|
256
|
+
useBelowBreakpoint,
|
|
241
257
|
useCaretTrigger,
|
|
242
258
|
useCopy,
|
|
243
259
|
useDensity,
|
|
244
260
|
useMatchHighlight,
|
|
261
|
+
useMediaQuery,
|
|
245
262
|
usePresentation,
|
|
246
263
|
useSableTheme,
|
|
247
264
|
useScrollActivity,
|
package/dist/layout/AspectBox.js
CHANGED
|
@@ -8,14 +8,11 @@ function AspectBox({
|
|
|
8
8
|
children,
|
|
9
9
|
...rest
|
|
10
10
|
}) {
|
|
11
|
-
const mergedStyle = {
|
|
12
|
-
aspectRatio: String(ratio),
|
|
13
|
-
...style
|
|
14
|
-
};
|
|
11
|
+
const mergedStyle = { "--sable-aspect": String(ratio), ...style };
|
|
15
12
|
return /* @__PURE__ */ jsx(
|
|
16
13
|
"div",
|
|
17
14
|
{
|
|
18
|
-
className: cn("relative w-full overflow-hidden", className),
|
|
15
|
+
className: cn("relative aspect-(--sable-aspect) w-full overflow-hidden", className),
|
|
19
16
|
style: mergedStyle,
|
|
20
17
|
...rest,
|
|
21
18
|
children
|
package/dist/layout/Bleed.js
CHANGED
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
function Bleed({ className, style, children, ref, ...rest }) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
return /* @__PURE__ */ jsx(
|
|
6
|
+
"div",
|
|
7
|
+
{
|
|
8
|
+
ref,
|
|
9
|
+
className: cn("mx-[calc(50%_-_50cqw)] w-[100cqw]", className),
|
|
10
|
+
style,
|
|
11
|
+
...rest,
|
|
12
|
+
children
|
|
13
|
+
}
|
|
14
|
+
);
|
|
11
15
|
}
|
|
12
16
|
Bleed.displayName = "Bleed";
|
|
13
17
|
export {
|
package/dist/layout/Center.js
CHANGED
|
@@ -10,11 +10,19 @@ var NAMED_MAX = {
|
|
|
10
10
|
function Center({ max, className, style, children, ref, ...rest }) {
|
|
11
11
|
const maxWidth = max ? NAMED_MAX[max] ?? max : void 0;
|
|
12
12
|
const mergedStyle = {
|
|
13
|
-
|
|
14
|
-
...maxWidth ? { maxWidth } : null,
|
|
13
|
+
...maxWidth ? { "--sable-center-max": maxWidth } : null,
|
|
15
14
|
...style
|
|
16
15
|
};
|
|
17
|
-
return /* @__PURE__ */ jsx(
|
|
16
|
+
return /* @__PURE__ */ jsx(
|
|
17
|
+
"div",
|
|
18
|
+
{
|
|
19
|
+
ref,
|
|
20
|
+
className: cn("mx-auto w-full", maxWidth && "max-w-(--sable-center-max)", className),
|
|
21
|
+
style: mergedStyle,
|
|
22
|
+
...rest,
|
|
23
|
+
children
|
|
24
|
+
}
|
|
25
|
+
);
|
|
18
26
|
}
|
|
19
27
|
export {
|
|
20
28
|
Center
|
package/dist/layout/Columns.js
CHANGED
|
@@ -21,8 +21,8 @@ function Columns({
|
|
|
21
21
|
}) {
|
|
22
22
|
const resolvedGap = resolveGap(gap);
|
|
23
23
|
const mergedStyle = {
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
"--sable-columns-row-gap": resolvedGap,
|
|
25
|
+
"--sable-columns-column-gap": cappedColumnGap(resolvedGap),
|
|
26
26
|
...responsiveVars("--cols", count),
|
|
27
27
|
...style
|
|
28
28
|
};
|
|
@@ -31,7 +31,10 @@ function Columns({
|
|
|
31
31
|
{
|
|
32
32
|
ref,
|
|
33
33
|
"data-sable-grid-cols": true,
|
|
34
|
-
className: cn(
|
|
34
|
+
className: cn(
|
|
35
|
+
"grid gap-x-(--sable-columns-column-gap) gap-y-(--sable-columns-row-gap)",
|
|
36
|
+
className
|
|
37
|
+
),
|
|
35
38
|
style: mergedStyle,
|
|
36
39
|
...rest,
|
|
37
40
|
children
|
package/dist/layout/Grid.js
CHANGED
|
@@ -21,15 +21,23 @@ function Grid({
|
|
|
21
21
|
}) {
|
|
22
22
|
if (minItemWidth) {
|
|
23
23
|
const mergedStyle2 = {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
gap: resolveGap(gap),
|
|
24
|
+
"--sable-grid-gap": resolveGap(gap),
|
|
25
|
+
"--sable-grid-template": `repeat(${track === "fit" ? "auto-fit" : "auto-fill"}, minmax(min(${minItemWidth}, 100%), 1fr))`,
|
|
27
26
|
...style
|
|
28
27
|
};
|
|
29
|
-
return /* @__PURE__ */ jsx(
|
|
28
|
+
return /* @__PURE__ */ jsx(
|
|
29
|
+
"div",
|
|
30
|
+
{
|
|
31
|
+
ref,
|
|
32
|
+
className: cn("grid grid-cols-(--sable-grid-template) gap-(--sable-grid-gap)", className),
|
|
33
|
+
style: mergedStyle2,
|
|
34
|
+
...rest,
|
|
35
|
+
children
|
|
36
|
+
}
|
|
37
|
+
);
|
|
30
38
|
}
|
|
31
39
|
const mergedStyle = {
|
|
32
|
-
gap: resolveGap(gap),
|
|
40
|
+
"--sable-grid-gap": resolveGap(gap),
|
|
33
41
|
...responsiveVars("--cols", cols),
|
|
34
42
|
...style
|
|
35
43
|
};
|
|
@@ -38,7 +46,7 @@ function Grid({
|
|
|
38
46
|
{
|
|
39
47
|
ref,
|
|
40
48
|
"data-sable-grid-cols": true,
|
|
41
|
-
className: cn(className),
|
|
49
|
+
className: cn("grid gap-(--sable-grid-gap)", className),
|
|
42
50
|
style: mergedStyle,
|
|
43
51
|
...rest,
|
|
44
52
|
children
|
|
@@ -10,18 +10,18 @@ function GridOverlay({
|
|
|
10
10
|
}) {
|
|
11
11
|
if (!show) return null;
|
|
12
12
|
const style = {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
display: "grid",
|
|
16
|
-
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
|
|
17
|
-
gap,
|
|
18
|
-
paddingInline: inset ? "var(--page-inset)" : void 0
|
|
13
|
+
"--sable-overlay-cols": `repeat(${columns}, minmax(0, 1fr))`,
|
|
14
|
+
"--sable-overlay-gap": gap
|
|
19
15
|
};
|
|
20
16
|
return /* @__PURE__ */ jsx(
|
|
21
17
|
"div",
|
|
22
18
|
{
|
|
23
19
|
"aria-hidden": true,
|
|
24
|
-
className: cn(
|
|
20
|
+
className: cn(
|
|
21
|
+
"pointer-events-none absolute inset-0 z-(--z-overlay) grid grid-cols-(--sable-overlay-cols) gap-(--sable-overlay-gap)",
|
|
22
|
+
inset && "px-(--page-inset)",
|
|
23
|
+
className
|
|
24
|
+
),
|
|
25
25
|
style,
|
|
26
26
|
children: Array.from({ length: columns }, (_, i) => /* @__PURE__ */ jsx(
|
|
27
27
|
"div",
|
package/dist/layout/Region.js
CHANGED
|
@@ -27,12 +27,27 @@ function Region({
|
|
|
27
27
|
const maxWidth = max ? NAMED_MAX[max] ?? max : void 0;
|
|
28
28
|
const safeEdges = safeArea === true ? ["top", "bottom"] : Array.isArray(safeArea) ? safeArea : [];
|
|
29
29
|
const mergedStyle = {
|
|
30
|
-
...maxWidth ? {
|
|
31
|
-
...!bleed && inset ? { paddingInline: "var(--page-inset)" } : null,
|
|
30
|
+
...maxWidth ? { "--sable-region-max": maxWidth } : null,
|
|
32
31
|
...safeAreaInsets(safeEdges),
|
|
33
32
|
...style
|
|
34
33
|
};
|
|
35
|
-
return /* @__PURE__ */ jsx(
|
|
34
|
+
return /* @__PURE__ */ jsx(
|
|
35
|
+
Comp,
|
|
36
|
+
{
|
|
37
|
+
ref,
|
|
38
|
+
className: cn(
|
|
39
|
+
"w-full",
|
|
40
|
+
// The cap class only with a cap: the variable inherits, so an uncapped
|
|
41
|
+
// Region inside a capped one would otherwise take its ancestor's width.
|
|
42
|
+
maxWidth && "mx-auto max-w-(--sable-region-max)",
|
|
43
|
+
!bleed && inset && "px-(--page-inset)",
|
|
44
|
+
className
|
|
45
|
+
),
|
|
46
|
+
style: mergedStyle,
|
|
47
|
+
...rest,
|
|
48
|
+
children
|
|
49
|
+
}
|
|
50
|
+
);
|
|
36
51
|
}
|
|
37
52
|
export {
|
|
38
53
|
Region
|
package/dist/layout/Row.js
CHANGED
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
var ALIGN = {
|
|
5
|
-
start: "
|
|
6
|
-
center: "center",
|
|
7
|
-
end: "
|
|
8
|
-
stretch: "stretch",
|
|
9
|
-
baseline: "baseline"
|
|
5
|
+
start: "items-start",
|
|
6
|
+
center: "items-center",
|
|
7
|
+
end: "items-end",
|
|
8
|
+
stretch: "items-stretch",
|
|
9
|
+
baseline: "items-baseline"
|
|
10
10
|
};
|
|
11
11
|
var JUSTIFY = {
|
|
12
|
-
start: "
|
|
13
|
-
center: "center",
|
|
14
|
-
end: "
|
|
15
|
-
between: "
|
|
16
|
-
around: "
|
|
12
|
+
start: "justify-start",
|
|
13
|
+
center: "justify-center",
|
|
14
|
+
end: "justify-end",
|
|
15
|
+
between: "justify-between",
|
|
16
|
+
around: "justify-around"
|
|
17
17
|
};
|
|
18
18
|
function resolveGap(gap) {
|
|
19
19
|
return typeof gap === "number" ? `calc(var(--rhythm-unit) * ${gap})` : gap;
|
|
@@ -29,16 +29,23 @@ function Row({
|
|
|
29
29
|
ref,
|
|
30
30
|
...rest
|
|
31
31
|
}) {
|
|
32
|
-
const mergedStyle = {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
const mergedStyle = { "--sable-row-gap": resolveGap(gap), ...style };
|
|
33
|
+
return /* @__PURE__ */ jsx(
|
|
34
|
+
"div",
|
|
35
|
+
{
|
|
36
|
+
ref,
|
|
37
|
+
className: cn(
|
|
38
|
+
"flex flex-row gap-(--sable-row-gap)",
|
|
39
|
+
ALIGN[align],
|
|
40
|
+
JUSTIFY[justify],
|
|
41
|
+
wrap ? "flex-wrap" : "flex-nowrap",
|
|
42
|
+
className
|
|
43
|
+
),
|
|
44
|
+
style: mergedStyle,
|
|
45
|
+
...rest,
|
|
46
|
+
children
|
|
47
|
+
}
|
|
48
|
+
);
|
|
42
49
|
}
|
|
43
50
|
export {
|
|
44
51
|
Row
|
package/dist/layout/Spacer.js
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
// src/layout/Spacer.tsx
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
2
3
|
import { jsx } from "react/jsx-runtime";
|
|
3
4
|
function Spacer({ size = 1, axis = "vertical", grow, className }) {
|
|
4
|
-
const style = grow ?
|
|
5
|
-
return /* @__PURE__ */ jsx(
|
|
5
|
+
const style = grow ? void 0 : { "--sable-spacer": `calc(var(--rhythm-unit) * ${size})` };
|
|
6
|
+
return /* @__PURE__ */ jsx(
|
|
7
|
+
"div",
|
|
8
|
+
{
|
|
9
|
+
"aria-hidden": true,
|
|
10
|
+
className: cn(
|
|
11
|
+
grow ? "flex-1" : axis === "vertical" ? "h-(--sable-spacer) shrink-0" : "w-(--sable-spacer) shrink-0",
|
|
12
|
+
className
|
|
13
|
+
),
|
|
14
|
+
style
|
|
15
|
+
}
|
|
16
|
+
);
|
|
6
17
|
}
|
|
7
18
|
export {
|
|
8
19
|
Spacer
|
package/dist/layout/SplitPane.js
CHANGED
package/dist/layout/Stack.js
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
var ALIGN = {
|
|
5
|
-
start: "
|
|
6
|
-
center: "center",
|
|
7
|
-
end: "
|
|
8
|
-
stretch: "stretch"
|
|
5
|
+
start: "items-start",
|
|
6
|
+
center: "items-center",
|
|
7
|
+
end: "items-end",
|
|
8
|
+
stretch: "items-stretch"
|
|
9
9
|
};
|
|
10
10
|
var JUSTIFY = {
|
|
11
|
-
start: "
|
|
12
|
-
center: "center",
|
|
13
|
-
end: "
|
|
14
|
-
between: "
|
|
15
|
-
around: "
|
|
11
|
+
start: "justify-start",
|
|
12
|
+
center: "justify-center",
|
|
13
|
+
end: "justify-end",
|
|
14
|
+
between: "justify-between",
|
|
15
|
+
around: "justify-around"
|
|
16
16
|
};
|
|
17
17
|
function resolveGap(gap) {
|
|
18
18
|
return typeof gap === "number" ? `calc(var(--rhythm-unit) * ${gap})` : gap;
|
|
@@ -27,15 +27,22 @@ function Stack({
|
|
|
27
27
|
ref,
|
|
28
28
|
...rest
|
|
29
29
|
}) {
|
|
30
|
-
const mergedStyle = {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
const mergedStyle = { "--sable-stack-gap": resolveGap(gap), ...style };
|
|
31
|
+
return /* @__PURE__ */ jsx(
|
|
32
|
+
"div",
|
|
33
|
+
{
|
|
34
|
+
ref,
|
|
35
|
+
className: cn(
|
|
36
|
+
"flex flex-col gap-(--sable-stack-gap)",
|
|
37
|
+
ALIGN[align],
|
|
38
|
+
JUSTIFY[justify],
|
|
39
|
+
className
|
|
40
|
+
),
|
|
41
|
+
style: mergedStyle,
|
|
42
|
+
...rest,
|
|
43
|
+
children
|
|
44
|
+
}
|
|
45
|
+
);
|
|
39
46
|
}
|
|
40
47
|
export {
|
|
41
48
|
Stack
|
package/dist/layout/index.d.ts
CHANGED
|
@@ -43,6 +43,11 @@ interface RegionProps extends ComponentPropsWithRef<"div"> {
|
|
|
43
43
|
* The page region. Fluid-first: full width with a fluid page inset, NOT a
|
|
44
44
|
* centered fixed-width container. Reach for `max` only when a specific region
|
|
45
45
|
* (a reading column, a settings pane) genuinely needs a cap.
|
|
46
|
+
*
|
|
47
|
+
* The inset and the cap are classes (`px-(--page-inset)`, `mx-auto
|
|
48
|
+
* max-w-(--sable-region-max)`), never inline style, so a `className` utility
|
|
49
|
+
* overrides them at any breakpoint (`max-sm:px-0`, `hidden lg:block`). Only the
|
|
50
|
+
* `safeArea` padding stays inline: `env()` is already zero where it does not apply.
|
|
46
51
|
*/
|
|
47
52
|
declare function Region({ as, inset, max, bleed, safeArea, className, style, children, ref, ...rest }: RegionProps): react.JSX.Element;
|
|
48
53
|
|
|
@@ -54,7 +59,13 @@ interface StackProps extends ComponentPropsWithRef<"div"> {
|
|
|
54
59
|
align?: Align$1;
|
|
55
60
|
justify?: Justify$1;
|
|
56
61
|
}
|
|
57
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* Vertical flow on the 8px rhythm. The default container for stacked content.
|
|
64
|
+
*
|
|
65
|
+
* Layout is carried by classes (`flex flex-col items-* justify-*`) and the gap
|
|
66
|
+
* by a CSS variable, so any utility in `className` overrides a prop, at any
|
|
67
|
+
* breakpoint: `max-lg:hidden`, `md:flex-row`, `md:gap-8`.
|
|
68
|
+
*/
|
|
58
69
|
declare function Stack({ gap, align, justify, className, style, children, ref, ...rest }: StackProps): react.JSX.Element;
|
|
59
70
|
|
|
60
71
|
interface PageProps extends RegionProps {
|
|
@@ -127,6 +138,10 @@ interface GridProps extends ComponentPropsWithRef<"div"> {
|
|
|
127
138
|
* `cols` breakpoint map (fewer columns on small screens), or `minItemWidth`
|
|
128
139
|
* cells that breathe with no breakpoints at all (`track` decides whether a
|
|
129
140
|
* sparse row keeps its empty columns or stretches its items across them).
|
|
141
|
+
*
|
|
142
|
+
* `display` is the `grid` class and the gap a CSS variable, never inline
|
|
143
|
+
* style, so a `className` utility overrides either at any breakpoint
|
|
144
|
+
* (`hidden md:grid`, `md:gap-6`, `lg:grid-cols-3`).
|
|
130
145
|
*/
|
|
131
146
|
declare function Grid({ cols, minItemWidth, track, gap, className, style, children, ref, ...rest }: GridProps): react.JSX.Element;
|
|
132
147
|
|
|
@@ -155,6 +170,11 @@ interface ColProps extends ComponentPropsWithRef<"div"> {
|
|
|
155
170
|
* the track itself can thin out on small screens (e.g. 2 → 6 → 12). Pair with
|
|
156
171
|
* `Col` for spans. The track template resolves through the `[data-sable-grid-cols]`
|
|
157
172
|
* rules in `styles.css`, mobile-first.
|
|
173
|
+
*
|
|
174
|
+
* `display` is the `grid` class and the gaps are CSS variables, never inline
|
|
175
|
+
* style, so a `className` utility overrides them at any breakpoint
|
|
176
|
+
* (`hidden md:grid`, `md:gap-x-8`). A bare `gap-*` utility replaces both gaps
|
|
177
|
+
* and with them the column-gap cap, so prefer the `gap` prop unless you mean it.
|
|
158
178
|
*/
|
|
159
179
|
declare function Columns({ count, gap, className, style, children, ref, ...rest }: ColumnsProps): react.JSX.Element;
|
|
160
180
|
/**
|
|
@@ -178,7 +198,13 @@ interface RowProps extends ComponentPropsWithRef<"div"> {
|
|
|
178
198
|
justify?: Justify;
|
|
179
199
|
wrap?: boolean;
|
|
180
200
|
}
|
|
181
|
-
/**
|
|
201
|
+
/**
|
|
202
|
+
* Horizontal flow. Inline rows of controls, toolbars, action clusters.
|
|
203
|
+
*
|
|
204
|
+
* Layout is carried by classes (`flex flex-row items-* justify-*`) and the gap
|
|
205
|
+
* by a CSS variable, so any utility in `className` overrides a prop, at any
|
|
206
|
+
* breakpoint: `hidden md:flex`, `max-md:flex-col`, `md:gap-6`.
|
|
207
|
+
*/
|
|
182
208
|
declare function Row({ gap, align, justify, wrap, className, style, children, ref, ...rest }: RowProps): react.JSX.Element;
|
|
183
209
|
|
|
184
210
|
interface SpacerProps {
|
|
@@ -190,21 +216,37 @@ interface SpacerProps {
|
|
|
190
216
|
grow?: boolean;
|
|
191
217
|
className?: string;
|
|
192
218
|
}
|
|
193
|
-
/**
|
|
219
|
+
/**
|
|
220
|
+
* Explicit space on the rhythm, or a flexible `grow` pusher between siblings.
|
|
221
|
+
*
|
|
222
|
+
* The size is a class reading a CSS variable, never inline style, so a
|
|
223
|
+
* `className` utility resizes or hides it at a breakpoint (`md:h-16`, `max-sm:hidden`).
|
|
224
|
+
*/
|
|
194
225
|
declare function Spacer({ size, axis, grow, className }: SpacerProps): react.JSX.Element;
|
|
195
226
|
|
|
196
227
|
interface CenterProps extends ComponentPropsWithRef<"div"> {
|
|
197
228
|
/** Cap width: a named region ("prose" | "feed" | "wide" | "ultrawide") or any raw CSS length. */
|
|
198
229
|
max?: string;
|
|
199
230
|
}
|
|
200
|
-
/**
|
|
231
|
+
/**
|
|
232
|
+
* Horizontally center a block, optionally capping its width (commonly "prose").
|
|
233
|
+
*
|
|
234
|
+
* The centering and the cap are classes (`mx-auto`, `max-w-(--sable-center-max)`),
|
|
235
|
+
* never inline style, so a `className` utility overrides them at any breakpoint.
|
|
236
|
+
*/
|
|
201
237
|
declare function Center({ max, className, style, children, ref, ...rest }: CenterProps): react.JSX.Element;
|
|
202
238
|
|
|
203
239
|
interface AspectBoxProps extends HTMLAttributes<HTMLDivElement> {
|
|
204
240
|
/** Width / height ratio. Default 16/9. Reserves space so late media never shifts layout. */
|
|
205
241
|
ratio?: number;
|
|
206
242
|
}
|
|
207
|
-
/**
|
|
243
|
+
/**
|
|
244
|
+
* Reserves grid space via aspect-ratio — async/media content loads without layout shift.
|
|
245
|
+
*
|
|
246
|
+
* The ratio is a class reading a CSS variable (`aspect-(--sable-aspect)`), never
|
|
247
|
+
* inline style, so a `className` utility reshapes it at a breakpoint
|
|
248
|
+
* (`md:aspect-video`).
|
|
249
|
+
*/
|
|
208
250
|
declare function AspectBox({ ratio, className, style, children, ...rest }: AspectBoxProps): react.JSX.Element;
|
|
209
251
|
|
|
210
252
|
interface BleedProps extends ComponentPropsWithRef<"div"> {
|
|
@@ -222,6 +264,10 @@ interface BleedProps extends ComponentPropsWithRef<"div"> {
|
|
|
222
264
|
* full-bleed app, or `Bleed` used standalone) `cqw` resolves against the viewport,
|
|
223
265
|
* so behavior is unchanged there. Nest a `Region` / `Center` inside to re-inset.
|
|
224
266
|
*
|
|
267
|
+
* The width and margins are classes, never inline style, so a breakpoint can
|
|
268
|
+
* turn the bleed off: `<Bleed className="lg:mx-0 lg:w-auto">` runs edge to edge
|
|
269
|
+
* on a phone and sits inside the column on a desktop.
|
|
270
|
+
*
|
|
225
271
|
* <Region max="feed">
|
|
226
272
|
* <Bleed><ProfileCover /></Bleed>
|
|
227
273
|
* …
|
|
@@ -258,7 +304,9 @@ interface SplitPaneProps extends Omit<ComponentPropsWithRef<"div">, "children">
|
|
|
258
304
|
* hand-building a desktop two-column layout plus a separate mobile stack.
|
|
259
305
|
*
|
|
260
306
|
* The breakpoint switch + ratio resolve through the `[data-sable-split]` rules in
|
|
261
|
-
* `styles.css` (inline CSS custom properties), so any `ratio` works.
|
|
307
|
+
* `styles.css` (inline CSS custom properties), so any `ratio` works. `display`
|
|
308
|
+
* is the `grid` class and those rules sit in `@layer components`, so a
|
|
309
|
+
* `className` utility (`hidden lg:grid`, `gap-8`) overrides them.
|
|
262
310
|
*
|
|
263
311
|
* `collapseBelow` is **viewport-relative**, not container-relative: it asks how
|
|
264
312
|
* wide the window is, not how wide this SplitPane is. Dropped inside a narrow
|
|
@@ -296,6 +344,9 @@ interface GridOverlayProps {
|
|
|
296
344
|
/**
|
|
297
345
|
* Dev/docs structure visualizer — overlays the column grid so the layout's
|
|
298
346
|
* skeleton is legible. Place inside a `position: relative` container.
|
|
347
|
+
*
|
|
348
|
+
* Positioning, `display` and the inset are classes, never inline style, so a
|
|
349
|
+
* `className` utility overrides them (`max-md:hidden` to draw it on desktop only).
|
|
299
350
|
*/
|
|
300
351
|
declare function GridOverlay({ columns, gap, inset, show, className, }: GridOverlayProps): react.JSX.Element | null;
|
|
301
352
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// src/lib/chart/activity.ts
|
|
2
|
+
import { dayNumber } from "./calendar.js";
|
|
3
|
+
function normalizeActivity(data) {
|
|
4
|
+
const days = /* @__PURE__ */ new Map();
|
|
5
|
+
const list = Array.isArray(data) ? data : Object.entries(data).map(([date, value]) => ({ date, value }));
|
|
6
|
+
for (const d of list) {
|
|
7
|
+
const n = dayNumber(d.date);
|
|
8
|
+
const prev = days.get(n);
|
|
9
|
+
const value = (prev?.value ?? 0) + (Number.isFinite(d.value) ? d.value : 0);
|
|
10
|
+
const level = d.level == null ? prev?.level : Math.max(d.level, prev?.level ?? 0);
|
|
11
|
+
days.set(n, { value, level, category: d.category ?? prev?.category });
|
|
12
|
+
}
|
|
13
|
+
return days;
|
|
14
|
+
}
|
|
15
|
+
function mergeActivity(sources, { both = "both" } = {}) {
|
|
16
|
+
const merged = /* @__PURE__ */ new Map();
|
|
17
|
+
for (const [key, data] of Object.entries(sources)) {
|
|
18
|
+
const list = Array.isArray(data) ? data : Object.entries(data).map(([date, value]) => ({ date, value }));
|
|
19
|
+
for (const d of list) {
|
|
20
|
+
if (!(d.value > 0)) continue;
|
|
21
|
+
const entry = merged.get(d.date) ?? { value: 0, keys: /* @__PURE__ */ new Set() };
|
|
22
|
+
entry.value += d.value;
|
|
23
|
+
entry.keys.add(key);
|
|
24
|
+
merged.set(d.date, entry);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return [...merged.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([date, { value, keys }]) => ({ date, value, category: keys.size > 1 ? both : [...keys][0] }));
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
mergeActivity,
|
|
31
|
+
normalizeActivity
|
|
32
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// src/lib/chart/calendar.ts
|
|
2
|
+
var DAY_MS = 864e5;
|
|
3
|
+
var ISO = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
4
|
+
function dayNumber(iso) {
|
|
5
|
+
const m = ISO.exec(iso);
|
|
6
|
+
if (!m) throw new Error(`Expected a YYYY-MM-DD date, got "${iso}"`);
|
|
7
|
+
return Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3])) / DAY_MS;
|
|
8
|
+
}
|
|
9
|
+
function isoDay(n) {
|
|
10
|
+
return new Date(n * DAY_MS).toISOString().slice(0, 10);
|
|
11
|
+
}
|
|
12
|
+
function weekday(n) {
|
|
13
|
+
return ((n + 4) % 7 + 7) % 7;
|
|
14
|
+
}
|
|
15
|
+
function dayOfMonth(n) {
|
|
16
|
+
return new Date(n * DAY_MS).getUTCDate();
|
|
17
|
+
}
|
|
18
|
+
function utcDate(n) {
|
|
19
|
+
return new Date(n * DAY_MS);
|
|
20
|
+
}
|
|
21
|
+
function today() {
|
|
22
|
+
const d = /* @__PURE__ */ new Date();
|
|
23
|
+
return Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()) / DAY_MS;
|
|
24
|
+
}
|
|
25
|
+
function localeWeekStart(locale) {
|
|
26
|
+
try {
|
|
27
|
+
const tag = new Intl.Locale(locale ?? new Intl.DateTimeFormat().resolvedOptions().locale);
|
|
28
|
+
const first = tag.getWeekInfo?.().firstDay ?? tag.weekInfo?.firstDay;
|
|
29
|
+
return first === 1 ? 1 : 0;
|
|
30
|
+
} catch {
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function activityGrid({ start, end, weekStart }) {
|
|
35
|
+
const origin = start - (weekday(start) - weekStart + 7) % 7;
|
|
36
|
+
const weeks = [];
|
|
37
|
+
for (let col = origin; col <= end; col += 7) {
|
|
38
|
+
const days = [];
|
|
39
|
+
for (let r = 0; r < 7; r++) {
|
|
40
|
+
const n = col + r;
|
|
41
|
+
days.push(n >= start && n <= end ? n : null);
|
|
42
|
+
}
|
|
43
|
+
weeks.push({ days });
|
|
44
|
+
}
|
|
45
|
+
const starts = [{ day: start, week: 0 }];
|
|
46
|
+
weeks.forEach((w, i) => {
|
|
47
|
+
const first = w.days.find((n) => n != null && dayOfMonth(n) === 1);
|
|
48
|
+
if (first != null && first !== start) starts.push({ day: first, week: i });
|
|
49
|
+
});
|
|
50
|
+
const months = starts.map((s, i) => ({ ...s, span: (starts[i + 1]?.week ?? weeks.length) - s.week }));
|
|
51
|
+
return { start, end, weekStart, origin, weeks, months: months.filter((m) => m.span > 0) };
|
|
52
|
+
}
|
|
53
|
+
function gridPosition(grid, n) {
|
|
54
|
+
const offset = n - grid.origin;
|
|
55
|
+
return { week: Math.floor(offset / 7), row: offset % 7 };
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
activityGrid,
|
|
59
|
+
dayNumber,
|
|
60
|
+
dayOfMonth,
|
|
61
|
+
gridPosition,
|
|
62
|
+
isoDay,
|
|
63
|
+
localeWeekStart,
|
|
64
|
+
today,
|
|
65
|
+
utcDate,
|
|
66
|
+
weekday
|
|
67
|
+
};
|
|
@@ -18,7 +18,7 @@ function ChartTooltipContent({
|
|
|
18
18
|
),
|
|
19
19
|
children: [
|
|
20
20
|
label != null ? /* @__PURE__ */ jsx("div", { className: "text-label-sm", children: label }) : null,
|
|
21
|
-
/* @__PURE__ */ jsxs("div", { className: "grid gap-1", children: [
|
|
21
|
+
rows.length || total != null ? /* @__PURE__ */ jsxs("div", { className: "grid gap-1", children: [
|
|
22
22
|
rows.map((row) => /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2", indicator === "line" || indicator === "dashed" ? "items-stretch" : ""), children: [
|
|
23
23
|
row.icon ? /* @__PURE__ */ jsx(Icon, { name: row.icon, className: "shrink-0 text-muted-foreground" }) : indicator === "dot" ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "size-2.5 shrink-0 rounded-[2px]", style: { background: row.color } }) : indicator === "line" ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "w-1 shrink-0 rounded-full", style: { background: row.color } }) : indicator === "dashed" ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "w-0 shrink-0 border-[1.5px] border-dashed", style: { borderColor: row.color } }) : null,
|
|
24
24
|
/* @__PURE__ */ jsx("span", { className: "flex-1 text-muted-foreground", children: row.label }),
|
|
@@ -28,7 +28,7 @@ function ChartTooltipContent({
|
|
|
28
28
|
/* @__PURE__ */ jsx("span", { className: "flex-1 text-label-sm", children: "Total" }),
|
|
29
29
|
/* @__PURE__ */ jsx("span", { className: "ms-3 font-mono tabular-nums text-foreground", children: total })
|
|
30
30
|
] }) : null
|
|
31
|
-
] })
|
|
31
|
+
] }) : null
|
|
32
32
|
]
|
|
33
33
|
}
|
|
34
34
|
);
|