@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
@@ -3,9 +3,9 @@ import { clsx } from "clsx";
3
3
  import { twMerge } from "tailwind-merge";
4
4
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
5
  import * as React from "react";
6
- import { createContext, forwardRef, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
6
+ import { createContext, forwardRef, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
7
7
  import { AlertDialogPanel, ComboboxGroup, ComboboxGroupLabel, ComboboxInput, ComboboxItem, ComboboxPopover, ComboboxRoot, Composite, CompositeItem, CompositeProvider, CompositeRow, DialogDescription, DialogDisclosure, DialogDismiss, DialogHeading, DialogPanel, DialogRoot, DisclosureContent, DisclosureRoot, DisclosureTrigger, FormCheckbox, FormControl, FormDescription, FormError, FormField, FormGroup, FormGroupLabel, FormInput, FormLabel, FormProvider, FormPush, FormRadio, FormRadioGroup, FormRemove, FormReset, FormRoot, FormSubmit, FormSwitch, MenuButtonArrow, MenuItem, MenuItemCheckbox, MenuItemRadio, MenuPopover, MenuRoot, MenuSeparator, MenuTrigger, MenubarContainer, MenubarRoot, PopoverClose, PopoverContent, PopoverRoot, PopoverTrigger, SelectItem, SelectLabel, SelectPopover, SelectRoot, SelectTrigger, Tab, TabList, TabPanel, TabsRoot, ToolbarButton, ToolbarContainer, ToolbarRoot, ToolbarSeparator, Tooltip, TooltipAnchor, TooltipProvider, useFieldValue, useFormContext, useFormStore } from "@reopt-ai/opt-ui-primitives";
8
- import { Bell, CalendarDays, ChartColumn, Check, ChevronLeft, ChevronRight, CircleAlert, CircleCheck, CreditCard, Download, Eye, FileText, Flag, Folder, Globe, Heart, Info, Link2, LogIn, Mail, MessageCircle, MousePointerClick, Search, Settings, Share2, ShoppingCart, Star, TrendingUp, TriangleAlert, Upload, User, UserPlus, XCircle, Zap } from "lucide-react";
8
+ import { AtSign, Bell, Binary, Braces, Brackets, Calendar, CalendarClock, CalendarDays, ChartColumn, Check, ChevronLeft, ChevronRight, CircleAlert, CircleCheck, CircleDollarSign, CircleHelp, CreditCard, Download, Eye, FileText, Fingerprint, Flag, Folder, Globe, Hash, Heart, Info, Layers, Link2, List, LogIn, Mail, MapPin, MessageCircle, MousePointerClick, Network, Percent, Search, Settings, Share2, ShoppingCart, Sigma, Star, Timer, ToggleLeft, TrendingUp, TriangleAlert, Type, Upload, User, UserPlus, XCircle, Zap } from "lucide-react";
9
9
  //#region src/lib/cn.ts
10
10
  /** Handles cn. */
11
11
  function cn(...inputs) {
@@ -324,7 +324,7 @@ const variantStyles$2 = {
324
324
  ghost: "text-text-secondary hover:bg-bg-subtle active:bg-bg-muted focus-visible:ring-offset-1",
325
325
  danger: "bg-danger text-danger-fg hover:bg-danger active:bg-danger focus-visible:ring-offset-2"
326
326
  };
327
- const sizeStyles$3 = {
327
+ const sizeStyles$4 = {
328
328
  sm: "h-[var(--opt-control-sm)] px-[var(--opt-control-padding-sm)] text-xs gap-[var(--opt-space-1-5)]",
329
329
  md: "h-[var(--opt-control-md)] px-[var(--opt-control-padding-md)] text-sm gap-[var(--opt-space-2)]",
330
330
  lg: "h-[var(--opt-control-lg)] px-[var(--opt-control-padding-lg)] text-base gap-[var(--opt-space-2)]"
@@ -335,7 +335,7 @@ function Button(_ref) {
335
335
  const isDisabled = disabled || loading;
336
336
  return /* @__PURE__ */ jsxs("button", _objectSpread2(_objectSpread2({
337
337
  type: "button",
338
- 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),
338
+ 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),
339
339
  disabled: isDisabled
340
340
  }, props), {}, {
341
341
  "aria-busy": loading ? true : ariaBusy,
@@ -444,7 +444,7 @@ const progressStroke = {
444
444
  error: "stroke-danger",
445
445
  info: "stroke-info"
446
446
  };
447
- const sizeStyles$2 = {
447
+ const sizeStyles$3 = {
448
448
  sm: "px-1.5 py-0.5 text-[10px]",
449
449
  md: "px-2 py-0.5 text-[11px]"
450
450
  };
@@ -456,7 +456,7 @@ function Badge(_ref) {
456
456
  const radius = (progressSize - 3) / 2;
457
457
  const circumference = 2 * Math.PI * radius;
458
458
  const offset = circumference - clampedProgress / 100 * circumference;
459
- return /* @__PURE__ */ jsxs("span", _objectSpread2(_objectSpread2({ className: cn(OPT_BADGE, variantStyles$1[variant], sizeStyles$2[size], className) }, props), {}, {
459
+ return /* @__PURE__ */ jsxs("span", _objectSpread2(_objectSpread2({ className: cn(OPT_BADGE, variantStyles$1[variant], sizeStyles$3[size], className) }, props), {}, {
460
460
  "data-opt-id": "9GBGZ",
461
461
  "data-opt-slug": "badge.Badge",
462
462
  children: [
@@ -503,7 +503,7 @@ const _excluded$13 = [
503
503
  "aria-hidden",
504
504
  "aria-label"
505
505
  ];
506
- const sizeStyles$1 = {
506
+ const sizeStyles$2 = {
507
507
  sm: "h-4 w-4",
508
508
  md: "h-6 w-6",
509
509
  lg: "h-8 w-8"
@@ -513,7 +513,7 @@ function Spinner(_ref) {
513
513
  let { size = "md", className, role, "aria-hidden": ariaHidden, "aria-label": ariaLabel } = _ref, props = _objectWithoutProperties(_ref, _excluded$13);
514
514
  const isDecorative = ariaHidden === true || ariaHidden === "true";
515
515
  return /* @__PURE__ */ jsxs("svg", _objectSpread2(_objectSpread2({
516
- className: cn("animate-spin text-current", sizeStyles$1[size], className),
516
+ className: cn("animate-spin text-current", sizeStyles$2[size], className),
517
517
  viewBox: "0 0 24 24",
518
518
  fill: "none",
519
519
  role: isDecorative ? void 0 : role !== null && role !== void 0 ? role : "status",
@@ -3532,7 +3532,7 @@ function setForwardedRef(ref, value) {
3532
3532
  if (typeof ref === "function") ref(value);
3533
3533
  else if (ref) ref.current = value;
3534
3534
  }
3535
- const sizeStyles = {
3535
+ const sizeStyles$1 = {
3536
3536
  sm: {
3537
3537
  box: "h-4 w-4",
3538
3538
  icon: "h-3 w-3",
@@ -3598,7 +3598,7 @@ const Checkbox = forwardRef((_ref, ref) => {
3598
3598
  const checkboxId = (_useInputId = useInputId(id, label, "checkbox")) !== null && _useInputId !== void 0 ? _useInputId : `checkbox-${fallbackId}`;
3599
3599
  const hintText = hint !== null && hint !== void 0 ? hint : description;
3600
3600
  const hasError = Boolean(error);
3601
- const styles = sizeStyles[size];
3601
+ const styles = sizeStyles$1[size];
3602
3602
  const isIndeterminate = Boolean(indeterminate);
3603
3603
  const inputRef = useCallback((node) => {
3604
3604
  if (node) node.indeterminate = isIndeterminate;
@@ -3845,6 +3845,386 @@ function Pagination(_ref) {
3845
3845
  }));
3846
3846
  }
3847
3847
  //#endregion
3848
- export { TooltipAnchor$1 as $, koBreadcrumbLabels as $t, FormSwitch$1 as A, OPT_SURFACE_POPOVER as An, SelectPopover$1 as At, getIconRegistryEntry as B, _objectSpread2 as Bn, Composite as Bt, FormRoot$1 as C, OPT_FOCUS as Cn, DialogFooter as Ct, FormSlider as D, OPT_INPUT_ERROR as Dn, DialogRoot$1 as Dt, FormSelectPopover as E, OPT_INPUT as En, DialogPanel$1 as Et, useFormStore as F, OPT_TEXT_LABEL as Fn, TabPanel$1 as Ft, PopoverTrigger$1 as G, StarRating as Gt, PopoverClose$1 as H, Kbd as Hn, CompositeProvider as Ht, DEFAULT_PRESETS as I, OPT_TEXT_PRIMARY as In, TabsRoot$1 as It, ToolbarButton$1 as J, Alert as Jt, Slider as K, Input as Kt, DateRangePicker as L, OPT_TEXT_SECONDARY as Ln, DisclosureContent$1 as Lt, FormTextarea as M, OPT_TD as Mn, SelectTrigger$1 as Mt, useFieldValue as N, OPT_TD_MUTED as Nn, Tab$1 as Nt, FormStarRating as O, OPT_ITEM_BASE as On, SelectItem$1 as Ot, useFormContext as P, OPT_TEXTAREA_SIZE as Pn, TabList$1 as Pt, Tooltip$1 as Q, Button as Qt, TagInput as R, OPT_TEXT_TERTIARY as Rn, DisclosureRoot$1 as Rt, FormReset$1 as S, OPT_DOT_YELLOW as Sn, DialogDismiss$1 as St, FormSelectItem as T, OPT_HOVER_ITEM as Tn, DialogHeading$1 as Tt, PopoverContent$1 as U, cn as Un, CompositeRow as Ut, ColorPicker as V, _objectWithoutProperties as Vn, CompositeItem as Vt, PopoverRoot$1 as W, CompositeZone as Wt, ToolbarRoot$1 as X, Badge as Xt, ToolbarContainer$1 as Y, Spinner as Yt, ToolbarSeparator$1 as Z, EmptyState as Zt, FormProvider$1 as _, OPT_DISCLOSURE_TRIGGER as _n, ComboboxRoot$1 as _t, FormColorPicker as a, RequiredMark as an, MenuPopover$1 as at, FormRadioGroup$1 as b, OPT_DOT_GREEN as bn, DialogDescription$1 as bt, FormDescription$1 as c, OPT_ANIMATE_DRAWER as cn, MenuTrigger$1 as ct, FormGroup$1 as d, OPT_BADGE_GREEN as dn, ComboboxEmpty as dt, koComboboxLabels as en, TooltipProvider$1 as et, FormGroupLabel$1 as f, OPT_BADGE_NEUTRAL as fn, ComboboxGroup$1 as ft, FormNumberInput as g, OPT_DISCLOSURE_CONTENT as gn, ComboboxPopover$1 as gt, FormLabel$1 as h, OPT_BORDER as hn, ComboboxItem$1 as ht, FormCheckbox$1 as i, FieldMessages as in, MenuItemRadio$1 as it, FormTagInput as j, OPT_TABLE_ROW_ACTIVE as jn, SelectRoot$1 as jt, FormSubmit$1 as k, OPT_SURFACE as kn, SelectLabel$1 as kt, FormError$1 as l, OPT_ANIMATE_ENTER_EXIT as ln, MenubarContainer$1 as lt, FormInput$1 as m, OPT_BADGE_YELLOW as mn, ComboboxInput$1 as mt, Checkbox as n, StatCard as nn, MenuItem$1 as nt, FormControl$1 as o, getDescribedBy as on, MenuRoot$1 as ot, FormIconPicker as p, OPT_BADGE_RED as pn, ComboboxGroupLabel$1 as pt, NumberInput as q, useInputId as qt, CheckboxGroup as r, useControlledState as rn, MenuItemCheckbox$1 as rt, FormDateRangePicker as s, OPT_ACTIVE_ITEM as sn, MenuSeparator$1 as st, Pagination as t, koStarRatingLabels as tn, MenuButtonArrow$1 as tt, FormField$1 as u, OPT_BADGE_BLUE as un, MenubarRoot$1 as ut, FormPush$1 as v, OPT_DIVIDER as vn, AlertDialogPanel$1 as vt, FormSelect as w, OPT_FOCUS_VISIBLE as wn, DialogHeader as wt, FormRemove$1 as x, OPT_DOT_RED as xn, DialogDisclosure$1 as xt, FormRadio$1 as y, OPT_DOT_GRAY as yn, DialogBody as yt, IconPicker as z, OPT_TH as zn, DisclosureTrigger$1 as zt };
3848
+ //#region src/core/field-token.tsx
3849
+ /**
3850
+ * The glyph and palette slot each field type reads as. Exported so a product
3851
+ * can render the same legend the tokens imply.
3852
+ */
3853
+ const FIELD_TYPE_TOKENS = {
3854
+ string: {
3855
+ icon: Type,
3856
+ tone: 1,
3857
+ name: "String"
3858
+ },
3859
+ number: {
3860
+ icon: Hash,
3861
+ tone: 2,
3862
+ name: "Number"
3863
+ },
3864
+ boolean: {
3865
+ icon: ToggleLeft,
3866
+ tone: 3,
3867
+ name: "Boolean"
3868
+ },
3869
+ date: {
3870
+ icon: Calendar,
3871
+ tone: 4,
3872
+ name: "Date"
3873
+ },
3874
+ datetime: {
3875
+ icon: CalendarClock,
3876
+ tone: 4,
3877
+ name: "Date and time"
3878
+ },
3879
+ duration: {
3880
+ icon: Timer,
3881
+ tone: 5,
3882
+ name: "Duration"
3883
+ },
3884
+ percent: {
3885
+ icon: Percent,
3886
+ tone: 6,
3887
+ name: "Percent"
3888
+ },
3889
+ currency: {
3890
+ icon: CircleDollarSign,
3891
+ tone: 7,
3892
+ name: "Currency"
3893
+ },
3894
+ enum: {
3895
+ icon: List,
3896
+ tone: 8,
3897
+ name: "Enum"
3898
+ },
3899
+ array: {
3900
+ icon: Brackets,
3901
+ tone: 9,
3902
+ name: "Array"
3903
+ },
3904
+ object: {
3905
+ icon: Braces,
3906
+ tone: 10,
3907
+ name: "Object"
3908
+ },
3909
+ json: {
3910
+ icon: Braces,
3911
+ tone: 10,
3912
+ name: "JSON"
3913
+ },
3914
+ ip: {
3915
+ icon: Network,
3916
+ tone: 11,
3917
+ name: "IP address"
3918
+ },
3919
+ geo: {
3920
+ icon: MapPin,
3921
+ tone: 12,
3922
+ name: "Geo point"
3923
+ },
3924
+ url: {
3925
+ icon: Link2,
3926
+ tone: 13,
3927
+ name: "URL"
3928
+ },
3929
+ email: {
3930
+ icon: AtSign,
3931
+ tone: 14,
3932
+ name: "Email"
3933
+ },
3934
+ id: {
3935
+ icon: Fingerprint,
3936
+ tone: 15,
3937
+ name: "Identifier"
3938
+ },
3939
+ binary: {
3940
+ icon: Binary,
3941
+ tone: 16,
3942
+ name: "Binary"
3943
+ },
3944
+ aggregate: {
3945
+ icon: Sigma,
3946
+ tone: 2,
3947
+ name: "Aggregate"
3948
+ },
3949
+ nested: {
3950
+ icon: Layers,
3951
+ tone: 9,
3952
+ name: "Nested"
3953
+ },
3954
+ unknown: {
3955
+ icon: CircleHelp,
3956
+ tone: 0,
3957
+ name: "Unknown"
3958
+ }
3959
+ };
3960
+ /**
3961
+ * Names a backend type as one of ours. Accepts the spellings Postgres,
3962
+ * ClickHouse, DuckDB, and JSON Schema actually emit, so a caller can hand
3963
+ * over a column type verbatim instead of maintaining its own table.
3964
+ */
3965
+ function resolveFieldType(raw) {
3966
+ if (!raw) return "unknown";
3967
+ const t = raw.trim().toLowerCase();
3968
+ if (t in FIELD_TYPE_TOKENS) return t;
3969
+ if (t.endsWith("[]")) return "array";
3970
+ if (/^(array|list|set|multi)/.test(t)) return "array";
3971
+ if (/^(bytea|blob|binary|fixedstring)/.test(t)) return "binary";
3972
+ if (/^bool/.test(t)) return "boolean";
3973
+ if (/^(timestamp|datetime)/.test(t)) return "datetime";
3974
+ if (/^date/.test(t)) return "date";
3975
+ if (/^interval/.test(t)) return "duration";
3976
+ if (/^(uuid|guid)/.test(t)) return "id";
3977
+ if (/^(inet|cidr|ipv?[46])/.test(t)) return "ip";
3978
+ if (/^(point|geo|geography|geometry|latlon)/.test(t)) return "geo";
3979
+ if (/^(jsonb?|map|struct|record|tuple)/.test(t)) return "json";
3980
+ if (/^(enum|lowcardinality|category|categorical)/.test(t)) return "enum";
3981
+ if (/^(u?int|float|double|decimal|numeric|real|bigint|smallint|tinyint|serial|money)/.test(t)) return "number";
3982
+ if (/^(text|n?varchar|n?char|keyword|str|string)/.test(t)) return "string";
3983
+ return "unknown";
3984
+ }
3985
+ const sizeStyles = {
3986
+ xs: "size-4 rounded-[3px] [&>svg]:size-2.5",
3987
+ sm: "size-5 rounded [&>svg]:size-3",
3988
+ md: "size-6 rounded-md [&>svg]:size-3.5",
3989
+ lg: "size-8 rounded-lg [&>svg]:size-4"
3990
+ };
3991
+ const shapeStyles = {
3992
+ square: "",
3993
+ circle: "rounded-full"
3994
+ };
3995
+ /**
3996
+ * FieldToken — the type of a field, as a glyph.
3997
+ *
3998
+ * A field list, a column header, and a property table all have to answer
3999
+ * "what kind of thing is this?" before anything else, and they have to answer
4000
+ * it in the width of an icon. This is that answer: one glyph per type, one
4001
+ * palette slot per glyph, stable across every surface that shows fields, so a
4002
+ * reader learns the vocabulary once.
4003
+ *
4004
+ * @example
4005
+ * ```tsx
4006
+ * <FieldToken type={resolveFieldType(column.dataType)} title={column.name} />
4007
+ * ```
4008
+ */
4009
+ function FieldToken({ type, size = "sm", fill = "subtle", shape = "square", title, decorative = false, className }) {
4010
+ var _FIELD_TYPE_TOKENS$ty;
4011
+ 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;
4012
+ const Icon = appearance.icon;
4013
+ const label = title !== null && title !== void 0 ? title : appearance.name;
4014
+ const hue = appearance.tone === 0 ? "var(--color-text-tertiary)" : `var(--opt-chart-${appearance.tone})`;
4015
+ const style = fill === "solid" ? {
4016
+ backgroundColor: hue,
4017
+ color: "var(--color-surface)"
4018
+ } : fill === "subtle" ? {
4019
+ backgroundColor: `color-mix(in oklab, ${hue} 14%, transparent)`,
4020
+ color: hue
4021
+ } : { color: hue };
4022
+ return /* @__PURE__ */ jsx("span", {
4023
+ className: cn("inline-flex shrink-0 items-center justify-center", sizeStyles[size], shapeStyles[shape], className),
4024
+ style,
4025
+ role: decorative ? void 0 : "img",
4026
+ "aria-label": decorative ? void 0 : label,
4027
+ "aria-hidden": decorative ? true : void 0,
4028
+ "data-opt-id": "V3AJ4",
4029
+ "data-opt-slug": "field-token.FieldToken",
4030
+ "data-field-type": type,
4031
+ children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" })
4032
+ });
4033
+ }
4034
+ //#endregion
4035
+ //#region src/core/highlight.tsx
4036
+ function escapeForRegExp(value) {
4037
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4038
+ }
4039
+ /**
4040
+ * Splits `text` on every occurrence of every term. Terms are matched longest
4041
+ * first so a search for both "check" and "checkout" marks "checkout" whole
4042
+ * rather than leaving a stray "out".
4043
+ */
4044
+ function segment(text, terms, caseSensitive, firstOnly) {
4045
+ const usable = terms.filter((term) => term.length > 0).sort((a, b) => b.length - a.length);
4046
+ if (usable.length === 0) return [{
4047
+ text,
4048
+ hit: false
4049
+ }];
4050
+ const pattern = new RegExp(`(${usable.map(escapeForRegExp).join("|")})`, caseSensitive ? "g" : "gi");
4051
+ const out = [];
4052
+ let cursor = 0;
4053
+ let matched = false;
4054
+ for (const match of text.matchAll(pattern)) {
4055
+ var _match$index;
4056
+ if (firstOnly && matched) break;
4057
+ const at = (_match$index = match.index) !== null && _match$index !== void 0 ? _match$index : 0;
4058
+ if (at > cursor) out.push({
4059
+ text: text.slice(cursor, at),
4060
+ hit: false
4061
+ });
4062
+ out.push({
4063
+ text: match[0],
4064
+ hit: true
4065
+ });
4066
+ cursor = at + match[0].length;
4067
+ matched = true;
4068
+ }
4069
+ if (cursor < text.length) out.push({
4070
+ text: text.slice(cursor),
4071
+ hit: false
4072
+ });
4073
+ return out;
4074
+ }
4075
+ /**
4076
+ * Highlight — marks the part of a string the reader searched for.
4077
+ *
4078
+ * Uses a real `<mark>`, so the match is carried by semantics rather than by
4079
+ * colour alone and survives a high-contrast theme. Terms are matched longest
4080
+ * first, so overlapping terms produce whole marks instead of fragments.
4081
+ *
4082
+ * @example
4083
+ * ```tsx
4084
+ * <Highlight text={row.message} search={query} />
4085
+ * ```
4086
+ */
4087
+ function Highlight({ text, search, caseSensitive = false, firstOnly = false, className, markClassName }) {
4088
+ const segments = segment(text, Array.isArray(search) ? search : [search], caseSensitive, firstOnly);
4089
+ return /* @__PURE__ */ jsx("span", {
4090
+ className,
4091
+ "data-opt-id": "448TR",
4092
+ "data-opt-slug": "highlight.Highlight",
4093
+ children: segments.map((part, index) => part.hit ? /* @__PURE__ */ jsx("mark", {
4094
+ className: cn("bg-warning-subtle text-text-primary rounded-[2px] px-0.5 font-medium", markClassName),
4095
+ children: part.text
4096
+ }, index) : /* @__PURE__ */ jsx("span", { children: part.text }, index))
4097
+ });
4098
+ }
4099
+ //#endregion
4100
+ //#region src/core/text-truncate.tsx
4101
+ const useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
4102
+ /** Measures text in the element's own resolved font, not an assumed one. */
4103
+ function createMeasurer(element) {
4104
+ const context = document.createElement("canvas").getContext("2d");
4105
+ if (!context) return null;
4106
+ const computed = window.getComputedStyle(element);
4107
+ context.font = [
4108
+ computed.fontStyle,
4109
+ computed.fontVariant,
4110
+ computed.fontWeight,
4111
+ `${computed.fontSize}/${computed.lineHeight}`,
4112
+ computed.fontFamily
4113
+ ].join(" ");
4114
+ const spacing = Number.parseFloat(computed.letterSpacing) || 0;
4115
+ return (value) => context.measureText(value).width + spacing * value.length;
4116
+ }
4117
+ /** Largest `n` in `[0, max]` for which `fits(n)` holds. */
4118
+ function largestFitting(max, fits) {
4119
+ let low = 0;
4120
+ let high = max;
4121
+ while (low < high) {
4122
+ const mid = Math.ceil((low + high) / 2);
4123
+ if (fits(mid)) low = mid;
4124
+ else high = mid - 1;
4125
+ }
4126
+ return low;
4127
+ }
4128
+ function truncateToWidth(text, side, ellipsis, available, measure) {
4129
+ if (measure(text) <= available) return text;
4130
+ const chars = Array.from(text);
4131
+ if (side === "end") {
4132
+ const keep = largestFitting(chars.length, (n) => measure(chars.slice(0, n).join("") + ellipsis) <= available);
4133
+ return chars.slice(0, keep).join("") + ellipsis;
4134
+ }
4135
+ if (side === "start") {
4136
+ const keep = largestFitting(chars.length, (n) => measure(ellipsis + chars.slice(chars.length - n).join("")) <= available);
4137
+ return ellipsis + chars.slice(chars.length - keep).join("");
4138
+ }
4139
+ if (side === "startEnd") {
4140
+ const keep = largestFitting(Math.floor(chars.length / 2), (n) => {
4141
+ const head = chars.slice(0, n).join("");
4142
+ const tail = chars.slice(chars.length - n).join("");
4143
+ return measure(head + ellipsis + tail) <= available;
4144
+ });
4145
+ if (keep === 0) return ellipsis;
4146
+ return chars.slice(0, keep).join("") + ellipsis + chars.slice(chars.length - keep).join("");
4147
+ }
4148
+ const span = largestFitting(chars.length, (n) => {
4149
+ const start = Math.floor((chars.length - n) / 2);
4150
+ return measure(ellipsis + chars.slice(start, start + n).join("") + ellipsis) <= available;
4151
+ });
4152
+ if (span === 0) return ellipsis;
4153
+ const start = Math.floor((chars.length - span) / 2);
4154
+ return ellipsis + chars.slice(start, start + span).join("") + ellipsis;
4155
+ }
4156
+ /**
4157
+ * TextTruncate — drops characters from the part of a string that identifies it
4158
+ * least.
4159
+ *
4160
+ * CSS can only cut the end. That is the wrong end for the strings analytics
4161
+ * shows most: two URLs that differ in their query string, two ids that share a
4162
+ * prefix, a stack frame whose filename is at the tail. This measures the real
4163
+ * text in the real font against the real available width and cuts where the
4164
+ * caller says.
4165
+ *
4166
+ * The full string stays in the accessibility tree and in `title`, so
4167
+ * truncation never costs the reader information — only space.
4168
+ *
4169
+ * @example
4170
+ * ```tsx
4171
+ * <TextTruncate text={event.url} truncation="startEnd" />
4172
+ * ```
4173
+ */
4174
+ function TextTruncate({ text, truncation = "startEnd", ellipsis = "…", width, children, className }) {
4175
+ const ref = useRef(null);
4176
+ const [available, setAvailable] = useState(width !== null && width !== void 0 ? width : null);
4177
+ useIsomorphicLayoutEffect(() => {
4178
+ if (width !== void 0) {
4179
+ setAvailable(width);
4180
+ return;
4181
+ }
4182
+ const element = ref.current;
4183
+ if (!element || typeof ResizeObserver === "undefined") return;
4184
+ const observer = new ResizeObserver((entries) => {
4185
+ const entry = entries[0];
4186
+ if (entry) setAvailable(entry.contentRect.width);
4187
+ });
4188
+ observer.observe(element);
4189
+ setAvailable(element.getBoundingClientRect().width);
4190
+ return () => observer.disconnect();
4191
+ }, [width]);
4192
+ const [rendered, setRendered] = useState(text);
4193
+ useIsomorphicLayoutEffect(() => {
4194
+ const element = ref.current;
4195
+ if (!element || available === null || available <= 0) {
4196
+ setRendered(text);
4197
+ return;
4198
+ }
4199
+ const measure = createMeasurer(element);
4200
+ if (!measure) {
4201
+ setRendered(text);
4202
+ return;
4203
+ }
4204
+ setRendered(truncateToWidth(text, truncation, ellipsis, available, measure));
4205
+ }, [
4206
+ text,
4207
+ truncation,
4208
+ ellipsis,
4209
+ available
4210
+ ]);
4211
+ const truncated = rendered !== text;
4212
+ return /* @__PURE__ */ jsxs("span", {
4213
+ ref,
4214
+ className: cn("block w-full overflow-hidden whitespace-nowrap", className),
4215
+ title: truncated ? text : void 0,
4216
+ "data-opt-id": "Y6BGX",
4217
+ "data-opt-slug": "text-truncate.TextTruncate",
4218
+ children: [/* @__PURE__ */ jsx("span", {
4219
+ "aria-hidden": truncated ? "true" : void 0,
4220
+ children: children ? children(rendered) : rendered
4221
+ }), truncated && /* @__PURE__ */ jsx("span", {
4222
+ className: "sr-only",
4223
+ children: text
4224
+ })]
4225
+ });
4226
+ }
4227
+ //#endregion
4228
+ export { ToolbarButton$1 as $, Alert as $t, FormSelectItem as A, OPT_HOVER_ITEM as An, DialogHeading$1 as At, useFormStore as B, OPT_TEXT_LABEL as Bn, TabPanel$1 as Bt, FormPush$1 as C, OPT_DIVIDER as Cn, AlertDialogPanel$1 as Ct, FormReset$1 as D, OPT_DOT_YELLOW as Dn, DialogDismiss$1 as Dt, FormRemove$1 as E, OPT_DOT_RED as En, DialogDisclosure$1 as Et, FormSwitch$1 as F, OPT_SURFACE_POPOVER as Fn, SelectPopover$1 as Ft, getIconRegistryEntry as G, _objectSpread2 as Gn, Composite as Gt, DateRangePicker as H, OPT_TEXT_SECONDARY as Hn, DisclosureContent$1 as Ht, FormTagInput as I, OPT_TABLE_ROW_ACTIVE as In, SelectRoot$1 as It, PopoverContent$1 as J, cn as Jn, CompositeRow as Jt, ColorPicker as K, _objectWithoutProperties as Kn, CompositeItem as Kt, FormTextarea as L, OPT_TD as Ln, SelectTrigger$1 as Lt, FormSlider as M, OPT_INPUT_ERROR as Mn, DialogRoot$1 as Mt, FormStarRating as N, OPT_ITEM_BASE as Nn, SelectItem$1 as Nt, FormRoot$1 as O, OPT_FOCUS as On, DialogFooter as Ot, FormSubmit$1 as P, OPT_SURFACE as Pn, SelectLabel$1 as Pt, NumberInput as Q, useInputId as Qt, useFieldValue as R, OPT_TD_MUTED as Rn, Tab$1 as Rt, FormProvider$1 as S, OPT_DISCLOSURE_TRIGGER as Sn, ComboboxRoot$1 as St, FormRadioGroup$1 as T, OPT_DOT_GREEN as Tn, DialogDescription$1 as Tt, TagInput as U, OPT_TEXT_TERTIARY as Un, DisclosureRoot$1 as Ut, DEFAULT_PRESETS as V, OPT_TEXT_PRIMARY as Vn, TabsRoot$1 as Vt, IconPicker as W, OPT_TH as Wn, DisclosureTrigger$1 as Wt, PopoverTrigger$1 as X, StarRating as Xt, PopoverRoot$1 as Y, CompositeZone as Yt, Slider as Z, Input as Zt, FormGroupLabel$1 as _, OPT_BADGE_NEUTRAL as _n, ComboboxGroup$1 as _t, resolveFieldType as a, koComboboxLabels as an, TooltipProvider$1 as at, FormLabel$1 as b, OPT_BORDER as bn, ComboboxItem$1 as bt, CheckboxGroup as c, useControlledState as cn, MenuItemCheckbox$1 as ct, FormControl$1 as d, getDescribedBy as dn, MenuRoot$1 as dt, Spinner as en, ToolbarContainer$1 as et, FormDateRangePicker as f, OPT_ACTIVE_ITEM as fn, MenuSeparator$1 as ft, FormGroup$1 as g, OPT_BADGE_GREEN as gn, ComboboxEmpty as gt, FormField$1 as h, OPT_BADGE_BLUE as hn, MenubarRoot$1 as ht, FieldToken as i, koBreadcrumbLabels as in, TooltipAnchor$1 as it, FormSelectPopover as j, OPT_INPUT as jn, DialogPanel$1 as jt, FormSelect as k, OPT_FOCUS_VISIBLE as kn, DialogHeader as kt, FormCheckbox$1 as l, FieldMessages as ln, MenuItemRadio$1 as lt, FormError$1 as m, OPT_ANIMATE_ENTER_EXIT as mn, MenubarContainer$1 as mt, Highlight as n, EmptyState as nn, ToolbarSeparator$1 as nt, Pagination as o, koStarRatingLabels as on, MenuButtonArrow$1 as ot, FormDescription$1 as p, OPT_ANIMATE_DRAWER as pn, MenuTrigger$1 as pt, PopoverClose$1 as q, Kbd as qn, CompositeProvider as qt, FIELD_TYPE_TOKENS as r, Button as rn, Tooltip$1 as rt, Checkbox as s, StatCard as sn, MenuItem$1 as st, TextTruncate as t, Badge as tn, ToolbarRoot$1 as tt, FormColorPicker as u, RequiredMark as un, MenuPopover$1 as ut, FormIconPicker as v, OPT_BADGE_RED as vn, ComboboxGroupLabel$1 as vt, FormRadio$1 as w, OPT_DOT_GRAY as wn, DialogBody as wt, FormNumberInput as x, OPT_DISCLOSURE_CONTENT as xn, ComboboxPopover$1 as xt, FormInput$1 as y, OPT_BADGE_YELLOW as yn, ComboboxInput$1 as yt, useFormContext as z, OPT_TEXTAREA_SIZE as zn, TabList$1 as zt };
3849
4229
 
3850
- //# sourceMappingURL=pagination-DWbvACuN.js.map
4230
+ //# sourceMappingURL=text-truncate-j6X3z7Pv.js.map