@pfm-platform/tags-ui-mui 0.1.1

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/index.cjs ADDED
@@ -0,0 +1,234 @@
1
+ 'use strict';
2
+
3
+ var material = require('@mui/material');
4
+ var tagsFeature = require('@pfm-platform/tags-feature');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var react = require('react');
7
+ var tagsDataAccess = require('@pfm-platform/tags-data-access');
8
+ var DeleteIcon = require('@mui/icons-material/Delete');
9
+
10
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
+
12
+ var DeleteIcon__default = /*#__PURE__*/_interopDefault(DeleteIcon);
13
+
14
+ // src/components/TagList.tsx
15
+ function TagList({
16
+ userId,
17
+ category = "all",
18
+ onTagClick,
19
+ title
20
+ }) {
21
+ const { allTags, defaultTags, customTags } = tagsFeature.useTagFilters(userId);
22
+ const tags = category === "default" ? defaultTags : category === "custom" ? customTags : allTags;
23
+ if (tags.length === 0) {
24
+ return /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { children: [
25
+ title && /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { variant: "body2", color: "text.secondary", gutterBottom: true, children: title }),
26
+ /* @__PURE__ */ jsxRuntime.jsxs(material.Typography, { variant: "body2", color: "text.secondary", children: [
27
+ "No ",
28
+ category !== "all" ? category : "",
29
+ " tags available"
30
+ ] })
31
+ ] });
32
+ }
33
+ return /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { children: [
34
+ title && /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { variant: "body2", color: "text.secondary", gutterBottom: true, children: title }),
35
+ /* @__PURE__ */ jsxRuntime.jsx(material.Box, { sx: { display: "flex", gap: 1, flexWrap: "wrap" }, children: tags.map((tag) => /* @__PURE__ */ jsxRuntime.jsx(
36
+ material.Chip,
37
+ {
38
+ label: tag,
39
+ size: "small",
40
+ onClick: onTagClick ? () => onTagClick(tag) : void 0,
41
+ clickable: !!onTagClick,
42
+ variant: category === "custom" ? "outlined" : "filled"
43
+ },
44
+ tag
45
+ )) })
46
+ ] });
47
+ }
48
+ function TagSelector({
49
+ userId,
50
+ selectedTags,
51
+ onSelectionChange,
52
+ category = "all",
53
+ title,
54
+ multiple = true
55
+ }) {
56
+ const { allTags, defaultTags, customTags } = tagsFeature.useTagFilters(userId);
57
+ const availableTags = category === "default" ? defaultTags : category === "custom" ? customTags : allTags;
58
+ const handleTagClick = (tag) => {
59
+ const isSelected = selectedTags.includes(tag);
60
+ if (multiple) {
61
+ const newSelection = isSelected ? selectedTags.filter((t) => t !== tag) : [...selectedTags, tag];
62
+ onSelectionChange(newSelection);
63
+ } else {
64
+ const newSelection = isSelected ? [] : [tag];
65
+ onSelectionChange(newSelection);
66
+ }
67
+ };
68
+ if (availableTags.length === 0) {
69
+ return /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { children: [
70
+ title && /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { variant: "body2", color: "text.secondary", gutterBottom: true, children: title }),
71
+ /* @__PURE__ */ jsxRuntime.jsxs(material.Typography, { variant: "body2", color: "text.secondary", children: [
72
+ "No ",
73
+ category !== "all" ? category : "",
74
+ " tags available"
75
+ ] })
76
+ ] });
77
+ }
78
+ return /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { children: [
79
+ title && /* @__PURE__ */ jsxRuntime.jsx(material.Typography, { variant: "body2", color: "text.secondary", gutterBottom: true, children: title }),
80
+ /* @__PURE__ */ jsxRuntime.jsx(material.Box, { sx: { display: "flex", gap: 1, flexWrap: "wrap" }, children: availableTags.map((tag) => {
81
+ const isSelected = selectedTags.includes(tag);
82
+ return /* @__PURE__ */ jsxRuntime.jsx(
83
+ material.Chip,
84
+ {
85
+ label: tag,
86
+ size: "small",
87
+ onClick: () => handleTagClick(tag),
88
+ clickable: true,
89
+ color: isSelected ? "primary" : "default",
90
+ variant: isSelected ? "filled" : "outlined"
91
+ },
92
+ tag
93
+ );
94
+ }) })
95
+ ] });
96
+ }
97
+ function TagCreateForm({
98
+ userId,
99
+ onSuccess
100
+ }) {
101
+ const [tagName, setTagName] = react.useState("");
102
+ const [error, setError] = react.useState(null);
103
+ const createTag = tagsDataAccess.useCreateTag({
104
+ onSuccess: () => {
105
+ setTagName("");
106
+ setError(null);
107
+ onSuccess?.();
108
+ },
109
+ onError: (err) => {
110
+ setError(err instanceof Error ? err.message : "Failed to create tag");
111
+ }
112
+ });
113
+ const handleSubmit = (e) => {
114
+ e.preventDefault();
115
+ if (!tagName.trim()) {
116
+ setError("Tag name is required");
117
+ return;
118
+ }
119
+ const data = {
120
+ name: tagName.trim()
121
+ };
122
+ createTag.mutate({ userId, data });
123
+ };
124
+ return /* @__PURE__ */ jsxRuntime.jsxs(
125
+ material.Box,
126
+ {
127
+ component: "form",
128
+ onSubmit: handleSubmit,
129
+ sx: { display: "flex", flexDirection: "column", gap: 2 },
130
+ children: [
131
+ /* @__PURE__ */ jsxRuntime.jsx(
132
+ material.TextField,
133
+ {
134
+ label: "Tag Name",
135
+ value: tagName,
136
+ onChange: (e) => {
137
+ setTagName(e.target.value);
138
+ setError(null);
139
+ },
140
+ required: true,
141
+ fullWidth: true,
142
+ disabled: createTag.isPending,
143
+ error: !!error,
144
+ helperText: error || "Enter a name for your new tag",
145
+ inputProps: {
146
+ maxLength: 50
147
+ }
148
+ }
149
+ ),
150
+ error && /* @__PURE__ */ jsxRuntime.jsx(material.Alert, { severity: "error", onClose: () => setError(null), children: error }),
151
+ /* @__PURE__ */ jsxRuntime.jsx(material.Box, { sx: { display: "flex", gap: 1 }, children: /* @__PURE__ */ jsxRuntime.jsx(
152
+ material.Button,
153
+ {
154
+ type: "submit",
155
+ variant: "contained",
156
+ disabled: createTag.isPending || !tagName.trim(),
157
+ startIcon: createTag.isPending ? /* @__PURE__ */ jsxRuntime.jsx(material.CircularProgress, { size: 20 }) : null,
158
+ children: createTag.isPending ? "Creating..." : "Create Tag"
159
+ }
160
+ ) })
161
+ ]
162
+ }
163
+ );
164
+ }
165
+ function TagDeleteButton({
166
+ userId,
167
+ tagName
168
+ }) {
169
+ const [open, setOpen] = react.useState(false);
170
+ const deleteTag = tagsDataAccess.useDeleteTag({
171
+ onSuccess: () => {
172
+ setOpen(false);
173
+ }
174
+ });
175
+ const handleDelete = () => {
176
+ deleteTag.mutate({ userId, tagName });
177
+ };
178
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
179
+ /* @__PURE__ */ jsxRuntime.jsx(material.Tooltip, { title: "Delete tag", children: /* @__PURE__ */ jsxRuntime.jsx(
180
+ material.IconButton,
181
+ {
182
+ onClick: () => setOpen(true),
183
+ color: "error",
184
+ size: "small",
185
+ "aria-label": `delete tag ${tagName}`,
186
+ children: /* @__PURE__ */ jsxRuntime.jsx(DeleteIcon__default.default, {})
187
+ }
188
+ ) }),
189
+ /* @__PURE__ */ jsxRuntime.jsxs(
190
+ material.Dialog,
191
+ {
192
+ open,
193
+ onClose: () => !deleteTag.isPending && setOpen(false),
194
+ "aria-labelledby": "delete-tag-dialog-title",
195
+ children: [
196
+ /* @__PURE__ */ jsxRuntime.jsx(material.DialogTitle, { id: "delete-tag-dialog-title", children: "Delete Tag?" }),
197
+ /* @__PURE__ */ jsxRuntime.jsx(material.DialogContent, { children: /* @__PURE__ */ jsxRuntime.jsxs(material.DialogContentText, { children: [
198
+ 'Are you sure you want to delete the tag "',
199
+ tagName,
200
+ '"? This action cannot be undone.'
201
+ ] }) }),
202
+ /* @__PURE__ */ jsxRuntime.jsxs(material.DialogActions, { children: [
203
+ /* @__PURE__ */ jsxRuntime.jsx(
204
+ material.Button,
205
+ {
206
+ onClick: () => setOpen(false),
207
+ disabled: deleteTag.isPending,
208
+ children: "Cancel"
209
+ }
210
+ ),
211
+ /* @__PURE__ */ jsxRuntime.jsx(
212
+ material.Button,
213
+ {
214
+ onClick: handleDelete,
215
+ color: "error",
216
+ variant: "contained",
217
+ disabled: deleteTag.isPending,
218
+ autoFocus: true,
219
+ children: deleteTag.isPending ? "Deleting..." : "Delete"
220
+ }
221
+ )
222
+ ] })
223
+ ]
224
+ }
225
+ )
226
+ ] });
227
+ }
228
+
229
+ exports.TagCreateForm = TagCreateForm;
230
+ exports.TagDeleteButton = TagDeleteButton;
231
+ exports.TagList = TagList;
232
+ exports.TagSelector = TagSelector;
233
+ //# sourceMappingURL=index.cjs.map
234
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/TagList.tsx","../src/components/TagSelector.tsx","../src/components/TagCreateForm.tsx","../src/components/TagDeleteButton.tsx"],"names":["useTagFilters","Box","jsx","Typography","jsxs","Chip","useState","useCreateTag","TextField","Alert","Button","CircularProgress","useDeleteTag","Fragment","Tooltip","IconButton","DeleteIcon","Dialog","DialogTitle","DialogContent","DialogContentText","DialogActions"],"mappings":"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","file":"index.cjs","sourcesContent":["import { Box, Chip, Typography } from '@mui/material';\nimport { useTagFilters } from '@pfm-platform/tags-feature';\n\nexport interface TagListProps {\n /**\n * User ID to fetch tags for\n */\n userId: string;\n\n /**\n * Category of tags to display\n * @default \"all\"\n */\n category?: 'all' | 'default' | 'custom';\n\n /**\n * Optional callback when a tag is clicked\n */\n onTagClick?: (tag: string) => void;\n\n /**\n * Optional title for the tag list\n */\n title?: string;\n}\n\n/**\n * Display a list of tags as chips\n *\n * Shows:\n * - All tags, default tags, or custom tags based on category prop\n * - Clickable chips if onTagClick provided\n * - Empty state when no tags available\n *\n * Uses useTagFilters hook for business logic\n */\nexport function TagList({\n userId,\n category = 'all',\n onTagClick,\n title,\n}: TagListProps) {\n const { allTags, defaultTags, customTags } = useTagFilters(userId);\n\n const tags =\n category === 'default'\n ? defaultTags\n : category === 'custom'\n ? customTags\n : allTags;\n\n if (tags.length === 0) {\n return (\n <Box>\n {title && (\n <Typography variant=\"body2\" color=\"text.secondary\" gutterBottom>\n {title}\n </Typography>\n )}\n <Typography variant=\"body2\" color=\"text.secondary\">\n No {category !== 'all' ? category : ''} tags available\n </Typography>\n </Box>\n );\n }\n\n return (\n <Box>\n {title && (\n <Typography variant=\"body2\" color=\"text.secondary\" gutterBottom>\n {title}\n </Typography>\n )}\n <Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>\n {tags.map((tag) => (\n <Chip\n key={tag}\n label={tag}\n size=\"small\"\n onClick={onTagClick ? () => onTagClick(tag) : undefined}\n clickable={!!onTagClick}\n variant={category === 'custom' ? 'outlined' : 'filled'}\n />\n ))}\n </Box>\n </Box>\n );\n}\n","import { Box, Chip, Typography } from '@mui/material';\nimport { useTagFilters } from '@pfm-platform/tags-feature';\n\nexport interface TagSelectorProps {\n /**\n * User ID to fetch tags for\n */\n userId: string;\n\n /**\n * Currently selected tags\n */\n selectedTags: string[];\n\n /**\n * Callback when tag selection changes\n */\n onSelectionChange: (tags: string[]) => void;\n\n /**\n * Category of tags to show for selection\n * @default \"all\"\n */\n category?: 'all' | 'default' | 'custom';\n\n /**\n * Optional title for the selector\n */\n title?: string;\n\n /**\n * Allow multiple selection\n * @default true\n */\n multiple?: boolean;\n}\n\n/**\n * Tag selector with toggle selection\n *\n * Shows:\n * - Available tags as clickable chips\n * - Visual distinction for selected vs unselected tags\n * - Support for single or multiple selection\n *\n * Uses useTagFilters hook for business logic\n */\nexport function TagSelector({\n userId,\n selectedTags,\n onSelectionChange,\n category = 'all',\n title,\n multiple = true,\n}: TagSelectorProps) {\n const { allTags, defaultTags, customTags } = useTagFilters(userId);\n\n const availableTags =\n category === 'default'\n ? defaultTags\n : category === 'custom'\n ? customTags\n : allTags;\n\n const handleTagClick = (tag: string) => {\n const isSelected = selectedTags.includes(tag);\n\n if (multiple) {\n // Multiple selection: toggle tag\n const newSelection = isSelected\n ? selectedTags.filter((t) => t !== tag)\n : [...selectedTags, tag];\n onSelectionChange(newSelection);\n } else {\n // Single selection: select tag or deselect if clicking same tag\n const newSelection = isSelected ? [] : [tag];\n onSelectionChange(newSelection);\n }\n };\n\n if (availableTags.length === 0) {\n return (\n <Box>\n {title && (\n <Typography variant=\"body2\" color=\"text.secondary\" gutterBottom>\n {title}\n </Typography>\n )}\n <Typography variant=\"body2\" color=\"text.secondary\">\n No {category !== 'all' ? category : ''} tags available\n </Typography>\n </Box>\n );\n }\n\n return (\n <Box>\n {title && (\n <Typography variant=\"body2\" color=\"text.secondary\" gutterBottom>\n {title}\n </Typography>\n )}\n <Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>\n {availableTags.map((tag) => {\n const isSelected = selectedTags.includes(tag);\n return (\n <Chip\n key={tag}\n label={tag}\n size=\"small\"\n onClick={() => handleTagClick(tag)}\n clickable\n color={isSelected ? 'primary' : 'default'}\n variant={isSelected ? 'filled' : 'outlined'}\n />\n );\n })}\n </Box>\n </Box>\n );\n}\n","import { useState } from 'react';\nimport {\n Box,\n Button,\n TextField,\n Alert,\n CircularProgress,\n} from '@mui/material';\nimport { useCreateTag } from '@pfm-platform/tags-data-access';\nimport type { TagCreate } from '@pfm-platform/tags-data-access';\n\nexport interface TagCreateFormProps {\n /** User ID for tag creation */\n userId: string;\n /** Callback fired when tag is successfully created */\n onSuccess?: () => void;\n}\n\n/**\n * TagCreateForm component for creating new tags.\n * Simple form with text input and submit button.\n */\nexport function TagCreateForm({\n userId,\n onSuccess,\n}: TagCreateFormProps) {\n const [tagName, setTagName] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const createTag = useCreateTag({\n onSuccess: () => {\n setTagName('');\n setError(null);\n onSuccess?.();\n },\n onError: (err) => {\n setError(err instanceof Error ? err.message : 'Failed to create tag');\n },\n });\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!tagName.trim()) {\n setError('Tag name is required');\n return;\n }\n\n const data: TagCreate = {\n name: tagName.trim(),\n };\n\n createTag.mutate({ userId, data });\n };\n\n return (\n <Box\n component=\"form\"\n onSubmit={handleSubmit}\n sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}\n >\n <TextField\n label=\"Tag Name\"\n value={tagName}\n onChange={(e) => {\n setTagName(e.target.value);\n setError(null);\n }}\n required\n fullWidth\n disabled={createTag.isPending}\n error={!!error}\n helperText={error || 'Enter a name for your new tag'}\n inputProps={{\n maxLength: 50,\n }}\n />\n\n {error && (\n <Alert severity=\"error\" onClose={() => setError(null)}>\n {error}\n </Alert>\n )}\n\n <Box sx={{ display: 'flex', gap: 1 }}>\n <Button\n type=\"submit\"\n variant=\"contained\"\n disabled={createTag.isPending || !tagName.trim()}\n startIcon={createTag.isPending ? <CircularProgress size={20} /> : null}\n >\n {createTag.isPending ? 'Creating...' : 'Create Tag'}\n </Button>\n </Box>\n </Box>\n );\n}\n","import { useState } from 'react';\nimport {\n IconButton,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogContentText,\n DialogActions,\n Button,\n Tooltip,\n} from '@mui/material';\nimport DeleteIcon from '@mui/icons-material/Delete';\nimport { useDeleteTag } from '@pfm-platform/tags-data-access';\n\nexport interface TagDeleteButtonProps {\n /** User ID for tag deletion */\n userId: string;\n /** Name of the tag to delete */\n tagName: string;\n}\n\n/**\n * TagDeleteButton component with confirmation dialog.\n * Simple delete button that confirms before removing the tag.\n */\nexport function TagDeleteButton({\n userId,\n tagName,\n}: TagDeleteButtonProps) {\n const [open, setOpen] = useState(false);\n\n const deleteTag = useDeleteTag({\n onSuccess: () => {\n setOpen(false);\n },\n });\n\n const handleDelete = () => {\n deleteTag.mutate({ userId, tagName });\n };\n\n return (\n <>\n <Tooltip title=\"Delete tag\">\n <IconButton\n onClick={() => setOpen(true)}\n color=\"error\"\n size=\"small\"\n aria-label={`delete tag ${tagName}`}\n >\n <DeleteIcon />\n </IconButton>\n </Tooltip>\n\n <Dialog\n open={open}\n onClose={() => !deleteTag.isPending && setOpen(false)}\n aria-labelledby=\"delete-tag-dialog-title\"\n >\n <DialogTitle id=\"delete-tag-dialog-title\">Delete Tag?</DialogTitle>\n <DialogContent>\n <DialogContentText>\n Are you sure you want to delete the tag \"{tagName}\"? This action cannot be undone.\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button\n onClick={() => setOpen(false)}\n disabled={deleteTag.isPending}\n >\n Cancel\n </Button>\n <Button\n onClick={handleDelete}\n color=\"error\"\n variant=\"contained\"\n disabled={deleteTag.isPending}\n autoFocus\n >\n {deleteTag.isPending ? 'Deleting...' : 'Delete'}\n </Button>\n </DialogActions>\n </Dialog>\n </>\n );\n}\n"]}
@@ -0,0 +1,98 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface TagListProps {
4
+ /**
5
+ * User ID to fetch tags for
6
+ */
7
+ userId: string;
8
+ /**
9
+ * Category of tags to display
10
+ * @default "all"
11
+ */
12
+ category?: 'all' | 'default' | 'custom';
13
+ /**
14
+ * Optional callback when a tag is clicked
15
+ */
16
+ onTagClick?: (tag: string) => void;
17
+ /**
18
+ * Optional title for the tag list
19
+ */
20
+ title?: string;
21
+ }
22
+ /**
23
+ * Display a list of tags as chips
24
+ *
25
+ * Shows:
26
+ * - All tags, default tags, or custom tags based on category prop
27
+ * - Clickable chips if onTagClick provided
28
+ * - Empty state when no tags available
29
+ *
30
+ * Uses useTagFilters hook for business logic
31
+ */
32
+ declare function TagList({ userId, category, onTagClick, title, }: TagListProps): react_jsx_runtime.JSX.Element;
33
+
34
+ interface TagSelectorProps {
35
+ /**
36
+ * User ID to fetch tags for
37
+ */
38
+ userId: string;
39
+ /**
40
+ * Currently selected tags
41
+ */
42
+ selectedTags: string[];
43
+ /**
44
+ * Callback when tag selection changes
45
+ */
46
+ onSelectionChange: (tags: string[]) => void;
47
+ /**
48
+ * Category of tags to show for selection
49
+ * @default "all"
50
+ */
51
+ category?: 'all' | 'default' | 'custom';
52
+ /**
53
+ * Optional title for the selector
54
+ */
55
+ title?: string;
56
+ /**
57
+ * Allow multiple selection
58
+ * @default true
59
+ */
60
+ multiple?: boolean;
61
+ }
62
+ /**
63
+ * Tag selector with toggle selection
64
+ *
65
+ * Shows:
66
+ * - Available tags as clickable chips
67
+ * - Visual distinction for selected vs unselected tags
68
+ * - Support for single or multiple selection
69
+ *
70
+ * Uses useTagFilters hook for business logic
71
+ */
72
+ declare function TagSelector({ userId, selectedTags, onSelectionChange, category, title, multiple, }: TagSelectorProps): react_jsx_runtime.JSX.Element;
73
+
74
+ interface TagCreateFormProps {
75
+ /** User ID for tag creation */
76
+ userId: string;
77
+ /** Callback fired when tag is successfully created */
78
+ onSuccess?: () => void;
79
+ }
80
+ /**
81
+ * TagCreateForm component for creating new tags.
82
+ * Simple form with text input and submit button.
83
+ */
84
+ declare function TagCreateForm({ userId, onSuccess, }: TagCreateFormProps): react_jsx_runtime.JSX.Element;
85
+
86
+ interface TagDeleteButtonProps {
87
+ /** User ID for tag deletion */
88
+ userId: string;
89
+ /** Name of the tag to delete */
90
+ tagName: string;
91
+ }
92
+ /**
93
+ * TagDeleteButton component with confirmation dialog.
94
+ * Simple delete button that confirms before removing the tag.
95
+ */
96
+ declare function TagDeleteButton({ userId, tagName, }: TagDeleteButtonProps): react_jsx_runtime.JSX.Element;
97
+
98
+ export { TagCreateForm, type TagCreateFormProps, TagDeleteButton, type TagDeleteButtonProps, TagList, type TagListProps, TagSelector, type TagSelectorProps };
@@ -0,0 +1,98 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface TagListProps {
4
+ /**
5
+ * User ID to fetch tags for
6
+ */
7
+ userId: string;
8
+ /**
9
+ * Category of tags to display
10
+ * @default "all"
11
+ */
12
+ category?: 'all' | 'default' | 'custom';
13
+ /**
14
+ * Optional callback when a tag is clicked
15
+ */
16
+ onTagClick?: (tag: string) => void;
17
+ /**
18
+ * Optional title for the tag list
19
+ */
20
+ title?: string;
21
+ }
22
+ /**
23
+ * Display a list of tags as chips
24
+ *
25
+ * Shows:
26
+ * - All tags, default tags, or custom tags based on category prop
27
+ * - Clickable chips if onTagClick provided
28
+ * - Empty state when no tags available
29
+ *
30
+ * Uses useTagFilters hook for business logic
31
+ */
32
+ declare function TagList({ userId, category, onTagClick, title, }: TagListProps): react_jsx_runtime.JSX.Element;
33
+
34
+ interface TagSelectorProps {
35
+ /**
36
+ * User ID to fetch tags for
37
+ */
38
+ userId: string;
39
+ /**
40
+ * Currently selected tags
41
+ */
42
+ selectedTags: string[];
43
+ /**
44
+ * Callback when tag selection changes
45
+ */
46
+ onSelectionChange: (tags: string[]) => void;
47
+ /**
48
+ * Category of tags to show for selection
49
+ * @default "all"
50
+ */
51
+ category?: 'all' | 'default' | 'custom';
52
+ /**
53
+ * Optional title for the selector
54
+ */
55
+ title?: string;
56
+ /**
57
+ * Allow multiple selection
58
+ * @default true
59
+ */
60
+ multiple?: boolean;
61
+ }
62
+ /**
63
+ * Tag selector with toggle selection
64
+ *
65
+ * Shows:
66
+ * - Available tags as clickable chips
67
+ * - Visual distinction for selected vs unselected tags
68
+ * - Support for single or multiple selection
69
+ *
70
+ * Uses useTagFilters hook for business logic
71
+ */
72
+ declare function TagSelector({ userId, selectedTags, onSelectionChange, category, title, multiple, }: TagSelectorProps): react_jsx_runtime.JSX.Element;
73
+
74
+ interface TagCreateFormProps {
75
+ /** User ID for tag creation */
76
+ userId: string;
77
+ /** Callback fired when tag is successfully created */
78
+ onSuccess?: () => void;
79
+ }
80
+ /**
81
+ * TagCreateForm component for creating new tags.
82
+ * Simple form with text input and submit button.
83
+ */
84
+ declare function TagCreateForm({ userId, onSuccess, }: TagCreateFormProps): react_jsx_runtime.JSX.Element;
85
+
86
+ interface TagDeleteButtonProps {
87
+ /** User ID for tag deletion */
88
+ userId: string;
89
+ /** Name of the tag to delete */
90
+ tagName: string;
91
+ }
92
+ /**
93
+ * TagDeleteButton component with confirmation dialog.
94
+ * Simple delete button that confirms before removing the tag.
95
+ */
96
+ declare function TagDeleteButton({ userId, tagName, }: TagDeleteButtonProps): react_jsx_runtime.JSX.Element;
97
+
98
+ export { TagCreateForm, type TagCreateFormProps, TagDeleteButton, type TagDeleteButtonProps, TagList, type TagListProps, TagSelector, type TagSelectorProps };
package/dist/index.js ADDED
@@ -0,0 +1,225 @@
1
+ import { Box, Typography, Chip, TextField, Alert, Button, CircularProgress, Tooltip, IconButton, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions } from '@mui/material';
2
+ import { useTagFilters } from '@pfm-platform/tags-feature';
3
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
+ import { useState } from 'react';
5
+ import { useCreateTag, useDeleteTag } from '@pfm-platform/tags-data-access';
6
+ import DeleteIcon from '@mui/icons-material/Delete';
7
+
8
+ // src/components/TagList.tsx
9
+ function TagList({
10
+ userId,
11
+ category = "all",
12
+ onTagClick,
13
+ title
14
+ }) {
15
+ const { allTags, defaultTags, customTags } = useTagFilters(userId);
16
+ const tags = category === "default" ? defaultTags : category === "custom" ? customTags : allTags;
17
+ if (tags.length === 0) {
18
+ return /* @__PURE__ */ jsxs(Box, { children: [
19
+ title && /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", gutterBottom: true, children: title }),
20
+ /* @__PURE__ */ jsxs(Typography, { variant: "body2", color: "text.secondary", children: [
21
+ "No ",
22
+ category !== "all" ? category : "",
23
+ " tags available"
24
+ ] })
25
+ ] });
26
+ }
27
+ return /* @__PURE__ */ jsxs(Box, { children: [
28
+ title && /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", gutterBottom: true, children: title }),
29
+ /* @__PURE__ */ jsx(Box, { sx: { display: "flex", gap: 1, flexWrap: "wrap" }, children: tags.map((tag) => /* @__PURE__ */ jsx(
30
+ Chip,
31
+ {
32
+ label: tag,
33
+ size: "small",
34
+ onClick: onTagClick ? () => onTagClick(tag) : void 0,
35
+ clickable: !!onTagClick,
36
+ variant: category === "custom" ? "outlined" : "filled"
37
+ },
38
+ tag
39
+ )) })
40
+ ] });
41
+ }
42
+ function TagSelector({
43
+ userId,
44
+ selectedTags,
45
+ onSelectionChange,
46
+ category = "all",
47
+ title,
48
+ multiple = true
49
+ }) {
50
+ const { allTags, defaultTags, customTags } = useTagFilters(userId);
51
+ const availableTags = category === "default" ? defaultTags : category === "custom" ? customTags : allTags;
52
+ const handleTagClick = (tag) => {
53
+ const isSelected = selectedTags.includes(tag);
54
+ if (multiple) {
55
+ const newSelection = isSelected ? selectedTags.filter((t) => t !== tag) : [...selectedTags, tag];
56
+ onSelectionChange(newSelection);
57
+ } else {
58
+ const newSelection = isSelected ? [] : [tag];
59
+ onSelectionChange(newSelection);
60
+ }
61
+ };
62
+ if (availableTags.length === 0) {
63
+ return /* @__PURE__ */ jsxs(Box, { children: [
64
+ title && /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", gutterBottom: true, children: title }),
65
+ /* @__PURE__ */ jsxs(Typography, { variant: "body2", color: "text.secondary", children: [
66
+ "No ",
67
+ category !== "all" ? category : "",
68
+ " tags available"
69
+ ] })
70
+ ] });
71
+ }
72
+ return /* @__PURE__ */ jsxs(Box, { children: [
73
+ title && /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", gutterBottom: true, children: title }),
74
+ /* @__PURE__ */ jsx(Box, { sx: { display: "flex", gap: 1, flexWrap: "wrap" }, children: availableTags.map((tag) => {
75
+ const isSelected = selectedTags.includes(tag);
76
+ return /* @__PURE__ */ jsx(
77
+ Chip,
78
+ {
79
+ label: tag,
80
+ size: "small",
81
+ onClick: () => handleTagClick(tag),
82
+ clickable: true,
83
+ color: isSelected ? "primary" : "default",
84
+ variant: isSelected ? "filled" : "outlined"
85
+ },
86
+ tag
87
+ );
88
+ }) })
89
+ ] });
90
+ }
91
+ function TagCreateForm({
92
+ userId,
93
+ onSuccess
94
+ }) {
95
+ const [tagName, setTagName] = useState("");
96
+ const [error, setError] = useState(null);
97
+ const createTag = useCreateTag({
98
+ onSuccess: () => {
99
+ setTagName("");
100
+ setError(null);
101
+ onSuccess?.();
102
+ },
103
+ onError: (err) => {
104
+ setError(err instanceof Error ? err.message : "Failed to create tag");
105
+ }
106
+ });
107
+ const handleSubmit = (e) => {
108
+ e.preventDefault();
109
+ if (!tagName.trim()) {
110
+ setError("Tag name is required");
111
+ return;
112
+ }
113
+ const data = {
114
+ name: tagName.trim()
115
+ };
116
+ createTag.mutate({ userId, data });
117
+ };
118
+ return /* @__PURE__ */ jsxs(
119
+ Box,
120
+ {
121
+ component: "form",
122
+ onSubmit: handleSubmit,
123
+ sx: { display: "flex", flexDirection: "column", gap: 2 },
124
+ children: [
125
+ /* @__PURE__ */ jsx(
126
+ TextField,
127
+ {
128
+ label: "Tag Name",
129
+ value: tagName,
130
+ onChange: (e) => {
131
+ setTagName(e.target.value);
132
+ setError(null);
133
+ },
134
+ required: true,
135
+ fullWidth: true,
136
+ disabled: createTag.isPending,
137
+ error: !!error,
138
+ helperText: error || "Enter a name for your new tag",
139
+ inputProps: {
140
+ maxLength: 50
141
+ }
142
+ }
143
+ ),
144
+ error && /* @__PURE__ */ jsx(Alert, { severity: "error", onClose: () => setError(null), children: error }),
145
+ /* @__PURE__ */ jsx(Box, { sx: { display: "flex", gap: 1 }, children: /* @__PURE__ */ jsx(
146
+ Button,
147
+ {
148
+ type: "submit",
149
+ variant: "contained",
150
+ disabled: createTag.isPending || !tagName.trim(),
151
+ startIcon: createTag.isPending ? /* @__PURE__ */ jsx(CircularProgress, { size: 20 }) : null,
152
+ children: createTag.isPending ? "Creating..." : "Create Tag"
153
+ }
154
+ ) })
155
+ ]
156
+ }
157
+ );
158
+ }
159
+ function TagDeleteButton({
160
+ userId,
161
+ tagName
162
+ }) {
163
+ const [open, setOpen] = useState(false);
164
+ const deleteTag = useDeleteTag({
165
+ onSuccess: () => {
166
+ setOpen(false);
167
+ }
168
+ });
169
+ const handleDelete = () => {
170
+ deleteTag.mutate({ userId, tagName });
171
+ };
172
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
173
+ /* @__PURE__ */ jsx(Tooltip, { title: "Delete tag", children: /* @__PURE__ */ jsx(
174
+ IconButton,
175
+ {
176
+ onClick: () => setOpen(true),
177
+ color: "error",
178
+ size: "small",
179
+ "aria-label": `delete tag ${tagName}`,
180
+ children: /* @__PURE__ */ jsx(DeleteIcon, {})
181
+ }
182
+ ) }),
183
+ /* @__PURE__ */ jsxs(
184
+ Dialog,
185
+ {
186
+ open,
187
+ onClose: () => !deleteTag.isPending && setOpen(false),
188
+ "aria-labelledby": "delete-tag-dialog-title",
189
+ children: [
190
+ /* @__PURE__ */ jsx(DialogTitle, { id: "delete-tag-dialog-title", children: "Delete Tag?" }),
191
+ /* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsxs(DialogContentText, { children: [
192
+ 'Are you sure you want to delete the tag "',
193
+ tagName,
194
+ '"? This action cannot be undone.'
195
+ ] }) }),
196
+ /* @__PURE__ */ jsxs(DialogActions, { children: [
197
+ /* @__PURE__ */ jsx(
198
+ Button,
199
+ {
200
+ onClick: () => setOpen(false),
201
+ disabled: deleteTag.isPending,
202
+ children: "Cancel"
203
+ }
204
+ ),
205
+ /* @__PURE__ */ jsx(
206
+ Button,
207
+ {
208
+ onClick: handleDelete,
209
+ color: "error",
210
+ variant: "contained",
211
+ disabled: deleteTag.isPending,
212
+ autoFocus: true,
213
+ children: deleteTag.isPending ? "Deleting..." : "Delete"
214
+ }
215
+ )
216
+ ] })
217
+ ]
218
+ }
219
+ )
220
+ ] });
221
+ }
222
+
223
+ export { TagCreateForm, TagDeleteButton, TagList, TagSelector };
224
+ //# sourceMappingURL=index.js.map
225
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/TagList.tsx","../src/components/TagSelector.tsx","../src/components/TagCreateForm.tsx","../src/components/TagDeleteButton.tsx"],"names":["useTagFilters","jsxs","Box","jsx","Typography","Chip","useState","Button"],"mappings":";;;;;;;;AAoCO,SAAS,OAAA,CAAQ;AAAA,EACtB,MAAA;AAAA,EACA,QAAA,GAAW,KAAA;AAAA,EACX,UAAA;AAAA,EACA;AACF,CAAA,EAAiB;AACf,EAAA,MAAM,EAAE,OAAA,EAAS,WAAA,EAAa,UAAA,EAAW,GAAI,cAAc,MAAM,CAAA;AAEjE,EAAA,MAAM,OACJ,QAAA,KAAa,SAAA,GACT,WAAA,GACA,QAAA,KAAa,WACX,UAAA,GACA,OAAA;AAER,EAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACrB,IAAA,4BACG,GAAA,EAAA,EACE,QAAA,EAAA;AAAA,MAAA,KAAA,oBACC,GAAA,CAAC,cAAW,OAAA,EAAQ,OAAA,EAAQ,OAAM,gBAAA,EAAiB,YAAA,EAAY,MAC5D,QAAA,EAAA,KAAA,EACH,CAAA;AAAA,sBAEF,IAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAQ,OAAA,EAAQ,OAAM,gBAAA,EAAiB,QAAA,EAAA;AAAA,QAAA,KAAA;AAAA,QAC7C,QAAA,KAAa,QAAQ,QAAA,GAAW,EAAA;AAAA,QAAG;AAAA,OAAA,EACzC;AAAA,KAAA,EACF,CAAA;AAAA,EAEJ;AAEA,EAAA,4BACG,GAAA,EAAA,EACE,QAAA,EAAA;AAAA,IAAA,KAAA,oBACC,GAAA,CAAC,cAAW,OAAA,EAAQ,OAAA,EAAQ,OAAM,gBAAA,EAAiB,YAAA,EAAY,MAC5D,QAAA,EAAA,KAAA,EACH,CAAA;AAAA,oBAEF,GAAA,CAAC,GAAA,EAAA,EAAI,EAAA,EAAI,EAAE,SAAS,MAAA,EAAQ,GAAA,EAAK,CAAA,EAAG,QAAA,EAAU,MAAA,EAAO,EAClD,QAAA,EAAA,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,qBACT,GAAA;AAAA,MAAC,IAAA;AAAA,MAAA;AAAA,QAEC,KAAA,EAAO,GAAA;AAAA,QACP,IAAA,EAAK,OAAA;AAAA,QACL,OAAA,EAAS,UAAA,GAAa,MAAM,UAAA,CAAW,GAAG,CAAA,GAAI,MAAA;AAAA,QAC9C,SAAA,EAAW,CAAC,CAAC,UAAA;AAAA,QACb,OAAA,EAAS,QAAA,KAAa,QAAA,GAAW,UAAA,GAAa;AAAA,OAAA;AAAA,MALzC;AAAA,KAOR,CAAA,EACH;AAAA,GAAA,EACF,CAAA;AAEJ;ACxCO,SAAS,WAAA,CAAY;AAAA,EAC1B,MAAA;AAAA,EACA,YAAA;AAAA,EACA,iBAAA;AAAA,EACA,QAAA,GAAW,KAAA;AAAA,EACX,KAAA;AAAA,EACA,QAAA,GAAW;AACb,CAAA,EAAqB;AACnB,EAAA,MAAM,EAAE,OAAA,EAAS,WAAA,EAAa,UAAA,EAAW,GAAIA,cAAc,MAAM,CAAA;AAEjE,EAAA,MAAM,gBACJ,QAAA,KAAa,SAAA,GACT,WAAA,GACA,QAAA,KAAa,WACX,UAAA,GACA,OAAA;AAER,EAAA,MAAM,cAAA,GAAiB,CAAC,GAAA,KAAgB;AACtC,IAAA,MAAM,UAAA,GAAa,YAAA,CAAa,QAAA,CAAS,GAAG,CAAA;AAE5C,IAAA,IAAI,QAAA,EAAU;AAEZ,MAAA,MAAM,YAAA,GAAe,UAAA,GACjB,YAAA,CAAa,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,KAAM,GAAG,CAAA,GACpC,CAAC,GAAG,YAAA,EAAc,GAAG,CAAA;AACzB,MAAA,iBAAA,CAAkB,YAAY,CAAA;AAAA,IAChC,CAAA,MAAO;AAEL,MAAA,MAAM,YAAA,GAAe,UAAA,GAAa,EAAC,GAAI,CAAC,GAAG,CAAA;AAC3C,MAAA,iBAAA,CAAkB,YAAY,CAAA;AAAA,IAChC;AAAA,EACF,CAAA;AAEA,EAAA,IAAI,aAAA,CAAc,WAAW,CAAA,EAAG;AAC9B,IAAA,uBACEC,IAAAA,CAACC,GAAAA,EAAA,EACE,QAAA,EAAA;AAAA,MAAA,KAAA,oBACCC,GAAAA,CAACC,UAAAA,EAAA,EAAW,OAAA,EAAQ,SAAQ,KAAA,EAAM,gBAAA,EAAiB,YAAA,EAAY,IAAA,EAC5D,QAAA,EAAA,KAAA,EACH,CAAA;AAAA,sBAEFH,IAAAA,CAACG,UAAAA,EAAA,EAAW,OAAA,EAAQ,OAAA,EAAQ,OAAM,gBAAA,EAAiB,QAAA,EAAA;AAAA,QAAA,KAAA;AAAA,QAC7C,QAAA,KAAa,QAAQ,QAAA,GAAW,EAAA;AAAA,QAAG;AAAA,OAAA,EACzC;AAAA,KAAA,EACF,CAAA;AAAA,EAEJ;AAEA,EAAA,uBACEH,IAAAA,CAACC,GAAAA,EAAA,EACE,QAAA,EAAA;AAAA,IAAA,KAAA,oBACCC,GAAAA,CAACC,UAAAA,EAAA,EAAW,OAAA,EAAQ,SAAQ,KAAA,EAAM,gBAAA,EAAiB,YAAA,EAAY,IAAA,EAC5D,QAAA,EAAA,KAAA,EACH,CAAA;AAAA,oBAEFD,GAAAA,CAACD,GAAAA,EAAA,EAAI,EAAA,EAAI,EAAE,OAAA,EAAS,MAAA,EAAQ,GAAA,EAAK,CAAA,EAAG,UAAU,MAAA,EAAO,EAClD,QAAA,EAAA,aAAA,CAAc,GAAA,CAAI,CAAC,GAAA,KAAQ;AAC1B,MAAA,MAAM,UAAA,GAAa,YAAA,CAAa,QAAA,CAAS,GAAG,CAAA;AAC5C,MAAA,uBACEC,GAAAA;AAAA,QAACE,IAAAA;AAAA,QAAA;AAAA,UAEC,KAAA,EAAO,GAAA;AAAA,UACP,IAAA,EAAK,OAAA;AAAA,UACL,OAAA,EAAS,MAAM,cAAA,CAAe,GAAG,CAAA;AAAA,UACjC,SAAA,EAAS,IAAA;AAAA,UACT,KAAA,EAAO,aAAa,SAAA,GAAY,SAAA;AAAA,UAChC,OAAA,EAAS,aAAa,QAAA,GAAW;AAAA,SAAA;AAAA,QAN5B;AAAA,OAOP;AAAA,IAEJ,CAAC,CAAA,EACH;AAAA,GAAA,EACF,CAAA;AAEJ;AClGO,SAAS,aAAA,CAAc;AAAA,EAC5B,MAAA;AAAA,EACA;AACF,CAAA,EAAuB;AACrB,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,EAAE,CAAA;AACzC,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAwB,IAAI,CAAA;AAEtD,EAAA,MAAM,YAAY,YAAA,CAAa;AAAA,IAC7B,WAAW,MAAM;AACf,MAAA,UAAA,CAAW,EAAE,CAAA;AACb,MAAA,QAAA,CAAS,IAAI,CAAA;AACb,MAAA,SAAA,IAAY;AAAA,IACd,CAAA;AAAA,IACA,OAAA,EAAS,CAAC,GAAA,KAAQ;AAChB,MAAA,QAAA,CAAS,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,sBAAsB,CAAA;AAAA,IACtE;AAAA,GACD,CAAA;AAED,EAAA,MAAM,YAAA,GAAe,CAAC,CAAA,KAAuB;AAC3C,IAAA,CAAA,CAAE,cAAA,EAAe;AAEjB,IAAA,IAAI,CAAC,OAAA,CAAQ,IAAA,EAAK,EAAG;AACnB,MAAA,QAAA,CAAS,sBAAsB,CAAA;AAC/B,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,GAAkB;AAAA,MACtB,IAAA,EAAM,QAAQ,IAAA;AAAK,KACrB;AAEA,IAAA,SAAA,CAAU,MAAA,CAAO,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA;AAAA,EACnC,CAAA;AAEA,EAAA,uBACEJ,IAAAA;AAAA,IAACC,GAAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,MAAA;AAAA,MACV,QAAA,EAAU,YAAA;AAAA,MACV,IAAI,EAAE,OAAA,EAAS,QAAQ,aAAA,EAAe,QAAA,EAAU,KAAK,CAAA,EAAE;AAAA,MAEvD,QAAA,EAAA;AAAA,wBAAAC,GAAAA;AAAA,UAAC,SAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAM,UAAA;AAAA,YACN,KAAA,EAAO,OAAA;AAAA,YACP,QAAA,EAAU,CAAC,CAAA,KAAM;AACf,cAAA,UAAA,CAAW,CAAA,CAAE,OAAO,KAAK,CAAA;AACzB,cAAA,QAAA,CAAS,IAAI,CAAA;AAAA,YACf,CAAA;AAAA,YACA,QAAA,EAAQ,IAAA;AAAA,YACR,SAAA,EAAS,IAAA;AAAA,YACT,UAAU,SAAA,CAAU,SAAA;AAAA,YACpB,KAAA,EAAO,CAAC,CAAC,KAAA;AAAA,YACT,YAAY,KAAA,IAAS,+BAAA;AAAA,YACrB,UAAA,EAAY;AAAA,cACV,SAAA,EAAW;AAAA;AACb;AAAA,SACF;AAAA,QAEC,KAAA,oBACCA,GAAAA,CAAC,KAAA,EAAA,EAAM,QAAA,EAAS,OAAA,EAAQ,OAAA,EAAS,MAAM,QAAA,CAAS,IAAI,CAAA,EACjD,QAAA,EAAA,KAAA,EACH,CAAA;AAAA,wBAGFA,GAAAA,CAACD,GAAAA,EAAA,EAAI,EAAA,EAAI,EAAE,OAAA,EAAS,MAAA,EAAQ,GAAA,EAAK,CAAA,EAAE,EACjC,QAAA,kBAAAC,GAAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,QAAA;AAAA,YACL,OAAA,EAAQ,WAAA;AAAA,YACR,QAAA,EAAU,SAAA,CAAU,SAAA,IAAa,CAAC,QAAQ,IAAA,EAAK;AAAA,YAC/C,SAAA,EAAW,UAAU,SAAA,mBAAYA,IAAC,gBAAA,EAAA,EAAiB,IAAA,EAAM,IAAI,CAAA,GAAK,IAAA;AAAA,YAEjE,QAAA,EAAA,SAAA,CAAU,YAAY,aAAA,GAAgB;AAAA;AAAA,SACzC,EACF;AAAA;AAAA;AAAA,GACF;AAEJ;ACvEO,SAAS,eAAA,CAAgB;AAAA,EAC9B,MAAA;AAAA,EACA;AACF,CAAA,EAAyB;AACvB,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIG,SAAS,KAAK,CAAA;AAEtC,EAAA,MAAM,YAAY,YAAA,CAAa;AAAA,IAC7B,WAAW,MAAM;AACf,MAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,IACf;AAAA,GACD,CAAA;AAED,EAAA,MAAM,eAAe,MAAM;AACzB,IAAA,SAAA,CAAU,MAAA,CAAO,EAAE,MAAA,EAAQ,OAAA,EAAS,CAAA;AAAA,EACtC,CAAA;AAEA,EAAA,uBACEL,KAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAAE,GAAAA,CAAC,OAAA,EAAA,EAAQ,KAAA,EAAM,YAAA,EACb,QAAA,kBAAAA,GAAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,OAAA,EAAS,MAAM,OAAA,CAAQ,IAAI,CAAA;AAAA,QAC3B,KAAA,EAAM,OAAA;AAAA,QACN,IAAA,EAAK,OAAA;AAAA,QACL,YAAA,EAAY,cAAc,OAAO,CAAA,CAAA;AAAA,QAEjC,QAAA,kBAAAA,IAAC,UAAA,EAAA,EAAW;AAAA;AAAA,KACd,EACF,CAAA;AAAA,oBAEAF,IAAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,IAAA;AAAA,QACA,SAAS,MAAM,CAAC,SAAA,CAAU,SAAA,IAAa,QAAQ,KAAK,CAAA;AAAA,QACpD,iBAAA,EAAgB,yBAAA;AAAA,QAEhB,QAAA,EAAA;AAAA,0BAAAE,GAAAA,CAAC,WAAA,EAAA,EAAY,EAAA,EAAG,yBAAA,EAA0B,QAAA,EAAA,aAAA,EAAW,CAAA;AAAA,0BACrDA,GAAAA,CAAC,aAAA,EAAA,EACC,QAAA,kBAAAF,KAAC,iBAAA,EAAA,EAAkB,QAAA,EAAA;AAAA,YAAA,2CAAA;AAAA,YACyB,OAAA;AAAA,YAAQ;AAAA,WAAA,EACpD,CAAA,EACF,CAAA;AAAA,0BACAA,KAAC,aAAA,EAAA,EACC,QAAA,EAAA;AAAA,4BAAAE,GAAAA;AAAA,cAACI,MAAAA;AAAA,cAAA;AAAA,gBACC,OAAA,EAAS,MAAM,OAAA,CAAQ,KAAK,CAAA;AAAA,gBAC5B,UAAU,SAAA,CAAU,SAAA;AAAA,gBACrB,QAAA,EAAA;AAAA;AAAA,aAED;AAAA,4BACAJ,GAAAA;AAAA,cAACI,MAAAA;AAAA,cAAA;AAAA,gBACC,OAAA,EAAS,YAAA;AAAA,gBACT,KAAA,EAAM,OAAA;AAAA,gBACN,OAAA,EAAQ,WAAA;AAAA,gBACR,UAAU,SAAA,CAAU,SAAA;AAAA,gBACpB,SAAA,EAAS,IAAA;AAAA,gBAER,QAAA,EAAA,SAAA,CAAU,YAAY,aAAA,GAAgB;AAAA;AAAA;AACzC,WAAA,EACF;AAAA;AAAA;AAAA;AACF,GAAA,EACF,CAAA;AAEJ","file":"index.js","sourcesContent":["import { Box, Chip, Typography } from '@mui/material';\nimport { useTagFilters } from '@pfm-platform/tags-feature';\n\nexport interface TagListProps {\n /**\n * User ID to fetch tags for\n */\n userId: string;\n\n /**\n * Category of tags to display\n * @default \"all\"\n */\n category?: 'all' | 'default' | 'custom';\n\n /**\n * Optional callback when a tag is clicked\n */\n onTagClick?: (tag: string) => void;\n\n /**\n * Optional title for the tag list\n */\n title?: string;\n}\n\n/**\n * Display a list of tags as chips\n *\n * Shows:\n * - All tags, default tags, or custom tags based on category prop\n * - Clickable chips if onTagClick provided\n * - Empty state when no tags available\n *\n * Uses useTagFilters hook for business logic\n */\nexport function TagList({\n userId,\n category = 'all',\n onTagClick,\n title,\n}: TagListProps) {\n const { allTags, defaultTags, customTags } = useTagFilters(userId);\n\n const tags =\n category === 'default'\n ? defaultTags\n : category === 'custom'\n ? customTags\n : allTags;\n\n if (tags.length === 0) {\n return (\n <Box>\n {title && (\n <Typography variant=\"body2\" color=\"text.secondary\" gutterBottom>\n {title}\n </Typography>\n )}\n <Typography variant=\"body2\" color=\"text.secondary\">\n No {category !== 'all' ? category : ''} tags available\n </Typography>\n </Box>\n );\n }\n\n return (\n <Box>\n {title && (\n <Typography variant=\"body2\" color=\"text.secondary\" gutterBottom>\n {title}\n </Typography>\n )}\n <Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>\n {tags.map((tag) => (\n <Chip\n key={tag}\n label={tag}\n size=\"small\"\n onClick={onTagClick ? () => onTagClick(tag) : undefined}\n clickable={!!onTagClick}\n variant={category === 'custom' ? 'outlined' : 'filled'}\n />\n ))}\n </Box>\n </Box>\n );\n}\n","import { Box, Chip, Typography } from '@mui/material';\nimport { useTagFilters } from '@pfm-platform/tags-feature';\n\nexport interface TagSelectorProps {\n /**\n * User ID to fetch tags for\n */\n userId: string;\n\n /**\n * Currently selected tags\n */\n selectedTags: string[];\n\n /**\n * Callback when tag selection changes\n */\n onSelectionChange: (tags: string[]) => void;\n\n /**\n * Category of tags to show for selection\n * @default \"all\"\n */\n category?: 'all' | 'default' | 'custom';\n\n /**\n * Optional title for the selector\n */\n title?: string;\n\n /**\n * Allow multiple selection\n * @default true\n */\n multiple?: boolean;\n}\n\n/**\n * Tag selector with toggle selection\n *\n * Shows:\n * - Available tags as clickable chips\n * - Visual distinction for selected vs unselected tags\n * - Support for single or multiple selection\n *\n * Uses useTagFilters hook for business logic\n */\nexport function TagSelector({\n userId,\n selectedTags,\n onSelectionChange,\n category = 'all',\n title,\n multiple = true,\n}: TagSelectorProps) {\n const { allTags, defaultTags, customTags } = useTagFilters(userId);\n\n const availableTags =\n category === 'default'\n ? defaultTags\n : category === 'custom'\n ? customTags\n : allTags;\n\n const handleTagClick = (tag: string) => {\n const isSelected = selectedTags.includes(tag);\n\n if (multiple) {\n // Multiple selection: toggle tag\n const newSelection = isSelected\n ? selectedTags.filter((t) => t !== tag)\n : [...selectedTags, tag];\n onSelectionChange(newSelection);\n } else {\n // Single selection: select tag or deselect if clicking same tag\n const newSelection = isSelected ? [] : [tag];\n onSelectionChange(newSelection);\n }\n };\n\n if (availableTags.length === 0) {\n return (\n <Box>\n {title && (\n <Typography variant=\"body2\" color=\"text.secondary\" gutterBottom>\n {title}\n </Typography>\n )}\n <Typography variant=\"body2\" color=\"text.secondary\">\n No {category !== 'all' ? category : ''} tags available\n </Typography>\n </Box>\n );\n }\n\n return (\n <Box>\n {title && (\n <Typography variant=\"body2\" color=\"text.secondary\" gutterBottom>\n {title}\n </Typography>\n )}\n <Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>\n {availableTags.map((tag) => {\n const isSelected = selectedTags.includes(tag);\n return (\n <Chip\n key={tag}\n label={tag}\n size=\"small\"\n onClick={() => handleTagClick(tag)}\n clickable\n color={isSelected ? 'primary' : 'default'}\n variant={isSelected ? 'filled' : 'outlined'}\n />\n );\n })}\n </Box>\n </Box>\n );\n}\n","import { useState } from 'react';\nimport {\n Box,\n Button,\n TextField,\n Alert,\n CircularProgress,\n} from '@mui/material';\nimport { useCreateTag } from '@pfm-platform/tags-data-access';\nimport type { TagCreate } from '@pfm-platform/tags-data-access';\n\nexport interface TagCreateFormProps {\n /** User ID for tag creation */\n userId: string;\n /** Callback fired when tag is successfully created */\n onSuccess?: () => void;\n}\n\n/**\n * TagCreateForm component for creating new tags.\n * Simple form with text input and submit button.\n */\nexport function TagCreateForm({\n userId,\n onSuccess,\n}: TagCreateFormProps) {\n const [tagName, setTagName] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const createTag = useCreateTag({\n onSuccess: () => {\n setTagName('');\n setError(null);\n onSuccess?.();\n },\n onError: (err) => {\n setError(err instanceof Error ? err.message : 'Failed to create tag');\n },\n });\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!tagName.trim()) {\n setError('Tag name is required');\n return;\n }\n\n const data: TagCreate = {\n name: tagName.trim(),\n };\n\n createTag.mutate({ userId, data });\n };\n\n return (\n <Box\n component=\"form\"\n onSubmit={handleSubmit}\n sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}\n >\n <TextField\n label=\"Tag Name\"\n value={tagName}\n onChange={(e) => {\n setTagName(e.target.value);\n setError(null);\n }}\n required\n fullWidth\n disabled={createTag.isPending}\n error={!!error}\n helperText={error || 'Enter a name for your new tag'}\n inputProps={{\n maxLength: 50,\n }}\n />\n\n {error && (\n <Alert severity=\"error\" onClose={() => setError(null)}>\n {error}\n </Alert>\n )}\n\n <Box sx={{ display: 'flex', gap: 1 }}>\n <Button\n type=\"submit\"\n variant=\"contained\"\n disabled={createTag.isPending || !tagName.trim()}\n startIcon={createTag.isPending ? <CircularProgress size={20} /> : null}\n >\n {createTag.isPending ? 'Creating...' : 'Create Tag'}\n </Button>\n </Box>\n </Box>\n );\n}\n","import { useState } from 'react';\nimport {\n IconButton,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogContentText,\n DialogActions,\n Button,\n Tooltip,\n} from '@mui/material';\nimport DeleteIcon from '@mui/icons-material/Delete';\nimport { useDeleteTag } from '@pfm-platform/tags-data-access';\n\nexport interface TagDeleteButtonProps {\n /** User ID for tag deletion */\n userId: string;\n /** Name of the tag to delete */\n tagName: string;\n}\n\n/**\n * TagDeleteButton component with confirmation dialog.\n * Simple delete button that confirms before removing the tag.\n */\nexport function TagDeleteButton({\n userId,\n tagName,\n}: TagDeleteButtonProps) {\n const [open, setOpen] = useState(false);\n\n const deleteTag = useDeleteTag({\n onSuccess: () => {\n setOpen(false);\n },\n });\n\n const handleDelete = () => {\n deleteTag.mutate({ userId, tagName });\n };\n\n return (\n <>\n <Tooltip title=\"Delete tag\">\n <IconButton\n onClick={() => setOpen(true)}\n color=\"error\"\n size=\"small\"\n aria-label={`delete tag ${tagName}`}\n >\n <DeleteIcon />\n </IconButton>\n </Tooltip>\n\n <Dialog\n open={open}\n onClose={() => !deleteTag.isPending && setOpen(false)}\n aria-labelledby=\"delete-tag-dialog-title\"\n >\n <DialogTitle id=\"delete-tag-dialog-title\">Delete Tag?</DialogTitle>\n <DialogContent>\n <DialogContentText>\n Are you sure you want to delete the tag \"{tagName}\"? This action cannot be undone.\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button\n onClick={() => setOpen(false)}\n disabled={deleteTag.isPending}\n >\n Cancel\n </Button>\n <Button\n onClick={handleDelete}\n color=\"error\"\n variant=\"contained\"\n disabled={deleteTag.isPending}\n autoFocus\n >\n {deleteTag.isPending ? 'Deleting...' : 'Delete'}\n </Button>\n </DialogActions>\n </Dialog>\n </>\n );\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@pfm-platform/tags-ui-mui",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "dependencies": {
7
+ "@emotion/react": "^11.14.0",
8
+ "@emotion/styled": "^11.14.1",
9
+ "@mui/x-charts": "8.18.0",
10
+ "@pfm-platform/shared": "0.0.1",
11
+ "@pfm-platform/tags-data-access": "^0.1.1",
12
+ "@pfm-platform/tags-feature": "0.1.1"
13
+ },
14
+ "devDependencies": {
15
+ "@tanstack/react-query": "5.90.9",
16
+ "@testing-library/jest-dom": "^6.9.1",
17
+ "@testing-library/react": "^16.3.0",
18
+ "@testing-library/user-event": "^14.6.1",
19
+ "@types/react": "19.2.5",
20
+ "@vitejs/plugin-react": "^5.1.1",
21
+ "@vitest/coverage-v8": "^4.0.9",
22
+ "jsdom": "^27.2.0",
23
+ "msw": "^2.12.2",
24
+ "vitest": "4.0.9"
25
+ },
26
+ "peerDependencies": {
27
+ "@mui/icons-material": "^7.3.5",
28
+ "@mui/material": "^7.3.5",
29
+ "react": "19.2.0"
30
+ },
31
+ "module": "./dist/index.js",
32
+ "types": "./dist/index.d.ts",
33
+ "exports": {
34
+ ".": {
35
+ "types": "./dist/index.d.ts",
36
+ "import": "./dist/index.js",
37
+ "require": "./dist/index.cjs"
38
+ }
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "README.md"
43
+ ],
44
+ "description": "Personal Finance Management - TAGS ui-mui layer",
45
+ "keywords": [
46
+ "pfm",
47
+ "finance",
48
+ "tags",
49
+ "ui-mui",
50
+ "react",
51
+ "typescript"
52
+ ],
53
+ "author": "Lenny Miller",
54
+ "license": "MIT",
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "https://github.com/lennylmiller/pfm-research",
58
+ "directory": "packages/tags/ui-mui"
59
+ },
60
+ "bugs": "https://github.com/lennylmiller/pfm-research/issues",
61
+ "homepage": "https://github.com/lennylmiller/pfm-research#readme",
62
+ "scripts": {
63
+ "test": "vitest run",
64
+ "test:watch": "vitest",
65
+ "test:ui": "vitest --ui",
66
+ "test:coverage": "vitest run --coverage",
67
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean"
68
+ }
69
+ }