@coopdigital/react 1.30.2 → 1.31.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 (53) hide show
  1. package/dist/components/Author/Author.d.ts +3 -1
  2. package/dist/components/Author/Author.js +1 -1
  3. package/dist/components/Author/index.d.ts +2 -2
  4. package/dist/components/Author/index.js +2 -2
  5. package/dist/components/Combobox/Combobox.d.ts +1 -1
  6. package/dist/components/Combobox/Combobox.js +10 -5
  7. package/dist/components/Combobox/{combobox-utils.js → combobox-internal.js} +4 -2
  8. package/dist/components/DateInput/DateInput.context.js +7 -0
  9. package/dist/components/DateInput/DateInput.d.ts +104 -0
  10. package/dist/components/DateInput/DateInput.js +128 -0
  11. package/dist/components/DateInput/dateinput-internal.d.ts +6 -0
  12. package/dist/components/DateInput/dateinput-internal.js +82 -0
  13. package/dist/components/DateInput/dateinput-utils.d.ts +66 -0
  14. package/dist/components/DateInput/dateinput-utils.js +91 -0
  15. package/dist/components/DateInput/index.d.ts +3 -0
  16. package/dist/components/DateInput/index.js +6 -0
  17. package/dist/components/Field/Field.context.js +1 -1
  18. package/dist/components/Field/Field.js +1 -1
  19. package/dist/components/FieldMarkers/{fieldmarkers-utils.js → fieldmarkers-internal.js} +1 -1
  20. package/dist/components/Fieldset/Fieldset.js +1 -1
  21. package/dist/components/Form/Form.d.ts +7 -1
  22. package/dist/components/Form/Form.js +2 -1
  23. package/dist/components/Squircle/Squircle.d.ts +3 -1
  24. package/dist/components/Squircle/Squircle.js +1 -1
  25. package/dist/components/Squircle/index.d.ts +2 -2
  26. package/dist/components/Squircle/index.js +2 -2
  27. package/dist/components/ValidationSummary/ValidationSummary.d.ts +1 -1
  28. package/dist/components/ValidationSummary/ValidationSummary.js +1 -1
  29. package/dist/components/ValidationSummary/{validationsummary-utils.d.ts → validationsummary-internal.d.ts} +1 -1
  30. package/dist/components/ValidationSummary/{validationsummary-utils.js → validationsummary-internal.js} +3 -2
  31. package/dist/index.d.ts +6 -3
  32. package/dist/index.js +6 -4
  33. package/package.json +6 -5
  34. package/src/components/Author/Author.tsx +1 -1
  35. package/src/components/Combobox/Combobox.tsx +29 -20
  36. package/src/components/Combobox/combobox-internal.ts +60 -0
  37. package/src/components/Combobox/combobox-utils.ts +1 -41
  38. package/src/components/Combobox/index.ts +1 -0
  39. package/src/components/DateInput/DateInput.context.ts +25 -0
  40. package/src/components/DateInput/DateInput.tsx +252 -0
  41. package/src/components/DateInput/dateinput-internal.ts +176 -0
  42. package/src/components/DateInput/dateinput-utils.ts +194 -0
  43. package/src/components/DateInput/index.ts +6 -0
  44. package/src/components/Field/Field.context.ts +1 -1
  45. package/src/components/Field/Field.tsx +1 -1
  46. package/src/components/Fieldset/Fieldset.tsx +5 -1
  47. package/src/components/Form/Form.tsx +9 -1
  48. package/src/components/Squircle/Squircle.tsx +1 -1
  49. package/src/components/ValidationSummary/ValidationSummary.context.ts +1 -1
  50. package/src/components/ValidationSummary/ValidationSummary.tsx +1 -1
  51. package/src/components/ValidationSummary/{validationsummary-utils.ts → validationsummary-internal.ts} +9 -8
  52. package/src/index.ts +1 -0
  53. /package/src/components/FieldMarkers/{fieldmarkers-utils.ts → fieldmarkers-internal.ts} +0 -0
@@ -1,3 +1,4 @@
1
+ import { StandardSizes } from "../../types/index.js";
1
2
  import { FormHTMLAttributes, JSX, ReactNode, Ref } from "react";
2
3
  //#region src/components/Form/Form.d.ts
