@flozy/editor 10.4.6 → 10.4.8

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.
@@ -10,10 +10,10 @@ import List from "@mui/material/List";
10
10
  import ListItem from "@mui/material/ListItem";
11
11
  import ListItemButton from "@mui/material/ListItemButton";
12
12
  import IconButton from "@mui/material/IconButton";
13
- import SwipeableDrawer from "@mui/material/SwipeableDrawer";
14
13
  import Tooltip from "@mui/material/Tooltip";
15
14
  import { CloseIcon } from "../../../../../common/iconslist";
16
15
  import { useEditorContext } from "../../../../../hooks/useMouseMove";
16
+ import SwipeableDrawer from "@mui/material/SwipeableDrawer";
17
17
  import Icon from "../../../../../common/Icon";
18
18
  import { colors } from "../../../../Color Picker/defaultColors";
19
19
  import PropertySettings from "../../Options";
@@ -26,6 +26,11 @@ const generateRandomColor = () => {
26
26
  const randomIndex = Math.floor(Math.random() * DEFAULT_COLORS?.length);
27
27
  return DEFAULT_COLORS[randomIndex];
28
28
  };
29
+ const hasIds = arr => Array.isArray(arr) && arr.every(item => 'id' in item);
30
+ const generateStableIds = arr => arr.map((item, index) => ({
31
+ ...item,
32
+ id: `multi_${item.value}_${index}`
33
+ }));
29
34
  const MultiSelectWithPopover = props => {
30
35
  const {
31
36
  options = [],
@@ -34,13 +39,13 @@ const MultiSelectWithPopover = props => {
34
39
  onUpdate,
35
40
  property,
36
41
  wrapColumn = false,
37
- translation
42
+ translation = () => {}
38
43
  } = props;
39
44
  const [anchorEl, setAnchorEl] = useState(null);
40
45
  const [anchorElOption, setAnchorElOption] = useState(null);
41
46
  const [currentIndex, setCurrentIndex] = useState(null);
42
- const [selectedOptions, setSelectedOptions] = useState(value);
43
- const [availableOptions, setAvailableOptions] = useState(options);
47
+ const [selectedOptions, setSelectedOptions] = useState([]);
48
+ const [availableOptions, setAvailableOptions] = useState([]);
44
49
  const [showSnackBar, setShowSnackBar] = useState(false);
45
50
  const [chipColor, setChipColor] = useState(generateRandomColor());
46
51
  const [inputValue, setInputValue] = useState("");
@@ -49,6 +54,21 @@ const MultiSelectWithPopover = props => {
49
54
  } = useEditorContext();
50
55
  const isMobile = window.matchMedia("(max-width: 899px)")?.matches || false;
51
56
  const PopoverComponent = isMobile ? SwipeableDrawer : Popover;
57
+ useEffect(() => {
58
+ console.log("Options received:", options);
59
+ const enrichedAvailable = hasIds(options) ? options : generateStableIds(options);
60
+ console.log("Enriched available options:", enrichedAvailable);
61
+ setAvailableOptions(enrichedAvailable);
62
+ const enrichedSelected = hasIds(value) ? value : value.map(sel => {
63
+ const match = enrichedAvailable.find(opt => opt.value === sel.value);
64
+ return match ? {
65
+ ...sel,
66
+ id: match.id
67
+ } : sel;
68
+ });
69
+ console.log("Enriched selected options:", enrichedSelected);
70
+ setSelectedOptions(enrichedSelected);
71
+ }, []);
52
72
  const mode = useMemo(() => ({
53
73
  type: "editOptionMulti",
54
74
  edit: {
@@ -56,11 +76,11 @@ const MultiSelectWithPopover = props => {
56
76
  visible: true,
57
77
  key: property,
58
78
  type: "multi-select",
59
- options: options || [],
79
+ options: availableOptions || [],
60
80
  optionIndex: currentIndex,
61
81
  hideBackButton: true
62
82
  }
63
- }), [options, property, currentIndex]);
83
+ }), [availableOptions, property, currentIndex]);
64
84
  const customScrollStyles = {
65
85
  scrollbarWidth: "thin",
66
86
  scrollbarColor: `${theme?.palette?.editor?.brainPopupScroll} transparent`,
@@ -81,10 +101,17 @@ const MultiSelectWithPopover = props => {
81
101
  }
82
102
  }, [inputValue, chipColor]);
