@ai-matrx/design-system 0.86.363 → 0.86.365

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/index.d.cts CHANGED
@@ -3432,6 +3432,8 @@ interface PeekRow {
3432
3432
  };
3433
3433
  /** The row's text is a link: a web address opens in a new tab, an app path moves within the app. */
3434
3434
  href?: string;
3435
+ /** The row's text is an action the host runs in place (opening its own window). Wins over `href`. */
3436
+ onSelect?: () => void;
3435
3437
  /** A short tag at the row's right edge ("Needs attention"), its reason as the tooltip. */
3436
3438
  status?: {
3437
3439
  text: string;
@@ -3830,13 +3832,16 @@ declare function compactCount(n: number): string;
3830
3832
  * <SocialHandleValue value="https://www.linkedin.com/in/ana-ruiz" followers={1840} />
3831
3833
  * <UrlValue url="solarbright.com" title="SolarBright — home solar" />
3832
3834
  *
3833
- * Address validation is the host's (it calls a server): mount `ContactValuesHostProvider` once with
3834
- * `validateAddress`. Without one, the peek simply has no Validate action.
3835
+ * Address validation and web search are the host's: mount `ContactValuesHostProvider` once with
3836
+ * `validateAddress` (a server check) and `searchWeb` (the host's own search, opened in place). Without
3837
+ * them, the peek has no Validate action and "Search the web" is a plain outbound search link.
3835
3838
  */
3836
3839
 
3837
3840
  interface ContactValuesHost {
3838
3841
  /** Check a postal address against a real geocoder (the host's server route). */
3839
3842
  validateAddress?: (address: string) => Promise<AddressValidation>;
3843
+ /** Run a web search in the host's own search (a window), instead of an outbound link. */
3844
+ searchWeb?: (query: string) => void;
3840
3845
  }
3841
3846
  declare function ContactValuesHostProvider({ children, ...host }: ContactValuesHost & {
3842
3847
  children: React$1.ReactNode;
package/dist/index.d.ts CHANGED
@@ -3432,6 +3432,8 @@ interface PeekRow {
3432
3432
  };
3433
3433
  /** The row's text is a link: a web address opens in a new tab, an app path moves within the app. */
3434
3434
  href?: string;
3435
+ /** The row's text is an action the host runs in place (opening its own window). Wins over `href`. */
3436
+ onSelect?: () => void;
3435
3437
  /** A short tag at the row's right edge ("Needs attention"), its reason as the tooltip. */
3436
3438
  status?: {
3437
3439
  text: string;
@@ -3830,13 +3832,16 @@ declare function compactCount(n: number): string;
3830
3832
  * <SocialHandleValue value="https://www.linkedin.com/in/ana-ruiz" followers={1840} />
3831
3833
  * <UrlValue url="solarbright.com" title="SolarBright — home solar" />
3832
3834
  *
3833
- * Address validation is the host's (it calls a server): mount `ContactValuesHostProvider` once with
3834
- * `validateAddress`. Without one, the peek simply has no Validate action.
3835
+ * Address validation and web search are the host's: mount `ContactValuesHostProvider` once with
3836
+ * `validateAddress` (a server check) and `searchWeb` (the host's own search, opened in place). Without
3837
+ * them, the peek has no Validate action and "Search the web" is a plain outbound search link.
3835
3838
  */
3836
3839
 
3837
3840
  interface ContactValuesHost {
3838
3841
  /** Check a postal address against a real geocoder (the host's server route). */
3839
3842
  validateAddress?: (address: string) => Promise<AddressValidation>;
3843
+ /** Run a web search in the host's own search (a window), instead of an outbound link. */
3844
+ searchWeb?: (query: string) => void;
3840
3845
  }
3841
3846
  declare function ContactValuesHostProvider({ children, ...host }: ContactValuesHost & {
3842
3847
  children: React$1.ReactNode;
package/dist/index.js CHANGED
@@ -3042,6 +3042,20 @@ function RowLink({ href, children }) {
3042
3042
  }
3043
3043
  return /* @__PURE__ */ jsx19("a", { href, className, onClick: stop, children });
3044
3044
  }
3045
+ function RowAction({ onSelect, children }) {
3046
+ return /* @__PURE__ */ jsx19(
3047
+ "button",
3048
+ {
3049
+ type: "button",
3050
+ className: "max-w-full truncate text-left text-primary underline-offset-2 hover:underline",
3051
+ onClick: (event) => {
3052
+ event.stopPropagation();
3053
+ onSelect();
3054
+ },
3055
+ children
3056
+ }
3057
+ );
3058
+ }
3045
3059
  function PeekCardRows({ rows, numbered = false, limit }) {
3046
3060
  const shown = limit && rows.length > limit ? rows.slice(0, limit) : rows;
3047
3061
  const rest = rows.length - shown.length;
@@ -3055,7 +3069,7 @@ function PeekCardRows({ rows, numbered = false, limit }) {
3055
3069
  row.tone ? /* @__PURE__ */ jsx19("span", { "aria-hidden": true, className: cn("mt-[7px] size-1.5 shrink-0 rounded-full", TONE_FILL[row.tone]) }) : null,
3056
3070
  row.glyph ? /* @__PURE__ */ jsx19(row.glyph, { className: cn("mt-0.5 size-3.5 shrink-0 text-muted-foreground", lead) }) : null,
3057
3071
  /* @__PURE__ */ jsxs9("div", { className: "flex min-w-0 flex-1 flex-col", children: [
3058
- /* @__PURE__ */ jsx19("span", { className: "type-secondary min-w-0 truncate text-foreground", title: row.title, children: row.href ? /* @__PURE__ */ jsx19(RowLink, { href: row.href, children: row.text }) : row.text }),
3072
+ /* @__PURE__ */ jsx19("span", { className: "type-secondary min-w-0 truncate text-foreground", title: row.title, children: row.onSelect ? /* @__PURE__ */ jsx19(RowAction, { onSelect: row.onSelect, children: row.text }) : row.href ? /* @__PURE__ */ jsx19(RowLink, { href: row.href, children: row.text }) : row.text }),
3059
3073
  row.detail ? /* @__PURE__ */ jsx19("span", { className: "type-meta break-words text-muted-foreground", children: row.detail }) : null
3060
3074
  ] }),
3061
3075
  row.status ? /* @__PURE__ */ jsx19(
@@ -3787,6 +3801,7 @@ function Block({ block }) {
3787
3801
  glyph: row.glyph,
3788
3802
  tone: row.tone,
3789
3803
  href: row.entity ? void 0 : row.href,
3804
+ onSelect: row.entity ? void 0 : row.onSelect,
3790
3805
  status: row.status,
3791
3806
  doors: row.doors
3792
3807
  }))
@@ -4238,12 +4253,16 @@ function compactCount(n) {
4238
4253
  import { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs11 } from "react/jsx-runtime";
4239
4254
  var HostContext = React18.createContext({});
4240
4255
  function ContactValuesHostProvider({ children, ...host }) {
4241
- const value = React18.useMemo(() => host, [host.validateAddress]);
4256
+ const value = React18.useMemo(() => host, [host.validateAddress, host.searchWeb]);
4242
4257
  return /* @__PURE__ */ jsx22(HostContext.Provider, { value, children });
4243
4258
  }
4244
4259
  function useContactValuesHost() {
4245
4260
  return React18.useContext(HostContext);
4246
4261
  }
4262
+ function useSearchRow() {
4263
+ const { searchWeb } = useContactValuesHost();
4264
+ return (query) => searchWeb ? { text: "Search the web", glyph: SearchIcon, onSelect: () => searchWeb(query) } : { text: "Search the web", glyph: SearchIcon, href: webSearchHref(query) };
4265
+ }
4247
4266
  var markCache = /* @__PURE__ */ new Map();
4248
4267
  function markFor(platform) {
4249
4268
  let glyph = markCache.get(platform);
@@ -4311,9 +4330,23 @@ function usePinnedHover() {
4311
4330
  }, [pinned]);
4312
4331
  return { open: hovered || held || pinned, setHovered, setHeld, setPinned, trigger };
4313
4332
  }
4333
+ function closingOnSelect(record, close) {
4334
+ if (!record.body?.length) return record;
4335
+ const body = record.body.map(
4336
+ (block) => typeof block === "object" && block !== null && "rows" in block && Array.isArray(block.rows) ? { ...block, rows: block.rows.map((row) => row.onSelect ? { ...row, onSelect: () => {
4337
+ close();
4338
+ row.onSelect();
4339
+ } } : row) } : block
4340
+ );
4341
+ return { ...record, body };
4342
+ }
4314
4343
  function ValueShell({ kind, glyph: Glyph, text, copy, state, record: own, peek, wrap, about }) {
4315
- const record = withAbout(own, about);
4316
4344
  const { open, setHovered, setHeld, setPinned, trigger } = usePinnedHover();
4345
+ const record = closingOnSelect(withAbout(own, about), () => {
4346
+ setPinned(false);
4347
+ setHeld(false);
4348
+ setHovered(false);
4349
+ });
4317
4350
  const off = state !== "ok";
4318
4351
  const value = /* @__PURE__ */ jsx22("span", { className: cn("min-w-0", wrap ? "whitespace-pre-line break-words" : "truncate", off ? "text-muted-foreground line-through" : "text-foreground"), children: text });
4319
4352
  const status = off ? /* @__PURE__ */ jsx22("span", { className: "shrink-0 text-muted-foreground no-underline", children: `\xB7 ${CONTACT_STATE_LABEL[state]}` }) : null;
@@ -4376,6 +4409,7 @@ var actions = (rows) => ({
4376
4409
  });
4377
4410
  function EmailValue({ email, owner, state = "ok", peek = true, about }) {
4378
4411
  const parsed = parseEmail(email);
4412
+ const searchRow = useSearchRow();
4379
4413
  const ok = state === "ok" && parsed.valid;
4380
4414
  const site = parsed.domain ? parseUrl(parsed.domain) : null;
4381
4415
  const record = {
@@ -4389,7 +4423,7 @@ function EmailValue({ email, owner, state = "ok", peek = true, about }) {
4389
4423
  ...!parsed.valid && state === "ok" ? [{ fact: "Not a complete email address", glyph: LinkOffGlyph, muted: true }] : [],
4390
4424
  ...parsed.domain ? [{ property: "Domain", value: parsed.domain, ...ok && site?.href ? { href: site.href } : {} }] : [],
4391
4425
  actions([
4392
- { text: "Search the web", glyph: SearchIcon, href: webSearchHref(`"${parsed.address}"`) }
4426
+ searchRow(`"${parsed.address}"`)
4393
4427
  ])
4394
4428
  ],
4395
4429
  ...ok ? { primary: { label: "Send email", onSelect: () => window.location.assign(mailtoHref(parsed.address)) } } : {},
@@ -4397,8 +4431,13 @@ function EmailValue({ email, owner, state = "ok", peek = true, about }) {
4397
4431
  };
4398
4432
  return /* @__PURE__ */ jsx22(ValueShell, { kind: "email", glyph: MailGlyph, text: parsed.address, copy: parsed.address, state, record, peek, ...about !== void 0 ? { about } : {} });
4399
4433
  }
4434
+ function phoneSearchText(parsed) {
4435
+ const na = parsed.e164?.match(/^\+1(\d{3})(\d{3})(\d{4})$/);
4436
+ return na ? `${na[1]}-${na[2]}-${na[3]}` : parsed.display;
4437
+ }
4400
4438
  function PhoneValue({ phone, owner, state = "ok", peek = true, defaultCallingCode = "1", about }) {
4401
4439
  const parsed = parsePhone(phone, defaultCallingCode);
4440
+ const searchRow = useSearchRow();
4402
4441
  const ok = state === "ok" && parsed.valid;
4403
4442
  const record = {
4404
4443
  kind: "Phone number",
@@ -4414,7 +4453,7 @@ function PhoneValue({ phone, owner, state = "ok", peek = true, defaultCallingCod
4414
4453
  ...parsed.extension ? [{ property: "Extension", value: parsed.extension }] : [],
4415
4454
  actions([
4416
4455
  ok && { text: "Send a text", glyph: MessageSquareGlyph, href: smsHref(parsed) },
4417
- { text: "Search the web", glyph: SearchIcon, href: webSearchHref(`"${parsed.display}"`) }
4456
+ searchRow(phoneSearchText(parsed))
4418
4457
  ])
4419
4458
  ],
4420
4459
  ...ok ? { primary: { label: "Call", onSelect: () => window.location.assign(telHref(parsed)) } } : {},
@@ -4428,6 +4467,7 @@ function AddressValue({ address, owner, state = "ok", peek = true, multiline = f
4428
4467
  const one = addressText(address);
4429
4468
  const lines = typeof address === "string" ? address.split(/\n+/).map((l) => l.trim()).filter(Boolean) : address.map((l) => (l ?? "").trim()).filter(Boolean);
4430
4469
  const { validateAddress } = useContactValuesHost();
4470
+ const searchRow = useSearchRow();
4431
4471
  const [validation, setValidation] = React18.useState({ phase: "idle" });
4432
4472
  React18.useEffect(() => setValidation({ phase: "idle" }), [one]);
4433
4473
  const ok = state === "ok" && one.length > 0;
@@ -4463,7 +4503,7 @@ function AddressValue({ address, owner, state = "ok", peek = true, multiline = f
4463
4503
  ...validationBlock ? [validationBlock] : [],
4464
4504
  actions([
4465
4505
  ok && validateAddress && validation.phase !== "done" && { text: "Open in maps", glyph: MapPinGlyph, href: mapsHref(one) },
4466
- { text: "Search the web", glyph: SearchIcon, href: webSearchHref(one) }
4506
+ searchRow(one)
4467
4507
  ])
4468
4508
  ],
4469
4509
  ...ok && validateAddress && validation.phase !== "done" ? { primary: { label: validation.phase === "checking" ? "Checking\u2026" : "Validate", onSelect: validate } } : ok ? { primary: { label: "Open in maps", onSelect: () => openOutbound2(mapsHref(validation.phase === "done" && validation.result.formatted ? validation.result.formatted : one)) } } : {},
@@ -4484,7 +4524,7 @@ function AddressValue({ address, owner, state = "ok", peek = true, multiline = f
4484
4524
  }
4485
4525
  );
4486
4526
  }
4487
- function socialRecord(parsed, props, glyph) {
4527
+ function socialRecord(parsed, props, glyph, searchRow) {
4488
4528
  const state = props.state ?? "ok";
4489
4529
  const ok = state === "ok" && Boolean(parsed.href);
4490
4530
  return {
@@ -4500,7 +4540,7 @@ function socialRecord(parsed, props, glyph) {
4500
4540
  ...props.followers != null ? [{ fact: `${compactCount(props.followers)} followers`, glyph: UsersIcon }] : [],
4501
4541
  ...props.owner ? [{ property: "For", value: props.owner }] : [],
4502
4542
  actions([
4503
- { text: "Search the web", glyph: SearchIcon, href: webSearchHref(`${parsed.display} ${parsed.platformName}`) }
4543
+ searchRow(`${parsed.display} ${parsed.platformName}`)
4504
4544
  ])
4505
4545
  ],
4506
4546
  ...ok ? { primary: { label: "Open profile", onSelect: () => openOutbound2(parsed.href) } } : {},
@@ -4510,6 +4550,7 @@ function socialRecord(parsed, props, glyph) {
4510
4550
  function SocialHandleValue(props) {
4511
4551
  const { value, platform, state = "ok", peek = true } = props;
4512
4552
  const parsed = parseSocial(value, platform);
4553
+ const searchRow = useSearchRow();
4513
4554
  const glyph = parsed.platform === "other" ? AtSignGlyph : markFor(parsed.platform);
4514
4555
  return /* @__PURE__ */ jsx22(
4515
4556
  ValueShell,
@@ -4519,7 +4560,7 @@ function SocialHandleValue(props) {
4519
4560
  text: parsed.display,
4520
4561
  copy: parsed.handle ? parsed.display : parsed.href ?? parsed.display,
4521
4562
  state,
4522
- record: socialRecord(parsed, props, glyph),
4563
+ record: socialRecord(parsed, props, glyph, searchRow),
4523
4564
  peek,
4524
4565
  ...props.about !== void 0 ? { about: props.about } : {}
4525
4566
  }
@@ -4527,6 +4568,7 @@ function SocialHandleValue(props) {
4527
4568
  }
4528
4569
  function UrlValue({ url, title, owner, state = "ok", peek = true, icon = true, about }) {
4529
4570
  const parsed = parseUrl(url);
4571
+ const searchRow = useSearchRow();
4530
4572
  const ok = state === "ok" && Boolean(parsed.href);
4531
4573
  const record = {
4532
4574
  kind: "Web address",
@@ -4539,7 +4581,7 @@ function UrlValue({ url, title, owner, state = "ok", peek = true, icon = true, a
4539
4581
  { property: "Domain", value: parsed.domain },
4540
4582
  ...owner && title ? [{ property: "For", value: owner }] : [],
4541
4583
  actions([
4542
- { text: "Search the web", glyph: SearchIcon, href: webSearchHref(parsed.domain) }
4584
+ searchRow(parsed.domain)
4543
4585
  ])
4544
4586
  ],
4545
4587
  ...ok ? { primary: { label: "Open", onSelect: () => openOutbound2(parsed.href) } } : {},