@jobber/components 4.30.0 → 4.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.
@@ -2,98 +2,12 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var React = require('react');
6
- var framerMotion = require('framer-motion');
7
- var Icon = require('../Icon-405a216c.js');
5
+ var AnimatedSwitcher = require('../AnimatedSwitcher-d1e1ddcf.js');
6
+ require('react');
7
+ require('framer-motion');
8
+ require('../Icon-405a216c.js');
8
9
  require('@jobber/design');
9
10
 
10
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
11
11
 
12
- var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
13
12
 
14
- const DURATION_SIMPLE = 0.1;
15
- const DURATION_AVERAGE = 0.2;
16
- const FADE = {
17
- visible: { opacity: 1 },
18
- hidden: { opacity: 0 },
19
- };
20
- const SLIDE_IN_UP = {
21
- visible: { y: 0, opacity: 1 },
22
- hidden: { y: "10%", opacity: 0 },
23
- };
24
- const SLIDE_IN_DOWN = {
25
- visible: { y: 0, opacity: 1 },
26
- hidden: { y: "-10%", opacity: 0 },
27
- };
28
- const SPIN_COUNTER_CLOCK_WISE = {
29
- visible: {
30
- rotate: 0,
31
- scale: 1,
32
- transition: { duration: DURATION_AVERAGE, ease: "easeOut" },
33
- },
34
- hidden: {
35
- rotate: 180,
36
- scale: 0.6,
37
- transition: { duration: DURATION_AVERAGE, ease: "easeIn" },
38
- },
39
- };
40
- const SPIN_CLOCK_WISE = {
41
- visible: {
42
- rotate: 0,
43
- scale: 1,
44
- transition: { duration: DURATION_AVERAGE, ease: "easeOut" },
45
- },
46
- hidden: {
47
- rotate: -180,
48
- scale: 0.6,
49
- transition: { duration: DURATION_AVERAGE, ease: "easeIn" },
50
- },
51
- };
52
-
53
- function AnimatedSwitcher({ initialChild, switched, switchTo, type = "slideFromBottom", }) {
54
- const [ref, setRef] = React.useState(null);
55
- const isSwitchingBetweenIcons = initialChild.type === Icon.Icon && switchTo.type === Icon.Icon;
56
- const { key, transition, child, duration } = getChildData();
57
- return (React__default["default"].createElement(framerMotion.AnimatePresence, { exitBeforeEnter: true, initial: false },
58
- React__default["default"].createElement(framerMotion.motion.div, { ref: setRef, key: key, variants: transition, initial: "hidden", animate: "visible", exit: "hidden", transition: { duration }, style: { display: getDisplayValue() } }, child)));
59
- function getDisplayValue() {
60
- if (ref === null || ref === void 0 ? void 0 : ref.firstElementChild) {
61
- return window.getComputedStyle(ref.firstElementChild).display;
62
- }
63
- }
64
- function getChildData() {
65
- let data = { key: `${initialChild.type}_1`, child: initialChild };
66
- if (switched) {
67
- data = { key: `${switchTo.type}_2`, child: switchTo };
68
- }
69
- return Object.assign(Object.assign({}, data), { transition: getTransitionType(), duration: getTransitionDuration() });
70
- }
71
- function getTransitionType() {
72
- if (isSwitchingBetweenIcons) {
73
- if (switched)
74
- return SPIN_COUNTER_CLOCK_WISE;
75
- return SPIN_CLOCK_WISE;
76
- }
77
- else if (type === "fade") {
78
- return FADE;
79
- }
80
- else {
81
- if (switched)
82
- return SLIDE_IN_UP;
83
- return SLIDE_IN_DOWN;
84
- }
85
- }
86
- function getTransitionDuration() {
87
- switch (type) {
88
- case "fade":
89
- return DURATION_AVERAGE;
90
- default:
91
- return DURATION_SIMPLE;
92
- }
93
- }
94
- }
95
- AnimatedSwitcher.Icon = function AnimatedSwitcherIcon({ switchToIcon, switched, initialIcon, }) {
96
- return (React__default["default"].createElement(AnimatedSwitcher, { switched: switched, initialChild: React__default["default"].createElement(Icon.Icon, { name: initialIcon }), switchTo: React__default["default"].createElement(Icon.Icon, { name: switchToIcon }) }));
97
- };
98
-
99
- exports.AnimatedSwitcher = AnimatedSwitcher;
13
+ exports.AnimatedSwitcher = AnimatedSwitcher.AnimatedSwitcher;
@@ -0,0 +1,96 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var framerMotion = require('framer-motion');
5
+ var Icon = require('./Icon-405a216c.js');
6
+
7
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
+
9
+ var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
10
+
11
+ const DURATION_SIMPLE = 0.1;
12
+ const DURATION_AVERAGE = 0.2;
13
+ const FADE = {
14
+ visible: { opacity: 1 },
15
+ hidden: { opacity: 0 },
16
+ };
17
+ const SLIDE_IN_UP = {
18
+ visible: { y: 0, opacity: 1 },
19
+ hidden: { y: "10%", opacity: 0 },
20
+ };
21
+ const SLIDE_IN_DOWN = {
22
+ visible: { y: 0, opacity: 1 },
23
+ hidden: { y: "-10%", opacity: 0 },
24
+ };
25
+ const SPIN_COUNTER_CLOCK_WISE = {
26
+ visible: {
27
+ rotate: 0,
28
+ scale: 1,
29
+ transition: { duration: DURATION_AVERAGE, ease: "easeOut" },
30
+ },
31
+ hidden: {
32
+ rotate: 180,
33
+ scale: 0.6,
34
+ transition: { duration: DURATION_AVERAGE, ease: "easeIn" },
35
+ },
36
+ };
37
+ const SPIN_CLOCK_WISE = {
38
+ visible: {
39
+ rotate: 0,
40
+ scale: 1,
41
+ transition: { duration: DURATION_AVERAGE, ease: "easeOut" },
42
+ },
43
+ hidden: {
44
+ rotate: -180,
45
+ scale: 0.6,
46
+ transition: { duration: DURATION_AVERAGE, ease: "easeIn" },
47
+ },
48
+ };
49
+
50
+ function AnimatedSwitcher({ initialChild, switched, switchTo, type = "slideFromBottom", }) {
51
+ const [ref, setRef] = React.useState(null);
52
+ const isSwitchingBetweenIcons = initialChild.type === Icon.Icon && switchTo.type === Icon.Icon;
53
+ const { key, transition, child, duration } = getChildData();
54
+ return (React__default["default"].createElement(framerMotion.AnimatePresence, { exitBeforeEnter: true, initial: false },
55
+ React__default["default"].createElement(framerMotion.motion.div, { ref: setRef, key: key, variants: transition, initial: "hidden", animate: "visible", exit: "hidden", transition: { duration }, style: { display: getDisplayValue() } }, child)));
56
+ function getDisplayValue() {
57
+ if (ref === null || ref === void 0 ? void 0 : ref.firstElementChild) {
58
+ return window.getComputedStyle(ref.firstElementChild).display;
59
+ }
60
+ }
61
+ function getChildData() {
62
+ let data = { key: `${initialChild.type}_1`, child: initialChild };
63
+ if (switched) {
64
+ data = { key: `${switchTo.type}_2`, child: switchTo };
65
+ }
66
+ return Object.assign(Object.assign({}, data), { transition: getTransitionType(), duration: getTransitionDuration() });
67
+ }
68
+ function getTransitionType() {
69
+ if (isSwitchingBetweenIcons) {
70
+ if (switched)
71
+ return SPIN_COUNTER_CLOCK_WISE;
72
+ return SPIN_CLOCK_WISE;
73
+ }
74
+ else if (type === "fade") {
75
+ return FADE;
76
+ }
77
+ else {
78
+ if (switched)
79
+ return SLIDE_IN_UP;
80
+ return SLIDE_IN_DOWN;
81
+ }
82
+ }
83
+ function getTransitionDuration() {
84
+ switch (type) {
85
+ case "fade":
86
+ return DURATION_AVERAGE;
87
+ default:
88
+ return DURATION_SIMPLE;
89
+ }
90
+ }
91
+ }
92
+ AnimatedSwitcher.Icon = function AnimatedSwitcherIcon({ switchToIcon, switched, initialIcon, }) {
93
+ return (React__default["default"].createElement(AnimatedSwitcher, { switched: switched, initialChild: React__default["default"].createElement(Icon.Icon, { name: initialIcon }), switchTo: React__default["default"].createElement(Icon.Icon, { name: switchToIcon }) }));
94
+ };
95
+
96
+ exports.AnimatedSwitcher = AnimatedSwitcher;
@@ -10,4 +10,5 @@ export declare const EMPTY_FILTER_RESULTS_ACTION_LABEL = "Clear Filters";
10
10
  */
