Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new layout for simplfied arrays (strings) #159

Merged
merged 3 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 47 additions & 33 deletions src/lib/editor/form/AbstractField.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import BooleanField from "./BooleanField.svelte";
import clsx from "clsx";
import { getBuilderContext } from "$lib/store/builder";
import type { Schema } from "./utils/schema";

const dispatch = createEventDispatcher();

Expand All @@ -34,6 +35,7 @@

export let readOnly = false;

$: childrenType = (field.schema.items as Schema)?.type;
$: isHover = $activeItem === field.id;
$: highlight = $highlightedItem === field.id;
$: showContextMenu = !readOnly && isHover;
Expand All @@ -42,19 +44,20 @@
const offset = top + height - window.innerHeight;
contextMenuOffset = offset > 0 ? offset : 0;
}
$: isParent = ["object", "array"].includes(field.type);
$: isParent = ["object", "array"].includes(field.type) && childrenType !== "string";
$: isSection = isParent && field.parent?.is.root;
$: wrapperClasses = clsx({
"bg-neutral-50 border-neutral-100": isHover && !isParent,
"border-transparent": !isHover,
"border-l border-t border-b": !isParent,
"border-l border-t border-b": !["object", "array"].includes(field.type),
"border-r rounded-r": readOnly && !isParent,
"pl-2": isParent && !isSection,
});
$: classes = clsx({
"bg-neutral-50": isHover,
"border-workspace-accent-500": highlight,
"border-neutral-200": !highlight,
"border-l border-t border-b": childrenType !== "string",
});
$: contextMenuClasses = clsx({
"mt-1": isParent && !isSection && !field.is.root,
Expand All @@ -78,20 +81,29 @@
function handleAddField() {
showAddMenu = false;

// Children of string arrays can add items directly from the row
if (field.type === "string" && field.parent?.type === "array") {
addFieldToArray(field.parent);
return;
}

// @note: Add field directly instead of showing the dropdown option list
if (field.type === "array" || field.controlType === "dictionary") {
const [childOption] = field.options || [];

const newField = field.addChildField(childOption);
newField?.tryFocus();
dispatch("fieldAdded", newField);

addFieldToArray(field);
return;
}

showAddMenu = true;
}

function addFieldToArray(f: UIModelField) {
const [childOption] = f.options || [];

const newField = f.addChildField(childOption);
newField?.tryFocus();
dispatch("fieldAdded", newField);
}

function focusNextField(nextField = field.getNextFocusableField()) {
if (!nextField) return;
nextField.tryFocus(5, 0);
Expand Down Expand Up @@ -156,7 +168,7 @@
>
<div class="{wrapperClasses} rounded-l flex" class:my-1={isParent}>
{#if isParent && !isSection && !field.is.root}
<div class="{classes} w-2 border-l border-t border-b flex-none rounded-l"></div>
<div class="{classes} w-2 flex-none rounded-l"></div>
{/if}
<div class="flex-1">
<svelte:component
Expand All @@ -172,31 +184,33 @@
/>
</div>
</div>
<div class="absolute top-0 right-0 w-9 h-10 -mr-9">
<span
class="{contextMenuClasses} absolute top-0 left-0 -ml-px pt-[7px] pr-2 pb-[5.5px] bg-neutral-50 border-t border-b border-r border-neutral-100 rounded-r border-l-neutral-50 border-l"
class:invisible={!showContextMenu}
>
<FieldContextMenu
{field}
on:addField={handleAddField}
on:fieldDeleted
on:fieldDuplicated
on:fieldMoved
on:fieldValueUpdated
on:fieldKeyUpdated
/>
</span>
{#if showAddMenu}
<div
class="absolute top-12 -right-8 w-64 z-20"
style={`margin-top: -${contextMenuOffset}px`}
bind:this={addMenuRef}
{#if childrenType !== "string"}
<div class="absolute top-0 right-0 w-9 h-10 -mr-9 bg-transparent">
<span
class="{contextMenuClasses} absolute top-0 left-0 -ml-px pt-[7px] pr-2 pb-[5.5px] bg-neutral-50 border-t border-b border-r border-neutral-100 rounded-r border-l-neutral-50 border-l"
class:invisible={!showContextMenu}
>
<AddFieldMenu on:fieldAdded {field} on:closeAddFieldMenu={() => (showAddMenu = false)} />
</div>
{/if}
</div>
<FieldContextMenu
{field}
on:addField={handleAddField}
on:fieldDeleted
on:fieldDuplicated
on:fieldMoved
on:fieldValueUpdated
on:fieldKeyUpdated
/>
</span>
{#if showAddMenu}
<div
class="absolute top-12 -right-8 w-64 z-20"
style={`margin-top: -${contextMenuOffset}px`}
bind:this={addMenuRef}
>
<AddFieldMenu on:fieldAdded {field} on:closeAddFieldMenu={() => (showAddMenu = false)} />
</div>
{/if}
</div>
{/if}
</div>

<style>
Expand Down
12 changes: 7 additions & 5 deletions src/lib/editor/form/FieldButtons.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@

const dispatch = createEventDispatcher();

$: showAdd = ["object", "array"].includes(field?.type || "");
$: parentIsArray = field?.parent?.type === "array";
$: isArrayStringChildren = parentIsArray && field?.type === "string";
$: showAdd = ["object", "array"].includes(field?.type || "") || isArrayStringChildren;
$: addLabel = field?.type === "array" ? "Add Row" : "Add Property";
$: canMove = field?.parent?.type === "array";
$: canMoveUp = canMove && Number(field?.key) > 0;
$: canMoveDown = canMove && Number(field?.key) < Number(field?.parent?.children?.length) - 1;
$: canDuplicate = field?.is.duplicable && !isArrayStringChildren;
$: canMoveUp = parentIsArray && Number(field?.key) > 0;
$: canMoveDown = parentIsArray && Number(field?.key) < Number(field?.parent?.children?.length) - 1;
</script>

<ul
Expand All @@ -25,7 +27,7 @@
<FieldButton icon={Plus} tooltipText={addLabel} on:click={() => dispatch("add")} />
</li>
{/if}
{#if field?.is.duplicable}
{#if canDuplicate}
<li>
<FieldButton icon={DocumentDuplicate} tooltipText="Duplicate" on:click={() => dispatch("duplicate")} />
</li>
Expand Down
25 changes: 19 additions & 6 deletions src/lib/editor/form/FieldTitle.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
import { clsx } from "clsx";
import type { UIModelField } from "$lib/editor/form/utils/model.js";
import { Calculator, Hashtag, Icon } from "svelte-hero-icons";
import type { Schema } from "./utils/schema";

export let field: UIModelField<unknown>;

$: childrenType = (field.schema.items as Schema)?.type;
$: arrayTitle = field.schema.title || "";
$: isParent = ["object", "array"].includes(field.type);
$: isParent = ["object", "array"].includes(field.type) && childrenType !== "string";
$: isSection = field.is.root || (field.parent?.is.root && isParent);
$: classes = clsx({
"font-medium text-neutral-800": isParent,
Expand All @@ -17,14 +19,25 @@
</script>

<span
class="{classes} whitespace-nowrap flex items-center"
class="{classes} whitespace-nowrap flex items-center w-full"
class:capitalize={!field.schema.title && !field.is.editableKey}
>
{#if field.parent?.isArray()}
<span class="flex items-center justify-start">
<Icon src={Hashtag} class="h-4 w-4" />
{arrayTitle}
{Number(field.key) + 1}
<span
class:justify-start={field.type !== "string"}
class:justify-between={field.type === "string"}
class="flex items-center w-full"
>
<span>
{#if field.key === "0" && field.type === "string"}
{field.parent.schema.title || field.parent.key}
{/if}
</span>
<span class="flex items-center">
<Icon src={Hashtag} class="h-4 w-4" />
{arrayTitle}
{Number(field.key) + 1}
</span>
</span>
{:else}
<span class="flex items-center space-x-1">
Expand Down
15 changes: 13 additions & 2 deletions src/lib/editor/form/RootField.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import type { UIModelRootField, UIModelField } from "$lib/editor/form/utils/model.js";
import AbstractField from "./AbstractField.svelte";
import SectionWrapper from "./SectionWrapper.svelte";
import type { Schema } from "./utils/schema";

export let field: UIModelRootField;
export let readOnly = false;
Expand All @@ -10,8 +11,18 @@

$: children = field.children?.filter((f) => f.key !== "$schema") || emptyChildren;
// @todo: Add title field to schema object on gobl
$: complexFields = children.filter((f) => ["array", "object"].includes(f.type));
$: simpleFields = children.filter((f) => !["array", "object"].includes(f.type));
$: complexFields = children.filter((f) => {
if ((f.schema.items as Schema)?.type === "string") {
return false;
}
return ["array", "object"].includes(f.type);
});
$: simpleFields = children.filter((f) => {
if ((f.schema.items as Schema)?.type === "string") {
return true;
}
return !["array", "object"].includes(f.type);
});
</script>

<SectionWrapper {readOnly} {field}>
Expand Down
33 changes: 19 additions & 14 deletions src/lib/editor/form/SectionWrapper.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import clsx from "clsx";
import { getContext } from "svelte";
import { getBuilderContext } from "$lib/store/builder";
import type { Schema } from "./utils/schema";

const { envelopeIsSigned } = getBuilderContext();

Expand Down Expand Up @@ -34,16 +35,18 @@
let element: HTMLElement;
let open = true;

$: childrenType = (field.schema.items as Schema)?.type;
$: label = $envelopeIsSigned
? "Document is signed and can not be edited"
: `${field.schema.description || ""}${field.is.calculated ? " (calculated)" : ""}`;
$: isParent = ["object", "array"].includes(field.type);
$: isParent = ["object", "array"].includes(field.type) && childrenType !== "string";
$: isSection = field.is.root || (isParent && field.parent?.is.root);
$: wrapperClasses = clsx({
"rounded-r": readOnly,
"border-neutral-100 bg-neutral-50": isActive,
"border-l rounded-l": isSection,
"border-transparent": !isActive,
"border-t border-b border-r": childrenType !== "string",
});
$: isActive = field.id === $activeItem;

Expand Down Expand Up @@ -71,25 +74,27 @@
}
</script>

<div bind:this={element} id={field.id} title={label} class="{wrapperClasses} border-t border-b border-r">
<div bind:this={element} id={field.id} title={label} class={wrapperClasses}>
{#if isSection}
<div use:intersect={intersectOptions}></div>
{/if}
<div class="py-2 pl-3 pr-2">
<button
class="flex items-center justify-start cursor-pointer"
on:click={() => {
open = !open;
}}
>
<FieldTitle {field} />
<ExpandButton {open} />
</button>
</div>
{#if isParent}
<div class="py-2 pl-3 pr-2">
<button
class="flex items-center justify-start cursor-pointer"
on:click={() => {
open = !open;
}}
>
<FieldTitle {field} />
<ExpandButton {open} />
</button>
</div>
{/if}

{#if open}
<div transition:slide class:overflow-hidden={!open} on:focusin|capture={handleFocusInner}>
<div class="grid grid-cols-1 w-full pb-1">
<div class:pb-1={isParent} class="grid grid-cols-1 w-full">
<slot />
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/lib/editor/form/utils/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export class UIModelField<V extends SchemaValue | unknown = unknown> {
root?: UIModelRootField,
) {
this.id = `${this.parent?.id ? `${this.parent.id}` : `${this.uniqueId}`}-${this.key}`.replace(
/[^a-zA-Z0-9-_\\$]/g,
/[^a-zA-Z0-9-_]/g,
"",
);
this.type = this.schema.type as string;
Expand Down
Loading