@luscii-healthtech/web-ui 58.2.5 → 58.4.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.
@@ -0,0 +1,34 @@
1
+ import type { ElementType, FC, ReactNode } from "react";
2
+ import { type Props as InstrumentCardProps } from "../InstrumentCard/InstrumentCard";
3
+ export type ActionType = "information" | "contact" | "lesson" | "calculation" | "measurement";
4
+ /**
5
+ * `Omit` distributes over the union so `InstrumentCard`'s discriminated
6
+ * `state`/`errorMessage` pairing survives; a plain `Omit` would collapse it.
7
+ */
8
+ type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
9
+ export type ActionCardMetadata = {
10
+ label: string;
11
+ /** Bordered instead of flat — used for schedule information. */
12
+ bordered?: boolean;
13
+ };
14
+ export type Props = {
15
+ type: ActionType;
16
+ title: string;
17
+ metadata?: ActionCardMetadata[];
18
+ /** Always rendered in the compact variant, so `variant` is not accepted. */
19
+ instruments?: DistributiveOmit<InstrumentCardProps, "variant">[];
20
+ actions?: ReactNode;
21
+ draggable?: boolean;
22
+ /**
23
+ * Element for the title. `variant` controls the visual size; this controls
24
+ * the document outline, which only the consuming page knows.
25
+ * @default "h3"
26
+ */
27
+ titleAs?: ElementType;
28
+ /** "list" drops the shadow so the card sits flush inside a list container. */
29
+ variant?: "default" | "compact" | "list";
30
+ onClick?: () => void;
31
+ className?: string;
32
+ };
33
+ export declare const ActionCard: FC<Props>;
34
+ export {};
@@ -0,0 +1,99 @@
1
+ import { type FC } from "react";
2
+ /**
3
+ * The kind of instrument, which selects the leading icon. Overridden by
4
+ * `ExclamationMarkIcon` when `state` is `"error"`.
5
+ */
6
+ export type InstrumentType = "info" | "questionnaire" | "numeric" | "document" | "contact" | "other" | "scripted" | "sum";
7
+ type BaseProps = {
8
+ /** Selects the leading icon. */
9
+ type: InstrumentType;
10
+ /**
11
+ * Name of the instrument. Truncates with an ellipsis in the `detailed`
12
+ * variant; in `compact` it is the pill label, and is replaced by
13
+ * `errorMessage` when `state` is `"error"`.
14
+ */
15
+ title: string;
16
+ /**
17
+ * First cell of the meta row, e.g. `"Lesson"`. `detailed` only, and not
18
+ * rendered when `state` is `"error"` — the error message takes its place.
19
+ */
20
+ category?: string;
21
+ /**
22
+ * Second cell of the meta row, e.g. `"Comments ON"`, separated from
23
+ * `category` by a vertical rule. Rendered only alongside `category`.
24
+ * `detailed` only, and replaced by the error message in the error state.
25
+ */
26
+ meta?: string;
27
+ /**
28
+ * Instrument identifier, e.g. `"Id-30ry3-7240"`. Rendered as a bordered
29
+ * tag beneath the meta row. `detailed` only; omitted when absent.
30
+ */
31
+ id?: string;
32
+ /**
33
+ * Number shown in the trailing tag — how many times the instrument is used.
34
+ * `detailed` only; omitted when absent. `0` renders, it is not treated as
35
+ * empty.
36
+ */
37
+ count?: number;
38
+ /**
39
+ * `"compact"` is an inline pill of icon plus label, for listing
40
+ * instruments inside an action card. `"detailed"` is a full-width row with
41
+ * a title, meta row and tags.
42
+ *
43
+ * @default "compact"
44
+ */
45
+ variant?: "compact" | "detailed";
46
+ /** Merged onto the root element. */
47
+ className?: string;
48
+ };
49
+ /**
50
+ * `errorMessage` is what the error state renders — it replaces the label in
51
+ * the compact variant and the meta row in the detailed variant. Requiring it
52
+ * alongside `state="error"` makes the empty-error-label state impossible to
53
+ * express, rather than guarding against it at runtime.
54
+ *
55
+ * In the error state the icon becomes `ExclamationMarkIcon` and the icon,
56
+ * title and message all take the error colour.
57
+ *
58
+ * @default state "default"
59
+ */
60
+ type StateProps = {
61
+ state?: "default";
62
+ errorMessage?: never;
63
+ } | {
64
+ state: "error";
65
+ errorMessage: string;
66
+ };
67
+ export type Props = BaseProps & StateProps;
68
+ /**
69
+ * Renders a single instrument belonging to an action.
70
+ *
71
+ * Use `variant="compact"` for the inline pills listed on an `ActionCard`, and
72
+ * `variant="detailed"` for the full rows in `ActionCardDetailed`. Both of
73
+ * those consumers force the variant, so it is not part of the props they
74
+ * accept.
75
+ *
76
+ * @example
77
+ * ```tsx
78
+ * <InstrumentCard type="info" title="How to stop smoking" />
79
+ *
80
+ * <InstrumentCard
81
+ * variant="detailed"
82
+ * type="questionnaire"
83
+ * title="Daily health questionnaire"
84
+ * category="Questionnaire"
85
+ * meta="Comments ON"
86
+ * id="Id-30ry3-7240"
87
+ * count={4}
88
+ * />
89
+ *
90
+ * <InstrumentCard
91
+ * type="other"
92
+ * title="Instrument title"
93
+ * state="error"
94
+ * errorMessage="Instrument missing in protocol"
95
+ * />
96
+ * ```
97
+ */
98
+ export declare const InstrumentCard: FC<Props>;
99
+ export {};
@@ -0,0 +1,34 @@
1
+ import type { ElementType, FC, ReactNode } from "react";
2
+ import { type Props as InstrumentCardProps } from "../InstrumentCard/InstrumentCard";
3
+ export type ActionType = "information" | "contact" | "lesson" | "calculation" | "measurement";
4
+ /**
5
+ * `Omit` distributes over the union so `InstrumentCard`'s discriminated
6
+ * `state`/`errorMessage` pairing survives; a plain `Omit` would collapse it.
7
+ */
8
+ type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
9
+ export type ActionCardMetadata = {
10
+ label: string;
11
+ /** Bordered instead of flat — used for schedule information. */
12
+ bordered?: boolean;
13
+ };
14
+ export type Props = {
15
+ type: ActionType;
16
+ title: string;
17
+ metadata?: ActionCardMetadata[];
18
+ /** Always rendered in the compact variant, so `variant` is not accepted. */
19
+ instruments?: DistributiveOmit<InstrumentCardProps, "variant">[];
20
+ actions?: ReactNode;
21
+ draggable?: boolean;
22
+ /**
23
+ * Element for the title. `variant` controls the visual size; this controls
24
+ * the document outline, which only the consuming page knows.
25
+ * @default "h3"
26
+ */
27
+ titleAs?: ElementType;
28
+ /** "list" drops the shadow so the card sits flush inside a list container. */
29
+ variant?: "default" | "compact" | "list";
30
+ onClick?: () => void;
31
+ className?: string;
32
+ };
33
+ export declare const ActionCard: FC<Props>;
34
+ export {};
@@ -0,0 +1,99 @@
1
+ import { type FC } from "react";
2
+ /**
3
+ * The kind of instrument, which selects the leading icon. Overridden by
4
+ * `ExclamationMarkIcon` when `state` is `"error"`.
5
+ */
6
+ export type InstrumentType = "info" | "questionnaire" | "numeric" | "document" | "contact" | "other" | "scripted" | "sum";
7
+ type BaseProps = {
8
+ /** Selects the leading icon. */
9
+ type: InstrumentType;
10
+ /**
11
+ * Name of the instrument. Truncates with an ellipsis in the `detailed`
12
+ * variant; in `compact` it is the pill label, and is replaced by
13
+ * `errorMessage` when `state` is `"error"`.
14
+ */
15
+ title: string;
16
+ /**
17
+ * First cell of the meta row, e.g. `"Lesson"`. `detailed` only, and not
18
+ * rendered when `state` is `"error"` — the error message takes its place.
19
+ */
20
+ category?: string;
21
+ /**
22
+ * Second cell of the meta row, e.g. `"Comments ON"`, separated from
23
+ * `category` by a vertical rule. Rendered only alongside `category`.
24
+ * `detailed` only, and replaced by the error message in the error state.
25
+ */
26
+ meta?: string;
27
+ /**
28
+ * Instrument identifier, e.g. `"Id-30ry3-7240"`. Rendered as a bordered
29
+ * tag beneath the meta row. `detailed` only; omitted when absent.
30
+ */
31
+ id?: string;
32
+ /**
33
+ * Number shown in the trailing tag — how many times the instrument is used.
34
+ * `detailed` only; omitted when absent. `0` renders, it is not treated as
35
+ * empty.
36
+ */
37
+ count?: number;
38
+ /**
39
+ * `"compact"` is an inline pill of icon plus label, for listing
40
+ * instruments inside an action card. `"detailed"` is a full-width row with
41
+ * a title, meta row and tags.
42
+ *
43
+ * @default "compact"
44
+ */
45
+ variant?: "compact" | "detailed";
46
+ /** Merged onto the root element. */
47
+ className?: string;
48
+ };
49
+ /**
50
+ * `errorMessage` is what the error state renders — it replaces the label in
51
+ * the compact variant and the meta row in the detailed variant. Requiring it
52
+ * alongside `state="error"` makes the empty-error-label state impossible to
53
+ * express, rather than guarding against it at runtime.
54
+ *
55
+ * In the error state the icon becomes `ExclamationMarkIcon` and the icon,
56
+ * title and message all take the error colour.
57
+ *
58
+ * @default state "default"
59
+ */
60
+ type StateProps = {
61
+ state?: "default";
62
+ errorMessage?: never;
63
+ } | {
64
+ state: "error";
65
+ errorMessage: string;
66
+ };
67
+ export type Props = BaseProps & StateProps;
68
+ /**
69
+ * Renders a single instrument belonging to an action.
70
+ *
71
+ * Use `variant="compact"` for the inline pills listed on an `ActionCard`, and
72
+ * `variant="detailed"` for the full rows in `ActionCardDetailed`. Both of
73
+ * those consumers force the variant, so it is not part of the props they
74
+ * accept.
75
+ *
76
+ * @example
77
+ * ```tsx
78
+ * <InstrumentCard type="info" title="How to stop smoking" />
79
+ *
80
+ * <InstrumentCard
81
+ * variant="detailed"
82
+ * type="questionnaire"
83
+ * title="Daily health questionnaire"
84
+ * category="Questionnaire"
85
+ * meta="Comments ON"
86
+ * id="Id-30ry3-7240"
87
+ * count={4}
88
+ * />
89
+ *
90
+ * <InstrumentCard
91
+ * type="other"
92
+ * title="Instrument title"
93
+ * state="error"
94
+ * errorMessage="Instrument missing in protocol"
95
+ * />
96
+ * ```
97
+ */
98
+ export declare const InstrumentCard: FC<Props>;
99
+ export {};
@@ -94,6 +94,8 @@ export { Breadcrumbs, type BreadcrumbProps } from "./components/Breadcrumbs";
94
94
  export { PageHeader, type PageHeaderProps } from "./components/PageHeader";
