Skip to content
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
35 changes: 22 additions & 13 deletions src/lib/controls/AnyOfControl.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
$: resetSelected(schemas);
$: resetData(selected, type);

function isObjSchema() {
return isObjectSchema({ type: selected?.type ?? type });
}

function getKey(schema: JSONSchema7) {
return keys.get(schema) ?? "";
}
Expand All @@ -50,24 +54,29 @@
}

async function resetData(selected: JSONSchema7 | null, type: JSONSchema7['type']) {
if (isObjectSchema({ type: selected?.type ?? type })) {
if (selectedProps) {
const omitted = omit(data, selectedProps);
// make sure it's changed (to prevent infinite loop)
if (Object.keys(data).length != Object.keys(omitted).length) {
await tick();
data = omitted;
}
let newData = data;
if (isObjSchema() && (selected != null)) {
if (selectedProps && data) {
newData = omit(data, selectedProps, { keepUnchanged: true });
}
else if ((data == null) || !isEmpty(data)) {
await tick();
data = {};
newData = {};
}
selectedProps = Object.keys(selected?.properties ?? {})
}
else {
if (data != null) data = undefined;
if (selectedProps != null) selectedProps = undefined;
newData = undefined;
}
if (newData !== data) {
await tick();
data = newData;
}
resetSelectedProps();
}

function resetSelectedProps() {
const newSelectedProps = isObjSchema() ? Object.keys(selected?.properties ?? {}) : undefined;
if (newSelectedProps !== selectedProps) {
selectedProps = newSelectedProps;
}
}
</script>
Expand Down
13 changes: 6 additions & 7 deletions src/lib/controls/ObjectControl.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import type { JSONSchema7Definition } from "json-schema";
import { hasRequired as checkRequired, isBoolean } from "$lib/utilities";
import { isBoolean } from "$lib/utilities";
import UISchema from "$lib/UISchema";
import Accordion, { Panel, Header, Content } from '@smui-extra/accordion';
import IconButton, { Icon } from '@smui/icon-button';
Expand All @@ -21,9 +21,8 @@

$: uiOptions = UISchema.Options.get(uischema);
$: hasProps = !!Object.keys(properties ?? {}).length || !!Object.keys(anyOf ?? {}).length;
$: hasRequired = isRequired || checkRequired({ properties, required, anyOf });
$: ignoreEmpty = $uiOptions.ignoreEmpty ?? false;
$: updateEnabled(data, hasRequired, ignoreEmpty);
$: updateEnabled(data, isRequired, ignoreEmpty);
$: updateData(enabled);
$: updateOpen(enabled);
$: updateOpen($uiOptions.collapse);
Expand All @@ -34,8 +33,8 @@
open = hasProps && (isBoolean(arg) ? arg : !UISchema.shouldCollapse($$props, arg, open));
}

function updateEnabled(data: any, hasRequired: boolean, ignoreEmpty: boolean) {
const shouldEnable = hasRequired || ignoreEmpty || !!data;
function updateEnabled(data: any, isRequired: boolean | undefined, ignoreEmpty: boolean) {
const shouldEnable = isRequired || ignoreEmpty || !!data;
if (shouldEnable != enabled) {
enabled = shouldEnable;
}
Expand All @@ -59,11 +58,11 @@
bind:open
variant="unelevated"
disabled={!enabled}
class={(hasRequired || ignoreEmpty) ? "no-disable" : undefined}
class={(isRequired || ignoreEmpty) ? "no-disable" : undefined}
nonInteractive={!hasProps}
>
<Header>
{#if !hasRequired && !ignoreEmpty}
{#if !isRequired && !ignoreEmpty}
<IconButton type="button" toggle bind:pressed={enabled} size="button" on:click={stop}>
<Icon class="material-icons" on>check_box</Icon>
<Icon class="material-icons">check_box_outline_blank</Icon>
Expand Down
27 changes: 20 additions & 7 deletions src/lib/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,26 @@ export function isObject(arg: any): arg is object {
return Object.prototype.toString.call(arg) === '[object Object]';
}

export function omit<T extends Record<any, any>, K extends keyof T>(obj: T, keys: K[]) {
return Object.keys(obj)
.filter(key => !keys.includes(key as K))
.reduce((acc, key) => {
acc[key as keyof typeof acc] = obj[key];
return acc;
}, {} as Omit<T, K>)
/**
* Omits properties from the given object.
*
* @param obj The object to omit properties from
* @param keys The array of property keys to omit from the object
* @param [options] Options for the omit function:
* {
* keepUnchanged: True to return the original object if no properties were omitted. Defaults to false.
* }
* @return An object with the given properties omitted
*/
export function omit<T extends Record<any, any>, K extends keyof T>(obj: T, keys: K[], options?: { keepUnchanged: boolean }) {
const { keepUnchanged = false } = options ?? {};
const objKeys = Object.keys(obj);
const keepKeys = objKeys.filter(key => !keys.includes(key as K));
const keepObj = (keepKeys.length === objKeys.length) && keepUnchanged;
return keepObj ? obj : keepKeys.reduce((acc, key) => {
acc[key as keyof typeof acc] = obj[key];
return acc;
}, {} as Omit<T, K>)
}

export function isDefined<T>(value: T | undefined): value is T {
Expand Down