Skip to content

Commit

Permalink
fix(buttons): fix line styling & rework action binding
Browse files Browse the repository at this point in the history
  • Loading branch information
dvcol committed Nov 10, 2024
1 parent 98ce6dc commit 46ff1b8
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 88 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ Neomorphic ui library for svelte 5

## TODO
- [x] Buttons
- @media any-pointer:coarse any-hover:none
- shallow
- [ ] @media any-pointer:coarse any-hover:none
- [x] Tabs
- @media any-pointer:coarse any-hover:none
- [ ] @media any-pointer:coarse any-hover:none
- [ ] tabs panels
- paper ?
- card
- border loading
Expand Down
29 changes: 15 additions & 14 deletions src/lib/buttons/NeoButton.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import type { NeoButtonProps } from '~/buttons/neo-button.model.js';
import IconCircleLoading from '~/icons/IconCircleLoading.svelte';
import { emptyFn } from '~/utils/action.utils.js';
import { toAction, toActionProps, toTransition, toTransitionProps } from '~/utils/action.utils.js';
/* eslint-disable prefer-const -- necessary for binding checked */
let {
Expand Down Expand Up @@ -42,15 +42,12 @@
onkeyup,
// Transition
in: inFn,
inProps,
out: outFn,
outProps,
transition: transitionFn,
transitionProps,
in: inAction,
out: outAction,
transition: transitionAction,
// Actions
use,
useProps,
// Other props
...rest
Expand Down Expand Up @@ -99,9 +96,13 @@
onkeyup?.(e);
};
const _inFn = $derived(inFn ?? transitionFn ?? emptyFn);
const _outFn = $derived(outFn ?? transitionFn ?? emptyFn);
const _useFn = $derived(use ?? (() => {}));
const inFn = $derived(toTransition(inAction ?? transitionAction));
const inProps = $derived(toTransitionProps(inAction ?? transitionAction));
const outFn = $derived(toTransition(outAction ?? transitionAction));
const outProps = $derived(toTransitionProps(outAction ?? transitionAction));
const useFn = $derived(toAction(use));
const useProps = $derived(toActionProps(use));
</script>

<svelte:element
Expand All @@ -123,9 +124,9 @@
class:rounded
class:rotate
class:empty
use:_useFn={useProps}
out:_outFn={outProps ?? transitionProps}
in:_inFn={inProps ?? transitionProps}
use:useFn={useProps}
out:outFn={outProps}
in:inFn={inProps}
onkeydown={onKeydownEnter}
onkeyup={onKeyUpEnter}
onclick={onClick}
Expand Down
28 changes: 17 additions & 11 deletions src/lib/buttons/NeoButtonGroup.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import type { NeoButtonGroup } from '~/buttons/neo-button-group.model.js';
import { emptyFn } from '~/utils/action.utils.js';
import { toAction, toActionProps, toTransition, toTransitionProps } from '~/utils/action.utils.js';
const {
// Snippets
Expand All @@ -23,19 +23,24 @@
vertical,
// Transition
in: inFn,
inProps,
out: outFn,
outProps,
transition: transitionFn,
transitionProps,
in: inAction,
out: outAction,
transition: transitionAction,
// Actions
use,
// Other props
...rest
}: NeoButtonGroup = $props();
const _inFn = $derived(inFn ?? transitionFn ?? emptyFn);
const _outFn = $derived(outFn ?? transitionFn ?? emptyFn);
const inFn = $derived(toTransition(inAction ?? transitionAction));
const inProps = $derived(toTransitionProps(inAction ?? transitionAction));
const outFn = $derived(toTransition(outAction ?? transitionAction));
const outProps = $derived(toTransitionProps(outAction ?? transitionAction));
const useFn = $derived(toAction(use));
const useProps = $derived(toActionProps(use));
</script>

