@devalok/shilp-sutra 0.42.1 → 0.44.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/MIGRATION.md +78 -0
- package/dist/_chunks/chat.js +164 -163
- package/dist/_chunks/chat.js.map +1 -1
- package/dist/_chunks/mention-suggestion.js +35 -33
- package/dist/_chunks/mention-suggestion.js.map +1 -1
- package/dist/_chunks/oauth-button.js +102 -97
- package/dist/_chunks/oauth-button.js.map +1 -1
- package/dist/_chunks/popover.js +1 -1
- package/dist/_chunks/popover.js.map +1 -1
- package/dist/_chunks/shared.js +1 -1
- package/dist/_chunks/shared.js.map +1 -1
- package/dist/_chunks/tree-view.js +1 -1
- package/dist/_chunks/tree-view.js.map +1 -1
- package/dist/ai/command-bar.js +1 -1
- package/dist/ai/command-bar.js.map +1 -1
- package/dist/composed/bulk-action-bar.js +1 -1
- package/dist/composed/bulk-action-bar.js.map +1 -1
- package/dist/composed/command-palette.js +4 -4
- package/dist/composed/command-palette.js.map +1 -1
- package/dist/composed/content-card.d.ts +6 -0
- package/dist/composed/content-card.d.ts.map +1 -1
- package/dist/composed/content-card.js +1 -1
- package/dist/composed/content-card.js.map +1 -1
- package/dist/composed/error-boundary.js +1 -1
- package/dist/composed/error-boundary.js.map +1 -1
- package/dist/composed/extensions/emoji-suggestion.js +1 -1
- package/dist/composed/extensions/emoji-suggestion.js.map +1 -1
- package/dist/composed/extensions/file-attachment.d.ts.map +1 -1
- package/dist/composed/file-preview/audio-preview.d.ts.map +1 -1
- package/dist/composed/file-preview.d.ts.map +1 -1
- package/dist/composed/file-preview.js +147 -143
- package/dist/composed/file-preview.js.map +1 -1
- package/dist/composed/filter-bar.js +5 -2
- package/dist/composed/filter-bar.js.map +1 -1
- package/dist/composed/master-detail.js +1 -1
- package/dist/composed/master-detail.js.map +1 -1
- package/dist/composed/page-header.js +1 -1
- package/dist/composed/page-header.js.map +1 -1
- package/dist/composed/rich-chat-input/attachment-strip.d.ts.map +1 -1
- package/dist/composed/rich-chat-input/audio-player.d.ts.map +1 -1
- package/dist/composed/rich-chat-input/schedule-send.d.ts.map +1 -1
- package/dist/composed/rich-chat-input.js +600 -591
- package/dist/composed/rich-chat-input.js.map +1 -1
- package/dist/composed/rich-text-editor.d.ts.map +1 -1
- package/dist/composed/rich-text-editor.js +166 -164
- package/dist/composed/rich-text-editor.js.map +1 -1
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/shell/notification-center.d.ts.map +1 -1
- package/dist/shell/notification-center.js +102 -98
- package/dist/shell/notification-center.js.map +1 -1
- package/dist/shell/sidebar.d.ts +6 -0
- package/dist/shell/sidebar.d.ts.map +1 -1
- package/dist/shell/sidebar.js +142 -118
- package/dist/shell/sidebar.js.map +1 -1
- package/dist/shell/top-bar.d.ts.map +1 -1
- package/dist/shell/top-bar.js +87 -84
- package/dist/shell/top-bar.js.map +1 -1
- package/dist/tokens/semantic.css +9 -2
- package/dist/ui/alert-dialog.js +1 -1
- package/dist/ui/alert-dialog.js.map +1 -1
- package/dist/ui/autocomplete.js +1 -1
- package/dist/ui/autocomplete.js.map +1 -1
- package/dist/ui/avatar.js +1 -1
- package/dist/ui/avatar.js.map +1 -1
- package/dist/ui/breadcrumb.js +2 -2
- package/dist/ui/breadcrumb.js.map +1 -1
- package/dist/ui/button.js.map +1 -1
- package/dist/ui/card.d.ts +82 -7
- package/dist/ui/card.d.ts.map +1 -1
- package/dist/ui/card.js +76 -110
- package/dist/ui/card.js.map +1 -1
- package/dist/ui/chat/message.d.ts.map +1 -1
- package/dist/ui/color-input.js +1 -1
- package/dist/ui/color-input.js.map +1 -1
- package/dist/ui/combobox.js +3 -3
- package/dist/ui/combobox.js.map +1 -1
- package/dist/ui/context-menu.js +2 -2
- package/dist/ui/context-menu.js.map +1 -1
- package/dist/ui/data-table-bulk-actions.js +1 -1
- package/dist/ui/data-table-bulk-actions.js.map +1 -1
- package/dist/ui/dialog.js +1 -1
- package/dist/ui/dialog.js.map +1 -1
- package/dist/ui/dropdown-menu.js +11 -5
- package/dist/ui/dropdown-menu.js.map +1 -1
- package/dist/ui/hover-card.js +1 -1
- package/dist/ui/hover-card.js.map +1 -1
- package/dist/ui/index.d.ts +3 -1
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +58 -56
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/input-otp.js +1 -1
- package/dist/ui/input-otp.js.map +1 -1
- package/dist/ui/menubar.js +12 -6
- package/dist/ui/menubar.js.map +1 -1
- package/dist/ui/navigation-menu.js +1 -1
- package/dist/ui/navigation-menu.js.map +1 -1
- package/dist/ui/oauth-button/oauth-button.d.ts.map +1 -1
- package/dist/ui/select.js +6 -3
- package/dist/ui/select.js.map +1 -1
- package/dist/ui/sidebar.js +74 -74
- package/dist/ui/sidebar.js.map +1 -1
- package/dist/ui/split-button.js +1 -1
- package/dist/ui/split-button.js.map +1 -1
- package/dist/ui/stat-card.d.ts +26 -3
- package/dist/ui/stat-card.d.ts.map +1 -1
- package/dist/ui/stat-card.js +183 -150
- package/dist/ui/stat-card.js.map +1 -1
- package/dist/ui/stat-flash.d.ts +46 -0
- package/dist/ui/stat-flash.d.ts.map +1 -0
- package/dist/ui/stat-flash.js +114 -0
- package/dist/ui/stat-flash.js.map +1 -0
- package/dist/ui/tabs.d.ts.map +1 -1
- package/dist/ui/tabs.js +5 -2
- package/dist/ui/tabs.js.map +1 -1
- package/dist/ui/toast.js +2 -2
- package/dist/ui/toast.js.map +1 -1
- package/dist/ui/truncated-text.d.ts +37 -0
- package/dist/ui/truncated-text.d.ts.map +1 -0
- package/dist/ui/truncated-text.js +53 -0
- package/dist/ui/truncated-text.js.map +1 -0
- package/docs/components/ui/card.md +13 -4
- package/docs/components/ui/stat-card.md +18 -3
- package/docs/components/ui/stat-flash.md +44 -0
- package/docs/components/ui/truncated-text.md +49 -0
- package/llms-full.txt +125 -8
- package/make-kit/components/card.md +14 -4
- package/package.json +1 -1
- package/skill/SKILL.md +1 -1
- package/skill/references/components-full.md +125 -8
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
type TruncateMode = 'end' | 'clamp' | 'middle';
|
|
4
|
+
export interface TruncatedTextProps {
|
|
5
|
+
/** The full text. Always the accessible name, even when visually shortened. */
|
|
6
|
+
children: string;
|
|
7
|
+
/**
|
|
8
|
+
* `end` (default) single-line "…end"; `clamp` multi-line (`lines`); `middle`
|
|
9
|
+
* keeps both ends (filenames, emails) — `name…end.pdf`.
|
|
10
|
+
*/
|
|
11
|
+
mode?: TruncateMode;
|
|
12
|
+
/** Lines for `mode="clamp"`. @default 2 */
|
|
13
|
+
lines?: number;
|
|
14
|
+
/** Element to render. @default 'span' */
|
|
15
|
+
as?: 'span' | 'p' | 'div';
|
|
16
|
+
/** Show the full text in a tooltip when (and only when) actually truncated. @default true */
|
|
17
|
+
tooltip?: boolean;
|
|
18
|
+
className?: string;
|
|
19
|
+
/** Override the full-text accessible name / tooltip (defaults to `children`). */
|
|
20
|
+
title?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Text that truncates and recovers. CSS handles `end`/`clamp` (full text stays in the
|
|
24
|
+
* DOM, screen-reader safe); `middle` measures + JS-shortens but keeps the full string as
|
|
25
|
+
* the accessible name. A tooltip appears ONLY when the text is actually clipped.
|
|
26
|
+
*
|
|
27
|
+
* Note: for `end`/`middle` in a flex/grid row, the shrinking item still needs `min-w-0`
|
|
28
|
+
* (CSS gotcha) — apply it at the call site.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* <TruncatedText>{user.name}</TruncatedText>
|
|
32
|
+
* <TruncatedText mode="middle">{fileName}</TruncatedText> // report…v2.pdf
|
|
33
|
+
* <TruncatedText mode="clamp" lines={2}>{description}</TruncatedText>
|
|
34
|
+
*/
|
|
35
|
+
export declare const TruncatedText: React.ForwardRefExoticComponent<TruncatedTextProps & React.RefAttributes<HTMLElement>>;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=truncated-text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"truncated-text.d.ts","sourceRoot":"","sources":["../../src/ui/truncated-text.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,KAAK,YAAY,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAA;AAE9C,MAAM,WAAW,kBAAkB;IACjC,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,yCAAyC;IACzC,EAAE,CAAC,EAAE,MAAM,GAAG,GAAG,GAAG,KAAK,CAAA;IACzB,6FAA6F;IAC7F,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iFAAiF;IACjF,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAYD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,aAAa,wFA2FzB,CAAA"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "./lib/utils.js";
|
|
3
|
+
import { Tooltip as t, TooltipContent as n, TooltipTrigger as r } from "./tooltip.js";
|
|
4
|
+
import * as i from "react";
|
|
5
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
6
|
+
//#region src/ui/truncated-text.tsx
|
|
7
|
+
function s(e, t, n = "…") {
|
|
8
|
+
if (t >= e.length || t < 2) return e;
|
|
9
|
+
let r = t - n.length, i = Math.ceil(r / 2), a = Math.floor(r / 2);
|
|
10
|
+
return e.slice(0, i) + n + (a > 0 ? e.slice(-a) : "");
|
|
11
|
+
}
|
|
12
|
+
var c = i.forwardRef(({ children: c, mode: l = "end", lines: u = 2, as: d = "span", tooltip: f = !0, className: p, title: m }, h) => {
|
|
13
|
+
let g = c, [_, v] = i.useState(!1), [y, b] = i.useState(g), x = i.useRef(null), S = /* @__PURE__ */ a(d, {
|
|
14
|
+
ref: i.useCallback((e) => {
|
|
15
|
+
if (typeof h == "function" ? h(e) : h && (h.current = e), x.current?.disconnect(), !e) return;
|
|
16
|
+
let t = () => {
|
|
17
|
+
if (l === "middle") {
|
|
18
|
+
if (e.textContent = g, e.clientWidth === 0 || e.scrollWidth <= e.clientWidth) {
|
|
19
|
+
b(g), v(!1);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
let t = 2, n = g.length, r = 2;
|
|
23
|
+
for (; t <= n;) {
|
|
24
|
+
let i = t + n >> 1;
|
|
25
|
+
e.textContent = s(g, i), e.scrollWidth <= e.clientWidth ? (r = i, t = i + 1) : n = i - 1;
|
|
26
|
+
}
|
|
27
|
+
b(s(g, r)), v(!0);
|
|
28
|
+
} else v(l === "clamp" ? e.scrollHeight > e.clientHeight + 1 : e.scrollWidth > e.clientWidth + 1);
|
|
29
|
+
};
|
|
30
|
+
t(), x.current = new ResizeObserver(t), x.current.observe(e);
|
|
31
|
+
}, [
|
|
32
|
+
g,
|
|
33
|
+
l,
|
|
34
|
+
u,
|
|
35
|
+
h
|
|
36
|
+
]),
|
|
37
|
+
className: e(l === "end" && "block truncate", l === "middle" && "block overflow-hidden whitespace-nowrap", l === "clamp" && "overflow-hidden", p),
|
|
38
|
+
style: l === "clamp" ? {
|
|
39
|
+
display: "-webkit-box",
|
|
40
|
+
WebkitBoxOrient: "vertical",
|
|
41
|
+
WebkitLineClamp: u
|
|
42
|
+
} : void 0,
|
|
43
|
+
"aria-label": _ ? m ?? g : void 0,
|
|
44
|
+
children: l === "middle" ? y : g
|
|
45
|
+
});
|
|
46
|
+
return f && _ ? /* @__PURE__ */ o(t, { children: [/* @__PURE__ */ a(r, {
|
|
47
|
+
asChild: !0,
|
|
48
|
+
children: S
|
|
49
|
+
}), /* @__PURE__ */ a(n, { children: m ?? g })] }) : S;
|
|
50
|
+
});
|
|
51
|
+
c.displayName = "TruncatedText";
|
|
52
|
+
//#endregion
|
|
53
|
+
export { c as TruncatedText };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"truncated-text.js","names":[],"sources":["../../src/ui/truncated-text.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\n\nimport { cn } from './lib/utils'\nimport { Tooltip, TooltipContent, TooltipTrigger } from './tooltip'\n\ntype TruncateMode = 'end' | 'clamp' | 'middle'\n\nexport interface TruncatedTextProps {\n /** The full text. Always the accessible name, even when visually shortened. */\n children: string\n /**\n * `end` (default) single-line \"…end\"; `clamp` multi-line (`lines`); `middle`\n * keeps both ends (filenames, emails) — `name…end.pdf`.\n */\n mode?: TruncateMode\n /** Lines for `mode=\"clamp\"`. @default 2 */\n lines?: number\n /** Element to render. @default 'span' */\n as?: 'span' | 'p' | 'div'\n /** Show the full text in a tooltip when (and only when) actually truncated. @default true */\n tooltip?: boolean\n className?: string\n /** Override the full-text accessible name / tooltip (defaults to `children`). */\n title?: string\n}\n\n/** Keep `keep` chars total, ellipsis in the middle. */\nfunction truncateMiddle(str: string, keep: number, ellipsis = '…'): string {\n if (keep >= str.length || keep < 2) return str\n const budget = keep - ellipsis.length\n const head = Math.ceil(budget / 2)\n const tail = Math.floor(budget / 2)\n // slice(-0) returns the whole string, so guard the tail explicitly.\n return str.slice(0, head) + ellipsis + (tail > 0 ? str.slice(-tail) : '')\n}\n\n/**\n * Text that truncates and recovers. CSS handles `end`/`clamp` (full text stays in the\n * DOM, screen-reader safe); `middle` measures + JS-shortens but keeps the full string as\n * the accessible name. A tooltip appears ONLY when the text is actually clipped.\n *\n * Note: for `end`/`middle` in a flex/grid row, the shrinking item still needs `min-w-0`\n * (CSS gotcha) — apply it at the call site.\n *\n * @example\n * <TruncatedText>{user.name}</TruncatedText>\n * <TruncatedText mode=\"middle\">{fileName}</TruncatedText> // report…v2.pdf\n * <TruncatedText mode=\"clamp\" lines={2}>{description}</TruncatedText>\n */\nexport const TruncatedText = React.forwardRef<HTMLElement, TruncatedTextProps>(\n ({ children, mode = 'end', lines = 2, as = 'span', tooltip = true, className, title }, forwardedRef) => {\n const full = children\n const [overflowing, setOverflowing] = React.useState(false)\n const [display, setDisplay] = React.useState(full)\n const roRef = React.useRef<ResizeObserver | null>(null)\n\n // Callback ref owns the ResizeObserver so it always tracks the CURRENT node.\n // Wrapping in a Tooltip remounts the element when `overflowing` flips — a one-shot\n // layout effect would leave a stale observer on the detached node (which then fires\n // with size 0 and resets the measurement). Reconnecting here avoids that loop.\n const attachRef = React.useCallback(\n (node: HTMLElement | null) => {\n if (typeof forwardedRef === 'function') forwardedRef(node)\n else if (forwardedRef) (forwardedRef as React.MutableRefObject<HTMLElement | null>).current = node\n\n roRef.current?.disconnect()\n if (!node) return\n\n const measure = () => {\n if (mode === 'middle') {\n node.textContent = full\n if (node.clientWidth === 0 || node.scrollWidth <= node.clientWidth) {\n setDisplay(full)\n setOverflowing(false)\n return\n }\n // Largest middle-truncated string that fits, via binary search.\n let lo = 2\n let hi = full.length\n let best = 2\n while (lo <= hi) {\n const midKeep = (lo + hi) >> 1\n node.textContent = truncateMiddle(full, midKeep)\n if (node.scrollWidth <= node.clientWidth) {\n best = midKeep\n lo = midKeep + 1\n } else {\n hi = midKeep - 1\n }\n }\n setDisplay(truncateMiddle(full, best))\n setOverflowing(true)\n } else if (mode === 'clamp') {\n setOverflowing(node.scrollHeight > node.clientHeight + 1)\n } else {\n setOverflowing(node.scrollWidth > node.clientWidth + 1)\n }\n }\n\n measure()\n roRef.current = new ResizeObserver(measure)\n roRef.current.observe(node)\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [full, mode, lines, forwardedRef],\n )\n\n const Tag = as as React.ElementType\n const content = (\n <Tag\n ref={attachRef}\n className={cn(\n mode === 'end' && 'block truncate',\n mode === 'middle' && 'block overflow-hidden whitespace-nowrap',\n mode === 'clamp' && 'overflow-hidden',\n className,\n )}\n style={\n mode === 'clamp'\n ? ({ display: '-webkit-box', WebkitBoxOrient: 'vertical', WebkitLineClamp: lines } as React.CSSProperties)\n : undefined\n }\n // When clipped, the visible text may be shortened (middle mode) — keep the full\n // string as the accessible name so screen readers always read it in full.\n aria-label={overflowing ? (title ?? full) : undefined}\n >\n {mode === 'middle' ? display : full}\n </Tag>\n )\n\n if (tooltip && overflowing) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>{content}</TooltipTrigger>\n <TooltipContent>{title ?? full}</TooltipContent>\n </Tooltip>\n )\n }\n return content\n },\n)\nTruncatedText.displayName = 'TruncatedText'\n"],"mappings":";;;;;;AA6BA,SAAS,EAAe,GAAa,GAAc,IAAW,KAAa;AACzE,KAAI,KAAQ,EAAI,UAAU,IAAO,EAAG,QAAO;CAC3C,IAAM,IAAS,IAAO,EAAS,QACzB,IAAO,KAAK,KAAK,IAAS,EAAE,EAC5B,IAAO,KAAK,MAAM,IAAS,EAAE;AAEnC,QAAO,EAAI,MAAM,GAAG,EAAK,GAAG,KAAY,IAAO,IAAI,EAAI,MAAM,CAAC,EAAK,GAAG;;AAgBxE,IAAa,IAAgB,EAAM,YAChC,EAAE,aAAU,UAAO,OAAO,WAAQ,GAAG,QAAK,QAAQ,aAAU,IAAM,cAAW,YAAS,MAAiB;CACtG,IAAM,IAAO,GACP,CAAC,GAAa,KAAkB,EAAM,SAAS,GAAM,EACrD,CAAC,GAAS,KAAc,EAAM,SAAS,EAAK,EAC5C,IAAQ,EAAM,OAA8B,KAAK,EAsDjD,IACJ,kBAFU,GAEV;EACE,KAlDc,EAAM,aACrB,MAA6B;AAK5B,OAJI,OAAO,KAAiB,aAAY,EAAa,EAAK,GACjD,MAAe,EAA4D,UAAU,IAE9F,EAAM,SAAS,YAAY,EACvB,CAAC,EAAM;GAEX,IAAM,UAAgB;AACpB,QAAI,MAAS,UAAU;AAErB,SADA,EAAK,cAAc,GACf,EAAK,gBAAgB,KAAK,EAAK,eAAe,EAAK,aAAa;AAElE,MADA,EAAW,EAAK,EAChB,EAAe,GAAM;AACrB;;KAGF,IAAI,IAAK,GACL,IAAK,EAAK,QACV,IAAO;AACX,YAAO,KAAM,IAAI;MACf,IAAM,IAAW,IAAK,KAAO;AAE7B,MADA,EAAK,cAAc,EAAe,GAAM,EAAQ,EAC5C,EAAK,eAAe,EAAK,eAC3B,IAAO,GACP,IAAK,IAAU,KAEf,IAAK,IAAU;;AAInB,KADA,EAAW,EAAe,GAAM,EAAK,CAAC,EACtC,EAAe,GAAK;WAEpB,EADS,MAAS,UACH,EAAK,eAAe,EAAK,eAAe,IAExC,EAAK,cAAc,EAAK,cAAc,EAAE;;AAM3D,GAFA,GAAS,EACT,EAAM,UAAU,IAAI,eAAe,EAAQ,EAC3C,EAAM,QAAQ,QAAQ,EAAK;KAG7B;GAAC;GAAM;GAAM;GAAO;GAAa,CAClC;EAMG,WAAW,EACT,MAAS,SAAS,kBAClB,MAAS,YAAY,2CACrB,MAAS,WAAW,mBACpB,EACD;EACD,OACE,MAAS,UACJ;GAAE,SAAS;GAAe,iBAAiB;GAAY,iBAAiB;GAAO,GAChF,KAAA;EAIN,cAAY,IAAe,KAAS,IAAQ,KAAA;YAE3C,MAAS,WAAW,IAAU;EAC3B,CAAA;AAWR,QARI,KAAW,IAEX,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;EAAgB,SAAA;YAAS;EAAyB,CAAA,EAClD,kBAAC,GAAD,EAAA,UAAiB,KAAS,GAAsB,CAAA,CACxC,EAAA,CAAA,GAGP;EAEV;AACD,EAAc,cAAc"}
|
|
@@ -9,11 +9,14 @@
|
|
|
9
9
|
color: "default" | "accent" | "error" | "success" | "warning" | "info" | "neutral" (border accent color)
|
|
10
10
|
size: "sm" | "md" | "lg" (padding — propagated to CardHeader/CardContent/CardFooter via context)
|
|
11
11
|
interactive: boolean (enables hover shadow lift + pointer cursor)
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
|
|
13
|
+
### CardAction
|
|
14
|
+
placement: "top-right" (default) | "top-left" | "bottom-right" | "bottom-left"
|
|
15
|
+
tuck: boolean (pull a step toward the corner so an icon button's glyph aligns to the content edge)
|
|
14
16
|
|
|
15
17
|
## Compound Components
|
|
16
18
|
Card (root)
|
|
19
|
+
CardAction ← absolutely-positioned corner slot (badge, icon button, menu)
|
|
17
20
|
CardHeader ← inherits size from Card context
|
|
18
21
|
CardTitle
|
|
19
22
|
CardDescription
|
|
@@ -37,15 +40,21 @@
|
|
|
37
40
|
## Composability
|
|
38
41
|
- **Size cascades through context** — Card's `size` prop sets padding on CardHeader, CardContent, and CardFooter via `CardSizeContext`. Don't set padding classes on sub-components directly; override via `className` if needed.
|
|
39
42
|
- **Not a compound state machine** — Card, CardHeader, CardTitle, etc. are purely structural. No open/close state.
|
|
40
|
-
- **
|
|
43
|
+
- **Corner slots via `CardAction`:** Pin a badge, icon button, or menu to any corner (`placement`), inset to match the card's content padding. `tuck` pulls an icon button so its glyph (not its padding box) aligns to the content edge. Replaces the removed `accent` decorative bar — composition, not a bespoke prop. Card is `relative` to anchor it.
|
|
44
|
+
- **`color` paints the border, never a stacked rail:** `color` tints the 1px edge (semantic accent/error/success/…). The DS never stacks a border + drop shadow or a colored rail on top (make-kit rule #6 — that reads as an AI tell).
|
|
41
45
|
- **Interactive cards:** Set `interactive={true}` + `onClick` for clickable cards (entire surface becomes the button). Add `aria-label` on the Card root when there's no visible heading. For complex multi-action cards, prefer standard Card with explicit buttons inside.
|
|
42
|
-
- **ContentCard (composed)
|
|
46
|
+
- **ContentCard (composed) is deprecated** — compose `Card` + `CardHeader`/`CardContent`/`CardAction` directly instead.
|
|
43
47
|
|
|
44
48
|
## Gotchas
|
|
45
49
|
- Use `interactive` prop for clickable cards — adds hover lift and pointer cursor
|
|
46
50
|
- Don't override CardHeader/CardContent/CardFooter padding via className if you want the size cascade to work — set size on Card instead
|
|
47
51
|
|
|
48
52
|
## Changes
|
|
53
|
+
### v0.44.0
|
|
54
|
+
- **BREAKING** Removed `accent` / `accentColor` (the colored edge-bar / left-rail). Use `<CardAction>` for corner content, or `color` for a tinted border. The DS no longer ships a stacked rail (make-kit rule #6).
|
|
55
|
+
- **Added** `<CardAction>` compound — composable corner slot (4 placements, `tuck`).
|
|
56
|
+
- **Changed** Gap-model padding: the container owns vertical `py` + inter-slot `gap`; slots own only horizontal `px`. Adding/removing a slot can't unbalance the bottom edge. Card is now `relative`.
|
|
57
|
+
|
|
49
58
|
### v0.31.0
|
|
50
59
|
- **Added** `color` prop: semantic border color (accent, error, success, warning, info, neutral)
|
|
51
60
|
- **Added** `size` prop: `sm | md | lg` — padding propagated to sub-components via React context
|
|
@@ -11,12 +11,17 @@
|
|
|
11
11
|
prefix: string (before value, e.g. "$")
|
|
12
12
|
suffix: string (after value, e.g. "users")
|
|
13
13
|
delta: { value: string, direction: "up" | "down" | "neutral" }
|
|
14
|
+
deltaPlacement: "block" | "inline" (block = below value [default]; inline = on the value's baseline)
|
|
14
15
|
icon: ReactNode | ComponentType<{ className?: string }>
|
|
15
16
|
loading: boolean (renders skeleton)
|
|
16
17
|
comparisonLabel: string (shown after delta, e.g. "vs last month")
|
|
17
18
|
secondaryLabel: string (below main value, e.g. "of $50,000 target")
|
|
18
19
|
progress: number (0-100, renders thin progress bar below value)
|
|
19
|
-
|
|
20
|
+
variant: "default" | "elevated" | "outline" | "flat" (default = ring-in-shadow, no border; elevated = stronger shadow; outline = border, no shadow; flat = filled, no edge) — delegated to Card
|
|
21
|
+
accentStyle: "none" | "icon" | "tint" (none [default]; icon = accent chip around icon; tint = accent surface wash + accent value)
|
|
22
|
+
iconFill: "soft" | "solid" (chip style when accentStyle="icon"; default soft)
|
|
23
|
+
flash: "up" | "down" | "goal" | "record" | "alert" | "live" | { tone, icon } (opt-in entrance flash; requires icon)
|
|
24
|
+
flashSpeed: "fast" | "normal" | "slow" (default normal)
|
|
20
25
|
sparkline: number[] (renders mini SVG line chart)
|
|
21
26
|
onClick: () => void (makes card clickable with hover state)
|
|
22
27
|
href: string (makes card a link via LinkContext)
|
|
@@ -34,7 +39,8 @@
|
|
|
34
39
|
delta={{ value: "+12%", direction: "up" }}
|
|
35
40
|
comparisonLabel="vs last month"
|
|
36
41
|
icon={<IconCurrencyDollar />}
|
|
37
|
-
|
|
42
|
+
accentStyle="icon"
|
|
43
|
+
flash="up"
|
|
38
44
|
/>
|
|
39
45
|
|
|
40
46
|
<StatCard
|
|
@@ -51,7 +57,8 @@
|
|
|
51
57
|
- **High-density metric card** — optimized for dashboards. Everything optional except `value`. Mix and match features (delta, sparkline, progress, secondary label, footer) per metric's needs.
|
|
52
58
|
- **Router integration via href:** Internally uses `LinkContext` to resolve framework-specific Link components (Next.js, react-router). Set `href` in a LinkProvider-wrapped tree to get seamless client-side navigation without custom asChild wiring.
|
|
53
59
|
- **Interactive modes:** `onClick` makes the entire card a button; `href` makes it a link. Mutually exclusive — href wins if both are set.
|
|
54
|
-
- **Accent
|
|
60
|
+
- **Accent (composable, opt-in):** `accentStyle="icon"` wraps `icon` in an accent chip (`iconFill="soft" | "solid"`); `accentStyle="tint"` applies a subtle accent surface wash + accent value. `variant` picks edge-vs-elevation. No colored rail — the DS never stacks a border + drop shadow (make-kit rule #6). Trend health reads from `delta.direction` (up=green, down=red).
|
|
61
|
+
- **Flash motion (opt-in):** `flash` mounts a toned state glyph (`up`/`down`/`goal`/`record`/`alert`/`live`, or `{ tone, icon }`) that settles to `icon`; `flashSpeed` tunes timing. Reuses the standalone `StatFlash` primitive. Honors `prefers-reduced-motion`.
|
|
55
62
|
- **Sparkline:** Pure SVG, lightweight — no chart library. For rich charts use Chart components. Minimum 2 data points.
|
|
56
63
|
- **Icon auto-sizing:** Accepts `ComponentType<{ className }>` OR `ReactNode`. The component prop (e.g. `icon={IconBolt}`) is preferred — icon is rendered at a consistent size.
|
|
57
64
|
- **Loading state:** `loading={true}` renders the full card skeleton — use during initial data fetch.
|
|
@@ -63,6 +70,14 @@
|
|
|
63
70
|
- `sparkline` needs at least 2 data points to render
|
|
64
71
|
|
|
65
72
|
## Changes
|
|
73
|
+
### v0.44.0
|
|
74
|
+
- **BREAKING** Renamed `surface` → `variant`, widened to a 4-way scale (`default` | `elevated` | `outline` | `flat`). StatCard now composes `<Card>`, so surface, gap-model padding, and elevation all live in one place. Migration: `surface="raised"` → `variant="default"`, `surface="flat"` → `variant="outline"`.
|
|
75
|
+
- **Added** `deltaPlacement` (`"block"` [default] | `"inline"`) — inline rides the value's baseline for compact dashboards.
|
|
76
|
+
|
|
77
|
+
### v0.43.0
|
|
78
|
+
- **BREAKING** Removed `accent` (colored left-rail). Use `accentStyle` (`"icon"` | `"tint"`) or rely on `delta` for trend colour.
|
|
79
|
+
- **Added** `surface`, `accentStyle`, `iconFill`, `flash`, `flashSpeed`. New standalone `StatFlash` primitive. Base no longer stacks border + shadow.
|
|
80
|
+
|
|
66
81
|
### v0.2.0
|
|
67
82
|
- **Added** `icon` prop now accepts `React.ComponentType` (e.g., `icon={IconBolt}`) in addition to `ReactNode`
|
|
68
83
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# StatFlash
|
|
2
|
+
|
|
3
|
+
- Import: @devalok/shilp-sutra/ui/stat-flash
|
|
4
|
+
- Server-safe: No
|
|
5
|
+
- Category: ui
|
|
6
|
+
|
|
7
|
+
## Props
|
|
8
|
+
icon: ReactNode | ComponentType<{ className?: string }> (REQUIRED — settled identity glyph)
|
|
9
|
+
flash: "up" | "down" | "goal" | "record" | "alert" | "live" | { tone, icon } (REQUIRED — the entrance state)
|
|
10
|
+
fill: "soft" | "solid" (resting chip style; default soft)
|
|
11
|
+
speed: "fast" | "normal" | "slow" (coarse motion preset; default normal)
|
|
12
|
+
holdMs: number (override how long the flash holds before settling)
|
|
13
|
+
settleTransition: Transition (override the icon scale-in transition)
|
|
14
|
+
flashTransition: Transition (override the flash enter/exit fade)
|
|
15
|
+
|
|
16
|
+
## Defaults
|
|
17
|
+
fill="soft", speed="normal"
|
|
18
|
+
|
|
19
|
+
## Example
|
|
20
|
+
```jsx
|
|
21
|
+
// Preset: green up-arrow flashes, then settles to the folder icon
|
|
22
|
+
<StatFlash icon={<IconFolder />} flash="up" />
|
|
23
|
+
|
|
24
|
+
// Explicit tone + glyph
|
|
25
|
+
<StatFlash icon={<IconActivity />} flash={{ tone: "info", icon: <IconBolt /> }} fill="solid" />
|
|
26
|
+
|
|
27
|
+
// Composable speed: coarse preset + fine override
|
|
28
|
+
<StatFlash icon={<IconFolder />} flash="up" speed="fast" holdMs={300} />
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Composability
|
|
32
|
+
- **State → identity primitive:** the chip mounts showing a transient *state* (a toned glyph — up-arrow on green, check, alert…) then settles into the stable *identity* icon. Used by `StatCard`'s `flash` prop, but standalone too (list rows, badges, toasts).
|
|
33
|
+
- **Tone slot:** six named presets (`up`/`down`/`goal`/`record`/`alert`/`live`) map to DS semantic tones (success/error/warning/info/accent) + a glyph. Pass `{ tone, icon }` for anything else.
|
|
34
|
+
- **Composable motion:** `speed` is a coarse preset built from the DS motion tokens; `holdMs` / `settleTransition` / `flashTransition` each override one slice and fall back to the preset.
|
|
35
|
+
- **Accessible by default:** honors `prefers-reduced-motion` — renders the settled identity directly, no flash. The chip is `aria-hidden` (decorative; the metric's text carries meaning).
|
|
36
|
+
|
|
37
|
+
## Gotchas
|
|
38
|
+
- `flash` and `icon` are both required — the flash settles *to* the icon.
|
|
39
|
+
- Fine overrides win over `speed`.
|
|
40
|
+
- Decorative only — never the sole carrier of meaning (it's `aria-hidden`).
|
|
41
|
+
|
|
42
|
+
## Changes
|
|
43
|
+
### v0.43.0
|
|
44
|
+
- **Added** Initial release. Powers `StatCard`'s `flash` prop.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# TruncatedText
|
|
2
|
+
|
|
3
|
+
- Import: @devalok/shilp-sutra/ui/truncated-text
|
|
4
|
+
- Server-safe: No
|
|
5
|
+
- Category: ui
|
|
6
|
+
|
|
7
|
+
## Props
|
|
8
|
+
children: string (REQUIRED — the full text; always the accessible name, even when shortened)
|
|
9
|
+
mode: "end" | "clamp" | "middle" (end [default] = single-line ellipsis; clamp = multi-line; middle = keep both ends)
|
|
10
|
+
lines: number (lines to show in mode="clamp"; default 2)
|
|
11
|
+
as: "span" | "p" | "div" (element to render; default "span")
|
|
12
|
+
tooltip: boolean (show the full text in a tooltip when — and only when — actually clipped; default true)
|
|
13
|
+
title: string (override the full-text accessible name / tooltip; defaults to children)
|
|
14
|
+
className: string
|
|
15
|
+
|
|
16
|
+
## Defaults
|
|
17
|
+
mode="end", lines=2, as="span", tooltip=true
|
|
18
|
+
|
|
19
|
+
## Example
|
|
20
|
+
```jsx
|
|
21
|
+
<TruncatedText>{user.name}</TruncatedText>
|
|
22
|
+
|
|
23
|
+
// Filenames / emails — keep the extension / domain:
|
|
24
|
+
<TruncatedText mode="middle">{fileName}</TruncatedText> // report…v7.pdf
|
|
25
|
+
|
|
26
|
+
// Multi-line description with a 2-line clamp:
|
|
27
|
+
<TruncatedText as="p" mode="clamp" lines={2}>{description}</TruncatedText>
|
|
28
|
+
|
|
29
|
+
// In a flex row, the shrinking item still needs min-w-0:
|
|
30
|
+
<div className="flex items-center gap-ds-03">
|
|
31
|
+
<Avatar … />
|
|
32
|
+
<TruncatedText className="min-w-0 flex-1">{user.name}</TruncatedText>
|
|
33
|
+
</div>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Composability
|
|
37
|
+
- **Truncates AND recovers in one place:** `end`/`clamp` are pure CSS (full text stays in the DOM → screen-reader safe). `middle` measures the element and JS-shortens the visible string but keeps the full string as the accessible name. A tooltip appears only when `scrollWidth`/`scrollHeight` actually exceeds the box (measured via ResizeObserver) — no tooltip noise on text that fits.
|
|
38
|
+
- **`mode="middle"` for identifiers:** filenames (extension), emails (domain), paths, hashes — end-ellipsis destroys the meaningful tail; middle keeps both ends. Middle is not native CSS, so it's measured + recomputed on resize.
|
|
39
|
+
- **The flexbox gotcha:** `truncate` (and `middle`) do nothing on a flex/grid child unless that item has `min-w-0` (or an explicit width). TruncatedText can't set that on its parent — add `min-w-0` at the call site.
|
|
40
|
+
- **Don't truncate everything:** page titles/H1, toast bodies, validation messages, and numbers must show fully (a truncated number is a wrong number). Use this for bounded list/nav/metadata text.
|
|
41
|
+
|
|
42
|
+
## Gotchas
|
|
43
|
+
- `children` must be a string (it's measured + used as the accessible name) — not arbitrary ReactNode.
|
|
44
|
+
- In a flex row, add `min-w-0` to the TruncatedText (or its column) or nothing truncates.
|
|
45
|
+
- `mode="middle"` clips to the largest middle-truncated string that fits; the last glyph may be sub-pixel-clipped by overflow-hidden — the tooltip + aria-label always carry the full string.
|
|
46
|
+
|
|
47
|
+
## Changes
|
|
48
|
+
### v0.44.0
|
|
49
|
+
- **Added** Initial release — end/clamp/middle truncation with overflow-aware tooltip recovery.
|
package/llms-full.txt
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
> All variant values and props verified from source CVA definitions.
|
|
6
6
|
>
|
|
7
7
|
> Package: @devalok/shilp-sutra
|
|
8
|
-
> Version: 0.
|
|
8
|
+
> Version: 0.44.0
|
|
9
9
|
>
|
|
10
10
|
> **If you are an AI agent reading this file top-to-bottom:** the Setup
|
|
11
11
|
> section below is authoritative. If any later per-component doc or a
|
|
@@ -1096,11 +1096,14 @@ See `Button` docs for the public API.
|
|
|
1096
1096
|
color: "default" | "accent" | "error" | "success" | "warning" | "info" | "neutral" (border accent color)
|
|
1097
1097
|
size: "sm" | "md" | "lg" (padding — propagated to CardHeader/CardContent/CardFooter via context)
|
|
1098
1098
|
interactive: boolean (enables hover shadow lift + pointer cursor)
|
|
1099
|
-
|
|
1100
|
-
|
|
1099
|
+
|
|
1100
|
+
### CardAction
|
|
1101
|
+
placement: "top-right" (default) | "top-left" | "bottom-right" | "bottom-left"
|
|
1102
|
+
tuck: boolean (pull a step toward the corner so an icon button's glyph aligns to the content edge)
|
|
1101
1103
|
|
|
1102
1104
|
## Compound Components
|
|
1103
1105
|
Card (root)
|
|
1106
|
+
CardAction ← absolutely-positioned corner slot (badge, icon button, menu)
|
|
1104
1107
|
CardHeader ← inherits size from Card context
|
|
1105
1108
|
CardTitle
|
|
1106
1109
|
CardDescription
|
|
@@ -1124,15 +1127,21 @@ See `Button` docs for the public API.
|
|
|
1124
1127
|
## Composability
|
|
1125
1128
|
- **Size cascades through context** — Card's `size` prop sets padding on CardHeader, CardContent, and CardFooter via `CardSizeContext`. Don't set padding classes on sub-components directly; override via `className` if needed.
|
|
1126
1129
|
- **Not a compound state machine** — Card, CardHeader, CardTitle, etc. are purely structural. No open/close state.
|
|
1127
|
-
- **
|
|
1130
|
+
- **Corner slots via `CardAction`:** Pin a badge, icon button, or menu to any corner (`placement`), inset to match the card's content padding. `tuck` pulls an icon button so its glyph (not its padding box) aligns to the content edge. Replaces the removed `accent` decorative bar — composition, not a bespoke prop. Card is `relative` to anchor it.
|
|
1131
|
+
- **`color` paints the border, never a stacked rail:** `color` tints the 1px edge (semantic accent/error/success/…). The DS never stacks a border + drop shadow or a colored rail on top (make-kit rule #6 — that reads as an AI tell).
|
|
1128
1132
|
- **Interactive cards:** Set `interactive={true}` + `onClick` for clickable cards (entire surface becomes the button). Add `aria-label` on the Card root when there's no visible heading. For complex multi-action cards, prefer standard Card with explicit buttons inside.
|
|
1129
|
-
- **ContentCard (composed)
|
|
1133
|
+
- **ContentCard (composed) is deprecated** — compose `Card` + `CardHeader`/`CardContent`/`CardAction` directly instead.
|
|
1130
1134
|
|
|
1131
1135
|
## Gotchas
|
|
1132
1136
|
- Use `interactive` prop for clickable cards — adds hover lift and pointer cursor
|
|
1133
1137
|
- Don't override CardHeader/CardContent/CardFooter padding via className if you want the size cascade to work — set size on Card instead
|
|
1134
1138
|
|
|
1135
1139
|
## Changes
|
|
1140
|
+
### v0.44.0
|
|
1141
|
+
- **BREAKING** Removed `accent` / `accentColor` (the colored edge-bar / left-rail). Use `<CardAction>` for corner content, or `color` for a tinted border. The DS no longer ships a stacked rail (make-kit rule #6).
|
|
1142
|
+
- **Added** `<CardAction>` compound — composable corner slot (4 placements, `tuck`).
|
|
1143
|
+
- **Changed** Gap-model padding: the container owns vertical `py` + inter-slot `gap`; slots own only horizontal `px`. Adding/removing a slot can't unbalance the bottom edge. Card is now `relative`.
|
|
1144
|
+
|
|
1136
1145
|
### v0.31.0
|
|
1137
1146
|
- **Added** `color` prop: semantic border color (accent, error, success, warning, info, neutral)
|
|
1138
1147
|
- **Added** `size` prop: `sm | md | lg` — padding propagated to sub-components via React context
|
|
@@ -4041,12 +4050,17 @@ import { SplitButton } from '@devalok/shilp-sutra'
|
|
|
4041
4050
|
prefix: string (before value, e.g. "$")
|
|
4042
4051
|
suffix: string (after value, e.g. "users")
|
|
4043
4052
|
delta: { value: string, direction: "up" | "down" | "neutral" }
|
|
4053
|
+
deltaPlacement: "block" | "inline" (block = below value [default]; inline = on the value's baseline)
|
|
4044
4054
|
icon: ReactNode | ComponentType<{ className?: string }>
|
|
4045
4055
|
loading: boolean (renders skeleton)
|
|
4046
4056
|
comparisonLabel: string (shown after delta, e.g. "vs last month")
|
|
4047
4057
|
secondaryLabel: string (below main value, e.g. "of $50,000 target")
|
|
4048
4058
|
progress: number (0-100, renders thin progress bar below value)
|
|
4049
|
-
|
|
4059
|
+
variant: "default" | "elevated" | "outline" | "flat" (default = ring-in-shadow, no border; elevated = stronger shadow; outline = border, no shadow; flat = filled, no edge) — delegated to Card
|
|
4060
|
+
accentStyle: "none" | "icon" | "tint" (none [default]; icon = accent chip around icon; tint = accent surface wash + accent value)
|
|
4061
|
+
iconFill: "soft" | "solid" (chip style when accentStyle="icon"; default soft)
|
|
4062
|
+
flash: "up" | "down" | "goal" | "record" | "alert" | "live" | { tone, icon } (opt-in entrance flash; requires icon)
|
|
4063
|
+
flashSpeed: "fast" | "normal" | "slow" (default normal)
|
|
4050
4064
|
sparkline: number[] (renders mini SVG line chart)
|
|
4051
4065
|
onClick: () => void (makes card clickable with hover state)
|
|
4052
4066
|
href: string (makes card a link via LinkContext)
|
|
@@ -4064,7 +4078,8 @@ import { SplitButton } from '@devalok/shilp-sutra'
|
|
|
4064
4078
|
delta={{ value: "+12%", direction: "up" }}
|
|
4065
4079
|
comparisonLabel="vs last month"
|
|
4066
4080
|
icon={<IconCurrencyDollar />}
|
|
4067
|
-
|
|
4081
|
+
accentStyle="icon"
|
|
4082
|
+
flash="up"
|
|
4068
4083
|
/>
|
|
4069
4084
|
|
|
4070
4085
|
<StatCard
|
|
@@ -4081,7 +4096,8 @@ import { SplitButton } from '@devalok/shilp-sutra'
|
|
|
4081
4096
|
- **High-density metric card** — optimized for dashboards. Everything optional except `value`. Mix and match features (delta, sparkline, progress, secondary label, footer) per metric's needs.
|
|
4082
4097
|
- **Router integration via href:** Internally uses `LinkContext` to resolve framework-specific Link components (Next.js, react-router). Set `href` in a LinkProvider-wrapped tree to get seamless client-side navigation without custom asChild wiring.
|
|
4083
4098
|
- **Interactive modes:** `onClick` makes the entire card a button; `href` makes it a link. Mutually exclusive — href wins if both are set.
|
|
4084
|
-
- **Accent
|
|
4099
|
+
- **Accent (composable, opt-in):** `accentStyle="icon"` wraps `icon` in an accent chip (`iconFill="soft" | "solid"`); `accentStyle="tint"` applies a subtle accent surface wash + accent value. `variant` picks edge-vs-elevation. No colored rail — the DS never stacks a border + drop shadow (make-kit rule #6). Trend health reads from `delta.direction` (up=green, down=red).
|
|
4100
|
+
- **Flash motion (opt-in):** `flash` mounts a toned state glyph (`up`/`down`/`goal`/`record`/`alert`/`live`, or `{ tone, icon }`) that settles to `icon`; `flashSpeed` tunes timing. Reuses the standalone `StatFlash` primitive. Honors `prefers-reduced-motion`.
|
|
4085
4101
|
- **Sparkline:** Pure SVG, lightweight — no chart library. For rich charts use Chart components. Minimum 2 data points.
|
|
4086
4102
|
- **Icon auto-sizing:** Accepts `ComponentType<{ className }>` OR `ReactNode`. The component prop (e.g. `icon={IconBolt}`) is preferred — icon is rendered at a consistent size.
|
|
4087
4103
|
- **Loading state:** `loading={true}` renders the full card skeleton — use during initial data fetch.
|
|
@@ -4093,11 +4109,63 @@ import { SplitButton } from '@devalok/shilp-sutra'
|
|
|
4093
4109
|
- `sparkline` needs at least 2 data points to render
|
|
4094
4110
|
|
|
4095
4111
|
## Changes
|
|
4112
|
+
### v0.44.0
|
|
4113
|
+
- **BREAKING** Renamed `surface` → `variant`, widened to a 4-way scale (`default` | `elevated` | `outline` | `flat`). StatCard now composes `<Card>`, so surface, gap-model padding, and elevation all live in one place. Migration: `surface="raised"` → `variant="default"`, `surface="flat"` → `variant="outline"`.
|
|
4114
|
+
- **Added** `deltaPlacement` (`"block"` [default] | `"inline"`) — inline rides the value's baseline for compact dashboards.
|
|
4115
|
+
|
|
4116
|
+
### v0.43.0
|
|
4117
|
+
- **BREAKING** Removed `accent` (colored left-rail). Use `accentStyle` (`"icon"` | `"tint"`) or rely on `delta` for trend colour.
|
|
4118
|
+
- **Added** `surface`, `accentStyle`, `iconFill`, `flash`, `flashSpeed`. New standalone `StatFlash` primitive. Base no longer stacks border + shadow.
|
|
4119
|
+
|
|
4096
4120
|
### v0.2.0
|
|
4097
4121
|
- **Added** `icon` prop now accepts `React.ComponentType` (e.g., `icon={IconBolt}`) in addition to `ReactNode`
|
|
4098
4122
|
|
|
4099
4123
|
### v0.1.0
|
|
4100
4124
|
- **Added** Initial release
|
|
4125
|
+
# StatFlash
|
|
4126
|
+
|
|
4127
|
+
- Import: @devalok/shilp-sutra/ui/stat-flash
|
|
4128
|
+
- Server-safe: No
|
|
4129
|
+
- Category: ui
|
|
4130
|
+
|
|
4131
|
+
## Props
|
|
4132
|
+
icon: ReactNode | ComponentType<{ className?: string }> (REQUIRED — settled identity glyph)
|
|
4133
|
+
flash: "up" | "down" | "goal" | "record" | "alert" | "live" | { tone, icon } (REQUIRED — the entrance state)
|
|
4134
|
+
fill: "soft" | "solid" (resting chip style; default soft)
|
|
4135
|
+
speed: "fast" | "normal" | "slow" (coarse motion preset; default normal)
|
|
4136
|
+
holdMs: number (override how long the flash holds before settling)
|
|
4137
|
+
settleTransition: Transition (override the icon scale-in transition)
|
|
4138
|
+
flashTransition: Transition (override the flash enter/exit fade)
|
|
4139
|
+
|
|
4140
|
+
## Defaults
|
|
4141
|
+
fill="soft", speed="normal"
|
|
4142
|
+
|
|
4143
|
+
## Example
|
|
4144
|
+
```jsx
|
|
4145
|
+
// Preset: green up-arrow flashes, then settles to the folder icon
|
|
4146
|
+
<StatFlash icon={<IconFolder />} flash="up" />
|
|
4147
|
+
|
|
4148
|
+
// Explicit tone + glyph
|
|
4149
|
+
<StatFlash icon={<IconActivity />} flash={{ tone: "info", icon: <IconBolt /> }} fill="solid" />
|
|
4150
|
+
|
|
4151
|
+
// Composable speed: coarse preset + fine override
|
|
4152
|
+
<StatFlash icon={<IconFolder />} flash="up" speed="fast" holdMs={300} />
|
|
4153
|
+
```
|
|
4154
|
+
|
|
4155
|
+
## Composability
|
|
4156
|
+
- **State → identity primitive:** the chip mounts showing a transient *state* (a toned glyph — up-arrow on green, check, alert…) then settles into the stable *identity* icon. Used by `StatCard`'s `flash` prop, but standalone too (list rows, badges, toasts).
|
|
4157
|
+
- **Tone slot:** six named presets (`up`/`down`/`goal`/`record`/`alert`/`live`) map to DS semantic tones (success/error/warning/info/accent) + a glyph. Pass `{ tone, icon }` for anything else.
|
|
4158
|
+
- **Composable motion:** `speed` is a coarse preset built from the DS motion tokens; `holdMs` / `settleTransition` / `flashTransition` each override one slice and fall back to the preset.
|
|
4159
|
+
- **Accessible by default:** honors `prefers-reduced-motion` — renders the settled identity directly, no flash. The chip is `aria-hidden` (decorative; the metric's text carries meaning).
|
|
4160
|
+
|
|
4161
|
+
## Gotchas
|
|
4162
|
+
- `flash` and `icon` are both required — the flash settles *to* the icon.
|
|
4163
|
+
- Fine overrides win over `speed`.
|
|
4164
|
+
- Decorative only — never the sole carrier of meaning (it's `aria-hidden`).
|
|
4165
|
+
|
|
4166
|
+
## Changes
|
|
4167
|
+
### v0.43.0
|
|
4168
|
+
- **Added** Initial release. Powers `StatCard`'s `flash` prop.
|
|
4101
4169
|
# StatusDot
|
|
4102
4170
|
|
|
4103
4171
|
- Import: @devalok/shilp-sutra/ui/status-dot
|
|
@@ -4806,6 +4874,55 @@ export default function RootLayout({ children }) {
|
|
|
4806
4874
|
|
|
4807
4875
|
### v0.1.0
|
|
4808
4876
|
- **Added** Initial release
|
|
4877
|
+
# TruncatedText
|
|
4878
|
+
|
|
4879
|
+
- Import: @devalok/shilp-sutra/ui/truncated-text
|
|
4880
|
+
- Server-safe: No
|
|
4881
|
+
- Category: ui
|
|
4882
|
+
|
|
4883
|
+
## Props
|
|
4884
|
+
children: string (REQUIRED — the full text; always the accessible name, even when shortened)
|
|
4885
|
+
mode: "end" | "clamp" | "middle" (end [default] = single-line ellipsis; clamp = multi-line; middle = keep both ends)
|
|
4886
|
+
lines: number (lines to show in mode="clamp"; default 2)
|
|
4887
|
+
as: "span" | "p" | "div" (element to render; default "span")
|
|
4888
|
+
tooltip: boolean (show the full text in a tooltip when — and only when — actually clipped; default true)
|
|
4889
|
+
title: string (override the full-text accessible name / tooltip; defaults to children)
|
|
4890
|
+
className: string
|
|
4891
|
+
|
|
4892
|
+
## Defaults
|
|
4893
|
+
mode="end", lines=2, as="span", tooltip=true
|
|
4894
|
+
|
|
4895
|
+
## Example
|
|
4896
|
+
```jsx
|
|
4897
|
+
<TruncatedText>{user.name}</TruncatedText>
|
|
4898
|
+
|
|
4899
|
+
// Filenames / emails — keep the extension / domain:
|
|
4900
|
+
<TruncatedText mode="middle">{fileName}</TruncatedText> // report…v7.pdf
|
|
4901
|
+
|
|
4902
|
+
// Multi-line description with a 2-line clamp:
|
|
4903
|
+
<TruncatedText as="p" mode="clamp" lines={2}>{description}</TruncatedText>
|
|
4904
|
+
|
|
4905
|
+
// In a flex row, the shrinking item still needs min-w-0:
|
|
4906
|
+
<div className="flex items-center gap-ds-03">
|
|
4907
|
+
<Avatar … />
|
|
4908
|
+
<TruncatedText className="min-w-0 flex-1">{user.name}</TruncatedText>
|
|
4909
|
+
</div>
|
|
4910
|
+
```
|
|
4911
|
+
|
|
4912
|
+
## Composability
|
|
4913
|
+
- **Truncates AND recovers in one place:** `end`/`clamp` are pure CSS (full text stays in the DOM → screen-reader safe). `middle` measures the element and JS-shortens the visible string but keeps the full string as the accessible name. A tooltip appears only when `scrollWidth`/`scrollHeight` actually exceeds the box (measured via ResizeObserver) — no tooltip noise on text that fits.
|
|
4914
|
+
- **`mode="middle"` for identifiers:** filenames (extension), emails (domain), paths, hashes — end-ellipsis destroys the meaningful tail; middle keeps both ends. Middle is not native CSS, so it's measured + recomputed on resize.
|
|
4915
|
+
- **The flexbox gotcha:** `truncate` (and `middle`) do nothing on a flex/grid child unless that item has `min-w-0` (or an explicit width). TruncatedText can't set that on its parent — add `min-w-0` at the call site.
|
|
4916
|
+
- **Don't truncate everything:** page titles/H1, toast bodies, validation messages, and numbers must show fully (a truncated number is a wrong number). Use this for bounded list/nav/metadata text.
|
|
4917
|
+
|
|
4918
|
+
## Gotchas
|
|
4919
|
+
- `children` must be a string (it's measured + used as the accessible name) — not arbitrary ReactNode.
|
|
4920
|
+
- In a flex row, add `min-w-0` to the TruncatedText (or its column) or nothing truncates.
|
|
4921
|
+
- `mode="middle"` clips to the largest middle-truncated string that fits; the last glyph may be sub-pixel-clipped by overflow-hidden — the tooltip + aria-label always carry the full string.
|
|
4922
|
+
|
|
4923
|
+
## Changes
|
|
4924
|
+
### v0.44.0
|
|
4925
|
+
- **Added** Initial release — end/clamp/middle truncation with overflow-aware tooltip recovery.
|
|
4809
4926
|
# VisuallyHidden
|
|
4810
4927
|
|
|
4811
4928
|
- Import: @devalok/shilp-sutra/ui/visually-hidden
|
|
@@ -5,6 +5,7 @@ The default container for any panel-style content that sits **on** the page.
|
|
|
5
5
|
```tsx
|
|
6
6
|
import {
|
|
7
7
|
Card,
|
|
8
|
+
CardAction,
|
|
8
9
|
CardHeader,
|
|
9
10
|
CardTitle,
|
|
10
11
|
CardDescription,
|
|
@@ -65,8 +66,13 @@ Card (root)
|
|
|
65
66
|
| Prop | Type | Notes |
|
|
66
67
|
|---|---|---|
|
|
67
68
|
| `interactive` | `boolean` | Adds hover lift + `cursor-pointer`. Make the whole card clickable. Provide `onClick` and `aria-label`. |
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
|
|
70
|
+
### CardAction (corner slot)
|
|
71
|
+
|
|
72
|
+
| Prop | Type | Notes |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| `placement` | `'top-right'` (default) `\| 'top-left' \| 'bottom-right' \| 'bottom-left'` | Which corner. Inset matches the card's content padding. |
|
|
75
|
+
| `tuck` | `boolean` | Pull a step toward the corner so an icon button's glyph (not its padding box) aligns to the content edge. |
|
|
70
76
|
|
|
71
77
|
## Examples
|
|
72
78
|
|
|
@@ -107,13 +113,17 @@ Card (root)
|
|
|
107
113
|
</Card>
|
|
108
114
|
```
|
|
109
115
|
|
|
110
|
-
**
|
|
116
|
+
**Corner slot — status badge / overflow action:**
|
|
111
117
|
```tsx
|
|
112
|
-
<Card
|
|
118
|
+
<Card>
|
|
119
|
+
<CardAction><Badge color="success" size="xs">DEPLOYED</Badge></CardAction>
|
|
113
120
|
<CardHeader>
|
|
114
121
|
<CardTitle>Deploy succeeded</CardTitle>
|
|
115
122
|
<CardDescription>main · 2 min ago</CardDescription>
|
|
116
123
|
</CardHeader>
|
|
124
|
+
<CardAction placement="bottom-right" tuck>
|
|
125
|
+
<Button variant="ghost" size="sm">View logs</Button>
|
|
126
|
+
</CardAction>
|
|
117
127
|
</Card>
|
|
118
128
|
```
|
|
119
129
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devalok/shilp-sutra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"description": "Devalok Design System — accessible React components, OKLCH design tokens, and Tailwind 4 CSS-first setup. Ships with AI-agent setup recipes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Devalok Design & Strategy Studios <shilp-sutra@devalok.in>",
|
package/skill/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: shilp-sutra
|
|
|
3
3
|
description: Add, configure, and use components from Devalok's shilp-sutra design system (@devalok/shilp-sutra) — a Tailwind 4 + React 19 + CVA library with 110+ accessible components, OKLCH design tokens, framer-motion animations, and per-component RSC-safe entry points. Use this skill whenever the user mentions shilp-sutra, Devalok, the @devalok npm scope, or asks to install/add/style/theme UI in any React project that already depends on the package — even if they don't name it explicitly. Use it instead of generic shadcn/ui, MUI, or Chakra knowledge when shilp-sutra is in the project. Covers Next.js (App + Pages), Vite, Astro, Remix, TanStack Start setup playbooks; component API and variant reference; brand token customization; Server Component import patterns; and a troubleshoot tree for the thirteen most common breakages.
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.44.0"
|
|
7
7
|
author: Devalok Design & Strategy Studios
|
|
8
8
|
homepage: https://github.com/devalok-design/shilp-sutra
|
|
9
9
|
npm: https://www.npmjs.com/package/@devalok/shilp-sutra
|