@servicetitan/anvil2-ext-common 0.3.20 → 0.5.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 (48) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +27 -0
  3. package/dist/Suggestions-ChGGcGuO.js +47 -0
  4. package/dist/Suggestions-ChGGcGuO.js.map +1 -0
  5. package/dist/Suggestions.d.ts +1 -0
  6. package/dist/Suggestions.js +2 -0
  7. package/dist/Suggestions.js.map +1 -0
  8. package/dist/index.css +2915 -0
  9. package/dist/index.js +35857 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/src/components/Suggestions/Suggestions.d.ts +59 -0
  12. package/dist/src/components/Suggestions/index.d.ts +2 -0
  13. package/dist/src/components/index.d.ts +1 -0
  14. package/dist/src/form/components/Form.d.ts +15 -0
  15. package/dist/src/form/components/FormCheckboxGroup/FormCheckboxGroup.d.ts +13 -0
  16. package/dist/src/form/components/FormCheckboxGroup/index.d.ts +1 -0
  17. package/dist/src/form/components/FormDateFieldRange/FormDateFieldRange.d.ts +6 -0
  18. package/dist/src/form/components/FormDateFieldRange/index.d.ts +1 -0
  19. package/dist/src/form/components/FormDateFieldSingle/FormDateFieldSingle.d.ts +6 -0
  20. package/dist/src/form/components/FormDateFieldSingle/index.d.ts +1 -0
  21. package/dist/src/form/components/FormDateFieldYearless/FormDateFieldYearless.d.ts +6 -0
  22. package/dist/src/form/components/FormDateFieldYearless/index.d.ts +1 -0
  23. package/dist/src/form/components/FormDateFieldYearlessRange/FormDateFieldYearlessRange.d.ts +6 -0
  24. package/dist/src/form/components/FormDateFieldYearlessRange/index.d.ts +1 -0
  25. package/dist/src/form/components/FormMultiSelect/FormMultiSelect.d.ts +12 -0
  26. package/dist/src/form/components/FormMultiSelect/index.d.ts +1 -0
  27. package/dist/src/form/components/FormNumberField/FormNumberField.d.ts +6 -0
  28. package/dist/src/form/components/FormNumberField/index.d.ts +1 -0
  29. package/dist/src/form/components/FormRadioGroup/FormRadioGroup.d.ts +13 -0
  30. package/dist/src/form/components/FormRadioGroup/index.d.ts +1 -0
  31. package/dist/src/form/components/FormSelect/FormSelect.d.ts +11 -0
  32. package/dist/src/form/components/FormSelect/index.d.ts +1 -0
  33. package/dist/src/form/components/FormTextField/FormTextField.d.ts +6 -0
  34. package/dist/src/form/components/FormTextField/index.d.ts +1 -0
  35. package/dist/src/form/components/FormTextarea/FormTextarea.d.ts +6 -0
  36. package/dist/src/form/components/FormTextarea/index.d.ts +1 -0
  37. package/dist/src/form/components/FormTimeField/FormTimeField.d.ts +6 -0
  38. package/dist/src/form/components/FormTimeField/index.d.ts +1 -0
  39. package/dist/src/form/components/FormTreeSelect/FormTreeSelect.d.ts +12 -0
  40. package/dist/src/form/components/FormTreeSelect/index.d.ts +1 -0
  41. package/dist/src/form/components/index.d.ts +13 -0
  42. package/dist/src/form/index.d.ts +3 -0
  43. package/dist/src/form/state/A2InputFieldState.d.ts +54 -0
  44. package/dist/src/form/state/SelectableOptionsFieldState.d.ts +20 -0
  45. package/dist/src/form/state/index.d.ts +3 -0
  46. package/dist/src/form/state/types.d.ts +31 -0
  47. package/dist/src/index.d.ts +1 -0
  48. package/package.json +8 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @servicetitan/anvil2-ext-common
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#2386](https://github.com/servicetitan/hammer/pull/2386) [`efd6121`](https://github.com/servicetitan/hammer/commit/efd6121759713cefb12e51387183bc97c0c3aec6) Thanks [@hbartlett-st](https://github.com/hbartlett-st)! - [Suggestions] Add new render-prop component that coordinates highlighting fields populated by a suggestion until they are edited. Exposes `suggestable` and `suggestor` prop-getters plus imperative `markSuggested`, `clearHighlight`, and `isHighlighted` methods.
8
+
9
+ ### Patch Changes
10
+
11
+ - [#2417](https://github.com/servicetitan/hammer/pull/2417) [`4c60241`](https://github.com/servicetitan/hammer/commit/4c602418f0fb7da06961e76781b44bac02d0a7d0) Thanks [@hbartlett-st](https://github.com/hbartlett-st)! - [Dependencies] Loosen `@servicetitan/anvil2` dependency to `workspace:^` so consumers resolve a caret range instead of a pinned version
12
+
13
+ - Updated dependencies [[`efd6121`](https://github.com/servicetitan/hammer/commit/efd6121759713cefb12e51387183bc97c0c3aec6), [`58eb789`](https://github.com/servicetitan/hammer/commit/58eb78970e47ffeabbe57a97c345cdbe8edd4a01), [`1c606c5`](https://github.com/servicetitan/hammer/commit/1c606c5f0bd4d7416cb0f4b6fcb409c4b6377d6b), [`efd6121`](https://github.com/servicetitan/hammer/commit/efd6121759713cefb12e51387183bc97c0c3aec6), [`efd6121`](https://github.com/servicetitan/hammer/commit/efd6121759713cefb12e51387183bc97c0c3aec6)]:
14
+ - @servicetitan/anvil2@3.2.0
15
+
16
+ ## 0.4.0
17
+
18
+ ### Minor Changes
19
+
20
+ - [#2393](https://github.com/servicetitan/hammer/pull/2393) [`fa64efa`](https://github.com/servicetitan/hammer/commit/fa64efa889ee2571504fe5ade2dbffc702132c80) Thanks [@pbuckingham-st](https://github.com/pbuckingham-st)! - [Form] Add formstate field-state wrappers (`A2InputFieldState`, `SelectableOptionsFieldState`) and a `Form.*` namespace of `observer` binding components for Anvil2 input fields (TextField, Textarea, NumberField, DateFieldSingle/Range/Yearless/YearlessRange, TimeField, beta Select/MultiSelect/TreeSelect, and RadioGroup/CheckboxGroup). Checkbox/radio are provided as the group wrappers only.
21
+
22
+ ### Patch Changes
23
+
24
+ - Updated dependencies [[`5e46d4f`](https://github.com/servicetitan/hammer/commit/5e46d4f7760c8f4c3374523f9777f34b61f72270), [`f13b003`](https://github.com/servicetitan/hammer/commit/f13b0036669dc8ba3e566de88374b4487bb9891b), [`f8e27ae`](https://github.com/servicetitan/hammer/commit/f8e27ae598b1a2c4900cd2dc16e6efcb3a11fbb9)]:
25
+ - @servicetitan/anvil2@3.1.0
26
+
3
27
  ## 0.3.20
4
28
 
5
29
  ### Patch Changes
package/README.md CHANGED
@@ -25,6 +25,33 @@ Components and hooks can be imported as named exports from the package root:
25
25
  import { useConfirmDialog } from "@servicetitan/anvil2-ext-common";
26
26
  ```
27
27
 
28
+ ### Form field-state wrappers (`Form.*`)
29
+
30
+ `Form.*` components bind Anvil2 fields to a `formstate`-based `A2InputFieldState`, eliminating boilerplate for value, onChange, error, and warning wiring.
31
+
32
+ **Quick example:**
33
+
34
+ ```tsx
35
+ import { A2InputFieldState } from "@servicetitan/anvil2-ext-common";
36
+ import { Form } from "@servicetitan/anvil2-ext-common";
37
+
38
+ const name = new A2InputFieldState("");
39
+
40
+ <Form.TextField fieldState={name} label="Name" />;
41
+ ```
42
+
43
+ Available wrappers: `Form.TextField`, `Form.Textarea`, `Form.NumberField`, `Form.DateFieldSingle`, `Form.DateFieldRange`, `Form.DateFieldYearless`, `Form.DateFieldYearlessRange`, `Form.TimeField`, `Form.Select`, `Form.MultiSelect`, `Form.TreeSelect`, `Form.RadioGroup`, `Form.CheckboxGroup`.
44
+
45
+ Checkbox and radio inputs are provided as the **group** wrappers (`Form.RadioGroup`, `Form.CheckboxGroup`) only. A standalone Anvil2 `Checkbox`/`Radio` exposes a boolean error (styling) with no message area, so it can't surface validation errors/warnings — the group is the form pattern that renders a `FieldMessage`.
46
+
47
+ `Form.Select`, `Form.MultiSelect`, and `Form.TreeSelect` wrap the beta Anvil2 components and are exported from the package root alongside the rest of the `Form.*` namespace. `Form.Select` and `Form.MultiSelect` support all loader modes (eager, page-lazy, offset-lazy, group-lazy) — pass a `loadOptions` function appropriate to your loader strategy.
48
+
49
+ **Known limitations:**
50
+
51
+ - Single checkbox/radio inputs have no standalone wrapper — use `Form.CheckboxGroup` / `Form.RadioGroup` (the Anvil2 group is the component that renders validation messages).
52
+ - `updateMode` is primarily meaningful for value-update controls (TextField, Textarea, NumberField).
53
+ - The following Anvil2 input types have no `Form.*` wrapper: SearchField, InputMask, Switch, ButtonToggle, SelectCard, Combobox, SegmentedControl, RichTextEditor.
54
+
28
55
  <br />
29
56
 
30
57
  # Contribution Guide
@@ -0,0 +1,47 @@
1
+ import { jsx, Fragment } from 'react/jsx-runtime';
2
+ import { useState, useRef } from 'react';
3
+
4
+ const wrap = (handlers, before) => {
5
+ if (!handlers) {
6
+ return {};
7
+ }
8
+ const out = {};
9
+ for (const name of Object.keys(handlers)) {
10
+ const fn = handlers[name];
11
+ out[name] = (...args) => {
12
+ before();
13
+ fn?.(...args);
14
+ };
15
+ }
16
+ return out;
17
+ };
18
+ function Suggestions({ children }) {
19
+ const [suggested, setSuggested] = useState(
20
+ () => /* @__PURE__ */ new Set()
21
+ );
22
+ const registry = useRef(/* @__PURE__ */ new Set());
23
+ const markSuggested = (keys) => setSuggested(new Set(keys ?? [...registry.current]));
24
+ const clearHighlight = (key) => setSuggested((prev) => {
25
+ if (!prev.has(key)) return prev;
26
+ const next = new Set(prev);
27
+ next.delete(key);
28
+ return next;
29
+ });
30
+ const api = {
31
+ suggestable: (key, handlers) => {
32
+ registry.current.add(key);
33
+ return {
34
+ isHighlighted: suggested.has(key),
35
+ ...wrap(handlers, () => clearHighlight(key))
36
+ };
37
+ },
38
+ suggestor: (handlers, keys) => wrap(handlers, () => markSuggested(keys)),
39
+ markSuggested,
40
+ clearHighlight,
41
+ isHighlighted: (key) => suggested.has(key)
42
+ };
43
+ return /* @__PURE__ */ jsx(Fragment, { children: children(api) });
44
+ }
45
+
46
+ export { Suggestions as S };
47
+ //# sourceMappingURL=Suggestions-ChGGcGuO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Suggestions-ChGGcGuO.js","sources":["../src/components/Suggestions/Suggestions.tsx"],"sourcesContent":["import { ReactNode, useRef, useState } from \"react\";\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- prop getters are\n * intentionally field-agnostic: they wrap whatever change handlers a given\n * field exposes (onChange, onSelectedOptionChange, ...) without caring about\n * their argument shapes. */\ntype Handler = (...args: any[]) => void;\ntype Handlers = Record<string, Handler | undefined>;\n\nexport interface SuggestionsApi {\n /**\n * Props for a field that should be highlighted while it holds a suggested\n * value. Returns `isHighlighted` plus copies of any handlers you pass, each\n * wrapped to clear the highlight on the first user edit. Pass the handler\n * under the field's own prop name (`onChange`, `onSelectedOptionChange`, ...).\n *\n * @example <TextField {...suggestable(\"city\", { onChange: setCity })} />\n */\n suggestable: <O extends Handlers>(\n key: string,\n handlers?: O,\n ) => { isHighlighted: boolean } & O;\n /**\n * Props for the field whose selection populates the others. Returns copies of\n * the handlers you pass, each wrapped to first mark the suggestable fields as\n * highlighted. Your handler still does the value population.\n *\n * @param keys which fields to highlight; defaults to every registered key.\n * @example <TypeaheadTextField {...suggestor({ onSelectOption: apply })} />\n */\n suggestor: <O extends Handlers>(handlers?: O, keys?: string[]) => O;\n /** Imperatively highlight fields (defaults to every registered key). */\n markSuggested: (keys?: string[]) => void;\n /** Imperatively clear a field's highlight. */\n clearHighlight: (key: string) => void;\n /** Whether a field is currently holding a suggested value. */\n isHighlighted: (key: string) => boolean;\n}\n\nexport interface SuggestionsProps {\n children: (api: SuggestionsApi) => ReactNode;\n}\n\nconst wrap = <O extends Handlers>(\n handlers: O | undefined,\n before: () => void,\n): O => {\n if (!handlers) {\n return {} as O;\n }\n\n const out: Handlers = {};\n for (const name of Object.keys(handlers)) {\n const fn = handlers[name];\n out[name] = (...args) => {\n before();\n fn?.(...args);\n };\n }\n return out as O;\n};\n\n/**\n * Coordinates the \"highlight fields populated by a suggestion until edited\"\n * behavior for a group of fields, without per-field state wiring.\n *\n * Relies on the fact that a controlled field's change handler fires only on\n * user input, never when its `value` prop changes programmatically — so we\n * track provenance per field rather than comparing values. A field stays\n * un-highlighted after an edit even if the suggested value is re-entered,\n * because typing still fires the handler.\n *\n * @example\n * <Suggestions>\n * {({ suggestor, suggestable }) => (\n * <>\n * <TypeaheadTextField\n * {...suggestor({ onSelectOption: (opt) => applyAddress(opt.value) })}\n * loadOptions={lookupAddresses}\n * />\n * <TextField {...suggestable(\"city\", { onChange: handleCity })} />\n * </>\n * )}\n * </Suggestions>\n */\nexport function Suggestions({ children }: SuggestionsProps) {\n const [suggested, setSuggested] = useState<ReadonlySet<string>>(\n () => new Set(),\n );\n const registry = useRef<Set<string>>(new Set());\n\n const markSuggested = (keys?: string[]) =>\n setSuggested(new Set(keys ?? [...registry.current]));\n\n const clearHighlight = (key: string) =>\n setSuggested((prev) => {\n if (!prev.has(key)) return prev; // keep referential stability\n const next = new Set(prev);\n next.delete(key);\n return next;\n });\n\n const api: SuggestionsApi = {\n suggestable: (key, handlers) => {\n registry.current.add(key); // learn keys so markSuggested() can target all\n return {\n isHighlighted: suggested.has(key),\n ...wrap(handlers, () => clearHighlight(key)),\n };\n },\n suggestor: (handlers, keys) => wrap(handlers, () => markSuggested(keys)),\n markSuggested,\n clearHighlight,\n isHighlighted: (key) => suggested.has(key),\n };\n\n return <>{children(api)}</>;\n}\n"],"names":[],"mappings":";;;AA2CA,MAAM,IAAA,GAAO,CACX,QAAA,EACA,MAAA,KACM;AACN,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,EAAC;AAAA,EACV;AAEA,EAAA,MAAM,MAAgB,EAAC;AACvB,EAAA,KAAA,MAAW,IAAA,IAAQ,MAAA,CAAO,IAAA,CAAK,QAAQ,CAAA,EAAG;AACxC,IAAA,MAAM,EAAA,GAAK,SAAS,IAAI,CAAA;AACxB,IAAA,GAAA,CAAI,IAAI,CAAA,GAAI,CAAA,GAAI,IAAA,KAAS;AACvB,MAAA,MAAA,EAAO;AACP,MAAA,EAAA,GAAK,GAAG,IAAI,CAAA;AAAA,IACd,CAAA;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT,CAAA;AAyBO,SAAS,WAAA,CAAY,EAAE,QAAA,EAAS,EAAqB;AAC1D,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,QAAA;AAAA,IAChC,0BAAU,GAAA;AAAI,GAChB;AACA,EAAA,MAAM,QAAA,GAAW,MAAA,iBAAoB,IAAI,GAAA,EAAK,CAAA;AAE9C,EAAA,MAAM,aAAA,GAAgB,CAAC,IAAA,KACrB,YAAA,CAAa,IAAI,GAAA,CAAI,IAAA,IAAQ,CAAC,GAAG,QAAA,CAAS,OAAO,CAAC,CAAC,CAAA;AAErD,EAAA,MAAM,cAAA,GAAiB,CAAC,GAAA,KACtB,YAAA,CAAa,CAAC,IAAA,KAAS;AACrB,IAAA,IAAI,CAAC,IAAA,CAAK,GAAA,CAAI,GAAG,GAAG,OAAO,IAAA;AAC3B,IAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,IAAI,CAAA;AACzB,IAAA,IAAA,CAAK,OAAO,GAAG,CAAA;AACf,IAAA,OAAO,IAAA;AAAA,EACT,CAAC,CAAA;AAEH,EAAA,MAAM,GAAA,GAAsB;AAAA,IAC1B,WAAA,EAAa,CAAC,GAAA,EAAK,QAAA,KAAa;AAC9B,MAAA,QAAA,CAAS,OAAA,CAAQ,IAAI,GAAG,CAAA;AACxB,MAAA,OAAO;AAAA,QACL,aAAA,EAAe,SAAA,CAAU,GAAA,CAAI,GAAG,CAAA;AAAA,QAChC,GAAG,IAAA,CAAK,QAAA,EAAU,MAAM,cAAA,CAAe,GAAG,CAAC;AAAA,OAC7C;AAAA,IACF,CAAA;AAAA,IACA,SAAA,EAAW,CAAC,QAAA,EAAU,IAAA,KAAS,KAAK,QAAA,EAAU,MAAM,aAAA,CAAc,IAAI,CAAC,CAAA;AAAA,IACvE,aAAA;AAAA,IACA,cAAA;AAAA,IACA,aAAA,EAAe,CAAC,GAAA,KAAQ,SAAA,CAAU,IAAI,GAAG;AAAA,GAC3C;AAEA,EAAA,uBAAO,GAAA,CAAA,QAAA,EAAA,EAAG,QAAA,EAAA,QAAA,CAAS,GAAG,CAAA,EAAE,CAAA;AAC1B;;;;"}
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,2 @@
1
+ export { S as Suggestions, S as default } from './Suggestions-ChGGcGuO.js';
2
+ //# sourceMappingURL=Suggestions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Suggestions.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}