83
103
  useEffect(() => {
84
- if (JSON.stringify(options) !== JSON.stringify(availableOptions)) {
85
- setAvailableOptions(options);
104
+ if (inputValue?.trim() && !chipColor) {
105
+ setChipColor(generateRandomColor());
106
+ }
107
+ }, [inputValue, chipColor]);
108
+ useEffect(() => {
109
+ const enriched = hasIds(options) ? options : generateStableIds(options);
110
+ const isDifferent = JSON.stringify(enriched) !== JSON.stringify(availableOptions);
111
+ if (isDifferent) {
112
+ setAvailableOptions(enriched);
86
113
  }
87
- }, [options, availableOptions]);
114
+ }, [options]);
88
115
  const handleOpenPopover = useCallback(event => {
89
116
  setAnchorEl(event.currentTarget);
90
117
  }, []);
@@ -0,0 +1,513 @@
1
+ import React, { useCallback, useEffect, useMemo, useState } from "react";
2
+ import Autocomplete from "@mui/material/Autocomplete";
3
+ import Box from "@mui/material/Box";
4
+ import Chip from "@mui/material/Chip";
5
+ import Divider from "@mui/material/Divider";
6
+ import Popover from "@mui/material/Popover";
7
+ import TextField from "@mui/material/TextField";
8
+ import Typography from "@mui/material/Typography";
9
+ import List from "@mui/material/List";
10
+ import ListItem from "@mui/material/ListItem";
11
+ import ListItemButton from "@mui/material/ListItemButton";
12
+ import IconButton from "@mui/material/IconButton";
13
+ import Tooltip from "@mui/material/Tooltip";
14
+ import { CloseIcon } from "../../../../../common/iconslist";
15
+ import { useEditorContext } from "../../../../../hooks/useMouseMove";
16
+ import SwipeableDrawer from "@mui/material/SwipeableDrawer";
17
+ import Icon from "../../../../../common/Icon";
18
+ import { colors } from "../../../../Color Picker/defaultColors";
19
+ import PropertySettings from "../../Options";
20
+ import SnackbarAlert from "../../../../../common/SnackBar";
21
+ import { jsx as _jsx } from "react/jsx-runtime";
22
+ import { jsxs as _jsxs } from "react/jsx-runtime";
23
+ const EXCLUDED_COLORS = new Set(["#000000", "#0F172A", "#2563EB", "#FFFFFF", "#64748B"]);
24
+ const DEFAULT_COLORS = colors?.filter(f => !f?.includes("linear") && !EXCLUDED_COLORS?.has(f));
25
+ const generateRandomColor = () => {
26
+ const randomIndex = Math.floor(Math.random() * DEFAULT_COLORS?.length);
27
+ return DEFAULT_COLORS[randomIndex];
28
+ };
29
+ const hasIds = arr => Array.isArray(arr) && arr.every(item => 'id' in item);
30
+ const generateStableIds = arr => arr.map((item, index) => ({
31
+ ...item,
32
+ id: `multi_${item.value}_${index}`
33
+ }));
34
+ const SelectV1 = props => {
35
+ const {
36
+ value,
37
+ options = [],
38
+ selectMultiple,
39
+ property,
40
+ wrapColumn = false,
41
+ onChange,
42
+ onUpdate,
43
+ translation
44
+ } = props;
45
+ const [anchorEl, setAnchorEl] = useState(null);
46
+ const [anchorElOption, setAnchorElOption] = useState(null);
47
+ const [currentIndex, setCurrentIndex] = useState(null);
48
+ const [selectedOptions, setSelectedOptions] = useState([]);
49
+ const [availableOptions, setAvailableOptions] = useState([]);
50
+ const [showSnackBar, setShowSnackBar] = useState(false);
51
+ const [chipColor, setChipColor] = useState(generateRandomColor());
52
+ const [inputValue, setInputValue] = useState("");
53
+ const [availableOptionsMap, setAvailableOptionsMap] = useState({});
54
+ const [resolvedSelectedOptions, setResolvedSelectedOptions] = useState([]);
55
+ const {
56
+ theme
57
+ } = useEditorContext();
58
+ const isMobile = window.matchMedia("(max-width: 899px)")?.matches || false;
59
+ const PopoverComponent = isMobile ? SwipeableDrawer : Popover;
60
+ useEffect(() => {
61
+ const obj = {};
62
+ for (const opt of availableOptions) {
63
+ if (opt?.id != null) {
64
+ obj[opt.id] = opt;
65
+ }
66
+ }
67
+ setAvailableOptionsMap(obj);
68
+ }, [availableOptions]);
69
+ useEffect(() => {
70
+ const resolved = selectedOptions?.filter(sel => sel?.id && availableOptionsMap?.[sel.id])?.map(sel => availableOptionsMap[sel?.id]) ?? [];
71
+ setResolvedSelectedOptions(resolved);
72
+ }, [selectedOptions, availableOptionsMap]);
73
+ useEffect(() => {
74
+ // console.log("Options received:", options);
75
+ const enrichedAvailable = hasIds(options) ? options : generateStableIds(options);
76
+ // console.log("Enriched available options:", enrichedAvailable);
77
+ setAvailableOptions(enrichedAvailable);
78
+ const enrichedSelected = hasIds(value) ? value : value?.map(sel => {
79
+ const match = enrichedAvailable.find(opt => opt?.value === sel?.value);
80
+ return match ? {
81
+ id: match.id
82
+ } : sel;
83
+ });
84
+ // console.log("Enriched selected options:", enrichedSelected);
85
+ setSelectedOptions(enrichedSelected);
86
+ }, []);
87
+ const mode = useMemo(() => ({
88
+ type: "editOptionMulti",
89
+ edit: {
90
+ label: selectMultiple ? "Multi Select" : "Select",
91
+ visible: true,
92
+ key: property,
93
+ type: selectMultiple ? "multi-select" : "select",
94
+ options: availableOptions || [],
95
+ optionIndex: currentIndex,
96
+ hideBackButton: true
97
+ }
98
+ }), [availableOptions, property, currentIndex, selectMultiple]);
99
+ const customScrollStyles = {
100
+ scrollbarWidth: "thin",
101
+ scrollbarColor: `${theme?.palette?.editor?.brainPopupScroll} transparent`,
102
+ "&::-webkit-scrollbar": {
103
+ width: "6px"
104
+ },
105
+ "&::-webkit-scrollbar-thumb": {
106
+ backgroundColor: theme?.palette?.editor?.brainPopupScroll,
107
+ borderRadius: "3px"
108
+ },
109
+ "&::-webkit-scrollbar-track": {
110
+ display: "none"
111
+ }
112
+ };
113
+ useEffect(() => {
114
+ if (inputValue?.trim() && !chipColor) {
115
+ setChipColor(generateRandomColor());
116
+ }
117
+ }, [inputValue, chipColor]);
118
+ useEffect(() => {
119
+ const enriched = hasIds(options) ? options : generateStableIds(options);
120
+ const isDifferent = JSON.stringify(enriched) !== JSON.stringify(availableOptions);
121
+ if (isDifferent) {
122
+ setAvailableOptions(enriched);
123
+ }
124
+ }, [options]);
125
+ const handleOpenPopover = useCallback(event => {
126
+ setAnchorEl(event.currentTarget);
127
+ }, []);
128
+ const handleClosePopover = useCallback(e => {
129
+ e?.stopPropagation();
130
+ setAnchorEl(null);
131
+ }, []);
132
+ const handleAddOption = newValue => {
133
+ const trimmedValue = newValue?.trim?.();
134
+ if (!trimmedValue) return;
135
+ const isDuplicate = availableOptions.some(opt => opt.value.toLowerCase() === trimmedValue.toLowerCase());
136
+ if (isDuplicate) {
137
+ const existing = availableOptions.find(opt => opt?.value?.toLowerCase() === trimmedValue?.toLowerCase());
138
+ const alreadySelected = selectedOptions?.some(sel => sel?.id === existing?.id);
139
+ if (!alreadySelected) {
140
+ const updatedSelected = selectMultiple ? [...selectedOptions, {
141
+ id: existing.id
142
+ }] : [{
143
+ id: existing.id
144
+ }];
145
+ setSelectedOptions(updatedSelected);
146
+ onChange?.(updatedSelected);
147
+ }
148
+ setInputValue("");
149
+ setChipColor("");
150
+ return;
151
+ }
152
+ const newOption = {
153
+ value: trimmedValue,
154
+ color: chipColor,
155
+ id: `multi_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 5)}`
156
+ };
157
+ const updatedAvailableOptions = [...availableOptions, newOption];
158
+ const updatedSelectedOptions = selectMultiple ? [...selectedOptions, {
159
+ id: newOption.id
160
+ }] : [{
161
+ id: newOption.id
162
+ }];
163
+ setAvailableOptions(updatedAvailableOptions);
164
+ setSelectedOptions(updatedSelectedOptions);
165
+ onUpdate?.(updatedAvailableOptions);
166
+ onChange?.(updatedSelectedOptions);
167
+ setInputValue("");
168
+ setChipColor("");
169
+ };
170
+ const onClose = () => {
171
+ setAnchorEl(anchorElOption);
172
+ setAnchorElOption(null);
173
+ };
174
+ const onEditOption = (type, data) => {
175
+ const updateData = data?.edit ? data?.edit?.options : data?.options;
176
+ onUpdate(updateData);
177
+ };
178
+ const handleEditOption = (e, index) => {
179
+ e.stopPropagation();
180
+ setCurrentIndex(index);
181
+ setAnchorElOption(anchorEl);
182
+ setAnchorEl(null);
183
+ };
184
+ const handleSelectOption = option => {
185
+ const isAlreadySelected = selectedOptions.some(opt => opt?.id === option?.id);
186
+ if (isAlreadySelected) {
187
+ setShowSnackBar(true);
188
+ return;
189
+ }
190
+ const updatedOptions = selectMultiple ? [...selectedOptions, {
191
+ id: option?.id
192
+ }] : [{
193
+ id: option?.id
194
+ }];
195
+ setSelectedOptions(updatedOptions);
196
+ onChange(updatedOptions);
197
+ if (!selectMultiple) {
198
+ handleClosePopover();
199
+ }
200
+ };
201
+ const handleClearSelection = () => {
202
+ setSelectedOptions([]);
203
+ };
204
+ const handleDeleteChip = (event, option) => {
205
+ event.stopPropagation();
206
+ const updatedOptions = selectedOptions?.filter(selected => selected?.id !== option?.id);
207
+ setSelectedOptions(updatedOptions);
208
+ onChange(selectMultiple ? updatedOptions : updatedOptions[0] || null);
209
+ };
210
+ const filteredOptions = availableOptions?.filter(option => option?.value?.toLowerCase()?.includes(inputValue?.toLowerCase()));
211
+ const isExactMatch = availableOptions?.some(opt => opt?.value?.toLowerCase() === inputValue?.toLowerCase());
212
+ const open = Boolean(anchorEl);
213
+ const openEditOption = Boolean(anchorElOption);
214
+ const id = open ? "autocomplete-popover" : undefined;
215
+ return /*#__PURE__*/_jsxs("div", {
216
+ children: [/*#__PURE__*/_jsx(Box, {
217
+ sx: {
218
+ display: "flex",
219
+ flexWrap: wrapColumn ? "wrap" : "nowrap",
220
+ overflowX: wrapColumn ? "hidden" : "auto",
221
+ gap: 0.5,
222
+ padding: "8px",
223
+ cursor: "pointer"
224
+ },
225
+ onClick: handleOpenPopover,
226
+ children: resolvedSelectedOptions?.map((option, index) => /*#__PURE__*/_jsx(Chip, {
227
+ label: option?.value,
228
+ onDelete: event => {
229
+ handleDeleteChip(event, option);
230
+ },
231
+ deleteIcon: /*#__PURE__*/_jsx(CloseIcon, {}),
232
+ variant: "filled",
233
+ sx: {
234
+ backgroundColor: option?.color,
235
+ color: '#0F172A',
236
+ "& .MuiChip-deleteIcon": {
237
+ flexShrink: 0,
238
+ "& path": {
239
+ stroke: '#0F172A !important'
240
+ }
241
+ },
242
+ "&:hover": {
243
+ opacity: 0.8
244
+ }
245
+ }
246
+ }, index))
247
+ }), /*#__PURE__*/_jsx(PopoverComponent, {
248
+ id: id,
249
+ open: open,
250
+ anchorEl: anchorEl,
251
+ anchor: "bottom",
252
+ onClose: e => handleClosePopover(e),
253
+ anchorOrigin: {
254
+ vertical: "top",
255
+ horizontal: "left"
256
+ },
257
+ transformOrigin: {
258
+ vertical: "top",
259
+ horizontal: "left"
260
+ },
261
+ sx: {
262
+ "& .MuiPaper-root": {
263
+ borderRadius: "20px",
264
+ background: theme?.palette?.editor?.textFormatBgColor,
265
+ border: `1px solid ${theme?.palette?.editor?.popUpBorderColor}`,
266
+ boxShadow: "0px 4px 10px 0px #00000029"
267
+ }
268
+ },
269
+ disableAutoFocus: true,
270
+ disableEnforceFocus: true,
271
+ disableRestoreFocus: true,
272
+ children: /*#__PURE__*/_jsxs(Box, {
273
+ sx: {
274
+ width: isMobile ? "100%" : 300
275
+ },
276
+ children: [/*#__PURE__*/_jsx(Autocomplete, {
277
+ multiple: true,
278
+ freeSolo: true,
279
+ disablePortal: true,
280
+ PopperComponent: () => null,
281
+ sx: {
282
+ "& .MuiFormControl-root": {
283
+ maxHeight: "250px",
284
+ overflowY: "auto",
285
+ overflowX: "hidden",
286
+ pr: '12px',
287
+ pl: '12px',
288
+ marginTop: '12px',
289
+ ...customScrollStyles
290
+ }
291
+ },
292
+ disableClearable: true,
293
+ options: [],
294
+ getOptionLabel: () => "",
295
+ value: resolvedSelectedOptions,
296
+ onChange: (event, newValues) => {
297
+ const stringOptions = newValues.filter(val => typeof val === "string" && val.trim());
298
+ for (const str of stringOptions) {
299
+ handleAddOption(str);
300
+ }
301
+ },
302
+ inputValue: inputValue,
303
+ onInputChange: (event, newInputValue) => setInputValue(newInputValue),
304
+ onKeyDown: event => {
305
+ if (event.key === "Enter" && inputValue.trim()) {
306
+ event.preventDefault();
307
+ handleAddOption(inputValue);
308
+ }
309
+ },
310
+ filterOptions: (options, params) => options?.filter(option => option?.value?.toLowerCase()?.includes(params?.inputValue?.toLowerCase())),
311
+ renderInput: params => /*#__PURE__*/_jsx(TextField, {
312
+ ...params,
313
+ variant: "standard",
314
+ InputProps: {
315
+ ...params.InputProps,
316
+ disableUnderline: true,
317
+ sx: {
318
+ display: "flex",
319
+ flexWrap: "wrap",
320
+ fontFamily: "Inter",
321
+ fontWeight: 400,
322
+ fontSize: "12px",
323
+ color: theme?.palette?.editor?.secondaryTextColor,
324
+ "&::placeholder": {
325
+ color: theme?.palette?.editor?.secondaryTextColor
326
+ },
327
+ "& .MuiAutocomplete-input": {
328
+ minWidth: "100px !important"
329
+ }
330
+ },
331
+ endAdornment: /*#__PURE__*/_jsx(Tooltip, {
332
+ arrow: true,
333
+ title: "Clear Selected",
334
+ children: /*#__PURE__*/_jsx(IconButton, {
335
+ onClick: handleClearSelection,
336
+ sx: {
337
+ padding: 0,
338
+ minWidth: "unset",
339
+ "& .MuiSvgIcon-root": {
340
+ fontSize: 20
341
+ },
342
+ '& rect': {
343
+ fill: theme?.palette?.editor?.closeButtonSvgStroke
344
+ },
345
+ '&:hover': {
346
+ backgroundColor: 'transparent'
347
+ }
348
+ },
349
+ children: /*#__PURE__*/_jsx(Icon, {
350
+ icon: "resetIconNew"
351
+ })
352
+ })
353
+ })
354
+ },
355
+ sx: {
356
+ backgroundColor: "transparent",
357
+ fontFamily: 'Inter',
358
+ fontWeight: 400,
359
+ fontSize: '12px'
360
+ },
361
+ placeholder: "Create new one..."
362
+ }),
363
+ renderTags: (tagValues, getTagProps) => {
364
+ return tagValues?.map((option, index) => /*#__PURE__*/_jsx(Chip, {
365
+ variant: "filled",
366
+ label: option?.value,
367
+ ...getTagProps({
368
+ index
369
+ }),
370
+ onDelete: event => {
371
+ handleDeleteChip(event, option);
372
+ },
373
+ deleteIcon: /*#__PURE__*/_jsx(CloseIcon, {}),
374
+ sx: {
375
+ backgroundColor: option?.color,
376
+ color: '#0F172A',
377
+ "& .MuiChip-deleteIcon": {
378
+ flexShrink: 0,
379
+ "& path": {
380
+ stroke: '#0F172A !important'
381
+ }
382
+ },
383
+ "&:hover": {
384
+ opacity: 0.8
385
+ }
386
+ }
387
+ }, index));
388
+ }
389
+ }), /*#__PURE__*/_jsx(Divider, {
390
+ sx: {
391
+ mt: '12px',
392
+ borderBottom: `1px solid ${theme?.palette?.editor?.popUpBorderColor}`,
393
+ boxShadow: theme?.palette?.editor?.dividerDropShadow
394
+ }
395
+ }), /*#__PURE__*/_jsx(Box, {
396
+ sx: {
397
+ pl: '4px'
398
+ },
399
+ children: /*#__PURE__*/_jsxs(List, {
400
+ sx: {
401
+ maxHeight: "250px",
402
+ overflowY: "auto",
403
+ ...customScrollStyles
404
+ },
405
+ children: [/*#__PURE__*/_jsx(Typography, {
406
+ sx: {
407
+ mb: 1,
408
+ pl: '8px',
409
+ color: theme?.palette?.editor?.secondaryTextColor,
410
+ fontFamily: 'Inter',
411
+ fontWeight: 400,
412
+ fontSize: '12px'
413
+ },
414
+ children: "Choose an option or create one"
415
+ }), filteredOptions?.map((option, index) => /*#__PURE__*/_jsx(ListItem, {
416
+ sx: {
417
+ padding: 0
418
+ },
419
+ disablePadding: true,
420
+ children: /*#__PURE__*/_jsxs(ListItemButton, {
421
+ onClick: () => handleSelectOption(option),
422
+ sx: {
423
+ paddingTop: "4px",
424
+ paddingBottom: "4px",
425
+ justifyContent: 'space-between',
426
+ '&:hover': {
427
+ '& path': {
428
+ stroke: theme?.palette?.editor?.activeColor
429
+ },
430
+ borderRadius: '12px'
431
+ }
432
+ },
433
+ children: [/*#__PURE__*/_jsx(Chip, {
434
+ label: option?.value,
435
+ sx: {
436
+ backgroundColor: option?.color,
437
+ color: '#0F172A',
438
+ fontWeight: 500,
439
+ fontSize: "12px",
440
+ fontFamily: "Inter",
441
+ padding: "4px 12px",
442
+ borderRadius: "16px",
443
+ maxWidth: "180px",
444
+ whiteSpace: "nowrap",
445
+ overflow: "hidden",
446
+ textOverflow: "ellipsis"
447
+ }
448
+ }), /*#__PURE__*/_jsx(IconButton, {
449
+ size: "small",
450
+ sx: {
451
+ '& path': {
452
+ stroke: theme?.palette?.editor?.closeButtonSvgStroke
453
+ },
454
+ '&:hover': {
455
+ backgroundColor: 'transparent'
456
+ }
457
+ },
458
+ onClick: e => handleEditOption(e, index),
459
+ children: /*#__PURE__*/_jsx(Icon, {
460
+ icon: "rightArrow"
461
+ })
462
+ })]
463
+ })
464
+ }, index)), inputValue?.trim() && !isExactMatch && /*#__PURE__*/_jsx(ListItem, {
465
+ disablePadding: true,
466
+ children: /*#__PURE__*/_jsxs(ListItemButton, {
467
+ onClick: () => handleAddOption(inputValue),
468
+ sx: {
469
+ display: "flex",
470
+ alignItems: "center"
471
+ },
472
+ children: [/*#__PURE__*/_jsx(Typography, {
473
+ sx: {
474
+ color: theme?.palette?.editor?.secondaryTextColor,
475
+ marginRight: "6px",
476
+ fontSize: "14px",
477
+ fontFamily: "Inter"
478
+ },
479
+ children: "Create"
480
+ }), /*#__PURE__*/_jsx(Chip, {
481
+ label: `${inputValue}`,
482
+ sx: {
483
+ backgroundColor: chipColor,
484
+ color: '#0F172A',
485
+ fontWeight: 500,
486
+ fontSize: "12px",
487
+ fontFamily: "Inter",
488
+ borderRadius: "16px",
489
+ maxWidth: "180px",
490
+ whiteSpace: "nowrap",
491
+ overflow: "hidden",
492
+ textOverflow: "ellipsis"
493
+ }
494
+ })]
495
+ })
496
+ })]
497
+ })
498
+ })]
499
+ })
500
+ }), openEditOption ? /*#__PURE__*/_jsx(PropertySettings, {
501
+ open: openEditOption,
502
+ anchorEl: anchorElOption,
503
+ mode: mode,
504
+ onClose: onClose,
505
+ onEvent: onEditOption,
506
+ translation: translation
507
+ }) : null, showSnackBar ? /*#__PURE__*/_jsx(SnackbarAlert, {
508
+ message: "Option already selected!",
509
+ setShowSnackBar: setShowSnackBar
510
+ }) : null]
511
+ });
512
+ };
513
+ export default SelectV1;
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import { useDataView } from "../../Providers/DataViewProvider";
3
3
  import MultiSelectWithPopover from "./Components/MultiSelect";