95
95
  export { Tag } from "./components/Tag/Tag";
96
96
  export { TagGroup } from "./components/Tag/TagGroup";
97
+ export { InstrumentCard, type InstrumentType, type Props as InstrumentCardProps, } from "./components/InstrumentCard/InstrumentCard";
98
+ export { ActionCard, type ActionType, type ActionCardMetadata, type Props as ActionCardProps, } from "./components/ActionCard/ActionCard";
97
99
  export { Textarea, type TextareaProps, type ResizeTypes, } from "./components/Textarea/Textarea";
98
100
  export { TextEditor, type TextEditorTag, } from "./components/TextEditor/TextEditor";
99
101
  export { type TextEditorTranslations } from "./components/TextEditor/textEditorTranslations";
@@ -94,6 +94,8 @@ export { Breadcrumbs, type BreadcrumbProps } from "./components/Breadcrumbs";
94
94
  export { PageHeader, type PageHeaderProps } from "./components/PageHeader";
95
95
  export { Tag } from "./components/Tag/Tag";
96
96
  export { TagGroup } from "./components/Tag/TagGroup";
97
+ export { InstrumentCard, type InstrumentType, type Props as InstrumentCardProps, } from "./components/InstrumentCard/InstrumentCard";
98
+ export { ActionCard, type ActionType, type ActionCardMetadata, type Props as ActionCardProps, } from "./components/ActionCard/ActionCard";
97
99
  export { Textarea, type TextareaProps, type ResizeTypes, } from "./components/Textarea/Textarea";
