Skip to content

Commit

Permalink
Got editor UI working
Browse files Browse the repository at this point in the history
  • Loading branch information
pmalacho-mit committed Nov 22, 2023
1 parent cbcd3c3 commit 8cb0613
Show file tree
Hide file tree
Showing 8 changed files with 365 additions and 62 deletions.
26 changes: 2 additions & 24 deletions extensions/src/common/components/Ok.svelte
Original file line number Diff line number Diff line change
@@ -1,28 +1,6 @@
<script lang="ts">
import { color } from "../ui";
import PrimaryButton from "./PrimaryButton.svelte";
export let disabled: boolean = false;
const text = color.ui.white;
const solid = color.motion.primary;
</script>

<style>
button:disabled {
background: var(--drop-highlight);
border: var(--drop-highlight);
}
button {
padding: 0.75rem 1rem;
border-radius: 0.25rem;
border-width: 1px;
border-style: solid;
font-weight: 600;
font-size: 0.85rem;
transition: background-color 0.25s, border-color 0.25s;
}
</style>

<button on:click {disabled} style:border-color={solid} style:background-color={solid} style:color={text} data-testid="ok">
OK
</button>
<PrimaryButton on:click {disabled} dataTestID={"ok"}>Ok</PrimaryButton>
41 changes: 41 additions & 0 deletions extensions/src/common/components/PrimaryButton.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script lang="ts">
import { color } from "../ui";
export let disabled: boolean = false;
export let dataTestID: string = null;
export let paddingVertical: string = "0.75rem";
export let paddingHorizontal: string = "1rem";
export let borderRadius: string = "0.25rem";
const text = color.ui.white;
const solid = color.motion.primary;
</script>

<button
on:click
on:mousedown
on:mouseup
{disabled}
style:border-color={solid}
style:background-color={solid}
style:color={text}
data-testid={dataTestID}
style:padding={`${paddingVertical} ${paddingHorizontal}`}
style:border-radius={borderRadius}
>
<slot />
</button>

<style>
button:disabled {
background: var(--drop-highlight) !important;
border: var(--drop-highlight) !important;
}
button {
border-width: 1px;
border-style: solid;
font-weight: 600;
font-size: 0.85rem;
transition: background-color 0.25s, border-color 0.25s;
}
</style>
60 changes: 60 additions & 0 deletions extensions/src/common/components/X.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<script lang="ts">
export let size: number = 12;
export let fill: string = "#f44";
export let stroke: string = "#fff";
export let strokeWidth: number = 1;
const dimension = size / 2;
const half = dimension / 2;
const quarter = dimension / 4;
const threeQuarters = (dimension * 3) / 4;
</script>

<!-- svelte-ignore a11y-click-events-have-key-events -->
<div style:width="{size}px" style:height="{size}px">
<svg
style:top="{-dimension}px"
style:right="{-dimension}px"
style:width="{size}px"
style:height="{size}px"
viewBox="0 0 {dimension} {dimension}"
style:fill
on:click
>
<circle cx={half} cy={half} r={half} />
<line
style:stroke
x1={quarter}
y1={quarter}
x2={threeQuarters}
y2={threeQuarters}
style:stroke-width={strokeWidth}
/>
<line
style:stroke
x1={threeQuarters}
y1={quarter}
x2={quarter}
y2={threeQuarters}
style:stroke-width={strokeWidth}
/>
</svg>
</div>

<style>
div {
position: relative;
}
svg {
position: absolute;
cursor: pointer;
transition: transform 0.3s;
}
svg:hover {
transform: scale(1.2);
}
line {
stroke-linecap: round;
}
</style>
75 changes: 58 additions & 17 deletions extensions/src/textClassification/Editor.svelte
Original file line number Diff line number Diff line change
@@ -1,35 +1,76 @@
<script lang="ts">
import type Extension from ".";
import { ReactiveInvoke, reactiveInvoke } from "$common";
import { ReactiveInvoke, color, reactiveInvoke } from "$common";
import Class from "./components/Class.svelte";
import PrimaryButton from "$common/components/PrimaryButton.svelte";
export let extension: Extension;
export let close: () => void;
const invoke: ReactiveInvoke<Extension> = (functionName, ...args) =>
reactiveInvoke((extension = extension), functionName, args);
let activeIndex = 0;
const getNextLabel = () => {
let label: string;
let count = extension.labels.length + 1;
do {
label = `Class ${count++}`;
} while (extension.labels.includes(label));
return label;
};
const add = () => {
invoke("addLabel", getNextLabel());
activeIndex = extension.labels.length - 1;
};
const drop = () => {
invoke("deleteLabel", extension.labels[activeIndex], activeIndex);
activeIndex = -1;
};
</script>

