-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16657 from ElectronicBlueberry/workflow-embed
Workflow Embed
- Loading branch information
Showing
28 changed files
with
1,301 additions
and
724 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
<script setup lang="ts"> | ||
import { library } from "@fortawesome/fontawesome-svg-core"; | ||
import { faCheck, faCopy, faEdit } from "@fortawesome/free-solid-svg-icons"; | ||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; | ||
import { BButton } from "bootstrap-vue"; | ||
import { computed, nextTick, ref } from "vue"; | ||
import { copy } from "@/utils/clipboard"; | ||
import SlugInput from "./SlugInput.vue"; | ||
library.add(faCopy, faEdit, faCheck); | ||
const props = defineProps<{ | ||
prefix: string; | ||
slug: string; | ||
}>(); | ||
const emit = defineEmits<{ | ||
(e: "change", value: string): void; | ||
(e: "submit", value: string): void; | ||
}>(); | ||
const editing = ref(false); | ||
const slugInput = ref<InstanceType<typeof SlugInput>>(); | ||
const url = computed(() => props.prefix + props.slug); | ||
async function onEdit() { | ||
editing.value = true; | ||
await nextTick(); | ||
(slugInput.value?.$el as HTMLInputElement).focus(); | ||
} | ||
function onChange(value: string) { | ||
emit("change", value); | ||
} | ||
function onSubmit() { | ||
editing.value = false; | ||
emit("submit", props.slug); | ||
} | ||
const copied = ref(false); | ||
const clipboardTitle = computed(() => (copied.value ? "Copied!" : "Copy URL")); | ||
function onCopy() { | ||
copy(url.value); | ||
copied.value = true; | ||
} | ||
function onCopyOut() { | ||
copied.value = false; | ||
} | ||
</script> | ||
|
||
<template> | ||
<div class="editable-url"> | ||
url: | ||
<a v-if="!editing" id="item-url" :href="url" target="_top">{{ url }}</a> | ||
<span v-else id="item-url-text"> | ||
{{ prefix }} | ||
<SlugInput | ||
ref="slugInput" | ||
class="ml-1" | ||
:slug="props.slug" | ||
@change="onChange" | ||
@cancel="onChange" | ||
@keyup.enter="onSubmit" /> | ||
</span> | ||
|
||
<BButton | ||
v-if="!editing" | ||
v-b-tooltip.hover | ||
class="inline-icon-button" | ||
title="Edit URL" | ||
size="md" | ||
@click="onEdit"> | ||
<FontAwesomeIcon icon="edit" fixed-width /> | ||
</BButton> | ||
<BButton v-else v-b-tooltip.hover class="inline-icon-button" title="Done" size="md" @click="onSubmit"> | ||
<FontAwesomeIcon icon="check" fixed-width /> | ||
</BButton> | ||
|
||
<BButton | ||
id="tooltip-clipboard" | ||
v-b-tooltip.hover | ||
size="md" | ||
class="inline-icon-button" | ||
:title="clipboardTitle" | ||
@click="onCopy" | ||
@mouseout="onCopyOut" | ||
@blur="onCopyOut"> | ||
<FontAwesomeIcon :icon="['far', 'copy']" fixed-width /> | ||
</BButton> | ||
</div> | ||
</template> | ||
|
||
<style scoped lang="scss"> | ||
.editable-url { | ||
height: 1.5rem; | ||
display: flex; | ||
align-items: center; | ||
gap: 0.25rem; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
<script setup lang="ts"> | ||
import { library } from "@fortawesome/fontawesome-svg-core"; | ||
import { faCopy } from "@fortawesome/free-solid-svg-icons"; | ||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; | ||
import { useDebounce } from "@vueuse/core"; | ||
import { BButton, BFormCheckbox, BFormInput, BInputGroup, BInputGroupAppend } from "bootstrap-vue"; | ||
import { computed, reactive, ref } from "vue"; | ||
import { getAppRoot } from "@/onload/loadConfig"; | ||
import { copy } from "@/utils/clipboard"; | ||
import ZoomControl from "@/components/Workflow/Editor/ZoomControl.vue"; | ||
import WorkflowPublished from "@/components/Workflow/Published/WorkflowPublished.vue"; | ||
library.add(faCopy); | ||
const props = defineProps<{ | ||
id: string; | ||
}>(); | ||
const settings = reactive({ | ||
buttons: true, | ||
about: false, | ||
heading: false, | ||
minimap: true, | ||
zoomControls: true, | ||
initialX: -20, | ||
initialY: -20, | ||
zoom: 1, | ||
}); | ||
function onChangePosition(event: Event, xy: "x" | "y") { | ||
const value = parseInt((event.target as HTMLInputElement).value); | ||
if (xy === "x") { | ||
settings.initialX = value; | ||
} else { | ||
settings.initialY = value; | ||
} | ||
} | ||
const root = computed(() => { | ||
const port = window.location.port ? `:${window.location.port}` : ""; | ||
return `${window.location.protocol}//${window.location.hostname}${port}${getAppRoot()}`; | ||
}); | ||
const embedUrl = computed(() => { | ||
let url = `${root.value}published/workflow?id=${props.id}&embed=true`; | ||
url += `&buttons=${settings.buttons}`; | ||
url += `&about=${settings.about}`; | ||
url += `&heading=${settings.heading}`; | ||
url += `&minimap=${settings.minimap}`; | ||
url += `&zoom_controls=${settings.zoomControls}`; | ||
url += `&initialX=${settings.initialX}&initialY=${settings.initialY}`; | ||
url += `&zoom=${settings.zoom}`; | ||
return url; | ||
}); | ||
const embed = computed(() => `<iframe title="Galaxy Workflow Embed" src="${embedUrl.value}" />`); | ||
// These Embed settings are not reactive, to we have to key them | ||
const embedKey = computed(() => `zoom: ${settings.zoom}, x: ${settings.initialX}, y: ${settings.initialY}`); | ||
const debouncedEmbedKey = useDebounce(embedKey, 500); | ||
const showEmbed = ref(false); | ||
const showEmbedDebounced = useDebounce(showEmbed, 100); | ||
const copied = ref(false); | ||
function onCopy() { | ||
copy(embed.value); | ||
copied.value = true; | ||
} | ||
function onCopyOut() { | ||
copied.value = false; | ||
} | ||
const clipboardTitle = computed(() => (copied.value ? "Copied!" : "Copy URL")); | ||
</script> | ||
|
||
<template> | ||
<div class="workflow-embed"> | ||
<div class="settings"> | ||
<BFormCheckbox v-model="settings.heading"> Show heading </BFormCheckbox> | ||
<BFormCheckbox v-model="settings.about"> Show about </BFormCheckbox> | ||
<BFormCheckbox v-model="settings.buttons"> Show buttons </BFormCheckbox> | ||
<BFormCheckbox v-model="settings.minimap"> Show minimap </BFormCheckbox> | ||
<BFormCheckbox v-model="settings.zoomControls"> Show zoom controls </BFormCheckbox> | ||
|
||
<label for="embed-position-control" class="control-label"> | ||
Initial position | ||
<div id="embed-position-control" class="position-control"> | ||
<label> | ||
x: | ||
<input | ||
:value="settings.initialX" | ||
type="number" | ||
@change="(event) => onChangePosition(event, 'x')" /> | ||
</label> | ||
|
||
<label> | ||
y: | ||
<input | ||
:value="settings.initialY" | ||
type="number" | ||
@change="(event) => onChangePosition(event, 'y')" /> | ||
</label> | ||
</div> | ||
</label> | ||
|
||
<label for="embed-zoom-control" class="control-label"> | ||
Initial zoom | ||
<ZoomControl | ||
id="embed-zoom-control" | ||
:zoom-level="settings.zoom" | ||
class="zoom-control" | ||
@onZoom="(level) => (settings.zoom = level)" /> | ||
</label> | ||
</div> | ||
<div class="preview"> | ||
<label for="embed-code" class="w-100"> | ||
Embed code | ||
<BInputGroup id="embed-code"> | ||
<BFormInput class="embed-code-input" :value="embed" readonly /> | ||
<BInputGroupAppend> | ||
<BButton | ||
v-b-tooltip.hover | ||
:title="clipboardTitle" | ||
variant="primary" | ||
@click="onCopy" | ||
@blur="onCopyOut"> | ||
<FontAwesomeIcon icon="copy" /> | ||
</BButton> | ||
</BInputGroupAppend> | ||
</BInputGroup> | ||
</label> | ||
|
||
<BFormCheckbox v-model="showEmbed" switch>Show embed Preview</BFormCheckbox> | ||
<WorkflowPublished | ||
v-if="showEmbedDebounced" | ||
:id="props.id" | ||
:key="debouncedEmbedKey" | ||
class="published-preview" | ||
:zoom="settings.zoom" | ||
embed | ||
:show-about="settings.about" | ||
:show-buttons="settings.buttons" | ||
:show-heading="settings.heading" | ||
:show-minimap="settings.minimap" | ||
:show-zoom-controls="settings.zoomControls" | ||
:initial-x="settings.initialX" | ||
:initial-y="settings.initialY" /> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<style scoped lang="scss"> | ||
@import "theme/blue.scss"; | ||
.workflow-embed { | ||
display: flex; | ||
gap: 0.5rem; | ||
} | ||
@container (max-width: 1200px) { | ||
.workflow-embed { | ||
flex-direction: column; | ||
} | ||
} | ||
.workflow-embed { | ||
.settings { | ||
flex: 1; | ||
display: flex; | ||
align-items: start; | ||
justify-content: start; | ||
flex-direction: column; | ||
} | ||
.preview { | ||
flex: 1; | ||
.published-preview { | ||
border: 2px solid $brand-primary; | ||
border-radius: 4px; | ||
width: 100%; | ||
height: 550px; | ||
} | ||
.embed-code-input { | ||
background-color: transparent; | ||
} | ||
} | ||
} | ||
.control-label { | ||
display: flex; | ||
flex-direction: column; | ||
margin-top: 0.25rem; | ||
} | ||
.zoom-control, | ||
.position-control { | ||
position: unset; | ||
padding: 2px 4px; | ||
border-color: $brand-primary; | ||
border-radius: 4px; | ||
border-width: 1px; | ||
border-style: solid; | ||
} | ||
.position-control { | ||
display: flex; | ||
flex-direction: column; | ||
width: 9rem; | ||
input { | ||
width: 100%; | ||
} | ||
label { | ||
margin-bottom: 0; | ||
display: flex; | ||
gap: 0.25rem; | ||
align-items: center; | ||
} | ||
} | ||
</style> |
Oops, something went wrong.