@luscii-healthtech/web-ui 30.10.4 → 30.11.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.
@@ -20,6 +20,11 @@ export interface ConfirmationDialogChoice<T extends string = string> {
20
20
  }
21
21
  export interface ConfirmationDialogProps {
22
22
  children?: React.ReactNode;
23
+ /**
24
+ * Defaults to "dialog-small". These are a subset
25
+ * of the sizes of the BaseModal component.
26
+ */
27
+ size?: "dialog-small" | "dialog-medium";
23
28
  texts: ConfirmationDialogTexts;
24
29
  isOpen: boolean;
25
30
  onCancel: () => void;
@@ -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 {};
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  import { type ModalHeaderProps } from "./ModalHeader";
3
3
  import { type ModalFooterProps } from "./ModalFooter";
4
- export type ModalSize = "small" | "medium" | "wide";
4
+ export type ModalSize = "dialog-small" | "dialog-medium" | "small" | "medium" | "wide";
5
5
  export type ModalBaseProps = Omit<ModalHeaderProps, "title"> & {
6
6
  children: React.ReactNode;
7
7
  size?: ModalSize;
@@ -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:
@@ -2402,7 +2402,8 @@ const ModalBase = (props) => {
2402
2402
  { isOpen, data: { "test-id": (_a = props.dataTestId) !== null && _a !== void 0 ? _a : "react-modal" }, contentLabel: props.title, overlayClassName: classNames__default.default("ui-px-2 ui-py-15", "ui-z-20", "ui-fixed ui-top-0 ui-bottom-0 ui-left-0 ui-right-0", "ui-overflow-x-hidden ui-overflow-y-auto", "ui-bg-overlay"), onRequestClose: props.onCloseClick, shouldFocusAfterRender: false, shouldCloseOnOverlayClick: false, className: classNames__default.default("ui-mx-auto ui-my-0", "ui-relative", "ui-rounded-lg ui-bg-white ui-shadow-lg ui-outline-none", {
2403
2403
  "ui-overflow-y-hidden": props.scrollableContent,
2404
2404
  "ui-mt-17": props.withExtraMarginTop,
2405
- "md:ui-w-80": size === "small",
2405
+ "md:ui-w-120": size === "dialog-medium",
2406
+ "md:ui-w-80": size === "small" || size === "dialog-small",
2406
2407
  "md:ui-w-132": size === "medium",
2407
2408
  "md:ui-w-216": size === "wide"
2408
2409
  }) },
@@ -3015,7 +3016,7 @@ const ConfirmationDialogMessage = (props) => {
3015
3016
  };
3016
3017
 
3017
3018
  const ConfirmationDialog = (_a) => {
3018
- var { dataTestId = "confirmation-dialog-modal" } = _a, props = __rest(_a, ["dataTestId"]);
3019
+ var { dataTestId = "confirmation-dialog-modal", size = "dialog-small" } = _a, props = __rest(_a, ["dataTestId", "size"]);
3019
3020
  const { children, choices } = props;
3020
3021
  const { message } = props.texts;
3021
3022
  const [selectedChoice, setSelectedChoice] = React.useState(choices === null || choices === void 0 ? void 0 : choices.options.find((choice) => choice.value === choices.defaultChoice));
@@ -3036,7 +3037,7 @@ const ConfirmationDialog = (_a) => {
3036
3037
  };
3037
3038
  return React__namespace.default.createElement(
3038
3039
  ModalBase,
3039
- { size: "small", dataTestId, isOpen: props.isOpen, withExtraMarginTop: true, onCloseClick: handleOnCloseClick, footerTrailingComponents: {
3040
+ { size, dataTestId, isOpen: props.isOpen, withExtraMarginTop: true, onCloseClick: handleOnCloseClick, footerTrailingComponents: {
3040
3041
  primaryButtonProps: {
3041
3042
  text: props.texts.confirmLabel,
3042
3043
  onClick: onConfirmHandler,
@@ -6296,7 +6297,7 @@ const ActiveFilters = (props) => {
6296
6297
  null,
6297
6298
  React__namespace.default.createElement(
6298
6299
  "span",
6299
- { 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" },
6300
+ { 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" },
6300
6301
  React__namespace.default.createElement(Text, { variant: "base" }, activeFilter.label),
6301
6302
  React__namespace.default.createElement(
6302
6303
  "button",
@@ -6391,16 +6392,58 @@ const FiltersMenus = (props) => {
6391
6392
  );
6392
6393
  };
6393
6394
 
6395
+ const PresetFiltersMenu = (props) => {
6396
+ const { localization, presetFilterOptions, onPresetFilterOptionChange } = props;
6397
+ const selectedPresetFilter = presetFilterOptions.find((presetFilterOption) => presetFilterOption.isChecked);
6398
+ const menuLabel = presetFilterOptions ? [localization.presetFiltersLabel, selectedPresetFilter === null || selectedPresetFilter === void 0 ? void 0 : selectedPresetFilter.label].filter(Boolean).join(": ") : localization.presetFiltersLabel;
6399
+ return React__namespace.default.createElement(
6400
+ react.Menu,
6401
+ { as: "div", className: "ui-relative ui-inline-block ui-text-left" },
6402
+ React__namespace.default.createElement(
6403
+ "div",
6404
+ null,
6405
+ React__namespace.default.createElement(
6406
+ react.Menu.Button,
6407
+ { className: "ui-group ui-inline-flex ui-justify-center ui-text-sm ui-font-medium ui-text-slate-700 hover:ui-text-slate-900" },
6408
+ menuLabel,
6409
+ 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" })
6410
+ )
6411
+ ),
6412
+ React__namespace.default.createElement(
6413
+ react.Transition,
6414
+ { 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" },
6415
+ React__namespace.default.createElement(
6416
+ react.Menu.Items,
6417
+ { 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" },
6418
+ 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))))
6419
+ )
6420
+ )
6421
+ );
6422
+ };
6423
+
6394
6424
  const FilterBar = (_a) => {
6395
- var { localization, categorizedFilters, sortingOptions } = _a, props = __rest(_a, ["localization", "categorizedFilters", "sortingOptions"]);
6425
+ var { localization, categorizedFilters, sortingOptions, presetFilterOptions } = _a, props = __rest(_a, ["localization", "categorizedFilters", "sortingOptions", "presetFilterOptions"]);
6396
6426
  const onSortOptionChange = (changedSortingOption) => {
6427
+ var _a2;
6397
6428
  const updatedSortingOptions = sortingOptions.map((previousSortingOption) => {
6398
6429
  if (previousSortingOption.id === changedSortingOption.id) {
6399
6430
  return Object.assign({}, changedSortingOption);
6400
6431
  }
6401
6432
  return Object.assign(Object.assign({}, previousSortingOption), { isChecked: false });
6402
6433
  });
6403
- props.onSortingChange(updatedSortingOptions);
6434
+ (_a2 = props.onSortingChange) === null || _a2 === void 0 ? void 0 : _a2.call(props, updatedSortingOptions);
6435
+ };
6436
+ const onPresetFilterOptionChange = (changedPresetFilterOption) => {
6437
+ if (!props.onPresetFilterChange || !presetFilterOptions) {
6438
+ return;
6439
+ }
6440
+ const updatedPresetFilterOptions = presetFilterOptions.map((previousPresetFilterOption) => {
6441
+ if (previousPresetFilterOption.id === changedPresetFilterOption.id) {
6442
+ return changedPresetFilterOption;
6443
+ }
6444
+ return Object.assign(Object.assign({}, previousPresetFilterOption), { isChecked: false });
6445
+ });
6446
+ props.onPresetFilterChange(updatedPresetFilterOptions);
6404
6447
  };
6405
6448
  const onFilterOptionChange = ({ changedFilterOption }) => {
6406
6449
  const updatedFilters = categorizedFilters.map((categorizedFilter) => {
@@ -6424,8 +6467,18 @@ const FilterBar = (_a) => {
6424
6467
  { className: "ui-rounded-lg ui-py-4" },
6425
6468
  React__namespace.default.createElement(
6426
6469
  Stack,
6427
- { axis: "x", align: "center", justify: "between" },
6428
- React__namespace.default.createElement(SortMenu, { localization, sortingOptions, onSortOptionChange }),
6470
+ { axis: "x", align: "normal", justify: "between" },
6471
+ React__namespace.default.createElement(
6472
+ Stack,
6473
+ { axis: "x", gap: "m", align: "normal" },
6474
+ presetFilterOptions && React__namespace.default.createElement(
6475
+ React__namespace.default.Fragment,
6476
+ null,
6477
+ React__namespace.default.createElement(PresetFiltersMenu, { localization, presetFilterOptions, onPresetFilterOptionChange }),
6478
+ React__namespace.default.createElement(Box, { className: "ui-h-full ui-w-[1px] ui-border-l-1 ui-border-color-border" })
6479
+ ),
6480
+ React__namespace.default.createElement(SortMenu, { localization, sortingOptions, onSortOptionChange })
6481
+ ),
6429
6482
  React__namespace.default.createElement(FiltersMenus, { filters: categorizedFilters, onFilterOptionChange })
6430
6483
  )
6431
6484
  ),