@fuego-systems/react 0.1.5 → 0.1.6
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.
- package/dist/HealthcareServiceDirectory/HealthcareServiceDirectory.d.ts +20 -0
- package/dist/HealthcareServiceDirectory/HealthcareServiceDirectory.d.ts.map +1 -0
- package/dist/HealthcareServiceDirectory/HealthcareServiceDirectory.js +100 -0
- package/dist/HealthcareServiceDirectory/HealthcareServiceDirectory.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { HealthcareService } from '@medplum/fhirtypes';
|
|
2
|
+
import type { JSX, ReactNode } from 'react';
|
|
3
|
+
export interface HealthcareServiceDirectoryProps {
|
|
4
|
+
/** Function that fetches all HealthcareService resources to display in the tree. */
|
|
5
|
+
readonly fetchServices: () => Promise<HealthcareService[]>;
|
|
6
|
+
/** Set of currently selected service IDs. */
|
|
7
|
+
readonly selectedIds?: ReadonlySet<string>;
|
|
8
|
+
/** Called when the user clicks on a service to select it. */
|
|
9
|
+
readonly onSelect?: (service: HealthcareService) => void;
|
|
10
|
+
/** Optional custom renderer for each service row. Receives the service and whether it is selected. */
|
|
11
|
+
readonly renderService?: (service: HealthcareService, selected: boolean) => ReactNode;
|
|
12
|
+
/** Icon element for the select button when unselected. Defaults to a "+" SVG. */
|
|
13
|
+
readonly selectIcon?: ReactNode;
|
|
14
|
+
/** Icon element for the select button when selected. Defaults to a check SVG. */
|
|
15
|
+
readonly selectedIcon?: ReactNode;
|
|
16
|
+
/** Content to show at the bottom of the directory (e.g. fallback/contact info). */
|
|
17
|
+
readonly footer?: ReactNode;
|
|
18
|
+
}
|
|
19
|
+
export declare function HealthcareServiceDirectory({ fetchServices, selectedIds, onSelect, renderService, selectIcon, selectedIcon, footer, }: HealthcareServiceDirectoryProps): JSX.Element;
|
|
20
|
+
//# sourceMappingURL=HealthcareServiceDirectory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HealthcareServiceDirectory.d.ts","sourceRoot":"","sources":["../../src/HealthcareServiceDirectory/HealthcareServiceDirectory.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAK3C,MAAM,WAAW,+BAA+B;IAC9C,oFAAoF;IACpF,QAAQ,CAAC,aAAa,EAAE,MAAM,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAA;IAC1D,6CAA6C;IAC7C,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAC1C,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACxD,sGAAsG;IACtG,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,OAAO,KAAK,SAAS,CAAA;IACrF,iFAAiF;IACjF,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,CAAA;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,CAAA;IACjC,mFAAmF;IACnF,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAA;CAC5B;AA0ED,wBAAgB,0BAA0B,CAAC,EACzC,aAAa,EACb,WAAW,EACX,QAAQ,EACR,aAAa,EACb,UAAU,EACV,YAAY,EACZ,MAAM,GACP,EAAE,+BAA+B,GAAG,GAAG,CAAC,OAAO,CAyG/C"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Accordion, ActionIcon, Alert, Group, Loader, Stack, Text, } from '@mantine/core';
|
|
3
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
4
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
5
|
+
function getSpecialty(hs) {
|
|
6
|
+
var _a, _b, _c, _d, _e;
|
|
7
|
+
return (_e = (_d = (_c = (_b = (_a = hs.specialty) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.coding) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.display) !== null && _e !== void 0 ? _e : 'Otros';
|
|
8
|
+
}
|
|
9
|
+
function getCategory(hs) {
|
|
10
|
+
var _a, _b, _c, _d, _e;
|
|
11
|
+
return (_e = (_d = (_c = (_b = (_a = hs.category) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.coding) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.display) !== null && _e !== void 0 ? _e : 'General';
|
|
12
|
+
}
|
|
13
|
+
function buildTree(services) {
|
|
14
|
+
const specialtyMap = new Map();
|
|
15
|
+
for (const hs of services) {
|
|
16
|
+
const specialty = getSpecialty(hs);
|
|
17
|
+
const category = getCategory(hs);
|
|
18
|
+
let catMap = specialtyMap.get(specialty);
|
|
19
|
+
if (!catMap) {
|
|
20
|
+
catMap = new Map();
|
|
21
|
+
specialtyMap.set(specialty, catMap);
|
|
22
|
+
}
|
|
23
|
+
let list = catMap.get(category);
|
|
24
|
+
if (!list) {
|
|
25
|
+
list = [];
|
|
26
|
+
catMap.set(category, list);
|
|
27
|
+
}
|
|
28
|
+
list.push(hs);
|
|
29
|
+
}
|
|
30
|
+
const tree = [];
|
|
31
|
+
for (const [specialty, catMap] of specialtyMap) {
|
|
32
|
+
const children = [];
|
|
33
|
+
for (const [category, svcs] of catMap) {
|
|
34
|
+
children.push({ name: category, services: svcs });
|
|
35
|
+
}
|
|
36
|
+
tree.push({ name: specialty, children });
|
|
37
|
+
}
|
|
38
|
+
return tree;
|
|
39
|
+
}
|
|
40
|
+
function PlusIcon() {
|
|
41
|
+
return (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }), _jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })] }));
|
|
42
|
+
}
|
|
43
|
+
function CheckIcon() {
|
|
44
|
+
return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("polyline", { points: "20 6 9 17 4 12" }) }));
|
|
45
|
+
}
|
|
46
|
+
// ─── Component ───────────────────────────────────────────────────────────────
|
|
47
|
+
export function HealthcareServiceDirectory({ fetchServices, selectedIds, onSelect, renderService, selectIcon, selectedIcon, footer, }) {
|
|
48
|
+
const [services, setServices] = useState([]);
|
|
49
|
+
const [loading, setLoading] = useState(true);
|
|
50
|
+
const [error, setError] = useState();
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
let cancelled = false;
|
|
53
|
+
setLoading(true);
|
|
54
|
+
setError(undefined);
|
|
55
|
+
fetchServices()
|
|
56
|
+
.then((result) => {
|
|
57
|
+
if (!cancelled) {
|
|
58
|
+
setServices(result);
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
.catch((err) => {
|
|
62
|
+
if (!cancelled) {
|
|
63
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
.finally(() => {
|
|
67
|
+
if (!cancelled) {
|
|
68
|
+
setLoading(false);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return () => { cancelled = true; };
|
|
72
|
+
}, [fetchServices]);
|
|
73
|
+
const tree = useMemo(() => buildTree(services), [services]);
|
|
74
|
+
const handleSelect = useCallback((hs) => {
|
|
75
|
+
var _a;
|
|
76
|
+
if (selectedIds === null || selectedIds === void 0 ? void 0 : selectedIds.has((_a = hs.id) !== null && _a !== void 0 ? _a : '')) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
onSelect === null || onSelect === void 0 ? void 0 : onSelect(hs);
|
|
80
|
+
}, [selectedIds, onSelect]);
|
|
81
|
+
if (loading) {
|
|
82
|
+
return (_jsx(Stack, { align: "center", py: "xl", children: _jsx(Loader, { size: "md" }) }));
|
|
83
|
+
}
|
|
84
|
+
if (error) {
|
|
85
|
+
return (_jsx(Alert, { color: "red", title: "Error", children: error }));
|
|
86
|
+
}
|
|
87
|
+
return (_jsxs(Stack, { gap: "md", children: [_jsx(Accordion, { variant: "separated", children: tree.map((specialty) => (_jsxs(Accordion.Item, { value: specialty.name, children: [_jsx(Accordion.Control, { children: _jsx(Text, { fw: 500, children: specialty.name }) }), _jsx(Accordion.Panel, { children: specialty.children.length === 1 ? (_jsx(ServiceList, { services: specialty.children[0].services, selectedIds: selectedIds, onSelect: handleSelect, renderService: renderService, selectIcon: selectIcon, selectedIcon: selectedIcon })) : (_jsx(Accordion, { variant: "default", children: specialty.children.map((sub) => (_jsxs(Accordion.Item, { value: sub.name, children: [_jsx(Accordion.Control, { children: _jsx(Text, { size: "sm", children: sub.name }) }), _jsx(Accordion.Panel, { children: _jsx(ServiceList, { services: sub.services, selectedIds: selectedIds, onSelect: handleSelect, renderService: renderService, selectIcon: selectIcon, selectedIcon: selectedIcon }) })] }, sub.name))) })) })] }, specialty.name))) }), footer] }));
|
|
88
|
+
}
|
|
89
|
+
// ─── ServiceList ─────────────────────────────────────────────────────────────
|
|
90
|
+
function ServiceList({ services, selectedIds, onSelect, renderService, selectIcon, selectedIcon, }) {
|
|
91
|
+
return (_jsx(Stack, { gap: "xs", children: services.map((hs) => {
|
|
92
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
93
|
+
const id = (_a = hs.id) !== null && _a !== void 0 ? _a : '';
|
|
94
|
+
const selected = (_b = selectedIds === null || selectedIds === void 0 ? void 0 : selectedIds.has(id)) !== null && _b !== void 0 ? _b : false;
|
|
95
|
+
return (_jsxs(Group, { justify: "space-between", wrap: "nowrap", py: 4, children: [renderService ? (renderService(hs, selected)) : (_jsxs(Stack, { gap: 0, children: [_jsx(Text, { size: "sm", children: (_c = hs.name) !== null && _c !== void 0 ? _c : id }), ((_g = (_f = (_e = (_d = hs.category) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.coding) === null || _f === void 0 ? void 0 : _f[0]) === null || _g === void 0 ? void 0 : _g.display) && (_jsx(Text, { size: "xs", c: "dimmed", children: hs.category[0].coding[0].display }))] })), _jsx(ActionIcon, { variant: selected ? 'filled' : 'light', color: selected ? 'green' : 'blue', onClick: () => onSelect(hs), disabled: selected, children: selected
|
|
96
|
+
? (selectedIcon !== null && selectedIcon !== void 0 ? selectedIcon : _jsx(CheckIcon, {}))
|
|
97
|
+
: (selectIcon !== null && selectIcon !== void 0 ? selectIcon : _jsx(PlusIcon, {})) })] }, id));
|
|
98
|
+
}) }));
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=HealthcareServiceDirectory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HealthcareServiceDirectory.js","sourceRoot":"","sources":["../../src/HealthcareServiceDirectory/HealthcareServiceDirectory.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n Accordion,\n ActionIcon,\n Alert,\n Group,\n Loader,\n Stack,\n Text,\n} from '@mantine/core'\nimport type { HealthcareService } from '@medplum/fhirtypes'\nimport type { JSX, ReactNode } from 'react'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface HealthcareServiceDirectoryProps {\n /** Function that fetches all HealthcareService resources to display in the tree. */\n readonly fetchServices: () => Promise<HealthcareService[]>\n /** Set of currently selected service IDs. */\n readonly selectedIds?: ReadonlySet<string>\n /** Called when the user clicks on a service to select it. */\n readonly onSelect?: (service: HealthcareService) => void\n /** Optional custom renderer for each service row. Receives the service and whether it is selected. */\n readonly renderService?: (service: HealthcareService, selected: boolean) => ReactNode\n /** Icon element for the select button when unselected. Defaults to a \"+\" SVG. */\n readonly selectIcon?: ReactNode\n /** Icon element for the select button when selected. Defaults to a check SVG. */\n readonly selectedIcon?: ReactNode\n /** Content to show at the bottom of the directory (e.g. fallback/contact info). */\n readonly footer?: ReactNode\n}\n\ninterface TreeCategory {\n name: string\n children: TreeSubcategory[]\n}\n\ninterface TreeSubcategory {\n name: string\n services: HealthcareService[]\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction getSpecialty(hs: HealthcareService): string {\n return hs.specialty?.[0]?.coding?.[0]?.display ?? 'Otros'\n}\n\nfunction getCategory(hs: HealthcareService): string {\n return hs.category?.[0]?.coding?.[0]?.display ?? 'General'\n}\n\nfunction buildTree(services: HealthcareService[]): TreeCategory[] {\n const specialtyMap = new Map<string, Map<string, HealthcareService[]>>()\n\n for (const hs of services) {\n const specialty = getSpecialty(hs)\n const category = getCategory(hs)\n\n let catMap = specialtyMap.get(specialty)\n if (!catMap) {\n catMap = new Map()\n specialtyMap.set(specialty, catMap)\n }\n\n let list = catMap.get(category)\n if (!list) {\n list = []\n catMap.set(category, list)\n }\n list.push(hs)\n }\n\n const tree: TreeCategory[] = []\n for (const [specialty, catMap] of specialtyMap) {\n const children: TreeSubcategory[] = []\n for (const [category, svcs] of catMap) {\n children.push({ name: category, services: svcs })\n }\n tree.push({ name: specialty, children })\n }\n\n return tree\n}\n\nfunction PlusIcon(): JSX.Element {\n return (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={16} height={16} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n )\n}\n\nfunction CheckIcon(): JSX.Element {\n return (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={16} height={16} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n )\n}\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nexport function HealthcareServiceDirectory({\n fetchServices,\n selectedIds,\n onSelect,\n renderService,\n selectIcon,\n selectedIcon,\n footer,\n}: HealthcareServiceDirectoryProps): JSX.Element {\n const [services, setServices] = useState<HealthcareService[]>([])\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<string>()\n\n useEffect(() => {\n let cancelled = false\n setLoading(true)\n setError(undefined)\n\n fetchServices()\n .then((result) => {\n if (!cancelled) {\n setServices(result)\n }\n })\n .catch((err) => {\n if (!cancelled) {\n setError(err instanceof Error ? err.message : String(err))\n }\n })\n .finally(() => {\n if (!cancelled) {\n setLoading(false)\n }\n })\n\n return () => { cancelled = true }\n }, [fetchServices])\n\n const tree = useMemo(() => buildTree(services), [services])\n\n const handleSelect = useCallback(\n (hs: HealthcareService) => {\n if (selectedIds?.has(hs.id ?? '')) {\n return\n }\n onSelect?.(hs)\n },\n [selectedIds, onSelect]\n )\n\n if (loading) {\n return (\n <Stack align=\"center\" py=\"xl\">\n <Loader size=\"md\" />\n </Stack>\n )\n }\n\n if (error) {\n return (\n <Alert color=\"red\" title=\"Error\">\n {error}\n </Alert>\n )\n }\n\n return (\n <Stack gap=\"md\">\n <Accordion variant=\"separated\">\n {tree.map((specialty) => (\n <Accordion.Item key={specialty.name} value={specialty.name}>\n <Accordion.Control>\n <Text fw={500}>{specialty.name}</Text>\n </Accordion.Control>\n <Accordion.Panel>\n {specialty.children.length === 1 ? (\n <ServiceList\n services={specialty.children[0].services}\n selectedIds={selectedIds}\n onSelect={handleSelect}\n renderService={renderService}\n selectIcon={selectIcon}\n selectedIcon={selectedIcon}\n />\n ) : (\n <Accordion variant=\"default\">\n {specialty.children.map((sub) => (\n <Accordion.Item key={sub.name} value={sub.name}>\n <Accordion.Control>\n <Text size=\"sm\">{sub.name}</Text>\n </Accordion.Control>\n <Accordion.Panel>\n <ServiceList\n services={sub.services}\n selectedIds={selectedIds}\n onSelect={handleSelect}\n renderService={renderService}\n selectIcon={selectIcon}\n selectedIcon={selectedIcon}\n />\n </Accordion.Panel>\n </Accordion.Item>\n ))}\n </Accordion>\n )}\n </Accordion.Panel>\n </Accordion.Item>\n ))}\n </Accordion>\n\n {footer}\n </Stack>\n )\n}\n\n// ─── ServiceList ─────────────────────────────────────────────────────────────\n\nfunction ServiceList({\n services,\n selectedIds,\n onSelect,\n renderService,\n selectIcon,\n selectedIcon,\n}: {\n services: HealthcareService[]\n selectedIds?: ReadonlySet<string>\n onSelect: (hs: HealthcareService) => void\n renderService?: (hs: HealthcareService, selected: boolean) => ReactNode\n selectIcon?: ReactNode\n selectedIcon?: ReactNode\n}): JSX.Element {\n return (\n <Stack gap=\"xs\">\n {services.map((hs) => {\n const id = hs.id ?? ''\n const selected = selectedIds?.has(id) ?? false\n return (\n <Group key={id} justify=\"space-between\" wrap=\"nowrap\" py={4}>\n {renderService ? (\n renderService(hs, selected)\n ) : (\n <Stack gap={0}>\n <Text size=\"sm\">{hs.name ?? id}</Text>\n {hs.category?.[0]?.coding?.[0]?.display && (\n <Text size=\"xs\" c=\"dimmed\">\n {hs.category[0].coding[0].display}\n </Text>\n )}\n </Stack>\n )}\n <ActionIcon\n variant={selected ? 'filled' : 'light'}\n color={selected ? 'green' : 'blue'}\n onClick={() => onSelect(hs)}\n disabled={selected}\n >\n {selected\n ? (selectedIcon ?? <CheckIcon />)\n : (selectIcon ?? <PlusIcon />)}\n </ActionIcon>\n </Group>\n )\n })}\n </Stack>\n )\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { HealthcareServiceCard } from './HealthcareServiceCard/HealthcareServiceCard';
|
|
2
2
|
export type { HealthcareServiceCardProps, HealthcareServiceAction } from './HealthcareServiceCard/HealthcareServiceCard';
|
|
3
|
+
export { HealthcareServiceDirectory } from './HealthcareServiceDirectory/HealthcareServiceDirectory';
|
|
4
|
+
export type { HealthcareServiceDirectoryProps } from './HealthcareServiceDirectory/HealthcareServiceDirectory';
|
|
3
5
|
export { FuegoLogo } from './Logo/FuegoLogo';
|
|
4
6
|
export { RossiLogo } from './Logo/RossiLogo';
|
|
5
7
|
export { ServiceAutocomplete } from './search/ServiceAutocomplete';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,+CAA+C,CAAA;AACrF,YAAY,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAA;AACxH,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAA;AAClE,YAAY,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAA;AAE5E,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAC7D,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,+CAA+C,CAAA;AACrF,YAAY,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAA;AACxH,OAAO,EAAE,0BAA0B,EAAE,MAAM,yDAAyD,CAAA;AACpG,YAAY,EAAE,+BAA+B,EAAE,MAAM,yDAAyD,CAAA;AAC9G,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAA;AAClE,YAAY,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAA;AAE5E,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAC7D,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { HealthcareServiceCard } from './HealthcareServiceCard/HealthcareServiceCard';
|
|
2
|
+
export { HealthcareServiceDirectory } from './HealthcareServiceDirectory/HealthcareServiceDirectory';
|
|
2
3
|
export { FuegoLogo } from './Logo/FuegoLogo';
|
|
3
4
|
export { RossiLogo } from './Logo/RossiLogo';
|
|
4
5
|
export { ServiceAutocomplete } from './search/ServiceAutocomplete';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,+CAA+C,CAAA;AAErF,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAA;AAGlE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA","sourcesContent":["export { HealthcareServiceCard } from './HealthcareServiceCard/HealthcareServiceCard'\nexport type { HealthcareServiceCardProps, HealthcareServiceAction } from './HealthcareServiceCard/HealthcareServiceCard'\nexport { FuegoLogo } from './Logo/FuegoLogo'\nexport { RossiLogo } from './Logo/RossiLogo'\nexport { ServiceAutocomplete } from './search/ServiceAutocomplete'\nexport type { ServiceAutocompleteProps } from './search/ServiceAutocomplete'\n\nexport { OtpInput } from './OtpInput/OtpInput'\nexport type { OtpInputProps } from './OtpInput/OtpInput'\nexport { WizardStepper } from './WizardStepper/WizardStepper'\nexport type { WizardStepperProps, WizardStep } from './WizardStepper/WizardStepper'\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,+CAA+C,CAAA;AAErF,OAAO,EAAE,0BAA0B,EAAE,MAAM,yDAAyD,CAAA;AAEpG,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAA;AAGlE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA","sourcesContent":["export { HealthcareServiceCard } from './HealthcareServiceCard/HealthcareServiceCard'\nexport type { HealthcareServiceCardProps, HealthcareServiceAction } from './HealthcareServiceCard/HealthcareServiceCard'\nexport { HealthcareServiceDirectory } from './HealthcareServiceDirectory/HealthcareServiceDirectory'\nexport type { HealthcareServiceDirectoryProps } from './HealthcareServiceDirectory/HealthcareServiceDirectory'\nexport { FuegoLogo } from './Logo/FuegoLogo'\nexport { RossiLogo } from './Logo/RossiLogo'\nexport { ServiceAutocomplete } from './search/ServiceAutocomplete'\nexport type { ServiceAutocompleteProps } from './search/ServiceAutocomplete'\n\nexport { OtpInput } from './OtpInput/OtpInput'\nexport type { OtpInputProps } from './OtpInput/OtpInput'\nexport { WizardStepper } from './WizardStepper/WizardStepper'\nexport type { WizardStepperProps, WizardStep } from './WizardStepper/WizardStepper'\n"]}
|