Skip to content

Commit

Permalink
chore(wip): version using ::before
Browse files Browse the repository at this point in the history
  • Loading branch information
dvcol committed Nov 9, 2024
1 parent 6919204 commit 11adc0e
Show file tree
Hide file tree
Showing 12 changed files with 206 additions and 53 deletions.
15 changes: 9 additions & 6 deletions demo/components/DemoTabs.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
import NeoTab from '~/nav/NeoTab.svelte';
import NeoTabs from '~/nav/NeoTabs.svelte';
const { onClick, loading: loading$, onLoading, skeleton: skeleton$, onSkeleton } = useButtonState('DemoTabsClicked');
const { onClick, loading: loading$, onLoading } = useButtonState('DemoTabsClicked');
const loading = $derived.by(loading$);
const skeleton = $derived.by(skeleton$);
let skeleton = $state(false);
const { matches } = useWatchMedia('(max-width: 1550px)');
const vertical = $derived.by(matches);
Expand All @@ -36,12 +36,14 @@
let active: unknown | undefined = $state('button');
let value: unknown | undefined = $state('button');
const onChange = (id: TabId, _value?: unknown) => {
const onChange = (id?: TabId, _value?: unknown) => {
active = id;
value = _value;
};
const options = $state({ disabled: false, close: true, add: true, slide: false });
const onClear = () => onChange();
const options = $state({ disabled: false, close: true, add: true, slide: true });
const columns = [
{ label: 'Default' },
Expand All @@ -63,13 +65,12 @@
<NeoTab tabId="icon" value="icon" {loading} close={false} onclick={onLoading} {icon} />
<NeoTab tabId="icon-label" value="icon-label" close={false} onclick={onClick} {icon}>Icon</NeoTab>
<NeoTab tabId="reversed" value="reversed" reverse close={false} onclick={onClick} {icon}>Reversed</NeoTab>
<NeoTab tabId="skeleton" value="skeleton" {skeleton} close={false} onclick={onSkeleton}>Skeleton</NeoTab>
{/snippet}

{#snippet group(props: NeoButtonGroupContext = {})}
<NeoTabs bind:active onchange={onChange} {vertical} {skeleton} {onclose} {onadd} {...options} {...props}>
{@render buttons()}
{#each added as { text, ...tab }}
{#each added as { text, ...tab } (tab.tabId)}
<NeoTab {...tab}>{text}</NeoTab>
{/each}
</NeoTabs>
Expand All @@ -87,6 +88,8 @@
<NeoButton toggle bind:checked={options.add}>Add</NeoButton>
<NeoButton toggle bind:checked={options.close}>Close</NeoButton>
<NeoButton toggle bind:checked={options.slide}>Slide</NeoButton>
<NeoButton toggle bind:checked={skeleton}>Skeleton</NeoButton>
<NeoButton onclick={onClear}>Clear</NeoButton>
</NeoButtonGroup>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/lib/buttons/NeoButton.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@
color: var(--neo-btn-text-color, inherit);
text-decoration: inherit;
background-color: var(--neo-btn-bg-color, var(--background-color));
border: 1px var(--neo-btn-border-color, transparent) solid;
border: var(--border-width, 1px) var(--neo-btn-border-color, transparent) solid;
border-radius: var(--neo-btn-border-radius, var(--border-radius));
box-shadow: var(--box-shadow-raised-2);
cursor: pointer;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/buttons/NeoButtonGroup.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
margin: 0.25rem;
padding: 0.25rem;
background-color: var(--neo-btn-bg-color, var(--background-color));
border: 1px var(--neo-btn-border-color, transparent) solid;
border: var(--border-width, 1px) var(--neo-btn-border-color, transparent) solid;
border-radius: calc(var(--neo-btn-border-radius, var(--border-radius)) + 0.25rem);
transition:
color 0.3s ease,
Expand Down
29 changes: 24 additions & 5 deletions src/lib/nav/NeoTab.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<script lang="ts">
import { height, width } from '@dvcol/svelte-utils/transition';
import { untrack } from 'svelte';
import type { NeoTabProps } from '~/nav/neo-tab.model.js';
import NeoButton from '~/buttons/NeoButton.svelte';
import IconClose from '~/icons/IconClose.svelte';
import { getTabContext } from '~/nav/neo-tabs-context.svelte.js';
import { defaultTransitionDuration, enterTransition } from '~/utils/transition.utils.js';
const {
// Snippets
Expand All @@ -32,26 +35,29 @@
const disabled = $derived(rest.disabled || (rest.disabled !== false && context?.disabled));
const closeable = $derived(close || (close !== false && context?.closeable));
const transition = $derived(context?.vertical ? height : width);
const slide = $derived(context?.slide);
const onClick: NeoTabProps['onclick'] = (e: MouseEvent) => {
context?.onChange(tabId);
onclick?.(e);
};
let ref: HTMLDivElement | undefined;
$effect(() => {
context?.register(tabId, value);
if (!ref) return;
untrack(() => context?.register(tabId, ref!, value));
return () => context?.remove(tabId);
});
const onClose = (e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
onclose?.(tabId);
context?.onClose(tabId);
onclose?.(tabId, value, ref);
context?.onClose(tabId, value, ref);
};
</script>

<div class="neo-tab" transition:transition={{ duration: 200, css: `overflow: hidden; white-space: nowrap` }} {...tabProps}>
<div bind:this={ref} class="neo-tab" class:active class:slide transition:transition={enterTransition} {...tabProps}>
<NeoButton
role="tab"
data-tab-id={tabId}
Expand All @@ -66,7 +72,13 @@
>
{@render children?.({ active, tabId, value })}
{#if closeable}
<button class="neo-tab-close" class:reverse={rest.reverse} class:disabled transition:width={{ duration: 200 }} onclick={onClose}>
<button
class="neo-tab-close"
class:reverse={rest.reverse}
class:disabled
transition:width={{ duration: defaultTransitionDuration }}
onclick={onClose}
>
<IconClose class="icon-close" />
</button>
{/if}
Expand All @@ -83,6 +95,13 @@
opacity: 1;
}
}
&.slide {
:global(.neo-button:active .icon-close),
:global(.neo-button.pressed .icon-close) {
transition-delay: 0.3s;
}
}
}
.neo-tab-close {
Expand Down
116 changes: 97 additions & 19 deletions src/lib/nav/NeoTabs.svelte
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
<script lang="ts">
import { height, width } from '@dvcol/svelte-utils/transition';
import { untrack } from 'svelte';
import type { 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 { setTabContext } from '~/nav/neo-tabs-context.svelte.js';
import { useTimeout } from '~/utils/timeout.util.js';
/* eslint-disable prefer-const -- necessary for binding checked */
let {
Expand Down Expand Up @@ -34,18 +37,47 @@
/* eslint-enable prefer-const */
// reflect context active to component
const onChange: OnChange = (_tabId, _value) => {
const onChange: OnChange = (_tabId, _value, _ref) => {
active = _tabId;
onchange?.(_tabId, _value);
onchange?.(_tabId, _value, _ref);
};
const context = setTabContext(active, { onChange, onClose: onclose });
const transition = $derived(rest.vertical ? height : width);
// to allow time for the box-shadow enter transition (0.3s)
let delay = $state();
const { start, immediate } = useTimeout(() => {
delay = active;
}, 300);
$effect.pre(() => {
if (!slide) return;
if (!active) return immediate();
start();
});
// to allow time for the box-shadow exit transition (0.3s)
let position = $state();
const { immediate: setPosition, start: clearPosition } = useTimeout((val = '') => {
position = val;
}, 300);
$effect.pre(() => {
if (!slide) return;
const { top: _top, left: _left, width: _width, height: _height } = context.position;
if (!_width || !_height) return clearPosition();
setPosition(
`--neo-tab-active-left: ${_left}px; --neo-tab-active-top: ${_top}px; --neo-tab-active-width: ${_width}px; --neo-tab-active-height: ${_height}px`,
);
});
const style = $derived([tabsProps?.style, position].filter(Boolean).join('; '));
// reflect component active to context
$effect(() => {
if (active === context.active) return;
context.onChange(active);
untrack(() => context.onChange(active));
});
$effect(() => {
Expand All @@ -57,7 +89,20 @@
<IconAdd />
{/snippet}

<div class="neo-tabs" class:add class:slide class:flat={rest.flat} class:text={rest.text} class:vertical={rest.vertical} {...tabsProps}>
<div
class="neo-tabs"
class:add
class:slide
class:active
class:delay
class:position
class:flat={rest.flat}
class:text={rest.text}
class:vertical={rest.vertical}
class:rounded={rest.rounded}
{...tabsProps}
{style}
>
<NeoButtonGroup {...rest}>
{@render children?.({ active, disabled, slide, close, add, vertical: rest.vertical })}
{#if add}
Expand Down Expand Up @@ -103,26 +148,59 @@
}
&.slide {
position: relative;
:global(.neo-button-group) {
position: relative;
overflow: hidden;
}
:global(:has(.neo-tab.active[inert]).neo-button-group::before),
&:not(.active) :global(.neo-button-group::before),
:global(.neo-tab .neo-button) {
box-shadow: var(--box-shadow-flat);
}
&.text {
::after {
position: absolute;
bottom: 2px;
left: var(--neo-tab-active-left, 0);
width: var(--neo-tab-active-width, 1.5rem);
height: 2px;
background-color: var(--color-primary);
transition:
left 1s var(--transition-bezier),
bottom 1s var(--transition-bezier),
width 1s var(--transition-bezier);
content: '';
}
:global(.neo-tab .neo-button:hover) {
color: var(--neo-tab-hover-color, var(--text-color-hover));
}
:global(.neo-tab .neo-button:hover.pressed),
:global(.neo-tab .neo-button:hover:active) {
color: var(--neo-tab-hover-color-active, var(--text-color-hover-active));
}
:global(:has(.neo-tab[inert].active).neo-button-group::before) {
transition: box-shadow 0.1s ease;
}
&:not(.delay) :global(.neo-button-group::before) {
transition:
left 0.3s var(--transition-bezier),
top 0.3s var(--transition-bezier),
box-shadow 0.3s ease;
}
:global(.neo-button-group::before) {
position: absolute;
top: calc(var(--neo-tab-active-top, 0) - var(--border-width, 1px));
left: calc(var(--neo-tab-active-left, 0) - var(--border-width, 1px));
z-index: var(--z-index-in-front, 1);
width: var(--neo-tab-active-width, 0);
height: var(--neo-tab-active-height, 0);
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-inset-2);
transition:
left 0.5s var(--transition-bezier),
top 0.5s var(--transition-bezier),
width 0.5s var(--transition-bezier),
height 0.5s var(--transition-bezier),
box-shadow 0.3s ease;
content: '';
pointer-events: none;
}
&.rounded :global(.neo-button-group::before) {
border-radius: var(--neo-tab-border-radius, var(--border-radius-lg));
}
}
}
Expand Down
Loading

0 comments on commit 11adc0e

Please sign in to comment.