@krizaka/ui 3.16.0 → 3.17.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/blocks.css CHANGED
@@ -720,6 +720,14 @@ a.kz-fact:focus-visible { outline: 2px solid var(--kz-ring, var(--kz-accent)); o
720
720
  .kz-tpl-catalogue-filters > summary::-webkit-details-marker { display: none; }
721
721
  .kz-tpl-catalogue-filters > div { padding: 0 14px 14px; }
722
722
  }
723
+ @media (max-width: 1279px) {
724
+ .kz-tpl-catalogue[data-rail="wide"] { grid-template-columns: minmax(0, 1fr); gap: 16px; }
725
+ .kz-tpl-catalogue[data-rail="wide"] .kz-tpl-catalogue-filters { position: static; border-radius: 14px; border: 1px solid var(--kz-border-default); background: var(--kz-surface-1); }
726
+ .kz-tpl-catalogue[data-rail="wide"] .kz-tpl-catalogue-filters > summary { display: flex; align-items: center; justify-content: space-between; min-height: 44px; padding: 0 14px; cursor: pointer; list-style: none;
727
+ font-size: 14px; font-weight: 650; color: var(--kz-text-primary); }
728
+ .kz-tpl-catalogue[data-rail="wide"] .kz-tpl-catalogue-filters > summary::-webkit-details-marker { display: none; }
729
+ .kz-tpl-catalogue[data-rail="wide"] .kz-tpl-catalogue-filters > div { padding: 0 14px 14px; }
730
+ }
723
731
 
724
732
  /* A long read: chapters with their big numbers, pull quotes in the margin, figures that break out. */
725
733
  .kz-tpl-chapters { display: grid; gap: clamp(40px, 7vw, 88px); }
@@ -27,8 +27,13 @@ type CataloguePageProps = Omit<React.HTMLAttributes<HTMLElement>, "title"> & {
27
27
  children: React.ReactNode;
28
28
  /** The app's link component (Next's `Link`). */
29
29
  linkAs?: LinkComponent;
30
+ /**
31
+ * From which window width the filters stay in their column beside the results. `default`: 900 px and above. `wide`:
32
+ * 1280 px and above — a wide table (DataTable) needs the room; under it the filters are a bar above the results.
33
+ */
34
+ railFrom?: "default" | "wide";
30
35
  };
31
36
  /** A catalogue: hero, sticky filters beside the results (folded on a phone), the count, the results. */
32
- declare function CataloguePage({ back, eyebrow, title, lead, search, filters, filtersLabel, count, toolbar, linkAs, children, ...props }: CataloguePageProps): React.JSX.Element;
37
+ declare function CataloguePage({ back, eyebrow, title, lead, search, filters, filtersLabel, count, toolbar, linkAs, railFrom, children, ...props }: CataloguePageProps): React.JSX.Element;
33
38
 
34
39
  export { CataloguePage, type CataloguePageProps };
@@ -2,10 +2,10 @@
2
2
  import { Page } from "@krizaka/ui/page";
3
3
  import { PageHero } from "@krizaka/ui/page-hero";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