{#each extension.labels as label, index}
<Class
{label}
setActive={() => (activeIndex = index)}
isActive={activeIndex === index}
examples={extension.modelData.get(label)}
/>
{/each}

<button
on:click={() => invoke("addLabel", `Class ${extension.labels.length + 1}`)}
>
Add Label</button
>
<button>Clear all</button>
<div class="container">
<div class="pane" style:background-color={color.ui.secondary}>
{#each extension.labels as label, index}
{@const examples = extension.modelData.get(label)}
{#if examples}
<Class
{label}
{examples}
setActive={() => (activeIndex = index)}
isActive={activeIndex === index}
deactivate={() => (activeIndex = -1)}
deleteSelf={drop}
rename={(newLabel) => invoke("renameLabel", label, newLabel, index)}
/>
{/if}
{/each}
</div>
<div class="footer">
<PrimaryButton on:click={add}>Add a Label</PrimaryButton>
<PrimaryButton on:click={() => invoke("clearLabels")}
>Clear All
</PrimaryButton>
<PrimaryButton on:click={close}>Done</PrimaryButton>
</div>
</div>

<style>
.container {
text-align: center;
padding: 30px;
width: 600px;
background-color: white;
}
.pane {
height: 300px;
text-align: left;
overflow-y: scroll;
}
.footer {
padding: 1rem 0;
}
</style>
158 changes: 149 additions & 9 deletions extensions/src/textClassification/components/Class.svelte
Original file line number Diff line number Diff line change
@@ -1,17 +1,157 @@
<script lang="ts">
import PrimaryButton from "$common/components/PrimaryButton.svelte";
import Example from "./Example.svelte";
export let isActive: boolean;
export let label: string;
export let examples: string[];
export let setActive: () => void;
export let deactivate: () => void;
export let deleteSelf: () => void;
export let rename: (newLabel: string) => void;
const paddingVertical = "0.3rem";
const paddingHorizontal = "0.5rem";
let input: HTMLInputElement;
const set = () => (label !== input?.value ? rename(input.value) : void 0);
let candidate: string = "";
$: valid = candidate !== "" && !examples.includes(candidate);
const addCandidate = () => {
examples.push(candidate);
examples = examples;
candidate = "";
};
const deleteExample = (index: number) => {
examples.splice(index, 1);
examples = examples;
};
const onEnter =
(...callbacks: (() => void)[]) =>
(event: KeyboardEvent) => {
if (event.key !== "Enter") return;
event.preventDefault();
callbacks.forEach((callback) => callback());
};
</script>

{#if isActive}
<span>{label}</span>
<span>({examples.length} example{examples.length === 1 ? "s" : ""})</span>
<button on:click={setActive}>Edit Label</button>
{:else}
{#each examples as example}
<div>{example}</div>
{/each}
{/if}
<div class="row">
{#if isActive}
<div>
<input
type="text"
value={label}
bind:this={input}
id="label"
name="label"
on:blur={set}
on:keypress={onEnter(set, deactivate)}
/>
<span>
({examples.length} example{examples.length !== 1 ? "s" : ""})
</span>

<PrimaryButton
{paddingVertical}
{paddingHorizontal}
on:click={deactivate}
>
Done Editing
</PrimaryButton>

<PrimaryButton
{paddingVertical}
{paddingHorizontal}
on:click={deleteSelf}
>
Delete Label
</PrimaryButton>
</div>
<div class="examples">
{#each examples as example, index}
<Example text={example} xOut={() => deleteExample(index)} />
{/each}
</div>
<center>
<div style:margin-top="1rem">
<input
bind:value={candidate}
on:keypress={onEnter(() =>
valid ? addCandidate() : void 0
)}
/>
<PrimaryButton
{paddingVertical}
{paddingHorizontal}
disabled={!valid}
on:click={addCandidate}
>
Add Example
</PrimaryButton>
</div>
</center>
{:else}
<div>
<span>{label}</span>
<span>
({examples.length} example{examples.length !== 1 ? "s" : ""})
</span>
<PrimaryButton
{paddingVertical}
{paddingHorizontal}
on:click={setActive}
>
Edit Label
</PrimaryButton>
</div>
<div class="examples">
{#each examples as example}
<Example text={example} />
{/each}
</div>
{/if}
</div>

<style>
.row {
margin: 0.5rem 0.5rem;
border-radius: 0.25rem;
background-color: white;
padding: 0.5rem;
}
input {
height: 1.3rem;
padding: 0 0.75rem;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 0.875rem;
font-weight: bold;
color: hsla(225, 15%, 40%, 1);
border-width: 1px;
border-style: solid;
border-color: hsla(0, 0%, 0%, 0.15);
border-radius: 0.25rem;
outline: none;
cursor: text;
transition: 0.25s ease-out;
box-shadow: none;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}
.examples {
display: flex;
width: 90%;
margin: auto;
overflow-y: auto;
flex-wrap: wrap;
}
</style>
Loading

0 comments on commit 8cb0613

Please sign in to comment.