2019-06-23 03:35:23 -07:00
|
|
|
<template>
|
2022-04-11 06:12:13 -07:00
|
|
|
<div :class="$style.container">
|
2022-08-02 01:36:11 -07:00
|
|
|
<n8n-callout
|
2022-11-14 04:28:26 -08:00
|
|
|
v-if="canPinData && hasPinData && !editMode.enabled && !isProductionExecutionPreview"
|
2022-07-20 08:50:39 -07:00
|
|
|
theme="secondary"
|
|
|
|
icon="thumbtack"
|
|
|
|
:class="$style['pinned-data-callout']"
|
|
|
|
>
|
|
|
|
{{ $locale.baseText('runData.pindata.thisDataIsPinned') }}
|
|
|
|
<span class="ml-4xs" v-if="!isReadOnly">
|
|
|
|
<n8n-link
|
|
|
|
theme="secondary"
|
|
|
|
size="small"
|
|
|
|
underline
|
|
|
|
bold
|
|
|
|
@click="onTogglePinData({ source: 'banner-link' })"
|
|
|
|
>
|
|
|
|
{{ $locale.baseText('runData.pindata.unpin') }}
|
|
|
|
</n8n-link>
|
|
|
|
</span>
|
|
|
|
<template #trailingContent>
|
|
|
|
<n8n-link
|
|
|
|
:to="dataPinningDocsUrl"
|
|
|
|
size="small"
|
|
|
|
theme="secondary"
|
|
|
|
bold
|
|
|
|
underline
|
|
|
|
@click="onClickDataPinningDocsLink"
|
|
|
|
>
|
|
|
|
{{ $locale.baseText('runData.pindata.learnMore') }}
|
|
|
|
</n8n-link>
|
|
|
|
</template>
|
2022-08-02 01:36:11 -07:00
|
|
|
</n8n-callout>
|
2022-07-20 08:50:39 -07:00
|
|
|
|
2019-06-23 03:35:23 -07:00
|
|
|
<BinaryDataDisplay :windowVisible="binaryDataDisplayVisible" :displayData="binaryDataDisplayData" @close="closeBinaryDataDisplay"/>
|
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
<div :class="$style.header">
|
2022-05-23 08:56:15 -07:00
|
|
|
<slot name="header"></slot>
|
2022-04-11 06:12:13 -07:00
|
|
|
|
2022-11-28 07:54:13 -08:00
|
|
|
<div v-show="!hasRunError" @click.stop :class="$style.displayModes" data-test-id="run-data-pane-header">
|
2022-04-11 06:12:13 -07:00
|
|
|
<n8n-radio-buttons
|
2022-07-20 08:50:39 -07:00
|
|
|
v-show="hasNodeRun && ((jsonData && jsonData.length > 0) || (binaryData && binaryData.length > 0)) && !editMode.enabled"
|
2022-04-11 06:12:13 -07:00
|
|
|
:value="displayMode"
|
|
|
|
:options="buttons"
|
|
|
|
@input="onDisplayModeChange"
|
2022-12-01 00:26:38 -08:00
|
|
|
data-test-id="ndv-run-data-display-mode"
|
2022-04-11 06:12:13 -07:00
|
|
|
/>
|
2022-07-20 08:50:39 -07:00
|
|
|
<n8n-icon-button
|
|
|
|
v-if="canPinData && !isReadOnly"
|
|
|
|
v-show="!editMode.enabled"
|
|
|
|
:title="$locale.baseText('runData.editOutput')"
|
|
|
|
:circle="false"
|
|
|
|
:disabled="node.disabled"
|
|
|
|
class="ml-2xs"
|
|
|
|
icon="pencil-alt"
|
|
|
|
type="tertiary"
|
|
|
|
@click="enterEditMode({ origin: 'editIconButton' })"
|
|
|
|
/>
|
|
|
|
<n8n-tooltip
|
|
|
|
placement="bottom-end"
|
|
|
|
v-if="canPinData && (jsonData && jsonData.length > 0)"
|
|
|
|
v-show="!editMode.enabled"
|
|
|
|
:value="pinDataDiscoveryTooltipVisible"
|
|
|
|
:manual="isControlledPinDataTooltip"
|
|
|
|
>
|
|
|
|
<template #content v-if="!isControlledPinDataTooltip">
|
|
|
|
<div :class="$style['tooltip-container']">
|
|
|
|
<strong>{{ $locale.baseText('ndv.pinData.pin.title') }}</strong>
|
|
|
|
<n8n-text size="small" tag="p">
|
|
|
|
{{ $locale.baseText('ndv.pinData.pin.description') }}
|
|
|
|
|
|
|
|
<n8n-link :to="dataPinningDocsUrl" size="small">
|
|
|
|
{{ $locale.baseText('ndv.pinData.pin.link') }}
|
|
|
|
</n8n-link>
|
|
|
|
</n8n-text>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<template #content v-else>
|
|
|
|
<div :class="$style['tooltip-container']">
|
|
|
|
{{ $locale.baseText('node.discovery.pinData.ndv') }}
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<n8n-icon-button
|
2022-09-21 03:21:08 -07:00
|
|
|
:class="['ml-2xs', $style['pin-data-button']]"
|
2022-07-20 08:50:39 -07:00
|
|
|
type="tertiary"
|
2022-09-21 03:21:08 -07:00
|
|
|
:active="hasPinData"
|
2022-07-20 08:50:39 -07:00
|
|
|
icon="thumbtack"
|
|
|
|
:disabled="editMode.enabled || (inputData.length === 0 && !hasPinData) || isReadOnly"
|
|
|
|
@click="onTogglePinData({ source: 'pin-icon-click' })"
|
|
|
|
/>
|
|
|
|
</n8n-tooltip>
|
|
|
|
|
|
|
|
<div :class="$style['edit-mode-actions']" v-show="editMode.enabled">
|
|
|
|
<n8n-button
|
|
|
|
type="tertiary"
|
|
|
|
:label="$locale.baseText('runData.editor.cancel')"
|
|
|
|
@click="onClickCancelEdit"
|
|
|
|
/>
|
|
|
|
<n8n-button
|
|
|
|
class="ml-2xs"
|
|
|
|
type="primary"
|
|
|
|
:label="$locale.baseText('runData.editor.save')"
|
|
|
|
@click="onClickSaveEdit"
|
|
|
|
/>
|
|
|
|
</div>
|
2019-06-23 03:35:23 -07:00
|
|
|
</div>
|
2022-04-11 06:12:13 -07:00
|
|
|
</div>
|
|
|
|
|
2022-07-20 08:50:39 -07:00
|
|
|
<div :class="$style.runSelector" v-if="maxRunIndex > 0" v-show="!editMode.enabled">
|
2022-09-26 22:03:08 -07:00
|
|
|
<n8n-select size="small" :value="runIndex" @input="onRunIndexChange" @click.stop popper-append-to-body>
|
2022-11-18 05:59:31 -08:00
|
|
|
<template #prepend>{{ $locale.baseText('ndv.output.run') }}</template>
|
2022-04-11 06:12:13 -07:00
|
|
|
<n8n-option v-for="option in (maxRunIndex + 1)" :label="getRunLabel(option)" :value="option - 1" :key="option"></n8n-option>
|
|
|
|
</n8n-select>
|
2022-05-23 08:56:15 -07:00
|
|
|
|
|
|
|
|
|
|
|
<n8n-tooltip placement="right" v-if="canLinkRuns" :content="$locale.baseText(linkedRuns ? 'runData.unlinking.hint': 'runData.linking.hint')">
|
2022-07-26 00:55:27 -07:00
|
|
|
<n8n-icon-button v-if="linkedRuns" icon="unlink" text type="tertiary" size="small" @click="unlinkRun" />
|
|
|
|
<n8n-icon-button v-else icon="link" text type="tertiary" size="small" @click="linkRun" />
|
2022-05-23 08:56:15 -07:00
|
|
|
</n8n-tooltip>
|
|
|
|
|
|
|
|
<slot name="run-info"></slot>
|
2022-04-11 06:12:13 -07:00
|
|
|
</div>
|
|
|
|
|
2022-12-06 03:50:06 -08:00
|
|
|
<div v-if="maxOutputIndex > 0 && branches.length > 1" :class="$style.tabs">
|
2022-05-23 08:56:15 -07:00
|
|
|
<n8n-tabs :value="currentOutputIndex" @input="onBranchChange" :options="branches" />
|
2022-04-11 06:12:13 -07:00
|
|
|
</div>
|
|
|
|
|
2022-07-20 08:50:39 -07:00
|
|
|
<div v-else-if="hasNodeRun && dataCount > 0 && maxRunIndex === 0" v-show="!editMode.enabled" :class="$style.itemsCount">
|
2022-04-11 06:12:13 -07:00
|
|
|
<n8n-text>
|
2022-05-23 08:56:15 -07:00
|
|
|
{{ dataCount }} {{ $locale.baseText('ndv.output.items', {adjustToNumber: dataCount}) }}
|
2022-04-11 06:12:13 -07:00
|
|
|
</n8n-text>
|
|
|
|
</div>
|
|
|
|
|
2022-07-20 08:50:39 -07:00
|
|
|
<div
|
2022-10-06 06:03:55 -07:00
|
|
|
:class="$style['data-container']"
|
2022-07-20 08:50:39 -07:00
|
|
|
ref="dataContainer"
|
2022-12-01 00:26:38 -08:00
|
|
|
data-test-id="ndv-data-container"
|
2022-07-20 08:50:39 -07:00
|
|
|
>
|
2022-05-23 08:56:15 -07:00
|
|
|
<div v-if="isExecuting" :class="$style.center">
|
|
|
|
<div :class="$style.spinner"><n8n-spinner type="ring" /></div>
|
|
|
|
<n8n-text>{{ executingMessage }}</n8n-text>
|
|
|
|
</div>
|
|
|
|
|
2022-07-20 08:50:39 -07:00
|
|
|
<div v-else-if="editMode.enabled" :class="$style['edit-mode']">
|
2022-09-26 05:27:07 -07:00
|
|
|
<div :class="[$style['edit-mode-body'], 'ignore-key-press']">
|
2022-07-20 08:50:39 -07:00
|
|
|
<code-editor
|
|
|
|
:value="editMode.value"
|
|
|
|
:options="{ scrollBeyondLastLine: false }"
|
|
|
|
type="json"
|
2022-11-04 06:04:31 -07:00
|
|
|
@input="ndvStore.setOutputPanelEditModeValue($event)"
|
2022-07-20 08:50:39 -07:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
<div :class="$style['edit-mode-footer']">
|
|
|
|
<n8n-info-tip :bold="false" :class="$style['edit-mode-footer-infotip']">
|
|
|
|
{{ $locale.baseText('runData.editor.copyDataInfo') }}
|
2022-08-04 05:57:19 -07:00
|
|
|
<n8n-link :to="dataEditingDocsUrl" size="small">
|
2022-07-20 08:50:39 -07:00
|
|
|
{{ $locale.baseText('generic.learnMore') }}
|
|
|
|
</n8n-link>
|
|
|
|
</n8n-info-tip>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
feat(editor, core, cli): implement new workflow experience (#4358)
* feat(ExecuteWorkflowTrigger node): Implement ExecuteWorkflowTrigger node (#4108)
* feat(ExecuteWorkflowTrigger node): Implement ExecuteWorkflowTrigger node
* feat(editor): Do not show duplicate button if canvas contains `maxNodes` amount of nodes
* feat(ManualTrigger node): Implement ManualTrigger node (#4110)
* feat(ManualTrigger node): Implement ManualTrigger node
* :memo: Remove generics doc items from ManualTrigger node
* feat(editor-ui): Trigger tab redesign (#4150)
* :construction: Begin with TriggerPanel implementation, add Other Trigger Nodes subcategory
* :construction: Extracted categorized categories/subcategory/nodes rendering into its own component — CategorizedItems, removed SubcategoryPanel, added translations
* :sparkles: Implement MainPanel background scrim
* :recycle: Move `categoriesWithNodes`, 'visibleNodeTypes` and 'categorizedItems` to store, implemented dynamic categories count based on `selectedType`
* :bug: Fix SlideTransition for all the NodeCreato panels
* :lipstick: Fix cursos for CategoryItem and NodeItem
* :bug: Make sure ALL_NODE_FILTER is always set when MainPanel is mounted
* :art: Address PR comments
* label: Use Array type for CategorizedItems props
* :label: Add proper types for Vue props
* 🎨 Use standard component registration for CategorizedItems inside TriggerHelperPanel
* 🎨 Use kebab case for main-panel and icon component
* :label: Improve types
* feat(editor-ui): Redesign search input inside node creator panel (#4204)
* :construction: Begin with TriggerPanel implementation, add Other Trigger Nodes subcategory
* :construction: Extracted categorized categories/subcategory/nodes rendering into its own component — CategorizedItems, removed SubcategoryPanel, added translations
* :sparkles: Implement MainPanel background scrim
* :recycle: Move `categoriesWithNodes`, 'visibleNodeTypes` and 'categorizedItems` to store, implemented dynamic categories count based on `selectedType`
* :bug: Fix SlideTransition for all the NodeCreato panels
* :lipstick: Fix cursos for CategoryItem and NodeItem
* :bug: Make sure ALL_NODE_FILTER is always set when MainPanel is mounted
* :art: Address PR comments
* label: Use Array type for CategorizedItems props
* :label: Add proper types for Vue props
* 🎨 Use standard component registration for CategorizedItems inside TriggerHelperPanel
* :sparkles: Redesign search input and unify usage of categorized items
* :label: Use lowercase "Boolean" as `isSearchVisible` computed return type
* :fire: Remove useless emit
* :sparkles: Implement no result view based on subcategory, minor fixes
* :art: Remove unused properties
* feat(node-email): Change EmailReadImap display name and name (#4239)
* feat(editor-ui): Implement "Choose a Triger" action and related behaviour (#4226)
* :sparkles: Implement "Choose a Triger" action and related behaviour
* :mute: Lint fix
* :recycle: Remove PlaceholderTrigger node, add a button instead
* :art: Merge onMouseEnter and onMouseLeave to a single function
* :bulb: Add comment
* :fire: Remove PlaceholderNode registration
* :art: Rename TriggerPlaceholderButton to CanvasAddButton
* :sparkles: Add method to unregister custom action and rework CanvasAddButton centering logic
* :art: Run `setRecenteredCanvasAddButtonPosition` on `CanvasAddButton` mount
* fix(editor): Fix selecting of node from node-creator panel by clicking
* :twisted_rightwards_arrows: Merge fixes
* fix(editor): Show execute workflow trigger instead of workflow trigger in the trigger helper panel
* feat(editor): Fix node creator panel slide transition (#4261)
* fix(editor): Fix node creator panel slide-in/slide-out transitions
* :art: Fix naming
* :art: Use kebab-case for transition component name
* feat(editor): Disable execution and show notice when user tries to run workflow without enabled triggers
* fix(editor): Address first batch of new WF experience review (#4279)
* fix(editor): Fix first batch of review items
* bug(editor): Fix nodeview canvas add button centering
* :mute: Fix linter errors
* bug(ManualTrigger Node): Fix manual trigger node execution
* fix(editor): Do not show canvas add button in execution or demo mode and prevent clicking if creator is open
* fix(editor): do not show pin data tooltip for manual trigger node
* fix(editor): do not use nodeViewOffset on zoomToFit
* :lipstick: Add margin for last node creator item and set font-weight to 700 for category title
* :sparkles: Position welcome note next to the added trigger node
* :bug: Remve always true welcome note
* feat(editor): Minor UI and UX tweaks (#4328)
* :lipstick: Make top viewport buttons less prominent
* :sparkles: Allow user to switch to all tabs if it contains filter results, move nodecreator state props to its own module
* :mute: Fix linting errors
* :mute: Fix linting errors
* :mute: Fix linting errors
* chore(build): Ping Turbo version to 1.5.5
* :lipstick: Minor traigger panel and node view style changes
* :speech_balloon: Update display name of execute workflow trigger
* feat(core, editor): Update subworkflow execution logic (#4269)
* :sparkles: Implement `findWorkflowStart`
* :zap: Extend `WorkflowOperationError`
* :zap: Add `WorkflowOperationError` to toast
* :blue_book: Extend interface
* :sparkles: Add `subworkflowExecutionError` to store
* :sparkles: Create `SubworkflowOperationError`
* :zap: Render subworkflow error as node error
* :truck: Move subworkflow start validation to `cli`
* :zap: Reset subworkflow execution error state
* :fire: Remove unused import
* :zap: Adjust CLI commands
* :fire: Remove unneeded check
* :fire: Remove stray log
* :zap: Simplify syntax
* :zap: Sort in case both Start and EWT present
* :recycle: Address Omar's feedback
* :fire: Remove unneeded lint exception
* :pencil2: Fix copy
* :shirt: Fix lint
* fix: moved find start node function to catchable place
Co-authored-by: Omar Ajoue <krynble@gmail.com>
* :lipstick: Change ExecuteWorkflow node to primary
* :sparkles: Allow user to navigate to all tab if it contains search results
* :bug: Fixed canvas control button while in demo, disable workflow activation for non-activavle nodes and revert zoomToFit bottom offset
* :fix: Do not chow request text if there's results
* :speech_balloon: Update noResults text
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
Co-authored-by: Omar Ajoue <krynble@gmail.com>
2022-10-18 05:23:22 -07:00
|
|
|
<div v-else-if="paneType === 'output' && hasSubworkflowExecutionError" :class="$style.stretchVertically">
|
|
|
|
<NodeErrorView :error="subworkflowExecutionError" :class="$style.errorDisplay" />
|
|
|
|
</div>
|
|
|
|
|
2022-05-23 08:56:15 -07:00
|
|
|
<div v-else-if="!hasNodeRun" :class="$style.center">
|
|
|
|
<slot name="node-not-run"></slot>
|
2022-04-11 06:12:13 -07:00
|
|
|
</div>
|
2019-10-02 06:30:51 -07:00
|
|
|
|
2022-07-20 08:50:39 -07:00
|
|
|
<div v-else-if="paneType === 'input' && node.disabled" :class="$style.center">
|
|
|
|
<n8n-text>
|
|
|
|
{{ $locale.baseText('ndv.input.disabled', { interpolate: { nodeName: node.name } }) }}
|
|
|
|
<n8n-link @click="enableNode">
|
|
|
|
{{ $locale.baseText('ndv.input.disabled.cta') }}
|
|
|
|
</n8n-link>
|
|
|
|
</n8n-text>
|
|
|
|
</div>
|
|
|
|
|
2022-09-19 03:26:02 -07:00
|
|
|
<div v-else-if="hasNodeRun && hasRunError" :class="$style.stretchVertically">
|
|
|
|
<n8n-text v-if="isPaneTypeInput" :class="$style.center" size="large" tag="p" bold>
|
|
|
|
{{ $locale.baseText('nodeErrorView.inputPanel.previousNodeError.title', { interpolate: { nodeName: node.name } }) }}
|
|
|
|
<n8n-link @click="goToErroredNode">
|
|
|
|
{{ $locale.baseText('nodeErrorView.inputPanel.previousNodeError.text') }}
|
|
|
|
</n8n-link>
|
|
|
|
</n8n-text>
|
2022-10-10 01:00:54 -07:00
|
|
|
<NodeErrorView v-else :error="workflowRunData[node.name][runIndex].error" :class="$style.dataDisplay" />
|
2022-04-11 06:12:13 -07:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<div v-else-if="hasNodeRun && jsonData && jsonData.length === 0 && branches.length > 1" :class="$style.center">
|
|
|
|
<n8n-text>
|
2022-05-23 08:56:15 -07:00
|
|
|
{{ noDataInBranchMessage }}
|
2022-04-11 06:12:13 -07:00
|
|
|
</n8n-text>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div v-else-if="hasNodeRun && jsonData && jsonData.length === 0" :class="$style.center">
|
2022-05-23 08:56:15 -07:00
|
|
|
<slot name="no-output-data"></slot>
|
2022-04-11 06:12:13 -07:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<div v-else-if="hasNodeRun && !showData" :class="$style.center">
|
2022-05-23 08:56:15 -07:00
|
|
|
<n8n-text :bold="true" color="text-dark" size="large">{{ tooMuchDataTitle }}</n8n-text>
|
2022-04-11 06:12:13 -07:00
|
|
|
<n8n-text align="center" tag="div"><span v-html="$locale.baseText('ndv.output.tooMuchData.message', { interpolate: {size: dataSizeInMB }})"></span></n8n-text>
|
|
|
|
|
|
|
|
<n8n-button
|
2022-07-20 08:50:39 -07:00
|
|
|
outline
|
2022-04-11 06:12:13 -07:00
|
|
|
:label="$locale.baseText('ndv.output.tooMuchData.showDataAnyway')"
|
2022-05-23 08:56:15 -07:00
|
|
|
@click="showTooMuchData"
|
2022-04-11 06:12:13 -07:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
2022-08-24 05:47:42 -07:00
|
|
|
<div v-else-if="hasNodeRun && displayMode === 'table' && binaryData.length > 0 && jsonData.length === 1 && Object.keys(jsonData[0] || {}).length === 0" :class="$style.center">
|
2022-05-23 08:56:15 -07:00
|
|
|
<n8n-text>
|
|
|
|
{{ $locale.baseText('runData.switchToBinary.info') }}
|
|
|
|
<a @click="switchToBinary">
|
|
|
|
{{ $locale.baseText('runData.switchToBinary.binary') }}
|
|
|
|
</a>
|
|
|
|
</n8n-text>
|
|
|
|
</div>
|
|
|
|
|
2022-10-06 06:03:55 -07:00
|
|
|
<run-data-table
|
|
|
|
v-else-if="hasNodeRun && displayMode === 'table'"
|
|
|
|
class="ph-no-capture"
|
|
|
|
:node="node"
|
|
|
|
:inputData="inputData"
|
|
|
|
:mappingEnabled="mappingEnabled"
|
|
|
|
:distanceFromActive="distanceFromActive"
|
|
|
|
:showMappingHint="showMappingHint"
|
|
|
|
:runIndex="runIndex"
|
2022-10-12 05:06:28 -07:00
|
|
|
:pageOffset="currentPageOffset"
|
2022-10-06 06:03:55 -07:00
|
|
|
:totalRuns="maxRunIndex"
|
2022-10-12 05:06:28 -07:00
|
|
|
:hasDefaultHoverState="paneType === 'input'"
|
2022-10-06 06:03:55 -07:00
|
|
|
@mounted="$emit('tableMounted', $event)"
|
2022-10-12 05:06:28 -07:00
|
|
|
@activeRowChanged="onItemHover"
|
2022-11-02 01:23:09 -07:00
|
|
|
@displayModeChange="onDisplayModeChange"
|
2022-10-06 06:03:55 -07:00
|
|
|
/>
|
|
|
|
|
|
|
|
<run-data-json
|
|
|
|
v-else-if="hasNodeRun && displayMode === 'json'"
|
|
|
|
class="ph-no-capture"
|
|
|
|
:paneType="paneType"
|
|
|
|
:editMode="editMode"
|
|
|
|
:sessioId="sessionId"
|
|
|
|
:node="node"
|
|
|
|
:inputData="inputData"
|
|
|
|
:mappingEnabled="mappingEnabled"
|
|
|
|
:distanceFromActive="distanceFromActive"
|
|
|
|
:showMappingHint="showMappingHint"
|
|
|
|
:runIndex="runIndex"
|
|
|
|
:totalRuns="maxRunIndex"
|
|
|
|
/>
|
2022-04-11 06:12:13 -07:00
|
|
|
|
2022-12-06 03:50:06 -08:00
|
|
|
<run-data-schema
|
2022-12-12 01:08:29 -08:00
|
|
|
v-else-if="hasNodeRun && displayMode === 'schema'"
|
2022-12-06 03:50:06 -08:00
|
|
|
:data="jsonData"
|
|
|
|
:mappingEnabled="mappingEnabled"
|
|
|
|
:distanceFromActive="distanceFromActive"
|
|
|
|
:node="node"
|
|
|
|
:runIndex="runIndex"
|
|
|
|
:totalRuns="maxRunIndex"
|
|
|
|
/>
|
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
<div v-else-if="displayMode === 'binary' && binaryData.length === 0" :class="$style.center">
|
|
|
|
<n8n-text align="center" tag="div">{{ $locale.baseText('runData.noBinaryDataFound') }}</n8n-text>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div v-else-if="displayMode === 'binary'" :class="$style.dataDisplay">
|
|
|
|
<div v-for="(binaryDataEntry, index) in binaryData" :key="index">
|
|
|
|
<div :class="$style.binaryIndex" v-if="binaryData.length > 1">
|
|
|
|
<div>
|
|
|
|
{{index + 1}}
|
2019-06-23 03:35:23 -07:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
<div :class="$style.binaryRow">
|
|
|
|
<div :class="$style.binaryCell" v-for="(binaryData, key) in binaryDataEntry" :key="index + '_' + key">
|
|
|
|
<div>
|
|
|
|
<div :class="$style.binaryHeader">
|
|
|
|
{{key}}
|
|
|
|
</div>
|
|
|
|
<div v-if="binaryData.fileName">
|
|
|
|
<div><n8n-text size="small" :bold="true">{{ $locale.baseText('runData.fileName') }}: </n8n-text></div>
|
|
|
|
<div :class="$style.binaryValue">{{binaryData.fileName}}</div>
|
|
|
|
</div>
|
|
|
|
<div v-if="binaryData.directory">
|
|
|
|
<div><n8n-text size="small" :bold="true">{{ $locale.baseText('runData.directory') }}: </n8n-text></div>
|
|
|
|
<div :class="$style.binaryValue">{{binaryData.directory}}</div>
|
|
|
|
</div>
|
|
|
|
<div v-if="binaryData.fileExtension">
|
|
|
|
<div><n8n-text size="small" :bold="true">{{ $locale.baseText('runData.fileExtension') }}:</n8n-text></div>
|
|
|
|
<div :class="$style.binaryValue">{{binaryData.fileExtension}}</div>
|
|
|
|
</div>
|
|
|
|
<div v-if="binaryData.mimeType">
|
|
|
|
<div><n8n-text size="small" :bold="true">{{ $locale.baseText('runData.mimeType') }}: </n8n-text></div>
|
|
|
|
<div :class="$style.binaryValue">{{binaryData.mimeType}}</div>
|
2019-06-23 03:35:23 -07:00
|
|
|
</div>
|
2022-11-24 07:54:43 -08:00
|
|
|
<div v-if="binaryData.fileSize">
|
|
|
|
<div><n8n-text size="small" :bold="true">{{ $locale.baseText('runData.fileSize') }}: </n8n-text></div>
|
|
|
|
<div :class="$style.binaryValue">{{binaryData.fileSize}}</div>
|
|
|
|
</div>
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
<div :class="$style.binaryButtonContainer">
|
2022-11-24 07:54:43 -08:00
|
|
|
<n8n-button v-if="isViewable(index, key)" size="small" :label="$locale.baseText('runData.showBinaryData')" class="binary-data-show-data-button" @click="displayBinaryData(index, key)" />
|
2022-07-20 08:50:39 -07:00
|
|
|
<n8n-button v-if="isDownloadable(index, key)" size="small" type="secondary" :label="$locale.baseText('runData.downloadBinaryData')" class="binary-data-show-data-button" @click="downloadBinaryData(index, key)" />
|
2019-06-23 03:35:23 -07:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2022-07-20 08:50:39 -07:00
|
|
|
<div :class="$style.pagination" v-if="hasNodeRun && !hasRunError && dataCount > pageSize" v-show="!editMode.enabled">
|
2022-04-11 06:12:13 -07:00
|
|
|
<el-pagination
|
|
|
|
background
|
|
|
|
:hide-on-single-page="true"
|
|
|
|
:current-page.sync="currentPage"
|
|
|
|
:pager-count="5"
|
|
|
|
:page-size="pageSize"
|
|
|
|
layout="prev, pager, next"
|
2022-05-23 08:56:15 -07:00
|
|
|
@current-change="onCurrentPageChange"
|
2022-04-11 06:12:13 -07:00
|
|
|
:total="dataCount">
|
|
|
|
</el-pagination>
|
|
|
|
|
|
|
|
<div :class="$style.pageSizeSelector">
|
2022-09-26 02:20:45 -07:00
|
|
|
<n8n-select size="mini" :value="pageSize" @input="onPageSizeChange" popper-append-to-body>
|
2022-11-18 05:59:31 -08:00
|
|
|
<template #prepend>{{ $locale.baseText('ndv.output.pageSize') }}</template>
|
2022-04-11 06:12:13 -07:00
|
|
|
<n8n-option
|
|
|
|
v-for="size in pageSizes"
|
|
|
|
:key="size"
|
|
|
|
:label="size"
|
|
|
|
:value="size">
|
|
|
|
</n8n-option>
|
|
|
|
<n8n-option
|
|
|
|
:label="$locale.baseText('ndv.output.all')"
|
|
|
|
:value="dataCount"
|
|
|
|
>
|
|
|
|
</n8n-option>
|
|
|
|
</n8n-select>
|
|
|
|
</div>
|
|
|
|
</div>
|
2022-10-31 10:59:53 -07:00
|
|
|
<n8n-block-ui :show="blockUI" :class="$style.uiBlocker" />
|
2019-06-23 03:35:23 -07:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script lang="ts">
|
2022-10-06 06:03:55 -07:00
|
|
|
import { PropType } from "vue";
|
|
|
|
import mixins from 'vue-typed-mixins';
|
|
|
|
import { saveAs } from 'file-saver';
|
2019-06-23 03:35:23 -07:00
|
|
|
import {
|
2022-03-28 03:59:53 -07:00
|
|
|
IBinaryData,
|
2019-06-23 03:35:23 -07:00
|
|
|
IBinaryKeyData,
|
|
|
|
IDataObject,
|
|
|
|
INodeExecutionData,
|
2021-11-19 01:17:13 -08:00
|
|
|
INodeTypeDescription,
|
2019-06-23 03:35:23 -07:00
|
|
|
IRunData,
|
|
|
|
IRunExecutionData,
|
|
|
|
} from 'n8n-workflow';
|
|
|
|
|
|
|
|
import {
|
|
|
|
IExecutionResponse,
|
|
|
|
INodeUi,
|
2022-11-04 06:04:31 -07:00
|
|
|
INodeUpdatePropertiesInformation,
|
2022-05-23 08:56:15 -07:00
|
|
|
IRunDataDisplayMode,
|
2022-04-11 06:12:13 -07:00
|
|
|
ITab,
|
2022-12-06 03:50:06 -08:00
|
|
|
NodePanelType,
|
2019-06-23 03:35:23 -07:00
|
|
|
} from '@/Interface';
|
|
|
|
|
2020-05-23 15:53:06 -07:00
|
|
|
import {
|
2022-07-20 08:50:39 -07:00
|
|
|
DATA_PINNING_DOCS_URL,
|
2022-08-04 05:57:19 -07:00
|
|
|
DATA_EDITING_DOCS_URL,
|
2022-07-20 08:50:39 -07:00
|
|
|
LOCAL_STORAGE_PIN_DATA_DISCOVERY_NDV_FLAG,
|
|
|
|
LOCAL_STORAGE_PIN_DATA_DISCOVERY_CANVAS_FLAG,
|
2020-05-23 15:53:06 -07:00
|
|
|
MAX_DISPLAY_DATA_SIZE,
|
|
|
|
MAX_DISPLAY_ITEMS_AUTO_ALL,
|
2022-07-20 08:50:39 -07:00
|
|
|
TEST_PIN_DATA,
|
2020-05-23 15:53:06 -07:00
|
|
|
} from '@/constants';
|
|
|
|
|
2019-06-23 03:35:23 -07:00
|
|
|
import BinaryDataDisplay from '@/components/BinaryDataDisplay.vue';
|
2022-04-11 06:12:13 -07:00
|
|
|
import WarningTooltip from '@/components/WarningTooltip.vue';
|
2021-11-10 10:41:40 -08:00
|
|
|
import NodeErrorView from '@/components/Error/NodeErrorView.vue';
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2022-11-23 04:41:53 -08:00
|
|
|
import { externalHooks } from "@/mixins/externalHooks";
|
|
|
|
import { genericHelpers } from '@/mixins/genericHelpers';
|
|
|
|
import { nodeHelpers } from '@/mixins/nodeHelpers';
|
|
|
|
import { pinData } from '@/mixins/pinData';
|
2022-07-20 08:50:39 -07:00
|
|
|
import { CodeEditor } from "@/components/forms";
|
2022-10-31 10:59:53 -07:00
|
|
|
import { dataPinningEventBus } from '@/event-bus/data-pinning-event-bus';
|
2022-11-23 04:41:53 -08:00
|
|
|
import { clearJsonKey, executionDataToJson, stringSizeInBytes } from '@/utils';
|
2022-10-06 06:03:55 -07:00
|
|
|
import { isEmpty } from '@/utils';
|
2022-11-04 06:04:31 -07:00
|
|
|
import { useWorkflowsStore } from "@/stores/workflows";
|
|
|
|
import { mapStores } from "pinia";
|
|
|
|
import { useNDVStore } from "@/stores/ndv";
|
|
|
|
import { useNodeTypesStore } from "@/stores/nodeTypes";
|
2020-12-18 09:55:53 -08:00
|
|
|
|
2022-11-07 03:27:02 -08:00
|
|
|
const RunDataTable = () => import('@/components/RunDataTable.vue');
|
|
|
|
const RunDataJson = () => import('@/components/RunDataJson.vue');
|
2022-12-06 03:50:06 -08:00
|
|
|
const RunDataSchema = () => import('@/components/RunDataSchema.vue');
|
2022-11-07 03:27:02 -08:00
|
|
|
|
2022-07-20 08:50:39 -07:00
|
|
|
export type EnterEditModeArgs = {
|
|
|
|
origin: 'editIconButton' | 'insertTestDataLink',
|
|
|
|
};
|
|
|
|
|
2019-06-23 03:35:23 -07:00
|
|
|
export default mixins(
|
2021-05-05 17:46:33 -07:00
|
|
|
externalHooks,
|
2019-06-23 03:35:23 -07:00
|
|
|
genericHelpers,
|
|
|
|
nodeHelpers,
|
2022-07-20 08:50:39 -07:00
|
|
|
pinData,
|
2019-06-23 03:35:23 -07:00
|
|
|
)
|
|
|
|
.extend({
|
|
|
|
name: 'RunData',
|
|
|
|
components: {
|
|
|
|
BinaryDataDisplay,
|
2021-04-16 09:33:36 -07:00
|
|
|
NodeErrorView,
|
2022-04-11 06:12:13 -07:00
|
|
|
WarningTooltip,
|
2022-07-20 08:50:39 -07:00
|
|
|
CodeEditor,
|
2022-07-20 04:32:51 -07:00
|
|
|
RunDataTable,
|
2022-10-06 06:03:55 -07:00
|
|
|
RunDataJson,
|
2022-12-06 03:50:06 -08:00
|
|
|
RunDataSchema,
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
2022-05-23 08:56:15 -07:00
|
|
|
props: {
|
|
|
|
nodeUi: {
|
2022-10-06 06:03:55 -07:00
|
|
|
type: Object as PropType<INodeUi>,
|
|
|
|
},
|
2022-05-23 08:56:15 -07:00
|
|
|
runIndex: {
|
|
|
|
type: Number,
|
|
|
|
},
|
|
|
|
linkedRuns: {
|
|
|
|
type: Boolean,
|
|
|
|
},
|
|
|
|
canLinkRuns: {
|
|
|
|
type: Boolean,
|
|
|
|
},
|
|
|
|
tooMuchDataTitle: {
|
|
|
|
type: String,
|
|
|
|
},
|
|
|
|
noDataInBranchMessage: {
|
|
|
|
type: String,
|
|
|
|
},
|
|
|
|
isExecuting: {
|
|
|
|
type: Boolean,
|
|
|
|
},
|
|
|
|
executingMessage: {
|
|
|
|
type: String,
|
|
|
|
},
|
|
|
|
sessionId: {
|
|
|
|
type: String,
|
|
|
|
},
|
|
|
|
paneType: {
|
2022-12-06 03:50:06 -08:00
|
|
|
type: String as PropType<NodePanelType>,
|
2022-05-23 08:56:15 -07:00
|
|
|
},
|
|
|
|
overrideOutputs: {
|
2022-10-12 05:06:28 -07:00
|
|
|
type: Array as PropType<number[]>,
|
2022-05-23 08:56:15 -07:00
|
|
|
},
|
2022-07-20 04:32:51 -07:00
|
|
|
mappingEnabled: {
|
|
|
|
type: Boolean,
|
|
|
|
},
|
|
|
|
distanceFromActive: {
|
|
|
|
type: Number,
|
|
|
|
},
|
|
|
|
showMappingHint: {
|
|
|
|
type: Boolean,
|
|
|
|
},
|
2022-10-31 10:59:53 -07:00
|
|
|
blockUI: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
2022-11-14 04:28:26 -08:00
|
|
|
isProductionExecutionPreview: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
2022-05-23 08:56:15 -07:00
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
data () {
|
|
|
|
return {
|
|
|
|
binaryDataPreviewActive: false,
|
2019-10-02 06:30:51 -07:00
|
|
|
dataSize: 0,
|
|
|
|
showData: false,
|
2019-06-23 03:35:23 -07:00
|
|
|
outputIndex: 0,
|
|
|
|
binaryDataDisplayVisible: false,
|
2022-11-24 07:54:43 -08:00
|
|
|
binaryDataDisplayData: null as IBinaryData | null,
|
2020-05-23 15:53:06 -07:00
|
|
|
|
|
|
|
MAX_DISPLAY_DATA_SIZE,
|
|
|
|
MAX_DISPLAY_ITEMS_AUTO_ALL,
|
2022-04-11 06:12:13 -07:00
|
|
|
currentPage: 1,
|
|
|
|
pageSize: 10,
|
|
|
|
pageSizes: [10, 25, 50, 100],
|
2022-07-20 08:50:39 -07:00
|
|
|
eventBus: dataPinningEventBus,
|
|
|
|
|
|
|
|
pinDataDiscoveryTooltipVisible: false,
|
|
|
|
isControlledPinDataTooltip: false,
|
2019-06-23 03:35:23 -07:00
|
|
|
};
|
|
|
|
},
|
2021-09-15 01:11:59 -07:00
|
|
|
mounted() {
|
|
|
|
this.init();
|
2022-07-20 08:50:39 -07:00
|
|
|
|
2022-09-19 03:26:02 -07:00
|
|
|
if (!this.isPaneTypeInput) {
|
2022-07-20 08:50:39 -07:00
|
|
|
this.eventBus.$on('data-pinning-error', this.onDataPinningError);
|
|
|
|
this.eventBus.$on('data-unpinning', this.onDataUnpinning);
|
|
|
|
|
|
|
|
const hasSeenPinDataTooltip = localStorage.getItem(LOCAL_STORAGE_PIN_DATA_DISCOVERY_NDV_FLAG);
|
|
|
|
if (!hasSeenPinDataTooltip) {
|
|
|
|
this.showPinDataDiscoveryTooltip(this.jsonData);
|
|
|
|
}
|
|
|
|
}
|
2022-11-04 06:04:31 -07:00
|
|
|
this.ndvStore.setNDVBranchIndex({
|
|
|
|
pane: this.paneType as "input" | "output",
|
2022-10-12 05:06:28 -07:00
|
|
|
branchIndex: this.currentOutputIndex,
|
|
|
|
});
|
2022-07-20 08:50:39 -07:00
|
|
|
},
|
|
|
|
destroyed() {
|
|
|
|
this.hidePinDataDiscoveryTooltip();
|
|
|
|
this.eventBus.$off('data-pinning-error', this.onDataPinningError);
|
|
|
|
this.eventBus.$off('data-unpinning', this.onDataUnpinning);
|
2021-09-15 01:11:59 -07:00
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
computed: {
|
2022-11-04 06:04:31 -07:00
|
|
|
...mapStores(
|
|
|
|
useNodeTypesStore,
|
|
|
|
useNDVStore,
|
|
|
|
useWorkflowsStore,
|
|
|
|
),
|
|
|
|
activeNode(): INodeUi | null {
|
|
|
|
return this.ndvStore.activeNode;
|
2022-05-23 08:56:15 -07:00
|
|
|
},
|
2022-07-20 08:50:39 -07:00
|
|
|
dataPinningDocsUrl(): string {
|
|
|
|
return DATA_PINNING_DOCS_URL;
|
|
|
|
},
|
2022-08-04 05:57:19 -07:00
|
|
|
dataEditingDocsUrl(): string{
|
|
|
|
return DATA_EDITING_DOCS_URL;
|
|
|
|
},
|
2022-05-23 08:56:15 -07:00
|
|
|
displayMode(): IRunDataDisplayMode {
|
2022-12-06 03:50:06 -08:00
|
|
|
return this.ndvStore.getPanelDisplayMode(this.paneType);
|
2022-05-23 08:56:15 -07:00
|
|
|
},
|
|
|
|
node(): INodeUi | null {
|
|
|
|
return (this.nodeUi as INodeUi | null) || null;
|
|
|
|
},
|
2022-04-11 06:12:13 -07:00
|
|
|
nodeType (): INodeTypeDescription | null {
|
|
|
|
if (this.node) {
|
2022-11-04 06:04:31 -07:00
|
|
|
return this.nodeTypesStore.getNodeType(this.node.type, this.node.typeVersion);
|
2022-04-11 06:12:13 -07:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
2022-07-20 08:50:39 -07:00
|
|
|
isTriggerNode (): boolean {
|
2022-11-04 06:04:31 -07:00
|
|
|
return this.nodeTypesStore.isTriggerNode(this.node.type);
|
2022-07-20 08:50:39 -07:00
|
|
|
},
|
|
|
|
canPinData (): boolean {
|
2022-09-19 03:26:02 -07:00
|
|
|
return !this.isPaneTypeInput &&
|
2022-07-20 08:50:39 -07:00
|
|
|
this.isPinDataNodeType &&
|
|
|
|
!(this.binaryData && this.binaryData.length > 0);
|
|
|
|
},
|
2022-04-11 06:12:13 -07:00
|
|
|
buttons(): Array<{label: string, value: string}> {
|
|
|
|
const defaults = [
|
|
|
|
{ label: this.$locale.baseText('runData.table'), value: 'table'},
|
|
|
|
{ label: this.$locale.baseText('runData.json'), value: 'json'},
|
|
|
|
];
|
2022-12-06 03:50:06 -08:00
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
if (this.binaryData.length) {
|
2022-12-06 03:50:06 -08:00
|
|
|
defaults.push({ label: this.$locale.baseText('runData.binary'), value: 'binary'});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.isPaneTypeInput && window.posthog?.isFeatureEnabled?.('schema-view')) {
|
|
|
|
defaults.unshift({ label: this.$locale.baseText('runData.schema'), value: 'schema'});
|
2022-04-11 06:12:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return defaults;
|
|
|
|
},
|
2021-08-29 04:36:17 -07:00
|
|
|
hasNodeRun(): boolean {
|
2022-07-20 08:50:39 -07:00
|
|
|
return Boolean(!this.isExecuting && this.node && (this.workflowRunData && this.workflowRunData.hasOwnProperty(this.node.name) || this.hasPinData));
|
2021-08-29 04:36:17 -07:00
|
|
|
},
|
feat(editor, core, cli): implement new workflow experience (#4358)
* feat(ExecuteWorkflowTrigger node): Implement ExecuteWorkflowTrigger node (#4108)
* feat(ExecuteWorkflowTrigger node): Implement ExecuteWorkflowTrigger node
* feat(editor): Do not show duplicate button if canvas contains `maxNodes` amount of nodes
* feat(ManualTrigger node): Implement ManualTrigger node (#4110)
* feat(ManualTrigger node): Implement ManualTrigger node
* :memo: Remove generics doc items from ManualTrigger node
* feat(editor-ui): Trigger tab redesign (#4150)
* :construction: Begin with TriggerPanel implementation, add Other Trigger Nodes subcategory
* :construction: Extracted categorized categories/subcategory/nodes rendering into its own component — CategorizedItems, removed SubcategoryPanel, added translations
* :sparkles: Implement MainPanel background scrim
* :recycle: Move `categoriesWithNodes`, 'visibleNodeTypes` and 'categorizedItems` to store, implemented dynamic categories count based on `selectedType`
* :bug: Fix SlideTransition for all the NodeCreato panels
* :lipstick: Fix cursos for CategoryItem and NodeItem
* :bug: Make sure ALL_NODE_FILTER is always set when MainPanel is mounted
* :art: Address PR comments
* label: Use Array type for CategorizedItems props
* :label: Add proper types for Vue props
* 🎨 Use standard component registration for CategorizedItems inside TriggerHelperPanel
* 🎨 Use kebab case for main-panel and icon component
* :label: Improve types
* feat(editor-ui): Redesign search input inside node creator panel (#4204)
* :construction: Begin with TriggerPanel implementation, add Other Trigger Nodes subcategory
* :construction: Extracted categorized categories/subcategory/nodes rendering into its own component — CategorizedItems, removed SubcategoryPanel, added translations
* :sparkles: Implement MainPanel background scrim
* :recycle: Move `categoriesWithNodes`, 'visibleNodeTypes` and 'categorizedItems` to store, implemented dynamic categories count based on `selectedType`
* :bug: Fix SlideTransition for all the NodeCreato panels
* :lipstick: Fix cursos for CategoryItem and NodeItem
* :bug: Make sure ALL_NODE_FILTER is always set when MainPanel is mounted
* :art: Address PR comments
* label: Use Array type for CategorizedItems props
* :label: Add proper types for Vue props
* 🎨 Use standard component registration for CategorizedItems inside TriggerHelperPanel
* :sparkles: Redesign search input and unify usage of categorized items
* :label: Use lowercase "Boolean" as `isSearchVisible` computed return type
* :fire: Remove useless emit
* :sparkles: Implement no result view based on subcategory, minor fixes
* :art: Remove unused properties
* feat(node-email): Change EmailReadImap display name and name (#4239)
* feat(editor-ui): Implement "Choose a Triger" action and related behaviour (#4226)
* :sparkles: Implement "Choose a Triger" action and related behaviour
* :mute: Lint fix
* :recycle: Remove PlaceholderTrigger node, add a button instead
* :art: Merge onMouseEnter and onMouseLeave to a single function
* :bulb: Add comment
* :fire: Remove PlaceholderNode registration
* :art: Rename TriggerPlaceholderButton to CanvasAddButton
* :sparkles: Add method to unregister custom action and rework CanvasAddButton centering logic
* :art: Run `setRecenteredCanvasAddButtonPosition` on `CanvasAddButton` mount
* fix(editor): Fix selecting of node from node-creator panel by clicking
* :twisted_rightwards_arrows: Merge fixes
* fix(editor): Show execute workflow trigger instead of workflow trigger in the trigger helper panel
* feat(editor): Fix node creator panel slide transition (#4261)
* fix(editor): Fix node creator panel slide-in/slide-out transitions
* :art: Fix naming
* :art: Use kebab-case for transition component name
* feat(editor): Disable execution and show notice when user tries to run workflow without enabled triggers
* fix(editor): Address first batch of new WF experience review (#4279)
* fix(editor): Fix first batch of review items
* bug(editor): Fix nodeview canvas add button centering
* :mute: Fix linter errors
* bug(ManualTrigger Node): Fix manual trigger node execution
* fix(editor): Do not show canvas add button in execution or demo mode and prevent clicking if creator is open
* fix(editor): do not show pin data tooltip for manual trigger node
* fix(editor): do not use nodeViewOffset on zoomToFit
* :lipstick: Add margin for last node creator item and set font-weight to 700 for category title
* :sparkles: Position welcome note next to the added trigger node
* :bug: Remve always true welcome note
* feat(editor): Minor UI and UX tweaks (#4328)
* :lipstick: Make top viewport buttons less prominent
* :sparkles: Allow user to switch to all tabs if it contains filter results, move nodecreator state props to its own module
* :mute: Fix linting errors
* :mute: Fix linting errors
* :mute: Fix linting errors
* chore(build): Ping Turbo version to 1.5.5
* :lipstick: Minor traigger panel and node view style changes
* :speech_balloon: Update display name of execute workflow trigger
* feat(core, editor): Update subworkflow execution logic (#4269)
* :sparkles: Implement `findWorkflowStart`
* :zap: Extend `WorkflowOperationError`
* :zap: Add `WorkflowOperationError` to toast
* :blue_book: Extend interface
* :sparkles: Add `subworkflowExecutionError` to store
* :sparkles: Create `SubworkflowOperationError`
* :zap: Render subworkflow error as node error
* :truck: Move subworkflow start validation to `cli`
* :zap: Reset subworkflow execution error state
* :fire: Remove unused import
* :zap: Adjust CLI commands
* :fire: Remove unneeded check
* :fire: Remove stray log
* :zap: Simplify syntax
* :zap: Sort in case both Start and EWT present
* :recycle: Address Omar's feedback
* :fire: Remove unneeded lint exception
* :pencil2: Fix copy
* :shirt: Fix lint
* fix: moved find start node function to catchable place
Co-authored-by: Omar Ajoue <krynble@gmail.com>
* :lipstick: Change ExecuteWorkflow node to primary
* :sparkles: Allow user to navigate to all tab if it contains search results
* :bug: Fixed canvas control button while in demo, disable workflow activation for non-activavle nodes and revert zoomToFit bottom offset
* :fix: Do not chow request text if there's results
* :speech_balloon: Update noResults text
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
Co-authored-by: Omar Ajoue <krynble@gmail.com>
2022-10-18 05:23:22 -07:00
|
|
|
subworkflowExecutionError(): Error | null {
|
2022-11-04 06:04:31 -07:00
|
|
|
return this.workflowsStore.subWorkflowExecutionError;
|
feat(editor, core, cli): implement new workflow experience (#4358)
* feat(ExecuteWorkflowTrigger node): Implement ExecuteWorkflowTrigger node (#4108)
* feat(ExecuteWorkflowTrigger node): Implement ExecuteWorkflowTrigger node
* feat(editor): Do not show duplicate button if canvas contains `maxNodes` amount of nodes
* feat(ManualTrigger node): Implement ManualTrigger node (#4110)
* feat(ManualTrigger node): Implement ManualTrigger node
* :memo: Remove generics doc items from ManualTrigger node
* feat(editor-ui): Trigger tab redesign (#4150)
* :construction: Begin with TriggerPanel implementation, add Other Trigger Nodes subcategory
* :construction: Extracted categorized categories/subcategory/nodes rendering into its own component — CategorizedItems, removed SubcategoryPanel, added translations
* :sparkles: Implement MainPanel background scrim
* :recycle: Move `categoriesWithNodes`, 'visibleNodeTypes` and 'categorizedItems` to store, implemented dynamic categories count based on `selectedType`
* :bug: Fix SlideTransition for all the NodeCreato panels
* :lipstick: Fix cursos for CategoryItem and NodeItem
* :bug: Make sure ALL_NODE_FILTER is always set when MainPanel is mounted
* :art: Address PR comments
* label: Use Array type for CategorizedItems props
* :label: Add proper types for Vue props
* 🎨 Use standard component registration for CategorizedItems inside TriggerHelperPanel
* 🎨 Use kebab case for main-panel and icon component
* :label: Improve types
* feat(editor-ui): Redesign search input inside node creator panel (#4204)
* :construction: Begin with TriggerPanel implementation, add Other Trigger Nodes subcategory
* :construction: Extracted categorized categories/subcategory/nodes rendering into its own component — CategorizedItems, removed SubcategoryPanel, added translations
* :sparkles: Implement MainPanel background scrim
* :recycle: Move `categoriesWithNodes`, 'visibleNodeTypes` and 'categorizedItems` to store, implemented dynamic categories count based on `selectedType`
* :bug: Fix SlideTransition for all the NodeCreato panels
* :lipstick: Fix cursos for CategoryItem and NodeItem
* :bug: Make sure ALL_NODE_FILTER is always set when MainPanel is mounted
* :art: Address PR comments
* label: Use Array type for CategorizedItems props
* :label: Add proper types for Vue props
* 🎨 Use standard component registration for CategorizedItems inside TriggerHelperPanel
* :sparkles: Redesign search input and unify usage of categorized items
* :label: Use lowercase "Boolean" as `isSearchVisible` computed return type
* :fire: Remove useless emit
* :sparkles: Implement no result view based on subcategory, minor fixes
* :art: Remove unused properties
* feat(node-email): Change EmailReadImap display name and name (#4239)
* feat(editor-ui): Implement "Choose a Triger" action and related behaviour (#4226)
* :sparkles: Implement "Choose a Triger" action and related behaviour
* :mute: Lint fix
* :recycle: Remove PlaceholderTrigger node, add a button instead
* :art: Merge onMouseEnter and onMouseLeave to a single function
* :bulb: Add comment
* :fire: Remove PlaceholderNode registration
* :art: Rename TriggerPlaceholderButton to CanvasAddButton
* :sparkles: Add method to unregister custom action and rework CanvasAddButton centering logic
* :art: Run `setRecenteredCanvasAddButtonPosition` on `CanvasAddButton` mount
* fix(editor): Fix selecting of node from node-creator panel by clicking
* :twisted_rightwards_arrows: Merge fixes
* fix(editor): Show execute workflow trigger instead of workflow trigger in the trigger helper panel
* feat(editor): Fix node creator panel slide transition (#4261)
* fix(editor): Fix node creator panel slide-in/slide-out transitions
* :art: Fix naming
* :art: Use kebab-case for transition component name
* feat(editor): Disable execution and show notice when user tries to run workflow without enabled triggers
* fix(editor): Address first batch of new WF experience review (#4279)
* fix(editor): Fix first batch of review items
* bug(editor): Fix nodeview canvas add button centering
* :mute: Fix linter errors
* bug(ManualTrigger Node): Fix manual trigger node execution
* fix(editor): Do not show canvas add button in execution or demo mode and prevent clicking if creator is open
* fix(editor): do not show pin data tooltip for manual trigger node
* fix(editor): do not use nodeViewOffset on zoomToFit
* :lipstick: Add margin for last node creator item and set font-weight to 700 for category title
* :sparkles: Position welcome note next to the added trigger node
* :bug: Remve always true welcome note
* feat(editor): Minor UI and UX tweaks (#4328)
* :lipstick: Make top viewport buttons less prominent
* :sparkles: Allow user to switch to all tabs if it contains filter results, move nodecreator state props to its own module
* :mute: Fix linting errors
* :mute: Fix linting errors
* :mute: Fix linting errors
* chore(build): Ping Turbo version to 1.5.5
* :lipstick: Minor traigger panel and node view style changes
* :speech_balloon: Update display name of execute workflow trigger
* feat(core, editor): Update subworkflow execution logic (#4269)
* :sparkles: Implement `findWorkflowStart`
* :zap: Extend `WorkflowOperationError`
* :zap: Add `WorkflowOperationError` to toast
* :blue_book: Extend interface
* :sparkles: Add `subworkflowExecutionError` to store
* :sparkles: Create `SubworkflowOperationError`
* :zap: Render subworkflow error as node error
* :truck: Move subworkflow start validation to `cli`
* :zap: Reset subworkflow execution error state
* :fire: Remove unused import
* :zap: Adjust CLI commands
* :fire: Remove unneeded check
* :fire: Remove stray log
* :zap: Simplify syntax
* :zap: Sort in case both Start and EWT present
* :recycle: Address Omar's feedback
* :fire: Remove unneeded lint exception
* :pencil2: Fix copy
* :shirt: Fix lint
* fix: moved find start node function to catchable place
Co-authored-by: Omar Ajoue <krynble@gmail.com>
* :lipstick: Change ExecuteWorkflow node to primary
* :sparkles: Allow user to navigate to all tab if it contains search results
* :bug: Fixed canvas control button while in demo, disable workflow activation for non-activavle nodes and revert zoomToFit bottom offset
* :fix: Do not chow request text if there's results
* :speech_balloon: Update noResults text
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
Co-authored-by: Omar Ajoue <krynble@gmail.com>
2022-10-18 05:23:22 -07:00
|
|
|
},
|
|
|
|
hasSubworkflowExecutionError(): boolean {
|
|
|
|
return Boolean(this.subworkflowExecutionError);
|
|
|
|
},
|
2021-08-29 04:36:17 -07:00
|
|
|
hasRunError(): boolean {
|
|
|
|
return Boolean(this.node && this.workflowRunData && this.workflowRunData[this.node.name] && this.workflowRunData[this.node.name][this.runIndex] && this.workflowRunData[this.node.name][this.runIndex].error);
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
workflowExecution (): IExecutionResponse | null {
|
2022-11-04 06:04:31 -07:00
|
|
|
return this.workflowsStore.getWorkflowExecution;
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
workflowRunData (): IRunData | null {
|
|
|
|
if (this.workflowExecution === null) {
|
|
|
|
return null;
|
|
|
|
}
|
2022-09-21 02:51:20 -07:00
|
|
|
const executionData: IRunExecutionData | undefined = this.workflowExecution.data;
|
2022-04-11 06:12:13 -07:00
|
|
|
if (executionData && executionData.resultData) {
|
|
|
|
return executionData.resultData.runData;
|
2020-07-10 01:12:30 -07:00
|
|
|
}
|
2022-04-11 06:12:13 -07:00
|
|
|
return null;
|
2020-05-23 15:53:06 -07:00
|
|
|
},
|
2022-04-11 06:12:13 -07:00
|
|
|
dataCount (): number {
|
2022-05-23 08:56:15 -07:00
|
|
|
return this.getDataCount(this.runIndex, this.currentOutputIndex);
|
2022-04-11 06:12:13 -07:00
|
|
|
},
|
|
|
|
dataSizeInMB(): string {
|
|
|
|
return (this.dataSize / 1024 / 1000).toLocaleString();
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
maxOutputIndex (): number {
|
|
|
|
if (this.node === null) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const runData: IRunData | null = this.workflowRunData;
|
|
|
|
|
|
|
|
if (runData === null || !runData.hasOwnProperty(this.node.name)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (runData[this.node.name].length < this.runIndex) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-05-23 08:56:15 -07:00
|
|
|
if (runData[this.node.name][this.runIndex]) {
|
|
|
|
const taskData = runData[this.node.name][this.runIndex].data;
|
|
|
|
if (taskData && taskData.main) {
|
|
|
|
return taskData.main.length - 1;
|
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
|
2022-05-23 08:56:15 -07:00
|
|
|
return 0;
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
2022-10-12 05:06:28 -07:00
|
|
|
currentPageOffset(): number {
|
|
|
|
return this.pageSize * (this.currentPage - 1);
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
maxRunIndex (): number {
|
|
|
|
if (this.node === null) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const runData: IRunData | null = this.workflowRunData;
|
|
|
|
|
|
|
|
if (runData === null || !runData.hasOwnProperty(this.node.name)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (runData[this.node.name].length) {
|
|
|
|
return runData[this.node.name].length - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
},
|
2022-07-20 08:50:39 -07:00
|
|
|
rawInputData (): INodeExecutionData[] {
|
|
|
|
let inputData: INodeExecutionData[] = [];
|
|
|
|
|
|
|
|
if (this.node) {
|
|
|
|
inputData = this.getNodeInputData(this.node, this.runIndex, this.currentOutputIndex);
|
|
|
|
}
|
|
|
|
|
2019-12-23 12:54:27 -08:00
|
|
|
if (inputData.length === 0 || !Array.isArray(inputData)) {
|
2019-06-23 03:35:23 -07:00
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2022-07-20 08:50:39 -07:00
|
|
|
return inputData;
|
|
|
|
},
|
|
|
|
inputData (): INodeExecutionData[] {
|
|
|
|
let inputData = this.rawInputData;
|
|
|
|
|
2022-11-14 04:28:26 -08:00
|
|
|
if (this.node && this.pinData && !this.isProductionExecutionPreview) {
|
2022-08-23 08:29:09 -07:00
|
|
|
inputData = Array.isArray(this.pinData)
|
|
|
|
? this.pinData.map((value) => ({
|
|
|
|
json: value,
|
|
|
|
}))
|
|
|
|
: [{
|
|
|
|
json: this.pinData,
|
|
|
|
}];
|
2022-07-20 08:50:39 -07:00
|
|
|
}
|
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
const offset = this.pageSize * (this.currentPage - 1);
|
|
|
|
inputData = inputData.slice(offset, offset + this.pageSize);
|
2020-05-23 15:53:06 -07:00
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
return inputData;
|
|
|
|
},
|
|
|
|
jsonData (): IDataObject[] {
|
2022-10-06 06:03:55 -07:00
|
|
|
return executionDataToJson(this.inputData);
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
binaryData (): IBinaryKeyData[] {
|
2022-05-23 08:56:15 -07:00
|
|
|
if (!this.node) {
|
2019-06-23 03:35:23 -07:00
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2022-06-20 12:39:24 -07:00
|
|
|
const binaryData = this.getBinaryData(this.workflowRunData, this.node.name, this.runIndex, this.currentOutputIndex);
|
|
|
|
return binaryData.filter((data) => Boolean(data && Object.keys(data).length));
|
2022-05-23 08:56:15 -07:00
|
|
|
},
|
|
|
|
currentOutputIndex(): number {
|
|
|
|
if (this.overrideOutputs && this.overrideOutputs.length && !this.overrideOutputs.includes(this.outputIndex)) {
|
|
|
|
return this.overrideOutputs[0] as number;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.outputIndex;
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
2022-04-11 06:12:13 -07:00
|
|
|
branches (): ITab[] {
|
|
|
|
function capitalize(name: string) {
|
|
|
|
return name.charAt(0).toLocaleUpperCase() + name.slice(1);
|
|
|
|
}
|
|
|
|
const branches: ITab[] = [];
|
|
|
|
for (let i = 0; i <= this.maxOutputIndex; i++) {
|
2022-05-23 08:56:15 -07:00
|
|
|
if (this.overrideOutputs && !this.overrideOutputs.includes(i)) {
|
|
|
|
continue;
|
|
|
|
}
|
2022-04-11 06:12:13 -07:00
|
|
|
const itemsCount = this.getDataCount(this.runIndex, i);
|
2022-05-23 08:56:15 -07:00
|
|
|
const items = this.$locale.baseText('ndv.output.items', {adjustToNumber: itemsCount});
|
2022-04-11 06:12:13 -07:00
|
|
|
let outputName = this.getOutputName(i);
|
|
|
|
if (`${outputName}` === `${i}`) {
|
|
|
|
outputName = `${this.$locale.baseText('ndv.output')} ${outputName}`;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
outputName = capitalize(`${this.getOutputName(i)} ${this.$locale.baseText('ndv.output.branch')}`);
|
|
|
|
}
|
|
|
|
branches.push({
|
|
|
|
label: itemsCount ? `${outputName} (${itemsCount} ${items})` : outputName,
|
|
|
|
value: i,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return branches;
|
|
|
|
},
|
2022-07-20 08:50:39 -07:00
|
|
|
editMode(): { enabled: boolean; value: string; } {
|
2022-09-19 03:26:02 -07:00
|
|
|
return this.isPaneTypeInput
|
|
|
|
? { enabled: false, value: '' }
|
2022-11-04 06:04:31 -07:00
|
|
|
: this.ndvStore.outputPanelEditMode;
|
2022-09-19 03:26:02 -07:00
|
|
|
},
|
|
|
|
isPaneTypeInput(): boolean {
|
|
|
|
return this.paneType === 'input';
|
2022-07-20 08:50:39 -07:00
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
methods: {
|
2022-10-12 05:06:28 -07:00
|
|
|
onItemHover(itemIndex: number | null) {
|
|
|
|
if (itemIndex === null) {
|
|
|
|
this.$emit('itemHover', null);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.$emit('itemHover', {
|
|
|
|
outputIndex: this.currentOutputIndex,
|
|
|
|
itemIndex,
|
|
|
|
});
|
|
|
|
},
|
2022-07-20 08:50:39 -07:00
|
|
|
onClickDataPinningDocsLink() {
|
|
|
|
this.$telemetry.track('User clicked ndv link', {
|
2022-11-04 06:04:31 -07:00
|
|
|
workflow_id: this.workflowsStore.workflowId,
|
2022-07-20 08:50:39 -07:00
|
|
|
session_id: this.sessionId,
|
2022-11-04 06:04:31 -07:00
|
|
|
node_type: this.activeNode?.type,
|
2022-07-20 08:50:39 -07:00
|
|
|
pane: 'output',
|
|
|
|
type: 'data-pinning-docs',
|
|
|
|
});
|
|
|
|
},
|
|
|
|
showPinDataDiscoveryTooltip(value: IDataObject[]) {
|
|
|
|
if (!this.isTriggerNode) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value && value.length > 0) {
|
|
|
|
this.pinDataDiscoveryComplete();
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
this.isControlledPinDataTooltip = true;
|
|
|
|
this.pinDataDiscoveryTooltipVisible = true;
|
|
|
|
this.eventBus.$emit('data-pinning-discovery', { isTooltipVisible: true });
|
|
|
|
}, 500); // Wait for NDV to open
|
|
|
|
}
|
|
|
|
},
|
|
|
|
hidePinDataDiscoveryTooltip() {
|
|
|
|
if (this.pinDataDiscoveryTooltipVisible) {
|
|
|
|
this.isControlledPinDataTooltip = false;
|
|
|
|
this.pinDataDiscoveryTooltipVisible = false;
|
|
|
|
this.eventBus.$emit('data-pinning-discovery', { isTooltipVisible: false });
|
|
|
|
}
|
|
|
|
},
|
|
|
|
pinDataDiscoveryComplete() {
|
|
|
|
localStorage.setItem(LOCAL_STORAGE_PIN_DATA_DISCOVERY_NDV_FLAG, 'true');
|
|
|
|
localStorage.setItem(LOCAL_STORAGE_PIN_DATA_DISCOVERY_CANVAS_FLAG, 'true');
|
|
|
|
},
|
|
|
|
enterEditMode({ origin }: EnterEditModeArgs) {
|
2022-08-22 08:46:22 -07:00
|
|
|
const inputData = this.pinData
|
2022-10-06 06:03:55 -07:00
|
|
|
? clearJsonKey(this.pinData)
|
|
|
|
: executionDataToJson(this.rawInputData);
|
2022-08-22 08:46:22 -07:00
|
|
|
|
2022-07-20 08:50:39 -07:00
|
|
|
const data = inputData.length > 0
|
|
|
|
? inputData
|
|
|
|
: TEST_PIN_DATA;
|
|
|
|
|
2022-11-04 06:04:31 -07:00
|
|
|
this.ndvStore.setOutputPanelEditModeEnabled(true);
|
|
|
|
this.ndvStore.setOutputPanelEditModeValue(JSON.stringify(data, null, 2));
|
2022-07-20 08:50:39 -07:00
|
|
|
|
|
|
|
this.$telemetry.track('User opened ndv edit state', {
|
2022-11-04 06:04:31 -07:00
|
|
|
node_type: this.activeNode?.type,
|
2022-07-20 08:50:39 -07:00
|
|
|
click_type: origin === 'editIconButton' ? 'button' : 'link',
|
|
|
|
session_id: this.sessionId,
|
|
|
|
run_index: this.runIndex,
|
|
|
|
is_output_present: this.hasNodeRun || this.hasPinData,
|
|
|
|
view: !this.hasNodeRun && !this.hasPinData ? 'undefined' : this.displayMode,
|
|
|
|
is_data_pinned: this.hasPinData,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
onClickCancelEdit() {
|
2022-11-04 06:04:31 -07:00
|
|
|
this.ndvStore.setOutputPanelEditModeEnabled(false);
|
|
|
|
this.ndvStore.setOutputPanelEditModeValue('');
|
2022-07-20 08:50:39 -07:00
|
|
|
this.onExitEditMode({ type: 'cancel' });
|
|
|
|
},
|
|
|
|
onClickSaveEdit() {
|
|
|
|
const { value } = this.editMode;
|
|
|
|
|
|
|
|
this.clearAllStickyNotifications();
|
|
|
|
|
|
|
|
if (!this.isValidPinDataSize(value)) {
|
|
|
|
this.onDataPinningError({ errorType: 'data-too-large', source: 'save-edit' });
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.isValidPinDataJSON(value)) {
|
|
|
|
this.onDataPinningError({ errorType: 'invalid-json', source: 'save-edit' });
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-11-04 06:04:31 -07:00
|
|
|
this.ndvStore.setOutputPanelEditModeEnabled(false);
|
|
|
|
this.workflowsStore.pinData({ node: this.node, data: clearJsonKey(value) as INodeExecutionData[] });
|
2022-07-20 08:50:39 -07:00
|
|
|
|
|
|
|
this.onDataPinningSuccess({ source: 'save-edit' });
|
|
|
|
|
|
|
|
this.onExitEditMode({ type: 'save' });
|
|
|
|
},
|
|
|
|
onExitEditMode({ type }: { type: 'save' | 'cancel' }) {
|
|
|
|
this.$telemetry.track('User closed ndv edit state', {
|
2022-11-04 06:04:31 -07:00
|
|
|
node_type: this.activeNode?.type,
|
2022-07-20 08:50:39 -07:00
|
|
|
session_id: this.sessionId,
|
|
|
|
run_index: this.runIndex,
|
|
|
|
view: this.displayMode,
|
|
|
|
type,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
onDataUnpinning(
|
|
|
|
{ source }: { source: 'banner-link' | 'pin-icon-click' | 'unpin-and-execute-modal' },
|
|
|
|
) {
|
|
|
|
this.$telemetry.track('User unpinned ndv data', {
|
2022-11-04 06:04:31 -07:00
|
|
|
node_type: this.activeNode?.type,
|
2022-07-20 08:50:39 -07:00
|
|
|
session_id: this.sessionId,
|
|
|
|
run_index: this.runIndex,
|
|
|
|
source,
|
|
|
|
data_size: stringSizeInBytes(this.pinData),
|
|
|
|
});
|
|
|
|
},
|
|
|
|
onDataPinningSuccess({ source }: { source: 'pin-icon-click' | 'save-edit' }) {
|
2022-08-19 06:35:39 -07:00
|
|
|
const telemetryPayload = {
|
2022-07-20 08:50:39 -07:00
|
|
|
pinning_source: source,
|
|
|
|
node_type: this.activeNode.type,
|
|
|
|
session_id: this.sessionId,
|
|
|
|
data_size: stringSizeInBytes(this.pinData),
|
|
|
|
view: this.displayMode,
|
|
|
|
run_index: this.runIndex,
|
2022-08-19 06:35:39 -07:00
|
|
|
};
|
|
|
|
this.$externalHooks().run('runData.onDataPinningSuccess', telemetryPayload);
|
|
|
|
this.$telemetry.track('Ndv data pinning success', telemetryPayload);
|
2022-07-20 08:50:39 -07:00
|
|
|
},
|
|
|
|
onDataPinningError(
|
|
|
|
{ errorType, source }: {
|
|
|
|
errorType: 'data-too-large' | 'invalid-json',
|
|
|
|
source: 'on-ndv-close-modal' | 'pin-icon-click' | 'save-edit'
|
|
|
|
},
|
|
|
|
) {
|
|
|
|
this.$telemetry.track('Ndv data pinning failure', {
|
|
|
|
pinning_source: source,
|
|
|
|
node_type: this.activeNode.type,
|
|
|
|
session_id: this.sessionId,
|
|
|
|
data_size: stringSizeInBytes(this.pinData),
|
|
|
|
view: this.displayMode,
|
|
|
|
run_index: this.runIndex,
|
|
|
|
error_type: errorType,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
async onTogglePinData(
|
|
|
|
{ source }: { source: 'banner-link' | 'pin-icon-click' | 'unpin-and-execute-modal' },
|
|
|
|
) {
|
|
|
|
if (source === 'pin-icon-click') {
|
2022-08-19 06:35:39 -07:00
|
|
|
const telemetryPayload = {
|
2022-07-20 08:50:39 -07:00
|
|
|
node_type: this.activeNode.type,
|
|
|
|
session_id: this.sessionId,
|
|
|
|
run_index: this.runIndex,
|
|
|
|
view: !this.hasNodeRun && !this.hasPinData ? 'none' : this.displayMode,
|
2022-08-19 06:35:39 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
this.$externalHooks().run('runData.onTogglePinData', telemetryPayload);
|
|
|
|
this.$telemetry.track('User clicked pin data icon', telemetryPayload);
|
2022-07-20 08:50:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
this.updateNodeParameterIssues(this.node);
|
|
|
|
|
|
|
|
if (this.hasPinData) {
|
|
|
|
this.onDataUnpinning({ source });
|
2022-11-04 06:04:31 -07:00
|
|
|
this.workflowsStore.unpinData({ node: this.node });
|
2022-07-20 08:50:39 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-11-04 06:04:31 -07:00
|
|
|
const data = executionDataToJson(this.rawInputData) as INodeExecutionData[];
|
2022-07-20 08:50:39 -07:00
|
|
|
|
|
|
|
if (!this.isValidPinDataSize(data)) {
|
|
|
|
this.onDataPinningError({ errorType: 'data-too-large', source: 'pin-icon-click' });
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-10-14 01:17:53 -07:00
|
|
|
this.onDataPinningSuccess({ source: 'pin-icon-click' });
|
2022-07-20 08:50:39 -07:00
|
|
|
|
2022-11-04 06:04:31 -07:00
|
|
|
this.workflowsStore.pinData({ node: this.node, data });
|
2022-07-20 08:50:39 -07:00
|
|
|
|
|
|
|
if (this.maxRunIndex > 0) {
|
|
|
|
this.$showToast({
|
|
|
|
title: this.$locale.baseText('ndv.pinData.pin.multipleRuns.title', {
|
|
|
|
interpolate: {
|
|
|
|
index: `${this.runIndex}`,
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
message: this.$locale.baseText('ndv.pinData.pin.multipleRuns.description'),
|
|
|
|
type: 'success',
|
|
|
|
duration: 2000,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
this.hidePinDataDiscoveryTooltip();
|
|
|
|
this.pinDataDiscoveryComplete();
|
|
|
|
},
|
2022-05-23 08:56:15 -07:00
|
|
|
switchToBinary() {
|
|
|
|
this.onDisplayModeChange('binary');
|
|
|
|
},
|
|
|
|
onBranchChange(value: number) {
|
|
|
|
this.outputIndex = value;
|
|
|
|
|
|
|
|
this.$telemetry.track('User changed ndv branch', {
|
|
|
|
session_id: this.sessionId,
|
|
|
|
branch_index: value,
|
|
|
|
node_type: this.activeNode.type,
|
|
|
|
node_type_input_selection: this.nodeType? this.nodeType.name: '',
|
|
|
|
pane: this.paneType,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
showTooMuchData() {
|
|
|
|
this.showData = true;
|
|
|
|
this.$telemetry.track('User clicked ndv button', {
|
|
|
|
node_type: this.activeNode.type,
|
2022-11-04 06:04:31 -07:00
|
|
|
workflow_id: this.workflowsStore.workflowId,
|
2022-05-23 08:56:15 -07:00
|
|
|
session_id: this.sessionId,
|
|
|
|
pane: this.paneType,
|
|
|
|
type: 'showTooMuchData',
|
|
|
|
});
|
|
|
|
},
|
|
|
|
linkRun() {
|
|
|
|
this.$emit('linkRun');
|
|
|
|
},
|
|
|
|
unlinkRun() {
|
|
|
|
this.$emit('unlinkRun');
|
|
|
|
},
|
|
|
|
onCurrentPageChange() {
|
|
|
|
this.$telemetry.track('User changed ndv page', {
|
2022-11-04 06:04:31 -07:00
|
|
|
node_type: this.activeNode?.type,
|
|
|
|
workflow_id: this.workflowsStore.workflowId,
|
2022-05-23 08:56:15 -07:00
|
|
|
session_id: this.sessionId,
|
|
|
|
pane: this.paneType,
|
|
|
|
page_selected: this.currentPage,
|
|
|
|
page_size: this.pageSize,
|
|
|
|
items_total: this.dataCount,
|
|
|
|
});
|
|
|
|
},
|
2022-04-11 06:12:13 -07:00
|
|
|
onPageSizeChange(pageSize: number) {
|
|
|
|
this.pageSize = pageSize;
|
|
|
|
const maxPage = Math.ceil(this.dataCount / this.pageSize);
|
|
|
|
if (maxPage < this.currentPage) {
|
|
|
|
this.currentPage = maxPage;
|
|
|
|
}
|
2022-05-23 08:56:15 -07:00
|
|
|
|
|
|
|
this.$telemetry.track('User changed ndv page size', {
|
2022-11-04 06:04:31 -07:00
|
|
|
node_type: this.activeNode?.type,
|
|
|
|
workflow_id: this.workflowsStore.workflowId,
|
2022-05-23 08:56:15 -07:00
|
|
|
session_id: this.sessionId,
|
|
|
|
pane: this.paneType,
|
|
|
|
page_selected: this.currentPage,
|
|
|
|
page_size: this.pageSize,
|
|
|
|
items_total: this.dataCount,
|
|
|
|
});
|
2022-04-11 06:12:13 -07:00
|
|
|
},
|
2022-05-23 08:56:15 -07:00
|
|
|
onDisplayModeChange(displayMode: IRunDataDisplayMode) {
|
2022-04-11 06:12:13 -07:00
|
|
|
const previous = this.displayMode;
|
2022-12-06 03:50:06 -08:00
|
|
|
this.ndvStore.setPanelDisplayMode({pane: this.paneType, mode: displayMode});
|
2022-04-11 06:12:13 -07:00
|
|
|
|
|
|
|
const dataContainer = this.$refs.dataContainer;
|
|
|
|
if (dataContainer) {
|
|
|
|
const dataDisplay = (dataContainer as Element).children[0];
|
|
|
|
|
|
|
|
if (dataDisplay){
|
|
|
|
dataDisplay.scrollTo(0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.closeBinaryDataDisplay();
|
|
|
|
this.$externalHooks().run('runData.displayModeChanged', { newValue: displayMode, oldValue: previous });
|
2022-05-23 08:56:15 -07:00
|
|
|
if(this.activeNode) {
|
|
|
|
this.$telemetry.track('User changed ndv item view', {
|
|
|
|
previous_view: previous,
|
|
|
|
new_view: displayMode,
|
|
|
|
node_type: this.activeNode.type,
|
2022-11-04 06:04:31 -07:00
|
|
|
workflow_id: this.workflowsStore.workflowId,
|
2022-05-23 08:56:15 -07:00
|
|
|
session_id: this.sessionId,
|
|
|
|
pane: this.paneType,
|
|
|
|
});
|
2022-04-11 06:12:13 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
getRunLabel(option: number) {
|
|
|
|
let itemsCount = 0;
|
|
|
|
for (let i = 0; i <= this.maxOutputIndex; i++) {
|
|
|
|
itemsCount += this.getDataCount(option - 1, i);
|
|
|
|
}
|
2022-05-23 08:56:15 -07:00
|
|
|
const items = this.$locale.baseText('ndv.output.items', {adjustToNumber: itemsCount});
|
2022-04-11 06:12:13 -07:00
|
|
|
const itemsLabel = itemsCount > 0 ? ` (${itemsCount} ${items})` : '';
|
|
|
|
return option + this.$locale.baseText('ndv.output.of') + (this.maxRunIndex+1) + itemsLabel;
|
|
|
|
},
|
|
|
|
getDataCount(runIndex: number, outputIndex: number) {
|
2022-10-12 07:13:59 -07:00
|
|
|
if (this.pinData) {
|
|
|
|
return this.pinData.length;
|
|
|
|
}
|
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
if (this.node === null) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const runData: IRunData | null = this.workflowRunData;
|
|
|
|
|
|
|
|
if (runData === null || !runData.hasOwnProperty(this.node.name)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (runData[this.node.name].length <= runIndex) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (runData[this.node.name][runIndex].hasOwnProperty('error')) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!runData[this.node.name][runIndex].hasOwnProperty('data') ||
|
|
|
|
runData[this.node.name][runIndex].data === undefined
|
|
|
|
) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const inputData = this.getMainInputData(runData[this.node.name][runIndex].data!, outputIndex);
|
|
|
|
|
|
|
|
return inputData.length;
|
|
|
|
},
|
2021-09-15 01:11:59 -07:00
|
|
|
init() {
|
|
|
|
// Reset the selected output index every time another node gets selected
|
|
|
|
this.outputIndex = 0;
|
|
|
|
this.refreshDataSize();
|
2022-05-23 08:56:15 -07:00
|
|
|
this.closeBinaryDataDisplay();
|
|
|
|
if (this.binaryData.length > 0) {
|
2022-11-04 06:04:31 -07:00
|
|
|
this.ndvStore.setPanelDisplayMode({pane: this.paneType as "input" | "output", mode: 'binary'});
|
2022-05-23 08:56:15 -07:00
|
|
|
}
|
|
|
|
else if (this.displayMode === 'binary') {
|
2022-11-04 06:04:31 -07:00
|
|
|
this.ndvStore.setPanelDisplayMode({pane: this.paneType as "input" | "output", mode: 'table'});
|
2021-09-15 01:11:59 -07:00
|
|
|
}
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
closeBinaryDataDisplay () {
|
|
|
|
this.binaryDataDisplayVisible = false;
|
|
|
|
this.binaryDataDisplayData = null;
|
|
|
|
},
|
|
|
|
clearExecutionData () {
|
2022-11-04 06:04:31 -07:00
|
|
|
this.workflowsStore.setWorkflowExecutionData(null);
|
2019-06-23 03:35:23 -07:00
|
|
|
this.updateNodesExecutionIssues();
|
|
|
|
},
|
2022-11-24 07:54:43 -08:00
|
|
|
isViewable (index: number, key: string): boolean {
|
2022-12-11 05:10:54 -08:00
|
|
|
const { fileType } = this.binaryData[index][key];
|
|
|
|
return !!fileType && ['image', 'video', 'text', 'json'].includes(fileType);
|
2022-11-24 07:54:43 -08:00
|
|
|
},
|
2022-03-28 03:59:53 -07:00
|
|
|
isDownloadable (index: number, key: string): boolean {
|
2022-12-11 05:10:54 -08:00
|
|
|
const { mimeType, fileName } = this.binaryData[index][key];
|
2022-11-24 07:54:43 -08:00
|
|
|
return !!(mimeType && fileName);
|
2022-03-28 03:59:53 -07:00
|
|
|
},
|
|
|
|
async downloadBinaryData (index: number, key: string) {
|
2022-12-11 05:10:54 -08:00
|
|
|
const { id, data, fileName, fileExtension, mimeType } = this.binaryData[index][key];
|
2022-03-28 03:59:53 -07:00
|
|
|
|
2022-11-24 07:54:43 -08:00
|
|
|
if(id) {
|
|
|
|
const url = this.restApi().getBinaryUrl(id);
|
|
|
|
saveAs(url, [fileName, fileExtension].join('.'));
|
|
|
|
return;
|
2022-03-28 03:59:53 -07:00
|
|
|
} else {
|
2022-11-24 07:54:43 -08:00
|
|
|
const bufferString = 'data:' + mimeType + ';base64,' + data;
|
|
|
|
const blob = await fetch(bufferString).then(d => d.blob());
|
|
|
|
saveAs(blob, fileName);
|
2022-03-28 03:59:53 -07:00
|
|
|
}
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
displayBinaryData (index: number, key: string) {
|
|
|
|
this.binaryDataDisplayVisible = true;
|
|
|
|
|
|
|
|
this.binaryDataDisplayData = {
|
|
|
|
node: this.node!.name,
|
|
|
|
runIndex: this.runIndex,
|
2022-05-23 08:56:15 -07:00
|
|
|
outputIndex: this.currentOutputIndex,
|
2019-06-23 03:35:23 -07:00
|
|
|
index,
|
|
|
|
key,
|
|
|
|
};
|
|
|
|
},
|
2020-12-18 09:55:53 -08:00
|
|
|
getOutputName (outputIndex: number) {
|
|
|
|
if (this.node === null) {
|
|
|
|
return outputIndex + 1;
|
|
|
|
}
|
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
const nodeType = this.nodeType;
|
2021-11-19 01:17:13 -08:00
|
|
|
if (!nodeType || !nodeType.outputNames || nodeType.outputNames.length <= outputIndex) {
|
2020-12-18 09:55:53 -08:00
|
|
|
return outputIndex + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nodeType.outputNames[outputIndex];
|
|
|
|
},
|
2019-10-04 04:27:50 -07:00
|
|
|
refreshDataSize () {
|
2019-10-02 06:30:51 -07:00
|
|
|
// Hide by default the data from being displayed
|
|
|
|
this.showData = false;
|
|
|
|
|
|
|
|
// Check how much data there is to display
|
2022-05-23 08:56:15 -07:00
|
|
|
const inputData = this.getNodeInputData(this.node, this.runIndex, this.currentOutputIndex);
|
2020-05-23 14:54:56 -07:00
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
const offset = this.pageSize * (this.currentPage - 1);
|
|
|
|
const jsonItems = inputData.slice(offset, offset + this.pageSize).map(item => item.json);
|
2020-05-23 14:54:56 -07:00
|
|
|
|
|
|
|
this.dataSize = JSON.stringify(jsonItems).length;
|
2019-10-04 04:27:50 -07:00
|
|
|
|
2020-05-23 15:53:06 -07:00
|
|
|
if (this.dataSize < this.MAX_DISPLAY_DATA_SIZE) {
|
2019-10-04 04:27:50 -07:00
|
|
|
// Data is reasonable small (< 200kb) so display it directly
|
2019-10-02 06:30:51 -07:00
|
|
|
this.showData = true;
|
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
2022-05-23 08:56:15 -07:00
|
|
|
onRunIndexChange(run: number) {
|
|
|
|
this.$emit('runChange', run);
|
|
|
|
},
|
2022-07-20 08:50:39 -07:00
|
|
|
enableNode() {
|
|
|
|
if (this.node) {
|
|
|
|
const updateInformation = {
|
|
|
|
name: this.node.name,
|
|
|
|
properties: {
|
|
|
|
disabled: !this.node.disabled,
|
2022-11-04 06:04:31 -07:00
|
|
|
} as IDataObject,
|
|
|
|
} as INodeUpdatePropertiesInformation;
|
2022-07-20 08:50:39 -07:00
|
|
|
|
2022-11-04 06:04:31 -07:00
|
|
|
this.workflowsStore.updateNodeProperties(updateInformation);
|
2022-07-20 08:50:39 -07:00
|
|
|
}
|
|
|
|
},
|
2022-09-19 03:26:02 -07:00
|
|
|
goToErroredNode() {
|
|
|
|
if (this.node) {
|
2022-11-04 06:04:31 -07:00
|
|
|
this.ndvStore.activeNodeName = this.node.name;
|
2022-09-19 03:26:02 -07:00
|
|
|
}
|
|
|
|
},
|
2019-10-04 04:27:50 -07:00
|
|
|
},
|
|
|
|
watch: {
|
2021-09-15 01:11:59 -07:00
|
|
|
node() {
|
|
|
|
this.init();
|
2019-10-04 04:27:50 -07:00
|
|
|
},
|
2022-10-06 06:03:55 -07:00
|
|
|
inputData:{
|
|
|
|
handler(data: INodeExecutionData[]) {
|
|
|
|
if(this.paneType && data){
|
2022-11-04 06:04:31 -07:00
|
|
|
this.ndvStore.setNDVPanelDataIsEmpty({ panel: this.paneType as "input" | "output", isEmpty: data.every(item => isEmpty(item.json)) });
|
2022-10-06 06:03:55 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
immediate: true,
|
|
|
|
deep: true,
|
|
|
|
},
|
2022-07-20 08:50:39 -07:00
|
|
|
jsonData (value: IDataObject[]) {
|
2019-10-04 04:27:50 -07:00
|
|
|
this.refreshDataSize();
|
2022-07-20 08:50:39 -07:00
|
|
|
|
|
|
|
const hasSeenPinDataTooltip = localStorage.getItem(LOCAL_STORAGE_PIN_DATA_DISCOVERY_NDV_FLAG);
|
|
|
|
if (!hasSeenPinDataTooltip) {
|
|
|
|
this.showPinDataDiscoveryTooltip(value);
|
|
|
|
}
|
2019-10-04 04:27:50 -07:00
|
|
|
},
|
2022-05-23 08:56:15 -07:00
|
|
|
binaryData (newData: IBinaryKeyData[], prevData: IBinaryKeyData[]) {
|
|
|
|
if (newData.length && !prevData.length && this.displayMode !== 'binary') {
|
|
|
|
this.switchToBinary();
|
|
|
|
}
|
|
|
|
else if (!newData.length && this.displayMode === 'binary') {
|
|
|
|
this.onDisplayModeChange('table');
|
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
2022-10-12 05:06:28 -07:00
|
|
|
currentOutputIndex(branchIndex: number) {
|
2022-11-04 06:04:31 -07:00
|
|
|
this.ndvStore.setNDVBranchIndex({
|
|
|
|
pane: this.paneType as "input" | "output",
|
2022-10-12 05:06:28 -07:00
|
|
|
branchIndex,
|
|
|
|
});
|
|
|
|
},
|
2019-06-23 03:35:23 -07:00
|
|
|
},
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
<style lang="scss" module>
|
|
|
|
.infoIcon {
|
|
|
|
color: var(--color-foreground-dark);
|
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
.center {
|
|
|
|
display: flex;
|
|
|
|
height: 100%;
|
|
|
|
flex-direction: column;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
2022-05-23 08:56:15 -07:00
|
|
|
padding: var(--spacing-s) var(--spacing-s) var(--spacing-xl) var(--spacing-s);
|
2022-04-11 06:12:13 -07:00
|
|
|
text-align: center;
|
|
|
|
|
|
|
|
> * {
|
|
|
|
max-width: 316px;
|
|
|
|
margin-bottom: var(--spacing-2xs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.container {
|
2019-06-23 03:35:23 -07:00
|
|
|
position: relative;
|
2021-09-11 01:15:36 -07:00
|
|
|
width: 100%;
|
2019-06-23 03:35:23 -07:00
|
|
|
height: 100%;
|
2022-05-23 08:56:15 -07:00
|
|
|
background-color: var(--color-background-base);
|
2022-04-11 06:12:13 -07:00
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2022-07-20 08:50:39 -07:00
|
|
|
.pinned-data-callout {
|
|
|
|
border-radius: inherit;
|
|
|
|
border-bottom-right-radius: 0;
|
|
|
|
border-top: 0;
|
|
|
|
border-left: 0;
|
|
|
|
border-right: 0;
|
|
|
|
}
|
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
.header {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
margin-bottom: var(--spacing-s);
|
|
|
|
padding: var(--spacing-s) var(--spacing-s) 0 var(--spacing-s);
|
|
|
|
position: relative;
|
2022-09-26 03:02:14 -07:00
|
|
|
overflow-x: auto;
|
|
|
|
overflow-y: hidden;
|
|
|
|
min-height: calc(30px + var(--spacing-s));
|
2022-03-28 03:59:53 -07:00
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
> *:first-child {
|
|
|
|
flex-grow: 1;
|
|
|
|
}
|
|
|
|
}
|
2019-06-23 03:35:23 -07:00
|
|
|
|
2022-07-20 08:50:39 -07:00
|
|
|
.data-container {
|
2022-04-11 06:12:13 -07:00
|
|
|
position: relative;
|
|
|
|
height: 100%;
|
2022-07-20 08:50:39 -07:00
|
|
|
|
2022-10-06 06:03:55 -07:00
|
|
|
&:hover{
|
2022-07-20 08:50:39 -07:00
|
|
|
.actions-group {
|
|
|
|
opacity: 1;
|
|
|
|
}
|
|
|
|
}
|
2022-04-11 06:12:13 -07:00
|
|
|
}
|
|
|
|
|
2022-10-10 01:00:54 -07:00
|
|
|
.dataDisplay {
|
2022-04-11 06:12:13 -07:00
|
|
|
position: absolute;
|
|
|
|
top: 0;
|
|
|
|
left: 0;
|
2022-10-06 06:03:55 -07:00
|
|
|
padding: 0 var(--spacing-s) var(--spacing-3xl) var(--spacing-s);
|
2022-04-11 06:12:13 -07:00
|
|
|
right: 0;
|
|
|
|
overflow-y: auto;
|
|
|
|
line-height: 1.5;
|
|
|
|
word-break: normal;
|
|
|
|
height: 100%;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tabs {
|
|
|
|
margin-bottom: var(--spacing-s);
|
|
|
|
}
|
|
|
|
|
|
|
|
.itemsCount {
|
|
|
|
margin-left: var(--spacing-s);
|
|
|
|
margin-bottom: var(--spacing-s);
|
|
|
|
}
|
|
|
|
|
|
|
|
.runSelector {
|
2022-07-20 08:50:39 -07:00
|
|
|
max-width: 210px;
|
2022-04-11 06:12:13 -07:00
|
|
|
margin-left: var(--spacing-s);
|
|
|
|
margin-bottom: var(--spacing-s);
|
2022-05-23 08:56:15 -07:00
|
|
|
display: flex;
|
2022-07-26 00:55:27 -07:00
|
|
|
align-items: center;
|
2022-05-23 08:56:15 -07:00
|
|
|
|
|
|
|
> * {
|
|
|
|
margin-right: var(--spacing-4xs);
|
|
|
|
}
|
2022-04-11 06:12:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
.pagination {
|
|
|
|
width: 100%;
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
|
|
|
bottom: 0;
|
|
|
|
padding: 5px;
|
2022-09-26 03:02:14 -07:00
|
|
|
overflow: auto;
|
|
|
|
}
|
|
|
|
|
|
|
|
.pageSizeSelector {
|
|
|
|
text-transform: capitalize;
|
|
|
|
max-width: 150px;
|
|
|
|
flex: 0 1 auto;
|
2022-04-11 06:12:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
.binaryIndex {
|
|
|
|
display: block;
|
|
|
|
padding: var(--spacing-2xs);
|
|
|
|
font-size: var(--font-size-2xs);
|
|
|
|
|
|
|
|
> * {
|
|
|
|
display: inline-block;
|
|
|
|
width: 30px;
|
|
|
|
height: 30px;
|
|
|
|
line-height: 30px;
|
|
|
|
border-radius: var(--border-radius-base);
|
|
|
|
text-align: center;
|
|
|
|
background-color: var(--color-foreground-xdark);
|
|
|
|
font-weight: var(--font-weight-bold);
|
|
|
|
color: var(--color-text-xlight);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.binaryRow {
|
|
|
|
display: inline-flex;
|
|
|
|
font-size: var(--font-size-2xs);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.binaryCell {
|
|
|
|
display: inline-block;
|
|
|
|
width: 300px;
|
|
|
|
overflow: hidden;
|
2022-07-26 03:45:55 -07:00
|
|
|
background-color: var(--color-foreground-xlight);
|
2022-04-11 06:12:13 -07:00
|
|
|
margin-right: var(--spacing-s);
|
|
|
|
margin-bottom: var(--spacing-s);
|
|
|
|
border-radius: var(--border-radius-base);
|
|
|
|
border: var(--border-base);
|
|
|
|
padding: var(--spacing-s);
|
|
|
|
}
|
|
|
|
|
|
|
|
.binaryHeader {
|
2022-09-23 07:14:28 -07:00
|
|
|
color: $color-primary;
|
2022-04-11 06:12:13 -07:00
|
|
|
font-weight: 600;
|
|
|
|
font-size: 1.2em;
|
|
|
|
padding-bottom: 0.5em;
|
|
|
|
margin-bottom: 0.5em;
|
2022-07-26 03:45:55 -07:00
|
|
|
border-bottom: 1px solid var(--color-text-light);
|
2022-04-11 06:12:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
.binaryButtonContainer {
|
|
|
|
margin-top: 1.5em;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: row;
|
|
|
|
justify-content: center;
|
|
|
|
|
|
|
|
> * {
|
|
|
|
flex-grow: 0;
|
|
|
|
margin-right: var(--spacing-3xs);
|
2019-06-23 03:35:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
.binaryValue {
|
|
|
|
white-space: initial;
|
|
|
|
word-wrap: break-word;
|
|
|
|
}
|
|
|
|
|
|
|
|
.displayModes {
|
2022-05-23 08:56:15 -07:00
|
|
|
display: flex;
|
|
|
|
justify-content: flex-end;
|
|
|
|
flex-grow: 1;
|
|
|
|
}
|
|
|
|
|
2022-07-20 08:50:39 -07:00
|
|
|
.tooltip-container {
|
|
|
|
max-width: 240px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.pin-data-button {
|
|
|
|
svg {
|
|
|
|
transition: transform 0.3s ease;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-23 08:56:15 -07:00
|
|
|
.spinner {
|
|
|
|
* {
|
|
|
|
color: var(--color-primary);
|
|
|
|
min-height: 40px;
|
|
|
|
min-width: 40px;
|
|
|
|
}
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
margin-bottom: var(--spacing-s);
|
2022-04-11 06:12:13 -07:00
|
|
|
}
|
|
|
|
|
2022-07-20 08:50:39 -07:00
|
|
|
.edit-mode {
|
|
|
|
height: calc(100% - var(--spacing-s));
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
justify-content: flex-end;
|
|
|
|
align-items: flex-end;
|
|
|
|
padding-left: var(--spacing-s);
|
|
|
|
padding-right: var(--spacing-s);
|
|
|
|
}
|
|
|
|
|
|
|
|
.edit-mode-body {
|
|
|
|
flex: 1 1 auto;
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
overflow: hidden;
|
|
|
|
}
|
|
|
|
|
|
|
|
.edit-mode-footer {
|
|
|
|
display: flex;
|
|
|
|
width: 100%;
|
|
|
|
justify-content: space-between;
|
|
|
|
align-items: center;
|
|
|
|
padding-top: var(--spacing-s);
|
|
|
|
}
|
|
|
|
|
|
|
|
.edit-mode-footer-infotip {
|
|
|
|
display: flex;
|
|
|
|
flex: 1;
|
|
|
|
width: 100%;
|
|
|
|
}
|
|
|
|
|
|
|
|
.edit-mode-actions {
|
|
|
|
display: flex;
|
|
|
|
justify-content: flex-end;
|
|
|
|
align-items: center;
|
|
|
|
margin-left: var(--spacing-s);
|
|
|
|
}
|
2022-09-19 03:26:02 -07:00
|
|
|
|
|
|
|
.stretchVertically {
|
|
|
|
height: 100%;
|
|
|
|
}
|
|
|
|
|
2022-10-31 10:59:53 -07:00
|
|
|
.uiBlocker {
|
|
|
|
border-top-left-radius: 0;
|
|
|
|
border-bottom-left-radius: 0;
|
|
|
|
}
|
|
|
|
|
2022-04-11 06:12:13 -07:00
|
|
|
</style>
|