<div
Expand All @@ -51,8 +56,9 @@
class:coalesce
class:skeleton
class:vertical
out:_outFn={outProps ?? transitionProps}
in:_inFn={inProps ?? transitionProps}
use:useFn={useProps}
out:outFn={outProps}
in:inFn={inProps}
{...rest}
>
{@render children?.({
Expand Down
31 changes: 18 additions & 13 deletions src/lib/nav/NeoTab.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import NeoButton from '~/buttons/NeoButton.svelte';
import IconClose from '~/icons/IconClose.svelte';
import { getTabContext } from '~/nav/neo-tabs-context.svelte.js';
import { toAction, toActionProps } from '~/utils/action.utils.js';
import { defaultTransitionDuration, enterTransition } from '~/utils/transition.utils.js';
const {
Expand Down Expand Up @@ -55,21 +56,23 @@
onclose?.(tabId, value, ref);
context?.onClose(tabId, value, ref);
};
const useFn = $derived(toAction(tabProps?.use));
const useProps = $derived(toActionProps(tabProps?.use));
</script>

<div bind:this={ref} class="neo-tab" class:active class:slide transition:transition={enterTransition} {...tabProps}>
<NeoButton
role="tab"
data-tab-id={tabId}
data-active={active}
toggle
readonly
checked={active}
onclick={onClick}
empty={!children}
{...rest}
{disabled}
>
<div
bind:this={ref}
class="neo-tab"
data-tab-id={tabId}
data-active={active}
class:active
class:slide
transition:transition={enterTransition}
{...tabProps}
use:useFn={useProps}
>
<NeoButton role="tab" toggle readonly checked={active} onclick={onClick} empty={!children} {...rest} {disabled}>
{@render children?.({ active, tabId, value })}
{#if closeable}
<button
Expand All @@ -93,6 +96,7 @@
:global(.neo-button:hover) {
:global(.icon-close) {
opacity: 1;
pointer-events: auto;
}
}
Expand Down Expand Up @@ -129,6 +133,7 @@
opacity 0.2s ease-in,
color 0.5s ease,
background-color 0.5s ease;
pointer-events: none;
}
&:focus-visible :global(.icon-close) {
Expand Down
95 changes: 70 additions & 25 deletions src/lib/nav/NeoTabs.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
import { untrack } from 'svelte';
import type { OnChange, TabsProps } from '~/nav/neo-tabs.model.js';
import type { NeoTabsContext, OnChange, TabsProps } from '~/nav/neo-tabs.model.js';
import NeoButton from '~/buttons/NeoButton.svelte';
import NeoButtonGroup from '~/buttons/NeoButtonGroup.svelte';
import IconAdd from '~/icons/IconAdd.svelte';
import { type NeoTabContextPositions, setTabContext } from '~/nav/neo-tabs-context.svelte.js';
import { toAction, toActionProps } from '~/utils/action.utils.js';
/* eslint-disable prefer-const -- necessary for binding checked */
let {
Expand Down Expand Up @@ -66,18 +67,20 @@
const style = $derived([tabsProps?.style, position].filter(Boolean).join('; '));
$inspect(position).with((...args) => console.info('position', ...args));
// reflect component active to context
$effect(() => {
console.info('active', active, context.active);
if (active === context.active) return;
untrack(() => context.onChange(active));
});
$effect(() => {
context.onOption({ slide, line, closeable: close, disabled, vertical: rest.vertical });
context.onOption({ line, slide, closeable: close, disabled, vertical: rest.vertical });
});
const childContext: NeoTabsContext = $derived({ active, disabled, slide, close, add, vertical: rest.vertical });
const useFn = $derived(toAction(tabsProps?.use));
const useProps = $derived(toActionProps(tabsProps?.use));
</script>

{#snippet icon()}
Expand All @@ -94,11 +97,13 @@
class:text={rest.text}
class:vertical={rest.vertical}
class:rounded={rest.rounded}
class:shallow={rest.shallow}
{...tabsProps}
use:useFn={useProps}
{style}
>
<NeoButtonGroup {...rest}>
{@render children?.({ active, disabled, slide, close, add, vertical: rest.vertical })}
{@render children?.(childContext)}
{#if add}
<div transition:transition={{ duration: 200, css: `overflow: hidden; white-space: nowrap` }}>
<NeoButton onclick={onadd} {icon} />
Expand Down Expand Up @@ -136,9 +141,27 @@
padding-bottom: 0.5rem;
}
&.line :global(.neo-tab.active::before) {
--neo-tab-full-width: 2px;
--neo-tab-old-width: 2px;
&.line {
:global(.neo-tab::before) {
--neo-tab-width: 2px;
--neo-tab-old-width: 2px;
--neo-tab-old-max-height: calc(var(--neo-tab-old-height, 100%) - 1rem);
--neo-tab-max-height: calc(var(--neo-tab-height, 100%) - 1rem);
top: 0;
width: 2px;
height: 0;
max-height: var(--neo-tab-max-height);
margin-left: 0.3rem;
transition:
box-shadow 0.3s ease,
height 0.3s var(--transition-bezier);
margin-block: 0.5rem;
}
:global(.neo-tab.active::before) {
height: var(--neo-tab-height, 100%);
}
}
}
Expand All @@ -147,11 +170,12 @@
}
&.slide {
--neo-tab-full-width: 100%;
--neo-tab-full-height: 100%;
--neo-tab-width: 100%;
--neo-tab-height: 100%;
:global(.neo-tab .neo-button) {
box-shadow: var(--box-shadow-flat);
color: var(--neo-btn-text-color, inherit);
box-shadow: var(--box-shadow-flat) !important;
transition: none;
}
Expand All @@ -165,8 +189,8 @@
top: calc(0 - var(--border-width, 1px));
left: calc(0 - var(--border-width, 1px));
z-index: var(--z-index-in-front, 1);
width: var(--neo-tab-full-width, 100%);
height: var(--neo-tab-full-height, 100%);
width: var(--neo-tab-width, 100%);
height: var(--neo-tab-height, 100%);
border: var(--border-width, 1px) var(--neo-tab-border-color, transparent) solid;
border-radius: var(--neo-tab-border-radius, var(--border-radius));
box-shadow: var(--box-shadow-flat);
Expand All @@ -178,17 +202,30 @@
&.line :global(.neo-tab.active::before) {
bottom: 0;
height: 2px;
background-color: var(--color-primary, var(--text-color));
box-shadow: var(--box-shadow-flat);
transition:
box-shadow 0.3s ease,
width 0.3s var(--transition-bezier);
}
&.line:not(.vertical) :global(.neo-tab.active::before) {
--neo-tab-full-height: 2px;
--neo-tab-old-height: 2px;
&.line:not(.vertical) {
:global(.neo-tab::before) {
--neo-tab-height: 2px;
--neo-tab-old-height: 2px;
--neo-tab-old-max-width: calc(var(--neo-tab-old-width, 100%) - 1.5rem);
--neo-tab-max-width: calc(var(--neo-tab-width, 100%) - 1.5rem);
width: 0;
max-width: var(--neo-tab-max-width);
height: 2px;
margin-bottom: 0.125rem;
transition:
box-shadow 0.3s ease,
width 0.3s var(--transition-bezier);
margin-inline: 0.75rem;
}
:global(.neo-tab.active::before) {
width: var(--neo-tab-width, 100%);
}
}
:global(.neo-tab.active::before) {
Expand All @@ -201,15 +238,19 @@
@keyframes slide {
0% {
width: var(--neo-tab-old-width, var(--neo-tab-full-width, 100%));
height: var(--neo-tab-old-height, var(--neo-tab-full-height, 100%));
width: var(--neo-tab-old-width, var(--neo-tab-width, 100%));
max-width: var(--neo-tab-old-max-width);
height: var(--neo-tab-old-height, var(--neo-tab-height, 100%));
max-height: var(--neo-tab-old-max-height);
box-shadow: var(--box-shadow-inset-2);
transform: var(--transform);
}
100% {
width: var(--neo-tab-full-width, 100%);
height: var(--neo-tab-full-height, 100%);
width: var(--neo-tab-width, 100%);
max-width: var(--neo-tab-max-width);
height: var(--neo-tab-height, 100%);
max-height: var(--neo-tab-max-height);
box-shadow: var(--box-shadow-inset-2);
transform: translate(0, 0);
}
Expand All @@ -225,6 +266,10 @@
}
}
&.shallow {
--box-shadow-inset-2: var(--box-shadow-inset-1);
}
&.rounded :global(.neo-button-group .neo-tab::before) {
border-radius: var(--neo-tab-border-radius, var(--border-radius-lg));
}
Expand Down
3 changes: 2 additions & 1 deletion src/lib/nav/neo-tab.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Snippet } from 'svelte';
import type { HTMLAttributes } from 'svelte/elements';
import type { NeoButtonProps } from '~/buttons/neo-button.model.js';
import type { OnChange } from '~/nav/neo-tabs.model.js';
import type { HTMLUseProps } from '~/utils/action.utils.js';

export type TabId = string | number | symbol;
export type NeoTabProps<T = unknown> = {
Expand Down Expand Up @@ -43,5 +44,5 @@ export type NeoTabProps<T = unknown> = {
/**
* Optional props to pass to the tab container.
*/
tabProps?: Partial<HTMLAttributes<HTMLDivElement>>;
tabProps?: Partial<HTMLAttributes<HTMLDivElement>> & HTMLUseProps;
} & Omit<NeoButtonProps, 'value' | 'children'>;
Loading

0 comments on commit 46ff1b8

Please sign in to comment.