Skip to content

Commit

Permalink
Fix type errors for Button's use of Icon data.
Browse files Browse the repository at this point in the history
  • Loading branch information
willnationsdev committed Nov 12, 2023
1 parent 8d988d4 commit ee76171
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 12 deletions.
6 changes: 6 additions & 0 deletions .changeset/slimy-starfishes-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'svelte-ux': patch
---

Added new `IconInput` and `IconData` types to enable inclusive & seamless passing of icon arguments between components. Also provides a `asIconData` utility function for type-safe conversion.
Fixed type errors for Button & TextField's use of Icon data.
3 changes: 2 additions & 1 deletion packages/svelte-ux/src/lib/components/Button.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import type { TailwindColors } from '$lib/types';
import { getComponentTheme } from './theme';
import { getButtonGroup } from './ButtonGroup.svelte';
import { asIconData, type IconData } from '$lib/utils/icons';
export let type: 'button' | 'submit' | 'reset' = 'button';
export let href: string | undefined = undefined;
Expand Down Expand Up @@ -261,7 +262,7 @@
<span in:slide={{ axis: 'x', duration: 200 }}>
{#if typeof icon === 'string' || 'icon' in icon}
<!-- font path/url/etc or font-awesome IconDefinition -->
<Icon data={icon} class={cls('pointer-events-none', theme.icon, classes.icon)} />
<Icon data={asIconData(icon)} class={cls('pointer-events-none', theme.icon, classes.icon)} />
{:else}
<Icon class={cls('pointer-events-none', theme.icon, classes.icon)} {...icon} />
{/if}
Expand Down
13 changes: 8 additions & 5 deletions packages/svelte-ux/src/lib/components/Icon.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
export let height = size;
export let viewBox = '0 0 24 24';
export let path: string | string[] = '';
export let data: IconDefinition | string | undefined = undefined;
export let data: IconDefinition | string | null | undefined = undefined;
export let svg: string | undefined = undefined;
export let svgUrl: string | undefined = undefined;
Expand All @@ -31,7 +31,7 @@
} = {};
const theme = getComponentTheme('Icon');
$: if (typeof data === 'object' && 'icon' in data) {
$: if (typeof data === 'object' && data && 'icon' in data) {
// Font Awesome
const [_width, _height, _ligatures, _unicode, _path] = data.icon;
viewBox = `0 0 ${_width} ${_height}`;
Expand Down Expand Up @@ -59,15 +59,18 @@
$: if (svgUrl) {
let promise;
if (cache.has(svgUrl)) {
cache.get(svgUrl).then((text) => (svg = text));
cache.get(svgUrl)?.then((text) => (svg = text));
} else {
promise = fetch(svgUrl)
.then((resp) => resp.text())
.catch((e) => {
.catch(() => {
// Failed request, remove promise so fetched again
cache.delete(svgUrl);
if (svgUrl && typeof(svgUrl) === "string") {
cache.delete(svgUrl);
}
// TODO: Consider showing error icon
// throw e;
return "";
});
cache.set(svgUrl, promise);
promise.then((text) => {
Expand Down
13 changes: 7 additions & 6 deletions packages/svelte-ux/src/lib/components/TextField.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
import Button from './Button.svelte';
import Icon from './Icon.svelte';
import Input from './Input.svelte';
import { type IconInput, asIconData } from '$lib/utils/icons';
type InputValue = string | number;
const dispatch = createEventDispatcher<{
clear: null;
change: { value: typeof value; inputValue: InputValue; operator: string };
change: { value: typeof value; inputValue: InputValue; operator?: string };
}>();
export let name: string | undefined = undefined;
Expand All @@ -45,8 +46,8 @@
export let base = false;
export let rounded = false;
export let dense = false;
export let icon: string | null = null;
export let iconRight: string | null = null;
export let icon: IconInput = null;
export let iconRight: IconInput = null;
export let align: 'left' | 'center' | 'right' = 'left';
export let autofocus: boolean | Parameters<typeof autoFocus>[1] = false;
// TODO: Find way to conditionally set type based on `multiline` value
Expand Down Expand Up @@ -176,7 +177,7 @@
$: hasInputValue = inputValue != null && inputValue !== '';
$: hasInsetLabel = ['inset', 'float'].includes(labelPlacement) && label !== '';
$: hasPrepend = $$slots.prepend || icon != null;
$: hasPrepend = $$slots.prepend || !!icon;
$: hasAppend =
$$slots.append || iconRight != null || clearable || error || operators || type === 'password';
$: hasPrefix = $$slots.prefix || type === 'currency';
Expand Down Expand Up @@ -247,7 +248,7 @@
<slot name="prepend" />
{#if icon}
<span class="mr-3">
<Icon path={icon} class="text-black/50" />
<Icon data={asIconData(icon)} class="text-black/50" />
</span>
{/if}
</div>
Expand Down Expand Up @@ -417,7 +418,7 @@
{#if error}
<Icon path={mdiInformationOutline} class="text-red-500" />
{:else if iconRight}
<Icon path={iconRight} class="text-black/50" />
<Icon data={asIconData(iconRight)} class="text-black/50" />
{/if}
</div>
{/if}
Expand Down
15 changes: 15 additions & 0 deletions packages/svelte-ux/src/lib/utils/icons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

import type { ComponentProps } from "svelte";
import type { default as Icon } from "$lib/components/Icon.svelte";
import { isLiteralObject } from "./object";

export type IconInput = ComponentProps<Icon>['data'] | ComponentProps<Icon>;
export type IconData = ComponentProps<Icon>['data'];

export function asIconData(v: IconInput): IconData {
return isIconComponentProps(v) ? v.data : v;
}

function isIconComponentProps(v: any): v is ComponentProps<Icon> {
return isLiteralObject(v) && typeof(v['iconName']) === "undefined";
}

0 comments on commit ee76171

Please sign in to comment.