@reopt-ai/opt-ui 1.8.0 → 1.10.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.
Files changed (61) hide show
  1. package/COMPONENT_CATALOG.md +222 -34
  2. package/README.md +70 -6
  3. package/dist/core/index.cjs +170 -155
  4. package/dist/core/index.d.cts +3 -2
  5. package/dist/core/index.d.ts +3 -2
  6. package/dist/core/index.js +3 -3
  7. package/dist/docs/02-components/01-core.md +223 -18
  8. package/dist/docs/02-components/02-visuals.md +1 -1
  9. package/dist/docs/02-components/03-shells.md +17 -16
  10. package/dist/docs/02-components/04-surfaces.md +1 -1
  11. package/dist/docs/02-components/index.md +4 -4
  12. package/dist/{field-sidebar-Bqfp6dgg.cjs → field-sidebar-B6Ndr8HE.cjs} +304 -289
  13. package/dist/{field-sidebar-BYD_UIjG.js → field-sidebar-BXjvV7Cz.js} +25 -10
  14. package/dist/{field-sidebar-BYD_UIjG.js.map → field-sidebar-BXjvV7Cz.js.map} +1 -1
  15. package/dist/{field-sidebar-5KYVlpSs.d.ts → field-sidebar-DWfJAJlx.d.ts} +32 -3
  16. package/dist/{field-sidebar-5KYVlpSs.d.ts.map → field-sidebar-DWfJAJlx.d.ts.map} +1 -1
  17. package/dist/{field-sidebar-BP0iWkoV.d.cts → field-sidebar-nwcE9LC4.d.cts} +32 -3
  18. package/dist/{field-sidebar-BP0iWkoV.d.cts.map → field-sidebar-nwcE9LC4.d.cts.map} +1 -1
  19. package/dist/id-registry.cjs +98 -2
  20. package/dist/id-registry.d.cts.map +1 -1
  21. package/dist/id-registry.d.ts.map +1 -1
  22. package/dist/id-registry.js +98 -2
  23. package/dist/id-registry.js.map +1 -1
  24. package/dist/id-registry.json +202 -4
  25. package/dist/index.cjs +664 -643
  26. package/dist/index.d.cts +5 -4
  27. package/dist/index.d.cts.map +1 -1
  28. package/dist/index.d.ts +5 -4
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +10 -4
  31. package/dist/index.js.map +1 -1
  32. package/dist/{shader-surface-BUPZ0P-n.js → key-pad-menu-BQunU20e.js} +727 -19
  33. package/dist/key-pad-menu-BQunU20e.js.map +1 -0
  34. package/dist/{shader-surface-BfTP9c1Q.d.ts → key-pad-menu-BsgJMjD4.d.ts} +415 -3
  35. package/dist/key-pad-menu-BsgJMjD4.d.ts.map +1 -0
  36. package/dist/{shader-surface-Fav5K9UC.cjs → key-pad-menu-D5mQcS5v.cjs} +969 -201
  37. package/dist/{shader-surface-DRrcLrWf.d.cts → key-pad-menu-DfZbeXhG.d.cts} +415 -3
  38. package/dist/key-pad-menu-DfZbeXhG.d.cts.map +1 -0
  39. package/dist/meta.cjs +641 -0
  40. package/dist/meta.d.cts.map +1 -1
  41. package/dist/meta.d.ts.map +1 -1
  42. package/dist/meta.js +641 -0
  43. package/dist/meta.js.map +1 -1
  44. package/dist/shells/index.cjs +1 -1
  45. package/dist/shells/index.d.cts +1 -1
  46. package/dist/shells/index.d.ts +1 -1
  47. package/dist/shells/index.js +1 -1
  48. package/dist/{types-D0FlcYnM.d.ts → text-truncate-BGr7bB_8.d.cts} +110 -2
  49. package/dist/text-truncate-BGr7bB_8.d.cts.map +1 -0
  50. package/dist/{types-D0FlcYnM.d.cts → text-truncate-BGr7bB_8.d.ts} +110 -2
  51. package/dist/text-truncate-BGr7bB_8.d.ts.map +1 -0
  52. package/dist/{pagination-DjfxBcjX.cjs → text-truncate-CxdzAnhS.cjs} +418 -8
  53. package/dist/{pagination-DWbvACuN.js → text-truncate-j6X3z7Pv.js} +392 -12
  54. package/dist/text-truncate-j6X3z7Pv.js.map +1 -0
  55. package/package.json +3 -3
  56. package/dist/pagination-DWbvACuN.js.map +0 -1
  57. package/dist/shader-surface-BUPZ0P-n.js.map +0 -1
  58. package/dist/shader-surface-BfTP9c1Q.d.ts.map +0 -1
  59. package/dist/shader-surface-DRrcLrWf.d.cts.map +0 -1
  60. package/dist/types-D0FlcYnM.d.cts.map +0 -1
  61. package/dist/types-D0FlcYnM.d.ts.map +0 -1
