@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 +234 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +98 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.js +225 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
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"]}
|
package/dist/index.d.cts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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":"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","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
|
+
}
|