@perses-dev/static-list-variable-plugin 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,46 +1,225 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Autocomplete, TextField } from '@mui/material';
1
+ /* eslint-disable jsx-a11y/no-autofocus */ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Autocomplete, Chip, IconButton, TextField, Typography } from '@mui/material';
3
+ import { useCallback, useState } from 'react';
4
+ import PlusCircleIcon from 'mdi-material-ui/PlusCircle';
3
5
  function StaticListVariableOptionEditor(props) {
4
- const value = (props.value.values || []).map((v)=>{
5
- if (typeof v === 'string') {
6
- return v;
7
- } else {
8
- return v.value;
6
+ const { value: { values: variables = [] }, onChange } = props;
7
+ const [editModeOption, setEditModeOption] = useState('');
8
+ const onChangeHandler = useCallback((_, value)=>{
9
+ const newVariable = value.pop();
10
+ const valueExists = variables.map((v)=>{
11
+ return typeof v === 'string' ? v : v?.value || '';
12
+ }).some((v)=>v === newVariable);
13
+ if (valueExists) return;
14
+ onChange({
15
+ values: [
16
+ ...variables,
17
+ {
18
+ value: String(newVariable),
19
+ label: String(newVariable)
20
+ }
21
+ ]
22
+ });
23
+ }, [
24
+ onChange,
25
+ variables
26
+ ]);
27
+ const onPasteHandler = useCallback((e)=>{
28
+ const v = e.clipboardData.getData('text/plain');
29
+ if (v) {
30
+ const items = v.split(',').filter((i)=>{
31
+ const exists = variables.map((v)=>{
32
+ return v?.value || String(v);
33
+ }).some((v)=>v === i);
34
+ return !exists;
35
+ }).map((item)=>({
36
+ value: item.trim(),
37
+ label: ''
38
+ }));
39
+ onChange({
40
+ values: [
41
+ ...variables,
42
+ ...items
43
+ ]
44
+ });
45
+ e.preventDefault();
9
46
  }
10
- });
11
- const onChange = (__, value)=>{
12
- props.onChange({
13
- values: value.map((v)=>{
14
- return {
15
- value: v,
16
- label: v
47
+ }, [
48
+ onChange,
49
+ variables
50
+ ]);
51
+ const tagDeleteHandler = useCallback((option)=>{
52
+ const filteredVariables = variables.filter((v)=>!(v === option || v?.value === option));
53
+ onChange({
54
+ values: [
55
+ ...filteredVariables
56
+ ]
57
+ });
58
+ setEditModeOption('');
59
+ }, [
60
+ variables,
61
+ onChange
62
+ ]);
63
+ const renderTagsHandler = useCallback((tagValue)=>{
64
+ const updateVariableWithLabel = (optionKey, label)=>{
65
+ if (!optionKey || !label) return variables;
66
+ /* Prevent duplicate label */ const labelAlreadyExists = variables.filter((v)=>typeof v !== 'string').some((v)=>v?.label === label);
67
+ if (labelAlreadyExists) return variables;
68
+ return variables.map((v)=>{
69
+ if (typeof v === 'string') return v;
70
+ const variableOption = v;
71
+ if (variableOption.value !== optionKey) return variableOption;
72
+ const updatedVariableOption = {
73
+ label,
74
+ value: variableOption.value
17
75
  };
18
- })
76
+ return updatedVariableOption;
77
+ });
78
+ };
79
+ return tagValue.map((_, index)=>{
80
+ const foundVariable = variables[index];
81
+ if (!foundVariable) return null;
82
+ const labelObject = {
83
+ value: '',
84
+ label: ''
85
+ };
86
+ if (typeof foundVariable === 'string') {
87
+ /* value and label are identical */ labelObject.value = foundVariable;
88
+ labelObject.label = foundVariable;
89
+ } else {
90
+ labelObject.value = foundVariable.value;
91
+ labelObject.label = foundVariable.label || foundVariable.value;
92
+ }
93
+ /* The value and key are the same thing, they can be used interchangeably */ const optionKey = foundVariable?.value || foundVariable;
94
+ return /*#__PURE__*/ _jsx(Chip, {
95
+ sx: {
96
+ margin: '4px'
97
+ },
98
+ label: editModeOption !== optionKey ? /*#__PURE__*/ _jsxs("div", {
99
+ style: {
100
+ display: 'flex',
101
+ alignItems: 'center',
102
+ gap: '8px'
103
+ },
104
+ children: [
105
+ /*#__PURE__*/ _jsx(Typography, {
106
+ variant: "body2",
107
+ children: labelObject.value
108
+ }),
109
+ labelObject?.value !== labelObject?.label && /*#__PURE__*/ _jsx(Typography, {
110
+ variant: "body2",
111
+ sx: {
112
+ backgroundColor: (theme)=>theme.palette.grey[200],
113
+ color: (theme)=>theme.palette.text.primary,
114
+ padding: '4px 8px',
115
+ borderRadius: '4px',
116
+ fontWeight: 500
117
+ },
118
+ children: labelObject.label
119
+ }),
120
+ typeof foundVariable !== 'string' && labelObject.value === labelObject.label && /*#__PURE__*/ _jsx(IconButton, {
121
+ size: "small",
122
+ onClick: (e)=>{
123
+ e.stopPropagation();
124
+ setEditModeOption(optionKey);
125
+ },
126
+ sx: {
127
+ color: (theme)=>theme.palette.action.disabled
128
+ },
129
+ children: /*#__PURE__*/ _jsx(PlusCircleIcon, {
130
+ fontSize: "small"
131
+ })
132
+ })
133
+ ]
134
+ }) : /*#__PURE__*/ _jsxs("div", {
135
+ style: {
136
+ display: 'flex',
137
+ alignItems: 'center',
138
+ gap: '8px'
139
+ },
140
+ children: [
141
+ /*#__PURE__*/ _jsx("span", {
142
+ children: optionKey
143
+ }),
144
+ /*#__PURE__*/ _jsx(TextField, {
145
+ defaultValue: labelObject.label,
146
+ onBlur: (e)=>{
147
+ const { target: { value: input } } = e;
148
+ if (input) {
149
+ const updatedVariables = updateVariableWithLabel(optionKey, input);
150
+ onChange({
151
+ values: updatedVariables
152
+ });
153
+ }
154
+ setEditModeOption('');
155
+ },
156
+ onKeyDown: (e)=>{
157
+ if (e.key === 'Enter') {
158
+ const { value: input } = e.target;
159
+ const updatedVariables = updateVariableWithLabel(optionKey, input);
160
+ onChange({
161
+ values: updatedVariables
162
+ });
163
+ setEditModeOption('');
164
+ } else if (e.key === 'Escape') {
165
+ setEditModeOption('');
166
+ }
167
+ },
168
+ size: "small",
169
+ autoFocus: true,
170
+ sx: {
171
+ width: '100%',
172
+ padding: 0,
173
+ margin: 0,
174
+ backgroundColor: (theme)=>theme.palette.background.default,
175
+ '& .MuiInputBase-root': {
176
+ fontSize: '0.875rem',
177
+ padding: 0
178
+ },
179
+ '& .MuiOutlinedInput-notchedOutline': {
180
+ border: 'none'
181
+ }
182
+ },
183
+ inputProps: {
184
+ style: {
185
+ padding: 0
186
+ }
187
+ }
188
+ })
189
+ ]
190
+ }),
191
+ onDelete: ()=>{
192
+ tagDeleteHandler(optionKey);
193
+ }
194
+ }, optionKey);
19
195
  });
20
- };
196
+ }, [
197
+ variables,
198
+ tagDeleteHandler,
199
+ editModeOption,
200
+ onChange
201
+ ]);
21
202
  return /*#__PURE__*/ _jsx("div", {
22
203
  children: /*#__PURE__*/ _jsx(Autocomplete, {
23
- onPaste: (e)=>{
24
- // Append new values on paste
25
- const v = e.clipboardData.getData('text/plain');
26
- if (v) {
27
- const values = v.split(',');
28
- onChange(null, value.concat(values));
29
- e.preventDefault();
30
- }
31
- },
204
+ onPaste: onPasteHandler,
32
205
  multiple: true,
33
- value: value,
34
- onChange: onChange,
206
+ value: variables.map((vr)=>typeof vr === 'string' ? vr : vr.label || vr.value),
207
+ onChange: onChangeHandler,
35
208
  options: [],
36
209
  freeSolo: true,
37
210
  clearOnBlur: true,
38
211
  readOnly: props.isReadonly,
212
+ renderTags: renderTagsHandler,
39
213
  renderInput: (params)=>/*#__PURE__*/ _jsx(TextField, {
40
214
  ...params,
41
215
  label: "Values",
42
216
  placeholder: "Values",
43
- helperText: 'Type new value then press "Enter" to add.'
217
+ helperText: 'Type new value then press "Enter" to add. Optionally define a label by clicking on the "+" button.',
218
+ onKeyDown: (e)=>{
219
+ if (e.key === 'Backspace' && !params.inputProps.value) {
220
+ e.stopPropagation();
221
+ }
222
+ }
44
223
  })
45
224
  })
46
225
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/StaticListVariable.tsx"],"sourcesContent":["import { VariablePlugin, VariableOption, OptionsEditorProps } from '@perses-dev/plugin-system';\nimport { Autocomplete, TextField } from '@mui/material';\n\ntype StaticListOption = string | VariableOption;\n\ntype StaticListVariableOptions = {\n values: StaticListOption[];\n};\n\nfunction StaticListVariableOptionEditor(props: OptionsEditorProps<StaticListVariableOptions>) {\n const value = (props.value.values || []).map((v) => {\n if (typeof v === 'string') {\n return v;\n } else {\n return v.value;\n }\n });\n\n const onChange = (__: unknown, value: string[]) => {\n props.onChange({\n values: value.map((v) => {\n return { value: v, label: v };\n }),\n });\n };\n\n return (\n <div>\n <Autocomplete\n onPaste={(e) => {\n // Append new values on paste\n const v = e.clipboardData.getData('text/plain');\n if (v) {\n const values = v.split(',');\n onChange(null, value.concat(values));\n e.preventDefault();\n }\n }}\n multiple\n value={value}\n onChange={onChange}\n options={[]}\n freeSolo\n clearOnBlur\n readOnly={props.isReadonly}\n renderInput={(params) => (\n <TextField\n {...params}\n label=\"Values\"\n placeholder=\"Values\"\n helperText='Type new value then press \"Enter\" to add.'\n />\n )}\n />\n </div>\n );\n}\n\nexport const StaticListVariable: VariablePlugin<StaticListVariableOptions> = {\n getVariableOptions: async (spec) => {\n const values = spec.values?.map((v) => {\n if (typeof v === 'string') {\n return { label: v, value: v };\n }\n return v;\n });\n return {\n data: values,\n };\n },\n dependsOn: () => {\n return { variables: [] };\n },\n OptionsEditorComponent: StaticListVariableOptionEditor,\n createInitialOptions: () => ({ values: [] }),\n};\n"],"names":["Autocomplete","TextField","StaticListVariableOptionEditor","props","value","values","map","v","onChange","__","label","div","onPaste","e","clipboardData","getData","split","concat","preventDefault","multiple","options","freeSolo","clearOnBlur","readOnly","isReadonly","renderInput","params","placeholder","helperText","StaticListVariable","getVariableOptions","spec","data","dependsOn","variables","OptionsEditorComponent","createInitialOptions"],"mappings":";AACA,SAASA,YAAY,EAAEC,SAAS,QAAQ,gBAAgB;AAQxD,SAASC,+BAA+BC,KAAoD;IAC1F,MAAMC,QAAQ,AAACD,CAAAA,MAAMC,KAAK,CAACC,MAAM,IAAI,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC;QAC5C,IAAI,OAAOA,MAAM,UAAU;YACzB,OAAOA;QACT,OAAO;YACL,OAAOA,EAAEH,KAAK;QAChB;IACF;IAEA,MAAMI,WAAW,CAACC,IAAaL;QAC7BD,MAAMK,QAAQ,CAAC;YACbH,QAAQD,MAAME,GAAG,CAAC,CAACC;gBACjB,OAAO;oBAAEH,OAAOG;oBAAGG,OAAOH;gBAAE;YAC9B;QACF;IACF;IAEA,qBACE,KAACI;kBACC,cAAA,KAACX;YACCY,SAAS,CAACC;gBACR,6BAA6B;gBAC7B,MAAMN,IAAIM,EAAEC,aAAa,CAACC,OAAO,CAAC;gBAClC,IAAIR,GAAG;oBACL,MAAMF,SAASE,EAAES,KAAK,CAAC;oBACvBR,SAAS,MAAMJ,MAAMa,MAAM,CAACZ;oBAC5BQ,EAAEK,cAAc;gBAClB;YACF;YACAC,QAAQ;YACRf,OAAOA;YACPI,UAAUA;YACVY,SAAS,EAAE;YACXC,QAAQ;YACRC,WAAW;YACXC,UAAUpB,MAAMqB,UAAU;YAC1BC,aAAa,CAACC,uBACZ,KAACzB;oBACE,GAAGyB,MAAM;oBACVhB,OAAM;oBACNiB,aAAY;oBACZC,YAAW;;;;AAMvB;AAEA,OAAO,MAAMC,qBAAgE;IAC3EC,oBAAoB,OAAOC;QACzB,MAAM1B,SAAS0B,KAAK1B,MAAM,EAAEC,IAAI,CAACC;YAC/B,IAAI,OAAOA,MAAM,UAAU;gBACzB,OAAO;oBAAEG,OAAOH;oBAAGH,OAAOG;gBAAE;YAC9B;YACA,OAAOA;QACT;QACA,OAAO;YACLyB,MAAM3B;QACR;IACF;IACA4B,WAAW;QACT,OAAO;YAAEC,WAAW,EAAE;QAAC;IACzB;IACAC,wBAAwBjC;IACxBkC,sBAAsB,IAAO,CAAA;YAAE/B,QAAQ,EAAE;QAAC,CAAA;AAC5C,EAAE"}
1
+ {"version":3,"sources":["../../src/StaticListVariable.tsx"],"sourcesContent":["/* eslint-disable jsx-a11y/no-autofocus */\nimport { VariablePlugin, VariableOption, OptionsEditorProps } from '@perses-dev/plugin-system';\nimport { Autocomplete, Chip, IconButton, TextField, Typography } from '@mui/material';\nimport { useCallback, useState } from 'react';\nimport PlusCircleIcon from 'mdi-material-ui/PlusCircle';\n\ntype StaticListOption = string | VariableOption;\n\ntype StaticListVariableOptions = {\n values: StaticListOption[];\n};\n\nfunction StaticListVariableOptionEditor(props: OptionsEditorProps<StaticListVariableOptions>) {\n const {\n value: { values: variables = [] },\n onChange,\n } = props;\n\n const [editModeOption, setEditModeOption] = useState<string>('');\n\n const onChangeHandler = useCallback(\n (_: unknown, value: string[]): void => {\n const newVariable = value.pop();\n\n const valueExists = variables\n .map((v) => {\n return typeof v === 'string' ? v : (v as VariableOption)?.value || '';\n })\n .some((v) => v === newVariable);\n\n if (valueExists) return;\n\n onChange({\n values: [...variables, { value: String(newVariable), label: String(newVariable) }],\n });\n },\n [onChange, variables]\n );\n\n const onPasteHandler = useCallback(\n (e: React.ClipboardEvent<HTMLDivElement>) => {\n const v = e.clipboardData.getData('text/plain');\n if (v) {\n const items = v\n .split(',')\n .filter((i) => {\n const exists = variables\n .map((v) => {\n return (v as VariableOption)?.value || String(v);\n })\n .some((v) => v === i);\n return !exists;\n })\n .map((item) => ({ value: item.trim(), label: '' }));\n onChange({ values: [...variables, ...items] });\n e.preventDefault();\n }\n },\n [onChange, variables]\n );\n\n const tagDeleteHandler = useCallback(\n (option: string) => {\n const filteredVariables = variables.filter(\n (v) => !((v as string) === option || (v as VariableOption)?.value === option)\n );\n onChange({ values: [...filteredVariables] });\n setEditModeOption('');\n },\n [variables, onChange]\n );\n\n const renderTagsHandler = useCallback(\n (tagValue: string[]) => {\n const updateVariableWithLabel = (optionKey: string, label: string): Array<string | VariableOption> => {\n if (!optionKey || !label) return variables;\n /* Prevent duplicate label */\n const labelAlreadyExists = variables.filter((v) => typeof v !== 'string').some((v) => v?.label === label);\n if (labelAlreadyExists) return variables;\n\n return variables.map((v) => {\n if (typeof v === 'string') return v;\n const variableOption = v as VariableOption;\n if (variableOption!.value !== optionKey) return variableOption;\n const updatedVariableOption: VariableOption = {\n label,\n value: variableOption!.value,\n };\n return updatedVariableOption;\n });\n };\n\n return tagValue.map((_, index) => {\n const foundVariable = variables[index];\n if (!foundVariable) return null;\n\n const labelObject: { value: string; label: string } = { value: '', label: '' };\n\n if (typeof foundVariable === 'string') {\n /* value and label are identical */\n labelObject.value = foundVariable;\n labelObject.label = foundVariable;\n } else {\n labelObject.value = foundVariable.value;\n labelObject.label = foundVariable.label || foundVariable.value;\n }\n\n /* The value and key are the same thing, they can be used interchangeably */\n const optionKey = (foundVariable as VariableOption)?.value || (foundVariable as string);\n\n return (\n <Chip\n key={optionKey}\n sx={{ margin: '4px' }}\n label={\n editModeOption !== optionKey ? (\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>\n <Typography variant=\"body2\">{labelObject.value}</Typography>\n {labelObject?.value !== labelObject?.label && (\n <Typography\n variant=\"body2\"\n sx={{\n backgroundColor: (theme) => theme.palette.grey[200],\n color: (theme) => theme.palette.text.primary,\n padding: '4px 8px',\n borderRadius: '4px',\n fontWeight: 500,\n }}\n >\n {labelObject.label}\n </Typography>\n )}\n {/* Label can be added once and then no longer it will be editable */}\n {typeof foundVariable !== 'string' && labelObject.value === labelObject.label && (\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n setEditModeOption(optionKey);\n }}\n sx={{\n color: (theme) => theme.palette.action.disabled,\n }}\n >\n <PlusCircleIcon fontSize=\"small\" />\n </IconButton>\n )}\n </div>\n ) : (\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>\n <span>{optionKey}</span>\n <TextField\n defaultValue={labelObject.label}\n onBlur={(e) => {\n const {\n target: { value: input },\n } = e;\n if (input) {\n const updatedVariables = updateVariableWithLabel(optionKey, input);\n onChange({ values: updatedVariables });\n }\n setEditModeOption('');\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n const { value: input } = e.target as HTMLInputElement;\n const updatedVariables = updateVariableWithLabel(optionKey, input);\n onChange({ values: updatedVariables });\n setEditModeOption('');\n } else if (e.key === 'Escape') {\n setEditModeOption('');\n }\n }}\n size=\"small\"\n autoFocus\n sx={{\n width: '100%',\n padding: 0,\n margin: 0,\n backgroundColor: (theme) => theme.palette.background.default,\n '& .MuiInputBase-root': {\n fontSize: '0.875rem',\n padding: 0,\n },\n '& .MuiOutlinedInput-notchedOutline': {\n border: 'none',\n },\n }}\n inputProps={{\n style: {\n padding: 0,\n },\n }}\n />\n </div>\n )\n }\n onDelete={() => {\n tagDeleteHandler(optionKey);\n }}\n />\n );\n });\n },\n [variables, tagDeleteHandler, editModeOption, onChange]\n );\n\n return (\n <div>\n <Autocomplete\n onPaste={onPasteHandler}\n multiple\n value={variables.map((vr) => (typeof vr === 'string' ? vr : vr.label || vr.value))}\n onChange={onChangeHandler}\n options={[]}\n freeSolo\n clearOnBlur\n readOnly={props.isReadonly}\n renderTags={renderTagsHandler}\n renderInput={(params) => (\n <TextField\n {...params}\n label=\"Values\"\n placeholder=\"Values\"\n helperText='Type new value then press \"Enter\" to add. Optionally define a label by clicking on the \"+\" button.'\n onKeyDown={(e) => {\n if (e.key === 'Backspace' && !params.inputProps.value) {\n e.stopPropagation();\n }\n }}\n />\n )}\n />\n </div>\n );\n}\n\nexport const StaticListVariable: VariablePlugin<StaticListVariableOptions> = {\n getVariableOptions: async (spec) => {\n const values = spec.values?.map((v) => {\n if (typeof v === 'string') {\n return { label: v, value: v };\n }\n return v;\n });\n return {\n data: values,\n };\n },\n dependsOn: () => {\n return { variables: [] };\n },\n OptionsEditorComponent: StaticListVariableOptionEditor,\n createInitialOptions: () => ({ values: [] }),\n};\n"],"names":["Autocomplete","Chip","IconButton","TextField","Typography","useCallback","useState","PlusCircleIcon","StaticListVariableOptionEditor","props","value","values","variables","onChange","editModeOption","setEditModeOption","onChangeHandler","_","newVariable","pop","valueExists","map","v","some","String","label","onPasteHandler","e","clipboardData","getData","items","split","filter","i","exists","item","trim","preventDefault","tagDeleteHandler","option","filteredVariables","renderTagsHandler","tagValue","updateVariableWithLabel","optionKey","labelAlreadyExists","variableOption","updatedVariableOption","index","foundVariable","labelObject","sx","margin","div","style","display","alignItems","gap","variant","backgroundColor","theme","palette","grey","color","text","primary","padding","borderRadius","fontWeight","size","onClick","stopPropagation","action","disabled","fontSize","span","defaultValue","onBlur","target","input","updatedVariables","onKeyDown","key","autoFocus","width","background","default","border","inputProps","onDelete","onPaste","multiple","vr","options","freeSolo","clearOnBlur","readOnly","isReadonly","renderTags","renderInput","params","placeholder","helperText","StaticListVariable","getVariableOptions","spec","data","dependsOn","OptionsEditorComponent","createInitialOptions"],"mappings":"AAAA,wCAAwC;AAExC,SAASA,YAAY,EAAEC,IAAI,EAAEC,UAAU,EAAEC,SAAS,EAAEC,UAAU,QAAQ,gBAAgB;AACtF,SAASC,WAAW,EAAEC,QAAQ,QAAQ,QAAQ;AAC9C,OAAOC,oBAAoB,6BAA6B;AAQxD,SAASC,+BAA+BC,KAAoD;IAC1F,MAAM,EACJC,OAAO,EAAEC,QAAQC,YAAY,EAAE,EAAE,EACjCC,QAAQ,EACT,GAAGJ;IAEJ,MAAM,CAACK,gBAAgBC,kBAAkB,GAAGT,SAAiB;IAE7D,MAAMU,kBAAkBX,YACtB,CAACY,GAAYP;QACX,MAAMQ,cAAcR,MAAMS,GAAG;QAE7B,MAAMC,cAAcR,UACjBS,GAAG,CAAC,CAACC;YACJ,OAAO,OAAOA,MAAM,WAAWA,IAAI,AAACA,GAAsBZ,SAAS;QACrE,GACCa,IAAI,CAAC,CAACD,IAAMA,MAAMJ;QAErB,IAAIE,aAAa;QAEjBP,SAAS;YACPF,QAAQ;mBAAIC;gBAAW;oBAAEF,OAAOc,OAAON;oBAAcO,OAAOD,OAAON;gBAAa;aAAE;QACpF;IACF,GACA;QAACL;QAAUD;KAAU;IAGvB,MAAMc,iBAAiBrB,YACrB,CAACsB;QACC,MAAML,IAAIK,EAAEC,aAAa,CAACC,OAAO,CAAC;QAClC,IAAIP,GAAG;YACL,MAAMQ,QAAQR,EACXS,KAAK,CAAC,KACNC,MAAM,CAAC,CAACC;gBACP,MAAMC,SAAStB,UACZS,GAAG,CAAC,CAACC;oBACJ,OAAO,AAACA,GAAsBZ,SAASc,OAAOF;gBAChD,GACCC,IAAI,CAAC,CAACD,IAAMA,MAAMW;gBACrB,OAAO,CAACC;YACV,GACCb,GAAG,CAAC,CAACc,OAAU,CAAA;oBAAEzB,OAAOyB,KAAKC,IAAI;oBAAIX,OAAO;gBAAG,CAAA;YAClDZ,SAAS;gBAAEF,QAAQ;uBAAIC;uBAAckB;iBAAM;YAAC;YAC5CH,EAAEU,cAAc;QAClB;IACF,GACA;QAACxB;QAAUD;KAAU;IAGvB,MAAM0B,mBAAmBjC,YACvB,CAACkC;QACC,MAAMC,oBAAoB5B,UAAUoB,MAAM,CACxC,CAACV,IAAM,CAAE,CAAA,AAACA,MAAiBiB,UAAU,AAACjB,GAAsBZ,UAAU6B,MAAK;QAE7E1B,SAAS;YAAEF,QAAQ;mBAAI6B;aAAkB;QAAC;QAC1CzB,kBAAkB;IACpB,GACA;QAACH;QAAWC;KAAS;IAGvB,MAAM4B,oBAAoBpC,YACxB,CAACqC;QACC,MAAMC,0BAA0B,CAACC,WAAmBnB;YAClD,IAAI,CAACmB,aAAa,CAACnB,OAAO,OAAOb;YACjC,2BAA2B,GAC3B,MAAMiC,qBAAqBjC,UAAUoB,MAAM,CAAC,CAACV,IAAM,OAAOA,MAAM,UAAUC,IAAI,CAAC,CAACD,IAAMA,GAAGG,UAAUA;YACnG,IAAIoB,oBAAoB,OAAOjC;YAE/B,OAAOA,UAAUS,GAAG,CAAC,CAACC;gBACpB,IAAI,OAAOA,MAAM,UAAU,OAAOA;gBAClC,MAAMwB,iBAAiBxB;gBACvB,IAAIwB,eAAgBpC,KAAK,KAAKkC,WAAW,OAAOE;gBAChD,MAAMC,wBAAwC;oBAC5CtB;oBACAf,OAAOoC,eAAgBpC,KAAK;gBAC9B;gBACA,OAAOqC;YACT;QACF;QAEA,OAAOL,SAASrB,GAAG,CAAC,CAACJ,GAAG+B;YACtB,MAAMC,gBAAgBrC,SAAS,CAACoC,MAAM;YACtC,IAAI,CAACC,eAAe,OAAO;YAE3B,MAAMC,cAAgD;gBAAExC,OAAO;gBAAIe,OAAO;YAAG;YAE7E,IAAI,OAAOwB,kBAAkB,UAAU;gBACrC,iCAAiC,GACjCC,YAAYxC,KAAK,GAAGuC;gBACpBC,YAAYzB,KAAK,GAAGwB;YACtB,OAAO;gBACLC,YAAYxC,KAAK,GAAGuC,cAAcvC,KAAK;gBACvCwC,YAAYzB,KAAK,GAAGwB,cAAcxB,KAAK,IAAIwB,cAAcvC,KAAK;YAChE;YAEA,2EAA2E,GAC3E,MAAMkC,YAAY,AAACK,eAAkCvC,SAAUuC;YAE/D,qBACE,KAAChD;gBAECkD,IAAI;oBAAEC,QAAQ;gBAAM;gBACpB3B,OACEX,mBAAmB8B,0BACjB,MAACS;oBAAIC,OAAO;wBAAEC,SAAS;wBAAQC,YAAY;wBAAUC,KAAK;oBAAM;;sCAC9D,KAACrD;4BAAWsD,SAAQ;sCAASR,YAAYxC,KAAK;;wBAC7CwC,aAAaxC,UAAUwC,aAAazB,uBACnC,KAACrB;4BACCsD,SAAQ;4BACRP,IAAI;gCACFQ,iBAAiB,CAACC,QAAUA,MAAMC,OAAO,CAACC,IAAI,CAAC,IAAI;gCACnDC,OAAO,CAACH,QAAUA,MAAMC,OAAO,CAACG,IAAI,CAACC,OAAO;gCAC5CC,SAAS;gCACTC,cAAc;gCACdC,YAAY;4BACd;sCAEClB,YAAYzB,KAAK;;wBAIrB,OAAOwB,kBAAkB,YAAYC,YAAYxC,KAAK,KAAKwC,YAAYzB,KAAK,kBAC3E,KAACvB;4BACCmE,MAAK;4BACLC,SAAS,CAAC3C;gCACRA,EAAE4C,eAAe;gCACjBxD,kBAAkB6B;4BACpB;4BACAO,IAAI;gCACFY,OAAO,CAACH,QAAUA,MAAMC,OAAO,CAACW,MAAM,CAACC,QAAQ;4BACjD;sCAEA,cAAA,KAAClE;gCAAemE,UAAS;;;;mCAK/B,MAACrB;oBAAIC,OAAO;wBAAEC,SAAS;wBAAQC,YAAY;wBAAUC,KAAK;oBAAM;;sCAC9D,KAACkB;sCAAM/B;;sCACP,KAACzC;4BACCyE,cAAc1B,YAAYzB,KAAK;4BAC/BoD,QAAQ,CAAClD;gCACP,MAAM,EACJmD,QAAQ,EAAEpE,OAAOqE,KAAK,EAAE,EACzB,GAAGpD;gCACJ,IAAIoD,OAAO;oCACT,MAAMC,mBAAmBrC,wBAAwBC,WAAWmC;oCAC5DlE,SAAS;wCAAEF,QAAQqE;oCAAiB;gCACtC;gCACAjE,kBAAkB;4BACpB;4BACAkE,WAAW,CAACtD;gCACV,IAAIA,EAAEuD,GAAG,KAAK,SAAS;oCACrB,MAAM,EAAExE,OAAOqE,KAAK,EAAE,GAAGpD,EAAEmD,MAAM;oCACjC,MAAME,mBAAmBrC,wBAAwBC,WAAWmC;oCAC5DlE,SAAS;wCAAEF,QAAQqE;oCAAiB;oCACpCjE,kBAAkB;gCACpB,OAAO,IAAIY,EAAEuD,GAAG,KAAK,UAAU;oCAC7BnE,kBAAkB;gCACpB;4BACF;4BACAsD,MAAK;4BACLc,SAAS;4BACThC,IAAI;gCACFiC,OAAO;gCACPlB,SAAS;gCACTd,QAAQ;gCACRO,iBAAiB,CAACC,QAAUA,MAAMC,OAAO,CAACwB,UAAU,CAACC,OAAO;gCAC5D,wBAAwB;oCACtBZ,UAAU;oCACVR,SAAS;gCACX;gCACA,sCAAsC;oCACpCqB,QAAQ;gCACV;4BACF;4BACAC,YAAY;gCACVlC,OAAO;oCACLY,SAAS;gCACX;4BACF;;;;gBAKRuB,UAAU;oBACRnD,iBAAiBM;gBACnB;eAvFKA;QA0FX;IACF,GACA;QAAChC;QAAW0B;QAAkBxB;QAAgBD;KAAS;IAGzD,qBACE,KAACwC;kBACC,cAAA,KAACrD;YACC0F,SAAShE;YACTiE,QAAQ;YACRjF,OAAOE,UAAUS,GAAG,CAAC,CAACuE,KAAQ,OAAOA,OAAO,WAAWA,KAAKA,GAAGnE,KAAK,IAAImE,GAAGlF,KAAK;YAChFG,UAAUG;YACV6E,SAAS,EAAE;YACXC,QAAQ;YACRC,WAAW;YACXC,UAAUvF,MAAMwF,UAAU;YAC1BC,YAAYzD;YACZ0D,aAAa,CAACC,uBACZ,KAACjG;oBACE,GAAGiG,MAAM;oBACV3E,OAAM;oBACN4E,aAAY;oBACZC,YAAW;oBACXrB,WAAW,CAACtD;wBACV,IAAIA,EAAEuD,GAAG,KAAK,eAAe,CAACkB,OAAOZ,UAAU,CAAC9E,KAAK,EAAE;4BACrDiB,EAAE4C,eAAe;wBACnB;oBACF;;;;AAMZ;AAEA,OAAO,MAAMgC,qBAAgE;IAC3EC,oBAAoB,OAAOC;QACzB,MAAM9F,SAAS8F,KAAK9F,MAAM,EAAEU,IAAI,CAACC;YAC/B,IAAI,OAAOA,MAAM,UAAU;gBACzB,OAAO;oBAAEG,OAAOH;oBAAGZ,OAAOY;gBAAE;YAC9B;YACA,OAAOA;QACT;QACA,OAAO;YACLoF,MAAM/F;QACR;IACF;IACAgG,WAAW;QACT,OAAO;YAAE/F,WAAW,EAAE;QAAC;IACzB;IACAgG,wBAAwBpG;IACxBqG,sBAAsB,IAAO,CAAA;YAAElG,QAAQ,EAAE;QAAC,CAAA;AAC5C,EAAE"}
@@ -1,4 +1,4 @@
1
- "use strict";
1
+ /* eslint-disable jsx-a11y/no-autofocus */ "use strict";
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
@@ -10,47 +10,231 @@ Object.defineProperty(exports, "StaticListVariable", {
10
10
  });
11
11
  const _jsxruntime = require("react/jsx-runtime");
12
12
  const _material = require("@mui/material");
13
+ const _react = require("react");
14
+ const _PlusCircle = /*#__PURE__*/ _interop_require_default(require("mdi-material-ui/PlusCircle"));
15
+ function _interop_require_default(obj) {
16
+ return obj && obj.__esModule ? obj : {
17
+ default: obj
18
+ };
19
+ }
13
20
  function StaticListVariableOptionEditor(props) {
14
- const value = (props.value.values || []).map((v)=>{
15
- if (typeof v === 'string') {
16
- return v;
17
- } else {
18
- return v.value;
21
+ const { value: { values: variables = [] }, onChange } = props;
22
+ const [editModeOption, setEditModeOption] = (0, _react.useState)('');
23
+ const onChangeHandler = (0, _react.useCallback)((_, value)=>{
24
+ const newVariable = value.pop();
25
+ const valueExists = variables.map((v)=>{
26
+ return typeof v === 'string' ? v : v?.value || '';
27
+ }).some((v)=>v === newVariable);
28
+ if (valueExists) return;
29
+ onChange({
30
+ values: [
31
+ ...variables,
32
+ {
33
+ value: String(newVariable),
34
+ label: String(newVariable)
35
+ }
36
+ ]
37
+ });
38
+ }, [
39
+ onChange,
40
+ variables
41
+ ]);
42
+ const onPasteHandler = (0, _react.useCallback)((e)=>{
43
+ const v = e.clipboardData.getData('text/plain');
44
+ if (v) {
45
+ const items = v.split(',').filter((i)=>{
46
+ const exists = variables.map((v)=>{
47
+ return v?.value || String(v);
48
+ }).some((v)=>v === i);
49
+ return !exists;
50
+ }).map((item)=>({
51
+ value: item.trim(),
52
+ label: ''
53
+ }));
54
+ onChange({
55
+ values: [
56
+ ...variables,
57
+ ...items
58
+ ]
59
+ });
60
+ e.preventDefault();
19
61
  }
20
- });
21
- const onChange = (__, value)=>{
22
- props.onChange({
23
- values: value.map((v)=>{
24
- return {
25
- value: v,
26
- label: v
62
+ }, [
63
+ onChange,
64
+ variables
65
+ ]);
66
+ const tagDeleteHandler = (0, _react.useCallback)((option)=>{
67
+ const filteredVariables = variables.filter((v)=>!(v === option || v?.value === option));
68
+ onChange({
69
+ values: [
70
+ ...filteredVariables
71
+ ]
72
+ });
73
+ setEditModeOption('');
74
+ }, [
75
+ variables,
76
+ onChange
77
+ ]);
78
+ const renderTagsHandler = (0, _react.useCallback)((tagValue)=>{
79
+ const updateVariableWithLabel = (optionKey, label)=>{
80
+ if (!optionKey || !label) return variables;
81
+ /* Prevent duplicate label */ const labelAlreadyExists = variables.filter((v)=>typeof v !== 'string').some((v)=>v?.label === label);
82
+ if (labelAlreadyExists) return variables;
83
+ return variables.map((v)=>{
84
+ if (typeof v === 'string') return v;
85
+ const variableOption = v;
86
+ if (variableOption.value !== optionKey) return variableOption;
87
+ const updatedVariableOption = {
88
+ label,
89
+ value: variableOption.value
27
90
  };
28
- })
91
+ return updatedVariableOption;
92
+ });
93
+ };
94
+ return tagValue.map((_, index)=>{
95
+ const foundVariable = variables[index];
96
+ if (!foundVariable) return null;
97
+ const labelObject = {
98
+ value: '',
99
+ label: ''
100
+ };
101
+ if (typeof foundVariable === 'string') {
102
+ /* value and label are identical */ labelObject.value = foundVariable;
103
+ labelObject.label = foundVariable;
104
+ } else {
105
+ labelObject.value = foundVariable.value;
106
+ labelObject.label = foundVariable.label || foundVariable.value;
107
+ }
108
+ /* The value and key are the same thing, they can be used interchangeably */ const optionKey = foundVariable?.value || foundVariable;
109
+ return /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Chip, {
110
+ sx: {
111
+ margin: '4px'
112
+ },
113
+ label: editModeOption !== optionKey ? /*#__PURE__*/ (0, _jsxruntime.jsxs)("div", {
114
+ style: {
115
+ display: 'flex',
116
+ alignItems: 'center',
117
+ gap: '8px'
118
+ },
119
+ children: [
120
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Typography, {
121
+ variant: "body2",
122
+ children: labelObject.value
123
+ }),
124
+ labelObject?.value !== labelObject?.label && /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Typography, {
125
+ variant: "body2",
126
+ sx: {
127
+ backgroundColor: (theme)=>theme.palette.grey[200],
128
+ color: (theme)=>theme.palette.text.primary,
129
+ padding: '4px 8px',
130
+ borderRadius: '4px',
131
+ fontWeight: 500
132
+ },
133
+ children: labelObject.label
134
+ }),
135
+ typeof foundVariable !== 'string' && labelObject.value === labelObject.label && /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.IconButton, {
136
+ size: "small",
137
+ onClick: (e)=>{
138
+ e.stopPropagation();
139
+ setEditModeOption(optionKey);
140
+ },
141
+ sx: {
142
+ color: (theme)=>theme.palette.action.disabled
143
+ },
144
+ children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_PlusCircle.default, {
145
+ fontSize: "small"
146
+ })
147
+ })
148
+ ]
149
+ }) : /*#__PURE__*/ (0, _jsxruntime.jsxs)("div", {
150
+ style: {
151
+ display: 'flex',
152
+ alignItems: 'center',
153
+ gap: '8px'
154
+ },
155
+ children: [
156
+ /*#__PURE__*/ (0, _jsxruntime.jsx)("span", {
157
+ children: optionKey
158
+ }),
159
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.TextField, {
160
+ defaultValue: labelObject.label,
161
+ onBlur: (e)=>{
162
+ const { target: { value: input } } = e;
163
+ if (input) {
164
+ const updatedVariables = updateVariableWithLabel(optionKey, input);
165
+ onChange({
166
+ values: updatedVariables
167
+ });
168
+ }
169
+ setEditModeOption('');
170
+ },
171
+ onKeyDown: (e)=>{
172
+ if (e.key === 'Enter') {
173
+ const { value: input } = e.target;
174
+ const updatedVariables = updateVariableWithLabel(optionKey, input);
175
+ onChange({
176
+ values: updatedVariables
177
+ });
178
+ setEditModeOption('');
179
+ } else if (e.key === 'Escape') {
180
+ setEditModeOption('');
181
+ }
182
+ },
183
+ size: "small",
184
+ autoFocus: true,
185
+ sx: {
186
+ width: '100%',
187
+ padding: 0,
188
+ margin: 0,
189
+ backgroundColor: (theme)=>theme.palette.background.default,
190
+ '& .MuiInputBase-root': {
191
+ fontSize: '0.875rem',
192
+ padding: 0
193
+ },
194
+ '& .MuiOutlinedInput-notchedOutline': {
195
+ border: 'none'
196
+ }
197
+ },
198
+ inputProps: {
199
+ style: {
200
+ padding: 0
201
+ }
202
+ }
203
+ })
204
+ ]
205
+ }),
206
+ onDelete: ()=>{
207
+ tagDeleteHandler(optionKey);
208
+ }
209
+ }, optionKey);
29
210
  });
30
- };
211
+ }, [
212
+ variables,
213
+ tagDeleteHandler,
214
+ editModeOption,
215
+ onChange
216
+ ]);
31
217
  return /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
32
218
  children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Autocomplete, {
33
- onPaste: (e)=>{
34
- // Append new values on paste
35
- const v = e.clipboardData.getData('text/plain');
36
- if (v) {
37
- const values = v.split(',');
38
- onChange(null, value.concat(values));
39
- e.preventDefault();
40
- }
41
- },
219
+ onPaste: onPasteHandler,
42
220
  multiple: true,
43
- value: value,
44
- onChange: onChange,
221
+ value: variables.map((vr)=>typeof vr === 'string' ? vr : vr.label || vr.value),
222
+ onChange: onChangeHandler,
45
223
  options: [],
46
224
  freeSolo: true,
47
225
  clearOnBlur: true,
48
226
  readOnly: props.isReadonly,
227
+ renderTags: renderTagsHandler,
49
228
  renderInput: (params)=>/*#__PURE__*/ (0, _jsxruntime.jsx)(_material.TextField, {
50
229
  ...params,
51
230
  label: "Values",
52
231
  placeholder: "Values",
53
- helperText: 'Type new value then press "Enter" to add.'
232
+ helperText: 'Type new value then press "Enter" to add. Optionally define a label by clicking on the "+" button.',
233
+ onKeyDown: (e)=>{
234
+ if (e.key === 'Backspace' && !params.inputProps.value) {
235
+ e.stopPropagation();
236
+ }
237
+ }
54
238
  })
55
239
  })
56
240
  });
package/mf-manifest.json CHANGED
@@ -5,11 +5,11 @@
5
5
  "name": "StaticListVariable",
6
6
  "type": "app",
7
7
  "buildInfo": {
8
- "buildVersion": "0.6.0",
8
+ "buildVersion": "0.7.0",
9
9
  "buildName": "@perses-dev/static-list-variable-plugin"
10
10
  },
11
11
  "remoteEntry": {
12
- "name": "__mf/js/StaticListVariable.97d532c4.js",
12
+ "name": "__mf/js/StaticListVariable.fd29486a.js",
13
13
  "path": "",
14
14
  "type": "global"
15
15
  },
@@ -111,8 +111,8 @@
111
111
  "assets": {
112
112
  "js": {
113
113
  "sync": [
114
- "__mf/js/async/526.cb9b2a3c.js",
115
- "__mf/js/async/__federation_expose_StaticListVariable.3195ca52.js"
114
+ "__mf/js/async/122.f46c6fa7.js",
115
+ "__mf/js/async/__federation_expose_StaticListVariable.79f063b2.js"
116
116
  ],
117
117
  "async": []
118
118
  },
package/mf-stats.json CHANGED
@@ -5,11 +5,11 @@
5
5
  "name": "StaticListVariable",
6
6
  "type": "app",
7
7
  "buildInfo": {
8
- "buildVersion": "0.6.0",
8
+ "buildVersion": "0.7.0",
9
9
  "buildName": "@perses-dev/static-list-variable-plugin"
10
10
  },
11
11
  "remoteEntry": {
12
- "name": "__mf/js/StaticListVariable.97d532c4.js",
12
+ "name": "__mf/js/StaticListVariable.fd29486a.js",
13
13
  "path": "",
14
14
  "type": "global"
15
15
  },
@@ -112,7 +112,9 @@
112
112
  "sync": []
113
113
  }
114
114
  },
115
- "usedIn": []
115
+ "usedIn": [
116
+ "./StaticListVariable"
117
+ ]
116
118
  }
117
119
  ],
118
120
  "remotes": [],
@@ -121,13 +123,15 @@
121
123
  "path": "./StaticListVariable",
122
124
  "id": "StaticListVariable:StaticListVariable",
123
125
  "name": "StaticListVariable",
124
- "requires": [],
126
+ "requires": [
127
+ "react"
128
+ ],
125
129
  "file": "src/StaticListVariable.tsx",
126
130
  "assets": {
127
131
  "js": {
128
132
  "sync": [
129
- "__mf/js/async/526.cb9b2a3c.js",
130
- "__mf/js/async/__federation_expose_StaticListVariable.3195ca52.js"
133
+ "__mf/js/async/122.f46c6fa7.js",
134
+ "__mf/js/async/__federation_expose_StaticListVariable.79f063b2.js"
131
135
  ],
132
136
  "async": []
133
137
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@perses-dev/static-list-variable-plugin",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "homepage": "https://github.com/perses/plugins/blob/main/README.md",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,9 +30,9 @@
30
30
  "@emotion/react": "^11.7.1",
31
31
  "@emotion/styled": "^11.6.0",
32
32
  "@hookform/resolvers": "^3.2.0",
33
- "@perses-dev/components": "^0.52.0",
34
- "@perses-dev/core": "^0.52.0",
35
- "@perses-dev/plugin-system": "^0.52.0",
33
+ "@perses-dev/components": "^0.53.0-beta.1",
34
+ "@perses-dev/core": "^0.53.0-beta.1",
35
+ "@perses-dev/plugin-system": "^0.53.0-beta.1",
36
36
  "date-fns": "^4.1.0",
37
37
  "date-fns-tz": "^3.2.0",
38
38
  "echarts": "5.5.0",