3
4
  export interface FormProps extends FormHTMLAttributes<HTMLFormElement> {
@@ -7,9 +8,14 @@ export interface FormProps extends FormHTMLAttributes<HTMLFormElement> {
7
8
  className?: string;
8
9
  /** Specify a custom React ref for this component. */
9
10
  ref?: Ref<HTMLFormElement>;
11
+ /** Specify the spacing.
12
+ *
13
+ * Default: `md`
14
+ */
15
+ spacing?: StandardSizes;
10
16
  }
11
17
  declare const Form: {
12
- ({ children, className, ref, ...props }: FormProps): JSX.Element;
18
+ ({ children, className, ref, spacing, ...props }: FormProps): JSX.Element;
13
19
  slot: {
14
20
  name: string;
15
21
  };
@@ -1,9 +1,10 @@
1
1
  import clsx from "clsx";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  //#region src/components/Form/Form.tsx
4
- const Form = ({ children, className, ref, ...props }) => {
4
+ const Form = ({ children, className, ref, spacing, ...props }) => {
5
5
  const componentProps = {
6
6
  className: clsx("coop-form", className),
7
+ "data-spacing": spacing ?? void 0,
7
8
  ...props
8
9
  };
9
10
  return /* @__PURE__ */ jsx("form", {
@@ -22,5 +22,7 @@ declare const Squircle: {
22
22
  name: string;
23
23
  };
24
24
  };
25
+ declare const SquircleSvg: ({ size, ...props }: SquircleSvgProps) => JSX.Element;
26
+ declare const SquircleText: ({ children, className, ...props }: HTMLAttributes<HTMLDivElement>) => JSX.Element;
25
27
  //#endregion
26
- export { Squircle, Squircle as default };
28
+ export { Squircle, Squircle as default, SquircleSvg, SquircleText };
@@ -46,4 +46,4 @@ Squircle.Svg = SquircleSvg;
46
46
  Squircle.Text = SquircleText;
47
47
  Squircle.slot = { name: "Squircle" };
48
48
  //#endregion
49
- export { Squircle, Squircle as default };
49
+ export { Squircle, Squircle as default, SquircleSvg, SquircleText };
@@ -1,2 +1,2 @@
1
- import { Squircle, SquircleProps, SquircleSvgProps } from "./Squircle.js";
2
- export { Squircle, Squircle as default, SquircleProps, SquircleSvgProps };
1
+ import { Squircle, SquircleProps, SquircleSvg, SquircleSvgProps, SquircleText } from "./Squircle.js";
2
+ export { Squircle, Squircle as default, SquircleProps, SquircleSvg, SquircleSvgProps, SquircleText };
@@ -1,5 +1,5 @@
1
- import { Squircle } from "./Squircle.js";
1
+ import { Squircle, SquircleSvg, SquircleText } from "./Squircle.js";
2
2
  //#region src/components/Squircle/index.ts
3
3
  var Squircle_default = Squircle;
4
4
  //#endregion
5
- export { Squircle, Squircle_default as default };
5
+ export { Squircle, SquircleSvg, SquircleText, Squircle_default as default };
@@ -1,4 +1,4 @@
1
- import { FieldError } from "./validationsummary-utils.js";
1
+ import { FieldError } from "./validationsummary-internal.js";
2
2
  import React, { HTMLAttributes, JSX, ReactNode, Ref, RefObject } from "react";
3
3
  //#region src/components/ValidationSummary/ValidationSummary.d.ts
4
4
  export interface ValidationSummaryProps extends HTMLAttributes<HTMLDivElement> {
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { getSlotProp } from "../../utils/slots.js";
3
3
  import { useSlots } from "../../hooks/useSlots.js";
4
- import { getFieldElement, getFieldOrder, getForm, sanitizeFieldId, sortErrorsByFieldOrder } from "./validationsummary-utils.js";
4
+ import { getFieldElement, getFieldOrder, getForm, sanitizeFieldId, sortErrorsByFieldOrder } from "./validationsummary-internal.js";
5
5
  import { ValidationSummaryContext, useValidationSummary } from "./ValidationSummary.context.js";
6
6
  import clsx from "clsx";
7
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -1,5 +1,5 @@
1
1
  import { RefObject } from "react";
2
- //#region src/components/ValidationSummary/validationsummary-utils.d.ts
2
+ //#region src/components/ValidationSummary/validationsummary-internal.d.ts
3
3
  export interface FieldError {
4
4
  message?: string | number;
5
5
  ref?: {
@@ -1,4 +1,4 @@
1
- //#region src/components/ValidationSummary/validationsummary-utils.ts
1
+ //#region src/components/ValidationSummary/validationsummary-internal.ts
2
2
  const sanitizeFieldId = (id) => id?.startsWith("#") ? id.slice(1) : id;
3
3
  const getFieldKeys = (field, id) => [
4
4
  ...id ? [sanitizeFieldId(id)] : [],
@@ -6,11 +6,12 @@ const getFieldKeys = (field, id) => [
6
6
  field
7
7
  ];
8
8
  const VISIBLE_CONTROL = "input:not([type=\"hidden\"]), select, textarea";
9
+ const INVALID_CONTROL = `:is(${VISIBLE_CONTROL})[aria-invalid="true"]`;
9
10
  const isHiddenInput = (el) => el instanceof HTMLInputElement && el.type === "hidden";
10
11
  const getVisibleControl = (form, el) => {
11
12
  for (let node = el.parentElement; node && node !== form; node = node.parentElement) {
12
13
  const control = node.querySelector(VISIBLE_CONTROL);
13
- if (control) return control;
14
+ if (control) return node.querySelector(INVALID_CONTROL) ?? control;
14
15
  }
15
16
  return el;
16
17
  };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AlertBanner, AlertBannerHeading, AlertBannerProps } from "./components/AlertBanner/AlertBanner.js";
2
2
  import { Image, ImageProps } from "./components/Image/Image.js";
3
- import { Author, AuthorImageProps, AuthorMetaProps, AuthorProps } from "./components/Author/Author.js";
3
+ import { Author, AuthorImage, AuthorImageProps, AuthorMeta, AuthorMetaProps, AuthorProps } from "./components/Author/Author.js";
4
4
  import { Box, BoxProps } from "./components/Box/Box.js";
5
5
  import "./components/Box/index.js";
6
6
  import { Button, ButtonProps } from "./components/Button/Button.js";
@@ -12,6 +12,9 @@ import { ComboboxItem, ComboboxOption } from "./components/Combobox/combobox-uti
12
12
  import { TextInput, TextInputProps } from "./components/TextInput/TextInput.js";
13
13
  import { Combobox, ComboboxButton, ComboboxButtonProps, ComboboxControls, ComboboxControlsProps, ComboboxInput, ComboboxInputProps, ComboboxOptions, ComboboxOptionsProps, ComboboxProps, ComboboxSelectEvent } from "./components/Combobox/Combobox.js";
14
14
  import "./components/Combobox/index.js";
15
+ import { DATE_ERROR_MESSAGES, DATE_PARTS, DateErrorCode, DateErrorMessage, DateInputMode, DatePart, DateParts, DateValidation, DateValidationError, DateValidationOptions, DateValidationSuccess, getAutoComplete, validateDate, validateDateParts, validateDob } from "./components/DateInput/dateinput-utils.js";
16
+ import { DateInput, DateInputChangeEvent, DateInputDay, DateInputMonth, DateInputPartProps, DateInputProps, DateInputYear } from "./components/DateInput/DateInput.js";
17
+ import "./components/DateInput/index.js";
15
18
  import { DateType, DateTypeStr } from "./components/DatePicker/datepicker-utils.js";
16
19
  import { DatePicker, DatePickerProps } from "./components/DatePicker/DatePicker.js";
17
20
  import { Dialog, DialogBody, DialogHeader, DialogProps } from "./components/Dialog/Dialog.js";
@@ -33,10 +36,10 @@ import "./components/Section/index.js";
33
36
  import { Select, SelectOption, SelectOptionGroup, SelectProps } from "./components/Select/Select.js";
34
37
  import { Signpost, SignpostProps } from "./components/Signpost/Signpost.js";
35
38
  import { SkipNav, SkipNavProps } from "./components/SkipNav/SkipNav.js";
36
- import { Squircle, SquircleProps, SquircleSvgProps } from "./components/Squircle/Squircle.js";
39
+ import { Squircle, SquircleProps, SquircleSvg, SquircleSvgProps, SquircleText } from "./components/Squircle/Squircle.js";
37
40
  import { Tag, TagProps } from "./components/Tag/Tag.js";
38
41
  import { Textarea, TextareaProps } from "./components/Textarea/Textarea.js";
39
42
  import { ValidationSummary, ValidationSummaryErrors, ValidationSummaryHeading, ValidationSummaryPrompt, ValidationSummaryProps } from "./components/ValidationSummary/ValidationSummary.js";
40
43
  import { Wrapper, WrapperProps } from "./components/Wrapper/Wrapper.js";
41
44
  import "./components/Wrapper/index.js";
42
- export { AlertBanner, AlertBannerHeading, AlertBannerProps, Author, AuthorImageProps, AuthorMetaProps, AuthorProps, Box, BoxProps, Button, ButtonProps, Card, CardBadge, CardContent, CardHeading, CardIcon, CardImage, CardLink, CardPicture, CardProps, Checkbox, CheckboxProps, Combobox, ComboboxButton, ComboboxButtonProps, ComboboxControls, ComboboxControlsProps, ComboboxInput, ComboboxInputProps, type ComboboxItem, type ComboboxOption, ComboboxOptions, ComboboxOptionsProps, ComboboxProps, ComboboxSelectEvent, DatePicker, DatePickerProps, type DateType, type DateTypeStr, Dialog, DialogBody, DialogHeader, DialogProps, Expandable, ExpandableContent, ExpandableProps, ExpandableSummary, Field, FieldError, FieldHint, FieldLabel, Fieldset, FieldsetError, FieldsetFields, FieldsetHint, FieldsetLegend, FieldsetProps, Flourish, FlourishProps, Form, FormProps, Image, ImageProps, LoadingSpinner, LoadingSpinnerProps, Logo, LogoProps, PasswordInput, PasswordInputProps, Picture, PictureProps, Pill, PillBadge, PillProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverProps, PopoverTrigger, Radio, RadioProps, RootSVG, Searchbox, SearchboxButton, SearchboxInput, SearchboxProps, Section, SectionProps, Select, SelectOption, SelectOptionGroup, SelectProps, Signpost, SignpostProps, SkipNav, SkipNavProps, Squircle, SquircleProps, SquircleSvgProps, Tag, TagProps, TextInput, TextInputProps, Textarea, TextareaProps, ValidationSummary, ValidationSummaryErrors, ValidationSummaryHeading, ValidationSummaryPrompt, ValidationSummaryProps, Wrapper, WrapperProps };
45
+ export { AlertBanner, AlertBannerHeading, AlertBannerProps, Author, AuthorImage, AuthorImageProps, AuthorMeta, AuthorMetaProps, AuthorProps, Box, BoxProps, Button, ButtonProps, Card, CardBadge, CardContent, CardHeading, CardIcon, CardImage, CardLink, CardPicture, CardProps, Checkbox, CheckboxProps, Combobox, ComboboxButton, ComboboxButtonProps, ComboboxControls, ComboboxControlsProps, ComboboxInput, ComboboxInputProps, type ComboboxItem, type ComboboxOption, ComboboxOptions, ComboboxOptionsProps, ComboboxProps, ComboboxSelectEvent, DATE_ERROR_MESSAGES, DATE_PARTS, DateErrorCode, DateErrorMessage, DateInput, DateInputChangeEvent, DateInputDay, DateInputMode, DateInputMonth, DateInputPartProps, DateInputProps, DateInputYear, DatePart, DateParts, DatePicker, DatePickerProps, type DateType, type DateTypeStr, DateValidation, DateValidationError, DateValidationOptions, DateValidationSuccess, Dialog, DialogBody, DialogHeader, DialogProps, Expandable, ExpandableContent, ExpandableProps, ExpandableSummary, Field, FieldError, FieldHint, FieldLabel, Fieldset, FieldsetError, FieldsetFields, FieldsetHint, FieldsetLegend, FieldsetProps, Flourish, FlourishProps, Form, FormProps, Image, ImageProps, LoadingSpinner, LoadingSpinnerProps, Logo, LogoProps, PasswordInput, PasswordInputProps, Picture, PictureProps, Pill, PillBadge, PillProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverProps, PopoverTrigger, Radio, RadioProps, RootSVG, Searchbox, SearchboxButton, SearchboxInput, SearchboxProps, Section, SectionProps, Select, SelectOption, SelectOptionGroup, SelectProps, Signpost, SignpostProps, SkipNav, SkipNavProps, Squircle, SquircleProps, SquircleSvg, SquircleSvgProps, SquircleText, Tag, TagProps, TextInput, TextInputProps, Textarea, TextareaProps, ValidationSummary, ValidationSummaryErrors, ValidationSummaryHeading, ValidationSummaryPrompt, ValidationSummaryProps, Wrapper, WrapperProps, getAutoComplete, validateDate, validateDateParts, validateDob };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AlertBanner, AlertBannerHeading } from "./components/AlertBanner/AlertBanner.js";
2
2
  import { Image } from "./components/Image/Image.js";
3
- import { Author } from "./components/Author/Author.js";
3
+ import { Author, AuthorImage, AuthorMeta } from "./components/Author/Author.js";
4
4
  import { Box } from "./components/Box/Box.js";
5
5
  import { LoadingSpinner } from "./components/LoadingSpinner/LoadingSpinner.js";
6
6
  import { Button } from "./components/Button/Button.js";
@@ -9,11 +9,13 @@ import { Card, CardBadge, CardContent, CardHeading, CardIcon, CardImage, CardLin
9
9
  import { Checkbox } from "./components/Checkbox/Checkbox.js";
10
10
  import { TextInput } from "./components/TextInput/TextInput.js";
11
11
  import { Combobox, ComboboxButton, ComboboxControls, ComboboxInput, ComboboxOptions } from "./components/Combobox/Combobox.js";
12
+ import { Field, FieldError, FieldHint, FieldLabel } from "./components/Field/Field.js";
13
+ import { DATE_ERROR_MESSAGES, DATE_PARTS, getAutoComplete, validateDate, validateDateParts, validateDob } from "./components/DateInput/dateinput-utils.js";
14
+ import { DateInput, DateInputDay, DateInputMonth, DateInputYear } from "./components/DateInput/DateInput.js";
12
15
  import { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger } from "./components/Popover/Popover.js";
13
16
  import { DatePicker } from "./components/DatePicker/DatePicker.js";
14
17
  import { Dialog, DialogBody, DialogHeader } from "./components/Dialog/Dialog.js";
15
18
  import { Expandable, ExpandableContent, ExpandableSummary } from "./components/Expandable/Expandable.js";
16
- import { Field, FieldError, FieldHint, FieldLabel } from "./components/Field/Field.js";
17
19
  import { Fieldset, FieldsetError, FieldsetFields, FieldsetHint, FieldsetLegend } from "./components/Fieldset/Fieldset.js";
18
20
  import { Flourish } from "./components/Flourish/Flourish.js";
19
21
  import { Form } from "./components/Form/Form.js";
@@ -27,9 +29,9 @@ import { Section } from "./components/Section/Section.js";
27
29
  import { Select, SelectOption, SelectOptionGroup } from "./components/Select/Select.js";
28
30
  import { Signpost } from "./components/Signpost/Signpost.js";
29
31
  import { SkipNav } from "./components/SkipNav/SkipNav.js";
30
- import { Squircle } from "./components/Squircle/Squircle.js";
32
+ import { Squircle, SquircleSvg, SquircleText } from "./components/Squircle/Squircle.js";
31
33
  import { Tag } from "./components/Tag/Tag.js";
32
34
  import { Textarea } from "./components/Textarea/Textarea.js";
33
35
  import { ValidationSummary, ValidationSummaryErrors, ValidationSummaryHeading, ValidationSummaryPrompt } from "./components/ValidationSummary/ValidationSummary.js";
34
36
  import { Wrapper } from "./components/Wrapper/Wrapper.js";
35
- export { AlertBanner, AlertBannerHeading, Author, Box, Button, Card, CardBadge, CardContent, CardHeading, CardIcon, CardImage, CardLink, CardPicture, Checkbox, Combobox, ComboboxButton, ComboboxControls, ComboboxInput, ComboboxOptions, DatePicker, Dialog, DialogBody, DialogHeader, Expandable, ExpandableContent, ExpandableSummary, Field, FieldError, FieldHint, FieldLabel, Fieldset, FieldsetError, FieldsetFields, FieldsetHint, FieldsetLegend, Flourish, Form, Image, LoadingSpinner, Logo, PasswordInput, Picture, Pill, PillBadge, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Radio, RootSVG, Searchbox, SearchboxButton, SearchboxInput, Section, Select, SelectOption, SelectOptionGroup, Signpost, SkipNav, Squircle, Tag, TextInput, Textarea, ValidationSummary, ValidationSummaryErrors, ValidationSummaryHeading, ValidationSummaryPrompt, Wrapper };
37
+ export { AlertBanner, AlertBannerHeading, Author, AuthorImage, AuthorMeta, Box, Button, Card, CardBadge, CardContent, CardHeading, CardIcon, CardImage, CardLink, CardPicture, Checkbox, Combobox, ComboboxButton, ComboboxControls, ComboboxInput, ComboboxOptions, DATE_ERROR_MESSAGES, DATE_PARTS, DateInput, DateInputDay, DateInputMonth, DateInputYear, DatePicker, Dialog, DialogBody, DialogHeader, Expandable, ExpandableContent, ExpandableSummary, Field, FieldError, FieldHint, FieldLabel, Fieldset, FieldsetError, FieldsetFields, FieldsetHint, FieldsetLegend, Flourish, Form, Image, LoadingSpinner, Logo, PasswordInput, Picture, Pill, PillBadge, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Radio, RootSVG, Searchbox, SearchboxButton, SearchboxInput, Section, Select, SelectOption, SelectOptionGroup, Signpost, SkipNav, Squircle, SquircleSvg, SquircleText, Tag, TextInput, Textarea, ValidationSummary, ValidationSummaryErrors, ValidationSummaryHeading, ValidationSummaryPrompt, Wrapper, getAutoComplete, validateDate, validateDateParts, validateDob };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@coopdigital/react",
3
3
  "type": "module",
4
- "version": "1.30.2",
4
+ "version": "1.31.0",
5
5
  "private": false,
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -26,7 +26,8 @@
26
26
  "dist",
27
27
  "src/**/*.{tsx,ts}",
28
28
  "!**/__{screenshots,tests}__",
29
- "!**/*.{stories,test}.*"
29
+ "!**/*.{stories,test}.*",
30
+ "!**/recipes/**"
30
31
  ],
31
32
  "keywords": [
32
33
  "co-op",
@@ -53,8 +54,8 @@
53
54
  "test:behaviour:ci": "npm run test:e2e:ci -- -g behaviour",
54
55
  "build": "tsx scripts build",
55
56
  "build:docs": "tsx scripts build-docs",
56
- "build:docs:all": "tsx scripts build-docs --all",
57
57
  "build:icons": "tsx scripts build-icons",
58
+ "build:pattern-images": "tsx scripts build-pattern-images",
58
59
  "build:manifest": "tsx scripts build-manifest",
59
60
  "build:storybook": "storybook build --disable-telemetry",
60
61
  "build:storybook:test": "storybook build --test --disable-telemetry --output-dir storybook-test",
@@ -110,7 +111,7 @@
110
111
  "storybook": "$storybook"
111
112
  },
112
113
  "dependencies": {
113
- "@coopdigital/styles": "^1.21.1",
114
+ "@coopdigital/styles": "^1.22.0",
114
115
  "@radix-ui/react-popover": "^1.3.1",
115
116
  "clsx": "^2.1.1",
116
117
  "date-fns": "^4.4.0",
@@ -119,5 +120,5 @@
119
120
  "merge-refs": "^2.0.0",
120
121
  "react-day-picker": "^10.0.2"
121
122
  },
122
- "gitHead": "eb06ec9a3ea4bb441c5b6738949da6dfdd9f0ce2"
123
+ "gitHead": "6a649143b3381563d36754d873c1410eb307fdc4"
123
124
  }
@@ -88,4 +88,4 @@ Author.Meta = AuthorMeta
88
88
  Author.slot = { name: "Author" }
89
89
 
90
90
  export default Author
91
- export { Author }
91
+ export { Author, AuthorImage, AuthorMeta }
@@ -1,5 +1,5 @@
1
1
  "use client"
2
- import type { ButtonHTMLAttributes, HTMLAttributes, JSX, ReactNode, Ref } from "react"
2
+ import type { ButtonHTMLAttributes, HTMLAttributes, JSX, MouseEvent, ReactNode, Ref } from "react"
3
3
 
4
4
  import clsx from "clsx"
5
5
  import {
@@ -13,16 +13,19 @@ import {
13
13
  import mergeRefs from "merge-refs"
14
14
  import { useEffect, useMemo, useRef, useState } from "react"
15
15
 
16
+ import type { ComboboxItem, ComboboxOption } from "@/components/Combobox/combobox-utils"
17
+
16
18
  import {
17
- type ComboboxItem,
18
- type ComboboxOption,
19
19
  defaultFilter,
20
20
  defaultIsItemDisabled,
21
21
  defaultItemToString,
22
+ defaultMaxResultsText,
22
23
  isMatchingText,
23
24
  normalizeOptions,
24
25
  resolveOption,
25
- } from "@/components/Combobox/combobox-utils"
26
+ type SelectionSnapshot,
27
+ shouldShowSelectionText,
28
+ } from "@/components/Combobox/combobox-internal"
26
29
  import { ComboboxContext, useComboboxContext } from "@/components/Combobox/Combobox.context"
27
30
  import { getFieldError, useField } from "@/components/Field/Field.context"
28
31
  import { ChevronDownIcon } from "@/components/icons"
@@ -31,8 +34,6 @@ import { useDebounce, useFormReset, useId, useSlots } from "@/hooks"
31
34
  import { StandardSizes } from "@/types"
32
35
  import { getSlotProp } from "@/utils"
33
36
 
34
- export type { ComboboxItem, ComboboxOption } from "@/components/Combobox/combobox-utils"
35
-
36
37
  export interface ComboboxSelectEvent {
37
38
  /** Whether the selection is a custom value, entered by users when `allowCustom` is set,
38
39
  * rather than one of the options.
@@ -190,9 +191,6 @@ type ComboboxState = UseComboboxState<ComboboxItem>
190
191
  const DEFAULT_MAX_RESULTS = 50
191
192
  const ANNOUNCEMENT_DELAY = 750
192
193
 
193
- const defaultMaxResultsText = (shown: number, total: number): string =>
194
- `Showing ${shown} of ${total} results.`
195
-
196
194
  const componentSlots = {
197
195
  ComboboxControls: null,
198
196
  ComboboxOptions: null,
@@ -353,24 +351,22 @@ const Combobox = ({
353
351
  },
354
352
  })
355
353
 
356
- // Options can arrive after the selection, such as when they are fetched,
357
- // so show the option text once it is known. Text that users are typing is
358
- // left alone.
354
+ // Keep the input text in step with the selection
359
355
  const selection = resolveOption(downshift.selectedItem, items)
360
356
  const selectionText = toString(selection)
361
357
  const selectionValue = selection?.value
362
- const previousSelection = useRef({ text: selectionText, value: selectionValue })
358
+ const previousSelection = useRef<SelectionSnapshot>({
359
+ text: selectionText,
360
+ value: selectionValue,
361
+ })
363
362
  const { inputValue, reset, setInputValue } = downshift
364
363
 
365
364
  useEffect(() => {
366
365
  const previous = previousSelection.current
367
- previousSelection.current = { text: selectionText, value: selectionValue }
366
+ const current = { text: selectionText, value: selectionValue }
367
+ previousSelection.current = current
368
368
 
369
- if (
370
- selectionValue === previous.value &&
371
- selectionText !== previous.text &&
372
- inputValue === previous.text
373
- ) {
369
+ if (shouldShowSelectionText(previous, current, inputValue)) {
374
370
  setInputValue(selectionText)
375
371
  }
376
372
  }, [inputValue, selectionText, selectionValue, setInputValue])
@@ -493,9 +489,22 @@ const ComboboxOptions = ({
493
489
  const message = getMessage()
494
490
  const announcement = useDebounce(message, ANNOUNCEMENT_DELAY)
495
491
 
492
+ // downshift bug: a mouse leave after the menu closes is handled with the
493
+ // props from before a new controlled selection, so it is ignored
494
+ const onMouseLeave = (event: MouseEvent<HTMLUListElement>) => {
495
+ props.onMouseLeave?.(event)
496
+ if (!isOpen) Object.assign(event, { preventDownshiftDefault: true })
497
+ }
498
+
496
499
  return (
497
500
  <>
498
- <ul {...getMenuProps({ ...props, className: clsx("coop-combobox-options", className) })}>
501
+ <ul
502
+ {...getMenuProps({
503
+ ...props,
504
+ className: clsx("coop-combobox-options", className),
505
+ onMouseLeave,
506
+ })}
507
+ >
499
508
  {isOpen &&
500
509
  items.map((item, index) => {
501
510
  // Keep the option fields off the element, and pass its handlers
@@ -0,0 +1,60 @@
1
+ import { matchSorter } from "match-sorter"
2
+
3
+ import type { ComboboxItem, ComboboxOption } from "./combobox-utils"
4
+
5
+ export const defaultFilter = (items: ComboboxItem[], inputValue: string): ComboboxItem[] =>
6
+ inputValue !== ""
7
+ ? matchSorter(items, inputValue, {
8
+ keys: [(item) => [item.label ?? item.value]],
9
+ })
10
+ : items
11
+
12
+ export const defaultIsItemDisabled = (item: ComboboxItem): boolean => item.disabled ?? false
13
+
14
+ export const defaultItemToString = (item: ComboboxItem | null): string =>
15
+ item?.label ?? item?.value ?? ""
16
+
17
+ export const defaultMaxResultsText = (shown: number, total: number): string =>
18
+ `Showing ${shown} of ${total} results.`
19
+
20
+ // Convert options given as strings or objects into a list of items
21
+ export const normalizeOptions = (options: ComboboxOption[]): ComboboxItem[] =>
22
+ options
23
+ .filter((option) => option && typeof option !== "number")
24
+ .map((option) => (typeof option === "string" ? { value: option } : option))
25
+
26
+ // Created once, as localeCompare with options creates a new collator on
27
+ // every call, which is slow when checking a long list of options
28
+ const collator = new Intl.Collator(undefined, { sensitivity: "base" })
29
+
30
+ // Check whether typed text matches an option, ignoring case, accents and
31
+ // surrounding whitespace
32
+ export const isMatchingText = (optionText: string, text: string): boolean =>
33
+ collator.compare(optionText.trim(), text.trim()) === 0
34
+
35
+ // Find the item for an option given as a value or an object. An option that
36
+ // is not in the list, such as a custom value, is kept as it is.
37
+ export const resolveOption = (
38
+ option: ComboboxOption | null | undefined,
39
+ items: ComboboxItem[]
40
+ ): ComboboxItem | null => {
41
+ if (option === null || option === undefined) return null
42
+
43
+ const item = typeof option === "string" ? { value: option } : option
44
+ return items.find(({ value }) => value === item.value) ?? item
45
+ }
46
+
47
+ export interface SelectionSnapshot {
48
+ text: string
49
+ value: string | undefined
50
+ }
51
+
52
+ // Whether the input should change to the text of the selection. Options can
53
+ // arrive after the selection, such as when they are fetched, so show the
54
+ // option text once it is known, unless users are typing.
55
+ export const shouldShowSelectionText = (
56
+ previous: SelectionSnapshot,
57
+ current: SelectionSnapshot,
58
+ inputValue: string
59
+ ): boolean =>
60
+ current.value === previous.value && current.text !== previous.text && inputValue === previous.text
@@ -1,5 +1,4 @@
1
- import { matchSorter } from "match-sorter"
2
- import { HTMLAttributes } from "react"
1
+ import type { HTMLAttributes } from "react"
3
2
 
4
3
  export interface ComboboxItem extends HTMLAttributes<HTMLLIElement> {
5
4
  /** Specify the disabled state.
@@ -19,42 +18,3 @@ export interface ComboboxItem extends HTMLAttributes<HTMLLIElement> {
19
18
  }
20
19
 
21
20
  export type ComboboxOption = ComboboxItem | string
22
-
23
- export const defaultFilter = (items: ComboboxItem[], inputValue: string): ComboboxItem[] =>
24
- inputValue !== ""
25
- ? matchSorter(items, inputValue, {
26
- keys: [(item) => [item.label ?? item.value]],
27
- })
28
- : items
29
-
30
- export const defaultIsItemDisabled = (item: ComboboxItem): boolean => item.disabled ?? false
31
-
32
- export const defaultItemToString = (item: ComboboxItem | null): string =>
33
- item?.label ?? item?.value ?? ""
34
-
35
- // Convert options given as strings or objects into a list of items
36
- export const normalizeOptions = (options: ComboboxOption[]): ComboboxItem[] =>
37
- options
38
- .filter((option) => option && typeof option !== "number")
39
- .map((option) => (typeof option === "string" ? { value: option } : option))
40
-
41
- // Created once, as localeCompare with options creates a new collator on
42
- // every call, which is slow when checking a long list of options
43
- const collator = new Intl.Collator(undefined, { sensitivity: "base" })
44
-
45
- // Check whether typed text matches an option, ignoring case, accents and
46
- // surrounding whitespace
47
- export const isMatchingText = (optionText: string, text: string): boolean =>
48
- collator.compare(optionText.trim(), text.trim()) === 0
49
-
50
- // Find the item for an option given as a value or an object. An option that
51
- // is not in the list, such as a custom value, is kept as it is.
52
- export const resolveOption = (
53
- option: ComboboxOption | null | undefined,
54
- items: ComboboxItem[]
55
- ): ComboboxItem | null => {
56
- if (option === null || option === undefined) return null
57
-
58
- const item = typeof option === "string" ? { value: option } : option
59
- return items.find(({ value }) => value === item.value) ?? item
60
- }
@@ -3,3 +3,4 @@ import Combobox from "./Combobox"
3
3
  export default Combobox
4
4
  export { Combobox }
5
5
  export * from "./Combobox"
6
+ export type * from "./combobox-utils"
@@ -0,0 +1,25 @@
1
+ "use client"
2
+ import { createContext, type RefObject, useContext } from "react"
3
+
4
+ import type { StandardSizes } from "@/types"
5
+
6
+ import type { DateInputMode, DatePart, DateParts } from "./dateinput-utils"
7
+
8
+ export interface DateInputContextValue {
9
+ changePart: (part: DatePart, value: string) => void
10
+ controlled: boolean
11
+ // The first rendered part attaches controlRef, which the form reset is
12
+ // looked up from
13
+ controlPart?: DatePart
14
+ controlRef: RefObject<HTMLInputElement | null>
15
+ defaultValue?: DateParts
16
+ id: string
17
+ mode?: DateInputMode
18
+ name?: string
19
+ parts: DateParts
20
+ size?: StandardSizes
21
+ }
22
+
23
+ export const DateInputContext = createContext<DateInputContextValue | null>(null)
24
+
25
+ export const useDateInput = () => useContext(DateInputContext)