11
11
  export declare const BREAKPOINTS: readonly ["xs", "sm", "md", "lg", "xl"];
12
12
  export declare const BREAKPOINT_SIZES: Record<Breakpoints, number>;
13
+ export declare const SEARCH_DEBOUNCE_DELAY: number;
13
14
  export {};
@@ -2,9 +2,11 @@
2
2
  import { DataListLayout } from "./components/DataListLayout";
3
3
  import { DataListObject, DataListProps } from "./DataList.types";
4
4
  import { DataListFilters } from "./components/DataListFilters";
5
+ import { DataListSearch } from "./components/DataListSearch";
5
6
  export declare function DataList<T extends DataListObject>(props: DataListProps<T>): JSX.Element;
6
7
  export declare namespace DataList {
7
8
  var Layout: typeof DataListLayout;
8
9
  var EmptyState: typeof import("./components/EmptyState").EmptyState;
9
10
  var Filters: typeof DataListFilters;
11
+ var Search: typeof DataListSearch;
10
12
  }
@@ -65,3 +65,14 @@ export interface DataListProps<T extends DataListObject> {
65
65
  [Breakpoint in Breakpoints]?: boolean;
66
66
  };
67
67
  }
68
+ export interface DataListSearchProps {
69
+ readonly placeholder?: string;
70
+ readonly onSearch: (search: string) => void;
71
+ }
72
+ export interface DataListFiltersProps {
73
+ readonly children: ReactElement | ReactElement[];
74
+ }
75
+ export interface DataListContextProps<T extends DataListObject> extends DataListProps<T> {
76
+ readonly filterComponent?: ReactElement<DataListFiltersProps>;
77
+ readonly searchComponent?: ReactElement<DataListSearchProps>;
78
+ }
@@ -0,0 +1,8 @@
1
+ /// <reference types="react" />
2
+ import { DataListSearchProps } from "../../DataList.types";
3
+ export declare const DATA_LIST_SEARCH_TEST_ID = "ATL-DataList-Search-input-wrapper";
4
+ export declare function DataListSearch(_: DataListSearchProps): null;
5
+ /**
6
+ * Renders the DataList.Search component
7
+ */
8
+ export declare function InternalDataListSearch(): JSX.Element | null;
@@ -0,0 +1 @@
1
+ export * from "./DataListSearch";
@@ -0,0 +1,2 @@
1
+ import { PropsWithChildren } from "react";
2
+ export declare function DataListStickyHeader({ children }: PropsWithChildren<object>): JSX.Element;
@@ -1,9 +1,10 @@
1
1
  /// <reference types="react" />
2
- import { DataListObject, DataListProps } from "../../DataList.types";
2
+ import { DataListContextProps, DataListObject } from "../../DataList.types";
3
3
  export declare const defaultValues: {
4
+ title: string;
4
5
  data: never[];
5
6
  headers: {};
6
7
  children: never[];
7
8
  };