98
100
  export { TextEditor, type TextEditorTag, } from "./components/TextEditor/TextEditor";
99
101
  export { type TextEditorTranslations } from "./components/TextEditor/textEditorTranslations";
@@ -0,0 +1,51 @@
1
+ import { type StoryObj } from "@storybook/react-vite";
2
+ import { type ActionType } from "../src/components/ActionCard/ActionCard";
3
+ declare const meta: {
4
+ title: string;
5
+ component: import("react").FC<import("../src").ActionCardProps>;
6
+ parameters: {
7
+ backgrounds: {
8
+ default: string;
9
+ };
10
+ };
11
+ argTypes: {
12
+ type: {
13
+ options: ActionType[];
14
+ control: {
15
+ type: "select";
16
+ };
17
+ };
18
+ variant: {
19
+ options: string[];
20
+ control: {
21
+ type: "select";
22
+ };
23
+ };
24
+ draggable: {
25
+ control: {
26
+ type: "boolean";
27
+ };
28
+ };
29
+ actions: {
30
+ control: false;
31
+ };
32
+ metadata: {
33
+ control: {
34
+ type: "object";
35
+ };
36
+ };
37
+ instruments: {
38
+ control: {
39
+ type: "object";
40
+ };
41
+ };
42
+ };
43
+ };
44
+ export default meta;
45
+ type Story = StoryObj<typeof meta>;
46
+ export declare const Base: Story;
47
+ export declare const EveryType: Story;
48
+ export declare const WithAndWithoutActions: Story;
49
+ export declare const DraggableAndStatic: Story;
50
+ export declare const ListVariant: Story;
51
+ export declare const CompactVariant: Story;
@@ -0,0 +1,43 @@
1
+ import { type StoryObj } from "@storybook/react-vite";
2
+ import { type InstrumentType } from "../src/components/InstrumentCard/InstrumentCard";
3
+ declare const meta: {
4
+ title: string;
5
+ component: import("react").FC<import("../src").InstrumentCardProps>;
6
+ parameters: {
7
+ backgrounds: {
8
+ default: string;
9
+ };
10
+ };
11
+ argTypes: {
12
+ type: {
13
+ options: InstrumentType[];
14
+ control: {
15
+ type: "select";
16
+ };
17
+ };
18
+ variant: {
19
+ options: string[];
20
+ control: {
21
+ type: "select";
22
+ };
23
+ };
24
+ state: {
25
+ options: string[];
26
+ control: {
27
+ type: "select";
28
+ };
29
+ };
30
+ count: {
31
+ control: {
32
+ type: "number";
33
+ };
34
+ };
35
+ };
36
+ };
37
+ export default meta;
38
+ type Story = StoryObj<typeof meta>;
39
+ export declare const Base: Story;
40
+ export declare const EveryTypeCompact: Story;
41
+ export declare const EveryTypeDetailed: Story;
42
+ export declare const DetailedOptionalFields: Story;
43
+ export declare const ErrorStates: Story;
@@ -1624,6 +1624,9 @@ button, input:where([type='button'], [type='reset'], [type='submit']), ::file-se
1624
1624
  .ui\:h-\[11px\] {
1625
1625
  height: 11px;
1626
1626
  }
