@luscii-healthtech/web-ui 30.10.5 → 31.0.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.
@@ -1,4 +1,4 @@
1
1
  import React from "react";
2
- import type { BreadcrumbDividerProps, Crumb } from "./Breadcrumbs.types";
3
- export declare const BreadcrumbItem: React.FC<Crumb>;
2
+ import type { BreadcrumbDividerProps, Crumb, CrumbRenderProp } from "./Breadcrumbs.types";
3
+ export declare const BreadcrumbItem: React.FC<Crumb & CrumbRenderProp>;
4
4
  export declare const BreadcrumbDividerItem: React.FC<BreadcrumbDividerProps>;
@@ -1,16 +1,15 @@
1
1
  /// <reference types="react" />
2
2
  import Text from "../Text/Text";
3
- type RenderBreadcrumb = (props: {
3
+ export type RenderBreadcrumbProps = {
4
4
  name: string;
5
5
  link?: string;
6
6
  textProps: React.ComponentProps<typeof Text>;
7
- }) => React.ReactNode;
8
- export type Crumb = {
7
+ };
8
+ export type RenderBreadcrumb = (props: RenderBreadcrumbProps) => React.ReactNode;
9
+ export type CrumbRenderProp = {
9
10
  render: RenderBreadcrumb;
10
- key?: string;
11
- name: never;
12
- link: never;
13
- } | {
11
+ };
12
+ export type Crumb = {
14
13
  name: string;
15
14
  key?: string;
16
15
  link?: string;
@@ -18,11 +17,10 @@ export type Crumb = {
18
17
  export interface BreadcrumbProps {
19
18
  crumbs: Crumb[];
20
19
  truncateAfter?: number;
21
- renderItem?: RenderBreadcrumb;
20
+ renderItem: RenderBreadcrumb;
22
21
  }
23
22
  export type BreadcrumbDividerProps = {
24
23
  onClick: () => void;
25
24
  truncatedCrumbs: Crumb[];
26
25
  };
27
26
  export declare const isBreadcrumbDividerProps: (subject: unknown) => subject is BreadcrumbDividerProps;
28
- export {};
@@ -1,5 +1,5 @@
1
1
  import { ReactElement } from "react";
2
- import { CategorizedFilters, FilterBarLocalization, TransformedSortingOptions } from "./FilterBarProps.type";
2
+ import { CategorizedFilters, FilterBarLocalization, TransformedPresetFilterOptions, TransformedSortingOptions } from "./FilterBarProps.type";
3
3
  type FilterBarProps<SortableEntity extends object> = {
4
4
  /**
5
5
  * A list of grouped filters to be displayed in the FilterBar.
@@ -84,13 +84,15 @@ type FilterBarProps<SortableEntity extends object> = {
84
84
  /**
85
85
  * For more details, see {@link TransformedSortingOptions}.
86
86
  */
87
- sortingOptions: TransformedSortingOptions<SortableEntity>;
88
87
  onFilterChange: (updatedFilters: CategorizedFilters) => void;
89
- onSortingChange: (updatedSortingOptions: TransformedSortingOptions<SortableEntity>) => void;
88
+ sortingOptions: TransformedSortingOptions<SortableEntity>;
89
+ onSortingChange?: (updatedSortingOptions: TransformedSortingOptions<SortableEntity>) => void;
90
+ presetFilterOptions?: TransformedPresetFilterOptions<SortableEntity>;
91
+ onPresetFilterChange?: (updatedPresetFilterOptions: TransformedPresetFilterOptions<SortableEntity>) => void;
90
92
  /**
91
93
  * See {@link FilterBarLocalization}.
92
94
  */
93
95
  localization: FilterBarLocalization;
94
96
  };
95
- export declare const FilterBar: <SortableEntity extends object>({ localization, categorizedFilters, sortingOptions, ...props }: FilterBarProps<SortableEntity>) => ReactElement | null;
97
+ export declare const FilterBar: <SortableEntity extends object>({ localization, categorizedFilters, sortingOptions, presetFilterOptions, ...props }: FilterBarProps<SortableEntity>) => ReactElement | null;
96
98
  export {};
@@ -51,6 +51,24 @@ export type FilterBarLocalization = {
51
51
  * @default "Sort"
52
52
  */
53
53
  sortLabel: string;
54
+ /**
55
+ * The string prefix displayed when a preset filter is selected from the menu.
56
+ *
57
+ * @example "Showing patients with status: "
58
+ *
59
+ * Example of preset filter options
60
+ * - Active
61
+ * - Stopped
62
+ *
63
+ * UI shows: `Showing patients with status: Active`.
64
+ */
65
+ presetFilteredByLabel?: string;
66
+ /**
67
+ * The string for the unselected state of the preset filter.
68
+ *
69
+ * @example "Show by patient status"
70
+ */
71
+ presetFiltersLabel?: string;
54
72
  /**
55
73
  * The string for the active filters and the mobile filter menu label.
56
74
  *
@@ -89,4 +107,13 @@ export type OnFilterBarFilterChange = (params: {
89
107
  changedFilterOption: TransformedFilterOption;
90
108
  }) => void;
91
109
  export type OnFilterBarSortingChange<SortableEntity extends object> = (changedSortingOption: TransformedSortingOption<SortableEntity>) => void;
110
+ export type TransformedPresetFilterOption<SortableEntity extends object> = {
111
+ id: string;
112
+ label: string;
113
+ value: string;
114
+ isChecked: boolean;
115
+ filterFn?: (item: SortableEntity) => boolean;
116
+ };
117
+ export type TransformedPresetFilterOptions<SortableEntity extends object> = TransformedPresetFilterOption<SortableEntity>[];
118
+ export type OnFilterBarPresetFilterChange<SortableEntity extends object> = (changedPresetFilterOption: TransformedPresetFilterOption<SortableEntity>) => void;
92
119
  export {};
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import { FilterBarLocalization, OnFilterBarPresetFilterChange, TransformedPresetFilterOptions } from "./FilterBarProps.type";
3
+ type Props<SortableEntity extends object> = {
4
+ localization: FilterBarLocalization;
5
+ presetFilterOptions: TransformedPresetFilterOptions<SortableEntity>;
6
+ onPresetFilterOptionChange: OnFilterBarPresetFilterChange<SortableEntity>;
7
+ };
8
+ export declare const PresetFiltersMenu: <SortableEntity extends object>(props: Props<SortableEntity>) => React.ReactNode;
9
+ export {};
@@ -16,20 +16,26 @@ type WithPageHeaderBanner = {
16
16
  icon?: string | React.FunctionComponent<IconProps>;
17
17
  };
18
18
  };
19
+ type AllOrNone<T> = T | {
20
+ [K in keyof T]?: never;
21
+ };
22
+ type WithBreadcrumbProps = {
23
+ truncateBreadcrumbsAfter?: number;
24
+ } & AllOrNone<{
25
+ breadcrumbs: BreadcrumbProps["crumbs"];
26
+ renderBreadcrumbItem: BreadcrumbProps["renderItem"];
27
+ }>;
19
28
  export type PageHeaderProps = {
20
29
  /**
21
30
  * These are props passed to the `Tabbar` component. The name
22
31
  * `navigation` does not convey this well.
23
32
  */
24
33
  navigation?: Omit<TabbarProps, "withoutPadding">;
25
- breadcrumbs?: BreadcrumbProps["crumbs"];
26
- truncateBreadcrumbsAfter?: number;
27
- renderBreadcrumbItem?: BreadcrumbProps["renderItem"];
28
34
  dataTestId?: string;
29
35
  title: string | React.ReactNode;
30
36
  isPolling?: boolean;
31
37
  className?: string;
32
38
  contained?: boolean;
33
39
  accessories?: React.ReactNode;
34
- } & WithPageHeaderBanner;
40
+ } & WithPageHeaderBanner & WithBreadcrumbProps;
35
41
  export {};
@@ -21,7 +21,7 @@ type Props<C extends React.ElementType> = React.ComponentPropsWithoutRef<C> & {
21
21
  * Positioning of items along the Stack’s cross axis.
22
22
  * The values are based on Tailwind's classnames and are therefore limited to the following:
23
23
  */
24
- align?: "start" | "end" | "center" | "baseline" | "stretch";
24
+ align?: "start" | "end" | "center" | "baseline" | "stretch" | "normal";
25
25
  /**
26
26
  * Positioning of items along the Stack’s main axis.
27
27
  * The values are based on Tailwind's classnames and are therefore limited to the following:
@@ -4867,21 +4867,11 @@ const defaultTextProps = {
4867
4867
  };
4868
4868
  const BreadcrumbContent = (_a) => {
4869
4869
  var { name, link } = _a, props = __rest(_a, ["name", "link"]);
4870
- if ("render" in props && !!props.render) {
4871
- return React__namespace.default.createElement(React__namespace.default.Fragment, null, props.render({
4872
- name,
4873
- link,
4874
- textProps: link ? linkTextProps : defaultTextProps
4875
- }));
4876
- }
4877
- if (link) {
4878
- return React__namespace.default.createElement(
4879
- router.Link,
4880
- { to: link },
4881
- React__namespace.default.createElement(Text, Object.assign({}, linkTextProps), name)
4882
- );
4883
- }
4884
- return React__namespace.default.createElement(Text, Object.assign({}, defaultTextProps), name);
4870
+ return React__namespace.default.createElement(React__namespace.default.Fragment, null, props.render({
4871
+ name,
4872
+ link,
4873
+ textProps: link ? linkTextProps : defaultTextProps
4874
+ }));
4885
4875
  };
4886
4876
  const BreadcrumbDividerItem = ({ onClick, truncatedCrumbs }) => {
4887
4877
  return React__namespace.default.createElement(
@@ -4890,7 +4880,7 @@ const BreadcrumbDividerItem = ({ onClick, truncatedCrumbs }) => {
4890
4880
  React__namespace.default.createElement(
4891
4881
  "button",
4892
4882
  { onClick, title: truncatedCrumbs.map((crumb) => crumb.name).join(" / "), className: "ui-px-1" },
4893
- React__namespace.default.createElement(Text, { color: "blue-800", type: "strong" }, "\u2026")
4883
+ React__namespace.default.createElement(Text, { color: "blue-800", variant: "strong" }, "\u2026")
4894
4884
  )
4895
4885
  );
4896
4886
  };
@@ -5085,7 +5075,11 @@ const PageHeaderBlock = ({ header, title }) => {
5085
5075
  );
5086
5076
  }
5087
5077
  if (header) {
5088
- pageHeaderComponent = React__namespace.default.createElement(PageHeader, Object.assign({}, header));
5078
+ if (header.breadcrumbs !== void 0 && header.renderBreadcrumbItem !== void 0) {
5079
+ pageHeaderComponent = React__namespace.default.createElement(PageHeader, Object.assign({}, header, { breadcrumbs: header.breadcrumbs, renderBreadcrumbItem: header.renderBreadcrumbItem }));
5080
+ } else {
5081
+ pageHeaderComponent = React__namespace.default.createElement(PageHeader, Object.assign({}, header, { breadcrumbs: void 0, renderBreadcrumbItem: void 0 }));
5082
+ }
5089
5083
  }
5090
5084
  return React__namespace.default.createElement(
5091
5085
  React__namespace.default.Fragment,
@@ -6297,7 +6291,7 @@ const ActiveFilters = (props) => {
6297
6291
  null,
6298
6292
  React__namespace.default.createElement(
6299
6293
  "span",
6300
- { key: activeFilter.value, className: "ui-mx-1 ui-inline-flex ui-items-center ui-rounded-full ui-border ui-border-neutral-interactive ui-bg-white ui-py-xxxxs ui-pl-m ui-pr-xxs hover:ui-border-neutral-interactive--hover hover:ui-bg-primary-background" },
6294
+ { key: activeFilter.id, className: "ui-mx-1 ui-inline-flex ui-items-center ui-rounded-full ui-border ui-border-neutral-interactive ui-bg-white ui-py-xxxxs ui-pl-m ui-pr-xxs hover:ui-border-neutral-interactive--hover hover:ui-bg-primary-background" },
6301
6295
  React__namespace.default.createElement(Text, { variant: "base" }, activeFilter.label),
6302
6296
  React__namespace.default.createElement(
6303
6297
  "button",
@@ -6392,16 +6386,58 @@ const FiltersMenus = (props) => {
6392
6386
  );
6393
6387
  };
6394
6388
 
6389
+ const PresetFiltersMenu = (props) => {
6390
+ const { localization, presetFilterOptions, onPresetFilterOptionChange } = props;
6391
+ const selectedPresetFilter = presetFilterOptions.find((presetFilterOption) => presetFilterOption.isChecked);
6392
+ const menuLabel = presetFilterOptions ? [localization.presetFiltersLabel, selectedPresetFilter === null || selectedPresetFilter === void 0 ? void 0 : selectedPresetFilter.label].filter(Boolean).join(": ") : localization.presetFiltersLabel;
6393
+ return React__namespace.default.createElement(
6394
+ react.Menu,
6395
+ { as: "div", className: "ui-relative ui-inline-block ui-text-left" },
6396
+ React__namespace.default.createElement(
6397
+ "div",
6398
+ null,
6399
+ React__namespace.default.createElement(
6400
+ react.Menu.Button,
6401
+ { className: "ui-group ui-inline-flex ui-justify-center ui-text-sm ui-font-medium ui-text-slate-700 hover:ui-text-slate-900" },
6402
+ menuLabel,
6403
+ React__namespace.default.createElement(solid.ChevronDownIcon, { className: "-ui-mr-1 ui-ml-1 ui-h-5 ui-w-5 ui-flex-shrink-0 ui-text-slate-400 group-hover:ui-text-slate-500", "aria-hidden": "true" })
6404
+ )
6405
+ ),
6406
+ React__namespace.default.createElement(
6407
+ react.Transition,
6408
+ { as: React.Fragment, enter: "ui-transition ui-ease-out ui-duration-100", enterFrom: "ui-transform ui-opacity-0 ui-scale-95", enterTo: "ui-transform ui-opacity-100 ui-scale-100", leave: "ui-transition ui-ease-in ui-duration-75", leaveFrom: "ui-transform ui-opacity-100 ui-scale-100", leaveTo: "ui-transform ui-opacity-0 ui-scale-95" },
6409
+ React__namespace.default.createElement(
6410
+ react.Menu.Items,
6411
+ { className: "ui-absolute ui-left-0 ui-z-10 ui-mt-2 ui-w-40 ui-origin-top-left ui-rounded-md ui-bg-white ui-shadow-2xl ui-ring-1 ui-ring-black ui-ring-opacity-5 focus:ui-outline-none" },
6412
+ React__namespace.default.createElement("div", { className: "ui-py-1" }, presetFilterOptions.map((option) => React__namespace.default.createElement(react.Menu.Item, { key: option.id }, ({ active }) => React__namespace.default.createElement("span", { onClick: () => onPresetFilterOptionChange(Object.assign(Object.assign({}, option), { isChecked: !option.isChecked })), role: "button", className: classNames__default.default(option.isChecked ? "ui-font-medium ui-text-slate-900" : "ui-text-slate-500", "ui-block ui-px-4 ui-py-2 ui-text-sm", "ui-cursor-pointer", { "ui-bg-slate-100": active }) }, option.label))))
6413
+ )
6414
+ )
6415
+ );
6416
+ };
6417
+
6395
6418
  const FilterBar = (_a) => {
6396
- var { localization, categorizedFilters, sortingOptions } = _a, props = __rest(_a, ["localization", "categorizedFilters", "sortingOptions"]);
6419
+ var { localization, categorizedFilters, sortingOptions, presetFilterOptions } = _a, props = __rest(_a, ["localization", "categorizedFilters", "sortingOptions", "presetFilterOptions"]);
6397
6420
  const onSortOptionChange = (changedSortingOption) => {
6421
+ var _a2;
6398
6422
  const updatedSortingOptions = sortingOptions.map((previousSortingOption) => {
6399
6423
  if (previousSortingOption.id === changedSortingOption.id) {
6400
6424
  return Object.assign({}, changedSortingOption);
6401
6425
  }
6402
6426
  return Object.assign(Object.assign({}, previousSortingOption), { isChecked: false });
6403
6427
  });
6404
- props.onSortingChange(updatedSortingOptions);
6428
+ (_a2 = props.onSortingChange) === null || _a2 === void 0 ? void 0 : _a2.call(props, updatedSortingOptions);
6429
+ };
6430
+ const onPresetFilterOptionChange = (changedPresetFilterOption) => {
6431
+ if (!props.onPresetFilterChange || !presetFilterOptions) {
6432
+ return;
6433
+ }
6434
+ const updatedPresetFilterOptions = presetFilterOptions.map((previousPresetFilterOption) => {
6435
+ if (previousPresetFilterOption.id === changedPresetFilterOption.id) {
6436
+ return changedPresetFilterOption;
6437
+ }
6438
+ return Object.assign(Object.assign({}, previousPresetFilterOption), { isChecked: false });
6439
+ });
6440
+ props.onPresetFilterChange(updatedPresetFilterOptions);
6405
6441
  };
6406
6442
  const onFilterOptionChange = ({ changedFilterOption }) => {
6407
6443
  const updatedFilters = categorizedFilters.map((categorizedFilter) => {
@@ -6425,8 +6461,18 @@ const FilterBar = (_a) => {
6425
6461
  { className: "ui-rounded-lg ui-py-4" },
6426
6462
  React__namespace.default.createElement(
6427
6463
  Stack,
6428
- { axis: "x", align: "center", justify: "between" },
6429
- React__namespace.default.createElement(SortMenu, { localization, sortingOptions, onSortOptionChange }),
6464
+ { axis: "x", align: "normal", justify: "between" },
6465
+ React__namespace.default.createElement(
6466
+ Stack,
6467
+ { axis: "x", gap: "m", align: "normal" },
6468
+ presetFilterOptions && React__namespace.default.createElement(
6469
+ React__namespace.default.Fragment,
6470
+ null,
6471
+ React__namespace.default.createElement(PresetFiltersMenu, { localization, presetFilterOptions, onPresetFilterOptionChange }),
6472
+ React__namespace.default.createElement(Box, { className: "ui-h-full ui-w-[1px] ui-border-l-1 ui-border-color-border" })
6473
+ ),
6474
+ React__namespace.default.createElement(SortMenu, { localization, sortingOptions, onSortOptionChange })
6475
+ ),
6430
6476
  React__namespace.default.createElement(FiltersMenus, { filters: categorizedFilters, onFilterOptionChange })
6431
6477
  )
6432
6478
  ),