@qoretechnologies/reqraft 0.10.25 → 0.10.26
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/.claude/CLAUDE.md +52 -23
- package/design/COMPACT_ENGINE_REDESIGN.md +156 -0
- package/design/FORM_ENGINE_COMPACT_UX_PLAN.md +353 -0
- package/dist/components/FocusedEditing/index.d.ts +9 -1
- package/dist/components/FocusedEditing/index.d.ts.map +1 -1
- package/dist/components/FocusedEditing/index.js +2 -2
- package/dist/components/FocusedEditing/index.js.map +1 -1
- package/dist/components/composer/Composer.d.ts +108 -0
- package/dist/components/composer/Composer.d.ts.map +1 -0
- package/dist/components/composer/Composer.js +238 -0
- package/dist/components/composer/Composer.js.map +1 -0
- package/dist/components/composer/helpers.d.ts +21 -0
- package/dist/components/composer/helpers.d.ts.map +1 -0
- package/dist/components/composer/helpers.js +75 -0
- package/dist/components/composer/helpers.js.map +1 -0
- package/dist/components/composer/index.d.ts +3 -0
- package/dist/components/composer/index.d.ts.map +1 -0
- package/dist/components/composer/index.js +19 -0
- package/dist/components/composer/index.js.map +1 -0
- package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
- package/dist/components/form/engine/CompactRow.js +307 -110
- package/dist/components/form/engine/CompactRow.js.map +1 -1
- package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
- package/dist/components/form/engine/CompactToolbar.js +123 -106
- package/dist/components/form/engine/CompactToolbar.js.map +1 -1
- package/dist/components/form/engine/FormEngine.d.ts +107 -11
- package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
- package/dist/components/form/engine/FormEngine.js +618 -179
- package/dist/components/form/engine/FormEngine.js.map +1 -1
- package/dist/components/form/engine/compactRowContext.d.ts +9 -0
- package/dist/components/form/engine/compactRowContext.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowContext.js.map +1 -1
- package/dist/components/form/engine/compactRowStyles.d.ts +7 -3
- package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowStyles.js +88 -51
- package/dist/components/form/engine/compactRowStyles.js.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.d.ts +1 -0
- package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.js.map +1 -1
- package/dist/components/form/engine/optionActions.d.ts +28 -0
- package/dist/components/form/engine/optionActions.d.ts.map +1 -0
- package/dist/components/form/engine/optionActions.js +18 -0
- package/dist/components/form/engine/optionActions.js.map +1 -0
- package/dist/components/form/engine/readFirst.d.ts +63 -2
- package/dist/components/form/engine/readFirst.d.ts.map +1 -1
- package/dist/components/form/engine/readFirst.js +121 -6
- package/dist/components/form/engine/readFirst.js.map +1 -1
- package/dist/components/form/engine/rendererTypes.d.ts +35 -0
- package/dist/components/form/engine/rendererTypes.d.ts.map +1 -0
- package/dist/components/form/engine/rendererTypes.js +63 -0
- package/dist/components/form/engine/rendererTypes.js.map +1 -0
- package/dist/components/form/engine/variants/VariantCalmTable.d.ts +6 -0
- package/dist/components/form/engine/variants/VariantCalmTable.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantCalmTable.js +94 -0
- package/dist/components/form/engine/variants/VariantCalmTable.js.map +1 -0
- package/dist/components/form/engine/variants/VariantCards.d.ts +6 -0
- package/dist/components/form/engine/variants/VariantCards.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantCards.js +80 -0
- package/dist/components/form/engine/variants/VariantCards.js.map +1 -0
- package/dist/components/form/engine/variants/VariantFocus.d.ts +7 -0
- package/dist/components/form/engine/variants/VariantFocus.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantFocus.js +138 -0
- package/dist/components/form/engine/variants/VariantFocus.js.map +1 -0
- package/dist/components/form/engine/variants/VariantMinimal.d.ts +6 -0
- package/dist/components/form/engine/variants/VariantMinimal.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantMinimal.js +73 -0
- package/dist/components/form/engine/variants/VariantMinimal.js.map +1 -0
- package/dist/components/form/engine/variants/focusDemo.d.ts +13 -0
- package/dist/components/form/engine/variants/focusDemo.d.ts.map +1 -0
- package/dist/components/form/engine/variants/focusDemo.js +139 -0
- package/dist/components/form/engine/variants/focusDemo.js.map +1 -0
- package/dist/components/form/engine/variants/variantModel.d.ts +70 -0
- package/dist/components/form/engine/variants/variantModel.d.ts.map +1 -0
- package/dist/components/form/engine/variants/variantModel.js +133 -0
- package/dist/components/form/engine/variants/variantModel.js.map +1 -0
- package/dist/components/form/engine/variants/variantParts.d.ts +79 -0
- package/dist/components/form/engine/variants/variantParts.d.ts.map +1 -0
- package/dist/components/form/engine/variants/variantParts.js +191 -0
- package/dist/components/form/engine/variants/variantParts.js.map +1 -0
- package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/index.js +13 -2
- package/dist/components/form/expressions/builder/index.js.map +1 -1
- package/dist/components/form/expressions/useExpressions.d.ts.map +1 -1
- package/dist/components/form/expressions/useExpressions.js +4 -1
- package/dist/components/form/expressions/useExpressions.js.map +1 -1
- package/dist/components/form/fields/Field.d.ts.map +1 -1
- package/dist/components/form/fields/Field.js +5 -3
- package/dist/components/form/fields/Field.js.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +2 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.js +8 -26
- package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.d.ts +3 -0
- package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.js +42 -8
- package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
- package/dist/components/form/fields/binary/Binary.d.ts +15 -0
- package/dist/components/form/fields/binary/Binary.d.ts.map +1 -0
- package/dist/components/form/fields/binary/Binary.js +74 -0
- package/dist/components/form/fields/binary/Binary.js.map +1 -0
- package/dist/components/form/fields/boolean/Boolean.js +1 -1
- package/dist/components/form/fields/boolean/Boolean.js.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.d.ts.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js +5 -4
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js.map +1 -1
- package/dist/components/form/fields/select/Select.d.ts.map +1 -1
- package/dist/components/form/fields/select/Select.js +29 -12
- package/dist/components/form/fields/select/Select.js.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.d.ts +8 -2
- package/dist/components/form/fields/select/SelectCollection.d.ts.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.js +52 -16
- package/dist/components/form/fields/select/SelectCollection.js.map +1 -1
- package/dist/components/form/index.d.ts +2 -0
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +2 -0
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/imageLightbox/ImageLightbox.d.ts +21 -0
- package/dist/components/imageLightbox/ImageLightbox.d.ts.map +1 -0
- package/dist/components/imageLightbox/ImageLightbox.js +169 -0
- package/dist/components/imageLightbox/ImageLightbox.js.map +1 -0
- package/dist/components/imageLightbox/index.d.ts +2 -0
- package/dist/components/imageLightbox/index.d.ts.map +1 -0
- package/dist/components/imageLightbox/index.js +18 -0
- package/dist/components/imageLightbox/index.js.map +1 -0
- package/dist/components/searchFilterBar/SearchFilterBar.d.ts +33 -0
- package/dist/components/searchFilterBar/SearchFilterBar.d.ts.map +1 -0
- package/dist/components/searchFilterBar/SearchFilterBar.js +76 -0
- package/dist/components/searchFilterBar/SearchFilterBar.js.map +1 -0
- package/dist/components/searchFilterBar/index.d.ts +2 -0
- package/dist/components/searchFilterBar/index.d.ts.map +1 -0
- package/dist/components/searchFilterBar/index.js +18 -0
- package/dist/components/searchFilterBar/index.js.map +1 -0
- package/dist/components/supportTicket/AttachmentChips.d.ts +10 -0
- package/dist/components/supportTicket/AttachmentChips.d.ts.map +1 -0
- package/dist/components/supportTicket/AttachmentChips.js +122 -0
- package/dist/components/supportTicket/AttachmentChips.js.map +1 -0
- package/dist/components/supportTicket/InterfaceReferenceTags.d.ts +43 -0
- package/dist/components/supportTicket/InterfaceReferenceTags.d.ts.map +1 -0
- package/dist/components/supportTicket/InterfaceReferenceTags.js +31 -0
- package/dist/components/supportTicket/InterfaceReferenceTags.js.map +1 -0
- package/dist/components/supportTicket/ReferencePicker.d.ts +87 -0
- package/dist/components/supportTicket/ReferencePicker.d.ts.map +1 -0
- package/dist/components/supportTicket/ReferencePicker.js +295 -0
- package/dist/components/supportTicket/ReferencePicker.js.map +1 -0
- package/dist/components/supportTicket/TicketHeader.d.ts +86 -0
- package/dist/components/supportTicket/TicketHeader.d.ts.map +1 -0
- package/dist/components/supportTicket/TicketHeader.js +149 -0
- package/dist/components/supportTicket/TicketHeader.js.map +1 -0
- package/dist/components/supportTicket/TicketMetaTags.d.ts +32 -0
- package/dist/components/supportTicket/TicketMetaTags.d.ts.map +1 -0
- package/dist/components/supportTicket/TicketMetaTags.js +21 -0
- package/dist/components/supportTicket/TicketMetaTags.js.map +1 -0
- package/dist/components/supportTicket/TicketReplyBox.d.ts +63 -0
- package/dist/components/supportTicket/TicketReplyBox.d.ts.map +1 -0
- package/dist/components/supportTicket/TicketReplyBox.js +115 -0
- package/dist/components/supportTicket/TicketReplyBox.js.map +1 -0
- package/dist/components/supportTicket/attachmentAccess.d.ts +37 -0
- package/dist/components/supportTicket/attachmentAccess.d.ts.map +1 -0
- package/dist/components/supportTicket/attachmentAccess.js +132 -0
- package/dist/components/supportTicket/attachmentAccess.js.map +1 -0
- package/dist/components/supportTicket/fileToAttachment.d.ts +9 -0
- package/dist/components/supportTicket/fileToAttachment.d.ts.map +1 -0
- package/dist/components/supportTicket/fileToAttachment.js +28 -0
- package/dist/components/supportTicket/fileToAttachment.js.map +1 -0
- package/dist/components/supportTicket/index.d.ts +11 -0
- package/dist/components/supportTicket/index.d.ts.map +1 -0
- package/dist/components/supportTicket/index.js +27 -0
- package/dist/components/supportTicket/index.js.map +1 -0
- package/dist/components/supportTicket/meta.d.ts +71 -0
- package/dist/components/supportTicket/meta.d.ts.map +1 -0
- package/dist/components/supportTicket/meta.js +115 -0
- package/dist/components/supportTicket/meta.js.map +1 -0
- package/dist/components/supportTicket/ticketModel.d.ts +102 -0
- package/dist/components/supportTicket/ticketModel.d.ts.map +1 -0
- package/dist/components/supportTicket/ticketModel.js +3 -0
- package/dist/components/supportTicket/ticketModel.js.map +1 -0
- package/dist/components/ticketReferences/TicketReferences.d.ts +52 -0
- package/dist/components/ticketReferences/TicketReferences.d.ts.map +1 -0
- package/dist/components/ticketReferences/TicketReferences.js +492 -0
- package/dist/components/ticketReferences/TicketReferences.js.map +1 -0
- package/dist/components/ticketReferences/index.d.ts +3 -0
- package/dist/components/ticketReferences/index.d.ts.map +1 -0
- package/dist/components/ticketReferences/index.js +6 -0
- package/dist/components/ticketReferences/index.js.map +1 -0
- package/dist/components/ticketThread/TicketThread.d.ts +57 -0
- package/dist/components/ticketThread/TicketThread.d.ts.map +1 -0
- package/dist/components/ticketThread/TicketThread.js +299 -0
- package/dist/components/ticketThread/TicketThread.js.map +1 -0
- package/dist/components/ticketThread/index.d.ts +3 -0
- package/dist/components/ticketThread/index.d.ts.map +1 -0
- package/dist/components/ticketThread/index.js +7 -0
- package/dist/components/ticketThread/index.js.map +1 -0
- package/dist/helpers/common.d.ts +5 -0
- package/dist/helpers/common.d.ts.map +1 -1
- package/dist/helpers/common.js +20 -1
- package/dist/helpers/common.js.map +1 -1
- package/dist/helpers/scrollbar.d.ts +2 -0
- package/dist/helpers/scrollbar.d.ts.map +1 -0
- package/dist/helpers/scrollbar.js +25 -0
- package/dist/helpers/scrollbar.js.map +1 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/providers/StorageProvider.d.ts +13 -0
- package/dist/providers/StorageProvider.d.ts.map +1 -1
- package/dist/providers/StorageProvider.js +29 -5
- package/dist/providers/StorageProvider.js.map +1 -1
- package/package.json +6 -5
- package/src/components/FocusedEditing/index.tsx +17 -2
- package/src/components/composer/Composer.stories.tsx +238 -0
- package/src/components/composer/Composer.tsx +545 -0
- package/src/components/composer/helpers.ts +74 -0
- package/src/components/composer/index.ts +2 -0
- package/src/components/dpqlEditor/DpqlEditor.stories.tsx +74 -2
- package/src/components/form/engine/CompactRow.tsx +561 -283
- package/src/components/form/engine/CompactToolbar.tsx +216 -182
- package/src/components/form/engine/FormEngine.stories.tsx +1736 -162
- package/src/components/form/engine/FormEngine.tsx +886 -170
- package/src/components/form/engine/FormEngineRemote.stories.tsx +377 -0
- package/src/components/form/engine/_structuredData/StructuredDataView.stories.tsx +8 -0
- package/src/components/form/engine/compactRowContext.ts +14 -0
- package/src/components/form/engine/compactRowStyles.ts +273 -146
- package/src/components/form/engine/compactToolbarContext.ts +1 -0
- package/src/components/form/engine/optionActions.ts +40 -0
- package/src/components/form/engine/readFirst.ts +169 -10
- package/src/components/form/engine/rendererTypes.ts +55 -0
- package/src/components/form/engine/variants/FormEngineVariants.stories.tsx +159 -0
- package/src/components/form/engine/variants/VariantCalmTable.tsx +242 -0
- package/src/components/form/engine/variants/VariantCards.tsx +212 -0
- package/src/components/form/engine/variants/VariantFocus.tsx +382 -0
- package/src/components/form/engine/variants/VariantMinimal.tsx +170 -0
- package/src/components/form/engine/variants/focusDemo.ts +145 -0
- package/src/components/form/engine/variants/variantModel.ts +216 -0
- package/src/components/form/engine/variants/variantParts.tsx +313 -0
- package/src/components/form/expressions/ExpressionField.stories.tsx +84 -2
- package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +213 -19
- package/src/components/form/expressions/builder/index.tsx +12 -1
- package/src/components/form/expressions/useExpressions.ts +4 -1
- package/src/components/form/fields/Field.stories.tsx +40 -0
- package/src/components/form/fields/Field.tsx +10 -1
- package/src/components/form/fields/allowed-values/AllowedValues.tsx +13 -36
- package/src/components/form/fields/array/ArrayAutoField.stories.tsx +64 -0
- package/src/components/form/fields/auto/AutoFormField.stories.tsx +146 -2
- package/src/components/form/fields/auto/AutoFormField.tsx +42 -1
- package/src/components/form/fields/binary/Binary.stories.tsx +122 -0
- package/src/components/form/fields/binary/Binary.tsx +81 -0
- package/src/components/form/fields/boolean/Boolean.stories.tsx +58 -0
- package/src/components/form/fields/boolean/Boolean.tsx +1 -1
- package/src/components/form/fields/byte-size/ByteSize.stories.tsx +24 -0
- package/src/components/form/fields/color/Color.stories.tsx +30 -0
- package/src/components/form/fields/cron/Cron.stories.tsx +8 -0
- package/src/components/form/fields/date/Date.stories.tsx +22 -0
- package/src/components/form/fields/file/File.stories.tsx +48 -0
- package/src/components/form/fields/long-string/LongString.stories.tsx +24 -0
- package/src/components/form/fields/markdown/Markdown.stories.tsx +8 -0
- package/src/components/form/fields/multi-select/MultiSelectFormField.tsx +14 -10
- package/src/components/form/fields/number/Number.stories.tsx +31 -0
- package/src/components/form/fields/object/Object.stories.tsx +88 -0
- package/src/components/form/fields/radio-group/RadioGroup.stories.tsx +24 -0
- package/src/components/form/fields/rich-text/RichText.stories.tsx +23 -0
- package/src/components/form/fields/schema-definition/SchemaDefinitionField.stories.tsx +72 -0
- package/src/components/form/fields/select/Select.stories.tsx +108 -0
- package/src/components/form/fields/select/Select.tsx +38 -12
- package/src/components/form/fields/select/SelectCollection.tsx +78 -17
- package/src/components/form/fields/string/String.stories.tsx +16 -0
- package/src/components/form/fields/template/TemplateField.stories.tsx +160 -0
- package/src/components/form/fields/url/Url.stories.tsx +24 -0
- package/src/components/form/index.tsx +2 -0
- package/src/components/imageLightbox/ImageLightbox.stories.tsx +238 -0
- package/src/components/imageLightbox/ImageLightbox.tsx +236 -0
- package/src/components/imageLightbox/index.ts +1 -0
- package/src/components/log/Log.stories.tsx +96 -0
- package/src/components/menu/Menu.stories.tsx +54 -0
- package/src/components/qonsoleSmartInput/QonsoleSmartInput.stories.tsx +83 -37
- package/src/components/searchFilterBar/SearchFilterBar.tsx +131 -0
- package/src/components/searchFilterBar/index.ts +1 -0
- package/src/components/smartEditor/SmartEditor.stories.tsx +32 -0
- package/src/components/supportTicket/AttachmentChips.stories.tsx +171 -0
- package/src/components/supportTicket/AttachmentChips.tsx +147 -0
- package/src/components/supportTicket/InterfaceReferenceTags.stories.tsx +171 -0
- package/src/components/supportTicket/InterfaceReferenceTags.tsx +96 -0
- package/src/components/supportTicket/ReferencePicker.stories.tsx +519 -0
- package/src/components/supportTicket/ReferencePicker.tsx +563 -0
- package/src/components/supportTicket/TicketConversation.stories.tsx +121 -0
- package/src/components/supportTicket/TicketHeader.stories.tsx +183 -0
- package/src/components/supportTicket/TicketHeader.tsx +247 -0
- package/src/components/supportTicket/TicketMetaTags.stories.tsx +103 -0
- package/src/components/supportTicket/TicketMetaTags.tsx +85 -0
- package/src/components/supportTicket/TicketReplyBox.stories.tsx +351 -0
- package/src/components/supportTicket/TicketReplyBox.tsx +146 -0
- package/src/components/supportTicket/attachmentAccess.ts +97 -0
- package/src/components/supportTicket/fileToAttachment.ts +24 -0
- package/src/components/supportTicket/index.ts +10 -0
- package/src/components/supportTicket/meta.ts +185 -0
- package/src/components/supportTicket/ticketModel.ts +131 -0
- package/src/components/ticketReferences/TicketReferences.stories.tsx +315 -0
- package/src/components/ticketReferences/TicketReferences.tsx +886 -0
- package/src/components/ticketReferences/index.ts +2 -0
- package/src/components/ticketThread/TicketThread.stories.tsx +828 -0
- package/src/components/ticketThread/TicketThread.tsx +704 -0
- package/src/components/ticketThread/index.ts +6 -0
- package/src/helpers/common.ts +19 -0
- package/src/helpers/scrollbar.ts +32 -0
- package/src/hooks/useFetch/useFetch.stories.tsx +30 -0
- package/src/hooks/useStorage/useStorage.stories.tsx +82 -0
- package/src/hooks/useWebSocket/useWebsocket.stories.tsx +127 -1
- package/src/index.tsx +8 -1
- package/src/providers/StorageProvider.tsx +30 -4
- package/src/stores/currentUser/currentUser.stories.tsx +49 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { ReqoreControlGroup, ReqoreInput, ReqoreTag } from '@qoretechnologies/reqore';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { ImageLightbox } from '../imageLightbox';
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* Renders the files staged on a ticket composer as removable chips. Image files — whether
|
|
7
|
+
* uploaded, pasted, or captured as a screenshot — show a thumbnail and open the shared
|
|
8
|
+
* `ImageLightbox` on click (paging across the staged images, click-to-zoom); everything else
|
|
9
|
+
* shows a paperclip + filename. When `onRename` is given, a pencil turns the chip into an
|
|
10
|
+
* inline input so a captured `screenshot-<timestamp>.png` can be given a meaningful name (a
|
|
11
|
+
* File's name is read-only, so the host swaps in a renamed copy).
|
|
12
|
+
*
|
|
13
|
+
* The thumbnail object URLs ARE the full-image URLs the lightbox shows, so they're created
|
|
14
|
+
* once here and revoked when the file set changes or on unmount. Renders nothing when empty.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export interface IAttachmentChipsProps {
|
|
18
|
+
files: File[];
|
|
19
|
+
onRemove: (index: number) => void;
|
|
20
|
+
/** Rename a staged file. Omit to make chips non-renamable. The host replaces the File with a
|
|
21
|
+
* renamed copy (`new File([file], newName, { type: file.type })`). */
|
|
22
|
+
onRename?: (index: number, newName: string) => void;
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const AttachmentChips = ({ files, onRemove, onRename, disabled }: IAttachmentChipsProps) => {
|
|
27
|
+
const [thumbnails, setThumbnails] = useState<string[]>([]);
|
|
28
|
+
// index into the image subset that's open in the lightbox, or null when closed.
|
|
29
|
+
const [previewIndex, setPreviewIndex] = useState<number | null>(null);
|
|
30
|
+
// the file index currently being renamed inline, plus its working name.
|
|
31
|
+
const [editingIndex, setEditingIndex] = useState<number | null>(null);
|
|
32
|
+
const [draftName, setDraftName] = useState<string>('');
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const urls = files.map((file) =>
|
|
36
|
+
file.type.startsWith('image/') ? URL.createObjectURL(file) : ''
|
|
37
|
+
);
|
|
38
|
+
setThumbnails(urls);
|
|
39
|
+
return () => urls.forEach((url) => url && URL.revokeObjectURL(url));
|
|
40
|
+
}, [files]);
|
|
41
|
+
|
|
42
|
+
if (!files.length) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// The staged images in order, each with its original file index + object URL — the set the
|
|
47
|
+
// lightbox pages across. Non-image files are skipped.
|
|
48
|
+
const imageEntries = files
|
|
49
|
+
.map((file, index) => ({ file, index, url: thumbnails[index] }))
|
|
50
|
+
.filter((entry) => entry.url);
|
|
51
|
+
|
|
52
|
+
const startRename = (index: number) => {
|
|
53
|
+
setDraftName(files[index].name);
|
|
54
|
+
setEditingIndex(index);
|
|
55
|
+
};
|
|
56
|
+
const commitRename = () => {
|
|
57
|
+
if (editingIndex === null) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const name = draftName.trim();
|
|
61
|
+
if (onRename && name && name !== files[editingIndex].name) {
|
|
62
|
+
onRename(editingIndex, name);
|
|
63
|
+
}
|
|
64
|
+
setEditingIndex(null);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<>
|
|
69
|
+
<ReqoreControlGroup wrap gapSize='small' verticalAlign='center'>
|
|
70
|
+
{files.map((file, index) => {
|
|
71
|
+
if (editingIndex === index) {
|
|
72
|
+
return (
|
|
73
|
+
<ReqoreInput
|
|
74
|
+
key={`edit-${index}`}
|
|
75
|
+
value={draftName}
|
|
76
|
+
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
|
77
|
+
setDraftName(event.target.value)
|
|
78
|
+
}
|
|
79
|
+
onBlur={commitRename}
|
|
80
|
+
onKeyDown={(event: React.KeyboardEvent) => {
|
|
81
|
+
if (event.key === 'Enter') {
|
|
82
|
+
commitRename();
|
|
83
|
+
} else if (event.key === 'Escape') {
|
|
84
|
+
setEditingIndex(null);
|
|
85
|
+
}
|
|
86
|
+
}}
|
|
87
|
+
size='small'
|
|
88
|
+
fixed
|
|
89
|
+
autoFocus
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const thumbnail = thumbnails[index];
|
|
95
|
+
const imageIndex = thumbnail
|
|
96
|
+
? imageEntries.findIndex((entry) => entry.index === index)
|
|
97
|
+
: -1;
|
|
98
|
+
const actions =
|
|
99
|
+
onRename && !disabled
|
|
100
|
+
? [
|
|
101
|
+
{
|
|
102
|
+
icon: 'Edit2Line' as const,
|
|
103
|
+
tooltip: 'Rename',
|
|
104
|
+
onClick: (event: React.MouseEvent<HTMLDivElement>) => {
|
|
105
|
+
event.stopPropagation();
|
|
106
|
+
startRename(index);
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
]
|
|
110
|
+
: undefined;
|
|
111
|
+
return (
|
|
112
|
+
<ReqoreTag
|
|
113
|
+
key={`${file.name}-${index}`}
|
|
114
|
+
icon={thumbnail ? undefined : 'AttachmentLine'}
|
|
115
|
+
leftIconProps={thumbnail ? { image: thumbnail } : undefined}
|
|
116
|
+
label={file.name}
|
|
117
|
+
size='small'
|
|
118
|
+
tooltip={thumbnail ? 'Click to preview' : undefined}
|
|
119
|
+
actions={actions}
|
|
120
|
+
onClick={thumbnail ? () => setPreviewIndex(imageIndex) : undefined}
|
|
121
|
+
onRemoveClick={disabled ? undefined : () => onRemove(index)}
|
|
122
|
+
/>
|
|
123
|
+
);
|
|
124
|
+
})}
|
|
125
|
+
</ReqoreControlGroup>
|
|
126
|
+
{previewIndex !== null && imageEntries[previewIndex] ? (
|
|
127
|
+
<ImageLightbox
|
|
128
|
+
images={imageEntries.map((entry) => ({
|
|
129
|
+
attachment_id: String(entry.index),
|
|
130
|
+
filename: entry.file.name,
|
|
131
|
+
}))}
|
|
132
|
+
index={previewIndex}
|
|
133
|
+
onIndex={setPreviewIndex}
|
|
134
|
+
onClose={() => setPreviewIndex(null)}
|
|
135
|
+
fetchAttachmentUrl={async (id) =>
|
|
136
|
+
imageEntries.find((entry) => String(entry.index) === id)?.url ?? ''
|
|
137
|
+
}
|
|
138
|
+
// the lightbox ids ARE the stringified file indices, so renaming from the
|
|
139
|
+
// preview funnels into the same host callback as the chip pencil
|
|
140
|
+
onRename={
|
|
141
|
+
onRename && !disabled ? (id, name) => onRename(Number(id), name) : undefined
|
|
142
|
+
}
|
|
143
|
+
/>
|
|
144
|
+
) : null}
|
|
145
|
+
</>
|
|
146
|
+
);
|
|
147
|
+
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
|
|
3
|
+
import { StoryMeta } from '../../types';
|
|
4
|
+
import { InterfaceReferenceTags } from './InterfaceReferenceTags';
|
|
5
|
+
import { IInterfaceReference } from './meta';
|
|
6
|
+
|
|
7
|
+
// Several references, including two of the same kind (workflow), as `{kind, name}`
|
|
8
|
+
// snapshots — no `resolveInterfaceIcon` is passed, so the built-in per-kind default
|
|
9
|
+
// icons are exercised.
|
|
10
|
+
const REFERENCES: IInterfaceReference[] = [
|
|
11
|
+
{ interface_kind: 'workflow', interface_name: 'order-sync:1.2', reference_id: 'r1' },
|
|
12
|
+
{ interface_kind: 'workflow', interface_name: 'invoice-sync:2.3', reference_id: 'r2' },
|
|
13
|
+
{ interface_kind: 'service', interface_name: 'notification-svc', reference_id: 'r3' },
|
|
14
|
+
{ interface_kind: 'job', interface_name: 'nightly-cleanup', reference_id: 'r4' },
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const meta = {
|
|
18
|
+
component: InterfaceReferenceTags,
|
|
19
|
+
title: 'Components/InterfaceReferenceTags',
|
|
20
|
+
args: {
|
|
21
|
+
references: REFERENCES,
|
|
22
|
+
label: 'References',
|
|
23
|
+
},
|
|
24
|
+
} as StoryMeta<typeof InterfaceReferenceTags>;
|
|
25
|
+
|
|
26
|
+
export default meta;
|
|
27
|
+
type Story = StoryObj<typeof meta>;
|
|
28
|
+
|
|
29
|
+
/** The ticket-header use: a "References" label followed by one chip per reference. */
|
|
30
|
+
export const Default: Story = {
|
|
31
|
+
parameters: {
|
|
32
|
+
docs: {
|
|
33
|
+
description: {
|
|
34
|
+
story:
|
|
35
|
+
"Renders the ticket-header use — a 'References' label followed by one chip per reference.",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
async play({ canvasElement }) {
|
|
40
|
+
const canvas = within(canvasElement);
|
|
41
|
+
await expect(canvas.getByText('References')).toBeInTheDocument();
|
|
42
|
+
await expect(canvas.getByText('order-sync:1.2')).toBeInTheDocument();
|
|
43
|
+
await expect(canvas.getByText('invoice-sync:2.3')).toBeInTheDocument();
|
|
44
|
+
await expect(canvas.getByText('notification-svc')).toBeInTheDocument();
|
|
45
|
+
await expect(canvas.getByText('nightly-cleanup')).toBeInTheDocument();
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* When `onInterfaceClick` is passed the chips become interactive; clicking one
|
|
51
|
+
* calls it with that reference (the customer view, which can open the interface on
|
|
52
|
+
* its own instance, wires this).
|
|
53
|
+
*/
|
|
54
|
+
export const Clickable: Story = {
|
|
55
|
+
parameters: {
|
|
56
|
+
docs: {
|
|
57
|
+
description: {
|
|
58
|
+
story:
|
|
59
|
+
'Renders interactive chips by passing onInterfaceClick — clicking a chip calls it with that reference, as the customer view (which can open the interface on its own instance) wires.',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
args: { onInterfaceClick: fn() },
|
|
64
|
+
async play({ canvasElement, args }) {
|
|
65
|
+
const canvas = within(canvasElement);
|
|
66
|
+
await userEvent.click(canvas.getByText('invoice-sync:2.3'));
|
|
67
|
+
await waitFor(() =>
|
|
68
|
+
expect(args.onInterfaceClick).toHaveBeenCalledWith(
|
|
69
|
+
expect.objectContaining({
|
|
70
|
+
interface_kind: 'workflow',
|
|
71
|
+
interface_name: 'invoice-sync:2.3',
|
|
72
|
+
})
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The composer use: the chips are references you're about to send, so each carries
|
|
80
|
+
* an "×" to detach it. The thread's chips deliberately don't — there, they're a
|
|
81
|
+
* record of what was sent, and an × would offer to undo something that already
|
|
82
|
+
* happened.
|
|
83
|
+
*/
|
|
84
|
+
export const Removable: Story = {
|
|
85
|
+
parameters: {
|
|
86
|
+
docs: {
|
|
87
|
+
description: {
|
|
88
|
+
story:
|
|
89
|
+
'Renders the composer use, where every chip carries a remove "×". Clicking one calls onRemove with that reference so the composer can detach it before sending.',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
args: { label: undefined, onRemove: fn() },
|
|
94
|
+
async play({ canvasElement, args }) {
|
|
95
|
+
const canvas = within(canvasElement);
|
|
96
|
+
const remove = canvasElement.querySelectorAll('.reqore-tag-remove');
|
|
97
|
+
|
|
98
|
+
// one × per chip — not just on the one under the cursor
|
|
99
|
+
await expect(remove.length).toBe(4);
|
|
100
|
+
|
|
101
|
+
await userEvent.click(remove[1] as HTMLElement);
|
|
102
|
+
await waitFor(() =>
|
|
103
|
+
expect(args.onRemove).toHaveBeenCalledWith(
|
|
104
|
+
expect.objectContaining({
|
|
105
|
+
interface_kind: 'workflow',
|
|
106
|
+
interface_name: 'invoice-sync:2.3',
|
|
107
|
+
})
|
|
108
|
+
)
|
|
109
|
+
);
|
|
110
|
+
// the chips themselves are unchanged — removal is the host's to apply
|
|
111
|
+
await expect(canvas.getByText('order-sync:1.2')).toBeInTheDocument();
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/** The in-thread use: no label — the chips sit under a message. */
|
|
116
|
+
export const NoLabel: Story = {
|
|
117
|
+
parameters: {
|
|
118
|
+
docs: {
|
|
119
|
+
description: {
|
|
120
|
+
story:
|
|
121
|
+
'Renders the in-thread use with label undefined — no label, just the chips as they sit under a message.',
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
args: { label: undefined },
|
|
126
|
+
async play({ canvasElement }) {
|
|
127
|
+
const canvas = within(canvasElement);
|
|
128
|
+
await expect(canvas.queryByText('References')).not.toBeInTheDocument();
|
|
129
|
+
await expect(canvas.getByText('order-sync:1.2')).toBeInTheDocument();
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* With no references the component renders nothing — no empty header, no chips —
|
|
135
|
+
* so a ticket without references shows a clean gap rather than a stray "References:"
|
|
136
|
+
* label. The dashed frame below is a story-only caption; the empty space inside it
|
|
137
|
+
* is the component.
|
|
138
|
+
*/
|
|
139
|
+
export const Empty: Story = {
|
|
140
|
+
parameters: {
|
|
141
|
+
docs: {
|
|
142
|
+
description: {
|
|
143
|
+
story:
|
|
144
|
+
'Renders the empty case — with no references the component renders nothing (no header, no chips), shown inside a dashed story-only caption frame.',
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
args: { references: [] },
|
|
149
|
+
decorators: [
|
|
150
|
+
(Story) => (
|
|
151
|
+
<div
|
|
152
|
+
style={{
|
|
153
|
+
border: '1px dashed rgba(150, 150, 150, 0.4)',
|
|
154
|
+
borderRadius: 6,
|
|
155
|
+
padding: 12,
|
|
156
|
+
fontSize: 12,
|
|
157
|
+
opacity: 0.6,
|
|
158
|
+
}}
|
|
159
|
+
>
|
|
160
|
+
No references → the row renders nothing (no empty header, no chips):
|
|
161
|
+
<Story />
|
|
162
|
+
</div>
|
|
163
|
+
),
|
|
164
|
+
],
|
|
165
|
+
async play({ canvasElement }) {
|
|
166
|
+
// The dashed frame + caption are the decorator; the component itself renders
|
|
167
|
+
// nothing — no header text, no chips.
|
|
168
|
+
await expect(within(canvasElement).queryByText('References')).not.toBeInTheDocument();
|
|
169
|
+
await expect(within(canvasElement).queryByText('order-sync:1.2')).not.toBeInTheDocument();
|
|
170
|
+
},
|
|
171
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ReqoreControlGroup, ReqoreP, ReqoreTag } from '@qoretechnologies/reqore';
|
|
2
|
+
import { TSizes } from '@qoretechnologies/reqore/dist/constants/sizes';
|
|
3
|
+
import { IReqoreIconName } from '@qoretechnologies/reqore/dist/types/icons';
|
|
4
|
+
import { ComponentProps } from 'react';
|
|
5
|
+
import { defaultInterfaceIcon, IInterfaceReference } from './meta';
|
|
6
|
+
|
|
7
|
+
export interface IInterfaceReferenceTagsProps {
|
|
8
|
+
references?: IInterfaceReference[];
|
|
9
|
+
/** Optional leading label, e.g. "References" in a ticket header. Omitted (the
|
|
10
|
+
* default) inside the thread, where the chips sit under a message. */
|
|
11
|
+
label?: string;
|
|
12
|
+
/** Resolve a per-kind icon. Consumers with their own icon vocabulary (the IDE)
|
|
13
|
+
* pass one; when omitted the built-in per-kind default is used. */
|
|
14
|
+
resolveInterfaceIcon?: (kind: string) => IReqoreIconName;
|
|
15
|
+
/** Open a referenced interface; chips are static (informational) when omitted —
|
|
16
|
+
* e.g. the staff view, which can't reach the customer's instance. */
|
|
17
|
+
onInterfaceClick?: (reference: IInterfaceReference) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Detach a reference. Each chip grows an "×" when given — for the composer,
|
|
20
|
+
* where the chips are the references you're about to send and so are still
|
|
21
|
+
* editable. Omitted everywhere the chips are a record of what WAS sent (the
|
|
22
|
+
* thread, the ticket header, the Qonsole card): those aren't editable, and an
|
|
23
|
+
* × on them would offer to undo something that already happened.
|
|
24
|
+
*/
|
|
25
|
+
onRemove?: (reference: IInterfaceReference) => void;
|
|
26
|
+
size?: TSizes;
|
|
27
|
+
/** Chip styling — pass `intent`/`customTheme` (e.g. `{ main: 'custom1' }`) to tint
|
|
28
|
+
* the reference chips to a surface's accent (the Qonsole/helpdesk composer does). */
|
|
29
|
+
intent?: ComponentProps<typeof ReqoreTag>['intent'];
|
|
30
|
+
customTheme?: ComponentProps<typeof ReqoreTag>['customTheme'];
|
|
31
|
+
/** Resolve a per-reference image — e.g. a qog's trigger-app logo, the way the
|
|
32
|
+
* qogs list does. When this (or the reference's own `logo`) returns a value, the
|
|
33
|
+
* chip shows the image in place of the kind icon. */
|
|
34
|
+
resolveInterfaceImage?: (reference: IInterfaceReference) => string | undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The chips for the Qorus interfaces a ticket or message references, as
|
|
39
|
+
* `{kind, name}` snapshots. The single renderer for interface references across
|
|
40
|
+
* every support surface — the ticket header (with a "References" label), the
|
|
41
|
+
* thread (per message, no label), and the Qonsole card — so a reference looks
|
|
42
|
+
* identical everywhere. Renders nothing when there are no references.
|
|
43
|
+
*/
|
|
44
|
+
export const InterfaceReferenceTags = ({
|
|
45
|
+
references,
|
|
46
|
+
label,
|
|
47
|
+
resolveInterfaceIcon,
|
|
48
|
+
onInterfaceClick,
|
|
49
|
+
onRemove,
|
|
50
|
+
size = 'small',
|
|
51
|
+
intent,
|
|
52
|
+
customTheme,
|
|
53
|
+
resolveInterfaceImage,
|
|
54
|
+
}: IInterfaceReferenceTagsProps) => {
|
|
55
|
+
if (!references?.length) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const resolveIcon = resolveInterfaceIcon ?? defaultInterfaceIcon;
|
|
59
|
+
return (
|
|
60
|
+
<ReqoreControlGroup verticalAlign='center' wrap gapSize='small'>
|
|
61
|
+
{label ? (
|
|
62
|
+
<ReqoreP size='small' effect={{ opacity: 0.6 }}>
|
|
63
|
+
{label}
|
|
64
|
+
</ReqoreP>
|
|
65
|
+
) : null}
|
|
66
|
+
{references.map((reference) => {
|
|
67
|
+
// an app-triggered qog shows the trigger app's logo (stored, or resolved by
|
|
68
|
+
// a viewer with the app catalogue); everything else shows its kind glyph.
|
|
69
|
+
const image = reference.logo ?? resolveInterfaceImage?.(reference);
|
|
70
|
+
return (
|
|
71
|
+
<ReqoreTag
|
|
72
|
+
key={
|
|
73
|
+
reference.reference_id ??
|
|
74
|
+
`${reference.interface_kind}:${reference.interface_name}`
|
|
75
|
+
}
|
|
76
|
+
icon={image ? undefined : resolveIcon(reference.interface_kind)}
|
|
77
|
+
leftIconProps={image ? { image } : undefined}
|
|
78
|
+
labelKey={reference.interface_kind}
|
|
79
|
+
label={reference.interface_name}
|
|
80
|
+
size={size}
|
|
81
|
+
intent={intent}
|
|
82
|
+
customTheme={customTheme}
|
|
83
|
+
tooltip={
|
|
84
|
+
onInterfaceClick
|
|
85
|
+
? `Open ${reference.interface_kind} ${reference.interface_name}`
|
|
86
|
+
: `${reference.interface_kind}: ${reference.interface_name}`
|
|
87
|
+
}
|
|
88
|
+
onClick={onInterfaceClick ? () => onInterfaceClick(reference) : undefined}
|
|
89
|
+
onRemoveClick={onRemove ? () => onRemove(reference) : undefined}
|
|
90
|
+
removeTooltip={`Remove ${reference.interface_kind} ${reference.interface_name}`}
|
|
91
|
+
/>
|
|
92
|
+
);
|
|
93
|
+
})}
|
|
94
|
+
</ReqoreControlGroup>
|
|
95
|
+
);
|
|
96
|
+
};
|