8
- export declare const DataListContext: import("react").Context<DataListProps<DataListObject>>;
9
- export declare function useDataListContext(): DataListProps<DataListObject>;
9
+ export declare const DataListContext: import("react").Context<DataListContextProps<DataListObject>>;
10
+ export declare function useDataListContext(): DataListContextProps<DataListObject>;
@@ -7,35 +7,45 @@ var styleInject_es = require('../style-inject.es-9d2f5f4e.js');
7
7
  var Button = require('../Button-7698424c.js');
8
8
  var Text = require('../Text-e7ed0974.js');
9
9
  var isEmpty = require('lodash/isEmpty');
10
+ var design = require('@jobber/design');
10
11
  var InlineLabel = require('../InlineLabel-afd5fc6f.js');
11
12
  var FormatDate = require('../FormatDate-70ea5b43.js');
12
13
  var Heading = require('../Heading-a1191b15.js');
13
14
  var Glimmer = require('../Glimmer-cfa92a88.js');
14
15
  var classnames = require('classnames');
15
16
  require('react-router-dom');
16
- require('@jobber/design');
17
17
  require('../Typography-fd6f932a.js');
18
18
  var useInView = require('@jobber/hooks/useInView');
19
+ var debounce = require('lodash/debounce');
20
+ var InputText = require('../InputText-bb03f6d1.js');
21
+ var AnimatedSwitcher = require('../AnimatedSwitcher-d1e1ddcf.js');
19
22
  require('../Icon-405a216c.js');
20
23
  require('../tslib.es6-5b8768b7.js');
21
24
  require('../Content-2ca1ffe1.js');
25
+ require('../FormField-0f880b07.js');
26
+ require('uuid');
27
+ require('react-hook-form');
28
+ require('../InputValidation-b5a3d92c.js');
29
+ require('framer-motion');
30
+ require('../Spinner-9d8fc7ff.js');
22
31
 
23
32
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
24
33
 
25
34
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
26
35
  var isEmpty__default = /*#__PURE__*/_interopDefaultLegacy(isEmpty);
27
36
  var classnames__default = /*#__PURE__*/_interopDefaultLegacy(classnames);
37
+ var debounce__default = /*#__PURE__*/_interopDefaultLegacy(debounce);
28
38
 
29
- var css_248z$5 = ".TkdrExYnvcY- {\n -ms-flex: 1;\n flex: 1;\n}\n\n.IcAlZHoB4LI- {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n margin-bottom: calc(16px / 2);\n margin-bottom: var(--space-small);\n}\n\n.Z8C7qJtq5Xc- {\n position: sticky;\n top: 0;\n z-index: 1;\n z-index: var(--elevation-base);\n background-color: rgba(255, 255, 255, 1);\n background-color: var(--color-surface);\n}\n\n.Z8C7qJtq5Xc-:empty {\n display: none;\n}\n\n.jGHZZYZm1ZY- {\n padding: calc(16px / 2);\n padding: var(--space-small);\n border-bottom: calc(16px / 8) solid rgb(225, 225, 225);\n border-bottom: var(--border-thick) solid var(--color-border);\n}\n\n.Gj9kAGnfKco- > * {\n font-weight: 500;\n}\n\n.ise8kHCfhCY- {\n padding: calc(16px / 2);\n padding: var(--space-small);\n border-bottom: calc(16px / 16) solid rgb(225, 225, 225);\n border-bottom: var(--border-base) solid var(--color-border);\n}\n";
30
- var styles$5 = {"wrapper":"TkdrExYnvcY-","titleContainer":"IcAlZHoB4LI-","header":"Z8C7qJtq5Xc-","headerTitles":"jGHZZYZm1ZY-","headerLabel":"Gj9kAGnfKco-","listItem":"ise8kHCfhCY-"};
31
- styleInject_es.styleInject(css_248z$5);
39
+ var css_248z$7 = ".TkdrExYnvcY- {\n -ms-flex: 1;\n flex: 1;\n}\n\n.IcAlZHoB4LI- {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n margin-bottom: calc(16px / 2);\n margin-bottom: var(--space-small);\n}\n\n.Z8C7qJtq5Xc- {\n position: sticky;\n top: 0;\n z-index: 1;\n z-index: var(--elevation-base);\n background-color: rgba(255, 255, 255, 1);\n background-color: var(--color-surface);\n}\n\n.Z8C7qJtq5Xc-:empty {\n display: none;\n}\n\n.TFO76ysivmg- {\n display: grid;\n padding: calc(16px / 2) 0;\n padding: var(--space-small) 0;\n gap: calc(16px / 2);\n grid-gap: calc(16px / 2);\n grid-gap: var(--space-small);\n gap: var(--space-small);\n grid-template-columns: auto -webkit-max-content;\n grid-template-columns: auto max-content;\n}\n\n.jGHZZYZm1ZY- {\n padding: calc(16px / 2);\n padding: var(--space-small);\n border-bottom: calc(16px / 8) solid rgb(225, 225, 225);\n border-bottom: var(--border-thick) solid var(--color-border);\n}\n\n.Gj9kAGnfKco- > * {\n font-weight: 500;\n}\n\n.ise8kHCfhCY- {\n padding: calc(16px / 2);\n padding: var(--space-small);\n border-bottom: calc(16px / 16) solid rgb(225, 225, 225);\n border-bottom: var(--border-base) solid var(--color-border);\n}\n";
40
+ var styles$7 = {"wrapper":"TkdrExYnvcY-","titleContainer":"IcAlZHoB4LI-","header":"Z8C7qJtq5Xc-","headerFilters":"TFO76ysivmg-","headerTitles":"jGHZZYZm1ZY-","headerLabel":"Gj9kAGnfKco-","listItem":"ise8kHCfhCY-"};
41
+ styleInject_es.styleInject(css_248z$7);
32
42
 
