You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
76 lines
2.3 KiB
76 lines
2.3 KiB
<script lang="ts">
|
|
/**
|
|
* The owner surface of a coordinated list. It creates the `PresenceGroup`
|
|
* (with the `when` that a real morfo would declare) and publishes it in
|
|
* context, then renders its child items — which discover the group via
|
|
* context (RFC §7.1) and register. The group sequences enter/exit per `when`
|
|
* and, on exit, RETAINS this container's DOM until the items have left (§8.1).
|
|
*/
|
|
import { untrack } from 'svelte';
|
|
import { Presence } from '$soma/layers/presence.svelte';
|
|
import { PresenceGroup, type PresenceWhen } from '$soma/layers/presence-group';
|
|
import { readableActive } from '$libs/reactive';
|
|
import { ActiveEidos } from '$uix/eidos';
|
|
import CoordItem from './coord-item.svelte';
|
|
|
|
let {
|
|
enter,
|
|
exit,
|
|
open,
|
|
count = 5
|
|
}: { enter: PresenceWhen; exit: PresenceWhen; open: boolean; count?: number } = $props();
|
|
|
|
const eidos = ActiveEidos.require();
|
|
// `enter`/`exit` are fixed per instance (a real morfo declares them once); read
|
|
// them untracked so creating the group doesn't pretend to react to a changing prop.
|
|
const group = untrack(() => PresenceGroup.create({ when: { enter, exit }, dom: eidos.dom }));
|
|
let containerEl = $state<HTMLElement | null>(null);
|
|
const container = new Presence({
|
|
dom: eidos.dom,
|
|
open: readableActive(() => open),
|
|
ref: readableActive(() => containerEl),
|
|
group,
|
|
groupRole: 'owner'
|
|
});
|
|
|
|
const items = $derived(Array.from({ length: count }, (_, i) => i));
|
|
</script>
|
|
|
|
{#if container.isPresent}
|
|
<div class="coord-container" bind:this={containerEl} {...container.transitionAttrs}>
|
|
{#each items as i (i)}
|
|
<CoordItem {open} index={i} />
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
|
|
<style>
|
|
.coord-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.4rem;
|
|
padding: 0.6rem;
|
|
border-radius: 12px;
|
|
background: var(--color-surface-overlay, rgba(99, 102, 241, 0.08));
|
|
border: 1px solid var(--color-border-subtle, rgba(0, 0, 0, 0.12));
|
|
opacity: 1;
|
|
transform: scale(1);
|
|
transition:
|
|
opacity 0.3s ease,
|
|
transform 0.3s ease;
|
|
}
|
|
.coord-container[data-starting-style] {
|
|
opacity: 0;
|
|
transform: scale(0.97);
|
|
}
|
|
.coord-container[data-ending-style] {
|
|
animation: coord-container-out 0.35s ease forwards;
|
|
}
|
|
@keyframes coord-container-out {
|
|
to {
|
|
opacity: 0;
|
|
transform: scale(0.96);
|
|
}
|
|
}
|
|
</style>
|