@pdfme/schemas 5.0.0 → 5.1.1-dev.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/cjs/src/date/date.js +54 -0
- package/dist/cjs/src/date/date.js.map +1 -0
- package/dist/cjs/src/date/dateTime.js +96 -0
- package/dist/cjs/src/date/dateTime.js.map +1 -0
- package/dist/cjs/src/date/helper.js +232 -0
- package/dist/cjs/src/date/helper.js.map +1 -0
- package/dist/cjs/src/date/time.js +25 -0
- package/dist/cjs/src/date/time.js.map +1 -0
- package/dist/cjs/src/date/types.js +3 -0
- package/dist/cjs/src/date/types.js.map +1 -0
- package/dist/cjs/src/index.js +9 -1
- package/dist/cjs/src/index.js.map +1 -1
- package/dist/cjs/src/select/index.js +162 -0
- package/dist/cjs/src/select/index.js.map +1 -0
- package/dist/cjs/src/text/extraFormatter.js.map +1 -1
- package/dist/cjs/src/text/uiRender.js +6 -4
- package/dist/cjs/src/text/uiRender.js.map +1 -1
- package/dist/esm/src/date/date.js +52 -0
- package/dist/esm/src/date/date.js.map +1 -0
- package/dist/esm/src/date/dateTime.js +94 -0
- package/dist/esm/src/date/dateTime.js.map +1 -0
- package/dist/esm/src/date/helper.js +225 -0
- package/dist/esm/src/date/helper.js.map +1 -0
- package/dist/esm/src/date/time.js +23 -0
- package/dist/esm/src/date/time.js.map +1 -0
- package/dist/esm/src/date/types.js +2 -0
- package/dist/esm/src/date/types.js.map +1 -0
- package/dist/esm/src/index.js +5 -1
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/select/index.js +157 -0
- package/dist/esm/src/select/index.js.map +1 -0
- package/dist/esm/src/text/extraFormatter.js.map +1 -1
- package/dist/esm/src/text/uiRender.js +3 -3
- package/dist/esm/src/text/uiRender.js.map +1 -1
- package/dist/types/src/date/date.d.ts +2 -0
- package/dist/types/src/date/dateTime.d.ts +2 -0
- package/dist/types/src/date/helper.d.ts +9 -0
- package/dist/types/src/date/time.d.ts +2 -0
- package/dist/types/src/date/types.d.ts +11 -0
- package/dist/types/src/index.d.ts +5 -1
- package/dist/types/src/select/index.d.ts +7 -0
- package/dist/types/src/text/extraFormatter.d.ts +7 -2
- package/dist/types/src/text/uiRender.d.ts +4 -0
- package/package.json +3 -1
- package/src/date/date.ts +58 -0
- package/src/date/dateTime.ts +100 -0
- package/src/date/helper.ts +281 -0
- package/src/date/time.ts +29 -0
- package/src/date/types.ts +12 -0
- package/src/index.ts +8 -0
- package/src/select/index.ts +197 -0
- package/src/text/extraFormatter.ts +7 -4
- package/src/text/uiRender.ts +4 -4
@@ -0,0 +1,281 @@
|
|
1
|
+
import type * as CSS from 'csstype';
|
2
|
+
import AirDatepicker, { AirDatepickerLocale } from 'air-datepicker';
|
3
|
+
import localeEn from 'air-datepicker/locale/en';
|
4
|
+
import localeZh from 'air-datepicker/locale/zh';
|
5
|
+
import localeJa from 'air-datepicker/locale/ja';
|
6
|
+
import localeKo from 'air-datepicker/locale/ko';
|
7
|
+
import localeAr from 'air-datepicker/locale/ar';
|
8
|
+
import localeTh from 'air-datepicker/locale/th';
|
9
|
+
import localePl from 'air-datepicker/locale/pl';
|
10
|
+
import localeIt from 'air-datepicker/locale/it';
|
11
|
+
import localeDe from 'air-datepicker/locale/de';
|
12
|
+
import localeEs from 'air-datepicker/locale/es';
|
13
|
+
import localeFr from 'air-datepicker/locale/fr';
|
14
|
+
import 'air-datepicker/air-datepicker.css';
|
15
|
+
import { format } from 'date-fns';
|
16
|
+
import { enUS, zhCN, ja, ko, ar, th, pl, it, de, es, fr, Locale } from 'date-fns/locale';
|
17
|
+
import {
|
18
|
+
Lang,
|
19
|
+
Plugin,
|
20
|
+
getDefaultFont,
|
21
|
+
getFallbackFontName,
|
22
|
+
DEFAULT_FONT_NAME,
|
23
|
+
PropPanelSchema,
|
24
|
+
} from '@pdfme/common';
|
25
|
+
import text from '../text';
|
26
|
+
import { DEFAULT_OPACITY, HEX_COLOR_PATTERN } from '../constants.js';
|
27
|
+
import { mapVerticalAlignToFlex, getBackgroundColor } from '../text/uiRender';
|
28
|
+
import { getFontKitFont, getBrowserVerticalFontAdjustments } from '../text/helper.js';
|
29
|
+
import {
|
30
|
+
DEFAULT_FONT_SIZE,
|
31
|
+
DEFAULT_ALIGNMENT,
|
32
|
+
VERTICAL_ALIGN_MIDDLE,
|
33
|
+
DEFAULT_LINE_HEIGHT,
|
34
|
+
DEFAULT_CHARACTER_SPACING,
|
35
|
+
DEFAULT_FONT_COLOR,
|
36
|
+
} from '../text/constants.js';
|
37
|
+
import { DateSchema } from './types';
|
38
|
+
import { getExtraFormatterSchema, Formatter } from '../text/extraFormatter';
|
39
|
+
import { isEditable } from '../utils';
|
40
|
+
|
41
|
+
const getDateFnsLocale = (lang: Lang): Locale | undefined =>
|
42
|
+
({ en: enUS, zh: zhCN, ja, ko, ar, th, pl, it, de, es, fr }[lang]);
|
43
|
+
|
44
|
+
const getAirDatepickerLocale = (lang: Lang): AirDatepickerLocale | undefined =>
|
45
|
+
({
|
46
|
+
en: localeEn,
|
47
|
+
zh: localeZh,
|
48
|
+
ja: localeJa,
|
49
|
+
ko: localeKo,
|
50
|
+
ar: localeAr,
|
51
|
+
th: localeTh,
|
52
|
+
pl: localePl,
|
53
|
+
it: localeIt,
|
54
|
+
de: localeDe,
|
55
|
+
es: localeEs,
|
56
|
+
fr: localeFr,
|
57
|
+
}[lang]);
|
58
|
+
|
59
|
+
export const getPlugin = ({
|
60
|
+
type,
|
61
|
+
defaultFormat,
|
62
|
+
icon,
|
63
|
+
formatsByLang,
|
64
|
+
}: {
|
65
|
+
type: 'date' | 'time' | 'dateTime';
|
66
|
+
defaultFormat: string;
|
67
|
+
icon: string;
|
68
|
+
formatsByLang: Record<Lang, string[]>;
|
69
|
+
}) => {
|
70
|
+
const plugin: Plugin<DateSchema> = {
|
71
|
+
ui: async (arg) => {
|
72
|
+
const { schema, value, onChange, rootElement, mode, options, i18n, _cache } = arg;
|
73
|
+
|
74
|
+
const beforeRemoveEvent = new Event('beforeRemove');
|
75
|
+
rootElement.dispatchEvent(beforeRemoveEvent);
|
76
|
+
|
77
|
+
const font = options?.font || getDefaultFont();
|
78
|
+
const fontKitFont = await getFontKitFont(schema.fontName, font, _cache);
|
79
|
+
|
80
|
+
const { topAdj, bottomAdj } = getBrowserVerticalFontAdjustments(
|
81
|
+
fontKitFont,
|
82
|
+
schema.fontSize ?? DEFAULT_FONT_SIZE,
|
83
|
+
DEFAULT_LINE_HEIGHT,
|
84
|
+
VERTICAL_ALIGN_MIDDLE
|
85
|
+
);
|
86
|
+
|
87
|
+
const topAdjustment = topAdj.toString();
|
88
|
+
const bottomAdjustment = bottomAdj.toString();
|
89
|
+
const textStyle: CSS.Properties = {
|
90
|
+
fontFamily: schema.fontName ? `'${schema.fontName}'` : 'inherit',
|
91
|
+
color: schema.fontColor ?? DEFAULT_FONT_COLOR,
|
92
|
+
fontSize: `${schema.fontSize ?? DEFAULT_FONT_SIZE}pt`,
|
93
|
+
letterSpacing: `${schema.characterSpacing ?? DEFAULT_CHARACTER_SPACING}pt`,
|
94
|
+
textAlign: schema.alignment ?? DEFAULT_ALIGNMENT,
|
95
|
+
backgroundColor: getBackgroundColor(value, schema),
|
96
|
+
|
97
|
+
margin: '0',
|
98
|
+
padding: '0',
|
99
|
+
border: 'none',
|
100
|
+
outline: 'none',
|
101
|
+
width: `${schema.width}mm`,
|
102
|
+
height: `${schema.height}mm`,
|
103
|
+
display: 'flex',
|
104
|
+
flexDirection: 'column',
|
105
|
+
justifyContent: mapVerticalAlignToFlex(VERTICAL_ALIGN_MIDDLE),
|
106
|
+
paddingTop: `${topAdjustment}px`,
|
107
|
+
marginBottom: `${bottomAdjustment}px`,
|
108
|
+
position: 'relative',
|
109
|
+
};
|
110
|
+
|
111
|
+
const input = document.createElement('input');
|
112
|
+
|
113
|
+
Object.assign(input.style, textStyle);
|
114
|
+
|
115
|
+
const commitChange = ({ datepicker }: { datepicker: AirDatepicker<HTMLInputElement> }) => {
|
116
|
+
if (onChange) {
|
117
|
+
const date = datepicker.selectedDates;
|
118
|
+
const fmt =
|
119
|
+
type === 'time' ? 'HH:mm' : type === 'date' ? 'yyyy/MM/dd' : 'yyyy/MM/dd HH:mm';
|
120
|
+
const d = Array.isArray(date) ? date[0] : date || '';
|
121
|
+
const value = d ? format(d, fmt) : '';
|
122
|
+
onChange({ key: 'content', value });
|
123
|
+
}
|
124
|
+
};
|
125
|
+
|
126
|
+
const airDatepicker = new AirDatepicker(input, {
|
127
|
+
locale: getAirDatepickerLocale(options.lang || 'en'),
|
128
|
+
selectedDates: [schema.type === 'time' ? new Date(`2021-01-01T${value}`) : new Date(value)],
|
129
|
+
dateFormat: (date) => (schema.format ? format(date, schema.format) : ''),
|
130
|
+
timepicker: type !== 'date',
|
131
|
+
onlyTimepicker: type === 'time',
|
132
|
+
buttons: [
|
133
|
+
'clear',
|
134
|
+
{
|
135
|
+
content: i18n('close'),
|
136
|
+
onClick: (datepicker) => {
|
137
|
+
datepicker.hide();
|
138
|
+
commitChange({ datepicker });
|
139
|
+
},
|
140
|
+
},
|
141
|
+
],
|
142
|
+
onSelect: ({ datepicker }) => {
|
143
|
+
mode !== 'designer' && commitChange({ datepicker });
|
144
|
+
},
|
145
|
+
onShow: () => {
|
146
|
+
input.disabled = !isEditable(mode, schema);
|
147
|
+
},
|
148
|
+
});
|
149
|
+
|
150
|
+
rootElement.addEventListener('beforeRemove', () => {
|
151
|
+
if (mode === 'designer') {
|
152
|
+
airDatepicker.destroy();
|
153
|
+
}
|
154
|
+
});
|
155
|
+
input.addEventListener('click', () => {
|
156
|
+
if (mode === 'designer') {
|
157
|
+
airDatepicker.show();
|
158
|
+
}
|
159
|
+
});
|
160
|
+
|
161
|
+
rootElement.appendChild(input);
|
162
|
+
},
|
163
|
+
pdf: (arg) => {
|
164
|
+
const { schema, value, options } = arg;
|
165
|
+
if (!value) return void 0;
|
166
|
+
const lang = (options.lang || 'en') as Lang;
|
167
|
+
const locale = getDateFnsLocale(lang);
|
168
|
+
const date = schema.type === 'time' ? new Date(`2021-01-01T${value}`) : new Date(value);
|
169
|
+
const formattedValue = format(date, schema.format, { locale });
|
170
|
+
return text.pdf(
|
171
|
+
Object.assign(arg, {
|
172
|
+
value: formattedValue,
|
173
|
+
schema: {
|
174
|
+
...schema,
|
175
|
+
verticalAlignment: VERTICAL_ALIGN_MIDDLE,
|
176
|
+
lineHeight: DEFAULT_LINE_HEIGHT,
|
177
|
+
},
|
178
|
+
})
|
179
|
+
);
|
180
|
+
},
|
181
|
+
propPanel: {
|
182
|
+
schema: ({ options, i18n }) => {
|
183
|
+
const font = options.font || { [DEFAULT_FONT_NAME]: { data: '', fallback: true } };
|
184
|
+
const lang = options.lang || 'en';
|
185
|
+
const locale = getDateFnsLocale(lang);
|
186
|
+
|
187
|
+
const fontNames = Object.keys(font);
|
188
|
+
const fallbackFontName = getFallbackFontName(font);
|
189
|
+
|
190
|
+
const formatter = getExtraFormatterSchema(i18n);
|
191
|
+
formatter.buttons = formatter.buttons.filter(
|
192
|
+
(button) => button.key === Formatter.ALIGNMENT
|
193
|
+
);
|
194
|
+
|
195
|
+
const currentDate = new Date();
|
196
|
+
|
197
|
+
const dateSchema: Record<string, PropPanelSchema> = {
|
198
|
+
format: {
|
199
|
+
title: i18n('schemas.date.format'),
|
200
|
+
type: 'string',
|
201
|
+
widget: 'select',
|
202
|
+
props: {
|
203
|
+
options: formatsByLang[lang].map((formatString) => ({
|
204
|
+
label: `${formatString} (${format(currentDate, formatString, { locale })})`,
|
205
|
+
value: formatString,
|
206
|
+
})),
|
207
|
+
},
|
208
|
+
span: 24,
|
209
|
+
},
|
210
|
+
fontName: {
|
211
|
+
title: i18n('schemas.text.fontName'),
|
212
|
+
type: 'string',
|
213
|
+
widget: 'select',
|
214
|
+
default: fallbackFontName,
|
215
|
+
props: { options: fontNames.map((name) => ({ label: name, value: name })) },
|
216
|
+
span: 12,
|
217
|
+
},
|
218
|
+
fontSize: {
|
219
|
+
title: i18n('schemas.text.size'),
|
220
|
+
type: 'number',
|
221
|
+
widget: 'inputNumber',
|
222
|
+
span: 6,
|
223
|
+
props: { min: 0 },
|
224
|
+
},
|
225
|
+
characterSpacing: {
|
226
|
+
title: i18n('schemas.text.spacing'),
|
227
|
+
type: 'number',
|
228
|
+
widget: 'inputNumber',
|
229
|
+
span: 6,
|
230
|
+
props: { min: 0 },
|
231
|
+
},
|
232
|
+
formatter,
|
233
|
+
fontColor: {
|
234
|
+
title: i18n('schemas.textColor'),
|
235
|
+
type: 'string',
|
236
|
+
widget: 'color',
|
237
|
+
rules: [
|
238
|
+
{
|
239
|
+
pattern: HEX_COLOR_PATTERN,
|
240
|
+
message: i18n('validation.hexColor'),
|
241
|
+
},
|
242
|
+
],
|
243
|
+
},
|
244
|
+
backgroundColor: {
|
245
|
+
title: i18n('schemas.bgColor'),
|
246
|
+
type: 'string',
|
247
|
+
widget: 'color',
|
248
|
+
rules: [
|
249
|
+
{
|
250
|
+
pattern: HEX_COLOR_PATTERN,
|
251
|
+
message: i18n('validation.hexColor'),
|
252
|
+
},
|
253
|
+
],
|
254
|
+
},
|
255
|
+
};
|
256
|
+
|
257
|
+
return dateSchema;
|
258
|
+
},
|
259
|
+
defaultSchema: {
|
260
|
+
name: '',
|
261
|
+
format: defaultFormat,
|
262
|
+
type,
|
263
|
+
content: format(new Date(), defaultFormat),
|
264
|
+
position: { x: 0, y: 0 },
|
265
|
+
width: 70,
|
266
|
+
height: 10,
|
267
|
+
rotate: 0,
|
268
|
+
alignment: DEFAULT_ALIGNMENT,
|
269
|
+
fontSize: DEFAULT_FONT_SIZE,
|
270
|
+
characterSpacing: DEFAULT_CHARACTER_SPACING,
|
271
|
+
fontColor: DEFAULT_FONT_COLOR,
|
272
|
+
fontName: undefined,
|
273
|
+
backgroundColor: '',
|
274
|
+
opacity: DEFAULT_OPACITY,
|
275
|
+
},
|
276
|
+
},
|
277
|
+
icon,
|
278
|
+
};
|
279
|
+
|
280
|
+
return plugin;
|
281
|
+
};
|
package/src/date/time.ts
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
import { Lang } from '@pdfme/common';
|
2
|
+
import { getPlugin } from './helper';
|
3
|
+
|
4
|
+
const type = 'time';
|
5
|
+
|
6
|
+
const defaultFormat = 'HH:mm';
|
7
|
+
|
8
|
+
const formatsByLang: Record<Lang, string[]> = {
|
9
|
+
en: [defaultFormat, 'hh:mm a', 'HH:mm:ss', 'hh:mm:ss a'],
|
10
|
+
zh: [defaultFormat, 'HH时mm分', 'HH:mm:ss'],
|
11
|
+
ja: [defaultFormat, 'HH時mm分', 'HH:mm:ss'],
|
12
|
+
ko: [defaultFormat, 'a h시 mm분', 'HH:mm:ss'],
|
13
|
+
ar: [defaultFormat, 'hh:mm a', 'HH:mm:ss'],
|
14
|
+
th: [defaultFormat, 'HH.mm', 'HH:mm:ss'],
|
15
|
+
pl: [defaultFormat, 'HH:mm:ss'],
|
16
|
+
it: [defaultFormat, 'HH.mm', 'HH:mm:ss'],
|
17
|
+
de: [defaultFormat, 'HH.mm', 'HH:mm:ss'],
|
18
|
+
es: [defaultFormat, 'hh:mm a', 'HH:mm:ss'],
|
19
|
+
fr: [defaultFormat, 'HH:mm:ss'],
|
20
|
+
};
|
21
|
+
|
22
|
+
const icon =
|
23
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" ' +
|
24
|
+
'viewBox="0 0 24 24" fill="none" stroke="currentColor" ' +
|
25
|
+
'stroke-width="2" stroke-linecap="round" stroke-linejoin="round" ' +
|
26
|
+
'class="lucide lucide-clock"><circle cx="12" cy="12" r="10"/>' +
|
27
|
+
'<polyline points="12 6 12 12 16 14"/></svg>';
|
28
|
+
|
29
|
+
export default getPlugin({ type, defaultFormat, icon, formatsByLang });
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { Schema } from '@pdfme/common';
|
2
|
+
import { ALIGNMENT } from '../text/types';
|
3
|
+
|
4
|
+
export interface DateSchema extends Schema {
|
5
|
+
format: string;
|
6
|
+
fontName?: string;
|
7
|
+
alignment: ALIGNMENT;
|
8
|
+
fontSize: number;
|
9
|
+
characterSpacing: number;
|
10
|
+
fontColor: string;
|
11
|
+
backgroundColor: string;
|
12
|
+
}
|
package/src/index.ts
CHANGED
@@ -6,6 +6,10 @@ import barcodes from './barcodes/index.js';
|
|
6
6
|
import line from './shapes/line.js';
|
7
7
|
import table from './tables/index.js';
|
8
8
|
import { rectangle, ellipse } from './shapes/rectAndEllipse.js';
|
9
|
+
import dateTime from './date/dateTime.js';
|
10
|
+
import date from './date/date.js';
|
11
|
+
import time from './date/time.js';
|
12
|
+
import select from './select/index.js';
|
9
13
|
|
10
14
|
const builtInPlugins = { Text: text };
|
11
15
|
|
@@ -21,4 +25,8 @@ export {
|
|
21
25
|
line,
|
22
26
|
rectangle,
|
23
27
|
ellipse,
|
28
|
+
dateTime,
|
29
|
+
date,
|
30
|
+
time,
|
31
|
+
select,
|
24
32
|
};
|
@@ -0,0 +1,197 @@
|
|
1
|
+
import type * as CSS from 'csstype';
|
2
|
+
import { propPanel as parentPropPanel } from '../text/propPanel';
|
3
|
+
import { Plugin, PropPanelWidgetProps, SchemaForUI } from '@pdfme/common';
|
4
|
+
import text from '../text';
|
5
|
+
import { TextSchema } from '../text/types';
|
6
|
+
|
7
|
+
const selectIcon =
|
8
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>';
|
9
|
+
|
10
|
+
interface Select extends TextSchema {
|
11
|
+
options: string[];
|
12
|
+
}
|
13
|
+
|
14
|
+
const addOptions = (props: PropPanelWidgetProps) => {
|
15
|
+
const { rootElement, changeSchemas, activeSchema, i18n } = props;
|
16
|
+
|
17
|
+
rootElement.style.width = '100%';
|
18
|
+
|
19
|
+
const selectSchema = activeSchema as SchemaForUI & Select;
|
20
|
+
const currentOptions = selectSchema.options ? [...selectSchema.options] : [];
|
21
|
+
|
22
|
+
const inputStyle = {
|
23
|
+
width: '100%',
|
24
|
+
padding: '6.25px 11px',
|
25
|
+
border: '1px solid #ccc',
|
26
|
+
borderRadius: '4px',
|
27
|
+
};
|
28
|
+
|
29
|
+
const buttonStyle = { border: 'none', borderRadius: '4px', cursor: 'pointer' };
|
30
|
+
|
31
|
+
const updateSchemas = () => {
|
32
|
+
changeSchemas([
|
33
|
+
{ key: 'options', value: currentOptions, schemaId: activeSchema.id },
|
34
|
+
{ key: 'content', value: currentOptions[0] || '', schemaId: activeSchema.id },
|
35
|
+
]);
|
36
|
+
};
|
37
|
+
|
38
|
+
const formContainer = document.createElement('div');
|
39
|
+
Object.assign(formContainer.style, {
|
40
|
+
width: '100%',
|
41
|
+
display: 'flex',
|
42
|
+
alignItems: 'center',
|
43
|
+
marginBottom: '10px',
|
44
|
+
});
|
45
|
+
|
46
|
+
const input = document.createElement('input');
|
47
|
+
input.type = 'text';
|
48
|
+
input.placeholder = i18n('schemas.select.optionPlaceholder');
|
49
|
+
Object.assign(input.style, inputStyle, { marginRight: '10px' });
|
50
|
+
|
51
|
+
const addButton = document.createElement('button');
|
52
|
+
addButton.textContent = '+';
|
53
|
+
Object.assign(addButton.style, buttonStyle, {
|
54
|
+
width: '25px',
|
55
|
+
height: '25px',
|
56
|
+
padding: '4px 8px',
|
57
|
+
});
|
58
|
+
|
59
|
+
addButton.addEventListener('click', () => {
|
60
|
+
const newValue = input.value.trim();
|
61
|
+
if (newValue) {
|
62
|
+
currentOptions.push(newValue);
|
63
|
+
updateSchemas();
|
64
|
+
renderOptions();
|
65
|
+
input.value = '';
|
66
|
+
}
|
67
|
+
});
|
68
|
+
|
69
|
+
formContainer.appendChild(input);
|
70
|
+
formContainer.appendChild(addButton);
|
71
|
+
|
72
|
+
const optionsList = document.createElement('ul');
|
73
|
+
Object.assign(optionsList.style, { listStyle: 'none', padding: '0' });
|
74
|
+
|
75
|
+
const renderOptions = () => {
|
76
|
+
optionsList.innerHTML = '';
|
77
|
+
currentOptions.forEach((option, index) => {
|
78
|
+
const li = document.createElement('li');
|
79
|
+
Object.assign(li.style, { display: 'flex', alignItems: 'center', marginBottom: '5px' });
|
80
|
+
|
81
|
+
const optionInput = document.createElement('input');
|
82
|
+
optionInput.type = 'text';
|
83
|
+
optionInput.value = option;
|
84
|
+
Object.assign(optionInput.style, inputStyle, { marginRight: '10px' });
|
85
|
+
|
86
|
+
optionInput.addEventListener('change', () => {
|
87
|
+
currentOptions[index] = optionInput.value;
|
88
|
+
updateSchemas();
|
89
|
+
});
|
90
|
+
|
91
|
+
const removeButton = document.createElement('button');
|
92
|
+
removeButton.textContent = 'x';
|
93
|
+
Object.assign(removeButton.style, buttonStyle, { padding: '4px 8px' });
|
94
|
+
|
95
|
+
removeButton.addEventListener('click', () => {
|
96
|
+
currentOptions.splice(index, 1);
|
97
|
+
updateSchemas();
|
98
|
+
renderOptions();
|
99
|
+
});
|
100
|
+
|
101
|
+
li.appendChild(optionInput);
|
102
|
+
li.appendChild(removeButton);
|
103
|
+
optionsList.appendChild(li);
|
104
|
+
});
|
105
|
+
};
|
106
|
+
|
107
|
+
rootElement.appendChild(formContainer);
|
108
|
+
rootElement.appendChild(optionsList);
|
109
|
+
|
110
|
+
renderOptions();
|
111
|
+
};
|
112
|
+
|
113
|
+
const schema: Plugin<Select> = {
|
114
|
+
ui: async (arg) => {
|
115
|
+
const { schema, value, onChange, rootElement, mode } = arg;
|
116
|
+
await text.ui(Object.assign(arg, { mode: 'viewer' }));
|
117
|
+
|
118
|
+
if (mode !== 'viewer' && !(mode === 'form' && schema.readOnly)) {
|
119
|
+
const buttonWidth = 30;
|
120
|
+
const selectButton = document.createElement('button');
|
121
|
+
selectButton.innerHTML = selectIcon;
|
122
|
+
const selectButtonStyle: CSS.Properties = {
|
123
|
+
position: 'absolute',
|
124
|
+
zIndex: -1,
|
125
|
+
right: `-${buttonWidth}px`,
|
126
|
+
top: '0',
|
127
|
+
padding: '0',
|
128
|
+
margin: '0',
|
129
|
+
cursor: 'pointer',
|
130
|
+
height: `${buttonWidth}px`,
|
131
|
+
width: `${buttonWidth}px`,
|
132
|
+
};
|
133
|
+
Object.assign(selectButton.style, selectButtonStyle);
|
134
|
+
|
135
|
+
rootElement.appendChild(selectButton);
|
136
|
+
|
137
|
+
const selectElement = document.createElement('select');
|
138
|
+
const selectElementStyle: CSS.Properties = {
|
139
|
+
opacity: '0',
|
140
|
+
position: 'absolute',
|
141
|
+
width: `calc(100% + ${buttonWidth}px)`,
|
142
|
+
height: '100%',
|
143
|
+
top: '0',
|
144
|
+
left: '0',
|
145
|
+
appearance: 'initial',
|
146
|
+
};
|
147
|
+
Object.assign(selectElement.style, selectElementStyle);
|
148
|
+
selectElement.value = value;
|
149
|
+
|
150
|
+
selectElement.addEventListener('change', (e) => {
|
151
|
+
if (onChange && e.target instanceof HTMLSelectElement) {
|
152
|
+
onChange && onChange({ key: 'content', value: e.target.value });
|
153
|
+
}
|
154
|
+
});
|
155
|
+
|
156
|
+
selectElement.innerHTML = schema.options
|
157
|
+
.map(
|
158
|
+
(option) =>
|
159
|
+
`<option value="${option}" ${option === value ? 'selected' : ''}>${option}</option>`
|
160
|
+
)
|
161
|
+
.join('');
|
162
|
+
rootElement.appendChild(selectElement);
|
163
|
+
}
|
164
|
+
},
|
165
|
+
pdf: text.pdf,
|
166
|
+
propPanel: {
|
167
|
+
...text.propPanel,
|
168
|
+
widgets: { ...parentPropPanel.widgets, addOptions },
|
169
|
+
schema: (propPanelProps: Omit<PropPanelWidgetProps, 'rootElement'>) => {
|
170
|
+
if (typeof parentPropPanel.schema !== 'function') {
|
171
|
+
throw Error('Oops, is text schema no longer a function?');
|
172
|
+
}
|
173
|
+
|
174
|
+
return {
|
175
|
+
...parentPropPanel.schema(propPanelProps),
|
176
|
+
'-------': { type: 'void', widget: 'Divider' },
|
177
|
+
|
178
|
+
optionsContainer: {
|
179
|
+
title: (propPanelProps as PropPanelWidgetProps).i18n('schemas.select.options'),
|
180
|
+
type: 'string',
|
181
|
+
widget: 'Card',
|
182
|
+
span: 24,
|
183
|
+
properties: { options: { widget: 'addOptions', span: 24 } },
|
184
|
+
},
|
185
|
+
};
|
186
|
+
},
|
187
|
+
defaultSchema: {
|
188
|
+
...text.propPanel.defaultSchema,
|
189
|
+
type: 'select',
|
190
|
+
content: 'option1',
|
191
|
+
options: ['option1', 'option2'],
|
192
|
+
},
|
193
|
+
},
|
194
|
+
icon: selectIcon,
|
195
|
+
};
|
196
|
+
|
197
|
+
export default schema;
|
@@ -35,14 +35,17 @@ interface GroupButtonString {
|
|
35
35
|
key: Formatter;
|
36
36
|
icon: string;
|
37
37
|
type: 'select';
|
38
|
-
value:
|
38
|
+
value: string;
|
39
39
|
}
|
40
40
|
|
41
41
|
export type GroupButton = GroupButtonBoolean | GroupButtonString;
|
42
42
|
|
43
|
-
export function getExtraFormatterSchema(
|
44
|
-
|
45
|
-
|
43
|
+
export function getExtraFormatterSchema(i18n: (key: keyof Dict | string) => string): {
|
44
|
+
title: string;
|
45
|
+
widget: string;
|
46
|
+
buttons: GroupButton[];
|
47
|
+
span: number;
|
48
|
+
} {
|
46
49
|
const buttons: GroupButton[] = [
|
47
50
|
{ key: Formatter.STRIKETHROUGH, icon: TextStrikethroughIcon, type: 'boolean' },
|
48
51
|
{ key: Formatter.UNDERLINE, icon: TextUnderlineIcon, type: 'boolean' },
|
package/src/text/uiRender.ts
CHANGED
@@ -228,7 +228,7 @@ export const buildStyledTextContainer = async (arg: UIRenderProps<TextSchema>, v
|
|
228
228
|
};
|
229
229
|
|
230
230
|
const textBlock = document.createElement('div');
|
231
|
-
textBlock.id = 'text-' + schema.id;
|
231
|
+
textBlock.id = 'text-' + String(schema.id);
|
232
232
|
Object.assign(textBlock.style, textBlockStyle);
|
233
233
|
|
234
234
|
container.appendChild(textBlock);
|
@@ -265,7 +265,7 @@ export const makeElementPlainTextContentEditable = (element: HTMLElement) => {
|
|
265
265
|
});
|
266
266
|
};
|
267
267
|
|
268
|
-
const mapVerticalAlignToFlex = (verticalAlignmentValue: string | undefined) => {
|
268
|
+
export const mapVerticalAlignToFlex = (verticalAlignmentValue: string | undefined) => {
|
269
269
|
switch (verticalAlignmentValue) {
|
270
270
|
case VERTICAL_ALIGN_TOP:
|
271
271
|
return 'flex-start';
|
@@ -277,7 +277,7 @@ const mapVerticalAlignToFlex = (verticalAlignmentValue: string | undefined) => {
|
|
277
277
|
return 'flex-start';
|
278
278
|
};
|
279
279
|
|
280
|
-
const getBackgroundColor = (value: string, schema:
|
280
|
+
export const getBackgroundColor = (value: string, schema: { backgroundColor?: string }) => {
|
281
281
|
if (!value || !schema.backgroundColor) return 'transparent';
|
282
|
-
return schema.backgroundColor
|
282
|
+
return schema.backgroundColor;
|
283
283
|
};
|