Component Library: The Part System¶
NEO 1984, Component Library v1. This covers one thing only: what a "part" is, why the catalog needs it, and how it attaches to the existing schema.
Naming¶
Stu is currently calling this "custom animations/transitions/effects." That
name describes one consequence of the mechanism, not the mechanism itself.
Every animation, transition, and effect in the schema is already custom, that
part isn't new. What's actually new is that a component can expose named,
independently addressable sub elements, and each of those can carry its own
props, animations, effects, and transitions rather than sharing one set at
the whole layer level. The right name for that is a part. Call the field
parts, not customAnimations or similar, since props and effects live on
it too, not just animation.
What a part is¶
- Fixed per component, declared by the author. A component's parts are authored once, in code, the same way its properties are. An end user never invents a new part.
- A part is effectively a mini layer, scoped inside a component. It gets
tagged with the same type discriminant real layers already use
(
boxText,pointText,image,shape, and so on). A "text part" is aboxText/pointTextlayer living inside a component instead of inside a scene. This isn't a new taxonomy, it's the existing layer type system applied one level deeper, which means the inspector's existing per type field rendering already knows how to render a part's fields, it just needs pointing at the part's data instead of the layer's. - Every part carries the full set: props, animations, effects, and transitions. Not a subset. See the concrete example below for why this isn't optional.
Why this can't be whole layer only¶
Take the Betta storyboard frame "Black Friday: Sale On Now."
| Part | Needs independently |
|---|---|
| "Black Friday" panel | Colour, pulse on or off |
| "Up To 50% Off" panel | Colour, pulse on or off, separate from the panel above |
| "Sale On Now" text | Editable text, its own fade and slide entrance, no pulse at all |
The brief for this frame explicitly asks to "turn the animations on or off for each panel if they wish." If animation lived at the whole layer level, the two panels would have to pulse together or not at all. There is no way to give the user independent control over one without parts.
Schema shape¶
Additive, optional sibling field on the existing componentLayerSchema.
Nothing else changes shape.
componentLayerSchema {
...
parts?: Record<partId, {
props?: ...
animations?: AnimationsSchema
effects?: EffectsSchema
transitions?: TransitionsSchema
}>
}
Checked this against how the schema actually works before calling it safe:
binding-paths.tswalks a plainstring[]through nested objects with no assumption about depth or shape. A path like['parts', 'title', 'props', 'text']resolves through the exact same function as['props', 'text']does today. Every existing binding, none of which ever has apartssegment, is untouched.animations,effects, andtransitionsare already optional sibling fields on every layer viacommonLayerFields. Addingpartsas one more optional sibling is the same pattern used to add those fields in the first place. Old documents with nopartskey validate and render exactly as they do now.- The transition and animation resolution functions are pure, keyed only on what's passed to them. Existing call sites keep passing layer level data unchanged. A part scoped call site is a new caller, not a rewrite of the old one.
Net effect: no existing component, template, or manifest binding needs to
change. Only a component being newly built, or deliberately upgraded, ever
touches parts.
One geometry problem worth flagging now¶
When two parts need to pulse from one shared point (the visual centre of the
whole component) rather than each part's own centre, that's the standard
"scale around an external pivot" problem: set each part's transform-origin
to the component's centre expressed as an offset from that part's own top
left corner, rather than letting it default to the part's own middle. This
stays entirely inside a component's own render code. The shared parts
schema above doesn't need to know pivots exist at all.
Properties panel, briefly¶
Once a component has parts, the inspector needs to know which part it's currently showing fields for. v1 answer: inline, collapsible sections per part in the existing panel, with the component author declaring which fields are standard (main panel) versus advanced (tucked away), reusing the per layer type field rendering that already exists. A fully immersive "open this component" editing mode, where the canvas dims and you click directly into a part, is a later phase, not v1.
Open question for Stu¶
rsts-changes-for-stu.md already proposes grouping and pins, "these move
together," "pin this layer to that layer's edge." That's the layout
relationship half of what multi part components need. The two Black Friday
panels almost certainly need to stay aligned as they resize, which is a pins
problem, not an animation problem. Worth deciding whether pins are a
property of a part (defined here) or a separate mechanism, before both get
built without talking to each other.