@jini-ai/admin 0.4.5 → 0.4.7
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/CHANGELOG.md +4 -0
- package/dist/agent-plugins/package-file-tree.d.ts +3 -1
- package/dist/agent-plugins/package-file-tree.js +7 -7
- package/dist/comments/controllers/comment-queue.controller.d.ts +5 -1
- package/dist/comments/controllers/comment-queue.controller.js +2 -2
- package/dist/comments/react/hooks/use-comment-queue.hooks.js +14 -3
- package/dist/consent/react/ConsentSettingsPanel.d.ts +27 -0
- package/dist/consent/react/ConsentSettingsPanel.js +24 -0
- package/dist/consent/react/index.d.ts +3 -0
- package/dist/consent/react/index.js +2 -0
- package/dist/consent/react/settings-panel.hooks.d.ts +19 -0
- package/dist/consent/react/settings-panel.hooks.js +30 -0
- package/dist/core/ports/forms.d.ts +23 -3
- package/dist/core/ports/forms.js +3 -3
- package/dist/core/ports/seo.d.ts +17 -0
- package/dist/forms/adapters/http.d.ts +5 -2
- package/dist/forms/adapters/http.js +7 -2
- package/dist/forms/controllers/field-attributes-dialog.controller.d.ts +59 -1
- package/dist/forms/controllers/field-attributes-dialog.controller.js +27 -2
- package/dist/forms/field-settings.d.ts +142 -0
- package/dist/forms/field-settings.js +327 -0
- package/dist/forms/form-editor-dirty.d.ts +42 -0
- package/dist/forms/form-editor-dirty.js +25 -0
- package/dist/forms/forms.module.d.ts +72 -0
- package/dist/forms/html-rules.d.ts +41 -3
- package/dist/forms/html-rules.js +118 -17
- package/dist/forms/messages.en.d.ts +72 -0
- package/dist/forms/messages.en.js +73 -0
- package/dist/forms/react/components/FieldAttributesDialog.js +20 -19
- package/dist/forms/react/components/FieldSettingsSection.d.ts +19 -0
- package/dist/forms/react/components/FieldSettingsSection.js +21 -0
- package/dist/forms/react/components/FormFieldsEditor.js +7 -8
- package/dist/forms/react/components/FormSubmissionDetail.js +5 -2
- package/dist/forms/react/hooks/forms-view.hooks.d.ts +75 -5
- package/dist/forms/react/hooks/forms-view.hooks.js +46 -2
- package/dist/forms/react/hooks/use-field-attributes-dialog.hooks.d.ts +10 -0
- package/dist/forms/react/hooks/use-field-attributes-dialog.hooks.js +12 -2
- package/dist/forms/react/hooks/use-form-editor.hooks.d.ts +11 -0
- package/dist/forms/react/hooks/use-form-editor.hooks.js +25 -2
- package/dist/forms/react/hooks/use-form-fields-editor.hooks.d.ts +2 -0
- package/dist/forms/react/hooks/use-form-fields-editor.hooks.js +5 -1
- package/dist/forms/react/hooks/use-form-submission-detail.hooks.d.ts +4 -0
- package/dist/forms/react/hooks/use-form-submission-detail.hooks.js +1 -0
- package/dist/forms/react/index.d.ts +216 -0
- package/dist/forms/react/pages/FormEditor.js +3 -2
- package/dist/forms/rules.d.ts +10 -1
- package/dist/forms/rules.js +8 -1
- package/dist/media/controllers/library.controller.d.ts +4 -0
- package/dist/media/controllers/library.controller.js +13 -0
- package/dist/media/react/components/MediaPicker.js +2 -2
- package/dist/media/react/hooks/EditMediaPanel.hooks.js +4 -2
- package/dist/media/react/hooks/GallerySelection.hooks.d.ts +31 -0
- package/dist/media/react/hooks/GallerySelection.hooks.js +32 -0
- package/dist/media/react/hooks/LibraryTab.hooks.d.ts +4 -0
- package/dist/media/react/hooks/LibraryTab.hooks.js +9 -3
- package/dist/media/react/hooks/MediaCard.hooks.d.ts +2 -2
- package/dist/media/react/hooks/MediaGrid.hooks.d.ts +2 -2
- package/dist/media/react/hooks/MediaPage.hooks.d.ts +4 -0
- package/dist/media/react/hooks/MediaPage.hooks.js +10 -13
- package/dist/media/react/hooks/MediaPicker.hooks.js +5 -1
- package/dist/media/react/hooks/MediaQuery.hooks.d.ts +27 -0
- package/dist/media/react/hooks/MediaQuery.hooks.js +41 -0
- package/dist/media/react/hooks/ProvidersTab.hooks.js +21 -2
- package/dist/media/react/index.d.ts +1 -0
- package/dist/media/react/index.js +1 -0
- package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.js +5 -0
- package/dist/react/components/Sidebar.d.ts +6 -0
- package/dist/react/components/Sidebar.js +1 -0
- package/dist/react/entities/use-entity-edit.d.ts +1 -1
- package/dist/react/hooks/use-sidebar-rail.d.ts +10 -1
- package/dist/react/hooks/use-sidebar-rail.js +16 -3
- package/dist/seo/adapters/http.d.ts +11 -1
- package/dist/seo/adapters/http.js +11 -1
- package/dist/seo/controllers/entry-picker.controller.d.ts +2 -0
- package/dist/seo/controllers/entry-picker.controller.js +2 -0
- package/dist/seo/models.d.ts +8 -0
- package/dist/seo/react/components/MediaRefField.js +2 -2
- package/dist/seo/react/hooks/MediaRefField.hooks.d.ts +4 -2
- package/dist/seo/react/hooks/MediaRefField.hooks.js +8 -3
- package/dist/seo/react/hooks/SeoQuery.hooks.d.ts +42 -0
- package/dist/seo/react/hooks/SeoQuery.hooks.js +65 -0
- package/dist/seo/react/hooks/use-entry-picker.hooks.js +7 -9
- package/dist/seo/react/hooks/use-seo-entry-panel.hooks.js +9 -2
- package/dist/seo/react/hooks/use-seo.hooks.js +22 -4
- package/dist/seo/react/hooks/use-share-image-warning.hooks.d.ts +19 -0
- package/dist/seo/react/hooks/use-share-image-warning.hooks.js +79 -0
- package/dist/seo/react/hooks/use-sitemap-modal.hooks.js +5 -3
- package/dist/seo/react/pages/SeoPage.js +1 -1
- package/dist/seo/rules.d.ts +21 -2
- package/dist/seo/rules.js +44 -4
- package/dist/widgets/react/hooks/WidgetsQuery.hooks.d.ts +37 -0
- package/dist/widgets/react/hooks/WidgetsQuery.hooks.js +70 -0
- package/dist/widgets/react/hooks/use-widget-instance-editor.hooks.js +5 -3
- package/dist/widgets/react/hooks/use-widget-region-editor.hooks.js +3 -1
- package/dist/widgets/react/hooks/use-widget-regions.hooks.js +7 -3
- package/dist/widgets/react/hooks/use-widgets-library.hooks.js +7 -3
- package/package.json +20 -14
|
@@ -84,6 +84,8 @@ export declare function forms(_required: Record<string, never>, options?: FormsR
|
|
|
84
84
|
readonly "Load more": "Load more";
|
|
85
85
|
readonly "Loading\u2026": "Loading…";
|
|
86
86
|
readonly "No submissions yet.": "No submissions yet.";
|
|
87
|
+
readonly Yes: "Yes";
|
|
88
|
+
readonly No: "No";
|
|
87
89
|
readonly "Create form": "Create form";
|
|
88
90
|
readonly Form: "Form";
|
|
89
91
|
readonly "Configure a new form's fields.": "Configure a new form's fields.";
|
|
@@ -120,6 +122,76 @@ export declare function forms(_required: Record<string, never>, options?: FormsR
|
|
|
120
122
|
readonly "failed to load submission": "failed to load submission";
|
|
121
123
|
readonly "delete failed": "delete failed";
|
|
122
124
|
readonly "Unknown field": "Unknown field";
|
|
125
|
+
readonly "Short text": "Short text";
|
|
126
|
+
readonly Paragraph: "Paragraph";
|
|
127
|
+
readonly Email: "Email";
|
|
128
|
+
readonly Phone: "Phone";
|
|
129
|
+
readonly "Website address": "Website address";
|
|
130
|
+
readonly Number: "Number";
|
|
131
|
+
readonly Slider: "Slider";
|
|
132
|
+
readonly Date: "Date";
|
|
133
|
+
readonly Time: "Time";
|
|
134
|
+
readonly "Date and time": "Date and time";
|
|
135
|
+
readonly Dropdown: "Dropdown";
|
|
136
|
+
readonly "Multi-select list": "Multi-select list";
|
|
137
|
+
readonly "Radio buttons": "Radio buttons";
|
|
138
|
+
readonly Checkbox: "Checkbox";
|
|
139
|
+
readonly Checkboxes: "Checkboxes";
|
|
140
|
+
readonly "Toggle switch": "Toggle switch";
|
|
141
|
+
readonly "File upload": "File upload";
|
|
142
|
+
readonly "Hidden value": "Hidden value";
|
|
143
|
+
readonly Text: "Text";
|
|
144
|
+
readonly Choices: "Choices";
|
|
145
|
+
readonly "Numbers and dates": "Numbers and dates";
|
|
146
|
+
readonly Other: "Other";
|
|
147
|
+
readonly "Field settings \u2014": "Field settings —";
|
|
148
|
+
readonly "Settings for field \"{name}\"": "Settings for field \"{name}\"";
|
|
149
|
+
readonly "CSS classes and HTML attributes": "CSS classes and HTML attributes";
|
|
150
|
+
readonly Required: "Required";
|
|
151
|
+
readonly Settings: "Settings";
|
|
152
|
+
readonly Placeholder: "Placeholder";
|
|
153
|
+
readonly "Help text": "Help text";
|
|
154
|
+
readonly "Shown under the field and read out by screen readers.": "Shown under the field and read out by screen readers.";
|
|
155
|
+
readonly Options: "Options";
|
|
156
|
+
readonly "The label is what visitors see. The value is what the submission stores; it follows the label until you edit it.": "The label is what visitors see. The value is what the submission stores; it follows the label until you edit it.";
|
|
157
|
+
readonly "Option {n} label": "Option {n} label";
|
|
158
|
+
readonly "Option {n} value": "Option {n} value";
|
|
159
|
+
readonly "Option {n} selected by default": "Option {n} selected by default";
|
|
160
|
+
readonly "Move option {n} up": "Move option {n} up";
|
|
161
|
+
readonly "Move option {n} down": "Move option {n} down";
|
|
162
|
+
readonly "Remove option {n}": "Remove option {n}";
|
|
163
|
+
readonly "Add option": "Add option";
|
|
164
|
+
readonly Default: "Default";
|
|
165
|
+
readonly "Checked by default": "Checked by default";
|
|
166
|
+
readonly None: "None";
|
|
167
|
+
readonly Length: "Length";
|
|
168
|
+
readonly "Min length": "Min length";
|
|
169
|
+
readonly Min: "Min";
|
|
170
|
+
readonly Max: "Max";
|
|
171
|
+
readonly Pattern: "Pattern";
|
|
172
|
+
readonly "Optional. The whole answer must match it, e.g. [0-9]+ for digits only. Groups, | and repeating more than once are not supported.": "Optional. The whole answer must match it, e.g. [0-9]+ for digits only. Groups, | and repeating more than once are not supported.";
|
|
173
|
+
readonly Range: "Range";
|
|
174
|
+
readonly Step: "Step";
|
|
175
|
+
readonly Files: "Files";
|
|
176
|
+
readonly "Leave all unticked to accept every safe type: images, video and PDF.": "Leave all unticked to accept every safe type: images, video and PDF.";
|
|
177
|
+
readonly Images: "Images";
|
|
178
|
+
readonly Videos: "Videos";
|
|
179
|
+
readonly PDF: "PDF";
|
|
180
|
+
readonly "Max size (MB)": "Max size (MB)";
|
|
181
|
+
readonly "Length must be a whole number.": "Length must be a whole number.";
|
|
182
|
+
readonly "Minimum must not be above maximum.": "Minimum must not be above maximum.";
|
|
183
|
+
readonly "That pattern isn't supported.": "That pattern isn't supported.";
|
|
184
|
+
readonly "Enter numbers for min, max and step.": "Enter numbers for min, max and step.";
|
|
185
|
+
readonly "Step must be above 0.": "Step must be above 0.";
|
|
186
|
+
readonly "Add at least one option.": "Add at least one option.";
|
|
187
|
+
readonly "Each option needs a label and a unique value.": "Each option needs a label and a unique value.";
|
|
188
|
+
readonly "Default must be a number.": "Default must be a number.";
|
|
189
|
+
readonly "Max size must be up to 10 MB.": "Max size must be up to 10 MB.";
|
|
190
|
+
readonly "Discard unsaved changes?": "Discard unsaved changes?";
|
|
191
|
+
readonly "Your changes to this form have not been saved.": "Your changes to this form have not been saved.";
|
|
192
|
+
readonly "Discard changes": "Discard changes";
|
|
193
|
+
readonly "Keep editing": "Keep editing";
|
|
194
|
+
readonly "Classes are unrestricted, e.g. Tailwind md:col-span-2. Attribute names must be aria-*, data-* or a safe layout attribute; event handlers like onclick are rejected.": "Classes are unrestricted, e.g. Tailwind md:col-span-2. Attribute names must be aria-*, data-* or a safe layout attribute; event handlers like onclick are rejected.";
|
|
123
195
|
};
|
|
124
196
|
pages: {
|
|
125
197
|
readonly list: {
|
|
@@ -230,6 +302,8 @@ export declare function forms(_required: Record<string, never>, options?: FormsR
|
|
|
230
302
|
readonly "Load more": "Load more";
|
|
231
303
|
readonly "Loading\u2026": "Loading…";
|
|
232
304
|
readonly "No submissions yet.": "No submissions yet.";
|
|
305
|
+
readonly Yes: "Yes";
|
|
306
|
+
readonly No: "No";
|
|
233
307
|
readonly "Create form": "Create form";
|
|
234
308
|
readonly Form: "Form";
|
|
235
309
|
readonly "Configure a new form's fields.": "Configure a new form's fields.";
|
|
@@ -266,6 +340,76 @@ export declare function forms(_required: Record<string, never>, options?: FormsR
|
|
|
266
340
|
readonly "failed to load submission": "failed to load submission";
|
|
267
341
|
readonly "delete failed": "delete failed";
|
|
268
342
|
readonly "Unknown field": "Unknown field";
|
|
343
|
+
readonly "Short text": "Short text";
|
|
344
|
+
readonly Paragraph: "Paragraph";
|
|
345
|
+
readonly Email: "Email";
|
|
346
|
+
readonly Phone: "Phone";
|
|
347
|
+
readonly "Website address": "Website address";
|
|
348
|
+
readonly Number: "Number";
|
|
349
|
+
readonly Slider: "Slider";
|
|
350
|
+
readonly Date: "Date";
|
|
351
|
+
readonly Time: "Time";
|
|
352
|
+
readonly "Date and time": "Date and time";
|
|
353
|
+
readonly Dropdown: "Dropdown";
|
|
354
|
+
readonly "Multi-select list": "Multi-select list";
|
|
355
|
+
readonly "Radio buttons": "Radio buttons";
|
|
356
|
+
readonly Checkbox: "Checkbox";
|
|
357
|
+
readonly Checkboxes: "Checkboxes";
|
|
358
|
+
readonly "Toggle switch": "Toggle switch";
|
|
359
|
+
readonly "File upload": "File upload";
|
|
360
|
+
readonly "Hidden value": "Hidden value";
|
|
361
|
+
readonly Text: "Text";
|
|
362
|
+
readonly Choices: "Choices";
|
|
363
|
+
readonly "Numbers and dates": "Numbers and dates";
|
|
364
|
+
readonly Other: "Other";
|
|
365
|
+
readonly "Field settings \u2014": "Field settings —";
|
|
366
|
+
readonly "Settings for field \"{name}\"": "Settings for field \"{name}\"";
|
|
367
|
+
readonly "CSS classes and HTML attributes": "CSS classes and HTML attributes";
|
|
368
|
+
readonly Required: "Required";
|
|
369
|
+
readonly Settings: "Settings";
|
|
370
|
+
readonly Placeholder: "Placeholder";
|
|
371
|
+
readonly "Help text": "Help text";
|
|
372
|
+
readonly "Shown under the field and read out by screen readers.": "Shown under the field and read out by screen readers.";
|
|
373
|
+
readonly Options: "Options";
|
|
374
|
+
readonly "The label is what visitors see. The value is what the submission stores; it follows the label until you edit it.": "The label is what visitors see. The value is what the submission stores; it follows the label until you edit it.";
|
|
375
|
+
readonly "Option {n} label": "Option {n} label";
|
|
376
|
+
readonly "Option {n} value": "Option {n} value";
|
|
377
|
+
readonly "Option {n} selected by default": "Option {n} selected by default";
|
|
378
|
+
readonly "Move option {n} up": "Move option {n} up";
|
|
379
|
+
readonly "Move option {n} down": "Move option {n} down";
|
|
380
|
+
readonly "Remove option {n}": "Remove option {n}";
|
|
381
|
+
readonly "Add option": "Add option";
|
|
382
|
+
readonly Default: "Default";
|
|
383
|
+
readonly "Checked by default": "Checked by default";
|
|
384
|
+
readonly None: "None";
|
|
385
|
+
readonly Length: "Length";
|
|
386
|
+
readonly "Min length": "Min length";
|
|
387
|
+
readonly Min: "Min";
|
|
388
|
+
readonly Max: "Max";
|
|
389
|
+
readonly Pattern: "Pattern";
|
|
390
|
+
readonly "Optional. The whole answer must match it, e.g. [0-9]+ for digits only. Groups, | and repeating more than once are not supported.": "Optional. The whole answer must match it, e.g. [0-9]+ for digits only. Groups, | and repeating more than once are not supported.";
|
|
391
|
+
readonly Range: "Range";
|
|
392
|
+
readonly Step: "Step";
|
|
393
|
+
readonly Files: "Files";
|
|
394
|
+
readonly "Leave all unticked to accept every safe type: images, video and PDF.": "Leave all unticked to accept every safe type: images, video and PDF.";
|
|
395
|
+
readonly Images: "Images";
|
|
396
|
+
readonly Videos: "Videos";
|
|
397
|
+
readonly PDF: "PDF";
|
|
398
|
+
readonly "Max size (MB)": "Max size (MB)";
|
|
399
|
+
readonly "Length must be a whole number.": "Length must be a whole number.";
|
|
400
|
+
readonly "Minimum must not be above maximum.": "Minimum must not be above maximum.";
|
|
401
|
+
readonly "That pattern isn't supported.": "That pattern isn't supported.";
|
|
402
|
+
readonly "Enter numbers for min, max and step.": "Enter numbers for min, max and step.";
|
|
403
|
+
readonly "Step must be above 0.": "Step must be above 0.";
|
|
404
|
+
readonly "Add at least one option.": "Add at least one option.";
|
|
405
|
+
readonly "Each option needs a label and a unique value.": "Each option needs a label and a unique value.";
|
|
406
|
+
readonly "Default must be a number.": "Default must be a number.";
|
|
407
|
+
readonly "Max size must be up to 10 MB.": "Max size must be up to 10 MB.";
|
|
408
|
+
readonly "Discard unsaved changes?": "Discard unsaved changes?";
|
|
409
|
+
readonly "Your changes to this form have not been saved.": "Your changes to this form have not been saved.";
|
|
410
|
+
readonly "Discard changes": "Discard changes";
|
|
411
|
+
readonly "Keep editing": "Keep editing";
|
|
412
|
+
readonly "Classes are unrestricted, e.g. Tailwind md:col-span-2. Attribute names must be aria-*, data-* or a safe layout attribute; event handlers like onclick are rejected.": "Classes are unrestricted, e.g. Tailwind md:col-span-2. Attribute names must be aria-*, data-* or a safe layout attribute; event handlers like onclick are rejected.";
|
|
269
413
|
};
|
|
270
414
|
readonly pages: {
|
|
271
415
|
readonly list: {
|
|
@@ -380,6 +524,8 @@ export declare function forms(_required: Record<string, never>, options?: FormsR
|
|
|
380
524
|
readonly "Load more": "Load more";
|
|
381
525
|
readonly "Loading\u2026": "Loading…";
|
|
382
526
|
readonly "No submissions yet.": "No submissions yet.";
|
|
527
|
+
readonly Yes: "Yes";
|
|
528
|
+
readonly No: "No";
|
|
383
529
|
readonly "Create form": "Create form";
|
|
384
530
|
readonly Form: "Form";
|
|
385
531
|
readonly "Configure a new form's fields.": "Configure a new form's fields.";
|
|
@@ -416,6 +562,76 @@ export declare function forms(_required: Record<string, never>, options?: FormsR
|
|
|
416
562
|
readonly "failed to load submission": "failed to load submission";
|
|
417
563
|
readonly "delete failed": "delete failed";
|
|
418
564
|
readonly "Unknown field": "Unknown field";
|
|
565
|
+
readonly "Short text": "Short text";
|
|
566
|
+
readonly Paragraph: "Paragraph";
|
|
567
|
+
readonly Email: "Email";
|
|
568
|
+
readonly Phone: "Phone";
|
|
569
|
+
readonly "Website address": "Website address";
|
|
570
|
+
readonly Number: "Number";
|
|
571
|
+
readonly Slider: "Slider";
|
|
572
|
+
readonly Date: "Date";
|
|
573
|
+
readonly Time: "Time";
|
|
574
|
+
readonly "Date and time": "Date and time";
|
|
575
|
+
readonly Dropdown: "Dropdown";
|
|
576
|
+
readonly "Multi-select list": "Multi-select list";
|
|
577
|
+
readonly "Radio buttons": "Radio buttons";
|
|
578
|
+
readonly Checkbox: "Checkbox";
|
|
579
|
+
readonly Checkboxes: "Checkboxes";
|
|
580
|
+
readonly "Toggle switch": "Toggle switch";
|
|
581
|
+
readonly "File upload": "File upload";
|
|
582
|
+
readonly "Hidden value": "Hidden value";
|
|
583
|
+
readonly Text: "Text";
|
|
584
|
+
readonly Choices: "Choices";
|
|
585
|
+
readonly "Numbers and dates": "Numbers and dates";
|
|
586
|
+
readonly Other: "Other";
|
|
587
|
+
readonly "Field settings \u2014": "Field settings —";
|
|
588
|
+
readonly "Settings for field \"{name}\"": "Settings for field \"{name}\"";
|
|
589
|
+
readonly "CSS classes and HTML attributes": "CSS classes and HTML attributes";
|
|
590
|
+
readonly Required: "Required";
|
|
591
|
+
readonly Settings: "Settings";
|
|
592
|
+
readonly Placeholder: "Placeholder";
|
|
593
|
+
readonly "Help text": "Help text";
|
|
594
|
+
readonly "Shown under the field and read out by screen readers.": "Shown under the field and read out by screen readers.";
|
|
595
|
+
readonly Options: "Options";
|
|
596
|
+
readonly "The label is what visitors see. The value is what the submission stores; it follows the label until you edit it.": "The label is what visitors see. The value is what the submission stores; it follows the label until you edit it.";
|
|
597
|
+
readonly "Option {n} label": "Option {n} label";
|
|
598
|
+
readonly "Option {n} value": "Option {n} value";
|
|
599
|
+
readonly "Option {n} selected by default": "Option {n} selected by default";
|
|
600
|
+
readonly "Move option {n} up": "Move option {n} up";
|
|
601
|
+
readonly "Move option {n} down": "Move option {n} down";
|
|
602
|
+
readonly "Remove option {n}": "Remove option {n}";
|
|
603
|
+
readonly "Add option": "Add option";
|
|
604
|
+
readonly Default: "Default";
|
|
605
|
+
readonly "Checked by default": "Checked by default";
|
|
606
|
+
readonly None: "None";
|
|
607
|
+
readonly Length: "Length";
|
|
608
|
+
readonly "Min length": "Min length";
|
|
609
|
+
readonly Min: "Min";
|
|
610
|
+
readonly Max: "Max";
|
|
611
|
+
readonly Pattern: "Pattern";
|
|
612
|
+
readonly "Optional. The whole answer must match it, e.g. [0-9]+ for digits only. Groups, | and repeating more than once are not supported.": "Optional. The whole answer must match it, e.g. [0-9]+ for digits only. Groups, | and repeating more than once are not supported.";
|
|
613
|
+
readonly Range: "Range";
|
|
614
|
+
readonly Step: "Step";
|
|
615
|
+
readonly Files: "Files";
|
|
616
|
+
readonly "Leave all unticked to accept every safe type: images, video and PDF.": "Leave all unticked to accept every safe type: images, video and PDF.";
|
|
617
|
+
readonly Images: "Images";
|
|
618
|
+
readonly Videos: "Videos";
|
|
619
|
+
readonly PDF: "PDF";
|
|
620
|
+
readonly "Max size (MB)": "Max size (MB)";
|
|
621
|
+
readonly "Length must be a whole number.": "Length must be a whole number.";
|
|
622
|
+
readonly "Minimum must not be above maximum.": "Minimum must not be above maximum.";
|
|
623
|
+
readonly "That pattern isn't supported.": "That pattern isn't supported.";
|
|
624
|
+
readonly "Enter numbers for min, max and step.": "Enter numbers for min, max and step.";
|
|
625
|
+
readonly "Step must be above 0.": "Step must be above 0.";
|
|
626
|
+
readonly "Add at least one option.": "Add at least one option.";
|
|
627
|
+
readonly "Each option needs a label and a unique value.": "Each option needs a label and a unique value.";
|
|
628
|
+
readonly "Default must be a number.": "Default must be a number.";
|
|
629
|
+
readonly "Max size must be up to 10 MB.": "Max size must be up to 10 MB.";
|
|
630
|
+
readonly "Discard unsaved changes?": "Discard unsaved changes?";
|
|
631
|
+
readonly "Your changes to this form have not been saved.": "Your changes to this form have not been saved.";
|
|
632
|
+
readonly "Discard changes": "Discard changes";
|
|
633
|
+
readonly "Keep editing": "Keep editing";
|
|
634
|
+
readonly "Classes are unrestricted, e.g. Tailwind md:col-span-2. Attribute names must be aria-*, data-* or a safe layout attribute; event handlers like onclick are rejected.": "Classes are unrestricted, e.g. Tailwind md:col-span-2. Attribute names must be aria-*, data-* or a safe layout attribute; event handlers like onclick are rejected.";
|
|
419
635
|
};
|
|
420
636
|
readonly pages: {
|
|
421
637
|
readonly list: {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { agentHandle } from '@jini-ai/agentic';
|
|
3
|
+
import { ConfirmDialog } from '../../../react/components/ConfirmDialog/ConfirmDialog.js';
|
|
3
4
|
import { useWiredFormEditor } from "../hooks/use-form-editor.hooks.js";
|
|
4
5
|
import { FormEditorFieldsBody } from "../components/FormEditorFieldsBody.js";
|
|
5
6
|
import { FormEditorHeaderText } from "../components/FormEditorHeaderText.js";
|
|
@@ -12,7 +13,7 @@ import { FormEditorMainPanel } from "../components/FormEditorMainPanel.js";
|
|
|
12
13
|
* `const`, because a `const` assigned inside `FormEditor` still executes in that function's own
|
|
13
14
|
* scope — every branch inside it would still count toward `FormEditor`'s own complexity score. */
|
|
14
15
|
export function FormEditor({ formId, tab, useFormEditorHook = useWiredFormEditor }, _optional = {}) {
|
|
15
|
-
const { answerColumns, mode, onModeChange, html, setHtml, copyEmbed, copyFeedback, isNew, form, name, setName, slug, setSlug, fields, setFields, tab: activeTab, onTabChange, error, saving, existingFieldIds, showTabs, tabRefs, onTabsKeyDown, handleSave, handleStatusToggle, t, } = useFormEditorHook({ formId, tab });
|
|
16
|
+
const { answerColumns, mode, onModeChange, html, setHtml, copyEmbed, copyFeedback, isNew, form, name, setName, slug, setSlug, fields, setFields, tab: activeTab, onTabChange, error, saving, existingFieldIds, showTabs, tabRefs, onTabsKeyDown, handleSave, handleStatusToggle, leave, t, } = useFormEditorHook({ formId, tab });
|
|
16
17
|
if (!isNew && !form && !error)
|
|
17
18
|
return _jsx("div", { className: "notice", children: t("Loading form…") });
|
|
18
19
|
// Previously this was the ONLY guard, and it only covers the pre-error case — once the load
|
|
@@ -34,7 +35,7 @@ export function FormEditor({ formId, tab, useFormEditorHook = useWiredFormEditor
|
|
|
34
35
|
return (_jsxs("div", { className: "page", children: [_jsxs("div", { className: "page-header page-header-split", ...agentHandle({ handle: "form-editor-header" }, {
|
|
35
36
|
role: "region",
|
|
36
37
|
label: "Form editor header — title and the Back to forms link",
|
|
37
|
-
}), children: [_jsx("div", { className: "page-header-lead", children: _jsxs("a", { className: "btn-secondary", href: "/admin/forms", "aria-label": t("Back to forms"), ...agentHandle({ handle: "form-editor-back" }, { role: "link", label: "Back to the list of all forms" }), children: ["\u2190 ", t("Back")] }) }), _jsx(FormEditorHeaderText, { isNew: isNew, name: name, t: t })] }), error ? _jsx("div", { className: "notice error", children: error }) : null, _jsx(FormEditorTabStrip, { showTabs: showTabs, tab: activeTab, onTabChange: onTabChange, tabRefs: tabRefs, onTabsKeyDown: onTabsKeyDown, t: t }), _jsx(FormEditorMainPanel, { isNew: isNew, tab: activeTab, formId: formId, fieldsBody: fieldsBody, t: t, answerColumns: answerColumns })] }));
|
|
38
|
+
}), children: [_jsx("div", { className: "page-header-lead", children: _jsxs("a", { className: "btn-secondary", href: "/admin/forms", "aria-label": t("Back to forms"), onClick: leave.onBack, ...agentHandle({ handle: "form-editor-back" }, { role: "link", label: "Back to the list of all forms" }), children: ["\u2190 ", t("Back")] }) }), _jsx(FormEditorHeaderText, { isNew: isNew, name: name, t: t })] }), error ? _jsx("div", { className: "notice error", children: error }) : null, _jsx(FormEditorTabStrip, { showTabs: showTabs, tab: activeTab, onTabChange: onTabChange, tabRefs: tabRefs, onTabsKeyDown: onTabsKeyDown, t: t }), _jsx(FormEditorMainPanel, { isNew: isNew, tab: activeTab, formId: formId, fieldsBody: fieldsBody, t: t, answerColumns: answerColumns }), _jsx(ConfirmDialog, { open: leave.confirmOpen, agentHandle: "form-editor-leave-confirm", title: t("Discard unsaved changes?"), body: _jsx("p", { children: t("Your changes to this form have not been saved.") }), confirmLabel: t("Discard changes"), cancelLabel: t("Keep editing"), tone: "danger", onConfirm: leave.confirm, onCancel: leave.cancel })] }));
|
|
38
39
|
}
|
|
39
40
|
export default FormEditor;
|
|
40
41
|
//# sourceMappingURL=FormEditor.js.map
|
package/dist/forms/rules.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RowMenuItem } from "../react/components/RowMenu/RowMenu.js";
|
|
2
2
|
import type { AdminFormDefinition, AdminFormField } from "./models.js";
|
|
3
3
|
import type { QueryKey } from "@jini-ai/ui/fetch-query";
|
|
4
|
+
import { type FieldType } from "@jini-ai/cms/forms";
|
|
4
5
|
/**
|
|
5
6
|
* @file Pure logic for the `forms` feature (`FormEditor.tsx` — see `FormsList.tsx`'s own
|
|
6
7
|
* `rules.ts`/hook for the list screen's extraction) — everything that computes a value rather than
|
|
@@ -183,7 +184,8 @@ export declare function formRowMenuItems({ form, handlers, t }: {
|
|
|
183
184
|
handlers: FormRowMenuHandlers;
|
|
184
185
|
t: (key: string) => string;
|
|
185
186
|
}, _optional?: Record<string, never>): RowMenuItem[];
|
|
186
|
-
|
|
187
|
+
/** Every registered field type, in registry order; the picker groups them via `field-settings.ts`. */
|
|
188
|
+
export declare const FIELD_TYPES: FieldType[];
|
|
187
189
|
/** The two tab-panel views on an existing form's editor (`formId !== "new"`). */
|
|
188
190
|
export declare const FORM_TABS: readonly [{
|
|
189
191
|
readonly id: "fields";
|
|
@@ -216,6 +218,13 @@ export declare function updateFormField({ fields, index, patch }: {
|
|
|
216
218
|
index: number;
|
|
217
219
|
patch: Partial<AdminFormField>;
|
|
218
220
|
}, _optional?: Record<string, never>): AdminFormField[];
|
|
221
|
+
/** Swap in a whole field (a type change drops settings, which a merge would keep).
|
|
222
|
+
* @complexity O(n) in `fields.length`. */
|
|
223
|
+
export declare function replaceFormField({ fields, index, field }: {
|
|
224
|
+
fields: AdminFormField[];
|
|
225
|
+
index: number;
|
|
226
|
+
field: AdminFormField;
|
|
227
|
+
}, _optional?: Record<string, never>): AdminFormField[];
|
|
219
228
|
/** @complexity O(n) in `fields.length`. */
|
|
220
229
|
export declare function addFormField({ fields }: {
|
|
221
230
|
fields: AdminFormField[];
|
package/dist/forms/rules.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AdminApiError as ApiError, describeApiError as describeCoreApiError } from "../core/transport/errors.js";
|
|
2
2
|
const describeApiError = (e, fallback) => describeCoreApiError({ e, fallback });
|
|
3
|
+
import { FIELD_TYPE_REGISTRY } from "@jini-ai/cms/forms";
|
|
3
4
|
/**
|
|
4
5
|
* @file Pure logic for the `forms` feature (`FormEditor.tsx` — see `FormsList.tsx`'s own
|
|
5
6
|
* `rules.ts`/hook for the list screen's extraction) — everything that computes a value rather than
|
|
@@ -199,7 +200,8 @@ export function formRowMenuItems({ form, handlers, t }, _optional = {}) {
|
|
|
199
200
|
{ key: "delete", label: t("Delete"), tone: "danger", onSelect: () => handlers.onDelete(form) },
|
|
200
201
|
];
|
|
201
202
|
}
|
|
202
|
-
|
|
203
|
+
/** Every registered field type, in registry order; the picker groups them via `field-settings.ts`. */
|
|
204
|
+
export const FIELD_TYPES = Object.keys(FIELD_TYPE_REGISTRY);
|
|
203
205
|
/** The two tab-panel views on an existing form's editor (`formId !== "new"`). */
|
|
204
206
|
export const FORM_TABS = [
|
|
205
207
|
{ id: "fields", label: "Fields" },
|
|
@@ -235,6 +237,11 @@ export function fieldDisplayName({ field, index }, _optional = {}) {
|
|
|
235
237
|
export function updateFormField({ fields, index, patch }, _optional = {}) {
|
|
236
238
|
return fields.map((f, i) => (i === index ? { ...f, ...patch } : f));
|
|
237
239
|
}
|
|
240
|
+
/** Swap in a whole field (a type change drops settings, which a merge would keep).
|
|
241
|
+
* @complexity O(n) in `fields.length`. */
|
|
242
|
+
export function replaceFormField({ fields, index, field }, _optional = {}) {
|
|
243
|
+
return fields.map((f, i) => (i === index ? field : f));
|
|
244
|
+
}
|
|
238
245
|
/** @complexity O(n) in `fields.length`. */
|
|
239
246
|
export function addFormField({ fields }, _optional = {}) {
|
|
240
247
|
return [...fields, blankField()];
|
|
@@ -22,6 +22,10 @@ export declare function createLibraryController({ api }: {
|
|
|
22
22
|
listener: () => void;
|
|
23
23
|
}) => () => void;
|
|
24
24
|
load: (_required?: Record<string, never>, _optional?: Record<string, never>) => Promise<void>;
|
|
25
|
+
settleLibrary: ({ all, error }: {
|
|
26
|
+
all?: readonly MediaAsset[];
|
|
27
|
+
error?: Error | null;
|
|
28
|
+
}, _optional?: Record<string, never>) => void;
|
|
25
29
|
setQuery({ query }: {
|
|
26
30
|
query: MediaQuery;
|
|
27
31
|
}, _optional?: Record<string, never>): Promise<void>;
|
|
@@ -69,6 +69,18 @@ export function createLibraryController({ api }, { query = {} } = {}) {
|
|
|
69
69
|
store.set({ patch: { loading: false } });
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
+
// A reactive cache observer may refresh this library without calling load again. Keep
|
|
73
|
+
// filtering/counts in the headless domain; accepting cache data must spend no extra read.
|
|
74
|
+
function settleLibrary({ all, error }, _optional = {}) {
|
|
75
|
+
if (store.signal.aborted)
|
|
76
|
+
return;
|
|
77
|
+
if (all) {
|
|
78
|
+
library = all;
|
|
79
|
+
store.set({ patch: { items: queryMedia({ media: all, query: store.getSnapshot().query }), counts: countMediaTabs({ media: all }), hasUntyped: all.some(item => item.contentType === null), error: null } });
|
|
80
|
+
}
|
|
81
|
+
if (error)
|
|
82
|
+
store.set({ patch: { error: error.message } });
|
|
83
|
+
}
|
|
72
84
|
let writes = Promise.resolve();
|
|
73
85
|
let exclusiveWrite = false;
|
|
74
86
|
function write(run, id) {
|
|
@@ -114,6 +126,7 @@ export function createLibraryController({ api }, { query = {} } = {}) {
|
|
|
114
126
|
getSnapshot: store.getSnapshot,
|
|
115
127
|
subscribe: store.subscribe,
|
|
116
128
|
load,
|
|
129
|
+
settleLibrary,
|
|
117
130
|
async setQuery({ query }, _optional = {}) {
|
|
118
131
|
// Already-fetched full library owns display order/filtering. Tab/search/order changes
|
|
119
132
|
// never spend another request or flash a loading zero badge.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { MediaDialog } from './MediaDialog.js';
|
|
3
|
-
import { Button, TextField } from '@jini-ai/ui-kit/react';
|
|
3
|
+
import { Button, IconButton, TextField } from '@jini-ai/ui-kit/react';
|
|
4
4
|
import { useMediaPicker } from '../hooks/MediaPicker.hooks.js';
|
|
5
5
|
import { MediaGrid } from './MediaGrid.js';
|
|
6
6
|
export function MediaPicker(props, _optional = {}) {
|
|
7
7
|
const vm = useMediaPicker(props);
|
|
8
|
-
return (_jsxs(MediaDialog, { className: "jini-media-picker-dialog", attrs: { 'data-jini-part': 'media.picker' }, title: "Choose media", open: true, onClose: vm.onClose, children: [_jsx(TextField, { attrs: { 'data-jini-part': 'media.picker.search' }, label: "Search media", value: vm.search, onValueChange: vm.onSearch }), vm.snapshot?.error && _jsxs("div", { className: "jini-notice jini-notice-error", children: [_jsx("p", { className: "jini-notice-text", role: "alert", children: vm.snapshot.error }), _jsx(Button, { onPress: vm.onRetry, children: "Retry" })] }), vm.loading ? (_jsx("p", { className: "jini-notice", role: "status", children: "Loading media\u2026" })) : (_jsx(MediaGrid, { actions: vm.actions, api: vm.api })), _jsx(Button, { attrs: { 'data-jini-part': 'media.picker.cancel', 'data-jini-autofocus': true }, onPress: vm.onClose, children: "Cancel" })] }));
|
|
8
|
+
return (_jsxs(MediaDialog, { className: "jini-media-picker-dialog", attrs: { 'data-jini-part': 'media.picker' }, title: "Choose media", open: true, onClose: vm.onClose, children: [_jsx(TextField, { attrs: { 'data-jini-part': 'media.picker.search' }, label: "Search media", value: vm.search, onValueChange: vm.onSearch }), vm.snapshot?.error && _jsxs("div", { className: "jini-notice jini-notice-error", children: [_jsx("p", { className: "jini-notice-text", role: "alert", children: vm.snapshot.error }), _jsx(Button, { onPress: vm.onRetry, children: "Retry" })] }), vm.loading ? (_jsx("p", { className: "jini-notice", role: "status", children: "Loading media\u2026" })) : (_jsx(MediaGrid, { actions: vm.actions, api: vm.api })), _jsxs("div", { className: "jini-media-picker-footer", "data-jini-part": "media.picker.footer", children: [_jsx(IconButton, { className: "jini-media-picker-back", label: "Back", attrs: { 'data-jini-part': 'media.picker.back' }, onPress: vm.onClose, children: "\u2190" }), _jsx(Button, { attrs: { 'data-jini-part': 'media.picker.cancel', 'data-jini-autofocus': true }, onPress: vm.onClose, children: "Cancel" })] })] }));
|
|
9
9
|
}
|
|
10
10
|
//# sourceMappingURL=MediaPicker.js.map
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { useMediaLibraryQuery } from './MediaQuery.hooks.js';
|
|
1
2
|
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
2
3
|
import { useController } from '../../../core/react/use-controller.js';
|
|
3
4
|
import { createEditMediaController } from '../../controllers/edit-media.controller.js';
|
|
4
5
|
import { canReplaceMedia, mediaEmbedSnippet, parseOptionalPixelSize, safeMediaUrl } from '../../rules.js';
|
|
5
6
|
import { parseMediaHtmlAttributes, describeMediaHtmlAttributeError } from '../../html-attributes.js';
|
|
6
7
|
export function useEditMediaPanel(props, _optional = {}) {
|
|
8
|
+
const cached = useMediaLibraryQuery({ api: props.api }, { enabled: false });
|
|
7
9
|
const { controller, snapshot } = useController({
|
|
8
|
-
create: () => createEditMediaController({ api:
|
|
9
|
-
dependencies: [
|
|
10
|
+
create: () => createEditMediaController({ api: cached.api, item: props.item }),
|
|
11
|
+
dependencies: [cached.api, props.item.id],
|
|
10
12
|
});
|
|
11
13
|
const [copied, setCopied] = useState({});
|
|
12
14
|
const timers = useRef(new Map());
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface GallerySelectionImage {
|
|
2
|
+
readonly mediaId: string;
|
|
3
|
+
readonly alt: string;
|
|
4
|
+
readonly caption: string;
|
|
5
|
+
}
|
|
6
|
+
/** Controlled gallery selection: metadata follows media ids through reordering and deduplication.
|
|
7
|
+
* The host owns persistence and media loading; these callbacks only derive the next ordered value.
|
|
8
|
+
* @complexity O(n+m) time/space per action; the default bound is 50 images.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useGallerySelection({ images, onChange }: {
|
|
11
|
+
images: readonly GallerySelectionImage[];
|
|
12
|
+
onChange: (images: GallerySelectionImage[]) => void;
|
|
13
|
+
}, { maxImages }?: {
|
|
14
|
+
maxImages?: number;
|
|
15
|
+
}): {
|
|
16
|
+
add({ images: additions }: {
|
|
17
|
+
images: readonly GallerySelectionImage[];
|
|
18
|
+
}, _optional?: Record<string, never>): void;
|
|
19
|
+
update({ mediaId, patch }: {
|
|
20
|
+
mediaId: string;
|
|
21
|
+
patch: Partial<Pick<GallerySelectionImage, "alt" | "caption">>;
|
|
22
|
+
}, _optional?: Record<string, never>): void;
|
|
23
|
+
remove({ mediaId }: {
|
|
24
|
+
mediaId: string;
|
|
25
|
+
}, _optional?: Record<string, never>): void;
|
|
26
|
+
move({ mediaId, beforeId }: {
|
|
27
|
+
mediaId: string;
|
|
28
|
+
beforeId: string;
|
|
29
|
+
}, _optional?: Record<string, never>): void;
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=GallerySelection.hooks.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** Controlled gallery selection: metadata follows media ids through reordering and deduplication.
|
|
2
|
+
* The host owns persistence and media loading; these callbacks only derive the next ordered value.
|
|
3
|
+
* @complexity O(n+m) time/space per action; the default bound is 50 images.
|
|
4
|
+
*/
|
|
5
|
+
export function useGallerySelection({ images, onChange }, { maxImages = 50 } = {}) {
|
|
6
|
+
return {
|
|
7
|
+
add({ images: additions }, _optional = {}) {
|
|
8
|
+
const next = new Map(images.map(image => [image.mediaId, image]));
|
|
9
|
+
for (const image of additions)
|
|
10
|
+
if (!next.has(image.mediaId))
|
|
11
|
+
next.set(image.mediaId, image);
|
|
12
|
+
if (next.size > maxImages)
|
|
13
|
+
throw new Error(`Choose at most ${maxImages} gallery images`);
|
|
14
|
+
onChange([...next.values()]);
|
|
15
|
+
},
|
|
16
|
+
update({ mediaId, patch }, _optional = {}) {
|
|
17
|
+
onChange(images.map(image => image.mediaId === mediaId ? { ...image, ...patch } : image));
|
|
18
|
+
},
|
|
19
|
+
remove({ mediaId }, _optional = {}) { onChange(images.filter(image => image.mediaId !== mediaId)); },
|
|
20
|
+
move({ mediaId, beforeId }, _optional = {}) {
|
|
21
|
+
if (mediaId === beforeId)
|
|
22
|
+
return;
|
|
23
|
+
const source = images.find(image => image.mediaId === mediaId);
|
|
24
|
+
if (!source || !images.some(image => image.mediaId === beforeId))
|
|
25
|
+
return;
|
|
26
|
+
const next = images.filter(image => image.mediaId !== mediaId);
|
|
27
|
+
next.splice(next.findIndex(image => image.mediaId === beforeId), 0, source);
|
|
28
|
+
onChange(next);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=GallerySelection.hooks.js.map
|
|
@@ -18,6 +18,10 @@ export declare function useLibraryTab({ params, permissions }: TabViewProps, _op
|
|
|
18
18
|
listener: () => void;
|
|
19
19
|
}) => () => void;
|
|
20
20
|
load: (_required?: Record<string, never>, _optional?: Record<string, never>) => Promise<void>;
|
|
21
|
+
settleLibrary: ({ all, error }: {
|
|
22
|
+
all?: readonly MediaAsset[];
|
|
23
|
+
error?: Error | null;
|
|
24
|
+
}, _optional?: Record<string, never>) => void;
|
|
21
25
|
setQuery({ query }: {
|
|
22
26
|
query: import("../../models.js").MediaQuery;
|
|
23
27
|
}, _optional?: Record<string, never>): Promise<void>;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { useInvalidate } from '@jini-ai/ui/fetch-query';
|
|
2
|
+
import { MEDIA_QUERY_KEYS, useMediaLibraryQuery } from './MediaQuery.hooks.js';
|
|
1
3
|
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
2
4
|
import { useController } from '../../../core/react/use-controller.js';
|
|
3
5
|
import { createLibraryController } from '../../controllers/library.controller.js';
|
|
@@ -9,9 +11,11 @@ export function useLibraryTab({ params, permissions = [] }, _optional = {}) {
|
|
|
9
11
|
const { mediaApi, mediaEvents } = useMediaPorts();
|
|
10
12
|
const filter = params.filter === 'images' || params.filter === 'videos' ? params.filter : 'all';
|
|
11
13
|
const shared = useSharedMediaLibrary();
|
|
14
|
+
const cached = useMediaLibraryQuery({ api: mediaApi }, { enabled: !shared });
|
|
15
|
+
const invalidate = useInvalidate();
|
|
12
16
|
const local = useController({
|
|
13
|
-
create: () => createLibraryController({ api:
|
|
14
|
-
dependencies: [
|
|
17
|
+
create: () => createLibraryController({ api: cached.api }, { query: { filter } }),
|
|
18
|
+
dependencies: [cached.api, filter],
|
|
15
19
|
}, {
|
|
16
20
|
start: ({ controller }) => {
|
|
17
21
|
if (!shared)
|
|
@@ -19,7 +23,9 @@ export function useLibraryTab({ params, permissions = [] }, _optional = {}) {
|
|
|
19
23
|
},
|
|
20
24
|
});
|
|
21
25
|
const { controller, snapshot } = shared ?? local;
|
|
22
|
-
useEffect(() => !shared ? mediaEvents?.subscribe({ onRefresh: () => {
|
|
26
|
+
useEffect(() => !shared ? mediaEvents?.subscribe({ onRefresh: () => invalidate({ key: MEDIA_QUERY_KEYS.root }) }) : undefined, [mediaEvents, controller, shared, invalidate]);
|
|
27
|
+
useEffect(() => { if (!shared)
|
|
28
|
+
local.controller?.settleLibrary({ ...(cached.query.data ? { all: cached.query.data } : {}), error: cached.query.error }); }, [shared, local.controller, cached.query.data, cached.query.error]);
|
|
23
29
|
const gridRef = useRef(null);
|
|
24
30
|
const [success, setSuccess] = useState(null);
|
|
25
31
|
const [focusAfterAction, setFocusAfterAction] = useState(null);
|
|
@@ -59,7 +59,7 @@ export declare function useMediaCard(props: MediaCardProps, _optional?: Record<s
|
|
|
59
59
|
disabled: boolean;
|
|
60
60
|
})[];
|
|
61
61
|
src: string | undefined;
|
|
62
|
-
stage: "
|
|
62
|
+
stage: "video" | "image" | "unsupported";
|
|
63
63
|
image: boolean | "" | undefined;
|
|
64
64
|
video: boolean | "" | undefined;
|
|
65
65
|
onImageError: () => void;
|
|
@@ -67,7 +67,7 @@ export declare function useMediaCard(props: MediaCardProps, _optional?: Record<s
|
|
|
67
67
|
alt: string;
|
|
68
68
|
statusClassName: string;
|
|
69
69
|
statusLabel: "active" | "trashed";
|
|
70
|
-
statusTone: "
|
|
70
|
+
statusTone: "warning" | "success";
|
|
71
71
|
trashed: boolean;
|
|
72
72
|
item: MediaAsset;
|
|
73
73
|
handleBase?: string;
|
|
@@ -10,10 +10,10 @@ export interface MediaGridProps {
|
|
|
10
10
|
export declare function useMediaGrid(props: MediaGridProps, _optional?: Record<string, never>): {
|
|
11
11
|
actions: {
|
|
12
12
|
handleBase: string;
|
|
13
|
+
item: import("../../models.js").MediaAsset;
|
|
14
|
+
locale?: string;
|
|
13
15
|
busy?: boolean;
|
|
14
16
|
onPurge?: () => void;
|
|
15
|
-
locale?: string;
|
|
16
|
-
item: import("../../models.js").MediaAsset;
|
|
17
17
|
onEdit?: () => void;
|
|
18
18
|
onPreview?: () => void;
|
|
19
19
|
onTrash?: () => void;
|
|
@@ -21,6 +21,10 @@ export declare function useMediaPage(props: MediaPageProps, _optional?: Record<s
|
|
|
21
21
|
listener: () => void;
|
|
22
22
|
}) => () => void;
|
|
23
23
|
load: (_required?: Record<string, never>, _optional?: Record<string, never>) => Promise<void>;
|
|
24
|
+
settleLibrary: ({ all, error }: {
|
|
25
|
+
all?: readonly import("../../models.js").MediaAsset[];
|
|
26
|
+
error?: Error | null;
|
|
27
|
+
}, _optional?: Record<string, never>) => void;
|
|
24
28
|
setQuery({ query }: {
|
|
25
29
|
query: import("../../models.js").MediaQuery;
|
|
26
30
|
}, _optional?: Record<string, never>): Promise<void>;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { useInvalidate } from '@jini-ai/ui/fetch-query';
|
|
2
|
+
import { MEDIA_QUERY_KEYS, useMediaLibraryQuery, useMediaProvidersQuery } from './MediaQuery.hooks.js';
|
|
1
3
|
import { createElement, createContext, useContext, useEffect, useState } from 'react';
|
|
2
4
|
import { useController } from '../../../core/react/use-controller.js';
|
|
3
5
|
import { createLibraryController } from '../../controllers/library.controller.js';
|
|
@@ -11,20 +13,15 @@ export function useSharedMediaLibrary(_required = {}, _optional = {}) {
|
|
|
11
13
|
export function useMediaPage(props, _optional = {}) {
|
|
12
14
|
const [localTab, setLocalTab] = useState('all');
|
|
13
15
|
const { mediaApi, mediaEvents, mediaProviders } = useMediaPorts();
|
|
14
|
-
const
|
|
16
|
+
const cached = useMediaLibraryQuery({ api: mediaApi }, { enabled: props.description.visible });
|
|
17
|
+
const providers = useMediaProvidersQuery({ port: mediaProviders }, { enabled: props.description.visible && props.description.grants.includes('media.providers') });
|
|
18
|
+
const invalidate = useInvalidate();
|
|
19
|
+
const library = useController({ create: () => createLibraryController({ api: cached.api }, { query: { filter: requestedFilter({ tabId: props.requestedTab ?? localTab }) } }), dependencies: [cached.api] }, { start: ({ controller }) => { if (props.description.visible)
|
|
15
20
|
void controller.load(); } });
|
|
16
|
-
useEffect(() => props.description.visible ? mediaEvents?.subscribe({ onRefresh: () => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return;
|
|
21
|
-
const abort = new AbortController();
|
|
22
|
-
void mediaProviders.list({}, { signal: abort.signal }).then(items => {
|
|
23
|
-
if (!abort.signal.aborted && items)
|
|
24
|
-
setProviderCount(items.length);
|
|
25
|
-
}).catch(() => { });
|
|
26
|
-
return () => abort.abort();
|
|
27
|
-
}, [mediaProviders, props.description.grants, props.description.visible]);
|
|
21
|
+
useEffect(() => props.description.visible ? mediaEvents?.subscribe({ onRefresh: () => { invalidate({ key: MEDIA_QUERY_KEYS.root }); } }) : undefined, [props.description.visible, mediaEvents, library.controller, invalidate]);
|
|
22
|
+
useEffect(() => { library.controller?.settleLibrary({ ...(cached.query.data ? { all: cached.query.data } : {}), error: cached.query.error }); }, [library.controller, cached.query.data, cached.query.error]);
|
|
23
|
+
/* An unavailable read is not evidence of a zero catalogue. */
|
|
24
|
+
const providerCount = providers.query.data?.length ?? null;
|
|
28
25
|
const visible = props.description.visible
|
|
29
26
|
? props.description.tabs.filter((tab) => tab.visible)
|
|
30
27
|
: [];
|