Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
seancolsen committed Dec 12, 2023
1 parent fa4bcb0 commit a8540fb
Show file tree
Hide file tree
Showing 19 changed files with 561 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
<script lang="ts">
import FieldsetGroup from '@mathesar-component-library-dir/fieldset-group/FieldsetGroup.svelte';
import type { SvelteComponent } from 'svelte';
import Checkbox from '@mathesar-component-library-dir/checkbox/Checkbox.svelte';
import type { LabelGetter } from '@mathesar-component-library-dir/common/utils/formatUtils';
import FieldsetGroup from '@mathesar-component-library-dir/fieldset-group/FieldsetGroup.svelte';
import type { ComponentWithProps } from '../types';
type Option = $$Generic;
export let values: Option[] = [];
export let isInline = false;
export let options: Option[] = [];
export let options: readonly Option[] = [];
export let label: string | undefined = undefined;
export let ariaLabel: string | undefined = undefined;
export let checkboxLabelKey: string | undefined = undefined;
export let getCheckboxLabel: LabelGetter<Option> | undefined = undefined;
export let getCheckboxHelp: <C extends SvelteComponent>(
value: Option,
) => string | ComponentWithProps<C> | undefined = () => undefined;
export let getCheckboxDisabled: (value: Option | undefined) => boolean = () =>
false;
/**
* By default, options will be compared by equality. If you're using objects as
* options, you can supply a custom function here to compare them.
Expand Down Expand Up @@ -41,12 +50,15 @@
{isInline}
{options}
{label}
{ariaLabel}
{disabled}
let:option
let:disabled={innerDisabled}
on:change
labelKey={checkboxLabelKey}
getLabel={getCheckboxLabel}
getHelp={getCheckboxHelp}
getDisabled={getCheckboxDisabled}
>
<Checkbox
on:change={({ detail: checked }) => handleChange(option, checked)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.fieldset-group {
padding: 0;
margin: 0;
--spacing-y-default: 0.5em;
--spacing-x-default: 1em;

Expand All @@ -14,10 +15,15 @@
&.has-label .options {
margin-top: 1em;
}
&:not(.inline) .option {
&:not(.inline) .option:not(:first-child) {
margin-top: var(--spacing-y, var(--spacing-y-default));
}
&:not(.inline) .option:not(:last-child) {
margin-bottom: var(--spacing-y, var(--spacing-y-default));
}
&:not(.inline) .option.has-help:not(:last-child) {
margin-bottom: calc(2 * var(--spacing-y, var(--spacing-y-default)));
}

&.inline .options {
display: flex;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
<script lang="ts">
import LabeledInput from '@mathesar-component-library-dir/labeled-input/LabeledInput.svelte';
import type { SvelteComponent } from 'svelte';
import type { LabelGetter } from '@mathesar-component-library-dir/common/utils/formatUtils';
import { getLabel as defaultGetLabel } from '@mathesar-component-library-dir/common/utils/formatUtils';
import LabeledInput from '@mathesar-component-library-dir/labeled-input/LabeledInput.svelte';
import StringOrComponent from '@mathesar-component-library-dir/string-or-component/StringOrComponent.svelte';
import StringOrComponentTyped from '@mathesar-component-library-dir/string-or-component/StringOrComponentTyped.svelte';
import type { ComponentWithProps } from '../types';
type Option = $$Generic;
export let isInline = false;
export let options: Option[] = [];
export let options: readonly Option[] = [];
export let label: string | undefined = undefined;
export let ariaLabel: string | undefined = undefined;
export let disabled = false;
export let labelKey = 'label';
export let getLabel: LabelGetter<Option> = (o: Option) =>
defaultGetLabel(o, labelKey);
export let boxed = false;
export let getDisabled: (value: Option | undefined) => boolean = () => false;
export let getHelp: <C extends SvelteComponent>(
value: Option,
) => string | ComponentWithProps<C> | undefined = () => undefined;
</script>

<fieldset
Expand All @@ -35,12 +41,18 @@
{/if}
<ul class="options">
{#each options as option (option)}
<li class="option">
{@const help = getHelp(option)}
<li class="option" class:has-help={!!help}>
<LabeledInput layout="inline-input-first">
<svelte:fragment slot="label">
<StringOrComponent arg={getLabel(option)} />
</svelte:fragment>
<slot {option} disabled={getDisabled(option) || disabled} />
<svelte:fragment slot="help">
{#if help}
<StringOrComponentTyped arg={help} />
{/if}
</svelte:fragment>
</LabeledInput>
</li>
{/each}
Expand Down
28 changes: 15 additions & 13 deletions mathesar_ui/src/component-library/labeled-input/LabeledInput.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
}

.help {
// TODO figure out how we want to support help text with an inline layout.
// This CSS assumes the layout is stacked
display: block;
font-size: var(--text-size-small);
color: var(--color-text-muted);
Expand All @@ -34,29 +32,33 @@
}
}

&.layout-inline .label-content,
&.layout-inline-input-first .label-content {
// TODO: add support for help text with an inline layout.
&.layout-inline .label-content {
display: inline-flex;
flex-direction: row;
align-items: center; // To support a text input that's taller than the label
.label {
flex: 1 1 auto;
}
.input {
flex: 0 0 auto;
}
}

&.layout-inline .label-content {
flex-direction: row;
align-items: center; // To support a text input that's taller than the label
.input {
margin-left: var(--spacing-x, var(--spacing-x-default));
}
}

&.layout-inline-input-first .label-content {
flex-direction: row-reverse;
align-items: flex-start; // To support a checkbox with a wrapping label
display: grid;
grid-template: auto auto / auto 1fr;
.input {
grid-area: 1 / 1 / 1 / 1;
margin-right: var(--spacing-x, var(--spacing-x-default));
}
.label {
grid-area: 1 / 2 / 1 / 2;
}
.help {
grid-area: 2 / 2 / 2 / 2;
margin-top: var(--spacing-y, var(--spacing-y-default));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@
>
<Label>
<span class="label-content">
<span class="label">
{label ?? ''}
<slot name="label" />
</span>
<span class="help">
{help ?? ''}
<slot name="help" />
</span>
<!--
⚠️ NOTE: Do not add any white space within `.label` or `.help`. We have
CSS that uses the `:empty` pseudo-class which does not work if there is
white space.
-->
<span class="label">{label ?? ''}<slot name="label" /></span>
<span class="help">{help ?? ''}<slot name="help" /></span>
<span class="input"><slot /></span>
</span>
</Label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
export let value: Option | undefined = undefined;
export let isInline = false;
export let options: Option[] = [];
export let options: readonly Option[] = [];
export let label: string | undefined = undefined;
export let ariaLabel: string | undefined = undefined;
export let radioLabelKey: string | undefined = undefined;
Expand Down
17 changes: 17 additions & 0 deletions mathesar_ui/src/component-library/render/Render.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script lang="ts">
import type { SvelteComponent } from 'svelte';
import type { ComponentWithProps } from '../types';
type T = $$Generic<SvelteComponent>;
export let componentWithProps: ComponentWithProps<T>;
// I'm not sure how to fix the use of `any` here. Suggestions welcome!
// eslint-disable-next-line @typescript-eslint/no-explicit-any
$: component = componentWithProps.component as any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
$: props = componentWithProps.props as any;
</script>

<svelte:component this={component} {...props} />
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
<!--
@component
@deprecated in favor of `StringOrComponentTyped` which uses
`ComponentWithProps` for better type safety in comparing a component to its
props.
-->
<script lang="ts">
import type { ComponentAndProps } from '@mathesar-component-library-dir/types';
import {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!--
@component
This component replaces `StringOrComponent` (which is deprecated)
-->
<script lang="ts">
import type { SvelteComponent } from 'svelte';
import type { ComponentWithProps } from '@mathesar-component-library-dir/types';
import Render from '../render/Render.svelte';
type T = $$Generic<SvelteComponent>;
export let arg: string | string[] | ComponentWithProps<T>;
</script>

{#if typeof arg === 'string'}
{arg}
{:else if Array.isArray(arg)}
{#each arg as paragraph}
<p>{paragraph}</p>
{/each}
{:else}
<Render componentWithProps={arg} />
{/if}
8 changes: 7 additions & 1 deletion mathesar_ui/src/components/form/Field.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import { LabeledInput, TextInput } from '@mathesar-component-library';
import type { ComponentWithProps } from '@mathesar-component-library/types';
import type { FieldStore } from './field';
import FieldErrors from './FieldErrors.svelte';
import FieldHelp from './FieldHelp.svelte';
import FieldLayout from './FieldLayout.svelte';
import type { FieldStore } from './field';
type Layout = ComponentProps<LabeledInput>['layout'];
type Value = $$Generic;
Expand Down Expand Up @@ -46,6 +47,11 @@
>
<slot />
</svelte:component>
{#if help || $$slots.help}
<FieldHelp>
<slot name="help">{help}</slot>
</FieldHelp>
{/if}
{/if}
<FieldErrors {field} />
</FieldLayout>
10 changes: 10 additions & 0 deletions mathesar_ui/src/components/form/FieldHelp.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<span class="field-help"><slot /></span>

<style>
.field-help {
display: block;
font-size: var(--text-size-small);
color: var(--color-text-muted);
margin-top: 0.5rem;
}
</style>
6 changes: 6 additions & 0 deletions mathesar_ui/src/components/form/GridFormInput.svelte
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
<!--
@component
@deprecated in favor of components in @mathesar/components/grid-form which
have looser coupling with the form validation system.
-->
<script lang="ts">
import type { SvelteComponent } from 'svelte';
import {
Expand Down
6 changes: 6 additions & 0 deletions mathesar_ui/src/components/form/GridFormInputRow.svelte
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
<!--
@component
@deprecated in favor of components in @mathesar/components/grid-form which
have looser coupling with the form validation system.
-->
<script lang="ts">
export let bypass = false;
</script>
Expand Down
40 changes: 40 additions & 0 deletions mathesar_ui/src/components/grid-form/GridForm.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<script lang="ts">
/** When the form is wider than this, it will display in a grid. When it's
* narrower, it will display vertically */
export let breakpointPx = 400;
let width: number;
</script>

