Skip to content

Commit bf0357f

Browse files
authored
Merge pull request #6310 from FlowFuse/expert-api-v4-and-flow-examples-ui
Expert api v4 and flow examples UI
2 parents 81dc1e1 + c46bbfe commit bf0357f

File tree

16 files changed

+1164
-308
lines changed

16 files changed

+1164
-308
lines changed

frontend/src/components/ExpertButton.vue

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div class="expert-button-wrapper flex items-center justify-center h-full px-3" style="height: 60px;">
2+
<div v-if="!isExpertDrawerOpen" class="expert-button-wrapper flex items-center justify-center h-full px-3" style="height: 60px;">
33
<button
44
class="expert-button flex items-center gap-1.5 justify-center rounded-md px-[9px] py-[6px] font-bold text-[0.85rem] leading-[20px] text-gray-800 whitespace-nowrap transition-colors"
55
data-el="expert-button"
@@ -19,7 +19,10 @@ export default {
1919
name: 'ExpertButton',
2020
components: {},
2121
computed: {
22-
...mapState('ux/drawers', ['rightDrawer'])
22+
...mapState('ux/drawers', ['rightDrawer']),
23+
isExpertDrawerOpen () {
24+
return this.rightDrawer.state && this.rightDrawer.component?.name === 'ExpertDrawer'
25+
}
2326
},
2427
methods: {
2528
...mapActions('ux/drawers', ['closeRightDrawer']),
@@ -45,8 +48,8 @@ export default {
4548
animation: gradient-border-rotate 4s linear infinite;
4649
4750
&:hover {
48-
background: linear-gradient(#f9fafb, #f9fafb) padding-box,
49-
linear-gradient(135deg, $ff-red-700, #4038d5, $ff-red-700) border-box;
51+
border: 2px solid transparent;
52+
margin: -1px;
5053
}
5154
}
5255

frontend/src/components/TextCopier.vue

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,21 @@
55
<span class="text">{{ text }}</span>
66
</slot>
77
</span>
8-
<DuplicateIcon v-if="text.length" class="ff-icon" @click="copyPath" @click.prevent.stop />
9-
<span ref="copied" class="ff-copied" :class="{ 'ff-copied-left': promptPosition === 'left'}">Copied!</span>
8+
<button v-if="text.length" class="ff-icon-button" @click="copyPath">
9+
<DuplicateIcon v-if="!copied" class="ff-icon" />
10+
<CheckIcon v-else class="ff-icon ff-icon-check" />
11+
</button>
1012
</span>
1113
</template>
1214

1315
<script>
14-
import { DuplicateIcon } from '@heroicons/vue/outline'
16+
import { CheckIcon, DuplicateIcon } from '@heroicons/vue/outline'
1517
1618
import Alert from '../services/alerts.js'
1719
1820
export default {
1921
name: 'TextCopier',
20-
components: { DuplicateIcon },
22+
components: { DuplicateIcon, CheckIcon },
2123
props: {
2224
text: {
2325
required: true,
@@ -46,20 +48,42 @@ export default {
4648
}
4749
},
4850
emits: ['copied'],
51+
data () {
52+
return {
53+
copied: false
54+
}
55+
},
4956
methods: {
50-
copyPath () {
51-
navigator.clipboard.writeText(this.text)
57+
async copyPath () {
58+
try {
59+
// Try modern clipboard API first
60+
if (navigator.clipboard && navigator.clipboard.writeText) {
61+
await navigator.clipboard.writeText(this.text)
62+
} else {
63+
// Fallback for non-secure contexts (HTTP, not HTTPS)
64+
const textArea = document.createElement('textarea')
65+
textArea.value = this.text
66+
textArea.style.position = 'fixed'
67+
textArea.style.left = '-999999px'
68+
textArea.style.top = '-999999px'
69+
document.body.appendChild(textArea)
70+
textArea.focus()
71+
textArea.select()
72+
document.execCommand('copy')
73+
document.body.removeChild(textArea)
74+
}
5275
53-
if (this.confirmationType === 'alert') {
54-
Alert.emit('Copied to Clipboard', 'confirmation')
55-
} else {
56-
// show "Copied" notification
57-
this.$refs.copied.style.display = 'inline'
58-
// hide after 500ms
59-
setTimeout(() => {
60-
this.$refs.copied.style.display = 'none'
61-
}, 500)
62-
this.$emit('copied')
76+
if (this.confirmationType === 'alert') {
77+
Alert.emit('Copied to Clipboard', 'confirmation')
78+
} else {
79+
this.copied = true
80+
setTimeout(() => { this.copied = false }, 2000)
81+
this.$emit('copied')
82+
}
83+
} catch (err) {
84+
console.error('Failed to copy to clipboard:', err)
85+
// Don't show alert to avoid inject() error
86+
// Just keep the icon in copy state (don't show checkmark)
6387
}
6488
}
6589
}
@@ -75,6 +99,35 @@ export default {
7599
&:hover {
76100
cursor: pointer;
77101
}
102+
.ff-icon-button {
103+
display: inline-flex;
104+
align-items: center;
105+
justify-content: center;
106+
padding: 4px;
107+
border: none;
108+
background: transparent;
109+
border-radius: 4px;
110+
cursor: pointer;
111+
transition: all 0.2s ease;
112+
color: $ff-grey-600;
113+
114+
&:hover {
115+
color: $ff-indigo-600;
116+
background-color: $ff-indigo-50;
117+
}
118+
119+
&:active {
120+
background-color: $ff-indigo-100;
121+
}
122+
123+
.ff-icon {
124+
pointer-events: none;
125+
}
126+
127+
.ff-icon-check {
128+
color: $ff-green-600;
129+
}
130+
}
78131
.ff-copied {
79132
background-color: black;
80133
color: white;

0 commit comments

Comments
 (0)