studio-engine 0.54.0 → 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +62 -0
- data/app/assets/tailwind/studio_engine/engine-motion.css +111 -18
- data/app/views/studio/profiles/_birthday_fields.html.erb +26 -4
- data/app/views/studio/profiles/_birthday_picker_script.html.erb +57 -5
- data/app/views/style/_tricks.html.erb +10 -4
- data/lib/studio/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 203b2f8cf1001d19c7ffb24c778ab0f1bdcb0b9aafa59762ded0ea5689eb2423
|
|
4
|
+
data.tar.gz: a6dbf4164a79cd6b5de2a9cbee178e5455e55c619dfa7197f0db08ba398aaf5b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0bb5c9bebe4977fef13ab139729ebbc5c560b52f31e573d153e63a00a8a40b970519ac783ad51b45773661e182f5dc848e424b4922e5748117c29560fdfc2b20
|
|
7
|
+
data.tar.gz: 4e16f3663bb43a61139cac420af9a2c265f91c5c5a6f708c32edf1ad5d1e92c6a559b545b148b4e134afe90d614a81de527906988a122b8326b9ae9ef881e6be
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,68 @@ The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This pro
|
|
|
6
6
|
|
|
7
7
|
### Added
|
|
8
8
|
|
|
9
|
+
- **`.studio-team-glow` rings a host that paints its own background**, and can
|
|
10
|
+
carry **two colors**.
|
|
11
|
+
|
|
12
|
+
The ring previously needed an opaque CHILD in front to cover its middle,
|
|
13
|
+
because both wedge layers are pseudo-elements at `z-index: -1 / -2` and CSS
|
|
14
|
+
paints an element's background BEFORE its negative-z descendants. A live board
|
|
15
|
+
card cannot supply that child: Turbo targets the card element itself for
|
|
16
|
+
replace and remove, so a wrapper host is orphaned every time a card leaves the
|
|
17
|
+
board. The primitive now cuts the host's own box out of both layers
|
|
18
|
+
(`padding` + `mask-composite`, the trick §1's `.studio-border-glow` already
|
|
19
|
+
used), so the ring works on the card directly.
|
|
20
|
+
|
|
21
|
+
**No-op for the wrapper shape** — its child was already covering that
|
|
22
|
+
interior. Verified side by side against both live consumers
|
|
23
|
+
(`style/_modal_specimen` and the hub's release-phase-meter): pixel-equivalent
|
|
24
|
+
before and after.
|
|
25
|
+
|
|
26
|
+
`--studio-team-glow-color-b` colors the SECOND of the two opposed wedges and
|
|
27
|
+
defaults to the first, so one-color callers see no change and a caller with a
|
|
28
|
+
pair to show — a Pokémon's two types — gets one per wedge.
|
|
29
|
+
|
|
30
|
+
The bloom twin needed care: a mask applies AFTER a filter, so its box carries a
|
|
31
|
+
transparent border twice the blur radius and its wedges stop at the padding
|
|
32
|
+
box. Without that room the halo ends on a hard line at its own edge.
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- **`.conic-surface` keeps its wash inside its own box.** The wash was an
|
|
37
|
+
OVERSIZED pseudo (`inset: -50%`) spun by a `transform` keyframe, cut back to
|
|
38
|
+
the element by the host's `overflow: hidden`. That clip is not dependable: the
|
|
39
|
+
rotating child is composited, and a `backdrop-filter` in its compositing path
|
|
40
|
+
drops it. The `.surface-glass` specimen — a backdrop-filter child of the wash
|
|
41
|
+
itself — is exactly that path, and on admin/style in Chromium the wash escaped
|
|
42
|
+
as a rotated slab across BOTH conic specimens.
|
|
43
|
+
|
|
44
|
+
The element no longer moves. The pseudo sits at `inset: 0` with
|
|
45
|
+
`border-radius: inherit`, and the sweep animates a registered
|
|
46
|
+
`--conic-surface-angle` inside the gradient — the technique §9b already used.
|
|
47
|
+
Nothing extends past the box, the rounded corners come from the pseudo, and no
|
|
48
|
+
clip has to hold. Same picture, one fewer thing that can fail.
|
|
49
|
+
`overflow: hidden` stays for the host's CONTENT.
|
|
50
|
+
|
|
51
|
+
The trade, stated: a registered-property animation repaints on the main thread
|
|
52
|
+
where a transform rode the compositor. At the default 16s sweep that is cheap,
|
|
53
|
+
and §9b already pays it at 4s.
|
|
54
|
+
|
|
55
|
+
- **`.studio-team-glow` is demoed and documented as the wrapper it is.** Its two
|
|
56
|
+
wedge layers are pseudos at z-index -1/-2, and CSS paints an element's own
|
|
57
|
+
background BEFORE its negative-z descendants — so a background on the glow HOST
|
|
58
|
+
is covered BY the wedges, not the other way round, and the ring becomes a wash
|
|
59
|
+
across the whole card face. What covers the middle is an opaque CHILD, which
|
|
60
|
+
paints later; that is TM's `holo-wrap` / `holo-card` split, and what both real
|
|
61
|
+
consumers already ship.
|
|
62
|
+
|
|
63
|
+
The §9b comment claimed the opposite ("host needs an opaque background"), and
|
|
64
|
+
the Effects specimen followed it, so the style guide showed two washed slabs
|
|
65
|
+
where the selection ring belongs. The specimen, its copyable snippet, and the
|
|
66
|
+
contract comment now all state the same thing. The primitive's geometry is
|
|
67
|
+
unchanged — the consumers tuned against it are untouched.
|
|
68
|
+
|
|
69
|
+
### Added
|
|
70
|
+
|
|
9
71
|
- **`studio/fields/_date_of_birth`** — the engine's one date-of-birth field,
|
|
10
72
|
three selects (Month / Day / Year). Rendered by
|
|
11
73
|
`studio/modals/blocks/_age_verify` and by `/profile/edit`'s birthday row.
|
|
@@ -507,26 +507,56 @@
|
|
|
507
507
|
}
|
|
508
508
|
|
|
509
509
|
/* =============================================================================
|
|
510
|
-
9b. studio-team-glow —
|
|
510
|
+
9b. studio-team-glow — rotating "selected card" ring glow, one or two colors
|
|
511
511
|
Ported from turf-monster's per-team selected-card glow (.holo-border-glow):
|
|
512
|
-
a
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
512
|
+
a conic-gradient ring rotates around the element's edge, with a blurred bloom
|
|
513
|
+
twin behind it. DISTINCT from .studio-border-glow (an animated RAINBOW border)
|
|
514
|
+
and .studio-glow (a static soft box-shadow halo) — this is the "this card is
|
|
515
|
+
selected / live" ring.
|
|
516
516
|
|
|
517
517
|
The color is a CSS var (default the CTA role, so purple on McRitchie Studio),
|
|
518
|
-
just like TM's per-team --glow-c.
|
|
518
|
+
just like TM's per-team --glow-c. TWO opposed wedges travel the edge, and they
|
|
519
|
+
can carry two colors: --studio-team-glow-color-b defaults to the first, so a
|
|
520
|
+
one-color caller sees no change, and a caller with a pair to show (a Pokémon's
|
|
521
|
+
two types) gets one per wedge. The wedges are swept by animating a registered
|
|
519
522
|
@property angle (rotating the GRADIENT, not the element — so no oversized-child
|
|
520
523
|
clipping trick is needed). Opacity is a var (--studio-team-glow-opacity) with a
|
|
521
524
|
transition, so a consumer can fade the glow in/out (the living style guide
|
|
522
525
|
slides it between step cards as a modal's step machine advances).
|
|
523
526
|
|
|
524
|
-
|
|
525
|
-
|
|
527
|
+
THE HOST MAY CARRY ITS OWN BACKGROUND — the ring cuts the host's box out of
|
|
528
|
+
both wedge layers, so only the band around the edge paints. That hole is what
|
|
529
|
+
makes the primitive work in the two shapes its consumers need:
|
|
530
|
+
|
|
531
|
+
· the WRAPPER shape (TM's holo-wrap / holo-card split) — a bare host with an
|
|
532
|
+
opaque child in front. The engine's style/_modal_specimen and the hub's
|
|
533
|
+
release-phase-meter both ship this. Unchanged by the hole: the child was
|
|
534
|
+
already covering that interior.
|
|
535
|
+
· the CARD shape — the host IS the opaque surface. This is what a live board
|
|
536
|
+
card needs, because it cannot take a wrapper at all: Turbo targets the card
|
|
537
|
+
element for both replace and remove, so a wrapper is orphaned every time a
|
|
538
|
+
card leaves the board.
|
|
539
|
+
|
|
540
|
+
Without the hole the card shape is impossible, and not for a tunable reason:
|
|
541
|
+
both layers are pseudo-elements at z-index -1 / -2, and CSS paints an element's
|
|
542
|
+
own background FIRST, then its negative-z descendants ON TOP of it (CSS 2.1
|
|
543
|
+
Appendix E, steps 1 and 2) — so a background on the host is covered BY the
|
|
544
|
+
wedges, and the ring becomes a wash across the whole face. Only a child, or
|
|
545
|
+
this hole, can cover the middle.
|
|
546
|
+
|
|
547
|
+
The host must still NOT be clipped by an overflow-hidden ancestor near its
|
|
548
|
+
border, or the ring is cut off with it.
|
|
526
549
|
|
|
527
550
|
Usage: <div class="studio-team-glow rounded-xl bg-surface p-5">Selected</div>
|
|
528
|
-
|
|
529
|
-
|
|
551
|
+
wrapper shape, when the face is its own element:
|
|
552
|
+
<div class="studio-team-glow rounded-xl">
|
|
553
|
+
<div class="rounded-xl bg-surface p-5">Selected</div>
|
|
554
|
+
</div>
|
|
555
|
+
red glow: style="--studio-team-glow-color: #E11D48"
|
|
556
|
+
two types: style="--studio-team-glow-color: #F08030;
|
|
557
|
+
--studio-team-glow-color-b: #6890F0"
|
|
558
|
+
Tune: --studio-team-glow-color / -color-b / -opacity / -speed / -thickness /
|
|
559
|
+
-bloom
|
|
530
560
|
========================================================================== */
|
|
531
561
|
@property --studio-team-glow-angle {
|
|
532
562
|
syntax: "<angle>";
|
|
@@ -535,6 +565,11 @@
|
|
|
535
565
|
}
|
|
536
566
|
.studio-team-glow {
|
|
537
567
|
--studio-team-glow-color: var(--color-cta);
|
|
568
|
+
/* The SECOND wedge's color. Defaults to the first, so a one-color caller sees
|
|
569
|
+
no change; a caller with two colors to show (a Pokémon's two types, a pair of
|
|
570
|
+
brand colors) gets one per wedge. Resolved on this element, so an inline
|
|
571
|
+
--studio-team-glow-color override feeds this default too. */
|
|
572
|
+
--studio-team-glow-color-b: var(--studio-team-glow-color);
|
|
538
573
|
--studio-team-glow-opacity: 1;
|
|
539
574
|
--studio-team-glow-speed: 4s;
|
|
540
575
|
--studio-team-glow-thickness: 4px;
|
|
@@ -559,14 +594,47 @@
|
|
|
559
594
|
var(--studio-team-glow-color) 12%,
|
|
560
595
|
transparent 33%,
|
|
561
596
|
transparent 50%,
|
|
562
|
-
var(--studio-team-glow-color) 62%,
|
|
597
|
+
var(--studio-team-glow-color-b) 62%,
|
|
563
598
|
transparent 83%
|
|
564
599
|
);
|
|
565
600
|
animation: studio-team-glow-spin var(--studio-team-glow-speed) linear infinite;
|
|
601
|
+
/* THE HOST'S OWN BOX IS PUNCHED OUT of both wedge layers, so what paints is the
|
|
602
|
+
band around the edge and nothing else. `padding` makes each layer's CONTENT
|
|
603
|
+
box exactly the host's box; mask-composite then removes that box from the
|
|
604
|
+
layer — the same mask trick §1's .studio-border-glow uses.
|
|
605
|
+
Why it is here: an opaque child in front covers the middle only when the host
|
|
606
|
+
CAN take a child. A live board card cannot — Turbo targets the card element
|
|
607
|
+
itself for replace and remove, so a wrapper is orphaned on every remove. With
|
|
608
|
+
the hole cut here the ring rides the card directly. For a wrapper host this
|
|
609
|
+
changes nothing visible: the child was already covering that interior. */
|
|
610
|
+
padding: var(--studio-team-glow-thickness);
|
|
611
|
+
-webkit-mask:
|
|
612
|
+
linear-gradient(#000 0 0) content-box,
|
|
613
|
+
linear-gradient(#000 0 0);
|
|
614
|
+
-webkit-mask-composite: xor;
|
|
615
|
+
mask:
|
|
616
|
+
linear-gradient(#000 0 0) content-box,
|
|
617
|
+
linear-gradient(#000 0 0);
|
|
618
|
+
mask-composite: exclude;
|
|
566
619
|
}
|
|
567
620
|
.studio-team-glow::after {
|
|
568
|
-
/* Blurred bloom twin, a touch larger
|
|
569
|
-
|
|
621
|
+
/* Blurred bloom twin, a touch larger: its wedges reach 4px PAST the ring, which
|
|
622
|
+
is what makes the bloom read as a halo, not a soft edge on the ring. Drop the
|
|
623
|
+
4px and every WRAPPER consumer's ring visibly lightens (measured: 26% of the
|
|
624
|
+
pixels on style/_modal_specimen, 13% on the hub's release-phase-meter). The
|
|
625
|
+
box then carries a transparent BORDER of bloom-reach on top of that, and the
|
|
626
|
+
mask still holes out the host's box — so the blur
|
|
627
|
+
spreads outward into that reach and is cut where it has already faded, while
|
|
628
|
+
the inward half is cut at the host's edge (invisible: that edge is either the
|
|
629
|
+
card's own or the wrapper child's). Mask applies AFTER filter, which is why
|
|
630
|
+
the reach has to out-measure the blur — without it the halo ends on a hard
|
|
631
|
+
line at the layer's own box. */
|
|
632
|
+
inset: calc(-1 * (var(--studio-team-glow-thickness) + 4px + var(--studio-team-glow-bloom) * 2));
|
|
633
|
+
padding: calc(var(--studio-team-glow-thickness) + 4px);
|
|
634
|
+
border: calc(var(--studio-team-glow-bloom) * 2) solid transparent;
|
|
635
|
+
/* The wedges stop at the padding box (host + ring + the 4px); the border is empty
|
|
636
|
+
room the blur is allowed to spread into. */
|
|
637
|
+
background-clip: padding-box;
|
|
570
638
|
z-index: -2;
|
|
571
639
|
filter: blur(var(--studio-team-glow-bloom)) saturate(1.35);
|
|
572
640
|
}
|
|
@@ -604,12 +672,36 @@
|
|
|
604
672
|
11. conic-surface — slow rotating conic-gradient wash (authored fresh)
|
|
605
673
|
A subtle animated ambient background for hero panels / empty states: a conic
|
|
606
674
|
gradient sweeps between two themed stops (CTA -> surface by default) on a slow
|
|
607
|
-
rotation. The gradient lives on a ::before
|
|
608
|
-
it stays crisp.
|
|
675
|
+
rotation. The gradient lives on a ::before behind the content, so content kept
|
|
676
|
+
above it stays crisp.
|
|
677
|
+
|
|
678
|
+
THE WASH IS CONTAINED BY CONSTRUCTION, NOT BY A CLIP. The first cut rotated an
|
|
679
|
+
OVERSIZED child (`inset: -50%` + `transform: rotate`) and leaned on the host's
|
|
680
|
+
`overflow: hidden` to cut it back to the box. That clip is not dependable: the
|
|
681
|
+
rotating child is composited, and a `backdrop-filter` anywhere in its
|
|
682
|
+
compositing path (its OWN .surface-glass child on the style guide, or an
|
|
683
|
+
unrelated overlay elsewhere on the page) drops the clip — the wash then paints
|
|
684
|
+
outside the box as a rotated slab. Observed on admin/style in Chromium, on both
|
|
685
|
+
conic specimens, with the .surface-glass demo the reliable trigger.
|
|
686
|
+
|
|
687
|
+
So the element never moves. The ::before sits at `inset: 0` with
|
|
688
|
+
`border-radius: inherit`, and the sweep animates a registered @property angle
|
|
689
|
+
inside the gradient — the same technique §9b uses. Nothing extends past the
|
|
690
|
+
box, the rounded corners come from the pseudo itself, and no clip has to hold.
|
|
691
|
+
`overflow: hidden` stays for the host's CONTENT, not for the wash.
|
|
692
|
+
|
|
693
|
+
The cost of the trade, stated: a registered-property animation repaints the
|
|
694
|
+
gradient on the main thread each frame, where a transform rode the compositor.
|
|
695
|
+
At the default 16s sweep that is cheap, and §9b already pays it at 4s.
|
|
609
696
|
|
|
610
697
|
Usage: <div class="conic-surface rounded-2xl p-8"> ...content... </div>
|
|
611
698
|
Tune: --conic-surface-from / --conic-surface-to / --conic-surface-duration
|
|
612
699
|
========================================================================== */
|
|
700
|
+
@property --conic-surface-angle {
|
|
701
|
+
syntax: "<angle>";
|
|
702
|
+
inherits: false;
|
|
703
|
+
initial-value: 0deg;
|
|
704
|
+
}
|
|
613
705
|
.conic-surface {
|
|
614
706
|
position: relative;
|
|
615
707
|
isolation: isolate;
|
|
@@ -619,10 +711,11 @@
|
|
|
619
711
|
.conic-surface::before {
|
|
620
712
|
content: "";
|
|
621
713
|
position: absolute;
|
|
622
|
-
inset:
|
|
714
|
+
inset: 0;
|
|
715
|
+
border-radius: inherit;
|
|
623
716
|
z-index: -1;
|
|
624
717
|
background: conic-gradient(
|
|
625
|
-
from
|
|
718
|
+
from var(--conic-surface-angle),
|
|
626
719
|
var(--conic-surface-from, var(--color-cta)),
|
|
627
720
|
var(--conic-surface-to, var(--color-surface)),
|
|
628
721
|
var(--conic-surface-from, var(--color-cta))
|
|
@@ -631,7 +724,7 @@
|
|
|
631
724
|
}
|
|
632
725
|
|
|
633
726
|
@keyframes conic-surface-spin {
|
|
634
|
-
to {
|
|
727
|
+
to { --conic-surface-angle: 360deg; }
|
|
635
728
|
}
|
|
636
729
|
|
|
637
730
|
/* =============================================================================
|
|
@@ -27,8 +27,11 @@
|
|
|
27
27
|
JS — the three selects. `alpine` comes from the enclosing
|
|
28
28
|
studioProfileForm and is true only once that component has booted.
|
|
29
29
|
%>
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
<%# NO FIELD LABEL HERE, deliberately (operator's call, 2026-08-15). The row is
|
|
31
|
+
already titled "Birthday" by the section wrapper, and the three selects carry
|
|
32
|
+
their own MONTH / DAY / YEAR labels — a "Date of birth" line between the two
|
|
33
|
+
said the same thing a third time. The no-JS <input type="date"> keeps its id
|
|
34
|
+
so a label elsewhere could still point at it. %>
|
|
32
35
|
<%
|
|
33
36
|
y = user.birth_year
|
|
34
37
|
m = user.birth_month
|
|
@@ -48,8 +51,27 @@
|
|
|
48
51
|
<template x-if="alpine">
|
|
49
52
|
<div>
|
|
50
53
|
<%# The value the form actually submits. The selects write here; nothing
|
|
51
|
-
types into it.
|
|
52
|
-
|
|
54
|
+
types into it.
|
|
55
|
+
|
|
56
|
+
THE NAME IS BOUND, AND THAT IS THE WHOLE POINT. A partly-filled date
|
|
57
|
+
has no ISO value, and submitting a BLANK birthday is not "no change" to
|
|
58
|
+
this controller — it is DELETE, and it clears all three columns on
|
|
59
|
+
purpose (see #birthday_attributes: someone deleting their birthday
|
|
60
|
+
means it). Those two facts met in a data-loss bug: a stored Jan 31,
|
|
61
|
+
switch the month to February, onMonthChange blanks the 31st because
|
|
62
|
+
February has no 31st, and the form is now one Save away from wiping a
|
|
63
|
+
birthday the person never asked to delete.
|
|
64
|
+
|
|
65
|
+
On THIS path a blank is always an edit in progress and never an
|
|
66
|
+
intentional clear, because the selects offer no empty option — once a
|
|
67
|
+
part is set the user cannot unset it. The intentional clear lives on
|
|
68
|
+
the no-JS <input type="date"> above, where it is a real gesture.
|
|
69
|
+
|
|
70
|
+
So while the date is incomplete the field submits NOTHING: Alpine
|
|
71
|
+
removes an attribute bound to false, an input with no name is not
|
|
72
|
+
submitted, and the controller's `return {} if raw.nil?` makes an absent
|
|
73
|
+
param a no-op. The birthday is left exactly as it was. %>
|
|
74
|
+
<input type="hidden" :name="complete ? 'profile[birthday]' : false" :value="isoValue">
|
|
53
75
|
|
|
54
76
|
<%= render "studio/fields/date_of_birth" %>
|
|
55
77
|
</div>
|
|
@@ -82,12 +82,64 @@
|
|
|
82
82
|
this.publish();
|
|
83
83
|
},
|
|
84
84
|
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
85
|
+
// WHAT THE DIRTY CHECK IS SHOWN, and why it is not isoValue.
|
|
86
|
+
//
|
|
87
|
+
// isoValue is "" for EVERY incomplete date, so publishing it made a whole
|
|
88
|
+
// class of edit invisible: on an empty birthday, choosing a month wrote ""
|
|
89
|
+
// over "" and the save bar never appeared. The operator reported exactly
|
|
90
|
+
// that. Choosing a month IS a change — the form just had no way to see it.
|
|
91
|
+
//
|
|
92
|
+
// So the signal is the three parts, which differ whenever any one of them
|
|
93
|
+
// does. It stays equal to isoValue for a COMPLETE date (same string, same
|
|
94
|
+
// shape) so a finished edit compares correctly against the value the page
|
|
95
|
+
// loaded with, and Discard restores it to that same string.
|
|
96
|
+
//
|
|
97
|
+
// `fields` belongs to the enclosing studioProfileForm — Alpine's scope
|
|
98
|
+
// chain makes it reachable and it is the SAME object, so assigning through
|
|
99
|
+
// it is what raises the save bar.
|
|
100
|
+
// THE EMPTY CASE IS NOT A PARTIAL ONE, and conflating them makes the page
|
|
101
|
+
// arrive already dirty. studioProfileForm's initial.birthday is "" for a
|
|
102
|
+
// person with no birthday on file, so an untouched empty field must also
|
|
103
|
+
// read "" — joining three blanks gives "--", which compares as a change and
|
|
104
|
+
// would raise the save bar on load, before anyone touched anything.
|
|
105
|
+
get signature() {
|
|
106
|
+
if (this.complete) return this.isoValue;
|
|
107
|
+
if (!this.year && !this.month && !this.day) return "";
|
|
108
|
+
return [this.year, this.month, this.day].join("-");
|
|
109
|
+
},
|
|
110
|
+
|
|
89
111
|
publish() {
|
|
90
|
-
if (this.fields) this.fields.birthday = this.
|
|
112
|
+
if (this.fields) this.fields.birthday = this.signature;
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// DISCARD MUST REACH THE SELECTS. studioProfileForm#discard reassigns its
|
|
116
|
+
// `fields` object, which restores every input bound with x-model — and the
|
|
117
|
+
// three selects are not, because they are bound to THIS component's own
|
|
118
|
+
// month/day/year. So Discard used to leave them showing the abandoned edit
|
|
119
|
+
// while the bar disappeared, and the hidden input went on carrying it: the
|
|
120
|
+
// next save would write the change the user had just discarded.
|
|
121
|
+
//
|
|
122
|
+
// Watching `fields.birthday` is what joins them. Discard sets it back to
|
|
123
|
+
// the initial string, this fires, and the selects follow — and because the
|
|
124
|
+
// guard below returns early when the value already matches, the publish →
|
|
125
|
+
// watch → publish path cannot loop.
|
|
126
|
+
init() {
|
|
127
|
+
var self = this;
|
|
128
|
+
this.publish();
|
|
129
|
+
this.$watch("fields.birthday", function (value) {
|
|
130
|
+
if (value === self.signature) return;
|
|
131
|
+
self.adopt(value);
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
// Takes an ISO date (or a blank) and puts the selects on it. Anything that
|
|
136
|
+
// is not a full date empties all three, which is what an initial blank and
|
|
137
|
+
// a cleared birthday both mean.
|
|
138
|
+
adopt(value) {
|
|
139
|
+
var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(value || ""));
|
|
140
|
+
this.year = m ? String(+m[1]) : "";
|
|
141
|
+
this.month = m ? String(+m[2]) : "";
|
|
142
|
+
this.day = m ? String(+m[3]) : "";
|
|
91
143
|
}
|
|
92
144
|
};
|
|
93
145
|
};
|
|
@@ -272,7 +272,7 @@
|
|
|
272
272
|
<h3 class="text-xl font-bold text-heading">Effects</h3>
|
|
273
273
|
<p class="text-muted text-sm">
|
|
274
274
|
The <code class="font-mono text-2xs">engine-motion.css</code> visual-effect primitives beyond motion -
|
|
275
|
-
gradient text, a soft glow halo, a
|
|
275
|
+
gradient text, a soft glow halo, a one- or two-color rotating <strong>selection glow</strong>
|
|
276
276
|
(ported from Turf Monster's selected-team-card ring), frosted glass, and a conic wash. Each is
|
|
277
277
|
themed through the role tokens and tunable via CSS-var knobs.
|
|
278
278
|
</p>
|
|
@@ -297,12 +297,18 @@
|
|
|
297
297
|
</div>
|
|
298
298
|
<% end %>
|
|
299
299
|
|
|
300
|
+
<%# The CARD shape: the host carries its own background and the ring still
|
|
301
|
+
rings, because the primitive punches the host's box out of both wedge
|
|
302
|
+
layers. The WRAPPER shape (bare host + opaque child) is staged by the
|
|
303
|
+
Modals section's glow cards — both are documented in engine-motion.css
|
|
304
|
+
§9b. Two wedges travel the edge, so a pair of colors reads as a pair. %>
|
|
300
305
|
<%= render layout: "style/specimen",
|
|
301
|
-
locals: { klass: ".studio-team-glow (
|
|
302
|
-
usage: %(<div class="studio-team-glow rounded-xl bg-surface p-5">Selected</div>\n<div class="studio-team-glow rounded-xl bg-surface p-5"
|
|
306
|
+
locals: { klass: ".studio-team-glow (knobs: --studio-team-glow-color / -color-b)",
|
|
307
|
+
usage: %(<div class="studio-team-glow rounded-xl bg-surface p-5">Selected</div>\n<div class="studio-team-glow rounded-xl bg-surface p-5"\n style="--studio-team-glow-color: #F08030; --studio-team-glow-color-b: #6890F0">Fire / Flying</div>) } do %>
|
|
303
308
|
<div class="flex flex-wrap items-center justify-center gap-6">
|
|
304
309
|
<div class="studio-team-glow rounded-xl bg-surface border border-subtle px-5 py-4 text-sm font-semibold text-heading text-center">Selected</div>
|
|
305
|
-
<div class="studio-team-glow rounded-xl bg-surface border border-subtle px-5 py-4 text-sm font-semibold text-heading text-center"
|
|
310
|
+
<div class="studio-team-glow rounded-xl bg-surface border border-subtle px-5 py-4 text-sm font-semibold text-heading text-center"
|
|
311
|
+
style="--studio-team-glow-color: #F08030; --studio-team-glow-color-b: #6890F0">Fire / Flying</div>
|
|
306
312
|
</div>
|
|
307
313
|
<% end %>
|
|
308
314
|
|
data/lib/studio/version.rb
CHANGED