@destyler/edit 0.0.7-beta.5 → 0.0.7-beta.9

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.js ADDED
@@ -0,0 +1,529 @@
1
+ import { createAnatomy } from "@destyler/anatomy";
2
+ import { ariaAttr, contains, createScope, dataAttr, isApple, isComposingEvent, raf } from "@destyler/dom";
3
+ import { trackInteractOutside } from "@destyler/interact-outside";
4
+ import { compact, createSplitProps, isEqual } from "@destyler/utils";
5
+ import { createMachine } from "@destyler/xstate";
6
+ import { createProps } from "@destyler/types";
7
+
8
+ //#region src/anatomy.ts
9
+ const anatomy = createAnatomy("edit").parts("root", "area", "label", "preview", "input", "editTrigger", "submitTrigger", "cancelTrigger", "control");
10
+ const parts = anatomy.build();
11
+
12
+ //#endregion
13
+ //#region src/dom.ts
14
+ const dom = createScope({
15
+ getRootId: (ctx) => ctx.ids?.root ?? `edit:${ctx.id}`,
16
+ getAreaId: (ctx) => ctx.ids?.area ?? `edit:${ctx.id}:area`,
17
+ getLabelId: (ctx) => ctx.ids?.label ?? `edit:${ctx.id}:label`,
18
+ getPreviewId: (ctx) => ctx.ids?.preview ?? `edit:${ctx.id}:preview`,
19
+ getInputId: (ctx) => ctx.ids?.input ?? `edit:${ctx.id}:input`,
20
+ getControlId: (ctx) => ctx.ids?.control ?? `edit:${ctx.id}:control`,
21
+ getSubmitTriggerId: (ctx) => ctx.ids?.submitTrigger ?? `edit:${ctx.id}:submit`,
22
+ getCancelTriggerId: (ctx) => ctx.ids?.cancelTrigger ?? `edit:${ctx.id}:cancel`,
23
+ getEditTriggerId: (ctx) => ctx.ids?.editTrigger ?? `edit:${ctx.id}:edit`,
24
+ getInputEl: (ctx) => dom.getById(ctx, dom.getInputId(ctx)),
25
+ getPreviewEl: (ctx) => dom.getById(ctx, dom.getPreviewId(ctx)),
26
+ getSubmitTriggerEl: (ctx) => dom.getById(ctx, dom.getSubmitTriggerId(ctx)),
27
+ getCancelTriggerEl: (ctx) => dom.getById(ctx, dom.getCancelTriggerId(ctx)),
28
+ getEditTriggerEl: (ctx) => dom.getById(ctx, dom.getEditTriggerId(ctx))
29
+ });
30
+
31
+ //#endregion
32
+ //#region src/connect.ts
33
+ function connect(state, send, normalize) {
34
+ const disabled = state.context.disabled;
35
+ const interactive = state.context.isInteractive;
36
+ const readOnly = state.context.readOnly;
37
+ const invalid = state.context.invalid;
38
+ const autoResize = state.context.autoResize;
39
+ const translations = state.context.translations;
40
+ const editing = state.matches("edit");
41
+ const placeholderProp = state.context.placeholder;
42
+ const placeholder = typeof placeholderProp === "string" ? {
43
+ edit: placeholderProp,
44
+ preview: placeholderProp
45
+ } : placeholderProp;
46
+ const value = state.context.value;
47
+ const empty = value.trim() === "";
48
+ const valueText = empty ? placeholder?.preview ?? "" : value;
49
+ return {
50
+ editing,
51
+ empty,
52
+ value,
53
+ valueText,
54
+ setValue(value$1) {
55
+ send({
56
+ type: "VALUE.SET",
57
+ value: value$1,
58
+ src: "setValue"
59
+ });
60
+ },
61
+ clearValue() {
62
+ send({
63
+ type: "VALUE.SET",
64
+ value: "",
65
+ src: "clearValue"
66
+ });
67
+ },
68
+ edit() {
69
+ if (!interactive) return;
70
+ send("EDIT");
71
+ },
72
+ cancel() {
73
+ if (!interactive) return;
74
+ send("CANCEL");
75
+ },
76
+ submit() {
77
+ if (!interactive) return;
78
+ send("SUBMIT");
79
+ },
80
+ getRootProps() {
81
+ return normalize.element({
82
+ ...parts.root.attrs,
83
+ id: dom.getRootId(state.context),
84
+ dir: state.context.dir
85
+ });
86
+ },
87
+ getAreaProps() {
88
+ return normalize.element({
89
+ ...parts.area.attrs,
90
+ "id": dom.getAreaId(state.context),
91
+ "dir": state.context.dir,
92
+ "style": autoResize ? { display: "inline-grid" } : void 0,
93
+ "data-focus": dataAttr(editing),
94
+ "data-disabled": dataAttr(disabled),
95
+ "data-placeholder-shown": dataAttr(empty)
96
+ });
97
+ },
98
+ getLabelProps() {
99
+ return normalize.label({
100
+ ...parts.label.attrs,
101
+ "id": dom.getLabelId(state.context),
102
+ "dir": state.context.dir,
103
+ "htmlFor": dom.getInputId(state.context),
104
+ "data-focus": dataAttr(editing),
105
+ "data-invalid": dataAttr(invalid),
106
+ onClick() {
107
+ if (editing) return;
108
+ const previewEl = dom.getPreviewEl(state.context);
109
+ previewEl?.focus({ preventScroll: true });
110
+ }
111
+ });
112
+ },
113
+ getInputProps() {
114
+ return normalize.input({
115
+ ...parts.input.attrs,
116
+ "dir": state.context.dir,
117
+ "aria-label": translations.input,
118
+ "name": state.context.name,
119
+ "form": state.context.form,
120
+ "id": dom.getInputId(state.context),
121
+ "hidden": autoResize ? void 0 : !editing,
122
+ "placeholder": placeholder?.edit,
123
+ "maxLength": state.context.maxLength,
124
+ "required": state.context.required,
125
+ "disabled": disabled,
126
+ "data-disabled": dataAttr(disabled),
127
+ "readOnly": readOnly,
128
+ "data-readonly": dataAttr(readOnly),
129
+ "aria-invalid": ariaAttr(invalid),
130
+ "data-invalid": dataAttr(invalid),
131
+ "data-autoresize": dataAttr(autoResize),
132
+ "defaultValue": value,
133
+ "size": autoResize ? 1 : void 0,
134
+ onChange(event) {
135
+ send({
136
+ type: "VALUE.SET",
137
+ src: "input.change",
138
+ value: event.currentTarget.value
139
+ });
140
+ },
141
+ onKeyDown(event) {
142
+ if (event.defaultPrevented) return;
143
+ if (isComposingEvent(event)) return;
144
+ const keyMap = {
145
+ Escape() {
146
+ send("CANCEL");
147
+ event.preventDefault();
148
+ },
149
+ Enter(event$1) {
150
+ if (!state.context.submitOnEnter) return;
151
+ const { localName } = event$1.currentTarget;
152
+ if (localName === "textarea") {
153
+ const submitMod = isApple() ? event$1.metaKey : event$1.ctrlKey;
154
+ if (!submitMod) return;
155
+ send({
156
+ type: "SUBMIT",
157
+ src: "keydown.enter"
158
+ });
159
+ return;
160
+ }
161
+ if (localName === "input" && !event$1.shiftKey && !event$1.metaKey) {
162
+ send({
163
+ type: "SUBMIT",
164
+ src: "keydown.enter"
165
+ });
166
+ event$1.preventDefault();
167
+ }
168
+ }
169
+ };
170
+ const exec = keyMap[event.key];
171
+ if (exec) exec(event);
172
+ },
173
+ "style": autoResize ? {
174
+ gridArea: "1 / 1 / auto / auto",
175
+ visibility: !editing ? "hidden" : void 0
176
+ } : void 0
177
+ });
178
+ },
179
+ getPreviewProps() {
180
+ return normalize.element({
181
+ "id": dom.getPreviewId(state.context),
182
+ ...parts.preview.attrs,
183
+ "dir": state.context.dir,
184
+ "data-placeholder-shown": dataAttr(empty),
185
+ "aria-readonly": ariaAttr(readOnly),
186
+ "data-readonly": dataAttr(disabled),
187
+ "data-disabled": dataAttr(disabled),
188
+ "aria-disabled": ariaAttr(disabled),
189
+ "aria-invalid": ariaAttr(invalid),
190
+ "data-invalid": dataAttr(invalid),
191
+ "aria-label": translations.edit,
192
+ "data-autoresize": dataAttr(autoResize),
193
+ "children": valueText,
194
+ "hidden": autoResize ? void 0 : editing,
195
+ "tabIndex": interactive ? 0 : void 0,
196
+ onClick() {
197
+ if (!interactive) return;
198
+ if (state.context.activationMode !== "click") return;
199
+ send({
200
+ type: "EDIT",
201
+ src: "click"
202
+ });
203
+ },
204
+ onFocus() {
205
+ if (!interactive) return;
206
+ if (state.context.activationMode !== "focus") return;
207
+ send({
208
+ type: "EDIT",
209
+ src: "focus"
210
+ });
211
+ },
212
+ onDoubleClick(event) {
213
+ if (event.defaultPrevented) return;
214
+ if (!interactive) return;
215
+ if (state.context.activationMode !== "dblclick") return;
216
+ send({
217
+ type: "EDIT",
218
+ src: "dblclick"
219
+ });
220
+ },
221
+ "style": autoResize ? {
222
+ whiteSpace: "pre",
223
+ userSelect: "none",
224
+ gridArea: "1 / 1 / auto / auto",
225
+ visibility: editing ? "hidden" : void 0,
226
+ overflow: "hidden",
227
+ textOverflow: "ellipsis"
228
+ } : void 0
229
+ });
230
+ },
231
+ getEditTriggerProps() {
232
+ return normalize.button({
233
+ ...parts.editTrigger.attrs,
234
+ "id": dom.getEditTriggerId(state.context),
235
+ "dir": state.context.dir,
236
+ "aria-label": translations.edit,
237
+ "hidden": editing,
238
+ "type": "button",
239
+ "disabled": disabled,
240
+ onClick(event) {
241
+ if (event.defaultPrevented) return;
242
+ if (!interactive) return;
243
+ send({
244
+ type: "EDIT",
245
+ src: "edit.click"
246
+ });
247
+ }
248
+ });
249
+ },
250
+ getControlProps() {
251
+ return normalize.element({
252
+ id: dom.getControlId(state.context),
253
+ ...parts.control.attrs,
254
+ dir: state.context.dir
255
+ });
256
+ },
257
+ getSubmitTriggerProps() {
258
+ return normalize.button({
259
+ ...parts.submitTrigger.attrs,
260
+ "dir": state.context.dir,
261
+ "id": dom.getSubmitTriggerId(state.context),
262
+ "aria-label": translations.submit,
263
+ "hidden": !editing,
264
+ "disabled": disabled,
265
+ "type": "button",
266
+ onClick(event) {
267
+ if (event.defaultPrevented) return;
268
+ if (!interactive) return;
269
+ send({
270
+ type: "SUBMIT",
271
+ src: "submit.click"
272
+ });
273
+ }
274
+ });
275
+ },
276
+ getCancelTriggerProps() {
277
+ return normalize.button({
278
+ ...parts.cancelTrigger.attrs,
279
+ "dir": state.context.dir,
280
+ "aria-label": translations.cancel,
281
+ "id": dom.getCancelTriggerId(state.context),
282
+ "hidden": !editing,
283
+ "type": "button",
284
+ "disabled": disabled,
285
+ onClick(event) {
286
+ if (event.defaultPrevented) return;
287
+ if (!interactive) return;
288
+ send({
289
+ type: "CANCEL",
290
+ src: "cancel.click"
291
+ });
292
+ }
293
+ });
294
+ }
295
+ };
296
+ }
297
+
298
+ //#endregion
299
+ //#region src/machine.ts
300
+ const submitOnEnter = (ctx) => ["both", "enter"].includes(ctx.submitMode);
301
+ const submitOnBlur = (ctx) => ["both", "blur"].includes(ctx.submitMode);
302
+ const sync = { value: (ctx) => {
303
+ const inputEl = dom.getInputEl(ctx);
304
+ dom.setValue(inputEl, ctx.value);
305
+ } };
306
+ const invoke = { change(ctx) {
307
+ ctx.onValueChange?.({ value: ctx.value });
308
+ sync.value(ctx);
309
+ } };
310
+ const set = { value(ctx, value) {
311
+ if (isEqual(ctx.value, value)) return;
312
+ ctx.value = value;
313
+ invoke.change(ctx);
314
+ } };
315
+ function machine(userContext) {
316
+ const ctx = compact(userContext);
317
+ return createMachine({
318
+ id: "editable",
319
+ initial: ctx.edit ? "edit" : "preview",
320
+ entry: ctx.edit ? ["focusInput"] : void 0,
321
+ context: {
322
+ activationMode: "focus",
323
+ submitMode: "both",
324
+ value: "",
325
+ previousValue: "",
326
+ selectOnFocus: true,
327
+ disabled: false,
328
+ readOnly: false,
329
+ ...ctx,
330
+ translations: {
331
+ input: "editable input",
332
+ edit: "edit",
333
+ submit: "submit",
334
+ cancel: "cancel",
335
+ ...ctx.translations
336
+ }
337
+ },
338
+ watch: {
339
+ value: ["syncInputValue"],
340
+ edit: ["toggleEditing"]
341
+ },
342
+ computed: {
343
+ submitOnEnter,
344
+ submitOnBlur,
345
+ isInteractive: (ctx$1) => !(ctx$1.disabled || ctx$1.readOnly)
346
+ },
347
+ on: { "VALUE.SET": { actions: "setValue" } },
348
+ states: {
349
+ preview: {
350
+ entry: ["blurInputIfNeeded"],
351
+ on: {
352
+ "CONTROLLED.EDIT": {
353
+ target: "edit",
354
+ actions: ["setPreviousValue", "focusInput"]
355
+ },
356
+ "EDIT": [{
357
+ guard: "isEditControlled",
358
+ actions: ["invokeOnEdit"]
359
+ }, {
360
+ target: "edit",
361
+ actions: [
362
+ "setPreviousValue",
363
+ "focusInput",
364
+ "invokeOnEdit"
365
+ ]
366
+ }]
367
+ }
368
+ },
369
+ edit: {
370
+ activities: ["trackInteractOutside"],
371
+ on: {
372
+ "CONTROLLED.PREVIEW": [{
373
+ guard: "isSubmitEvent",
374
+ target: "preview",
375
+ actions: [
376
+ "setPreviousValue",
377
+ "restoreFocus",
378
+ "invokeOnSubmit"
379
+ ]
380
+ }, {
381
+ target: "preview",
382
+ actions: [
383
+ "revertValue",
384
+ "restoreFocus",
385
+ "invokeOnCancel"
386
+ ]
387
+ }],
388
+ "CANCEL": [{
389
+ guard: "isEditControlled",
390
+ actions: ["invokeOnPreview"]
391
+ }, {
392
+ target: "preview",
393
+ actions: [
394
+ "revertValue",
395
+ "restoreFocus",
396
+ "invokeOnCancel",
397
+ "invokeOnPreview"
398
+ ]
399
+ }],
400
+ "SUBMIT": [{
401
+ guard: "isEditControlled",
402
+ actions: ["invokeOnPreview"]
403
+ }, {
404
+ target: "preview",
405
+ actions: [
406
+ "setPreviousValue",
407
+ "restoreFocus",
408
+ "invokeOnSubmit",
409
+ "invokeOnPreview"
410
+ ]
411
+ }]
412
+ }
413
+ }
414
+ }
415
+ }, {
416
+ guards: {
417
+ isEditControlled: (ctx$1) => !!ctx$1["edit.controlled"],
418
+ isSubmitEvent: (_ctx, evt) => evt.previousEvent?.type === "SUBMIT"
419
+ },
420
+ activities: { trackInteractOutside(ctx$1, _evt, { send }) {
421
+ return trackInteractOutside(dom.getInputEl(ctx$1), {
422
+ exclude(target) {
423
+ const ignore = [dom.getCancelTriggerEl(ctx$1), dom.getSubmitTriggerEl(ctx$1)];
424
+ return ignore.some((el) => contains(el, target));
425
+ },
426
+ onFocusOutside: ctx$1.onFocusOutside,
427
+ onPointerDownOutside: ctx$1.onPointerDownOutside,
428
+ onInteractOutside(event) {
429
+ ctx$1.onInteractOutside?.(event);
430
+ if (event.defaultPrevented) return;
431
+ const { focusable } = event.detail;
432
+ send({
433
+ type: submitOnBlur(ctx$1) ? "SUBMIT" : "CANCEL",
434
+ src: "interact-outside",
435
+ focusable
436
+ });
437
+ }
438
+ });
439
+ } },
440
+ actions: {
441
+ restoreFocus(ctx$1, evt) {
442
+ if (evt.focusable) return;
443
+ raf(() => {
444
+ const finalEl = ctx$1.finalFocusEl?.() ?? dom.getEditTriggerEl(ctx$1);
445
+ finalEl?.focus({ preventScroll: true });
446
+ });
447
+ },
448
+ focusInput(ctx$1) {
449
+ raf(() => {
450
+ const inputEl = dom.getInputEl(ctx$1);
451
+ if (!inputEl) return;
452
+ if (ctx$1.selectOnFocus) inputEl.select();
453
+ else inputEl.focus({ preventScroll: true });
454
+ });
455
+ },
456
+ invokeOnCancel(ctx$1) {
457
+ ctx$1.onValueRevert?.({ value: ctx$1.previousValue });
458
+ },
459
+ invokeOnSubmit(ctx$1) {
460
+ ctx$1.onValueCommit?.({ value: ctx$1.value });
461
+ },
462
+ invokeOnEdit(ctx$1) {
463
+ ctx$1.onEditChange?.({ edit: true });
464
+ },
465
+ invokeOnPreview(ctx$1) {
466
+ ctx$1.onEditChange?.({ edit: false });
467
+ },
468
+ toggleEditing(ctx$1, evt, { send }) {
469
+ send({
470
+ type: ctx$1.edit ? "CONTROLLED.EDIT" : "CONTROLLED.PREVIEW",
471
+ previousEvent: evt
472
+ });
473
+ },
474
+ syncInputValue(ctx$1) {
475
+ sync.value(ctx$1);
476
+ },
477
+ setValue(ctx$1, evt) {
478
+ const value = ctx$1.maxLength != null ? evt.value.slice(0, ctx$1.maxLength) : evt.value;
479
+ set.value(ctx$1, value);
480
+ },
481
+ setPreviousValue(ctx$1) {
482
+ ctx$1.previousValue = ctx$1.value;
483
+ },
484
+ revertValue(ctx$1) {
485
+ set.value(ctx$1, ctx$1.previousValue);
486
+ },
487
+ blurInputIfNeeded(ctx$1) {
488
+ dom.getInputEl(ctx$1)?.blur();
489
+ }
490
+ }
491
+ });
492
+ }
493
+
494
+ //#endregion
495
+ //#region src/props.ts
496
+ const props = createProps()([
497
+ "activationMode",
498
+ "autoResize",
499
+ "dir",
500
+ "disabled",
501
+ "finalFocusEl",
502
+ "form",
503
+ "getRootNode",
504
+ "id",
505
+ "ids",
506
+ "invalid",
507
+ "maxLength",
508
+ "name",
509
+ "onEditChange",
510
+ "onFocusOutside",
511
+ "onInteractOutside",
512
+ "onPointerDownOutside",
513
+ "onValueChange",
514
+ "onValueCommit",
515
+ "onValueRevert",
516
+ "placeholder",
517
+ "readOnly",
518
+ "required",
519
+ "selectOnFocus",
520
+ "edit",
521
+ "edit.controlled",
522
+ "submitMode",
523
+ "translations",
524
+ "value"
525
+ ]);
526
+ const splitProps = createSplitProps(props);
527
+
528
+ //#endregion
529
+ export { anatomy, connect, machine, props, splitProps };
package/package.json CHANGED
@@ -1,38 +1,32 @@
1
1
  {
2
2
  "name": "@destyler/edit",
3
3
  "type": "module",
4
- "version": "0.0.7-beta.5",
4
+ "version": "0.0.7-beta.9",
5
5
  "description": "a component that allows users to edit text in place.",
6
6
  "author": "Elone Hoo <elonehoo@gmail.com>",
7
7
  "license": "MIT",
8
8
  "exports": {
9
- ".": {
10
- "types": "./dist/index.d.mts",
11
- "default": "./dist/index.mjs"
12
- },
13
- "./*": "./*"
9
+ ".": "./dist/index.js"
14
10
  },
15
- "main": "dist/index.cjs",
16
- "module": "dist/index.mjs",
11
+ "main": "dist/index.js",
12
+ "module": "dist/index.js",
17
13
  "types": "dist/index.d.ts",
18
14
  "files": [
19
15
  "*.d.ts",
20
16
  "dist"
21
17
  ],
22
18
  "dependencies": {
23
- "@zag-js/anatomy": "^0.82.1",
24
- "@zag-js/core": "^0.82.1",
25
- "@zag-js/dom-event": "^0.80.0",
26
- "@zag-js/dom-query": "^0.82.1",
27
- "@zag-js/form-utils": "^0.81.1",
28
- "@zag-js/interact-outside": "^0.82.1",
29
- "@zag-js/types": "^0.82.1",
30
- "@zag-js/utils": "^0.82.1"
19
+ "@destyler/dom": "0.0.7-beta.9",
20
+ "@destyler/interact-outside": "0.0.7-beta.9",
21
+ "@destyler/types": "0.0.7-beta.9",
22
+ "@destyler/anatomy": "0.0.7-beta.9",
23
+ "@destyler/xstate": "0.0.7-beta.9",
24
+ "@destyler/utils": "0.0.7-beta.9"
31
25
  },
32
26
  "scripts": {
33
27
  "clear": "rimraf ./dist",
34
28
  "clear:node_modules": "rimraf ./node_modules",
35
- "build": "pnpm run clear && unbuild",
36
- "stub": "unbuild --stub"
29
+ "build": "pnpm run clear && tsdown",
30
+ "watch": "tsdown --watch"
37
31
  }
38
32
  }