1627
+ .ui\:h-\[12px\] {
1628
+ height: 12px;
1629
+ }
1627
1630
  .ui\:h-\[20px\] {
1628
1631
  height: 20px;
1629
1632
  }
@@ -1966,6 +1969,12 @@ button, input:where([type='button'], [type='reset'], [type='submit']), ::file-se
1966
1969
  .ui\:max-w-\[500px\] {
1967
1970
  max-width: 500px;
1968
1971
  }
1972
+ .ui\:max-w-\[560px\] {
1973
+ max-width: 560px;
1974
+ }
1975
+ .ui\:max-w-\[720px\] {
1976
+ max-width: 720px;
1977
+ }
1969
1978
  .ui\:max-w-\[900px\] {
1970
1979
  max-width: 900px;
1971
1980
  }
@@ -1978,6 +1987,9 @@ button, input:where([type='button'], [type='reset'], [type='submit']), ::file-se
1978
1987
  .ui\:max-w-screen {
1979
1988
  max-width: 100vw;
1980
1989
  }
1990
+ .ui\:min-w-0 {
1991
+ min-width: calc(var(--ui-spacing) * 0);
1992
+ }
1981
1993
  .ui\:min-w-24 {
1982
1994
  min-width: 1.5rem;
1983
1995
  }
@@ -2922,6 +2934,9 @@ button, input:where([type='button'], [type='reset'], [type='submit']), ::file-se
2922
2934
  .ui\:bg-blue-400 {
2923
2935
  background-color: var(--ui-color-blue-400);
2924
2936
  }
2937
+ .ui\:bg-border-neutral-primary-default {
2938
+ background-color: var(--ui-primitive-color-grey-300);
2939
+ }
2925
2940
  .ui\:bg-color-divider {
2926
2941
  background-color: #eeeeee;
2927
2942
  }
@@ -3848,6 +3863,12 @@ button, input:where([type='button'], [type='reset'], [type='submit']), ::file-se
3848
3863
  .ui\:text-green-800 {
3849
3864
  color: var(--ui-color-green-800);
3850
3865
  }
3866
+ .ui\:text-icon-brand-primary-default {
3867
+ color: var(--ui-primitive-color-brand-700);
3868
+ }
3869
+ .ui\:text-icon-error-primary-default {
3870
+ color: var(--ui-primitive-color-red-900);
3871
+ }
3851
3872
  .ui\:text-negative-solid {
3852
3873
  color: var(--ui-color-red-400);
3853
3874
  }
@@ -4805,6 +4826,12 @@ button, input:where([type='button'], [type='reset'], [type='submit']), ::file-se
4805
4826
  position: absolute;
4806
4827
  }
4807
4828
  }
4829
+ .ui\:after\:inset-0 {
4830
+ &::after {
4831
+ content: var(--tw-content);
4832
+ inset: 0px;
4833
+ }
4834
+ }
4808
4835
  .ui\:after\:start-\[2px\] {
4809
4836
  &::after {
4810
4837
  content: var(--tw-content);