Extract
This commit is contained in:
parent
97706e5109
commit
ab6907691f
|
@ -7,7 +7,8 @@
|
|||
import { BindingMode } from "@budibase/types"
|
||||
import { EditorModes } from "../CodeEditor"
|
||||
import CodeEditor from "../CodeEditor/CodeEditor.svelte"
|
||||
import SnippetDrawer from "./SnippetDrawer.svelte"
|
||||
import SnippetsSidePanelHeader from "./snippets/SidePanelHeader.svelte"
|
||||
import SnippetsSidePanelContent from "./snippets/SidePanelContent.svelte"
|
||||
|
||||
export let addHelper: (_helper: Helper, _js?: boolean) => void
|
||||
export let addBinding: (_binding: EnrichedBinding) => void
|
||||
|
@ -24,15 +25,13 @@
|
|||
let popover: Popover
|
||||
let popoverAnchor: HTMLElement | null
|
||||
let hoverTarget: {
|
||||
type: "binding" | "helper" | "snippet"
|
||||
type: "binding" | "helper"
|
||||
code: string
|
||||
description?: string
|
||||
} | null
|
||||
let helpers = handlebarsCompletions()
|
||||
let selectedCategory: string | null
|
||||
let hideTimeout: ReturnType<typeof setTimeout> | null
|
||||
let snippetDrawer: SnippetDrawer
|
||||
let editableSnippet: Snippet | null
|
||||
|
||||
$: bindingIcons = bindings?.reduce<Record<string, string>>((acc, ele) => {
|
||||
if (ele.icon) {
|
||||
|
@ -47,13 +46,10 @@
|
|||
} as Record<string, string>
|
||||
$: categories = Object.entries(groupBy("category", bindings))
|
||||
|
||||
$: filteredSnippets = getFilteredSnippets(
|
||||
mode,
|
||||
allowSnippets,
|
||||
snippets || [],
|
||||
search
|
||||
$: categoryNames = getCategoryNames(
|
||||
categories,
|
||||
allowSnippets && mode === BindingMode.JavaScript
|
||||
)
|
||||
$: categoryNames = getCategoryNames(categories, filteredSnippets.length > 0)
|
||||
$: searchRgx = new RegExp(search, "ig")
|
||||
$: filteredCategories = categories
|
||||
.map(([name, categoryBindings]) => ({
|
||||
|
@ -82,26 +78,6 @@
|
|||
}
|
||||
$: onModeChange(mode)
|
||||
|
||||
const getFilteredSnippets = (
|
||||
mode: BindingMode,
|
||||
enableSnippets: boolean,
|
||||
snippets: Snippet[],
|
||||
search: string
|
||||
) => {
|
||||
if (mode !== BindingMode.JavaScript) {
|
||||
return []
|
||||
}
|
||||
if (!enableSnippets || !snippets.length) {
|
||||
return []
|
||||
}
|
||||
if (!search?.length) {
|
||||
return snippets
|
||||
}
|
||||
return snippets.filter(snippet =>
|
||||
snippet.name.toLowerCase().includes(search.toLowerCase())
|
||||
)
|
||||
}
|
||||
|
||||
const getHelperExample = (helper: Helper, js: boolean) => {
|
||||
let example = helper.example || ""
|
||||
if (js) {
|
||||
|
@ -157,19 +133,6 @@
|
|||
popover.show()
|
||||
}
|
||||
|
||||
const showSnippetPopover = (snippet: Snippet, target: HTMLElement) => {
|
||||
stopHidingPopover()
|
||||
if (!snippet.code) {
|
||||
return
|
||||
}
|
||||
popoverAnchor = target
|
||||
hoverTarget = {
|
||||
type: "snippet",
|
||||
code: snippet.code,
|
||||
}
|
||||
popover.show()
|
||||
}
|
||||
|
||||
const hidePopover = () => {
|
||||
hideTimeout = setTimeout(() => {
|
||||
popover.hide()
|
||||
|
@ -196,18 +159,6 @@
|
|||
searching = false
|
||||
search = ""
|
||||
}
|
||||
|
||||
const createSnippet = () => {
|
||||
editableSnippet = null
|
||||
snippetDrawer.show()
|
||||
}
|
||||
|
||||
const editSnippet = (e: Event, snippet: Snippet) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
editableSnippet = snippet
|
||||
snippetDrawer.show()
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if popoverAnchor && hoverTarget}
|
||||
|
@ -262,15 +213,8 @@
|
|||
/>
|
||||
{selectedCategory}
|
||||
{#if selectedCategory === "Snippets"}
|
||||
<div class="add-snippet-button">
|
||||
<Icon
|
||||
size="S"
|
||||
name="Add"
|
||||
hoverable
|
||||
newStyles
|
||||
on:click={createSnippet}
|
||||
/>
|
||||
</div>{/if}
|
||||
<SnippetsSidePanelHeader />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
@ -389,43 +333,12 @@
|
|||
{/if}
|
||||
|
||||
{#if selectedCategory === "Snippets" || search}
|
||||
{#if filteredSnippets?.length}
|
||||
<div class="sub-section">
|
||||
<ul>
|
||||
{#each filteredSnippets as snippet}
|
||||
<li
|
||||
class="binding"
|
||||
on:mouseenter={e =>
|
||||
showSnippetPopover(snippet, e.currentTarget)}
|
||||
on:mouseleave={hidePopover}
|
||||
on:click={() => addSnippet(snippet)}
|
||||
>
|
||||
<span class="binding__label">{snippet.name}</span>
|
||||
{#if search}
|
||||
<span class="binding__typeWrap">
|
||||
<span class="binding__type">snippet</span>
|
||||
</span>
|
||||
{:else}
|
||||
<Icon
|
||||
name="Edit"
|
||||
hoverable
|
||||
newStyles
|
||||
size="S"
|
||||
on:click={e => editSnippet(e, snippet)}
|
||||
/>
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
<SnippetsSidePanelContent {snippets} {addSnippet} {search} />
|
||||
{/if}
|
||||
{/if}
|
||||
</Layout>
|
||||
</div>
|
||||
|
||||
<SnippetDrawer bind:this={snippetDrawer} snippet={editableSnippet} />
|
||||
|
||||
<style>
|
||||
.binding-side-panel {
|
||||
border-left: var(--border-light);
|
||||
|
@ -585,8 +498,4 @@
|
|||
.binding-popover.has-code :global(.cm-content) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.add-snippet-button {
|
||||
margin-left: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
<script lang="ts">
|
||||
import { Icon, Popover, Body, Button } from "@budibase/bbui"
|
||||
import CodeEditor from "@/components/common/CodeEditor/CodeEditor.svelte"
|
||||
import { EditorModes } from "@/components/common/CodeEditor"
|
||||
import SnippetDrawer from "../SnippetDrawer.svelte"
|
||||
import { licensing } from "@/stores/portal"
|
||||
import UpgradeButton from "@/pages/builder/portal/_components/UpgradeButton.svelte"
|
||||
import type { Snippet } from "@budibase/types"
|
||||
|
||||
export let addSnippet
|
||||
export let snippets
|
||||
export let search
|
||||
|
||||
let popover: Popover
|
||||
let popoverAnchor: HTMLElement | null
|
||||
let hoverTarget: {
|
||||
type: "binding" | "helper" | "snippet"
|
||||
code: string
|
||||
description?: string
|
||||
} | null
|
||||
let hideTimeout: ReturnType<typeof setTimeout> | null
|
||||
let snippetDrawer: SnippetDrawer
|
||||
let editableSnippet: Snippet | null
|
||||
|
||||
$: enableSnippets = !$licensing.isFreePlan
|
||||
|
||||
$: filteredSnippets = getFilteredSnippets(enableSnippets, snippets, search)
|
||||
|
||||
const showSnippet = (snippet: Snippet, target: HTMLElement) => {
|
||||
stopHidingPopover()
|
||||
if (!snippet.code) {
|
||||
return
|
||||
}
|
||||
popoverAnchor = target
|
||||
hoverTarget = {
|
||||
type: "snippet",
|
||||
code: snippet.code,
|
||||
}
|
||||
popover.show()
|
||||
}
|
||||
|
||||
const hidePopover = () => {
|
||||
hideTimeout = setTimeout(() => {
|
||||
popover.hide()
|
||||
popoverAnchor = null
|
||||
hoverTarget = null
|
||||
hideTimeout = null
|
||||
}, 100)
|
||||
}
|
||||
|
||||
const stopHidingPopover = () => {
|
||||
if (hideTimeout) {
|
||||
clearTimeout(hideTimeout)
|
||||
hideTimeout = null
|
||||
}
|
||||
}
|
||||
|
||||
const createSnippet = () => {
|
||||
editableSnippet = null
|
||||
snippetDrawer.show()
|
||||
}
|
||||
|
||||
const editSnippet = (e: Event, snippet: Snippet) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
editableSnippet = snippet
|
||||
snippetDrawer.show()
|
||||
}
|
||||
|
||||
const getFilteredSnippets = (
|
||||
enableSnippets: boolean,
|
||||
snippets: Snippet[],
|
||||
search: string
|
||||
) => {
|
||||
if (!enableSnippets || !snippets.length) {
|
||||
return []
|
||||
}
|
||||
if (!search?.length) {
|
||||
return snippets
|
||||
}
|
||||
return snippets.filter(snippet =>
|
||||
snippet.name.toLowerCase().includes(search.toLowerCase())
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="snippet-list">
|
||||
{#if enableSnippets}
|
||||
{#each filteredSnippets as snippet}
|
||||
<div
|
||||
class="snippet"
|
||||
on:mouseenter={e => showSnippet(snippet, e.currentTarget)}
|
||||
on:mouseleave={hidePopover}
|
||||
on:click={() => addSnippet(snippet)}
|
||||
>
|
||||
{snippet.name}
|
||||
<Icon
|
||||
name="Edit"
|
||||
hoverable
|
||||
newStyles
|
||||
size="S"
|
||||
on:click={e => editSnippet(e, snippet)}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
{:else if !search}
|
||||
<div class="upgrade">
|
||||
<Body size="S">
|
||||
Snippets let you create reusable JS functions and values that can all be
|
||||
managed in one place
|
||||
</Body>
|
||||
{#if enableSnippets}
|
||||
<Button cta on:click={createSnippet}>Create snippet</Button>
|
||||
{:else}
|
||||
<UpgradeButton />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if hoverTarget && popoverAnchor}
|
||||
<Popover
|
||||
align="left-outside"
|
||||
bind:this={popover}
|
||||
anchor={popoverAnchor}
|
||||
minWidth={0}
|
||||
maxWidth={480}
|
||||
maxHeight={480}
|
||||
dismissible={false}
|
||||
on:mouseenter={stopHidingPopover}
|
||||
on:mouseleave={hidePopover}
|
||||
>
|
||||
<div class="snippet-popover">
|
||||
{#key hoverTarget}
|
||||
<CodeEditor
|
||||
value={hoverTarget.code?.trim()}
|
||||
mode={EditorModes.JS}
|
||||
readonly
|
||||
/>
|
||||
{/key}
|
||||
</div>
|
||||
</Popover>
|
||||
{/if}
|
||||
|
||||
<SnippetDrawer bind:this={snippetDrawer} snippet={editableSnippet} />
|
||||
|
||||
<style>
|
||||
/* Upgrade */
|
||||
.upgrade {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--spacing-l);
|
||||
}
|
||||
.upgrade :global(p) {
|
||||
text-align: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
/* List */
|
||||
.snippet-list {
|
||||
padding: 0 var(--spacing-l);
|
||||
padding-bottom: var(--spacing-l);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-s);
|
||||
}
|
||||
.snippet {
|
||||
font-size: var(--font-size-s);
|
||||
padding: var(--spacing-m);
|
||||
border-radius: 4px;
|
||||
background-color: var(--spectrum-global-color-gray-200);
|
||||
transition: background-color 130ms ease-out, color 130ms ease-out,
|
||||
border-color 130ms ease-out;
|
||||
word-wrap: break-word;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.snippet:hover {
|
||||
color: var(--spectrum-global-color-gray-900);
|
||||
background-color: var(--spectrum-global-color-gray-50);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Popover */
|
||||
.snippet-popover {
|
||||
width: 400px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,46 @@
|
|||
<script lang="ts">
|
||||
import { Icon, Tags, Tag } from "@budibase/bbui"
|
||||
import { licensing } from "@/stores/portal"
|
||||
import SnippetDrawer from "../SnippetDrawer.svelte"
|
||||
import type { Snippet } from "@budibase/types"
|
||||
|
||||
$: enableSnippets = !$licensing.isFreePlan
|
||||
|
||||
let snippetDrawer: SnippetDrawer
|
||||
let editableSnippet: Snippet | null
|
||||
|
||||
const createSnippet = () => {
|
||||
editableSnippet = null
|
||||
snippetDrawer.show()
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if enableSnippets}
|
||||
<div class="add-button">
|
||||
<Icon size="S" name="Add" hoverable newStyles on:click={createSnippet} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="title">
|
||||
<Tags>
|
||||
<Tag icon="LockClosed">Premium</Tag>
|
||||
</Tags>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<SnippetDrawer bind:this={snippetDrawer} snippet={editableSnippet} />
|
||||
|
||||
<style>
|
||||
.title {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: var(--spacing-m);
|
||||
}
|
||||
.add-button {
|
||||
margin-left: auto;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue