Bumps all 47 packages, the root manifest and the VS Code extension to 0.8.6, and rebuilds the editor compiler, language server and extension bundles that embed the version. The release carries the output delivery fix: camelCase outputs now reach parent bindings, and 18 components emit through output.* instead of hand-built CustomEvents. See the 0.8.6 migration entry for what changes for consumers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
226 lines
6.6 KiB
Plaintext
226 lines
6.6 KiB
Plaintext
// LayoutSplitter -- two panes with a divider the reader can move.
|
|
//
|
|
// <LayoutSplitter size={40} minSize={20}>
|
|
// <div data-slot="start">...</div>
|
|
// <div data-slot="end">...</div>
|
|
// </LayoutSplitter>
|
|
//
|
|
// The dragging lives in the reactive runtime behind data-wrn-splitter. Pointer
|
|
// moves fire far too often to route through a client function, and a state
|
|
// write made inside a pointermove callback is dropped, so the resolved size is
|
|
// held in the DOM as the --wrn-split custom property and these styles read it.
|
|
//
|
|
// This component previously declared resizeStart, resize and resizeEnd with no
|
|
// pointer handling whatsoever: a caller wired up @resize and received nothing,
|
|
// for ever, with no error.
|
|
//
|
|
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
|
// and silently swallows the rule that follows it.
|
|
component LayoutSplitter {
|
|
outputs {
|
|
// Named sizeChange rather than resize so it cannot be confused with the
|
|
// native window event a caller may already be listening for. An earlier
|
|
// comment here claimed a natively-named output could never reach a parent
|
|
// binding; that was wrong, and the rename was never what fixed anything.
|
|
sizeChange(payload: { size: number })
|
|
}
|
|
|
|
props {
|
|
color: string = "primary"
|
|
size: number = 50
|
|
orientation: string = "horizontal"
|
|
// Smallest share either pane may take, as a percentage. It also fixes the
|
|
// upper bound at 100 - minSize, so neither pane can be dragged away to
|
|
// nothing and left unrecoverable by pointer.
|
|
minSize: number = 15
|
|
step: number = 5
|
|
label: string = "Resize panels"
|
|
class: string = ""
|
|
}
|
|
|
|
functions {
|
|
// The runtime resolves the size and announces it; this turns that into the
|
|
// declared output so a parent @resize binding receives it.
|
|
client function reportResize(sourceEvent) {
|
|
var detail = sourceEvent ? sourceEvent.detail : null
|
|
if (!detail) {
|
|
return
|
|
}
|
|
output.sizeChange({ size: detail.size })
|
|
}
|
|
|
|
shared function isVertical() {
|
|
return orientation === "vertical"
|
|
}
|
|
|
|
shared function lowerBound() {
|
|
var value = Number(minSize)
|
|
if (!value || value < 0) {
|
|
return 15
|
|
}
|
|
return Math.min(45, value)
|
|
}
|
|
|
|
shared function upperBound() {
|
|
return 100 - lowerBound()
|
|
}
|
|
|
|
// Sizes arrive as HTML attributes, so a value outside the bounds is
|
|
// routine rather than exceptional. Clamp instead of rendering something
|
|
// the reader cannot undo.
|
|
shared function currentSize() {
|
|
var value = Number(size)
|
|
if (!value || value < 0) {
|
|
return 50
|
|
}
|
|
return Math.min(upperBound(), Math.max(lowerBound(), value))
|
|
}
|
|
|
|
shared function stepSize() {
|
|
var value = Number(step)
|
|
return value && value > 0 ? value : 5
|
|
}
|
|
}
|
|
|
|
view {
|
|
<div
|
|
{...attrs}
|
|
data-ui-component="LayoutSplitter"
|
|
class='wire-splitter {class}'
|
|
data-color='{color}'
|
|
data-orientation='{orientation}'
|
|
data-wrn-splitter='{isVertical() ? "vertical" : "horizontal"}'
|
|
data-wrn-splitter-min='{lowerBound()}'
|
|
data-wrn-splitter-step='{stepSize()}'
|
|
style='--wrn-split:{currentSize()}%;'
|
|
@wrnexus:splitter:resize='reportResize(event)'
|
|
>
|
|
<div class="wire-splitter__pane wire-splitter__pane--start">
|
|
<slot name="start"></slot>
|
|
</div>
|
|
|
|
<div
|
|
class="wire-splitter__handle"
|
|
data-wrn-splitter-handle="true"
|
|
role="separator"
|
|
tabindex="0"
|
|
aria-label='{label}'
|
|
aria-orientation='{isVertical() ? "horizontal" : "vertical"}'
|
|
aria-valuenow='{currentSize()}'
|
|
aria-valuemin='{lowerBound()}'
|
|
aria-valuemax='{upperBound()}'
|
|
>
|
|
<span class="wire-splitter__grip" aria-hidden="true"></span>
|
|
</div>
|
|
|
|
<div class="wire-splitter__pane wire-splitter__pane--end">
|
|
<slot name="end"></slot>
|
|
</div>
|
|
|
|
<slot />
|
|
</div>
|
|
}
|
|
|
|
style {
|
|
.wire-splitter {
|
|
--splitter-accent: var(--wire-color-primary);
|
|
display: grid;
|
|
/* The first track follows the size the runtime resolves. */
|
|
grid-template-columns: var(--wrn-split, 50%) auto minmax(0, 1fr);
|
|
align-items: stretch;
|
|
width: 100%;
|
|
min-width: 0;
|
|
}
|
|
|
|
.wire-splitter[data-color="secondary"] {
|
|
--splitter-accent: var(--wire-color-secondary);
|
|
}
|
|
|
|
.wire-splitter[data-color="success"] {
|
|
--splitter-accent: var(--wire-color-success);
|
|
}
|
|
|
|
.wire-splitter[data-color="danger"] {
|
|
--splitter-accent: var(--wire-color-danger);
|
|
}
|
|
|
|
.wire-splitter[data-color="info"] {
|
|
--splitter-accent: var(--wire-color-info);
|
|
}
|
|
|
|
.wire-splitter[data-orientation="vertical"] {
|
|
grid-template-columns: minmax(0, 1fr);
|
|
grid-template-rows: var(--wrn-split, 50%) auto minmax(0, 1fr);
|
|
}
|
|
|
|
.wire-splitter__pane {
|
|
min-width: 0;
|
|
min-height: 0;
|
|
overflow: auto;
|
|
}
|
|
|
|
.wire-splitter__handle {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex: 0 0 auto;
|
|
padding: 0 0.25rem;
|
|
border: 0;
|
|
background: transparent;
|
|
cursor: col-resize;
|
|
/* Without this the pointer drag selects the text in both panes. */
|
|
touch-action: none;
|
|
user-select: none;
|
|
}
|
|
|
|
.wire-splitter[data-orientation="vertical"] .wire-splitter__handle {
|
|
padding: 0.25rem 0;
|
|
cursor: row-resize;
|
|
}
|
|
|
|
.wire-splitter__grip {
|
|
display: block;
|
|
width: 2px;
|
|
height: 100%;
|
|
min-height: 1.5rem;
|
|
border-radius: 999px;
|
|
background: var(--wire-color-border);
|
|
transition: background 140ms ease;
|
|
}
|
|
|
|
.wire-splitter[data-orientation="vertical"] .wire-splitter__grip {
|
|
width: 100%;
|
|
min-width: 1.5rem;
|
|
height: 2px;
|
|
}
|
|
|
|
.wire-splitter__handle:hover .wire-splitter__grip,
|
|
.wire-splitter[data-wrn-splitter-dragging="true"] .wire-splitter__grip {
|
|
background: var(--splitter-accent);
|
|
}
|
|
|
|
.wire-splitter__handle:focus-visible {
|
|
outline: 2px solid var(--splitter-accent);
|
|
outline-offset: -2px;
|
|
border-radius: var(--wire-radius-sm);
|
|
}
|
|
|
|
/*
|
|
* Two panes side by side stop making sense on a phone. Stacking them keeps
|
|
* both readable, and the divider stops being draggable because the grid
|
|
* no longer has a second track to trade against.
|
|
*/
|
|
@media (max-width: 639px) {
|
|
.wire-splitter,
|
|
.wire-splitter[data-orientation="vertical"] {
|
|
grid-template-columns: minmax(0, 1fr);
|
|
grid-template-rows: auto auto auto;
|
|
}
|
|
|
|
.wire-splitter__handle {
|
|
cursor: default;
|
|
}
|
|
}
|
|
}
|
|
}
|