@qoretechnologies/reqraft 0.10.46-pr.95.ge595a13 → 0.10.46
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 +12 -0
- package/.tasks/INDEX.md +1 -0
- package/.tasks/TEMPLATE_TOKEN_GRAMMAR.md +72 -0
- package/dist/components/composer/Composer.d.ts.map +1 -1
- package/dist/components/composer/Composer.js +5 -1
- package/dist/components/composer/Composer.js.map +1 -1
- package/dist/components/form/engine/CompactRow.d.ts +14 -0
- package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
- package/dist/components/form/engine/CompactRow.js +199 -41
- 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 +72 -72
- package/dist/components/form/engine/CompactToolbar.js.map +1 -1
- package/dist/components/form/engine/FormEngine.d.ts +65 -1
- package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
- package/dist/components/form/engine/FormEngine.js +276 -108
- package/dist/components/form/engine/FormEngine.js.map +1 -1
- package/dist/components/form/engine/_structuredData/SchemaDataView.d.ts.map +1 -1
- package/dist/components/form/engine/_structuredData/SchemaDataView.js +55 -13
- package/dist/components/form/engine/_structuredData/SchemaDataView.js.map +1 -1
- package/dist/components/form/engine/_structuredData/structuredData.d.ts +15 -0
- package/dist/components/form/engine/_structuredData/structuredData.d.ts.map +1 -1
- package/dist/components/form/engine/_structuredData/structuredData.js +22 -1
- package/dist/components/form/engine/_structuredData/structuredData.js.map +1 -1
- package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowStyles.js +15 -3
- package/dist/components/form/engine/compactRowStyles.js.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.d.ts +8 -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/readFirst.d.ts +24 -0
- package/dist/components/form/engine/readFirst.d.ts.map +1 -1
- package/dist/components/form/engine/readFirst.js +84 -6
- package/dist/components/form/engine/readFirst.js.map +1 -1
- package/dist/components/form/fields/Field.d.ts.map +1 -1
- package/dist/components/form/fields/Field.js +1 -1
- package/dist/components/form/fields/Field.js.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +12 -2
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.js +16 -6
- package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.js +64 -14
- package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
- package/dist/components/form/fields/long-string/LongString.d.ts +9 -1
- package/dist/components/form/fields/long-string/LongString.d.ts.map +1 -1
- package/dist/components/form/fields/long-string/LongString.js +25 -4
- package/dist/components/form/fields/long-string/LongString.js.map +1 -1
- package/dist/components/form/fields/template/ExampleValueModal.d.ts +29 -0
- package/dist/components/form/fields/template/ExampleValueModal.d.ts.map +1 -0
- package/dist/components/form/fields/template/ExampleValueModal.js +122 -0
- package/dist/components/form/fields/template/ExampleValueModal.js.map +1 -0
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts.map +1 -1
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.js +3 -3
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.js.map +1 -1
- package/dist/components/form/fields/template/TemplateField.d.ts.map +1 -1
- package/dist/components/form/fields/template/TemplateField.js +81 -27
- package/dist/components/form/fields/template/TemplateField.js.map +1 -1
- package/dist/components/form/fields/template/__fixtures__/longExampleValue.d.ts +2 -0
- package/dist/components/form/fields/template/__fixtures__/longExampleValue.d.ts.map +1 -0
- package/dist/components/form/fields/template/__fixtures__/longExampleValue.js +9 -0
- package/dist/components/form/fields/template/__fixtures__/longExampleValue.js.map +1 -0
- package/dist/components/form/index.d.ts +1 -0
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +1 -0
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/searchFilterBar/SearchFilterBar.d.ts +12 -1
- package/dist/components/searchFilterBar/SearchFilterBar.d.ts.map +1 -1
- package/dist/components/searchFilterBar/SearchFilterBar.js +2 -2
- package/dist/components/searchFilterBar/SearchFilterBar.js.map +1 -1
- package/dist/components/ticketThread/TicketThread.d.ts.map +1 -1
- package/dist/components/ticketThread/TicketThread.js +1 -1
- package/dist/components/ticketThread/TicketThread.js.map +1 -1
- package/dist/helpers/common.d.ts +8 -1
- package/dist/helpers/common.d.ts.map +1 -1
- package/dist/helpers/common.js +9 -1
- package/dist/helpers/common.js.map +1 -1
- package/dist/helpers/expressions.d.ts.map +1 -1
- package/dist/helpers/expressions.js +15 -2
- package/dist/helpers/expressions.js.map +1 -1
- package/dist/helpers/optionUiTypes.d.ts +30 -0
- package/dist/helpers/optionUiTypes.d.ts.map +1 -0
- package/dist/helpers/optionUiTypes.js +156 -0
- package/dist/helpers/optionUiTypes.js.map +1 -0
- package/dist/helpers/options.d.ts +19 -0
- package/dist/helpers/options.d.ts.map +1 -1
- package/dist/helpers/options.js +25 -1
- package/dist/helpers/options.js.map +1 -1
- package/dist/helpers/providerValue.d.ts +68 -0
- package/dist/helpers/providerValue.d.ts.map +1 -0
- package/dist/helpers/providerValue.js +50 -0
- package/dist/helpers/providerValue.js.map +1 -0
- package/dist/helpers/singleLineString.d.ts +37 -0
- package/dist/helpers/singleLineString.d.ts.map +1 -0
- package/dist/helpers/singleLineString.js +66 -0
- package/dist/helpers/singleLineString.js.map +1 -0
- package/dist/helpers/templates.d.ts +100 -0
- package/dist/helpers/templates.d.ts.map +1 -1
- package/dist/helpers/templates.js +237 -4
- package/dist/helpers/templates.js.map +1 -1
- package/dist/helpers/validations.d.ts +103 -2
- package/dist/helpers/validations.d.ts.map +1 -1
- package/dist/helpers/validations.js +858 -91
- package/dist/helpers/validations.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/composer/Composer.tsx +7 -3
- package/src/components/form/engine/CompactRow.tsx +279 -33
- package/src/components/form/engine/CompactToolbar.tsx +105 -98
- package/src/components/form/engine/FormEngine.stories.tsx +995 -12
- package/src/components/form/engine/FormEngine.tsx +377 -94
- package/src/components/form/engine/_structuredData/SchemaDataView.stories.tsx +104 -0
- package/src/components/form/engine/_structuredData/SchemaDataView.tsx +158 -101
- package/src/components/form/engine/_structuredData/structuredData.ts +20 -0
- package/src/components/form/engine/compactRowStyles.ts +55 -1
- package/src/components/form/engine/compactToolbarContext.ts +8 -0
- package/src/components/form/engine/readFirst.ts +125 -35
- package/src/components/form/fields/Field.tsx +3 -1
- package/src/components/form/fields/allowed-values/AllowedValues.tsx +24 -2
- package/src/components/form/fields/array/ArrayAutoField.stories.tsx +81 -0
- package/src/components/form/fields/auto/AutoFormField.stories.tsx +47 -1
- package/src/components/form/fields/auto/AutoFormField.tsx +73 -11
- package/src/components/form/fields/long-string/LongString.tsx +46 -5
- package/src/components/form/fields/schema-definition/SchemaDefinitionField.stories.tsx +5 -0
- package/src/components/form/fields/template/ExampleValueModal.stories.tsx +114 -0
- package/src/components/form/fields/template/ExampleValueModal.tsx +99 -0
- package/src/components/form/fields/template/ReadOnlyTemplateTag.tsx +8 -4
- package/src/components/form/fields/template/TemplateField.stories.tsx +389 -1
- package/src/components/form/fields/template/TemplateField.tsx +100 -35
- package/src/components/form/fields/template/__fixtures__/longExampleValue.ts +7 -0
- package/src/components/form/index.tsx +1 -0
- package/src/components/searchFilterBar/SearchFilterBar.stories.tsx +62 -0
- package/src/components/searchFilterBar/SearchFilterBar.tsx +13 -0
- package/src/components/ticketThread/TicketThread.tsx +7 -0
- package/src/helpers/common.ts +11 -1
- package/src/helpers/expressions.ts +16 -2
- package/src/helpers/optionUiTypes.ts +161 -0
- package/src/helpers/options.ts +23 -0
- package/src/helpers/providerValue.ts +125 -0
- package/src/helpers/singleLineString.ts +61 -0
- package/src/helpers/templates.ts +272 -3
- package/src/helpers/validations.ts +1177 -109
- package/src/hooks/useWebSocket/useWebsocket.stories.tsx +23 -2
- package/src/index.tsx +1 -0
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
import { ReqoreTextarea } from '@qoretechnologies/reqore';
|
|
2
2
|
import { IReqoreTextareaProps } from '@qoretechnologies/reqore/dist/components/Textarea';
|
|
3
|
-
import { ChangeEvent, useCallback, useEffect, useState } from 'react';
|
|
3
|
+
import { ChangeEvent, KeyboardEvent, useCallback, useEffect, useState } from 'react';
|
|
4
4
|
import { useDebounce } from 'react-use';
|
|
5
|
+
import {
|
|
6
|
+
flattenToSingleLine,
|
|
7
|
+
hasLineBreak,
|
|
8
|
+
isSingleLineStringType,
|
|
9
|
+
} from '../../../../helpers/singleLineString';
|
|
5
10
|
|
|
6
11
|
export interface ILongStringFormFieldProps extends Omit<IReqoreTextareaProps, 'onChange'> {
|
|
7
12
|
value?: string;
|
|
8
13
|
onChange?: (value: string, event?: ChangeEvent<HTMLTextAreaElement>) => void;
|
|
14
|
+
/**
|
|
15
|
+
* The field's declared type, which decides whether it holds one line.
|
|
16
|
+
*
|
|
17
|
+
* Without it every string field is a growing textarea, so a technical name, a
|
|
18
|
+
* version and an IP address all accept Enter — and become YAML keys, class
|
|
19
|
+
* names and URLs with a newline in them. See `helpers/singleLineString`.
|
|
20
|
+
*/
|
|
21
|
+
type?: string;
|
|
9
22
|
}
|
|
10
23
|
|
|
11
24
|
export const LongStringFormField = ({
|
|
12
25
|
value,
|
|
13
26
|
onChange,
|
|
14
27
|
onClearClick,
|
|
28
|
+
type,
|
|
15
29
|
...rest
|
|
16
30
|
}: ILongStringFormFieldProps) => {
|
|
17
31
|
const [localValue, setLocalValue] = useState<string>(value ?? '');
|
|
@@ -32,9 +46,32 @@ export const LongStringFormField = ({
|
|
|
32
46
|
[localValue, onChange]
|
|
33
47
|
);
|
|
34
48
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
const singleLine = isSingleLineStringType(type);
|
|
50
|
+
|
|
51
|
+
const handleChange = useCallback(
|
|
52
|
+
(event: ChangeEvent<HTMLTextAreaElement>): void => {
|
|
53
|
+
const raw = event.target.value;
|
|
54
|
+
// Flatten here rather than only blocking the Enter key: a line break also
|
|
55
|
+
// arrives by paste, by drag-and-drop, from autofill and from an IME, and
|
|
56
|
+
// a keydown guard catches none of those.
|
|
57
|
+
setLocalValue(singleLine && hasLineBreak(raw) ? flattenToSingleLine(raw) : raw);
|
|
58
|
+
},
|
|
59
|
+
[singleLine]
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// Stop Enter before it inserts anything, so the caret does not jump to a
|
|
63
|
+
// second line that is then flattened away under the cursor.
|
|
64
|
+
const handleKeyDown = useCallback(
|
|
65
|
+
(event: KeyboardEvent<HTMLTextAreaElement>): void => {
|
|
66
|
+
if (singleLine && event.key === 'Enter') {
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
}
|
|
69
|
+
(rest as { onKeyDown?: (event: KeyboardEvent<HTMLTextAreaElement>) => void }).onKeyDown?.(
|
|
70
|
+
event
|
|
71
|
+
);
|
|
72
|
+
},
|
|
73
|
+
[singleLine, rest.onKeyDown]
|
|
74
|
+
);
|
|
38
75
|
|
|
39
76
|
const handleClearClick = useCallback(() => {
|
|
40
77
|
setLocalValue('');
|
|
@@ -44,12 +81,16 @@ export const LongStringFormField = ({
|
|
|
44
81
|
|
|
45
82
|
return (
|
|
46
83
|
<ReqoreTextarea
|
|
47
|
-
|
|
84
|
+
// A one-line field must not grow, and must not offer a resize grip that
|
|
85
|
+
// implies it can hold more than it will keep.
|
|
86
|
+
scaleWithContent={!singleLine}
|
|
87
|
+
rows={singleLine ? 1 : undefined}
|
|
48
88
|
fluid
|
|
49
89
|
value={localValue}
|
|
50
90
|
onChange={handleChange}
|
|
51
91
|
onClearClick={handleClearClick}
|
|
52
92
|
{...rest}
|
|
93
|
+
onKeyDown={handleKeyDown}
|
|
53
94
|
/>
|
|
54
95
|
);
|
|
55
96
|
};
|
|
@@ -55,6 +55,11 @@ export const Empty: Story = {
|
|
|
55
55
|
await expect(canvas.getByText('Tables')).toBeInTheDocument();
|
|
56
56
|
await expect(canvas.getByText('Sequences')).toBeInTheDocument();
|
|
57
57
|
await expect(canvas.getByText('Migrations')).toBeInTheDocument();
|
|
58
|
+
// The form rows fade in after the chrome — wait for the Schema tab's own
|
|
59
|
+
// catalogue rows, or the capture shows empty shells where the form goes
|
|
60
|
+
// (rejected on build #123 for exactly that).
|
|
61
|
+
await expect(await canvas.findByText('Schema Name')).toBeInTheDocument();
|
|
62
|
+
await expect(await canvas.findByText('Version')).toBeInTheDocument();
|
|
58
63
|
},
|
|
59
64
|
};
|
|
60
65
|
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { expect, fireEvent, fn, waitFor } from 'storybook/test';
|
|
3
|
+
import { LONG_EXAMPLE_VALUE } from './__fixtures__/longExampleValue';
|
|
4
|
+
import { ReqraftTemplateExampleValueModal } from './ExampleValueModal';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
component: ReqraftTemplateExampleValueModal,
|
|
8
|
+
title: 'Components/Form/TemplateExampleValueModal',
|
|
9
|
+
args: {
|
|
10
|
+
// Standalone render — in the app the modal rides reqore's global modal
|
|
11
|
+
// queue, whose wrapper injects `isOpen`/`onClose` itself.
|
|
12
|
+
isOpen: true,
|
|
13
|
+
label: 'Attachment Body',
|
|
14
|
+
value: LONG_EXAMPLE_VALUE,
|
|
15
|
+
onClose: fn(),
|
|
16
|
+
},
|
|
17
|
+
} as Meta<typeof ReqraftTemplateExampleValueModal>;
|
|
18
|
+
|
|
19
|
+
export default meta;
|
|
20
|
+
|
|
21
|
+
export const Default: StoryObj<typeof meta> = {
|
|
22
|
+
parameters: {
|
|
23
|
+
docs: {
|
|
24
|
+
description: {
|
|
25
|
+
story:
|
|
26
|
+
'The full example-value viewer for template items whose value is too long for the picker: the whole value in a selectable, scrollable textarea filling the modal body, with a Copy action. Copy surfaces a notification either way — success, or a danger notice when the browser denies clipboard access — never an unhandled rejection.',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
play: async () => {
|
|
31
|
+
const modal = await waitFor(
|
|
32
|
+
() => {
|
|
33
|
+
const el = document.querySelector('.reqraft-template-example-value-modal');
|
|
34
|
+
if (!el) throw new Error('modal not rendered yet');
|
|
35
|
+
return el;
|
|
36
|
+
},
|
|
37
|
+
{ timeout: 10000 }
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// The modal holds the WHOLE value — down to the sentinel at the very end.
|
|
41
|
+
const textarea = modal.querySelector('textarea') as HTMLTextAreaElement;
|
|
42
|
+
await expect(textarea.value).toBe(LONG_EXAMPLE_VALUE);
|
|
43
|
+
await expect(textarea.value.endsWith('THE_VERY_END')).toBe(true);
|
|
44
|
+
|
|
45
|
+
// Copy notifies (success locally, clipboard-denied danger in headless CI).
|
|
46
|
+
const copy = Array.from(modal.querySelectorAll('button')).find((button) =>
|
|
47
|
+
button.textContent?.includes('Copy')
|
|
48
|
+
);
|
|
49
|
+
await expect(copy).toBeTruthy();
|
|
50
|
+
fireEvent.click(copy);
|
|
51
|
+
await waitFor(
|
|
52
|
+
() => {
|
|
53
|
+
if (!document.querySelector('.reqore-notification')) {
|
|
54
|
+
throw new Error('no notification yet');
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{ timeout: 10000 }
|
|
58
|
+
);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const Mobile: StoryObj<typeof meta> = {
|
|
63
|
+
args: {
|
|
64
|
+
// The overridable strings double as the prop-passthrough coverage: the
|
|
65
|
+
// play below asserts THIS notification text, proving consumers (e.g.
|
|
66
|
+
// qorus-ide under Lingui) can supply translated copy.
|
|
67
|
+
copyLabel: 'Copy value',
|
|
68
|
+
copyNotificationContent: 'Value copied',
|
|
69
|
+
copyFailedNotificationContent: 'Copy was blocked',
|
|
70
|
+
},
|
|
71
|
+
parameters: {
|
|
72
|
+
qlip: { viewport: { width: 390, height: 844 } },
|
|
73
|
+
docs: {
|
|
74
|
+
description: {
|
|
75
|
+
story:
|
|
76
|
+
'The phone presentation of the full-value modal — the textarea still fills the modal body at phone width, and every user-visible string (Copy label, both notifications) is prop-overridden here, which doubles as coverage that consumers can pass translated copy.',
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
play: async () => {
|
|
81
|
+
const modal = await waitFor(
|
|
82
|
+
() => {
|
|
83
|
+
const el = document.querySelector('.reqraft-template-example-value-modal');
|
|
84
|
+
if (!el) throw new Error('modal not rendered yet');
|
|
85
|
+
return el;
|
|
86
|
+
},
|
|
87
|
+
{ timeout: 10000 }
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const textarea = modal.querySelector('textarea') as HTMLTextAreaElement;
|
|
91
|
+
await expect(textarea.value).toBe(LONG_EXAMPLE_VALUE);
|
|
92
|
+
|
|
93
|
+
// The overridden copy label renders…
|
|
94
|
+
const copy = Array.from(modal.querySelectorAll('button')).find((button) =>
|
|
95
|
+
button.textContent?.includes('Copy value')
|
|
96
|
+
);
|
|
97
|
+
await expect(copy).toBeTruthy();
|
|
98
|
+
|
|
99
|
+
// …and the overridden notification content is what surfaces (success or
|
|
100
|
+
// clipboard-denied, both prop-supplied).
|
|
101
|
+
fireEvent.click(copy);
|
|
102
|
+
await waitFor(
|
|
103
|
+
() => {
|
|
104
|
+
const notification = document.querySelector('.reqore-notification');
|
|
105
|
+
if (!notification) throw new Error('no notification yet');
|
|
106
|
+
const text = notification.textContent || '';
|
|
107
|
+
if (!text.includes('Value copied') && !text.includes('Copy was blocked')) {
|
|
108
|
+
throw new Error(`unexpected notification: ${text}`);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{ timeout: 10000 }
|
|
112
|
+
);
|
|
113
|
+
},
|
|
114
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { ReqoreModal, ReqoreTextarea, useReqoreProperty } from '@qoretechnologies/reqore';
|
|
2
|
+
import { IReqoreModalProps } from '@qoretechnologies/reqore/dist/components/Modal';
|
|
3
|
+
import { CSSProperties, memo, useCallback } from 'react';
|
|
4
|
+
|
|
5
|
+
export interface IReqraftTemplateExampleValueModalProps extends Partial<IReqoreModalProps> {
|
|
6
|
+
/** The template field's display name — the modal heading. */
|
|
7
|
+
label?: string;
|
|
8
|
+
/** The FULL serialized example value. */
|
|
9
|
+
value: string;
|
|
10
|
+
/** Copy-action label. @default 'Copy' */
|
|
11
|
+
copyLabel?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Notification shown after a successful copy (prop-overridable so consumers
|
|
14
|
+
* can pass translated copy — same contract as `ReqoreExportModal`).
|
|
15
|
+
* @default 'Example value copied to clipboard'
|
|
16
|
+
*/
|
|
17
|
+
copyNotificationContent?: string;
|
|
18
|
+
/** Notification shown when the browser denies clipboard access. @default 'Could not copy to clipboard' */
|
|
19
|
+
copyFailedNotificationContent?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Fill the modal's fixed-height body instead of scaling with content: a
|
|
23
|
+
content-scaled textarea either stops short of the body (dead space below, a
|
|
24
|
+
partial-height scrollbar) or explodes it on huge values. Filling hands the
|
|
25
|
+
whole body to the textarea, so its scrollbar spans the modal body. Module
|
|
26
|
+
scope for stable identity. */
|
|
27
|
+
const FILL_STYLE: CSSProperties = { height: '100%' };
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Full example-value viewer for template/context-data items whose
|
|
31
|
+
* `example_value` is too long for the picker's item description (a
|
|
32
|
+
* binary-carrying field's example can be an entire base64 file). Opened by the
|
|
33
|
+
* "?" item action wired in `helpers/templates.ts` via reqore's global
|
|
34
|
+
* `modalStore.addModal()` — the modals wrapper injects `isOpen`/`onClose`, so
|
|
35
|
+
* this component stays presentation-only (pass `isOpen` yourself only when
|
|
36
|
+
* rendering it standalone, e.g. in a story).
|
|
37
|
+
*/
|
|
38
|
+
export const ReqraftTemplateExampleValueModal = memo(
|
|
39
|
+
({
|
|
40
|
+
label,
|
|
41
|
+
value,
|
|
42
|
+
copyLabel = 'Copy',
|
|
43
|
+
copyNotificationContent = 'Example value copied to clipboard',
|
|
44
|
+
copyFailedNotificationContent = 'Could not copy to clipboard',
|
|
45
|
+
...rest
|
|
46
|
+
}: IReqraftTemplateExampleValueModalProps) => {
|
|
47
|
+
const addNotification = useReqoreProperty('addNotification');
|
|
48
|
+
|
|
49
|
+
const handleCopyClick = useCallback(async () => {
|
|
50
|
+
// Clipboard access can be denied (permissions, insecure context) — the
|
|
51
|
+
// failure surfaces as a notification instead of an unhandled rejection.
|
|
52
|
+
try {
|
|
53
|
+
await navigator.clipboard.writeText(value);
|
|
54
|
+
addNotification({
|
|
55
|
+
content: copyNotificationContent,
|
|
56
|
+
intent: 'success',
|
|
57
|
+
duration: 3000,
|
|
58
|
+
});
|
|
59
|
+
} catch (error) {
|
|
60
|
+
addNotification({
|
|
61
|
+
content: copyFailedNotificationContent,
|
|
62
|
+
intent: 'danger',
|
|
63
|
+
duration: 3000,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}, [value, addNotification, copyNotificationContent, copyFailedNotificationContent]);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<ReqoreModal
|
|
70
|
+
label={label || 'Example value'}
|
|
71
|
+
icon='FileTextLine'
|
|
72
|
+
height='70vh'
|
|
73
|
+
className='reqraft-template-example-value-modal'
|
|
74
|
+
bottomActions={[
|
|
75
|
+
{
|
|
76
|
+
position: 'right',
|
|
77
|
+
label: copyLabel,
|
|
78
|
+
icon: 'ClipboardLine',
|
|
79
|
+
onClick: () => {
|
|
80
|
+
handleCopyClick();
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
]}
|
|
84
|
+
{...rest}
|
|
85
|
+
>
|
|
86
|
+
<ReqoreTextarea
|
|
87
|
+
readOnly
|
|
88
|
+
value={value}
|
|
89
|
+
minimal
|
|
90
|
+
fluid
|
|
91
|
+
style={FILL_STYLE}
|
|
92
|
+
wrapperStyle={FILL_STYLE}
|
|
93
|
+
/>
|
|
94
|
+
</ReqoreModal>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
ReqraftTemplateExampleValueModal.displayName = 'ReqraftTemplateExampleValueModal';
|
|
@@ -2,7 +2,11 @@ import { ReqoreTag, ReqoreTagGroup } from '@qoretechnologies/reqore';
|
|
|
2
2
|
import { IReqoreFormTemplates } from '@qoretechnologies/reqore/dist/components/Textarea';
|
|
3
3
|
import { TSizes } from '@qoretechnologies/reqore/dist/constants/sizes';
|
|
4
4
|
import { memo } from 'react';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
getTemplateTagStyle,
|
|
7
|
+
describeTemplateReference,
|
|
8
|
+
TTemplateMeta,
|
|
9
|
+
} from '../../../../helpers/templates';
|
|
6
10
|
|
|
7
11
|
export interface IReadOnlyTemplateTagProps {
|
|
8
12
|
/** The template reference value, e.g. `$local:test`. */
|
|
@@ -22,14 +26,14 @@ export interface IReadOnlyTemplateTagProps {
|
|
|
22
26
|
*/
|
|
23
27
|
export const ReadOnlyTemplateTag = memo(
|
|
24
28
|
({ value, templates, size }: IReadOnlyTemplateTagProps) => {
|
|
25
|
-
const
|
|
26
|
-
const metadata =
|
|
29
|
+
const { label, item } = describeTemplateReference(templates, value);
|
|
30
|
+
const metadata = item?.metadata as TTemplateMeta | undefined;
|
|
27
31
|
return (
|
|
28
32
|
<ReqoreTagGroup size={size}>
|
|
29
33
|
<ReqoreTag
|
|
30
34
|
icon='ExchangeDollarLine'
|
|
31
35
|
leftIconProps={{ image: metadata?.image }}
|
|
32
|
-
label={
|
|
36
|
+
label={label}
|
|
33
37
|
tooltip={value}
|
|
34
38
|
{...getTemplateTagStyle(metadata)}
|
|
35
39
|
/>
|