33
- var css_248z$4 = "._--dXJma0jX0- {\n display: -ms-flexbox;\n display: flex;\n height: 100%;\n box-sizing: border-box;\n padding: calc(16px * 1);\n padding: var(--space-base);\n -ms-flex-pack: center;\n justify-content: center;\n -ms-flex-direction: column;\n flex-direction: column;\n row-gap: calc(16px * 1);\n row-gap: var(--space-base);\n -ms-flex-align: center;\n align-items: center;\n}\n";
34
- var styles$4 = {"emptyStateWrapper":"_--dXJma0jX0-"};
35
- styleInject_es.styleInject(css_248z$4);
43
+ var css_248z$6 = "._--dXJma0jX0- {\n display: -ms-flexbox;\n display: flex;\n height: 100%;\n box-sizing: border-box;\n padding: calc(16px * 1);\n padding: var(--space-base);\n -ms-flex-pack: center;\n justify-content: center;\n -ms-flex-direction: column;\n flex-direction: column;\n row-gap: calc(16px * 1);\n row-gap: var(--space-base);\n -ms-flex-align: center;\n align-items: center;\n}\n";
44
+ var styles$6 = {"emptyStateWrapper":"_--dXJma0jX0-"};
45
+ styleInject_es.styleInject(css_248z$6);
36
46
 
