@servicetitan/dte-pdf-editor 1.92.1 → 1.92.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/field-config-panel/field-sidebar.d.ts.map +1 -1
- package/dist/components/field-config-panel/field-sidebar.js +8 -4
- package/dist/components/field-config-panel/field-sidebar.js.map +1 -1
- package/dist/components/field-sidebar/data-model-field-type-list.d.ts +15 -1
- package/dist/components/field-sidebar/data-model-field-type-list.d.ts.map +1 -1
- package/dist/components/field-sidebar/data-model-field-type-list.js +54 -10
- package/dist/components/field-sidebar/data-model-field-type-list.js.map +1 -1
- package/dist/components/field-sidebar/field-sidebar.d.ts +7 -0
- package/dist/components/field-sidebar/field-sidebar.d.ts.map +1 -1
- package/dist/components/field-sidebar/field-sidebar.js +8 -11
- package/dist/components/field-sidebar/field-sidebar.js.map +1 -1
- package/dist/components/field-sidebar/form-fields-type-list.d.ts +28 -5
- package/dist/components/field-sidebar/form-fields-type-list.d.ts.map +1 -1
- package/dist/components/field-sidebar/form-fields-type-list.js +27 -6
- package/dist/components/field-sidebar/form-fields-type-list.js.map +1 -1
- package/dist/components/pdf-editor/pdf-editor.d.ts.map +1 -1
- package/dist/components/pdf-editor/pdf-editor.js +3 -2
- package/dist/components/pdf-editor/pdf-editor.js.map +1 -1
- package/dist/components/pdf-view/pdf-view.js +1 -1
- package/dist/components/pdf-view/pdf-view.js.map +1 -1
- package/dist/constants/menu-group.d.ts +5 -0
- package/dist/constants/menu-group.d.ts.map +1 -1
- package/dist/constants/menu-group.js +5 -2
- package/dist/constants/menu-group.js.map +1 -1
- package/dist/hooks/useFormFields.d.ts +3 -1
- package/dist/hooks/useFormFields.d.ts.map +1 -1
- package/dist/hooks/useFormFields.js +20 -3
- package/dist/hooks/useFormFields.js.map +1 -1
- package/dist/styles/field-type.css +5 -0
- package/dist/styles/formula-modal.css +17 -0
- package/dist/styles/pdf-canvas.css +5 -5
- package/package.json +1 -1
- package/src/components/field-config-panel/field-sidebar.tsx +24 -8
- package/src/components/field-sidebar/data-model-field-type-list.tsx +143 -29
- package/src/components/field-sidebar/field-sidebar.tsx +19 -18
- package/src/components/field-sidebar/form-fields-type-list.tsx +137 -95
- package/src/components/pdf-editor/pdf-editor.tsx +4 -3
- package/src/components/pdf-view/pdf-view.tsx +1 -1
- package/src/constants/menu-group.ts +5 -2
- package/src/hooks/useFormFields.ts +24 -2
- package/src/styles/field-type.css +5 -0
- package/src/styles/formula-modal.css +17 -0
- package/src/styles/pdf-canvas.css +5 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Button, Flex, Text } from '@servicetitan/anvil2';
|
|
1
|
+
import { Button, Flex, Skeleton, Text } from '@servicetitan/anvil2';
|
|
2
2
|
import IconArrowBack from '@servicetitan/anvil2/assets/icons/material/round/keyboard_arrow_left.svg';
|
|
3
3
|
import IconArrowForward from '@servicetitan/anvil2/assets/icons/material/round/keyboard_arrow_right.svg';
|
|
4
4
|
import { FC, Fragment } from 'react';
|
|
@@ -6,112 +6,154 @@ import { type FieldTypeOption, type FormInfo } from '../../interface/types';
|
|
|
6
6
|
import { getFieldSampleData } from '../../utils';
|
|
7
7
|
import { FieldType } from './field-type';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
/** Heading of the Forms group inside the Merge Tags tab. */
|
|
10
|
+
export const FORMS_GROUP_NAME = 'Forms';
|
|
11
|
+
|
|
12
|
+
/** A form's field count is unknown until it answers, so the placeholder settles on a short list. */
|
|
13
|
+
const FIELD_SKELETON_ROWS = 4;
|
|
14
|
+
|
|
15
|
+
/** Placeholder rows, shaped like the real ones, for a list whose contents are still in flight. */
|
|
16
|
+
const SkeletonRows: FC<{ count: number; className: string }> = ({ className, count }) => {
|
|
17
|
+
return (
|
|
18
|
+
<Fragment>
|
|
19
|
+
{Array.from({ length: count }, (_, index) => (
|
|
20
|
+
<div key={index} className={`${className} --skeleton`} aria-hidden>
|
|
21
|
+
<Skeleton.Text />
|
|
22
|
+
</div>
|
|
23
|
+
))}
|
|
24
|
+
</Fragment>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export interface FormsListProps {
|
|
10
29
|
forms: FormInfo[];
|
|
11
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Rows to draw as placeholders instead of the list. Forms are only listed once they are known
|
|
32
|
+
* to have fields, so until the fields arrive there is nothing truthful to show.
|
|
33
|
+
*/
|
|
34
|
+
loadingCount?: number;
|
|
35
|
+
/** Rendered when the (possibly search-filtered) list is empty. */
|
|
36
|
+
emptyText?: string;
|
|
37
|
+
onSelectForm(form: FormInfo): void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Root level of the Forms group: one clickable row per form. Selecting a form hands off to
|
|
42
|
+
* `FormFieldsPanel`, which lists that form's draggable fields.
|
|
43
|
+
*/
|
|
44
|
+
export const FormsList: FC<FormsListProps> = ({
|
|
45
|
+
emptyText = 'No forms configured.',
|
|
46
|
+
forms,
|
|
47
|
+
loadingCount = 0,
|
|
48
|
+
onSelectForm,
|
|
49
|
+
}) => {
|
|
50
|
+
return (
|
|
51
|
+
<div className="dte-field-type-group">
|
|
52
|
+
<Flex alignItems="center" gap={1} className="dte-field-type-group-header">
|
|
53
|
+
<Text variant="headline" el="h6" size="small">
|
|
54
|
+
{FORMS_GROUP_NAME}
|
|
55
|
+
</Text>
|
|
56
|
+
</Flex>
|
|
57
|
+
{loadingCount > 0 ? (
|
|
58
|
+
<SkeletonRows count={loadingCount} className="dte-formula-field-list-item" />
|
|
59
|
+
) : forms.length > 0 ? (
|
|
60
|
+
<ul className="dte-formula-field-list" role="listbox" aria-label={FORMS_GROUP_NAME}>
|
|
61
|
+
{forms.map(form => (
|
|
62
|
+
<li
|
|
63
|
+
key={form.id}
|
|
64
|
+
role="option"
|
|
65
|
+
className="dte-formula-field-list-item"
|
|
66
|
+
onMouseDown={e => {
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
onSelectForm(form);
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
<Flex justifyContent="space-between" alignItems="center">
|
|
72
|
+
{/*
|
|
73
|
+
* Same Text as the data model rows use (via FieldType): a bare
|
|
74
|
+
* span would inherit the host page's font instead of Anvil2's,
|
|
75
|
+
* so the Forms rows would not match the groups above them.
|
|
76
|
+
*/}
|
|
77
|
+
<Text size="small" className="dte-text-ellipsis" title={form.name}>
|
|
78
|
+
{form.name}
|
|
79
|
+
</Text>
|
|
80
|
+
<IconArrowForward />
|
|
81
|
+
</Flex>
|
|
82
|
+
</li>
|
|
83
|
+
))}
|
|
84
|
+
</ul>
|
|
85
|
+
) : (
|
|
86
|
+
<Text size="small" subdued variant="body">
|
|
87
|
+
{emptyText}
|
|
88
|
+
</Text>
|
|
89
|
+
)}
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export interface FormFieldsPanelProps {
|
|
95
|
+
form: FormInfo;
|
|
12
96
|
fieldOptions: FieldTypeOption[];
|
|
13
|
-
|
|
14
|
-
|
|
97
|
+
isLoading?: boolean;
|
|
98
|
+
/** Rendered when the form has fields but none match the current search. */
|
|
99
|
+
hasHiddenFields?: boolean;
|
|
100
|
+
onBack(): void;
|
|
15
101
|
onDragEnd(): void;
|
|
16
102
|
onDragStart(fieldOption: FieldTypeOption): void;
|
|
17
103
|
}
|
|
18
104
|
|
|
19
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Detail level of the Forms group: a back button naming the form, then its fields as the same
|
|
107
|
+
* draggable rows the data model groups use, so a form field drops onto the PDF like any merge tag.
|
|
108
|
+
*/
|
|
109
|
+
export const FormFieldsPanel: FC<FormFieldsPanelProps> = ({
|
|
20
110
|
fieldOptions,
|
|
21
|
-
|
|
22
|
-
|
|
111
|
+
form,
|
|
112
|
+
hasHiddenFields = false,
|
|
113
|
+
isLoading = false,
|
|
114
|
+
onBack,
|
|
23
115
|
onDragEnd,
|
|
24
116
|
onDragStart,
|
|
25
|
-
onSelectForm,
|
|
26
|
-
selectedForm,
|
|
27
117
|
}) => {
|
|
28
118
|
return (
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
119
|
+
<Fragment>
|
|
120
|
+
<Button
|
|
121
|
+
style={{ width: '100%' }}
|
|
122
|
+
appearance="ghost"
|
|
123
|
+
size="small"
|
|
124
|
+
icon={IconArrowBack}
|
|
125
|
+
title={form.name}
|
|
126
|
+
onClick={onBack}
|
|
127
|
+
>
|
|
128
|
+
<Text
|
|
129
|
+
size="medium"
|
|
130
|
+
className="dte-text-ellipsis"
|
|
131
|
+
style={{ fontWeight: 'bold', width: '100%' }}
|
|
33
132
|
>
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
</li>
|
|
58
|
-
))}
|
|
59
|
-
</ul>
|
|
60
|
-
) : (
|
|
61
|
-
<Text size="small" subdued variant="body">
|
|
62
|
-
No forms configured.
|
|
63
|
-
</Text>
|
|
64
|
-
)}
|
|
65
|
-
</div>
|
|
66
|
-
|
|
67
|
-
<div className="dte-formula-sidebar-slide-panel --form-fields">
|
|
68
|
-
{selectedForm ? (
|
|
69
|
-
<Fragment>
|
|
70
|
-
<Button
|
|
71
|
-
style={{ width: '100%' }}
|
|
72
|
-
appearance="ghost"
|
|
73
|
-
size="small"
|
|
74
|
-
icon={IconArrowBack}
|
|
75
|
-
title={selectedForm.name}
|
|
76
|
-
onClick={() => onSelectForm(null)}
|
|
77
|
-
>
|
|
78
|
-
<Text
|
|
79
|
-
size="medium"
|
|
80
|
-
className="dte-text-ellipsis"
|
|
81
|
-
style={{ fontWeight: 'bold', width: '100%' }}
|
|
82
|
-
>
|
|
83
|
-
{selectedForm.name}
|
|
84
|
-
</Text>
|
|
85
|
-
</Button>
|
|
86
|
-
<div className="dte-field-type-group">
|
|
87
|
-
{isLoadingFormFields ? (
|
|
88
|
-
<Text size="small" subdued variant="body">
|
|
89
|
-
Loading fields…
|
|
90
|
-
</Text>
|
|
91
|
-
) : fieldOptions.length === 0 ? (
|
|
92
|
-
<Text size="small" subdued variant="body">
|
|
93
|
-
No fields on this form.
|
|
94
|
-
</Text>
|
|
95
|
-
) : (
|
|
96
|
-
fieldOptions.map(option => (
|
|
97
|
-
<FieldType
|
|
98
|
-
key={option.path}
|
|
99
|
-
label={option.label}
|
|
100
|
-
sampleData={getFieldSampleData(
|
|
101
|
-
option.sampleData,
|
|
102
|
-
option.fieldType,
|
|
103
|
-
)}
|
|
104
|
-
onDragEnd={onDragEnd}
|
|
105
|
-
onDragStart={() => onDragStart(option)}
|
|
106
|
-
/>
|
|
107
|
-
))
|
|
108
|
-
)}
|
|
109
|
-
</div>
|
|
110
|
-
</Fragment>
|
|
111
|
-
) : null}
|
|
112
|
-
</div>
|
|
113
|
-
</div>
|
|
133
|
+
{form.name}
|
|
134
|
+
</Text>
|
|
135
|
+
</Button>
|
|
136
|
+
<div className="dte-field-type-group">
|
|
137
|
+
{isLoading ? (
|
|
138
|
+
<SkeletonRows count={FIELD_SKELETON_ROWS} className="dte-field-type-item" />
|
|
139
|
+
) : fieldOptions.length === 0 ? (
|
|
140
|
+
<Text size="small" subdued variant="body">
|
|
141
|
+
{hasHiddenFields
|
|
142
|
+
? 'No fields match the search.'
|
|
143
|
+
: 'No fields on this form.'}
|
|
144
|
+
</Text>
|
|
145
|
+
) : (
|
|
146
|
+
fieldOptions.map(option => (
|
|
147
|
+
<FieldType
|
|
148
|
+
key={option.path}
|
|
149
|
+
label={option.label}
|
|
150
|
+
sampleData={getFieldSampleData(option.sampleData, option.fieldType)}
|
|
151
|
+
onDragEnd={onDragEnd}
|
|
152
|
+
onDragStart={() => onDragStart(option)}
|
|
153
|
+
/>
|
|
154
|
+
))
|
|
155
|
+
)}
|
|
114
156
|
</div>
|
|
115
|
-
</
|
|
157
|
+
</Fragment>
|
|
116
158
|
);
|
|
117
159
|
};
|
|
@@ -59,7 +59,7 @@ export const PdfEditor: FC<PdfEditorProps> = ({
|
|
|
59
59
|
const pdfContainerRef = useRef<HTMLDivElement>(null);
|
|
60
60
|
const pdfWrapperRef = useRef<HTMLDivElement>(null);
|
|
61
61
|
const pdfFieldSelection = usePdfFieldSelection(fields, onFieldsChange);
|
|
62
|
-
const formFields = useFormFields(fields, onFormSelect);
|
|
62
|
+
const formFields = useFormFields(fields, onFormSelect, forms);
|
|
63
63
|
const pdfFieldDnd = usePdfFieldDnD({
|
|
64
64
|
fields,
|
|
65
65
|
recipients,
|
|
@@ -90,7 +90,7 @@ export const PdfEditor: FC<PdfEditorProps> = ({
|
|
|
90
90
|
dataModel={dataModel}
|
|
91
91
|
documentFields={fields}
|
|
92
92
|
formFieldsByFormId={formFields.formFieldsByFormId}
|
|
93
|
-
forms={
|
|
93
|
+
forms={formFields.formsWithFields}
|
|
94
94
|
formulaLoadingFormId={formFields.formulaLoadingFormId}
|
|
95
95
|
selectedField={pdfFieldSelection.selectedField}
|
|
96
96
|
onFieldConfigChange={pdfFieldSelection.updateField}
|
|
@@ -108,7 +108,8 @@ export const PdfEditor: FC<PdfEditorProps> = ({
|
|
|
108
108
|
hideFields={hideFields}
|
|
109
109
|
dataModelGroups={dataModelGroups}
|
|
110
110
|
formFieldOptions={formFields.formFieldOptions}
|
|
111
|
-
forms={
|
|
111
|
+
forms={formFields.formsWithFields}
|
|
112
|
+
loadingFormsCount={formFields.isLoadingForms ? (forms?.length ?? 0) : 0}
|
|
112
113
|
isLoadingFormFields={formFields.isLoading}
|
|
113
114
|
onDragStart={pdfFieldDnd.handleDragStart}
|
|
114
115
|
onDragEnd={pdfFieldDnd.handleDragEnd}
|
|
@@ -187,7 +187,7 @@ export const PdfView: FC<PdfViewProps> = ({
|
|
|
187
187
|
return (
|
|
188
188
|
<Flex
|
|
189
189
|
direction="column"
|
|
190
|
-
style={{ position: 'relative', minWidth: 0 }}
|
|
190
|
+
style={{ position: 'relative', minWidth: 0, height: '100%' }}
|
|
191
191
|
className="dte-pdf-editor-theme"
|
|
192
192
|
ref={contentContainerRef}
|
|
193
193
|
>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import IconBorderColor from '@servicetitan/anvil2/assets/icons/material/round/border_color.svg';
|
|
2
2
|
import IconCalculate from '@servicetitan/anvil2/assets/icons/material/round/calculate.svg';
|
|
3
|
-
import IconFactCheck from '@servicetitan/anvil2/assets/icons/material/round/fact_check.svg';
|
|
4
3
|
import IconPhotoSizeSelectSmall from '@servicetitan/anvil2/assets/icons/material/round/photo_size_select_small.svg';
|
|
5
4
|
import IconCommercial from '@servicetitan/anvil2/assets/icons/st/commercial.svg';
|
|
6
5
|
import IconEstimate from '@servicetitan/anvil2/assets/icons/st/estimate.svg';
|
|
@@ -12,6 +11,11 @@ export interface MenuGroupModel {
|
|
|
12
11
|
key: FieldTypeEnum;
|
|
13
12
|
}
|
|
14
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Sidebar tabs. Forms (`FieldTypeEnum.forms`) has no tab of its own: form fields are offered as a
|
|
16
|
+
* group inside Merge Tags, next to the data model groups. `hideFields: [FieldTypeEnum.forms]` still
|
|
17
|
+
* hides that group.
|
|
18
|
+
*/
|
|
15
19
|
export const menuGroups: MenuGroupModel[] = [
|
|
16
20
|
{ svgIcon: IconEstimate, label: 'Merge Tags', key: FieldTypeEnum.dataModel },
|
|
17
21
|
{ svgIcon: IconBorderColor, label: 'E-Sign', key: FieldTypeEnum.eSign },
|
|
@@ -30,5 +34,4 @@ export const menuGroups: MenuGroupModel[] = [
|
|
|
30
34
|
label: 'Generic Fields',
|
|
31
35
|
key: FieldTypeEnum.generic,
|
|
32
36
|
},
|
|
33
|
-
{ svgIcon: IconFactCheck, label: 'Forms', key: FieldTypeEnum.forms },
|
|
34
37
|
];
|
|
@@ -10,6 +10,8 @@ import { formFieldInfoToFieldTypeOption } from '../utils';
|
|
|
10
10
|
|
|
11
11
|
interface UseFormFieldsResult {
|
|
12
12
|
isLoading: boolean;
|
|
13
|
+
isLoadingForms: boolean;
|
|
14
|
+
formsWithFields: FormInfo[];
|
|
13
15
|
formFieldsByFormId: FormFieldsByFormIdI;
|
|
14
16
|
formulaLoadingFormId: null | number;
|
|
15
17
|
formFieldOptions: FieldTypeOption[];
|
|
@@ -18,12 +20,15 @@ interface UseFormFieldsResult {
|
|
|
18
20
|
requestFormFieldsById: (formId: number) => void;
|
|
19
21
|
}
|
|
20
22
|
|
|
23
|
+
const EMPTY_FORMS: FormInfo[] = [];
|
|
24
|
+
|
|
21
25
|
export const useFormFields = (
|
|
22
26
|
fields: PdfField[],
|
|
23
27
|
onFormSelect?: (
|
|
24
28
|
formIds: number[],
|
|
25
29
|
sendFormFields: (formFieldsByFormId: FormFieldsByFormIdI) => void,
|
|
26
30
|
) => void,
|
|
31
|
+
forms: FormInfo[] = EMPTY_FORMS,
|
|
27
32
|
): UseFormFieldsResult => {
|
|
28
33
|
const [formFieldsByFormId, setFormFieldsByFormId] = useState<FormFieldsByFormIdI>({});
|
|
29
34
|
const [formulaLoadingFormId, setFormulaLoadingFormId] = useState<number | null>(null);
|
|
@@ -40,7 +45,13 @@ export const useFormFields = (
|
|
|
40
45
|
return;
|
|
41
46
|
}
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
/*
|
|
49
|
+
* Every form is asked for up front, not just the ones a formula already references: a form
|
|
50
|
+
* with no fields has nothing to offer and is dropped from the list (`formsWithFields`), and
|
|
51
|
+
* that cannot be decided without its fields. They go out as one batched call, and the reply
|
|
52
|
+
* is cached, so opening a form afterwards costs nothing.
|
|
53
|
+
*/
|
|
54
|
+
const initialFormIds = new Set<number>(forms.map(form => form.id));
|
|
44
55
|
fields.forEach(field => {
|
|
45
56
|
if (field.type === FieldTypeEnum.calculated) {
|
|
46
57
|
if (field?.formula?.tokens.length) {
|
|
@@ -90,7 +101,7 @@ export const useFormFields = (
|
|
|
90
101
|
cancelled = true;
|
|
91
102
|
cancelAnimationFrame(rafId);
|
|
92
103
|
};
|
|
93
|
-
}, [fields, onFormSelect]);
|
|
104
|
+
}, [fields, forms, onFormSelect]);
|
|
94
105
|
|
|
95
106
|
const formFieldOptions = useMemo(() => {
|
|
96
107
|
if (!selectedForm) {
|
|
@@ -143,10 +154,21 @@ export const useFormFields = (
|
|
|
143
154
|
[formFieldsByFormId, onFormSelect],
|
|
144
155
|
);
|
|
145
156
|
|
|
157
|
+
/*
|
|
158
|
+
* A form is offered only once it is known to have something to drag out of it. Until its reply
|
|
159
|
+
* lands it is neither listed nor counted as empty — `isLoadingForms` stands in for it.
|
|
160
|
+
*/
|
|
161
|
+
const formsWithFields = useMemo(() => {
|
|
162
|
+
return forms.filter(form => !!formFieldsByFormId[form.id]?.length);
|
|
163
|
+
}, [forms, formFieldsByFormId]);
|
|
164
|
+
|
|
146
165
|
const isLoading = selectedForm != null && formFieldsLoadingFormIds[selectedForm.id];
|
|
166
|
+
const isLoadingForms = forms.some(form => !!formFieldsLoadingFormIds[form.id]);
|
|
147
167
|
|
|
148
168
|
return {
|
|
149
169
|
isLoading,
|
|
170
|
+
isLoadingForms,
|
|
171
|
+
formsWithFields,
|
|
150
172
|
formFieldsByFormId,
|
|
151
173
|
formulaLoadingFormId,
|
|
152
174
|
formFieldOptions,
|
|
@@ -69,6 +69,18 @@
|
|
|
69
69
|
gap: 0;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/*
|
|
73
|
+
* The panels share one scroller, so the list — always the taller of the two — would otherwise keep
|
|
74
|
+
* driving the scroll height while the fields panel is shown, leaving the fields followed by a
|
|
75
|
+
* screenful of nothing. Both bounds are needed: the min-content floor above keeps a max-height on
|
|
76
|
+
* its own from taking effect.
|
|
77
|
+
*/
|
|
78
|
+
.dte-formula-sidebar-slide-track.--detail .dte-formula-sidebar-slide-panel:not(.--form-fields) {
|
|
79
|
+
min-height: 0;
|
|
80
|
+
max-height: 0;
|
|
81
|
+
overflow: hidden;
|
|
82
|
+
}
|
|
83
|
+
|
|
72
84
|
.dte-formula-sidebar-forms-heading {
|
|
73
85
|
flex-shrink: 0;
|
|
74
86
|
}
|
|
@@ -94,6 +106,11 @@
|
|
|
94
106
|
white-space: nowrap;
|
|
95
107
|
}
|
|
96
108
|
|
|
109
|
+
.dte-formula-field-list-item.--skeleton {
|
|
110
|
+
cursor: default;
|
|
111
|
+
pointer-events: none;
|
|
112
|
+
}
|
|
113
|
+
|
|
97
114
|
.dte-formula-field-list-item:hover,
|
|
98
115
|
.dte-formula-field-list-item.--highlight {
|
|
99
116
|
color: var(--color-neutral-100, #e8e8e8);
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
background-color: var(--bg-color-light, #ffffff);
|
|
21
21
|
border: 1px solid var(--border-color, #e0e0e0);
|
|
22
22
|
border-radius: var(--radius-2, 8px);
|
|
23
|
-
position:
|
|
23
|
+
position: absolute;
|
|
24
24
|
z-index: 2;
|
|
25
25
|
|
|
26
26
|
&.--vertical {
|
|
@@ -28,14 +28,14 @@
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
&.--right-center {
|
|
31
|
-
right:
|
|
31
|
+
right: 0;
|
|
32
32
|
top: 50%;
|
|
33
|
-
transform:
|
|
33
|
+
transform: translate(100%, -50%);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
&.--right-bottom {
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
bottom: 12px;
|
|
38
|
+
right: 12px;
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|