<div
class="grid-form"
bind:clientWidth={width}
class:grid={width > breakpointPx}
>
<slot />
</div>

<style>
.grid-form:not(.grid) > :global(* + *) {
margin-top: 1rem;
}
.grid-form:not(.grid) > :global(.divider + .divider) {
display: none;
}
.grid-form:not(.grid) > :global(:nth-child(even)) {
margin-left: 1rem;
margin-bottom: 2rem;
}
.grid-form.grid {
display: grid;
grid-template-columns: fit-content(30%) 1fr;
gap: 1.5rem 0;
}
.grid-form.grid > :global(:nth-child(odd)) {
text-align: right;
padding-right: 1rem;
}
</style>
9 changes: 9 additions & 0 deletions mathesar_ui/src/components/grid-form/GridFormDivider.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div class="divider" />
<div class="divider" />

<style>
.divider {
height: 0;
border-top: solid 1px var(--slate-300);
}
</style>
28 changes: 28 additions & 0 deletions mathesar_ui/src/components/grid-form/GridFormLabelRow.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script lang="ts">
import {
Label,
LabelController,
setLabelControllerInContext,
} from '@mathesar-component-library';
const labelController = new LabelController();
setLabelControllerInContext(labelController);
export let label: string;
</script>

<div class="label-cell">
<Label controller={labelController}>
{label}
</Label>
</div>

<div class="input-cell">
<slot />
</div>

<style>
.label-cell {
padding-top: 0.5rem;
}
</style>
Loading

0 comments on commit a8540fb

Please sign in to comment.