- function CataloguePage({ back, eyebrow, title, lead, search, filters, filtersLabel, count, toolbar, linkAs, children, ...props }) {
5
+ function CataloguePage({ back, eyebrow, title, lead, search, filters, filtersLabel, count, toolbar, linkAs, railFrom = "default", children, ...props }) {
6
6
  return /* @__PURE__ */ jsxs("div", { ...props, children: [
7
7
  /* @__PURE__ */ jsx(PageHero, { back, linkAs, eyebrow, title, lead, backdrop: "plain", children: search }),
8
- /* @__PURE__ */ jsx(Page, { className: "kz-tpl-catalogue-frame", children: /* @__PURE__ */ jsxs("div", { className: "kz-tpl-catalogue", children: [
8
+ /* @__PURE__ */ jsx(Page, { className: "kz-tpl-catalogue-frame", children: /* @__PURE__ */ jsxs("div", { className: "kz-tpl-catalogue", "data-rail": railFrom, children: [
9
9
  /* @__PURE__ */ jsxs("details", { className: "kz-tpl-catalogue-filters", open: true, children: [
10
10
  /* @__PURE__ */ jsx("summary", { children: filtersLabel }),
11
11
  /* @__PURE__ */ jsx("div", { children: filters })
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/catalogue-page/catalogue-page.tsx"],"sourcesContent":["// Server-safe. A catalogue or an index (blocks.css `.kz-tpl-catalogue`): the hero, then the filters in a sticky column\n// beside the results — folded into a disclosure above them on a phone — the count of what matches and the sort, and\n// the results (a CardGrid of LinkCards, a list). The filters live in the address: a link per choice, no client state.\nimport { Page } from \"@krizaka/ui/page\";\nimport type { LinkComponent } from \"@krizaka/ui/page-hero\";\nimport { PageHero } from \"@krizaka/ui/page-hero\";\nimport type * as React from \"react\";\n\nexport type CataloguePageProps = Omit<React.HTMLAttributes<HTMLElement>, \"title\"> & {\n /** The way back. */\n back?: { href: string; label: string };\n /** A mono line above the title. */\n eyebrow?: React.ReactNode;\n /** The page's `h1`. */\n title: React.ReactNode;\n /** One sentence under it. */\n lead?: React.ReactNode;\n /** In the hero: a search field. */\n search?: React.ReactNode;\n /** The filters (groups of ChipLinks, a FilterBar): sticky on a desktop. */\n filters: React.ReactNode;\n /** On a phone, the words that open the filters (`Filters`). */\n filtersLabel: string;\n /** What matches, in words (`24 components`). */\n count?: React.ReactNode;\n /** On the right of the count: the sort, a view switch. */\n toolbar?: React.ReactNode;\n /** The results: a CardGrid, a list, an empty state. */\n children: React.ReactNode;\n /** The app's link component (Next's `Link`). */\n linkAs?: LinkComponent;\n};\n\n/** A catalogue: hero, sticky filters beside the results (folded on a phone), the count, the results. */\nexport function CataloguePage({ back, eyebrow, title, lead, search, filters, filtersLabel, count, toolbar, linkAs, children, ...props }: CataloguePageProps) {\n return (\n <div {...props}>\n <PageHero back={back} linkAs={linkAs} eyebrow={eyebrow} title={title} lead={lead} backdrop=\"plain\">\n {search}\n </PageHero>\n <Page className=\"kz-tpl-catalogue-frame\">\n <div className=\"kz-tpl-catalogue\">\n <details className=\"kz-tpl-catalogue-filters\" open>\n <summary>{filtersLabel}</summary>\n <div>{filters}</div>\n </details>\n <div className=\"min-w-0\">\n {(count || toolbar) && (\n <div className=\"kz-tpl-catalogue-head\">\n {count && (\n <p className=\"kz-tpl-count\" aria-live=\"polite\">\n {count}\n </p>\n )}\n {toolbar}\n </div>\n )}\n {children}\n </div>\n </div>\n </Page>\n </div>\n );\n}\n"],"mappings":";AAGA,SAAS,YAAY;AAErB,SAAS,gBAAgB;AAgCnB,cAKI,YALJ;AAHC,SAAS,cAAc,EAAE,MAAM,SAAS,OAAO,MAAM,QAAQ,SAAS,cAAc,OAAO,SAAS,QAAQ,UAAU,GAAG,MAAM,GAAuB;AAC3J,SACE,qBAAC,SAAK,GAAG,OACP;AAAA,wBAAC,YAAS,MAAY,QAAgB,SAAkB,OAAc,MAAY,UAAS,SACxF,kBACH;AAAA,IACA,oBAAC,QAAK,WAAU,0BACd,+BAAC,SAAI,WAAU,oBACb;AAAA,2BAAC,aAAQ,WAAU,4BAA2B,MAAI,MAChD;AAAA,4BAAC,aAAS,wBAAa;AAAA,QACvB,oBAAC,SAAK,mBAAQ;AAAA,SAChB;AAAA,MACA,qBAAC,SAAI,WAAU,WACX;AAAA,kBAAS,YACT,qBAAC,SAAI,WAAU,yBACZ;AAAA,mBACC,oBAAC,OAAE,WAAU,gBAAe,aAAU,UACnC,iBACH;AAAA,UAED;AAAA,WACH;AAAA,QAED;AAAA,SACH;AAAA,OACF,GACF;AAAA,KACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../src/catalogue-page/catalogue-page.tsx"],"sourcesContent":["// Server-safe. A catalogue or an index (blocks.css `.kz-tpl-catalogue`): the hero, then the filters in a sticky column\n// beside the results — folded into a disclosure above them on a phone — the count of what matches and the sort, and\n// the results (a CardGrid of LinkCards, a list). The filters live in the address: a link per choice, no client state.\nimport { Page } from \"@krizaka/ui/page\";\nimport type { LinkComponent } from \"@krizaka/ui/page-hero\";\nimport { PageHero } from \"@krizaka/ui/page-hero\";\nimport type * as React from \"react\";\n\nexport type CataloguePageProps = Omit<React.HTMLAttributes<HTMLElement>, \"title\"> & {\n /** The way back. */\n back?: { href: string; label: string };\n /** A mono line above the title. */\n eyebrow?: React.ReactNode;\n /** The page's `h1`. */\n title: React.ReactNode;\n /** One sentence under it. */\n lead?: React.ReactNode;\n /** In the hero: a search field. */\n search?: React.ReactNode;\n /** The filters (groups of ChipLinks, a FilterBar): sticky on a desktop. */\n filters: React.ReactNode;\n /** On a phone, the words that open the filters (`Filters`). */\n filtersLabel: string;\n /** What matches, in words (`24 components`). */\n count?: React.ReactNode;\n /** On the right of the count: the sort, a view switch. */\n toolbar?: React.ReactNode;\n /** The results: a CardGrid, a list, an empty state. */\n children: React.ReactNode;\n /** The app's link component (Next's `Link`). */\n linkAs?: LinkComponent;\n /**\n * From which window width the filters stay in their column beside the results. `default`: 900 px and above. `wide`:\n * 1280 px and above — a wide table (DataTable) needs the room; under it the filters are a bar above the results.\n */\n railFrom?: \"default\" | \"wide\";\n};\n\n/** A catalogue: hero, sticky filters beside the results (folded on a phone), the count, the results. */\nexport function CataloguePage({ back, eyebrow, title, lead, search, filters, filtersLabel, count, toolbar, linkAs, railFrom = \"default\", children, ...props }: CataloguePageProps) {\n return (\n <div {...props}>\n <PageHero back={back} linkAs={linkAs} eyebrow={eyebrow} title={title} lead={lead} backdrop=\"plain\">\n {search}\n </PageHero>\n <Page className=\"kz-tpl-catalogue-frame\">\n <div className=\"kz-tpl-catalogue\" data-rail={railFrom}>\n <details className=\"kz-tpl-catalogue-filters\" open>\n <summary>{filtersLabel}</summary>\n <div>{filters}</div>\n </details>\n <div className=\"min-w-0\">\n {(count || toolbar) && (\n <div className=\"kz-tpl-catalogue-head\">\n {count && (\n <p className=\"kz-tpl-count\" aria-live=\"polite\">\n {count}\n </p>\n )}\n {toolbar}\n </div>\n )}\n {children}\n </div>\n </div>\n </Page>\n </div>\n );\n}\n"],"mappings":";AAGA,SAAS,YAAY;AAErB,SAAS,gBAAgB;AAqCnB,cAKI,YALJ;AAHC,SAAS,cAAc,EAAE,MAAM,SAAS,OAAO,MAAM,QAAQ,SAAS,cAAc,OAAO,SAAS,QAAQ,WAAW,WAAW,UAAU,GAAG,MAAM,GAAuB;AACjL,SACE,qBAAC,SAAK,GAAG,OACP;AAAA,wBAAC,YAAS,MAAY,QAAgB,SAAkB,OAAc,MAAY,UAAS,SACxF,kBACH;AAAA,IACA,oBAAC,QAAK,WAAU,0BACd,+BAAC,SAAI,WAAU,oBAAmB,aAAW,UAC3C;AAAA,2BAAC,aAAQ,WAAU,4BAA2B,MAAI,MAChD;AAAA,4BAAC,aAAS,wBAAa;AAAA,QACvB,oBAAC,SAAK,mBAAQ;AAAA,SAChB;AAAA,MACA,qBAAC,SAAI,WAAU,WACX;AAAA,kBAAS,YACT,qBAAC,SAAI,WAAU,yBACZ;AAAA,mBACC,oBAAC,OAAE,WAAU,gBAAe,aAAU,UACnC,iBACH;AAAA,UAED;AAAA,WACH;AAAA,QAED;AAAA,SACH;AAAA,OACF,GACF;AAAA,KACF;AAEJ;","names":[]}
@@ -21,7 +21,7 @@ var sites = [
21
21
  site({ id: "orochia", name: "Orochia", url: "https://dev.orochia.com", kind: "product", group: "products", status: "released", mark: "orochia", repo: "https://github.com/krizaka/orochia" }),
22
22
  site({ id: "yasakia", name: "Yasakia", url: "https://dev.yasakia.com", kind: "product", group: "products", status: "upcoming", mark: "yasakia", repo: "https://github.com/krizaka/yasakia" }),
23
23
  site({ id: "yororis", name: "Yororis", url: "https://dev.yororis.com", kind: "product", group: "products", status: "construction", mark: "yororis", repo: "https://github.com/krizaka/yororis" }),
24
- site({ id: "yomizaka", name: "Yomizaka", url: "https://dev.yomizaka.com", kind: "product", group: "products", status: "upcoming", mark: "yomizaka", repo: "https://github.com/krizaka/yomizaka" }),
24
+ site({ id: "yomizaka", name: "Yomizaka", url: "https://dev.yomizaka.com", kind: "product", group: "products", status: "development", mark: "yomizaka", repo: "https://github.com/krizaka/yomizaka" }),
25
25
  // In development: nothing deployed yet, so the address is the repository (never a dead link to an unborn dev site).
26
26
  site({ id: "kachizaka", name: "Kachizaka", url: "https://github.com/krizaka/kachizaka", kind: "product", group: "products", status: "development", mark: "kachizaka", repo: "https://github.com/krizaka/kachizaka" }),
27
27
  site({ id: "akinaza", name: "Akinaza", url: "https://github.com/krizaka/akinaza", kind: "product", group: "products", status: "development", mark: "akinaza", repo: "https://github.com/krizaka/akinaza" }),
@@ -92,4 +92,4 @@ export {
92
92
  sitesByGroup,
93
93
  familyLabels
94
94
  };
95
- //# sourceMappingURL=chunk-SKMJRM5K.js.map
95
+ //# sourceMappingURL=chunk-53LQPYQZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/family-mark.tsx","../src/sites/sites.ts"],"sourcesContent":["// The mark of a site of the family (`@krizaka/ui/sites`): its brand's `ProductLogo`, still (a list of marks never\n// moves), or GitHub's, in the text colour. Shared by `NavBar.Family` and `SiteFooter` — not an entry of its own.\nimport type * as React from \"react\";\n\nimport { ProductLogo } from \"./marks/ProductLogo\";\nimport type { SiteMark } from \"./sites/sites\";\n\n/** GitHub's mark, in the text colour (both themes). */\nexport function GitHubGlyph({ size = 16, className }: { size?: number; className?: string }) {\n return (\n <svg viewBox=\"0 0 16 16\" width={size} height={size} fill=\"currentColor\" aria-hidden className={className}>\n <path d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8Z\" />\n </svg>\n );\n}\n\n/** A site's mark, decorative (its name is always written beside it). */\nexport function FamilyMark({ mark, size }: { mark: SiteMark; size: number }): React.ReactElement {\n if (mark === \"github\") return <GitHubGlyph size={Math.round(size * 0.8)} />;\n return <ProductLogo id={mark} size={size} animated={false} />;\n}\n","// Server-safe: plain data. The Krizaka family — every site Krizaka runs, once. The family switcher of the bar\n// (`NavBar.Family`) and the common footer (`SiteFooter`) both read it, so adding a site is one release of this package:\n// every bar and every footer of the family follow.\n//\n// The words of the family (a site's one line, the groups, the statuses, the footer's signature) are brand data, written\n// here in English and French (`familyLabels`) — a site passes the set of its locale, or its own translation.\n\n/** A site of the family. */\nexport type SiteId = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\" | \"ui\" | \"java\" | \"github\";\n\n/** What a site is: the company, a product, or an open-source project. It chooses the footer's signature line. */\nexport type SiteKind = \"company\" | \"product\" | \"open-source\";\n\n/**\n * `released`: open to visit. `upcoming`: announced, not released yet (said \"Coming soon\"). `construction`: under\n * construction — nothing deployed beyond its page, nothing offered. `development`: being built in the open, nothing\n * deployed yet — its address is its repository (said \"In development\").\n */\nexport type SiteStatus = \"released\" | \"upcoming\" | \"construction\" | \"development\";\n\n/** The groups of the family, in reading order: Krizaka · Products · Open source · GitHub. */\nexport type SiteGroup = \"krizaka\" | \"products\" | \"open-source\" | \"github\";\n\n/** The mark a site wears: a brand of `ProductLogo` (Krizaka UI and Krizaka Java wear Krizaka's), or GitHub's. */\nexport type SiteMark = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\" | \"github\";\n\nexport interface Site {\n readonly id: SiteId;\n /** The name, never translated. */\n readonly name: string;\n /** The address of the site (its home). */\n readonly url: string;\n /** The address without the scheme, as people say it (`ui.krizaka.com`). */\n readonly host: string;\n readonly kind: SiteKind;\n readonly group: SiteGroup;\n readonly status: SiteStatus;\n readonly mark: SiteMark;\n /** The repository the site is built from. */\n readonly repo: string;\n}\n\nconst site = (s: Omit<Site, \"host\">): Site => ({ ...s, host: s.url.replace(/^https?:\\/\\//, \"\").replace(/\\/$/, \"\") });\n\n/** Every site of the family, in reading order (the group order, then the order inside each group). */\nexport const sites: readonly Site[] = [\n site({ id: \"krizaka\", name: \"Krizaka\", url: \"https://www.krizaka.com\", kind: \"company\", group: \"krizaka\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-com\" }),\n site({ id: \"orazaka\", name: \"Orazaka\", url: \"https://dev.orazaka.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orazaka\", repo: \"https://github.com/krizaka/orazaka\" }),\n site({ id: \"orochia\", name: \"Orochia\", url: \"https://dev.orochia.com\", kind: \"product\", group: \"products\", status: \"released\", mark: \"orochia\", repo: \"https://github.com/krizaka/orochia\" }),\n site({ id: \"yasakia\", name: \"Yasakia\", url: \"https://dev.yasakia.com\", kind: \"product\", group: \"products\", status: \"upcoming\", mark: \"yasakia\", repo: \"https://github.com/krizaka/yasakia\" }),\n site({ id: \"yororis\", name: \"Yororis\", url: \"https://dev.yororis.com\", kind: \"product\", group: \"products\", status: \"construction\", mark: \"yororis\", repo: \"https://github.com/krizaka/yororis\" }),\n site({ id: \"yomizaka\", name: \"Yomizaka\", url: \"https://dev.yomizaka.com\", kind: \"product\", group: \"products\", status: \"development\", mark: \"yomizaka\", repo: \"https://github.com/krizaka/yomizaka\" }),\n // In development: nothing deployed yet, so the address is the repository (never a dead link to an unborn dev site).\n site({ id: \"kachizaka\", name: \"Kachizaka\", url: \"https://github.com/krizaka/kachizaka\", kind: \"product\", group: \"products\", status: \"development\", mark: \"kachizaka\", repo: \"https://github.com/krizaka/kachizaka\" }),\n site({ id: \"akinaza\", name: \"Akinaza\", url: \"https://github.com/krizaka/akinaza\", kind: \"product\", group: \"products\", status: \"development\", mark: \"akinaza\", repo: \"https://github.com/krizaka/akinaza\" }),\n site({ id: \"ui\", name: \"Krizaka UI\", url: \"https://ui.krizaka.com\", kind: \"open-source\", group: \"open-source\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-ui\" }),\n site({ id: \"java\", name: \"Krizaka Java\", url: \"https://java.krizaka.com\", kind: \"open-source\", group: \"open-source\", status: \"released\", mark: \"krizaka\", repo: \"https://github.com/krizaka/krizaka-build\" }),\n site({ id: \"github\", name: \"GitHub\", url: \"https://github.com/krizaka\", kind: \"open-source\", group: \"github\", status: \"released\", mark: \"github\", repo: \"https://github.com/krizaka\" }),\n];\n\n/** The groups, in reading order. */\nexport const siteGroups: readonly SiteGroup[] = [\"krizaka\", \"products\", \"open-source\", \"github\"];\n\n/** A site by id. */\nexport function siteById(id: SiteId): Site {\n const found = sites.find((s) => s.id === id);\n if (!found) throw new Error(`Unknown Krizaka site \"${id}\".`);\n return found;\n}\n\n/** The family grouped, in reading order: `[{ group, sites }]`, empty groups left out. */\nexport function sitesByGroup(): { group: SiteGroup; sites: Site[] }[] {\n return siteGroups.map((group) => ({ group, sites: sites.filter((s) => s.group === group) })).filter((g) => g.sites.length > 0);\n}\n\n/** The languages the family's words are written in. */\nexport type SiteLocale = \"en\" | \"fr\";\n\n/** The words of the family: the switcher, the footer, each site's one line. */\nexport interface FamilyLabels {\n /** The family's name: the switcher's navigation and its button, the footer's navigation (“Krizaka family”). */\n family: string;\n /** The heading of each group. */\n groups: Record<SiteGroup, string>;\n /** The badge of a site that is not released. */\n status: Record<Exclude<SiteStatus, \"released\">, string>;\n /** Says which site is the current one (“You are here”). */\n current: string;\n /** One line per site: what it is. */\n descriptions: Record<SiteId, string>;\n /** The footer's signature, by the kind of the current site (“A Krizaka product”). */\n signature: Record<SiteKind, string>;\n /** The footer's place line (“Made in Montréal”). */\n place: string;\n /** The accessible name of the version (“Version”). */\n version: string;\n}\n\n/** The family's words in English and French. Pass `familyLabels[locale]`, or your own translation of the same shape. */\nexport const familyLabels: Record<SiteLocale, FamilyLabels> = {\n en: {\n family: \"Krizaka family\",\n groups: { krizaka: \"Krizaka\", products: \"Products\", \"open-source\": \"Open source\", github: \"GitHub\" },\n status: { upcoming: \"Coming soon\", construction: \"Under construction\", development: \"In development\" },\n current: \"You are here\",\n descriptions: {\n krizaka: \"The company: our products, our open source, our story.\",\n orazaka: \"Sovereign AI, on your own servers.\",\n orochia: \"A video platform for creators.\",\n yasakia: \"The dating app where you talk first.\",\n yororis: \"A listening space with a face and a voice.\",\n yomizaka: \"Your documents, read and checked by Orazaka.\",\n kachizaka: \"Predictions among friends — live data, in real time.\",\n akinaza: \"Running a shop in Québec, for less — Loi 25 by design.\",\n ui: \"Components, tokens, icons and charts — web and React Native.\",\n java: \"Spring Boot building blocks on Maven Central.\",\n github: \"Every repository, in the open.\",\n },\n signature: { company: \"A Krizaka company\", product: \"A Krizaka product\", \"open-source\": \"A Krizaka open-source project\" },\n place: \"Made in Montréal\",\n version: \"Version\",\n },\n fr: {\n family: \"Famille Krizaka\",\n groups: { krizaka: \"Krizaka\", products: \"Produits\", \"open-source\": \"Open source\", github: \"GitHub\" },\n status: { upcoming: \"Bientôt\", construction: \"En construction\", development: \"En développement\" },\n current: \"Vous êtes ici\",\n descriptions: {\n krizaka: \"La compagnie : nos produits, notre open source, notre histoire.\",\n orazaka: \"L’IA souveraine, sur vos propres serveurs.\",\n orochia: \"Une plateforme vidéo pour les créateurs.\",\n yasakia: \"L’app de rencontre où l’on se parle d’abord.\",\n yororis: \"Un espace d’écoute avec un visage et une voix.\",\n yomizaka: \"Vos documents, lus et vérifiés par Orazaka.\",\n kachizaka: \"Les pronostics entre amis — des données en direct.\",\n akinaza: \"Tenir une boutique au Québec, pour moins cher — Loi 25 dès la conception.\",\n ui: \"Composants, jetons, icônes et graphiques — web et React Native.\",\n java: \"Des briques Spring Boot sur Maven Central.\",\n github: \"Tous les dépôts, au grand jour.\",\n },\n signature: { company: \"Une compagnie Krizaka\", product: \"Un produit Krizaka\", \"open-source\": \"Un projet open source de Krizaka\" },\n place: \"Fait à Montréal\",\n version: \"Version\",\n },\n};\n"],"mappings":"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","names":[]}
@@ -97,6 +97,18 @@ declare const dataTable: tailwind_variants.TVReturnType<{
97
97
  td: string;
98
98
  };
99
99
  };
100
+ sticky: {
101
+ true: {};
102
+ false: {};
103
+ };
104
+ striped: {
105
+ true: {};
106
+ false: {};
107
+ };
108
+ typed: {
109
+ true: {};
110
+ false: {};
111
+ };
100
112
  }, {
101
113
  root: string;
102
114
  toolbar: string;
@@ -157,6 +169,18 @@ declare const dataTable: tailwind_variants.TVReturnType<{
157
169
  td: string;
158
170
  };
159
171
  };
172
+ sticky: {
173
+ true: {};
174
+ false: {};
175
+ };
176
+ striped: {
177
+ true: {};
178
+ false: {};
179
+ };
180
+ typed: {
181
+ true: {};
182
+ false: {};
183
+ };
160
184
  }, {
161
185
  root: string;
162
186
  toolbar: string;
@@ -217,6 +241,18 @@ declare const dataTable: tailwind_variants.TVReturnType<{
217
241
  td: string;
218
242
  };
219
243
  };
244
+ sticky: {
245
+ true: {};
246
+ false: {};
247
+ };
248
+ striped: {
249
+ true: {};
250
+ false: {};
251
+ };
252
+ typed: {
253
+ true: {};
254
+ false: {};
255
+ };
220
256
  }, {
221
257
  root: string;
222
258
  toolbar: string;
@@ -240,6 +276,7 @@ declare const dataTable: tailwind_variants.TVReturnType<{
240
276
  skeleton: string;
241
277
  footer: string;
242
278
  }>>;
279
+ type DataTableColumnType = "title" | "badge" | "number" | "link" | "date" | "status" | "text";
243
280
  type SortDirection = "ascending" | "descending";
244
281
  type DataTableSort = {
245
282
  column: string;
@@ -283,7 +320,7 @@ type DataTableColumn<T> = {
283
320
  align?: "start" | "center" | "end";
284
321
  /** The width, in px (the starting width when `resizing`). */
285
322
  width?: number;
286
- /** The narrowest a resized column may be, in px. Default 64. */
323
+ /** The narrowest the column may be, in px (also what a resized one may shrink to). Default 64. */
287
324
  minWidth?: number;
288
325
  /** In a card: `title` (the heading of the card), `label` (default: "Header value"), `hidden`. */
289
326
  card?: "title" | "label" | "hidden";
@@ -291,6 +328,29 @@ type DataTableColumn<T> = {
291
328
  headerHidden?: boolean;
292
329
  /** Listed in `DataTableColumnsMenu`, so a person can hide it. Default `true`. */
293
330
  hideable?: boolean;
331
+ /**
332
+ * What the column holds, which sizes and aligns it: `title` (the name, takes the slack, truncated, `subtitle` under it in
333
+ * mono — and the card's heading), `badge` · `status` (as wide as the badge, never stretched), `number` (end-aligned,
334
+ * tabular), `date`, `link` (truncated, `href`), `text` (wraps, two lines). Untyped: as before.
335
+ */
336
+ type?: DataTableColumnType;
337
+ /**
338
+ * 1 (default) always in the row; 2 to 7 leave it when the table is narrower than 42 · 48 · 56 · 64 · 72 · 80 rem and
339
+ * go to the row's detail (a chevron opens it); under 36 rem (cards) 2 stays on the card, 3+ go to its detail.
340
+ */
341
+ priority?: 1 | 2 | 3 | 4 | 5 | 6 | 7;
342
+ /** `title`: the line under the name (an id), in mono. */
343
+ subtitle?: (row: T) => React.ReactNode;
344
+ /** `link`: where the value leads. */
345
+ href?: (row: T) => string | undefined;
346
+ /** `number`: the unit after the value (muted). */
347
+ unit?: string;
348
+ /** The widest the column may be in the table layouts, in px; longer text is clamped, the full value on hover. */
349
+ maxWidth?: number;
350
+ /** One line only, truncated (the full value on hover). */
351
+ nowrap?: boolean;
352
+ /** Lines of text kept before the ellipsis (default 2 for `text`). */
353
+ clamp?: 1 | 2 | 3;
294
354
  };
295
355
  type DataTableLabels = {
296
356
  /** The checkbox of the header ("Select all rows"). */
@@ -327,6 +387,8 @@ type DataTableLabels = {
327
387
  rangeTo: (header: string) => string;
328
388
  /** The button of the columns menu ("Columns"). */
329
389
  columns: string;
390
+ /** The button that opens what left the row: `(name) => "Show more about " + name`. */
391
+ showMore: (name: string) => string;
330
392
  /** The export button ("Export CSV"). */
331
393
  export: string;
332
394
  /** The words of the pagination under the table. */
@@ -429,8 +491,19 @@ type DataTableProps<T> = {
429
491
  empty?: React.ReactNode;
430
492
  /** `auto` (default): a table, cards below 36rem of container width. `table` or `cards` to force one. */
431
493
  layout?: "auto" | "table" | "cards";
432
- /** comfortable (default) · compact. */
494
+ /** comfortable (default) · compact — one row height per density. */
433
495
  density?: "comfortable" | "compact";
496
+ /** The header stays at the top of the window while the page scrolls (`--dt-top` on the table, or a number of px, clears a fixed bar). */
497
+ stickyHeader?: boolean | number;
498
+ /** Zebra rows. */
499
+ striped?: boolean;
500
+ /**
501
+ * The table never scrolls sideways by default once its columns have a `type` or a `priority`: what does not fit goes to
502
+ * the row's detail, then to cards. `true` opts in to a sideways scroll instead, with fades at the edges.
503
+ */
504
+ scroll?: boolean;
505
+ /** Keeps the first column in view while the table scrolls sideways (with `scroll`). */
506
+ stickyFirstColumn?: boolean;
434
507
  /** Renders only the rows in view, in a scrolling body of `height` px; rows are measured (`estimateRowHeight` is a guess). Forces the table layout. */
435
508
  virtualized?: {
436
509
  height: number;
@@ -468,7 +541,7 @@ declare function useDataTable<T extends RowData = object>(): Omit<Context, "tabl
468
541
  exportRows: () => readonly T[];
469
542
  };
470
543
  /** A table of records — sort, filters and facets, groups, details, pages, selection, visibility, pinning, resizing — on the client or the server, cards on a phone, windowing for long lists. */
471
- declare function DataTable<T extends RowData>({ rows, columns, getRowId, caption, showCaption, sort: sortProp, defaultSort, onSortChange, filters: filtersProp, defaultFilters, onFiltersChange, selectable, selected: selectedProp, defaultSelected, onSelectedChange, rowLabel, groupBy: groupByProp, defaultGroupBy, onGroupByChange, columnVisibility: visibilityProp, defaultColumnVisibility, onColumnVisibilityChange, pinning, resizing, getSubRows, renderDetail, expanded: expandedProp, defaultExpanded, onExpandedChange, pageSize: pageSizeProp, defaultPageSize, onPageSizeChange, pageSizeOptions, page: pageProp, defaultPage, onPageChange, manual, rowCount, onStateChange, loading, loadingRows, error, empty, layout, density, virtualized, toolbar, footer, labels: given, className, }: DataTableProps<T>): React.JSX.Element;
544
+ declare function DataTable<T extends RowData>({ rows, columns, getRowId, caption, showCaption, sort: sortProp, defaultSort, onSortChange, filters: filtersProp, defaultFilters, onFiltersChange, selectable, selected: selectedProp, defaultSelected, onSelectedChange, rowLabel, groupBy: groupByProp, defaultGroupBy, onGroupByChange, columnVisibility: visibilityProp, defaultColumnVisibility, onColumnVisibilityChange, pinning, resizing, getSubRows, renderDetail, expanded: expandedProp, defaultExpanded, onExpandedChange, pageSize: pageSizeProp, defaultPageSize, onPageSizeChange, pageSizeOptions, page: pageProp, defaultPage, onPageChange, manual, rowCount, onStateChange, loading, loadingRows, error, empty, layout, density, stickyHeader, striped, scroll, stickyFirstColumn, virtualized, toolbar, footer, labels: given, className, }: DataTableProps<T>): React.JSX.Element;
472
545
 
473
546
  type DataTableFiltersProps = {
474
547
  /** The search's wait after the last key, in ms, before the filter applies (a request, in server mode). Default 250. */
@@ -22,7 +22,9 @@ import {
22
22
  SearchField
23
23
  } from "./chunk-REPTKUJ6.js";
24
24
  import "./chunk-ZIXB33X7.js";
25
- import "./chunk-2FRWNSRO.js";
25
+ import {
26
+ cn
27
+ } from "./chunk-2FRWNSRO.js";
26
28
  import {
27
29
  useControllable
28
30
  } from "./chunk-FZBWEDQI.js";
@@ -257,7 +259,7 @@ var FILTERS = { text: filterText, facet: filterFacet, range: filterRange };
257
259
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
258
260
  var dataTable = tv2({
259
261
  slots: {
260
- root: "@container w-full overflow-hidden rounded-xl border border-border-default bg-surface-1",
262
+ root: "@container w-full overflow-clip rounded-xl border border-border-default bg-surface-1",
261
263
  toolbar: "border-b border-border-default p-3",
262
264
  scroller: "w-full overflow-x-auto",
263
265
  table: "w-full border-collapse text-left text-sm text-fg",
@@ -314,10 +316,63 @@ var dataTable = tv2({
314
316
  density: {
315
317
  comfortable: {},
316
318
  compact: { th: "py-2", td: "py-1.5" }
317
- }
319
+ },
320
+ // The header stays in view while the page scrolls (table layouts only; `--dt-top` clears a fixed bar).
321
+ sticky: { true: {}, false: {} },
322
+ // Zebra rows (the table layouts); the cards keep their frame.
323
+ striped: { true: {}, false: {} },
324
+ // Typed columns: one height per row (`typed`); untyped tables keep the height of their content.
325
+ typed: { true: {}, false: {} }
318
326
  },
319
- defaultVariants: { layout: "auto", density: "comfortable" }
327
+ compoundVariants: [
328
+ { layout: "table", sticky: true, class: { th: "sticky top-(--dt-top,0px) z-[3] bg-surface-2" } },
329
+ { layout: "auto", sticky: true, class: { th: "@xl:sticky @xl:top-(--dt-top,0px) @xl:z-[3] @xl:bg-surface-2" } },
330
+ { layout: "table", striped: true, class: { tr: "even:bg-surface-2/40" } },
331
+ { layout: "auto", striped: true, class: { tr: "@xl:even:bg-surface-2/40" } },
332
+ // One row height per density (a two-line row — a name and its id — is the tallest).
333
+ { layout: "table", density: "comfortable", typed: true, class: { td: "h-15" } },
334
+ { layout: "auto", density: "comfortable", typed: true, class: { td: "@xl:h-15" } },
335
+ { layout: "table", density: "compact", typed: true, class: { td: "h-12" } },
336
+ { layout: "auto", density: "compact", typed: true, class: { td: "@xl:h-12" } }
337
+ ],
338
+ defaultVariants: { layout: "auto", density: "comfortable", sticky: false, striped: false, typed: false }
320
339
  });
340
+ var NO_CLASS = { 2: "", 3: "", 4: "", 5: "", 6: "", 7: "" };
341
+ var LEAVES = {
342
+ auto: { 2: "@xl:@max-2xl:hidden!", 3: "@max-3xl:hidden!", 4: "@max-4xl:hidden!", 5: "@max-5xl:hidden!", 6: "@max-6xl:hidden!", 7: "@max-7xl:hidden!" },
343
+ table: { 2: "@max-2xl:hidden!", 3: "@max-3xl:hidden!", 4: "@max-4xl:hidden!", 5: "@max-5xl:hidden!", 6: "@max-6xl:hidden!", 7: "@max-7xl:hidden!" },
344
+ cards: { 2: "", 3: "hidden!", 4: "hidden!", 5: "hidden!", 6: "hidden!", 7: "hidden!" }
345
+ };
346
+ var LEAVES_HEAD = {
347
+ auto: { 2: "@xl:@max-2xl:hidden!", 3: "@xl:@max-3xl:hidden!", 4: "@xl:@max-4xl:hidden!", 5: "@xl:@max-5xl:hidden!", 6: "@xl:@max-6xl:hidden!", 7: "@xl:@max-7xl:hidden!" },
348
+ table: LEAVES.table,
349
+ cards: NO_CLASS
350
+ };
351
+ var IN_DETAIL = {
352
+ auto: { 2: "hidden @xl:@max-2xl:grid", 3: "hidden @max-3xl:grid", 4: "hidden @max-4xl:grid", 5: "hidden @max-5xl:grid", 6: "hidden @max-6xl:grid", 7: "hidden @max-7xl:grid" },
353
+ table: { 2: "hidden @max-2xl:grid", 3: "hidden @max-3xl:grid", 4: "hidden @max-4xl:grid", 5: "hidden @max-5xl:grid", 6: "hidden @max-6xl:grid", 7: "hidden @max-7xl:grid" },
354
+ cards: { 2: "hidden", 3: "grid", 4: "grid", 5: "grid", 6: "grid", 7: "grid" }
355
+ };
356
+ var TOGGLE = {
357
+ auto: { 2: "hidden @xl:@max-2xl:inline-flex", 3: "hidden @max-3xl:inline-flex", 4: "hidden @max-4xl:inline-flex", 5: "hidden @max-5xl:inline-flex", 6: "hidden @max-6xl:inline-flex", 7: "hidden @max-7xl:inline-flex" },
358
+ table: { 2: "hidden @max-2xl:inline-flex", 3: "hidden @max-3xl:inline-flex", 4: "hidden @max-4xl:inline-flex", 5: "hidden @max-5xl:inline-flex", 6: "hidden @max-6xl:inline-flex", 7: "hidden @max-7xl:inline-flex" },
359
+ cards: { 2: "hidden", 3: "inline-flex", 4: "inline-flex", 5: "inline-flex", 6: "inline-flex", 7: "inline-flex" }
360
+ };
361
+ var PANEL = {
362
+ auto: { 2: "hidden @xl:@max-2xl:block", 3: "hidden @max-3xl:block", 4: "hidden @max-4xl:block", 5: "hidden @max-5xl:block", 6: "hidden @max-6xl:block", 7: "hidden @max-7xl:block" },
363
+ table: { 2: "hidden @max-2xl:block", 3: "hidden @max-3xl:block", 4: "hidden @max-4xl:block", 5: "hidden @max-5xl:block", 6: "hidden @max-6xl:block", 7: "hidden @max-7xl:block" },
364
+ cards: { 2: "hidden", 3: "block", 4: "block", 5: "block", 6: "block", 7: "block" }
365
+ };
366
+ var TYPE_CELL = {
367
+ title: { auto: "@xl:w-full @xl:max-w-0 @xl:min-w-44", table: "w-full max-w-0 min-w-44", cards: "" },
368
+ badge: { auto: "@xl:w-px", table: "w-px", cards: "" },
369
+ status: { auto: "@xl:w-px @xl:whitespace-nowrap", table: "w-px whitespace-nowrap", cards: "" },
370
+ number: { auto: "@xl:w-px @xl:whitespace-nowrap tabular-nums", table: "w-px whitespace-nowrap tabular-nums", cards: "tabular-nums" },
371
+ date: { auto: "@xl:w-px @xl:whitespace-nowrap tabular-nums", table: "w-px whitespace-nowrap tabular-nums", cards: "tabular-nums" },
372
+ link: { auto: "@xl:min-w-28 @xl:max-w-56", table: "min-w-28 max-w-56", cards: "" },
373
+ text: { auto: "@xl:min-w-36", table: "min-w-36", cards: "" }
374
+ };
375
+ var CLAMP = { 1: "line-clamp-1", 2: "line-clamp-2", 3: "line-clamp-3" };
321
376
  var dataTableLabels = {
322
377
  selectAll: "Select all rows",
323
378
  selectRow: (name) => `Select ${name}`,
@@ -336,6 +391,7 @@ var dataTableLabels = {
336
391
  rangeFrom: (header) => `${header} from`,
337
392
  rangeTo: (header) => `${header} to`,
338
393
  columns: "Columns",
394
+ showMore: (name) => `Show more about ${name}`,
339
395
  export: "Export CSV"
340
396
  };
341
397
  var DataTableContext = React.createContext(null);
@@ -344,6 +400,10 @@ function useDataTable() {
344
400
  if (!context) throw new Error("useDataTable: call it inside a DataTable (its `toolbar`, `footer` or a cell)");
345
401
  return context;
346
402
  }
403
+ var TRUNCATE = { auto: "@xl:truncate", table: "truncate", cards: "" };
404
+ var MIN = { auto: "@xl:min-w-(--dt-min)!", table: "min-w-(--dt-min)!", cards: "" };
405
+ var MAX = { auto: "@xl:max-w-(--dt-max)!", table: "max-w-(--dt-max)!", cards: "" };
406
+ var SHADOWS = "[background:linear-gradient(to_right,var(--kz-surface-1),transparent)_left/24px_100%_no-repeat_local,linear-gradient(to_left,var(--kz-surface-1),transparent)_right/24px_100%_no-repeat_local,linear-gradient(to_right,rgb(0_0_0/0.18),transparent)_left/10px_100%_no-repeat_scroll,linear-gradient(to_left,rgb(0_0_0/0.18),transparent)_right/10px_100%_no-repeat_scroll]";
347
407
  var ALIGN = { start: "text-left", center: "text-center", end: "text-right" };
348
408
  var NONE = [];
349
409
  var NO_RECORD = {};
@@ -396,6 +456,10 @@ function DataTable({
396
456
  empty: empty2,
397
457
  layout = "auto",
398
458
  density,
459
+ stickyHeader,
460
+ striped,
461
+ scroll,
462
+ stickyFirstColumn,
399
463
  virtualized,
400
464
  toolbar,
401
465
  footer,
@@ -404,7 +468,9 @@ function DataTable({
404
468
  }) {
405
469
  const labels = { ...dataTableLabels, ...given };
406
470
  const effectiveLayout = virtualized ? "table" : layout;
407
- const s2 = dataTable({ layout: effectiveLayout, density });
471
+ const L = effectiveLayout;
472
+ const typed = columns.some((c) => c.type || c.priority);
473
+ const s2 = dataTable({ layout: L, density, sticky: Boolean(stickyHeader), striped: Boolean(striped), typed });
408
474
  const [sort, setSort] = useControllable(sortProp, defaultSort, onSortChange);
409
475
  const [filters, setFiltersState] = useControllable(filtersProp, defaultFilters, onFiltersChange);
410
476
  const [selected, setSelected] = useControllable(selectedProp, defaultSelected, onSelectedChange);
@@ -437,6 +503,10 @@ function DataTable({
437
503
  announce({ pageSize: next, page: 1 });
438
504
  };
439
505
  const hasExpander = Boolean(renderDetail || getSubRows);
506
+ const priorityOf = (column) => column.priority && column.priority > 1 ? column.priority : 1;
507
+ const maxPriority = columns.reduce((max, column) => priorityOf(column) > max ? priorityOf(column) : max, 1);
508
+ const inlineToggle = maxPriority > 1 && !hasExpander;
509
+ const detailed = Boolean(renderDetail) || inlineToggle;
440
510
  const defs = React.useMemo(
441
511
  () => columns.map((column) => ({
442
512
  id: column.id,
@@ -455,6 +525,8 @@ function DataTable({
455
525
  })),
456
526
  [columns, resizing]
457
527
  );
528
+ const firstId = columns[0]?.id;
529
+ const pinned = stickyFirstColumn && firstId && !pinning?.start?.length ? { ...pinning, start: [firstId] } : pinning;
458
530
  const pageIndex = Math.max(0, page - 1);
459
531
  const table = useTable({
460
532
  features,
@@ -462,7 +534,7 @@ function DataTable({
462
534
  data: rows,
463
535
  getRowId: (row) => getRowId(row),
464
536
  getSubRows: getSubRows ? (row) => getSubRows(row) : void 0,
465
- getRowCanExpand: renderDetail ? () => true : void 0,
537
+ getRowCanExpand: detailed ? () => true : void 0,
466
538
  state: {
467
539
  sorting: sort ? [{ id: sort.column, desc: sort.direction === "descending" }] : [],
468
540
  columnFilters: Object.entries(filters).map(([id, value]) => ({ id, value })),
@@ -471,7 +543,7 @@ function DataTable({
471
543
  columnVisibility: { ...visibility },
472
544
  expanded: { ...expanded },
473
545
  pagination: { pageIndex, pageSize: paginated ? pageSize : Math.max(1, rows.length) },
474
- columnPinning: { start: [...pinning?.start ?? []], end: [...pinning?.end ?? []] },
546
+ columnPinning: { start: [...pinned?.start ?? []], end: [...pinned?.end ?? []] },
475
547
  columnSizing: sizing,
476
548
  ...resizeState ? { columnResizing: resizeState } : {}
477
549
  },
@@ -521,7 +593,8 @@ function DataTable({
521
593
  const someChosen = selectableIds.some((id) => chosen.has(id));
522
594
  const toggleAll = () => setSelected(allChosen ? selected.filter((id) => !selectableIds.includes(id)) : [.../* @__PURE__ */ new Set([...selected, ...selectableIds])]);
523
595
  const span = visibleColumns.length + (selectable ? 1 : 0) + (hasExpander ? 1 : 0);
524
- const fixedLayout = Boolean(resizing || pinning?.start?.length || pinning?.end?.length);
596
+ const fixedLayout = Boolean(resizing || pinned?.start?.length || pinned?.end?.length);
597
+ const sideways = scroll ?? (!typed && !stickyHeader);
525
598
  const nameOf = (row) => rowLabel ? rowLabel(row.original) : row.id;
526
599
  const scroller = React.useRef(null);
527
600
  const fallbacks = useLayoutFallbacks(virtualized?.height ?? 0, virtualized?.estimateRowHeight ?? 44);
@@ -546,11 +619,40 @@ function DataTable({
546
619
  };
547
620
  const width = (column) => fixedLayout || byId.get(column.id)?.width ? { width: column.getSize() } : void 0;
548
621
  const message = (content, alert) => /* @__PURE__ */ jsx2("tr", { role: "row", className: s2.tr({ className: "hover:bg-transparent" }), children: /* @__PURE__ */ jsx2("td", { role: "cell", colSpan: span, "data-card": "message", className: s2.td({ className: s2.message() }), children: alert ? /* @__PURE__ */ jsx2("div", { role: "alert", children: content }) : content }) });
549
- const cardOf = (column) => column?.card === "title" ? "title" : column?.card === "hidden" ? "hidden" : void 0;
622
+ const cardOf = (column) => column?.card === "title" || column && !column.card && column.type === "title" ? "title" : column?.card === "hidden" ? "hidden" : void 0;
623
+ const contentOf = (column, original) => {
624
+ if (column.cell) return column.cell(original);
625
+ const value = valueOf(column, original);
626
+ const text = column.type === "date" && value instanceof Date ? value.toISOString().slice(0, 10) : display(value);
627
+ const cut = TRUNCATE[L];
628
+ switch (column.type) {
629
+ case "title": {
630
+ const sub = column.subtitle?.(original);
631
+ return /* @__PURE__ */ jsxs2("span", { className: "block min-w-0", children: [
632
+ /* @__PURE__ */ jsx2("span", { className: cn("block font-semibold text-fg", cut), title: text, children: text }),
633
+ sub ? /* @__PURE__ */ jsx2("span", { className: cn("block font-mono text-xs text-fg-tertiary", cut), children: sub }) : null
634
+ ] });
635
+ }
636
+ case "number":
637
+ return /* @__PURE__ */ jsxs2("span", { children: [
638
+ typeof value === "number" ? value.toLocaleString("en") : text,
639
+ column.unit ? /* @__PURE__ */ jsx2("span", { className: "ms-1 text-fg-secondary", children: column.unit }) : null
640
+ ] });
641
+ case "link": {
642
+ const href = column.href?.(original);
643
+ return href ? /* @__PURE__ */ jsx2("a", { href, title: text, className: cn("block text-fg-accent underline-offset-2 hover:underline", cut), children: text }) : /* @__PURE__ */ jsx2("span", { className: cn("block", cut), title: text, children: text });
644
+ }
645
+ default: {
646
+ const lines = column.clamp ?? (column.type === "text" ? 2 : void 0);
647
+ if (!column.nowrap && !lines) return text;
648
+ return /* @__PURE__ */ jsx2("span", { className: column.nowrap ? cn("block", cut || "truncate") : CLAMP[lines], title: text, children: text });
649
+ }
650
+ }
651
+ };
550
652
  const dataCell = (row, cell, first) => {
551
653
  const column = byId.get(cell.column.id);
552
654
  if (!column) return null;
553
- const { style, ...pinned } = pin(cell.column);
655
+ const { style, ...pinnedProps } = pin(cell.column);
554
656
  let content;
555
657
  if (row.getIsGrouped()) {
556
658
  const leaves = row.getLeafRows().filter((r) => !r.getIsGrouped()).map((r) => r.original);
@@ -561,16 +663,48 @@ function DataTable({
561
663
  labels.group(value, leaves.length)
562
664
  ] });
563
665
  } else content = column.aggregate ? column.aggregate(leaves) : null;
564
- } else content = column.cell ? column.cell(row.original) : display(valueOf(column, row.original));
666
+ } else {
667
+ content = contentOf(column, row.original);
668
+ if (first && inlineToggle) {
669
+ content = /* @__PURE__ */ jsxs2("span", { className: "flex min-w-0 items-center gap-1", children: [
670
+ /* @__PURE__ */ jsx2(
671
+ "button",
672
+ {
673
+ type: "button",
674
+ className: s2.toggle({ className: TOGGLE[L][maxPriority] }),
675
+ "aria-expanded": row.getIsExpanded(),
676
+ "aria-label": labels.showMore(nameOf(row)),
677
+ onClick: row.getToggleExpandedHandler(),
678
+ children: /* @__PURE__ */ jsx2(ChevronGlyph, { direction: "right", className: "h-4 w-4" })
679
+ }
680
+ ),
681
+ /* @__PURE__ */ jsx2("span", { className: "min-w-0 flex-1", children: content })
682
+ ] });
683
+ }
684
+ }
685
+ const level = priorityOf(column);
686
+ const type = column.type;
687
+ const cellClass = [
688
+ ALIGN[column.align ?? (type === "number" ? "end" : "start")],
689
+ type ? TYPE_CELL[type][L] : "",
690
+ level > 1 ? LEAVES[L][level] : "",
691
+ (type || column.priority) && column.minWidth !== void 0 ? MIN[L] : "",
692
+ column.maxWidth !== void 0 ? MAX[L] : ""
693
+ ].join(" ");
565
694
  return /* @__PURE__ */ jsx2(
566
695
  "td",
567
696
  {
568
697
  role: "cell",
569
698
  "data-label": column.header,
570
699
  "data-card": row.getIsGrouped() ? cell.column.id === row.groupingColumnId ? "title" : column.aggregate ? void 0 : "hidden" : cardOf(column),
571
- ...pinned,
572
- className: s2.td({ className: ALIGN[column.align ?? "start"] }),
573
- style: { ...style, ...first && row.depth > 0 && !row.getIsGrouped() ? { paddingInlineStart: `${1 + row.depth * 1.25}rem` } : {} },
700
+ ...pinnedProps,
701
+ className: s2.td({ className: cellClass }),
702
+ style: {
703
+ ...style,
704
+ ...column.minWidth !== void 0 ? { ["--dt-min"]: `${column.minWidth}px` } : {},
705
+ ...column.maxWidth !== void 0 ? { ["--dt-max"]: `${column.maxWidth}px` } : {},
706
+ ...first && row.depth > 0 && !row.getIsGrouped() ? { paddingInlineStart: `${1 + row.depth * 1.25}rem` } : {}
707
+ },
574
708
  children: content
575
709
  },
576
710
  cell.id
@@ -580,6 +714,7 @@ function DataTable({
580
714
  const isChosen = chosen.has(row.id);
581
715
  const grouped = row.getIsGrouped();
582
716
  const canExpand = !grouped && (renderDetail ? true : row.getCanExpand());
717
+ const showDetail = detailed && !grouped && row.getIsExpanded() && !virtualized;
583
718
  const cells = row.getVisibleCells();
584
719
  const main = /* @__PURE__ */ jsxs2(
585
720
  "tr",
@@ -596,13 +731,22 @@ function DataTable({
596
731
  hasExpander && /* @__PURE__ */ jsx2("td", { role: "cell", className: s2.td({ className: s2.expander() }), children: canExpand ? /* @__PURE__ */ jsx2("button", { type: "button", className: s2.toggle(), "aria-expanded": row.getIsExpanded(), "aria-label": labels.expandRow(nameOf(row)), onClick: row.getToggleExpandedHandler(), children: /* @__PURE__ */ jsx2(ChevronGlyph, { direction: "right", className: "h-4 w-4" }) }) : null }),
597
732
  cells.map((cell, i) => dataCell(row, cell, i === 0))
598
733
  ]
599
- },
600
- row.id
734
+ }
601
735
  );
602
- if (!renderDetail || grouped || !row.getIsExpanded() || virtualized) return main;
736
+ if (!showDetail) return /* @__PURE__ */ jsx2(React.Fragment, { children: main }, row.id);
737
+ const left = visibleColumns.filter((c) => priorityOf(byId.get(c.id)) > 1);
603
738
  return /* @__PURE__ */ jsxs2(React.Fragment, { children: [
604
739
  main,
605
- /* @__PURE__ */ jsx2("tr", { role: "row", className: s2.tr({ className: "hover:bg-transparent" }), children: /* @__PURE__ */ jsx2("td", { role: "cell", colSpan: span, "data-card": "message", className: s2.td({ className: s2.detail() }), children: renderDetail(row.original) }) })
740
+ /* @__PURE__ */ jsx2("tr", { role: "row", className: s2.tr({ className: "hover:bg-transparent" }), children: /* @__PURE__ */ jsxs2("td", { role: "cell", colSpan: span, "data-card": "message", className: s2.td({ className: renderDetail ? s2.detail() : "bg-surface-0 p-0 text-left" }), children: [
741
+ left.length > 0 ? /* @__PURE__ */ jsx2("dl", { className: cn("m-0 grid gap-3 text-left", renderDetail ? "mb-4" : cn("px-4 py-4", PANEL[L][maxPriority])), children: left.map((c) => {
742
+ const column = byId.get(c.id);
743
+ return /* @__PURE__ */ jsxs2("div", { className: cn(IN_DETAIL[L][priorityOf(column)], "grid-cols-[minmax(5rem,8rem)_minmax(0,1fr)] items-baseline gap-3"), children: [
744
+ /* @__PURE__ */ jsx2("dt", { className: "text-xs font-semibold text-fg-secondary", children: column.header }),
745
+ /* @__PURE__ */ jsx2("dd", { className: "m-0 min-w-0 text-sm text-fg", children: contentOf(column, row.original) })
746
+ ] }, c.id);
747
+ }) }) : null,
748
+ renderDetail ? renderDetail(row.original) : null
749
+ ] }) })
606
750
  ] }, row.id);
607
751
  };
608
752
  let body;
@@ -646,7 +790,8 @@ function DataTable({
646
790
  if (!column) return null;
647
791
  const sorted = header.column.getIsSorted();
648
792
  const direction = sorted === "asc" ? "ascending" : sorted === "desc" ? "descending" : void 0;
649
- const { style, ...pinned } = pin(header.column);
793
+ const { style, ...pinnedProps } = pin(header.column);
794
+ const level = priorityOf(column);
650
795
  return /* @__PURE__ */ jsxs2(
651
796
  "th",
652
797
  {
@@ -654,9 +799,9 @@ function DataTable({
654
799
  scope: "col",
655
800
  "aria-sort": column.sortable ? direction ?? "none" : void 0,
656
801
  "data-keep": column.sortable ? "" : void 0,
657
- ...pinned,
658
- className: s2.th({ className: ALIGN[column.align ?? "start"] }),
659
- style: { ...style, ...width(header.column) },
802
+ ...pinnedProps,
803
+ className: s2.th({ className: cn(ALIGN[column.align ?? (column.type === "number" ? "end" : "start")], level > 1 && LEAVES_HEAD[L][level]) }),
804
+ style: { ...style, ...typeof stickyHeader === "number" ? { ["--dt-top"]: `${stickyHeader}px` } : {}, ...width(header.column) },
660
805
  children: [
661
806
  column.sortable ? /* @__PURE__ */ jsxs2("button", { type: "button", "aria-pressed": Boolean(direction), className: s2.sortButton(), onClick: () => header.column.toggleSorting(void 0, false), children: [
662
807
  column.header,
@@ -727,7 +872,7 @@ function DataTable({
727
872
  children: tableElement
728
873
  }
729
874
  )
730
- ) : /* @__PURE__ */ jsx2("div", { className: s2.scroller(), children: tableElement }),
875
+ ) : /* @__PURE__ */ jsx2("div", { className: s2.scroller({ className: sideways ? scroll ? SHADOWS : void 0 : "overflow-x-clip" }), children: tableElement }),
731
876
  pagination2 || footer ? /* @__PURE__ */ jsxs2("div", { className: s2.footer({ className: "flex flex-col gap-3" }), children: [
732
877
  pagination2,
733
878
  footer