@pipelex/mthds-form 0.15.0 → 0.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.
@@ -2155,6 +2155,7 @@ function ResultEnvProvider({
2155
2155
  resolveShareUrl,
2156
2156
  proseImages,
2157
2157
  tableColumns,
2158
+ tableLayout,
2158
2159
  saveFiles,
2159
2160
  downloads,
2160
2161
  children
@@ -2166,15 +2167,26 @@ function ResultEnvProvider({
2166
2167
  resolveShareUrl,
2167
2168
  proseImages,
2168
2169
  tableColumns,
2170
+ tableLayout,
2169
2171
  saveFiles,
2170
2172
  downloads
2171
2173
  }),
2172
- [resolveUrl, resolveUrls, resolveShareUrl, proseImages, tableColumns, saveFiles, downloads]
2174
+ [
2175
+ resolveUrl,
2176
+ resolveUrls,
2177
+ resolveShareUrl,
2178
+ proseImages,
2179
+ tableColumns,
2180
+ tableLayout,
2181
+ saveFiles,
2182
+ downloads
2183
+ ]
2173
2184
  );
2174
2185
  return /* @__PURE__ */ jsx(ResultEnvContext, { value: env, children });
2175
2186
  }
2176
- function DownloadDisplayOverride({
2187
+ function PanelEnvOverride({
2177
2188
  downloads,
2189
+ tableLayout,
2178
2190
  children
2179
2191
  }) {
2180
2192
  const env = use(ResultEnvContext);
@@ -2182,18 +2194,22 @@ function DownloadDisplayOverride({
2182
2194
  const files = downloads?.files;
2183
2195
  const pdf = downloads?.pdf;
2184
2196
  const askFileName = downloads?.askFileName;
2185
- const merged = useMemo(
2186
- () => result === void 0 && files === void 0 && pdf === void 0 && askFileName === void 0 ? env : {
2197
+ const merged = useMemo(() => {
2198
+ const ownDownloads = result !== void 0 || files !== void 0 || pdf !== void 0 || askFileName !== void 0;
2199
+ if (!ownDownloads && tableLayout === void 0) return env;
2200
+ return {
2187
2201
  ...env,
2188
- downloads: {
2189
- result: result ?? env.downloads?.result,
2190
- files: files ?? env.downloads?.files,
2191
- pdf: pdf ?? env.downloads?.pdf,
2192
- askFileName: askFileName ?? env.downloads?.askFileName
2193
- }
2194
- },
2195
- [env, result, files, pdf, askFileName]
2196
- );
2202
+ ...tableLayout === void 0 ? {} : { tableLayout },
2203
+ ...ownDownloads ? {
2204
+ downloads: {
2205
+ result: result ?? env.downloads?.result,
2206
+ files: files ?? env.downloads?.files,
2207
+ pdf: pdf ?? env.downloads?.pdf,
2208
+ askFileName: askFileName ?? env.downloads?.askFileName
2209
+ }
2210
+ } : {}
2211
+ };
2212
+ }, [env, result, files, pdf, askFileName, tableLayout]);
2197
2213
  return /* @__PURE__ */ jsx(ResultEnvContext, { value: merged, children });
2198
2214
  }
2199
2215
  function useResolvedUrl(url) {
@@ -2255,6 +2271,9 @@ function useTableColumns() {
2255
2271
  if (budget === void 0 || Number.isNaN(budget)) return DEFAULT_TABLE_COLUMNS;
2256
2272
  return Math.max(1, Math.floor(budget));
2257
2273
  }
2274
+ function useTableLayout() {
2275
+ return use(ResultEnvContext).tableLayout ?? "scroll";
2276
+ }
2258
2277
  var ResultLocationContext = createContext(void 0);
2259
2278
  function useResultLocation() {
2260
2279
  return use(ResultLocationContext);
@@ -4046,6 +4065,44 @@ function isInlineColumn(field) {
4046
4065
  }
4047
4066
  var CELL_LENGTH_LIMIT = 80;
4048
4067
  var EXPAND_COLUMN = "2rem";
4068
+ var STACK_NEVER_UP_TO = 3;
4069
+ var STACK_ALWAYS_BEYOND = 6;
4070
+ function stackingFor(columns) {
4071
+ if (columns <= STACK_NEVER_UP_TO) return "never";
4072
+ return columns > STACK_ALWAYS_BEYOND ? "always" : "narrow";
4073
+ }
4074
+ var STACKED = {
4075
+ narrow: {
4076
+ table: "@max-[640px]:block",
4077
+ head: "@max-[640px]:hidden",
4078
+ body: "@max-[640px]:block",
4079
+ row: "@max-[640px]:relative @max-[640px]:block @max-[640px]:px-3 @max-[640px]:py-2",
4080
+ rowWithToggle: "@max-[640px]:pl-8",
4081
+ toggle: "@max-[640px]:absolute @max-[640px]:top-2 @max-[640px]:left-1 @max-[640px]:p-0",
4082
+ cell: "@max-[640px]:grid @max-[640px]:grid-cols-[minmax(0,min(40%,10rem))_minmax(0,1fr)] @max-[640px]:items-baseline @max-[640px]:gap-x-3 @max-[640px]:px-0 @max-[640px]:py-0.5",
4083
+ label: "@max-[640px]:block",
4084
+ detail: "@max-[640px]:block",
4085
+ detailInner: "@max-[640px]:px-0 @max-[640px]:py-1"
4086
+ },
4087
+ always: {
4088
+ table: "block",
4089
+ head: "hidden",
4090
+ body: "block",
4091
+ row: "relative block px-3 py-2",
4092
+ rowWithToggle: "pl-8",
4093
+ toggle: "absolute top-2 left-1 p-0",
4094
+ cell: "grid grid-cols-[minmax(0,min(40%,10rem))_minmax(0,1fr)] items-baseline gap-x-3 px-0 py-0.5",
4095
+ label: "block",
4096
+ detail: "block",
4097
+ detailInner: "px-0 py-1"
4098
+ }
4099
+ };
4100
+ var FIT_CELL = "min-w-[min(8ch,14cqw)] wrap-anywhere [&>span]:whitespace-normal";
4101
+ var FIT_NAME = "min-w-[min(16ch,28cqw)] wrap-anywhere [&>span]:whitespace-normal";
4102
+ function clampsInStack(column, name) {
4103
+ if (column === name || fitsACellWhole(column)) return false;
4104
+ return column.kind !== "list" && column.kind !== "document" && column.kind !== "image";
4105
+ }
4049
4106
  function fitsACellWhole(field) {
4050
4107
  switch (field.kind) {
4051
4108
  case "boolean":
@@ -4072,21 +4129,25 @@ function ObjectTable({
4072
4129
  const s = useFieldStrings();
4073
4130
  const presentation = useFieldPresentation();
4074
4131
  const budget = useTableColumns();
4132
+ const layout = useTableLayout();
4075
4133
  const scrollerRef = useRef(null);
4076
4134
  const [expanded, setExpanded] = useState(() => /* @__PURE__ */ new Set());
4077
4135
  const [viewportWidth, setViewportWidth] = useState();
4078
4136
  const { shown, hidden, name } = chooseColumns(columns, budget);
4079
4137
  const canExpand = hidden > 0 || shown.some((column) => column !== name && !fitsACellWhole(column));
4138
+ const fits = layout === "stack";
4139
+ const stacking = fits ? stackingFor(shown.length) : "never";
4140
+ const stacked = stacking === "never" ? void 0 : STACKED[stacking];
4080
4141
  useEffect(() => {
4081
4142
  const scroller = scrollerRef.current;
4082
- if (!scroller) return;
4143
+ if (!scroller || fits) return;
4083
4144
  const measure = () => setViewportWidth(scroller.clientWidth);
4084
4145
  measure();
4085
4146
  if (typeof ResizeObserver === "undefined") return;
4086
4147
  const observer = new ResizeObserver(measure);
4087
4148
  observer.observe(scroller);
4088
4149
  return () => observer.disconnect();
4089
- }, []);
4150
+ }, [fits]);
4090
4151
  const toggle = (index) => setExpanded((current) => {
4091
4152
  const next = new Set(current);
4092
4153
  if (!next.delete(index)) next.add(index);
@@ -4110,23 +4171,32 @@ function ObjectTable({
4110
4171
  // of them with the same name - which is what a result rendered twice, or a
4111
4172
  // structure holding two lists, produces - is `landmark-unique`. A group is
4112
4173
  // named without claiming to be a section of the page.
4174
+ //
4175
+ // Under `tableLayout="stack"` the box is the SIZE CONTAINER its stacking
4176
+ // rules query, so the table answers to its own width rather than the
4177
+ // viewport's, and it takes no focus: nothing scrolls, so the stop would have
4178
+ // nothing to do. It still clips to its rounded border, and should anything
4179
+ // ever overflow it, the a11y gate names the scroll a keyboard cannot reach.
4113
4180
  /* @__PURE__ */ jsx(
4114
4181
  "div",
4115
4182
  {
4116
4183
  ref: scrollerRef,
4117
4184
  role: "group",
4118
4185
  "aria-label": label,
4119
- tabIndex: 0,
4120
- className: "overflow-x-auto rounded-lg border border-border",
4121
- children: /* @__PURE__ */ jsxs("table", { className: "w-full min-w-full border-collapse text-[13px]", children: [
4122
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border bg-muted text-left", children: [
4186
+ ...fits ? {} : { tabIndex: 0 },
4187
+ className: cn("overflow-x-auto rounded-lg border border-border", fits && "@container"),
4188
+ children: /* @__PURE__ */ jsxs("table", { className: cn("w-full min-w-full border-collapse text-[13px]", stacked?.table), children: [
4189
+ /* @__PURE__ */ jsx("thead", { className: stacked?.head, children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border bg-muted text-left", children: [
4123
4190
  canExpand && /* @__PURE__ */ jsx("th", { scope: "col", style: { width: EXPAND_COLUMN }, className: "px-1", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: s.rowDetailsColumn }) }),
4124
4191
  shown.map((column) => /* @__PURE__ */ jsx(
4125
4192
  "th",
4126
4193
  {
4127
4194
  scope: "col",
4128
4195
  className: cn(
4129
- "whitespace-nowrap px-3 py-2 text-[12px] font-semibold text-foreground",
4196
+ // One line, or wrapped anywhere so a header is never what
4197
+ // widens a table that has to fit its box. See `FIT_CELL`.
4198
+ fits ? "wrap-anywhere" : "whitespace-nowrap",
4199
+ "px-3 py-2 text-[12px] font-semibold text-foreground",
4130
4200
  presentation === "app" ? void 0 : "font-mono"
4131
4201
  ),
4132
4202
  children: /* @__PURE__ */ jsx(Described, { description: column.description, children: fieldLabel(column.title, column.name, presentation) })
@@ -4134,50 +4204,85 @@ function ObjectTable({
4134
4204
  column.name
4135
4205
  ))
4136
4206
  ] }) }),
4137
- /* @__PURE__ */ jsx("tbody", { children: items.map((item, index) => {
4207
+ /* @__PURE__ */ jsx("tbody", { className: stacked?.body, children: items.map((item, index) => {
4138
4208
  const isOpen = canExpand && expanded.has(index);
4139
- return /* @__PURE__ */ jsx(ResultAt, { segment: String(index), children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border/60 last:border-b-0", children: [
4140
- canExpand && /* @__PURE__ */ jsx("td", { className: "px-1 pt-px align-top", children: /* @__PURE__ */ jsx(
4141
- "button",
4142
- {
4143
- type: "button",
4144
- onClick: () => toggle(index),
4145
- "aria-expanded": isOpen,
4146
- "aria-label": s.toggleRowDetails(index + 1),
4147
- className: "mt-0.5 flex size-6 items-center justify-center rounded text-muted-foreground hover:bg-card hover:text-foreground focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-1",
4148
- children: isOpen ? /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": true, className: "size-3.5" }) : /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": true, className: "size-3.5" })
4149
- }
4150
- ) }),
4151
- isOpen ? /* @__PURE__ */ jsx("td", { colSpan: shown.length, className: "bg-card/40 p-0", children: /* @__PURE__ */ jsx(
4152
- "div",
4153
- {
4154
- className: "sticky left-0 px-3.5 py-3",
4155
- ...viewportWidth ? {
4156
- style: {
4157
- // Minus the chevron column: this cell starts
4158
- // after it, so pinning it to the FULL visible
4159
- // width pushes its right edge that far past the
4160
- // scroller. See `EXPAND_COLUMN`.
4161
- width: canExpand ? `calc(${viewportWidth}px - ${EXPAND_COLUMN})` : viewportWidth
4209
+ return /* @__PURE__ */ jsx(ResultAt, { segment: String(index), children: /* @__PURE__ */ jsxs(
4210
+ "tr",
4211
+ {
4212
+ className: cn(
4213
+ "border-b border-border/60 last:border-b-0",
4214
+ stacked?.row,
4215
+ canExpand && stacked?.rowWithToggle
4216
+ ),
4217
+ children: [
4218
+ canExpand && /* @__PURE__ */ jsx("td", { className: cn("px-1 pt-px align-top", stacked?.toggle), children: /* @__PURE__ */ jsx(
4219
+ "button",
4220
+ {
4221
+ type: "button",
4222
+ onClick: () => toggle(index),
4223
+ "aria-expanded": isOpen,
4224
+ "aria-label": s.toggleRowDetails(index + 1),
4225
+ className: "mt-0.5 flex size-6 items-center justify-center rounded text-muted-foreground hover:bg-card hover:text-foreground focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-1",
4226
+ children: isOpen ? /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": true, className: "size-3.5" }) : /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": true, className: "size-3.5" })
4162
4227
  }
4163
- } : {},
4164
- children: /* @__PURE__ */ jsx(ResultField, { field: element, value: item, hideLabel: true })
4165
- }
4166
- ) }) : shown.map((column) => {
4167
- const cell = typeof item === "object" && item !== null ? ownProp(item, column.name) : void 0;
4168
- return /* @__PURE__ */ jsx("td", { className: "px-3 py-1.5 align-top", children: /* @__PURE__ */ jsx(
4169
- "div",
4170
- {
4171
- className: cn(
4172
- "max-w-[44ch]",
4173
- column === name ? "max-w-none min-w-[16ch] wrap-anywhere [&>span]:whitespace-normal" : column.kind === "list" && isInlineColumn(column) ? "min-w-[16ch]" : "truncate"
4174
- ),
4175
- ...typeof cell === "string" || typeof cell === "number" ? { title: cellTitle(column, cell, presentation) } : {},
4176
- children: /* @__PURE__ */ jsx(ResultAt, { segment: column.name, children: /* @__PURE__ */ jsx(LeafValue, { field: column, value: cell, compact: true }) })
4177
- }
4178
- ) }, column.name);
4179
- })
4180
- ] }) }, index);
4228
+ ) }),
4229
+ isOpen ? /* @__PURE__ */ jsx("td", { colSpan: shown.length, className: cn("bg-card/40 p-0", stacked?.detail), children: /* @__PURE__ */ jsx(
4230
+ "div",
4231
+ {
4232
+ className: cn(
4233
+ !fits && "sticky left-0",
4234
+ "px-3.5 py-3",
4235
+ stacked?.detailInner
4236
+ ),
4237
+ ...viewportWidth && !fits ? {
4238
+ style: {
4239
+ // Minus the chevron column: this cell starts
4240
+ // after it, so pinning it to the FULL visible
4241
+ // width pushes its right edge that far past the
4242
+ // scroller. See `EXPAND_COLUMN`.
4243
+ width: canExpand ? `calc(${viewportWidth}px - ${EXPAND_COLUMN})` : viewportWidth
4244
+ }
4245
+ } : {},
4246
+ children: /* @__PURE__ */ jsx(ResultField, { field: element, value: item, hideLabel: true })
4247
+ }
4248
+ ) }) : shown.map((column) => {
4249
+ const cell = typeof item === "object" && item !== null ? ownProp(item, column.name) : void 0;
4250
+ const columnLabel = fieldLabel(column.title, column.name, presentation);
4251
+ return /* @__PURE__ */ jsxs(
4252
+ "td",
4253
+ {
4254
+ className: cn("px-3 py-1.5 align-top", stacked?.cell),
4255
+ children: [
4256
+ stacked && /* @__PURE__ */ jsx(
4257
+ "span",
4258
+ {
4259
+ className: cn(
4260
+ "hidden text-[12px] text-muted-foreground wrap-anywhere",
4261
+ presentation === "app" ? void 0 : "font-mono",
4262
+ stacked.label
4263
+ ),
4264
+ children: canExpand ? columnLabel : /* @__PURE__ */ jsx(Described, { description: column.description, children: columnLabel })
4265
+ }
4266
+ ),
4267
+ /* @__PURE__ */ jsx(
4268
+ "div",
4269
+ {
4270
+ className: fits ? column === name ? FIT_NAME : cn(FIT_CELL, clampsInStack(column, name) && "line-clamp-3") : cn(
4271
+ "max-w-[44ch]",
4272
+ column === name ? "max-w-none min-w-[16ch] wrap-anywhere [&>span]:whitespace-normal" : column.kind === "list" && isInlineColumn(column) ? "min-w-[16ch]" : "truncate"
4273
+ ),
4274
+ ...typeof cell === "string" || typeof cell === "number" ? { title: cellTitle(column, cell, presentation) } : {},
4275
+ children: /* @__PURE__ */ jsx(ResultAt, { segment: column.name, children: /* @__PURE__ */ jsx(LeafValue, { field: column, value: cell, compact: true }) })
4276
+ }
4277
+ )
4278
+ ]
4279
+ },
4280
+ column.name
4281
+ );
4282
+ })
4283
+ ]
4284
+ }
4285
+ ) }, index);
4181
4286
  }) })
4182
4287
  ] })
4183
4288
  }
@@ -4513,8 +4618,8 @@ function resultKey(field, baseName, value) {
4513
4618
  return void 0;
4514
4619
  }
4515
4620
  }
4516
- function StuffViewer({ downloads, ...props }) {
4517
- return /* @__PURE__ */ jsx(DownloadDisplayOverride, { downloads, children: /* @__PURE__ */ jsx(StuffPanel, { ...props }) });
4621
+ function StuffViewer({ downloads, tableLayout, ...props }) {
4622
+ return /* @__PURE__ */ jsx(PanelEnvOverride, { downloads, tableLayout, children: /* @__PURE__ */ jsx(StuffPanel, { ...props }) });
4518
4623
  }
4519
4624
  function StuffPanel({
4520
4625
  field,
@@ -4680,5 +4785,5 @@ function pdfFileName(field, value, baseName) {
4680
4785
  }
4681
4786
 
4682
4787
  export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, EN_FIELD_STRINGS, EnumField, FIELD_STRINGS_BY_LOCALE, FR_FIELD_STRINGS, FieldDomIdProvider, FieldPresentationProvider, FieldRenderer, FieldShell, FieldStringsProvider, HtmlPreview, ImageField, JsonView, ListField, Markdown, MethodForm, NumberField, ObjectField, OptionalToggle, PdfDownloadButton, ProseField, ResultEnvProvider, ResultField, ResultRoot, SUPPORTED_LOCALES, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, StuffViewer, Switch, TextField, ToggleGroup, ToggleGroupItem, UnknownField, cn, fieldControlClass, fieldStringsFor, humanizeFieldName, renderHtmlPdf, resolveHtmlStorageUrls, saveInBrowser, useFieldDomId, useFieldPresentation, useFieldStrings, useMethodForm, useProseImages, useResolveShareUrl, useResolveUrl, useResolvedUrl };
4683
- //# sourceMappingURL=chunk-AMBQ6PQR.js.map
4684
- //# sourceMappingURL=chunk-AMBQ6PQR.js.map
4788
+ //# sourceMappingURL=chunk-LXYSQWZG.js.map
4789
+ //# sourceMappingURL=chunk-LXYSQWZG.js.map