37
47
  function DataListEmptyState({ message, action }) {
38
- return (React__default["default"].createElement("div", { className: styles$4.emptyStateWrapper },
48
+ return (React__default["default"].createElement("div", { className: styles$6.emptyStateWrapper },
39
49
  React__default["default"].createElement(Text.Text, { align: "center" }, message),
40
50
  action && (React__default["default"].createElement(Button.Button, Object.assign({}, Object.assign(Object.assign({}, action), { variation: "subtle" }))))));
41
51
  }
@@ -59,10 +69,11 @@ const BREAKPOINT_SIZES = {
59
69
  lg: 1080,
60
70
  xl: 1440,
61
71
  };
72
+ const SEARCH_DEBOUNCE_DELAY = design.tokens["timing-slowest"];
62
73
 
63
- var css_248z$3 = ".WRV-UmQmPzo- {\n display: -ms-flexbox;\n display: flex;\n position: relative;\n z-index: 0;\n z-index: var(--elevation-default);\n overflow: hidden;\n gap: calc(16px / 2);\n gap: var(--space-small);\n}\n\n.liQeWCMenD0- {\n --overflow-bg: var(--color-white);\n\n display: -ms-flexbox;\n\n display: flex;\n position: absolute;\n top: 0;\n right: 0;\n height: 100%;\n padding-left: calc(16px * 1);\n padding-left: var(--space-base);\n background-image: linear-gradient(\n 90deg,\n transparent 0,\n rgba(255, 255, 255, 1) calc(16px * 1),\n rgba(255, 255, 255, 1) 100%\n );\n background-image: linear-gradient(\n 90deg,\n transparent 0,\n var(--overflow-bg) var(--space-base),\n var(--overflow-bg) 100%\n );\n -ms-flex-align: center;\n align-items: center;\n}\n";
64
- var styles$3 = {"tags":"WRV-UmQmPzo-","tagCount":"liQeWCMenD0-"};
65
- styleInject_es.styleInject(css_248z$3);
74
+ var css_248z$5 = ".WRV-UmQmPzo- {\n display: -ms-flexbox;\n display: flex;\n position: relative;\n z-index: 0;\n z-index: var(--elevation-default);\n overflow: hidden;\n gap: calc(16px / 2);\n gap: var(--space-small);\n}\n\n.liQeWCMenD0- {\n --overflow-bg: var(--color-white);\n\n display: -ms-flexbox;\n\n display: flex;\n position: absolute;\n top: 0;\n right: 0;\n height: 100%;\n padding-left: calc(16px * 1);\n padding-left: var(--space-base);\n background-image: linear-gradient(\n 90deg,\n transparent 0,\n rgba(255, 255, 255, 1) calc(16px * 1),\n rgba(255, 255, 255, 1) 100%\n );\n background-image: linear-gradient(\n 90deg,\n transparent 0,\n var(--overflow-bg) var(--space-base),\n var(--overflow-bg) 100%\n );\n -ms-flex-align: center;\n align-items: center;\n}\n";
75
+ var styles$5 = {"tags":"WRV-UmQmPzo-","tagCount":"liQeWCMenD0-"};
76
+ styleInject_es.styleInject(css_248z$5);
66
77
 
67
78
  function DataListTags({ items }) {
68
79
  const ref = React.useRef(null);
@@ -84,10 +95,10 @@ function DataListTags({ items }) {
84
95
  elements === null || elements === void 0 ? void 0 : elements.forEach(element => observer.unobserve(element));
85
96
  };
86
97
  }, [items]);
87
- return (React__default["default"].createElement("div", { className: styles$3.tags, ref: ref },
98
+ return (React__default["default"].createElement("div", { className: styles$5.tags, ref: ref },
88
99
  items.filter(Boolean).map((tag, index) => (React__default["default"].createElement("div", { key: tag, "data-tag-element": index },
89
100
  React__default["default"].createElement(InlineLabel.InlineLabel, null, tag)))),
90
- Boolean(visibleItems) && (React__default["default"].createElement("div", { className: styles$3.tagCount },
101
+ Boolean(visibleItems) && (React__default["default"].createElement("div", { className: styles$5.tagCount },
91
102
  React__default["default"].createElement(Text.Text, null,
92
103
  "+",
93
104
  visibleItems)))));
@@ -165,7 +176,7 @@ function generateListItemElements(data) {
165
176
  * Generate the header elements with the default styling
166
177
  */
167
178
  function generateHeaderElements(headers) {
168
- const headerElements = Object.keys(headers).reduce((acc, key) => (Object.assign(Object.assign({}, acc), { [key]: (React__default["default"].createElement("div", { className: styles$5.headerLabel },
179
+ const headerElements = Object.keys(headers).reduce((acc, key) => (Object.assign(Object.assign({}, acc), { [key]: (React__default["default"].createElement("div", { className: styles$7.headerLabel },
169
180
  React__default["default"].createElement(Text.Text, { variation: "subdued", maxLines: "single", size: "small" }, headers[key]))) })), {});
170
181
  return isEmpty__default["default"](headerElements) ? undefined : headerElements;
171
182
  }
@@ -198,9 +209,9 @@ function sortSizeProp(sizeProp) {
198
209
  return sizeProp.sort((a, b) => BREAKPOINTS.indexOf(a) - BREAKPOINTS.indexOf(b));
199
210
  }
200
211
 
201
- var css_248z$2 = ".F56prQsXm3A- {\n min-width: 80px;\n margin-left: calc(16px / 2);\n margin-left: var(--space-small);\n}\n";
202
- var styles$2 = {"results":"F56prQsXm3A-"};
203
- styleInject_es.styleInject(css_248z$2);
212
+ var css_248z$4 = ".F56prQsXm3A- {\n min-width: 80px;\n margin-left: calc(16px / 2);\n margin-left: var(--space-small);\n}\n";
213
+ var styles$4 = {"results":"F56prQsXm3A-"};
214
+ styleInject_es.styleInject(css_248z$4);
204
215
 
205
216
  const DATALIST_TOTALCOUNT_TEST_ID = "ATL-DataList-TotalCount";
206
217
  function DataListTotalCount({ totalCount, loading, }) {
@@ -216,12 +227,12 @@ function DataListTotalCount({ totalCount, loading, }) {
216
227
  totalCount.toLocaleString(),
217
228
  " results)"));
218
229
  }
219
- return (React__default["default"].createElement("div", { className: styles$2.results, "data-testid": DATALIST_TOTALCOUNT_TEST_ID }, output));
230
+ return (React__default["default"].createElement("div", { className: styles$4.results, "data-testid": DATALIST_TOTALCOUNT_TEST_ID }, output));
220
231
  }
221
232
 
222
- var css_248z$1 = ".Rh--6fVpkm4- {\n padding: calc(16px / 2);\n padding: var(--space-small);\n}\n\n.zbS7UvpVoWI- {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n -ms-flex-pack: justify;\n justify-content: space-between;\n height: calc(16px * 3);\n height: var(--space-largest);\n padding: calc(16px * 1) 0;\n padding: var(--space-base) 0;\n}\n";
223
- var styles$1 = {"loadingItem":"Rh--6fVpkm4-","mobileLoadingState":"zbS7UvpVoWI-"};
224
- styleInject_es.styleInject(css_248z$1);
233
+ var css_248z$3 = ".Rh--6fVpkm4- {\n padding: calc(16px / 2);\n padding: var(--space-small);\n}\n\n.zbS7UvpVoWI- {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n -ms-flex-pack: justify;\n justify-content: space-between;\n height: calc(16px * 3);\n height: var(--space-largest);\n padding: calc(16px * 1) 0;\n padding: var(--space-base) 0;\n}\n";
234
+ var styles$3 = {"loadingItem":"Rh--6fVpkm4-","mobileLoadingState":"zbS7UvpVoWI-"};
235
+ styleInject_es.styleInject(css_248z$3);
225
236
 
226
237
  function DataListLayoutInternal({ layouts, renderLayout, mediaMatches, }) {
227
238
  const sizePropsOfLayouts = layouts === null || layouts === void 0 ? void 0 : layouts.map(layout => layout.props.size || "xs");
@@ -259,7 +270,7 @@ function DataListItems({ layouts, mediaMatches, data, }) {
259
270
  const elementData = generateListItemElements(data);
260
271
  return (React__default["default"].createElement(DataListLayoutInternal, { layouts: layouts, mediaMatches: mediaMatches, renderLayout: layout => {
261
272
  return (React__default["default"].createElement(React__default["default"].Fragment, null, elementData.map((child, i) => {
262
- return (React__default["default"].createElement("div", { className: styles$5.listItem, key: `${data[i].id}` }, layout.props.children(child)));
273
+ return (React__default["default"].createElement("div", { className: styles$7.listItem, key: `${data[i].id}` }, layout.props.children(child)));
263
274
  })));
264
275
  } }));
265
276
  }
@@ -275,7 +286,7 @@ function DataListHeader({ layouts, mediaMatches, headerData, headerVisibility, }
275
286
  if (!showHeader || !headerData)
276
287
  return React__default["default"].createElement(React__default["default"].Fragment, null);
277
288
  return (React__default["default"].createElement(DataListLayoutInternal, { layouts: layouts, renderLayout: layout => {
278
- return (React__default["default"].createElement("div", { className: styles$5.headerTitles }, layout.props.children(headerData)));
289
+ return (React__default["default"].createElement("div", { className: styles$7.headerTitles }, layout.props.children(headerData)));
279
290
  }, mediaMatches: mediaMatches }));
280
291
  }
281
292
 
@@ -293,13 +304,13 @@ function DataListLoadingState({ loading, headers, layouts, mediaMatches, }) {
293
304
  if (layout.props.size === "xs") {
294
305
  return React__default["default"].createElement(LoadingStateXSBreakpoint, null);
295
306
  }
296
- return (React__default["default"].createElement(React__default["default"].Fragment, null, loadingElements.map((child, i) => (React__default["default"].createElement("div", { className: styles$1.loadingItem, key: i, "data-testid": DATALIST_LOADINGSTATE_ROW_TEST_ID }, layout.props.children(child))))));
307
+ return (React__default["default"].createElement(React__default["default"].Fragment, null, loadingElements.map((child, i) => (React__default["default"].createElement("div", { className: styles$3.loadingItem, key: i, "data-testid": DATALIST_LOADINGSTATE_ROW_TEST_ID }, layout.props.children(child))))));
297
308
  } }));
298
309
  }
299
310
  function LoadingStateXSBreakpoint() {
300
311
  const loadingData = new Array(LOADING_STATE_LIMIT_ITEMS).fill(0);
301
312
  return (React__default["default"].createElement(React__default["default"].Fragment, null, loadingData.map((_, i) => {
302
- return (React__default["default"].createElement("div", { className: styles$1.mobileLoadingState, key: i },
313
+ return (React__default["default"].createElement("div", { className: styles$3.mobileLoadingState, key: i },
303
314
  React__default["default"].createElement(Glimmer.Glimmer.Text, null)));
304
315
  })));
305
316
  }
@@ -330,6 +341,7 @@ function breakpointToMediaQuery(breakpoint) {
330
341
  }
331
342
 
332
343
  const defaultValues = {
344
+ title: "",
333
345
  data: [],
334
346
  headers: {},
335
347
  children: [],
@@ -339,9 +351,9 @@ function useDataListContext() {
339
351
  return React.useContext(DataListContext);
340
352
  }
341
353
 
342
- var css_248z = ".E-mG3prPSN8- {\n position: relative;\n}\n\n._0LGNoZxtqSE- {\n display: grid;\n padding: calc(16px / 2) 0;\n padding: var(--space-small) 0;\n overflow-x: auto;\n grid-auto-flow: column;\n grid-auto-columns: -webkit-max-content;\n grid-auto-columns: max-content;\n -ms-flex-align: center;\n align-items: center;\n}\n\n._0LGNoZxtqSE- > :nth-child(n + 3):not(:last-child) {\n margin-left: calc(16px / 2);\n margin-left: var(--space-small);\n}\n\n.LA3-gtgehrE- {\n visibility: hidden;\n}\n\n.fSPvawYWzZ8-::before,\n.ChfEJMPPVTk-::after {\n content: \"\";\n position: absolute;\n top: 0;\n width: calc(16px * 1.5);\n width: var(--space-large);\n height: 100%;\n background-image: linear-gradient(\n to right,\n rgba(255, 255, 255, 1) 0%,\n rgba(255, 255, 255, 0) 100%\n );\n background-image: linear-gradient(\n var(--data-list-filters-overflow-shadow-angle, to right),\n var(--color-surface) 0%,\n rgba(var(--color-white--rgb), 0) 100%\n );\n pointer-events: none;\n}\n\n.fSPvawYWzZ8-::before {\n left: 0;\n}\n\n.ChfEJMPPVTk-::after {\n --data-list-filters-overflow-shadow-angle: to left;\n right: 0;\n}\n";
343
- var styles = {"filters":"E-mG3prPSN8-","filterActions":"_0LGNoZxtqSE-","overflowTrigger":"LA3-gtgehrE-","overflowLeft":"fSPvawYWzZ8-","overflowRight":"ChfEJMPPVTk-"};
344
- styleInject_es.styleInject(css_248z);
354
+ var css_248z$2 = ".E-mG3prPSN8- {\n -ms-flex-item-align: center;\n align-self: center;\n position: relative;\n min-width: 0;\n}\n\n._0LGNoZxtqSE- {\n display: grid;\n overflow-x: auto;\n grid-auto-flow: column;\n grid-auto-columns: -webkit-max-content;\n grid-auto-columns: max-content;\n -ms-flex-align: center;\n align-items: center;\n}\n\n._0LGNoZxtqSE- > :nth-child(n + 3):not(:last-child) {\n margin-left: calc(16px / 2);\n margin-left: var(--space-small);\n}\n\n.LA3-gtgehrE- {\n visibility: hidden;\n}\n\n.fSPvawYWzZ8-::before,\n.ChfEJMPPVTk-::after {\n content: \"\";\n position: absolute;\n top: 0;\n width: calc(16px * 1.5);\n width: var(--space-large);\n height: 100%;\n background-image: linear-gradient(\n to right,\n rgba(255, 255, 255, 1) 0%,\n rgba(255, 255, 255, 0) 100%\n );\n background-image: linear-gradient(\n var(--data-list-filters-overflow-shadow-angle, to right),\n var(--color-surface) 0%,\n rgba(var(--color-white--rgb), 0) 100%\n );\n pointer-events: none;\n}\n\n.fSPvawYWzZ8-::before {\n left: 0;\n}\n\n.ChfEJMPPVTk-::after {\n --data-list-filters-overflow-shadow-angle: to left;\n right: 0;\n}\n";
355
+ var styles$2 = {"filters":"E-mG3prPSN8-","filterActions":"_0LGNoZxtqSE-","overflowTrigger":"LA3-gtgehrE-","overflowLeft":"fSPvawYWzZ8-","overflowRight":"ChfEJMPPVTk-"};
356
+ styleInject_es.styleInject(css_248z$2);
345
357
 
346
358
  const CONTAINER_TEST_ID = "ATL-DataListFilters-Container";
347
359
 
@@ -361,21 +373,88 @@ function InternalDataListFilters() {
361
373
  if (!component)
362
374
  return null;
363
375
  const { children } = component.props;
364
- return (React__default["default"].createElement("div", { "data-testid": CONTAINER_TEST_ID, className: classnames__default["default"](styles.filters, {
365
- [styles.overflowLeft]: !isLeftVisible,
366
- [styles.overflowRight]: !isRightVisible,
376
+ return (React__default["default"].createElement("div", { "data-testid": CONTAINER_TEST_ID, className: classnames__default["default"](styles$2.filters, {
377
+ [styles$2.overflowLeft]: !isLeftVisible,
378
+ [styles$2.overflowRight]: !isRightVisible,
367
379
  }) },
368
- React__default["default"].createElement("div", { className: styles.filterActions },
369
- React__default["default"].createElement("span", { ref: leftRef, className: styles.overflowTrigger }),
380
+ React__default["default"].createElement("div", { className: styles$2.filterActions },
381
+ React__default["default"].createElement("span", { ref: leftRef, className: styles$2.overflowTrigger }),
370
382
  children,
371
- React__default["default"].createElement("span", { ref: rightRef, className: styles.overflowTrigger }))));
383
+ React__default["default"].createElement("span", { ref: rightRef, className: styles$2.overflowTrigger }))));
384
+ }
385
+
386
+ var css_248z$1 = ".SaPMOnvw-3c- {\n --transition-properties: var(--timing-base) ease-in-out;\n --button-offset: calc(var(--space-largest) - var(--space-smallest));\n\n position: absolute;\n /* Inputs are off by 1 when put beside a button */\n top: calc((16px / 2) - 1px);\n top: calc(calc(16px / 2) - 1px);\n top: calc(var(--space-small) - 1px);\n right: calc((16px * 3) - (16px / 8));\n right: calc(calc(16px * 3) - calc(16px / 8));\n right: var(--button-offset);\n visibility: hidden;\n width: 0;\n opacity: 0;\n transition: opacity 200ms ease-in-out,\n width 200ms ease-in-out, visibility 200ms ease-in-out;\n transition: opacity var(--transition-properties),\n width var(--transition-properties), visibility var(--transition-properties);\n}\n\n._3S1-91v2SYI- {\n visibility: visible;\n width: calc(100% - var(--button-offset));\n opacity: 1;\n}\n\n@media (min-width: 768px) {\n\n.SaPMOnvw-3c-,\n._3S1-91v2SYI- {\n position: static;\n visibility: visible;\n width: auto;\n opacity: 1;\n transition: none;\n}\n }\n\n@media (--medium-screens-and-up) {\n\n.SaPMOnvw-3c-,\n._3S1-91v2SYI- {\n position: static;\n visibility: visible;\n width: auto;\n opacity: 1;\n transition: none;\n}\n }\n\n._9GlBktsUALw- {\n display: block;\n}\n\n@media (min-width: 768px) {\n\n._9GlBktsUALw- {\n display: none;\n}\n }\n\n@media (--medium-screens-and-up) {\n\n._9GlBktsUALw- {\n display: none;\n}\n }\n\n/*\n * No Filters Styling\n *\n * When there are no filters, the search input is always visible.\n * ------------------------------------------------------------------------- */\n\n._3Pjrt42x454- .SaPMOnvw-3c- {\n position: static;\n visibility: visible;\n width: auto;\n opacity: 1;\n transition: none;\n}\n\n@media (min-width: 768px) {\n\n._3Pjrt42x454- .SaPMOnvw-3c- {\n max-width: 30%;\n}\n }\n\n@media (--medium-screens-and-up) {\n\n._3Pjrt42x454- .SaPMOnvw-3c- {\n max-width: 30%;\n}\n }\n\n._3Pjrt42x454- ._9GlBktsUALw- {\n display: none;\n}\n";
387
+ var styles$1 = {"searchInput":"SaPMOnvw-3c-","searchInputVisible":"_3S1-91v2SYI-","searchButton":"_9GlBktsUALw-","withNoFilters":"_3Pjrt42x454-"};
388
+ styleInject_es.styleInject(css_248z$1);
389
+
390
+ const DATA_LIST_SEARCH_TEST_ID = "ATL-DataList-Search-input-wrapper";
391
+ // This component is meant to capture the props of the DataList.Search
392
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
393
+ function DataListSearch(_) {
394
+ return null;
395
+ }
396
+ /**
397
+ * Renders the DataList.Search component
398
+ */
399
+ function InternalDataListSearch() {
400
+ const inputRef = React.useRef(null);
401
+ const [visible, setVisible] = React.useState(false);
402
+ const { searchComponent, filterComponent, title } = useDataListContext();
403
+ const debouncedSearch = React.useCallback(debounce__default["default"]((value) => { var _a; return (_a = searchComponent === null || searchComponent === void 0 ? void 0 : searchComponent.props) === null || _a === void 0 ? void 0 : _a.onSearch(value); }, SEARCH_DEBOUNCE_DELAY), [searchComponent === null || searchComponent === void 0 ? void 0 : searchComponent.props.onSearch]);
404
+ if (!searchComponent)
405
+ return null;
406
+ const { placeholder } = searchComponent.props;
407
+ return (React__default["default"].createElement("div", { className: classnames__default["default"]({ [styles$1.withNoFilters]: !filterComponent }) },
408
+ React__default["default"].createElement("div", { "data-testid": DATA_LIST_SEARCH_TEST_ID, className: classnames__default["default"](styles$1.searchInput, {
409
+ [styles$1.searchInputVisible]: visible,
410
+ }) },
411
+ React__default["default"].createElement(InputText.InputText, { ref: inputRef, placeholder: getPlaceholder(), onChange: debouncedSearch, prefix: { icon: "search" }, size: "small" })),
412
+ React__default["default"].createElement("div", { className: styles$1.searchButton },
413
+ React__default["default"].createElement(AnimatedSwitcher.AnimatedSwitcher, { switched: visible, initialChild: React__default["default"].createElement(Button.Button, { variation: "subtle", icon: "search", ariaLabel: "Search", onClick: toggleSearch }), switchTo: React__default["default"].createElement(Button.Button, { variation: "subtle", icon: "remove", ariaLabel: "Close search bar", onClick: toggleSearch }) }))));
414
+ function getPlaceholder() {
415
+ if (placeholder)
416
+ return placeholder;
417
+ if (title)
418
+ return `Search ${title}...`;
419
+ return `Search...`;
420
+ }
421
+ function toggleSearch() {
422
+ const visibility = !visible;
423
+ setVisible(visibility);
424
+ if (visibility && inputRef.current) {
425
+ setTimeout(inputRef.current.focus, design.tokens["timing-quick"]);
426
+ }
427
+ }
428
+ }
429
+
430
+ var css_248z = ".PGOHzrMh374- {\n --offset: 1px;\n --sticky-header-transition-properties: var(--timing-base) ease-in-out;\n\n position: sticky;\n top: calc(1px * -1);\n top: calc(var(--offset) * -1);\n z-index: 1;\n z-index: var(--elevation-base);\n padding-top: 1px;\n padding-top: var(--offset);\n background-color: rgba(255, 255, 255, 1);\n background-color: var(--color-surface);\n}\n\n/**\n * Draw a border that gets covered by the column headers border when it shows up.\n *\n * Mostly to prevent us from writing some complex JS to remove the border when\n * the column headers show up.\n */\n\n.PGOHzrMh374-::before {\n content: \"\";\n display: block;\n position: absolute;\n bottom: 0;\n left: 50%;\n z-index: -1;\n width: 0;\n height: 0;\n background-color: rgb(225, 225, 225);\n background-color: var(--color-border);\n -webkit-transform: translateX(-50%);\n transform: translateX(-50%);\n transition: height var(--sticky-header-transition-properties)\n 100ms,\n width var(--sticky-header-transition-properties);\n transition: height var(--sticky-header-transition-properties)\n var(--timing-quick),\n width var(--sticky-header-transition-properties);\n}\n\n.PGjWc5ocjpI-::before {\n width: 100%;\n height: calc(16px / 8);\n height: var(--border-thick);\n transition: height var(--sticky-header-transition-properties),\n width var(--sticky-header-transition-properties) 100ms;\n transition: height var(--sticky-header-transition-properties),\n width var(--sticky-header-transition-properties) var(--timing-quick);\n}\n";
431
+ var styles = {"header":"PGOHzrMh374-","stuck":"PGjWc5ocjpI-"};
432
+ styleInject_es.styleInject(css_248z);
433
+
434
+ function DataListStickyHeader({ children }) {
435
+ const [isStuck, setIsStuck] = React.useState(false);
436
+ const ref = React.useRef(null);
437
+ const handleObserver = React.useCallback(([e]) => setIsStuck(e.intersectionRatio < 1), [setIsStuck]);
438
+ React.useEffect(() => {
439
+ if (!window.IntersectionObserver)
440
+ return;
441
+ const observer = new IntersectionObserver(handleObserver, { threshold: 1 });
442
+ ref.current && observer.observe(ref.current);
443
+ return () => {
444
+ ref.current && observer.unobserve(ref.current);
445
+ };
446
+ }, [handleObserver, ref.current]);
447
+ return (React__default["default"].createElement("div", { ref: ref, className: classnames__default["default"](styles.header, { [styles.stuck]: isStuck }) }, children));
372
448
  }
373
449
 
374
450
  function DataList(props) {
375
- return (React__default["default"].createElement(DataListContext.Provider, { value: props },
376
- React__default["default"].createElement(InternalDataList, Object.assign({}, props))));
451
+ const searchComponent = getCompoundComponent(props.children, DataListSearch);
452
+ const filterComponent = getCompoundComponent(props.children, DataListFilters);
453
+ return (React__default["default"].createElement(DataListContext.Provider, { value: Object.assign({ searchComponent, filterComponent }, props) },
454
+ React__default["default"].createElement(InternalDataList, null)));
377
455
  }
378
- function InternalDataList({ data, headers, loading = false, filterApplied = false, children, title, totalCount, headerVisibility = { xs: true, sm: true, md: true, lg: true, xl: true }, }) {
456
+ function InternalDataList() {
457
+ const { data, headers, loading = false, filterApplied = false, children, title, totalCount, headerVisibility = { xs: true, sm: true, md: true, lg: true, xl: true }, } = useDataListContext();
379
458
  const allLayouts = getCompoundComponents(children, DataListLayout);
380
459
  const headerData = generateHeaderElements(headers);
381
460
  const mediaMatches = useLayoutMediaQueries();
@@ -386,12 +465,14 @@ function InternalDataList({ data, headers, loading = false, filterApplied = fals
386
465
  isFilterApplied,
387
466
  setIsFilterApplied,
388
467
  });
389
- return (React__default["default"].createElement("div", { className: styles$5.wrapper },
390
- React__default["default"].createElement("div", { className: styles$5.titleContainer },
468
+ return (React__default["default"].createElement("div", { className: styles$7.wrapper },
469
+ React__default["default"].createElement("div", { className: styles$7.titleContainer },
391
470
  title && React__default["default"].createElement(Heading.Heading, { level: 3 }, title),
392
471
  React__default["default"].createElement(DataListTotalCount, { totalCount: totalCount, loading: loading })),
393
- React__default["default"].createElement("div", { className: styles$5.header },
394
- React__default["default"].createElement(InternalDataListFilters, null),
472
+ React__default["default"].createElement(DataListStickyHeader, null,
473
+ React__default["default"].createElement("div", { className: styles$7.headerFilters },
474
+ React__default["default"].createElement(InternalDataListFilters, null),
475
+ React__default["default"].createElement(InternalDataListSearch, null)),
395
476
  headerData && (React__default["default"].createElement(DataListHeader, { layouts: allLayouts, headerData: headerData, headerVisibility: headerVisibility, mediaMatches: mediaMatches }))),
396
477
  React__default["default"].createElement(DataListLoadingState, { loading: loading, headers: headers, layouts: allLayouts, mediaMatches: mediaMatches }),
397
478
  !loading && (React__default["default"].createElement(DataListItems, { data: data, layouts: allLayouts, mediaMatches: mediaMatches })),
@@ -400,5 +481,6 @@ function InternalDataList({ data, headers, loading = false, filterApplied = fals
400
481
  DataList.Layout = DataListLayout;
401
482
  DataList.EmptyState = DataListEmptyState;
402
483
  DataList.Filters = DataListFilters;
484
+ DataList.Search = DataListSearch;
403
485
 
404
486
  exports.DataList = DataList;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components",
3
- "version": "4.30.0",
3
+ "version": "4.31.0",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -84,5 +84,5 @@
84
84
  "> 1%",
85
85
  "IE 10"
86
86
  ],
87
- "gitHead": "3e19cd88bd918e5e8a6c047bc5b3683d60c31451"
87
+ "gitHead": "da7a60fec85ae3efd2451ff0d0e80c1e9b32d84b"
88
88
  }