4
+ import SelectV1 from "./Components/SelectV1";
4
5
  import { jsx as _jsx } from "react/jsx-runtime";
5
6
  const MultiSelectType = props => {
6
7
  const {
@@ -24,12 +25,12 @@ const MultiSelectType = props => {
24
25
  const coloredValues = [...(value || [])]?.map(m => {
25
26
  return {
26
27
  ...m,
27
- color: options?.find(f => f.value === m.value)?.color || "#FFF"
28
+ color: (options || []).find(f => f?.value === m?.value || f?.id === m?.id)?.color || "#FFF"
28
29
  };
29
30
  });
30
31
  const handleChange = data => {
31
32
  onChange(rowIndex, {
32
- [property]: data?.filter(f => f?.value)
33
+ [property]: data?.filter(f => f?.id)
33
34
  });
34
35
  };
35
36
  const handleUpdate = data => {
@@ -42,12 +43,12 @@ const MultiSelectType = props => {
42
43
  };
43
44
  onUpdateProperty(updateData);
44
45
  };
45
- return /*#__PURE__*/_jsx(MultiSelectWithPopover, {
46
+ return /*#__PURE__*/_jsx(SelectV1, {
46
47
  value: coloredValues,
47
48
  onChange: handleChange,
48
49
  onUpdate: handleUpdate,
49
50
  options: options,
50
- multiple: true,
51
+ selectMultiple: true,
51
52
  limitTags: 2,
52
53
  placeholder: label,
53
54
  disabled: readOnly,
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import { useDataView } from "../../Providers/DataViewProvider";
3
- import Select from "./Components/Select";
3
+ // import Select from "./Components/Select";
4
+ import SelectV1 from "./Components/SelectV1";
4
5
  import { jsx as _jsx } from "react/jsx-runtime";
5
6
  const SelectType = props => {
6
7
  const {
@@ -8,28 +9,47 @@ const SelectType = props => {
8
9
  property,
9
10
  value,
10
11
  options,
11
- readOnly
12
+ readOnly,
13
+ translation
12
14
  } = props;
13
15
  const {
14
- onChange
16
+ onChange,
17
+ onUpdateProperty
15
18
  } = useDataView();
19
+ const label = "";
16
20
  const coloredValues = [...(value || [])]?.map(m => {
17
21
  return {
18
22
  ...m,
19
- color: options?.find(f => f.value === m.value)?.color
23
+ color: (options || []).find(f => f?.value === m?.value || f?.id === m?.id)?.color || "#FFF"
20
24
  };
21
25
  });
22
26
  const handleChange = data => {
23
27
  onChange(rowIndex, {
24
- [property]: data?.filter(f => f?.value)
28
+ [property]: data?.filter(f => f?.id)
25
29
  });
26
30
  };
27
- return /*#__PURE__*/_jsx(Select, {
31
+ const handleUpdate = data => {
32
+ const updateData = {
33
+ "label": "Select",
34
+ "visible": true,
35
+ "key": property,
36
+ "type": "select",
37
+ "options": data
38
+ };
39
+ onUpdateProperty(updateData);
40
+ };
41
+ return /*#__PURE__*/_jsx(SelectV1, {
28
42
  value: coloredValues,
29
- onChange: handleChange,
30
43
  options: options,
31
- multiple: false,
32
- disabled: readOnly
44
+ selectMultiple: false,
45
+ disabled: readOnly,
46
+ limitTags: 2,
47
+ placeholder: label,
48
+ property: property,
49
+ wrapColumn: false,
50
+ onChange: handleChange,
51
+ onUpdate: handleUpdate,
52
+ translation: translation
33
53
  });
34
54
  };
35
55
  export default SelectType;
@@ -46,7 +46,8 @@ const AddOptions = props => {
46
46
  ...edit,
47
47
  options: [{
48
48
  value: value,
49
- color: getRandomColor()
49
+ color: getRandomColor(),
50
+ id: `multi_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 5)}`
50
51
  }, ...(options || [])]
51
52
  });
52
53
  setValue("");
@@ -19,6 +19,7 @@ const EditOption = props => {
19
19
  const [edit, setEdit] = useState({
20
20
  ...(mode?.edit || {})
21
21
  });
22
+ const [errorMessage, setErrorMessage] = useState("");
22
23
  const editData = useRef({
23
24
  ...edit
24
25
  });
@@ -31,10 +32,13 @@ const EditOption = props => {
31
32
  const selectedOption = edit?.options[optionIndex] || {};
32
33
  const pickerStyles = ColorPickerStyles(theme);
33
34
  const hideBackButton = edit?.hideBackButton || false;
35
+ const errorMessageRef = useRef(errorMessage);
36
+ useEffect(() => {
37
+ errorMessageRef.current = errorMessage;
38
+ }, [errorMessage]);
34
39
  useEffect(() => {
35
40
  return () => {
36
- // on un-mount update the option change
37
- if (editData?.current) {
41
+ if (editData?.current && !errorMessageRef.current) {
38
42
  delete editData?.current?.edited;
39
43
  onEvent("updateProperty", {
40
44
  ...editData?.current
@@ -43,18 +47,24 @@ const EditOption = props => {
43
47
  };
44
48
  }, []);
45
49
  const onChange = e => {
46
- const updatedOptions = edit?.options.map((m, i) => {
50
+ const {
51
+ name,
52
+ value,
53
+ delete: isDelete
54
+ } = e?.target || {};
55
+ const targetValue = value?.toLowerCase();
56
+ const updatedOptions = edit?.options?.map((m, i) => {
47
57
  if (i === edit?.optionIndex) {
48
58
  return {
49
59
  ...m,
50
- [e.target.name]: e?.target?.value
60
+ [name]: value
51
61
  };
52
62
  }
53
63
  return m;
54
64
  });
55
65
 
56
- // for delete
57
- if (edit?.optionIndex > -1 && e?.target?.delete) {
66
+ // If deleting the option
67
+ if (edit?.optionIndex > -1 && isDelete) {
58
68
  updatedOptions.splice(edit?.optionIndex, 1);
59
69
  }
60
70
  const latest = {
@@ -68,14 +78,28 @@ const EditOption = props => {
68
78
  ...latest,
69
79
  edited: true
70
80
  };
81
+ if (name === "value" && !value?.trim()) {
82
+ setErrorMessage("Option name must not be empty");
83
+ return;
84
+ }
85
+ const isDuplicate = name === "value" && targetValue && edit?.options?.some((op, i) => i !== edit?.optionIndex && op?.value?.toLowerCase?.() === targetValue);
86
+ if (isDuplicate) {
87
+ setErrorMessage("Option name must be unique");
88
+ return;
89
+ }
90
+ setErrorMessage(""); // Clear error on valid input
71
91
 
72
- // if delete go back
73
- if (e?.target?.delete) {
74
- onEvent("editProperty", {
75
- edit: {
76
- ...latest
77
- }
78
- });
92
+ // If delete go back
93
+ if (isDelete) {
94
+ if (hideBackButton) {
95
+ onClose();
96
+ } else {
97
+ onEvent("editProperty", {
98
+ edit: {
99
+ ...latest
100
+ }
101
+ });
102
+ }
79
103
  }
80
104
  };
81
105
  const onBack = () => {
@@ -137,7 +161,9 @@ const EditOption = props => {
137
161
  value: selectedOption?.value,
138
162
  onChange: onChange,
139
163
  fullWidth: true,
140
- placeholder: translation("Option Name")
164
+ placeholder: translation("Option Name"),
165
+ helperText: errorMessage,
166
+ error: !!errorMessage
141
167
  }),
142
168
  labelPlacement: "top"
143
169
  })
@@ -7,7 +7,7 @@ function getTextStyles(props) {
7
7
  color,
8
8
  ...rest
9
9
  } = props;
10
- const device = getDevice();
10
+ const device = getDevice(window.innerWidth);
11
11
  const fontS = typeof fontSize === "object" ? fontSize[device] : fontSize;
12
12
  return {
13
13
  ...rest,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flozy/editor",
3
- "version": "10.4.6",
3
+ "version": "10.4.8",
4
4
  "description": "An Editor for flozy app brain",
5
5
  "files": [
6
6
  "dist"