@@ -346,7 +346,7 @@ const variantStyles$2 = {
346
346
  ghost: "text-text-secondary hover:bg-bg-subtle active:bg-bg-muted focus-visible:ring-offset-1",
347
347
  danger: "bg-danger text-danger-fg hover:bg-danger active:bg-danger focus-visible:ring-offset-2"
348
348
  };
349
- const sizeStyles$3 = {
349
+ const sizeStyles$4 = {
350
350
  sm: "h-[var(--opt-control-sm)] px-[var(--opt-control-padding-sm)] text-xs gap-[var(--opt-space-1-5)]",
351
351
  md: "h-[var(--opt-control-md)] px-[var(--opt-control-padding-md)] text-sm gap-[var(--opt-space-2)]",
352
352
  lg: "h-[var(--opt-control-lg)] px-[var(--opt-control-padding-lg)] text-base gap-[var(--opt-space-2)]"
@@ -357,7 +357,7 @@ function Button(_ref) {
357
357
  const isDisabled = disabled || loading;
358
358
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", _objectSpread2(_objectSpread2({
359
359
  type: "button",
360
- className: cn("inline-flex cursor-pointer items-center justify-center rounded-lg font-medium transition-colors", OPT_FOCUS_VISIBLE, variantStyles$2[variant], sizeStyles$3[size], isDisabled && "pointer-events-none cursor-not-allowed opacity-50", className),
360
+ className: cn("inline-flex cursor-pointer items-center justify-center rounded-lg font-medium transition-colors", OPT_FOCUS_VISIBLE, variantStyles$2[variant], sizeStyles$4[size], isDisabled && "pointer-events-none cursor-not-allowed opacity-50", className),
361
361
  disabled: isDisabled
362
362
  }, props), {}, {
363
363
  "aria-busy": loading ? true : ariaBusy,
@@ -466,7 +466,7 @@ const progressStroke = {
466
466
  error: "stroke-danger",
467
467
  info: "stroke-info"
468
468
  };
469
- const sizeStyles$2 = {
469
+ const sizeStyles$3 = {
470
470
  sm: "px-1.5 py-0.5 text-[10px]",
471
471
  md: "px-2 py-0.5 text-[11px]"
472
472
  };
@@ -478,7 +478,7 @@ function Badge(_ref) {
478
478
  const radius = (progressSize - 3) / 2;
479
479
  const circumference = 2 * Math.PI * radius;
480
480
  const offset = circumference - clampedProgress / 100 * circumference;
481
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", _objectSpread2(_objectSpread2({ className: cn(OPT_BADGE, variantStyles$1[variant], sizeStyles$2[size], className) }, props), {}, {
481
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", _objectSpread2(_objectSpread2({ className: cn(OPT_BADGE, variantStyles$1[variant], sizeStyles$3[size], className) }, props), {}, {
482
482
  "data-opt-id": "9GBGZ",
483
483
  "data-opt-slug": "badge.Badge",
484
484
  children: [
@@ -525,7 +525,7 @@ const _excluded$13 = [
525
525
  "aria-hidden",
526
526
  "aria-label"
527
527
  ];
528
- const sizeStyles$1 = {
528
+ const sizeStyles$2 = {
529
529
  sm: "h-4 w-4",
530
530
  md: "h-6 w-6",
531
531
  lg: "h-8 w-8"
@@ -535,7 +535,7 @@ function Spinner(_ref) {
535
535
  let { size = "md", className, role, "aria-hidden": ariaHidden, "aria-label": ariaLabel } = _ref, props = _objectWithoutProperties(_ref, _excluded$13);
536
536
  const isDecorative = ariaHidden === true || ariaHidden === "true";
537
537
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", _objectSpread2(_objectSpread2({
538
- className: cn("animate-spin text-current", sizeStyles$1[size], className),
538
+ className: cn("animate-spin text-current", sizeStyles$2[size], className),
539
539
  viewBox: "0 0 24 24",
540
540
  fill: "none",
541
541
  role: isDecorative ? void 0 : role !== null && role !== void 0 ? role : "status",
@@ -3554,7 +3554,7 @@ function setForwardedRef(ref, value) {
3554
3554
  if (typeof ref === "function") ref(value);
3555
3555
  else if (ref) ref.current = value;
3556
3556
  }
3557
- const sizeStyles = {
3557
+ const sizeStyles$1 = {
3558
3558
  sm: {
3559
3559
  box: "h-4 w-4",
3560
3560
  icon: "h-3 w-3",
@@ -3620,7 +3620,7 @@ const Checkbox = (0, react.forwardRef)((_ref, ref) => {
3620
3620
  const checkboxId = (_useInputId = useInputId(id, label, "checkbox")) !== null && _useInputId !== void 0 ? _useInputId : `checkbox-${fallbackId}`;
3621
3621
  const hintText = hint !== null && hint !== void 0 ? hint : description;
3622
3622
  const hasError = Boolean(error);
3623
- const styles = sizeStyles[size];
3623
+ const styles = sizeStyles$1[size];
3624
3624
  const isIndeterminate = Boolean(indeterminate);
3625
3625
  const inputRef = (0, react.useCallback)((node) => {
3626
3626
  if (node) node.indeterminate = isIndeterminate;
@@ -3867,6 +3867,386 @@ function Pagination(_ref) {
3867
3867
  }));
3868
3868
  }
3869
3869
  //#endregion
3870
+ //#region src/core/field-token.tsx
3871
+ /**
3872
+ * The glyph and palette slot each field type reads as. Exported so a product
3873
+ * can render the same legend the tokens imply.
3874
+ */
3875
+ const FIELD_TYPE_TOKENS = {
3876
+ string: {
3877
+ icon: lucide_react.Type,
3878
+ tone: 1,
3879
+ name: "String"
3880
+ },
3881
+ number: {
3882
+ icon: lucide_react.Hash,
3883
+ tone: 2,
3884
+ name: "Number"
3885
+ },
3886
+ boolean: {
3887
+ icon: lucide_react.ToggleLeft,
3888
+ tone: 3,
3889
+ name: "Boolean"
3890
+ },
3891
+ date: {
3892
+ icon: lucide_react.Calendar,
3893
+ tone: 4,
3894
+ name: "Date"
3895
+ },
3896
+ datetime: {
3897
+ icon: lucide_react.CalendarClock,
3898
+ tone: 4,
3899
+ name: "Date and time"
3900
+ },
3901
+ duration: {
3902
+ icon: lucide_react.Timer,
3903
+ tone: 5,
3904
+ name: "Duration"
3905
+ },
3906
+ percent: {
3907
+ icon: lucide_react.Percent,
3908
+ tone: 6,
3909
+ name: "Percent"
3910
+ },
3911
+ currency: {
3912
+ icon: lucide_react.CircleDollarSign,
3913
+ tone: 7,
3914
+ name: "Currency"
3915
+ },
3916
+ enum: {
3917
+ icon: lucide_react.List,
3918
+ tone: 8,
3919
+ name: "Enum"
3920
+ },
3921
+ array: {
3922
+ icon: lucide_react.Brackets,
3923
+ tone: 9,
3924
+ name: "Array"
3925
+ },
3926
+ object: {
3927
+ icon: lucide_react.Braces,
3928
+ tone: 10,
3929
+ name: "Object"
3930
+ },
3931
+ json: {
3932
+ icon: lucide_react.Braces,
3933
+ tone: 10,
3934
+ name: "JSON"
3935
+ },
3936
+ ip: {
3937
+ icon: lucide_react.Network,
3938
+ tone: 11,
3939
+ name: "IP address"
3940
+ },
3941
+ geo: {
3942
+ icon: lucide_react.MapPin,
3943
+ tone: 12,
3944
+ name: "Geo point"
3945
+ },
3946
+ url: {
3947
+ icon: lucide_react.Link2,
3948
+ tone: 13,
3949
+ name: "URL"
3950
+ },
3951
+ email: {
3952
+ icon: lucide_react.AtSign,
3953
+ tone: 14,
3954
+ name: "Email"
3955
+ },
3956
+ id: {
3957
+ icon: lucide_react.Fingerprint,
3958
+ tone: 15,
3959
+ name: "Identifier"
3960
+ },
3961
+ binary: {
3962
+ icon: lucide_react.Binary,
3963
+ tone: 16,
3964
+ name: "Binary"
3965
+ },
3966
+ aggregate: {
3967
+ icon: lucide_react.Sigma,
3968
+ tone: 2,
3969
+ name: "Aggregate"
3970
+ },
3971
+ nested: {
3972
+ icon: lucide_react.Layers,
3973
+ tone: 9,
3974
+ name: "Nested"
3975
+ },
3976
+ unknown: {
3977
+ icon: lucide_react.CircleHelp,
3978
+ tone: 0,
3979
+ name: "Unknown"
3980
+ }
3981
+ };
3982
+ /**
3983
+ * Names a backend type as one of ours. Accepts the spellings Postgres,
3984
+ * ClickHouse, DuckDB, and JSON Schema actually emit, so a caller can hand
3985
+ * over a column type verbatim instead of maintaining its own table.
3986
+ */
3987
+ function resolveFieldType(raw) {
3988
+ if (!raw) return "unknown";
3989
+ const t = raw.trim().toLowerCase();
3990
+ if (t in FIELD_TYPE_TOKENS) return t;
3991
+ if (t.endsWith("[]")) return "array";
3992
+ if (/^(array|list|set|multi)/.test(t)) return "array";
3993
+ if (/^(bytea|blob|binary|fixedstring)/.test(t)) return "binary";
3994
+ if (/^bool/.test(t)) return "boolean";
3995
+ if (/^(timestamp|datetime)/.test(t)) return "datetime";
3996
+ if (/^date/.test(t)) return "date";
3997
+ if (/^interval/.test(t)) return "duration";
3998
+ if (/^(uuid|guid)/.test(t)) return "id";
3999
+ if (/^(inet|cidr|ipv?[46])/.test(t)) return "ip";
4000
+ if (/^(point|geo|geography|geometry|latlon)/.test(t)) return "geo";
4001
+ if (/^(jsonb?|map|struct|record|tuple)/.test(t)) return "json";
4002
+ if (/^(enum|lowcardinality|category|categorical)/.test(t)) return "enum";
4003
+ if (/^(u?int|float|double|decimal|numeric|real|bigint|smallint|tinyint|serial|money)/.test(t)) return "number";
4004
+ if (/^(text|n?varchar|n?char|keyword|str|string)/.test(t)) return "string";
4005
+ return "unknown";
4006
+ }
4007
+ const sizeStyles = {
4008
+ xs: "size-4 rounded-[3px] [&>svg]:size-2.5",
4009
+ sm: "size-5 rounded [&>svg]:size-3",
4010
+ md: "size-6 rounded-md [&>svg]:size-3.5",
4011
+ lg: "size-8 rounded-lg [&>svg]:size-4"
4012
+ };
4013
+ const shapeStyles = {
4014
+ square: "",
4015
+ circle: "rounded-full"
4016
+ };
4017
+ /**
4018
+ * FieldToken — the type of a field, as a glyph.
4019
+ *
4020
+ * A field list, a column header, and a property table all have to answer
4021
+ * "what kind of thing is this?" before anything else, and they have to answer
4022
+ * it in the width of an icon. This is that answer: one glyph per type, one
4023
+ * palette slot per glyph, stable across every surface that shows fields, so a
4024
+ * reader learns the vocabulary once.
4025
+ *
4026
+ * @example
4027
+ * ```tsx
4028
+ * <FieldToken type={resolveFieldType(column.dataType)} title={column.name} />
4029
+ * ```
4030
+ */
4031
+ function FieldToken({ type, size = "sm", fill = "subtle", shape = "square", title, decorative = false, className }) {
4032
+ var _FIELD_TYPE_TOKENS$ty;
4033
+ const appearance = (_FIELD_TYPE_TOKENS$ty = FIELD_TYPE_TOKENS[type]) !== null && _FIELD_TYPE_TOKENS$ty !== void 0 ? _FIELD_TYPE_TOKENS$ty : FIELD_TYPE_TOKENS.unknown;
4034
+ const Icon = appearance.icon;
4035
+ const label = title !== null && title !== void 0 ? title : appearance.name;
4036
+ const hue = appearance.tone === 0 ? "var(--color-text-tertiary)" : `var(--opt-chart-${appearance.tone})`;
4037
+ const style = fill === "solid" ? {
4038
+ backgroundColor: hue,
4039
+ color: "var(--color-surface)"
4040
+ } : fill === "subtle" ? {
4041
+ backgroundColor: `color-mix(in oklab, ${hue} 14%, transparent)`,
4042
+ color: hue
4043
+ } : { color: hue };
4044
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4045
+ className: cn("inline-flex shrink-0 items-center justify-center", sizeStyles[size], shapeStyles[shape], className),
4046
+ style,
4047
+ role: decorative ? void 0 : "img",
4048
+ "aria-label": decorative ? void 0 : label,
4049
+ "aria-hidden": decorative ? true : void 0,
4050
+ "data-opt-id": "V3AJ4",
4051
+ "data-opt-slug": "field-token.FieldToken",
4052
+ "data-field-type": type,
4053
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { "aria-hidden": "true" })
4054
+ });
4055
+ }
4056
+ //#endregion
4057
+ //#region src/core/highlight.tsx
4058
+ function escapeForRegExp(value) {
4059
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4060
+ }
4061
+ /**
4062
+ * Splits `text` on every occurrence of every term. Terms are matched longest
4063
+ * first so a search for both "check" and "checkout" marks "checkout" whole
4064
+ * rather than leaving a stray "out".
4065
+ */
4066
+ function segment(text, terms, caseSensitive, firstOnly) {
4067
+ const usable = terms.filter((term) => term.length > 0).sort((a, b) => b.length - a.length);
4068
+ if (usable.length === 0) return [{
4069
+ text,
4070
+ hit: false
4071
+ }];
4072
+ const pattern = new RegExp(`(${usable.map(escapeForRegExp).join("|")})`, caseSensitive ? "g" : "gi");
4073
+ const out = [];
4074
+ let cursor = 0;
4075
+ let matched = false;
4076
+ for (const match of text.matchAll(pattern)) {
4077
+ var _match$index;
4078
+ if (firstOnly && matched) break;
4079
+ const at = (_match$index = match.index) !== null && _match$index !== void 0 ? _match$index : 0;
4080
+ if (at > cursor) out.push({
4081
+ text: text.slice(cursor, at),
4082
+ hit: false
4083
+ });
4084
+ out.push({
4085
+ text: match[0],
4086
+ hit: true
4087
+ });
4088
+ cursor = at + match[0].length;
4089
+ matched = true;
4090
+ }
4091
+ if (cursor < text.length) out.push({
4092
+ text: text.slice(cursor),
4093
+ hit: false
4094
+ });
4095
+ return out;
4096
+ }
4097
+ /**
4098
+ * Highlight — marks the part of a string the reader searched for.
4099
+ *
4100
+ * Uses a real `<mark>`, so the match is carried by semantics rather than by
4101
+ * colour alone and survives a high-contrast theme. Terms are matched longest
4102
+ * first, so overlapping terms produce whole marks instead of fragments.
4103
+ *
4104
+ * @example
4105
+ * ```tsx
4106
+ * <Highlight text={row.message} search={query} />
4107
+ * ```
4108
+ */
4109
+ function Highlight({ text, search, caseSensitive = false, firstOnly = false, className, markClassName }) {
4110
+ const segments = segment(text, Array.isArray(search) ? search : [search], caseSensitive, firstOnly);
4111
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4112
+ className,
4113
+ "data-opt-id": "448TR",
4114
+ "data-opt-slug": "highlight.Highlight",
4115
+ children: segments.map((part, index) => part.hit ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("mark", {
4116
+ className: cn("bg-warning-subtle text-text-primary rounded-[2px] px-0.5 font-medium", markClassName),
4117
+ children: part.text
4118
+ }, index) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: part.text }, index))
4119
+ });
4120
+ }
4121
+ //#endregion
4122
+ //#region src/core/text-truncate.tsx
4123
+ const useIsomorphicLayoutEffect = typeof window === "undefined" ? react.useEffect : react.useLayoutEffect;
4124
+ /** Measures text in the element's own resolved font, not an assumed one. */
4125
+ function createMeasurer(element) {
4126
+ const context = document.createElement("canvas").getContext("2d");
4127
+ if (!context) return null;
4128
+ const computed = window.getComputedStyle(element);
4129
+ context.font = [
4130
+ computed.fontStyle,
4131
+ computed.fontVariant,
4132
+ computed.fontWeight,
4133
+ `${computed.fontSize}/${computed.lineHeight}`,
4134
+ computed.fontFamily
4135
+ ].join(" ");
4136
+ const spacing = Number.parseFloat(computed.letterSpacing) || 0;
4137
+ return (value) => context.measureText(value).width + spacing * value.length;
4138
+ }
4139
+ /** Largest `n` in `[0, max]` for which `fits(n)` holds. */
4140
+ function largestFitting(max, fits) {
4141
+ let low = 0;
4142
+ let high = max;
4143
+ while (low < high) {
4144
+ const mid = Math.ceil((low + high) / 2);
4145
+ if (fits(mid)) low = mid;
4146
+ else high = mid - 1;
4147
+ }
4148
+ return low;
4149
+ }
4150
+ function truncateToWidth(text, side, ellipsis, available, measure) {
4151
+ if (measure(text) <= available) return text;
4152
+ const chars = Array.from(text);
4153
+ if (side === "end") {
4154
+ const keep = largestFitting(chars.length, (n) => measure(chars.slice(0, n).join("") + ellipsis) <= available);
4155
+ return chars.slice(0, keep).join("") + ellipsis;
4156
+ }
4157
+ if (side === "start") {
4158
+ const keep = largestFitting(chars.length, (n) => measure(ellipsis + chars.slice(chars.length - n).join("")) <= available);
4159
+ return ellipsis + chars.slice(chars.length - keep).join("");
4160
+ }
4161
+ if (side === "startEnd") {
4162
+ const keep = largestFitting(Math.floor(chars.length / 2), (n) => {
4163
+ const head = chars.slice(0, n).join("");
4164
+ const tail = chars.slice(chars.length - n).join("");
4165
+ return measure(head + ellipsis + tail) <= available;
4166
+ });
4167
+ if (keep === 0) return ellipsis;
4168
+ return chars.slice(0, keep).join("") + ellipsis + chars.slice(chars.length - keep).join("");
4169
+ }
4170
+ const span = largestFitting(chars.length, (n) => {
4171
+ const start = Math.floor((chars.length - n) / 2);
4172
+ return measure(ellipsis + chars.slice(start, start + n).join("") + ellipsis) <= available;
4173
+ });
4174
+ if (span === 0) return ellipsis;
4175
+ const start = Math.floor((chars.length - span) / 2);
4176
+ return ellipsis + chars.slice(start, start + span).join("") + ellipsis;
4177
+ }
4178
+ /**
4179
+ * TextTruncate — drops characters from the part of a string that identifies it
4180
+ * least.
4181
+ *
4182
+ * CSS can only cut the end. That is the wrong end for the strings analytics
4183
+ * shows most: two URLs that differ in their query string, two ids that share a
4184
+ * prefix, a stack frame whose filename is at the tail. This measures the real
4185
+ * text in the real font against the real available width and cuts where the
4186
+ * caller says.
4187
+ *
4188
+ * The full string stays in the accessibility tree and in `title`, so
4189
+ * truncation never costs the reader information — only space.
4190
+ *
4191
+ * @example
4192
+ * ```tsx
4193
+ * <TextTruncate text={event.url} truncation="startEnd" />
4194
+ * ```
4195
+ */
4196
+ function TextTruncate({ text, truncation = "startEnd", ellipsis = "…", width, children, className }) {
4197
+ const ref = (0, react.useRef)(null);
4198
+ const [available, setAvailable] = (0, react.useState)(width !== null && width !== void 0 ? width : null);
4199
+ useIsomorphicLayoutEffect(() => {
4200
+ if (width !== void 0) {
4201
+ setAvailable(width);
4202
+ return;
4203
+ }
4204
+ const element = ref.current;
4205
+ if (!element || typeof ResizeObserver === "undefined") return;
4206
+ const observer = new ResizeObserver((entries) => {
4207
+ const entry = entries[0];
4208
+ if (entry) setAvailable(entry.contentRect.width);
4209
+ });
4210
+ observer.observe(element);
4211
+ setAvailable(element.getBoundingClientRect().width);
4212
+ return () => observer.disconnect();
4213
+ }, [width]);
4214
+ const [rendered, setRendered] = (0, react.useState)(text);
4215
+ useIsomorphicLayoutEffect(() => {
4216
+ const element = ref.current;
4217
+ if (!element || available === null || available <= 0) {
4218
+ setRendered(text);
4219
+ return;
4220
+ }
4221
+ const measure = createMeasurer(element);
4222
+ if (!measure) {
4223
+ setRendered(text);
4224
+ return;
4225
+ }
4226
+ setRendered(truncateToWidth(text, truncation, ellipsis, available, measure));
4227
+ }, [
4228
+ text,
4229
+ truncation,
4230
+ ellipsis,
4231
+ available
4232
+ ]);
4233
+ const truncated = rendered !== text;
4234
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4235
+ ref,
4236
+ className: cn("block w-full overflow-hidden whitespace-nowrap", className),
4237
+ title: truncated ? text : void 0,
4238
+ "data-opt-id": "Y6BGX",
4239
+ "data-opt-slug": "text-truncate.TextTruncate",
4240
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4241
+ "aria-hidden": truncated ? "true" : void 0,
4242
+ children: children ? children(rendered) : rendered
4243
+ }), truncated && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4244
+ className: "sr-only",
4245
+ children: text
4246
+ })]
4247
+ });
4248
+ }
4249
+ //#endregion
3870
4250
  Object.defineProperty(exports, "Alert", {
3871
4251
  enumerable: true,
3872
4252
  get: function() {
@@ -4047,12 +4427,24 @@ Object.defineProperty(exports, "EmptyState", {
4047
4427
  return EmptyState;
4048
4428
  }
4049
4429
  });
4430
+ Object.defineProperty(exports, "FIELD_TYPE_TOKENS", {
4431
+ enumerable: true,
4432
+ get: function() {
4433
+ return FIELD_TYPE_TOKENS;
4434
+ }
4435
+ });
4050
4436
  Object.defineProperty(exports, "FieldMessages", {
4051
4437
  enumerable: true,
4052
4438
  get: function() {
4053
4439
  return FieldMessages;
4054
4440
  }
4055
4441
  });
4442
+ Object.defineProperty(exports, "FieldToken", {
4443
+ enumerable: true,
4444
+ get: function() {
4445
+ return FieldToken;
4446
+ }
4447
+ });
4056
4448
  Object.defineProperty(exports, "FormCheckbox", {
4057
4449
  enumerable: true,
4058
4450
  get: function() {
@@ -4227,6 +4619,12 @@ Object.defineProperty(exports, "FormTextarea", {
4227
4619
  return FormTextarea;
4228
4620
  }
4229
4621
  });
4622
+ Object.defineProperty(exports, "Highlight", {
4623
+ enumerable: true,
4624
+ get: function() {
4625
+ return Highlight;
4626
+ }
4627
+ });
4230
4628
  Object.defineProperty(exports, "IconPicker", {
4231
4629
  enumerable: true,
4232
4630
  get: function() {
@@ -4629,6 +5027,12 @@ Object.defineProperty(exports, "TagInput", {
4629
5027
  return TagInput;
4630
5028
  }
4631
5029
  });
5030
+ Object.defineProperty(exports, "TextTruncate", {
5031
+ enumerable: true,
5032
+ get: function() {
5033
+ return TextTruncate;
5034
+ }
5035
+ });
4632
5036
  Object.defineProperty(exports, "ToolbarButton", {
4633
5037
  enumerable: true,
4634
5038
  get: function() {
@@ -4725,6 +5129,12 @@ Object.defineProperty(exports, "koStarRatingLabels", {
4725
5129
  return koStarRatingLabels;
4726
5130
  }
4727
5131
  });
5132
+ Object.defineProperty(exports, "resolveFieldType", {
5133
+ enumerable: true,
5134
+ get: function() {
5135
+ return resolveFieldType;
5136
+ }
5137
+ });
4728
5138
  Object.defineProperty(exports, "useControlledState", {
4729
5139
  enumerable: true,
4730
5140
  get: function() {