janela 0.3.0 → 0.4.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 +25 -0
- data/LICENSE.txt +1 -1
- data/README.md +47 -6
- data/UPGRADING.md +37 -1
- data/app/assets/javascripts/janela/chart_controller.js +12 -6
- data/app/assets/javascripts/janela/frame_controller.js +140 -17
- data/app/assets/javascripts/janela/vitral_controller.js +263 -0
- data/app/assets/stylesheets/janela.css +1 -0
- data/app/assets/stylesheets/vitral.css +343 -0
- data/app/controllers/janela/application_controller.rb +7 -0
- data/app/helpers/janela/frames_helper.rb +2 -1
- data/app/models/janela/query.rb +18 -5
- data/app/views/janela/frames/_frame.html.erb +2 -1
- data/app/views/janela/queries/_query.html.erb +2 -2
- data/app/views/layouts/janela/application.html.erb +10 -1
- data/config/importmap.rb +1 -0
- data/config/locales/en.yml +1 -0
- data/docs/decisions/022-host-route-helpers-work-inside-the-engine.md +104 -0
- data/docs/decisions/023-vitral-is-a-theme-not-the-stylesheet.md +86 -0
- data/docs/decisions/024-selecting-more-than-one-value.md +127 -0
- data/docs/decisions/INDEX.md +4 -1
- data/docs/naming.md +172 -0
- data/lib/janela/engine.rb +10 -1
- data/lib/janela/host_routes.rb +31 -0
- data/lib/janela/version.rb +1 -1
- data/lib/janela.rb +7 -0
- metadata +13 -10
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/* Vitral: Janela's optional theme.
|
|
2
|
+
|
|
3
|
+
A vitral is a stained glass window, which is what this makes of a dashboard.
|
|
4
|
+
Each pane holds its own colour, dark leading runs between them, and the
|
|
5
|
+
light comes from behind rather than from a lamp in the room.
|
|
6
|
+
|
|
7
|
+
It is separate from janela.css on purpose. That one is structure: the grid
|
|
8
|
+
classes an analyst's numbers choose from, and enough style that a pane is
|
|
9
|
+
legible on install. This one is taste, and taste is the first thing a host
|
|
10
|
+
will want to replace (ADR 023).
|
|
11
|
+
|
|
12
|
+
<%= stylesheet_link_tag "janela" %>
|
|
13
|
+
<%= stylesheet_link_tag "vitral" %>
|
|
14
|
+
|
|
15
|
+
Order matters: vitral is written to sit on top of janela. Everything here
|
|
16
|
+
is a custom property or a janela-* class, so it touches nothing of yours.
|
|
17
|
+
Set the properties on :root to retheme it without editing this file, and
|
|
18
|
+
put class="vitral" on the element that should carry the light. */
|
|
19
|
+
|
|
20
|
+
:root {
|
|
21
|
+
--vitral-ink: #16202b;
|
|
22
|
+
--vitral-muted: #4a5766;
|
|
23
|
+
|
|
24
|
+
/* The light behind the window. */
|
|
25
|
+
--vitral-light-cobalt: rgba(54, 122, 235, 0.34);
|
|
26
|
+
--vitral-light-teal: rgba(30, 170, 160, 0.26);
|
|
27
|
+
--vitral-light-amber: rgba(242, 178, 74, 0.26);
|
|
28
|
+
--vitral-light-rose: rgba(226, 98, 132, 0.20);
|
|
29
|
+
--vitral-light-violet: rgba(138, 110, 228, 0.22);
|
|
30
|
+
--vitral-ground: #e3eaf3;
|
|
31
|
+
/* The sun outside the window, the shafts it throws across the room, and
|
|
32
|
+
the ghosts a lens makes of it. */
|
|
33
|
+
--vitral-flare: rgba(255, 252, 244, 0.7);
|
|
34
|
+
--vitral-sun-x: 88vw;
|
|
35
|
+
--vitral-sun-y: -2vh;
|
|
36
|
+
--vitral-shaft: rgba(255, 253, 248, 0.92);
|
|
37
|
+
|
|
38
|
+
/* The glass, one hue per pane, laid over white so that only the surface
|
|
39
|
+
takes the colour and the text keeps its contrast. */
|
|
40
|
+
--vitral-pane-1: rgba(54, 122, 235, 0.32);
|
|
41
|
+
--vitral-pane-2: rgba(28, 168, 158, 0.30);
|
|
42
|
+
--vitral-pane-3: rgba(240, 176, 70, 0.34);
|
|
43
|
+
--vitral-pane-4: rgba(224, 96, 130, 0.28);
|
|
44
|
+
--vitral-pane-5: rgba(138, 110, 228, 0.30);
|
|
45
|
+
|
|
46
|
+
--vitral-glass: rgba(255, 255, 255, 0.55);
|
|
47
|
+
--vitral-glass-strong: rgba(255, 255, 255, 0.78);
|
|
48
|
+
--vitral-blur: saturate(160%) blur(14px);
|
|
49
|
+
|
|
50
|
+
/* Came: the dark lead that holds each pane. */
|
|
51
|
+
--vitral-came: rgba(26, 38, 54, 0.38);
|
|
52
|
+
--vitral-came-soft: rgba(26, 38, 54, 0.22);
|
|
53
|
+
|
|
54
|
+
/* The lead's own colour, and how much of it survives behind the reading
|
|
55
|
+
column. Both deliberately faint: this is texture at the edge of sight,
|
|
56
|
+
and it competes with the text the moment it is anything more. */
|
|
57
|
+
--vitral-lattice-ink: rgba(44, 62, 84, 0.14);
|
|
58
|
+
/* Held back only where the words are, which on a wide screen is the left of
|
|
59
|
+
the reading column rather than the middle of the window. The dip used to
|
|
60
|
+
span the whole centre and took the pattern out of a large area that has
|
|
61
|
+
nothing in it to clash with. */
|
|
62
|
+
--vitral-lattice-mask: linear-gradient(90deg,
|
|
63
|
+
#000 0 10%, rgba(0, 0, 0, 0.42) 20%, rgba(0, 0, 0, 0.42) 46%, #000 62% 100%);
|
|
64
|
+
|
|
65
|
+
--vitral-radius: 10px;
|
|
66
|
+
--vitral-shadow: 0 1px 2px rgba(16, 24, 40, 0.05), 0 16px 34px -22px rgba(16, 24, 40, 0.45);
|
|
67
|
+
|
|
68
|
+
/* Janela's own two properties, restated in the theme's terms so the parts
|
|
69
|
+
janela.css draws agree with the parts this one does. */
|
|
70
|
+
--janela-line: var(--vitral-came-soft);
|
|
71
|
+
--janela-accent: rgb(40, 110, 205);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Opt in, because a theme that repaints a host's <body> uninvited is not a
|
|
75
|
+
theme, it is a takeover. Janela's own pages carry it themselves. */
|
|
76
|
+
.vitral, .janela-page {
|
|
77
|
+
color: var(--vitral-ink);
|
|
78
|
+
background-color: var(--vitral-ground);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.vitral, body:has(> .janela-page) { min-height: 100vh; }
|
|
82
|
+
|
|
83
|
+
.vitral::before, .janela-page::before {
|
|
84
|
+
content: "";
|
|
85
|
+
position: fixed;
|
|
86
|
+
inset: -20vmax;
|
|
87
|
+
z-index: -2;
|
|
88
|
+
background:
|
|
89
|
+
/* Shafts of light, angled and parallel, the way a leadlight window throws
|
|
90
|
+
them across a floor. A linear gradient is already a band at an angle,
|
|
91
|
+
so no rotated element is needed for it. Soft stops on both sides,
|
|
92
|
+
because a hard edge reads as a graphic rather than as light. */
|
|
93
|
+
/* Shafts of light, angled and parallel, the way a leadlight window throws
|
|
94
|
+
them across a floor. A band is perpendicular to its gradient's angle, so
|
|
95
|
+
falling from the top right means an axis pointing down and right. */
|
|
96
|
+
linear-gradient(135deg,
|
|
97
|
+
transparent 36%, rgba(255, 252, 246, 0.22) 41%, var(--vitral-shaft) 45.5%,
|
|
98
|
+
rgba(255, 252, 246, 0.3) 49%, transparent 55%),
|
|
99
|
+
linear-gradient(135deg,
|
|
100
|
+
transparent 54%, rgba(255, 252, 247, 0.55) 58%, rgba(255, 252, 247, 0.16) 61%, transparent 66%),
|
|
101
|
+
linear-gradient(135deg,
|
|
102
|
+
transparent 64%, rgba(255, 251, 245, 0.34) 68%, transparent 74%),
|
|
103
|
+
/* A cooler band between the shafts, because light is only bright next to
|
|
104
|
+
something that is not. */
|
|
105
|
+
linear-gradient(135deg,
|
|
106
|
+
transparent 49%, rgba(104, 128, 166, 0.14) 52.5%, transparent 56%),
|
|
107
|
+
|
|
108
|
+
/* The flare, first so it sits over the colour rather than under it.
|
|
109
|
+
This layer hangs 20vmax outside the window on every side, so a
|
|
110
|
+
percentage here is not a percentage of the screen. Positions are given
|
|
111
|
+
in screen units and shifted by the overhang, which is why every one of
|
|
112
|
+
them carries the same + 20vmax. */
|
|
113
|
+
radial-gradient(4vmax 4vmax at calc(var(--vitral-sun-x) + 20vmax) calc(var(--vitral-sun-y) + 20vmax),
|
|
114
|
+
rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.5) 42%, transparent 66%),
|
|
115
|
+
radial-gradient(22vmax 22vmax at calc(var(--vitral-sun-x) + 20vmax) calc(var(--vitral-sun-y) + 20vmax),
|
|
116
|
+
var(--vitral-flare), rgba(255, 250, 238, 0.24) 40%, transparent 64%),
|
|
117
|
+
radial-gradient(4vmax 4vmax at calc(68vw + 20vmax) calc(24vh + 20vmax),
|
|
118
|
+
rgba(255, 236, 198, 0.42), transparent 62%),
|
|
119
|
+
radial-gradient(2.4vmax 2.4vmax at calc(60vw + 20vmax) calc(35vh + 20vmax),
|
|
120
|
+
rgba(198, 226, 255, 0.4), transparent 64%),
|
|
121
|
+
radial-gradient(6vmax 6vmax at calc(47vw + 20vmax) calc(50vh + 20vmax),
|
|
122
|
+
rgba(255, 224, 212, 0.26), transparent 66%),
|
|
123
|
+
radial-gradient(1.8vmax 1.8vmax at calc(37vw + 20vmax) calc(64vh + 20vmax),
|
|
124
|
+
rgba(206, 238, 222, 0.34), transparent 62%),
|
|
125
|
+
radial-gradient(34vmax 34vmax at 11% 6%, var(--vitral-light-cobalt), transparent 58%),
|
|
126
|
+
radial-gradient(22vmax 22vmax at 38% 27%, var(--vitral-light-teal), transparent 55%),
|
|
127
|
+
radial-gradient(30vmax 30vmax at 83% 14%, var(--vitral-light-rose), transparent 56%),
|
|
128
|
+
radial-gradient(26vmax 26vmax at 64% 47%, var(--vitral-light-violet), transparent 58%),
|
|
129
|
+
radial-gradient(38vmax 38vmax at 91% 69%, var(--vitral-light-amber), transparent 60%),
|
|
130
|
+
radial-gradient(28vmax 28vmax at 23% 81%, var(--vitral-light-teal), transparent 58%),
|
|
131
|
+
radial-gradient(24vmax 24vmax at 57% 96%, var(--vitral-light-cobalt), transparent 55%);
|
|
132
|
+
/* The far layer of the parallax: light moves with the pointer, lead moves
|
|
133
|
+
against it, which is what gives the glass its depth. Both sit still until
|
|
134
|
+
something sets the properties, so this is nothing without the controller
|
|
135
|
+
and nothing for anyone who asked for less motion. */
|
|
136
|
+
transform: translate3d(calc(var(--vitral-shift-x, 0) * 26px),
|
|
137
|
+
calc(var(--vitral-shift-y, 0) * 26px + var(--vitral-drift-far, 0px)), 0);
|
|
138
|
+
transition: transform .45s cubic-bezier(.2, .7, .3, 1);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* The leadlight itself: cut once with a seeded random hand, so no two cells
|
|
142
|
+
are the same shape and nothing repeats across the viewport. Inlined rather
|
|
143
|
+
than shipped as an image, because a stylesheet in a gem cannot rely on a
|
|
144
|
+
host's asset pipeline to resolve a url().
|
|
145
|
+
|
|
146
|
+
Masked away from the middle, which is the part of a real window you put
|
|
147
|
+
clear glass in so that you can see out of it. Text sits there; the leading
|
|
148
|
+
stays in the margins where it belongs. */
|
|
149
|
+
.vitral::after, .janela-page::after {
|
|
150
|
+
content: "";
|
|
151
|
+
position: fixed;
|
|
152
|
+
inset: -6vmax;
|
|
153
|
+
z-index: -1;
|
|
154
|
+
pointer-events: none;
|
|
155
|
+
opacity: 0.5;
|
|
156
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1600 1200' preserveAspectRatio='xMidYMid slice'%3E%3Cg stroke='rgba(44,62,84,0.11)' stroke-width='1' stroke-linejoin='round'%3E%3Cpolygon points='0.0,0.0 66.9,0.0 78.6,121.4 0.0,98.3' fill='none'/%3E%3Cpolygon points='66.9,0.0 192.5,0.0 178.8,94.6 78.6,121.4' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='192.5,0.0 290.9,0.0 305.4,64.6' fill='none'/%3E%3Cpolygon points='192.5,0.0 305.4,64.6 178.8,94.6' fill='none'/%3E%3Cpolygon points='290.9,0.0 350.1,0.0 363.5,120.0 305.4,64.6' fill='none'/%3E%3Cpolygon points='350.1,0.0 494.7,0.0 363.5,120.0' fill='none'/%3E%3Cpolygon points='494.7,0.0 457.4,69.4 363.5,120.0' fill='none'/%3E%3Cpolygon points='494.7,0.0 575.6,0.0 457.4,69.4' fill='none'/%3E%3Cpolygon points='575.6,0.0 554.0,78.0 457.4,69.4' fill='none'/%3E%3Cpolygon points='575.6,0.0 637.9,0.0 676.6,71.5 554.0,78.0' fill='none'/%3E%3Cpolygon points='637.9,0.0 776.8,0.0 722.9,91.5 676.6,71.5' fill='none'/%3E%3Cpolygon points='776.8,0.0 846.7,0.0 823.2,107.7' fill='none'/%3E%3Cpolygon points='776.8,0.0 823.2,107.7 722.9,91.5' fill='none'/%3E%3Cpolygon points='846.7,0.0 966.9,0.0 823.2,107.7' fill='none'/%3E%3Cpolygon points='966.9,0.0 932.0,109.5 823.2,107.7' fill='none'/%3E%3Cpolygon points='966.9,0.0 1063.4,0.0 1024.1,73.2 932.0,109.5' fill='none'/%3E%3Cpolygon points='1063.4,0.0 1104.8,0.0 1158.1,71.1 1024.1,73.2' fill='none'/%3E%3Cpolygon points='1104.8,0.0 1226.9,0.0 1198.1,64.3 1158.1,71.1' fill='none'/%3E%3Cpolygon points='1226.9,0.0 1339.6,0.0 1198.1,64.3' fill='none'/%3E%3Cpolygon points='1339.6,0.0 1313.3,67.5 1198.1,64.3' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1339.6,0.0 1386.6,0.0 1426.1,85.2' fill='none'/%3E%3Cpolygon points='1339.6,0.0 1426.1,85.2 1313.3,67.5' fill='none'/%3E%3Cpolygon points='1386.6,0.0 1501.7,0.0 1511.4,112.5 1426.1,85.2' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1501.7,0.0 1600.0,0.0 1600.0,98.7 1511.4,112.5' fill='none'/%3E%3Cpolygon points='0.0,98.3 78.6,121.4 0.0,176.0' fill='none'/%3E%3Cpolygon points='78.6,121.4 84.3,182.0 0.0,176.0' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='78.6,121.4 178.8,94.6 166.6,196.7 84.3,182.0' fill='none'/%3E%3Cpolygon points='178.8,94.6 305.4,64.6 309.4,174.0 166.6,196.7' fill='none'/%3E%3Cpolygon points='305.4,64.6 363.5,120.0 309.4,174.0' fill='none'/%3E%3Cpolygon points='363.5,120.0 384.6,204.4 309.4,174.0' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='363.5,120.0 457.4,69.4 476.7,177.4 384.6,204.4' fill='none'/%3E%3Cpolygon points='457.4,69.4 554.0,78.0 476.7,177.4' fill='none'/%3E%3Cpolygon points='554.0,78.0 554.0,189.5 476.7,177.4' fill='none'/%3E%3Cpolygon points='554.0,78.0 676.6,71.5 675.8,168.7' fill='none'/%3E%3Cpolygon points='554.0,78.0 675.8,168.7 554.0,189.5' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='676.6,71.5 722.9,91.5 774.9,166.4' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='676.6,71.5 774.9,166.4 675.8,168.7' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='722.9,91.5 823.2,107.7 859.6,171.5' fill='none'/%3E%3Cpolygon points='722.9,91.5 859.6,171.5 774.9,166.4' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='823.2,107.7 932.0,109.5 923.8,164.1 859.6,171.5' fill='none'/%3E%3Cpolygon points='932.0,109.5 1024.1,73.2 1044.8,185.9 923.8,164.1' fill='none'/%3E%3Cpolygon points='1024.1,73.2 1158.1,71.1 1143.2,205.1 1044.8,185.9' fill='none'/%3E%3Cpolygon points='1158.1,71.1 1198.1,64.3 1226.6,187.2 1143.2,205.1' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='1198.1,64.3 1313.3,67.5 1328.8,197.5 1226.6,187.2' fill='none'/%3E%3Cpolygon points='1313.3,67.5 1426.1,85.2 1384.3,200.6' fill='none'/%3E%3Cpolygon points='1313.3,67.5 1384.3,200.6 1328.8,197.5' fill='none'/%3E%3Cpolygon points='1426.1,85.2 1511.4,112.5 1527.8,181.8 1384.3,200.6' fill='none'/%3E%3Cpolygon points='1511.4,112.5 1600.0,98.7 1600.0,165.0' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='1511.4,112.5 1600.0,165.0 1527.8,181.8' fill='none'/%3E%3Cpolygon points='0.0,176.0 84.3,182.0 0.0,269.6' fill='none'/%3E%3Cpolygon points='84.3,182.0 121.3,291.5 0.0,269.6' fill='none'/%3E%3Cpolygon points='84.3,182.0 166.6,196.7 160.3,281.3 121.3,291.5' fill='none'/%3E%3Cpolygon points='166.6,196.7 309.4,174.0 287.4,278.9' fill='none'/%3E%3Cpolygon points='166.6,196.7 287.4,278.9 160.3,281.3' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='309.4,174.0 384.6,204.4 389.5,304.1 287.4,278.9' fill='none'/%3E%3Cpolygon points='384.6,204.4 476.7,177.4 474.3,256.5 389.5,304.1' fill='none'/%3E%3Cpolygon points='476.7,177.4 554.0,189.5 569.7,251.7 474.3,256.5' fill='none'/%3E%3Cpolygon points='554.0,189.5 675.8,168.7 672.5,283.9 569.7,251.7' fill='none'/%3E%3Cpolygon points='675.8,168.7 774.9,166.4 734.9,272.3 672.5,283.9' fill='none'/%3E%3Cpolygon points='774.9,166.4 859.6,171.5 846.6,287.4 734.9,272.3' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='859.6,171.5 923.8,164.1 963.1,257.5 846.6,287.4' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='923.8,164.1 1044.8,185.9 1060.5,253.6 963.1,257.5' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1044.8,185.9 1143.2,205.1 1117.1,289.7' fill='none'/%3E%3Cpolygon points='1044.8,185.9 1117.1,289.7 1060.5,253.6' fill='none'/%3E%3Cpolygon points='1143.2,205.1 1226.6,187.2 1117.1,289.7' fill='none'/%3E%3Cpolygon points='1226.6,187.2 1230.4,286.1 1117.1,289.7' fill='none'/%3E%3Cpolygon points='1226.6,187.2 1328.8,197.5 1343.4,275.3 1230.4,286.1' fill='none'/%3E%3Cpolygon points='1328.8,197.5 1384.3,200.6 1343.4,275.3' fill='none'/%3E%3Cpolygon points='1384.3,200.6 1383.9,263.9 1343.4,275.3' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1384.3,200.6 1527.8,181.8 1482.4,282.6 1383.9,263.9' fill='none'/%3E%3Cpolygon points='1527.8,181.8 1600.0,165.0 1600.0,268.9 1482.4,282.6' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='0.0,269.6 121.3,291.5 113.1,370.3 0.0,344.0' fill='none'/%3E%3Cpolygon points='121.3,291.5 160.3,281.3 188.0,371.8 113.1,370.3' fill='none'/%3E%3Cpolygon points='160.3,281.3 287.4,278.9 282.9,349.5' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='160.3,281.3 282.9,349.5 188.0,371.8' fill='none'/%3E%3Cpolygon points='287.4,278.9 389.5,304.1 403.9,354.6 282.9,349.5' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='389.5,304.1 474.3,256.5 452.9,358.5 403.9,354.6' fill='none'/%3E%3Cpolygon points='474.3,256.5 569.7,251.7 547.1,374.2 452.9,358.5' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='569.7,251.7 672.5,283.9 547.1,374.2' fill='none'/%3E%3Cpolygon points='672.5,283.9 664.3,359.3 547.1,374.2' fill='none'/%3E%3Cpolygon points='672.5,283.9 734.9,272.3 765.0,342.7 664.3,359.3' fill='none'/%3E%3Cpolygon points='734.9,272.3 846.6,287.4 822.4,394.5 765.0,342.7' fill='none'/%3E%3Cpolygon points='846.6,287.4 963.1,257.5 915.0,368.7' fill='none'/%3E%3Cpolygon points='846.6,287.4 915.0,368.7 822.4,394.5' fill='none'/%3E%3Cpolygon points='963.1,257.5 1060.5,253.6 1030.4,358.6' fill='none'/%3E%3Cpolygon points='963.1,257.5 1030.4,358.6 915.0,368.7' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1060.5,253.6 1117.1,289.7 1150.6,379.6 1030.4,358.6' fill='none'/%3E%3Cpolygon points='1117.1,289.7 1230.4,286.1 1208.7,362.3 1150.6,379.6' fill='none'/%3E%3Cpolygon points='1230.4,286.1 1343.4,275.3 1208.7,362.3' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='1343.4,275.3 1345.7,347.6 1208.7,362.3' fill='none'/%3E%3Cpolygon points='1343.4,275.3 1383.9,263.9 1416.4,374.4 1345.7,347.6' fill='none'/%3E%3Cpolygon points='1383.9,263.9 1482.4,282.6 1416.4,374.4' fill='none'/%3E%3Cpolygon points='1482.4,282.6 1480.3,354.4 1416.4,374.4' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1482.4,282.6 1600.0,268.9 1600.0,374.1 1480.3,354.4' fill='none'/%3E%3Cpolygon points='0.0,344.0 113.1,370.3 118.3,454.2 0.0,486.0' fill='none'/%3E%3Cpolygon points='113.1,370.3 188.0,371.8 118.3,454.2' fill='none'/%3E%3Cpolygon points='188.0,371.8 198.5,435.2 118.3,454.2' fill='none'/%3E%3Cpolygon points='188.0,371.8 282.9,349.5 292.9,458.6 198.5,435.2' fill='none'/%3E%3Cpolygon points='282.9,349.5 403.9,354.6 376.0,452.4 292.9,458.6' fill='none'/%3E%3Cpolygon points='403.9,354.6 452.9,358.5 466.8,442.9 376.0,452.4' fill='none'/%3E%3Cpolygon points='452.9,358.5 547.1,374.2 568.1,474.3' fill='none'/%3E%3Cpolygon points='452.9,358.5 568.1,474.3 466.8,442.9' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='547.1,374.2 664.3,359.3 654.3,476.5 568.1,474.3' fill='none'/%3E%3Cpolygon points='664.3,359.3 765.0,342.7 740.7,453.5' fill='none'/%3E%3Cpolygon points='664.3,359.3 740.7,453.5 654.3,476.5' fill='none'/%3E%3Cpolygon points='765.0,342.7 822.4,394.5 856.4,442.5 740.7,453.5' fill='none'/%3E%3Cpolygon points='822.4,394.5 915.0,368.7 856.4,442.5' fill='none'/%3E%3Cpolygon points='915.0,368.7 962.7,475.9 856.4,442.5' fill='none'/%3E%3Cpolygon points='915.0,368.7 1030.4,358.6 1024.7,457.0 962.7,475.9' fill='none'/%3E%3Cpolygon points='1030.4,358.6 1150.6,379.6 1132.2,441.4 1024.7,457.0' fill='none'/%3E%3Cpolygon points='1150.6,379.6 1208.7,362.3 1236.3,459.1 1132.2,441.4' fill='none'/%3E%3Cpolygon points='1208.7,362.3 1345.7,347.6 1346.5,441.2 1236.3,459.1' fill='none'/%3E%3Cpolygon points='1345.7,347.6 1416.4,374.4 1346.5,441.2' fill='none'/%3E%3Cpolygon points='1416.4,374.4 1433.8,440.8 1346.5,441.2' fill='none'/%3E%3Cpolygon points='1416.4,374.4 1480.3,354.4 1520.8,465.4' fill='none'/%3E%3Cpolygon points='1416.4,374.4 1520.8,465.4 1433.8,440.8' fill='none'/%3E%3Cpolygon points='1480.3,354.4 1600.0,374.1 1600.0,488.9 1520.8,465.4' fill='none'/%3E%3Cpolygon points='0.0,486.0 118.3,454.2 68.3,539.9 0.0,581.1' fill='none'/%3E%3Cpolygon points='118.3,454.2 198.5,435.2 185.8,554.4 68.3,539.9' fill='none'/%3E%3Cpolygon points='198.5,435.2 292.9,458.6 185.8,554.4' fill='none'/%3E%3Cpolygon points='292.9,458.6 257.8,535.1 185.8,554.4' fill='none'/%3E%3Cpolygon points='292.9,458.6 376.0,452.4 383.9,581.5' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='292.9,458.6 383.9,581.5 257.8,535.1' fill='none'/%3E%3Cpolygon points='376.0,452.4 466.8,442.9 383.9,581.5' fill='none'/%3E%3Cpolygon points='466.8,442.9 493.7,548.1 383.9,581.5' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='466.8,442.9 568.1,474.3 555.8,577.4 493.7,548.1' fill='none'/%3E%3Cpolygon points='568.1,474.3 654.3,476.5 555.8,577.4' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='654.3,476.5 655.8,581.0 555.8,577.4' fill='none'/%3E%3Cpolygon points='654.3,476.5 740.7,453.5 778.0,533.8 655.8,581.0' fill='none'/%3E%3Cpolygon points='740.7,453.5 856.4,442.5 820.0,534.9' fill='none'/%3E%3Cpolygon points='740.7,453.5 820.0,534.9 778.0,533.8' fill='none'/%3E%3Cpolygon points='856.4,442.5 962.7,475.9 928.9,548.1 820.0,534.9' fill='none'/%3E%3Cpolygon points='962.7,475.9 1024.7,457.0 1051.5,558.3 928.9,548.1' fill='none'/%3E%3Cpolygon points='1024.7,457.0 1132.2,441.4 1126.4,575.9 1051.5,558.3' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='1132.2,441.4 1236.3,459.1 1213.6,571.2 1126.4,575.9' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='1236.3,459.1 1346.5,441.2 1333.7,566.1 1213.6,571.2' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='1346.5,441.2 1433.8,440.8 1384.4,570.5' fill='none'/%3E%3Cpolygon points='1346.5,441.2 1384.4,570.5 1333.7,566.1' fill='none'/%3E%3Cpolygon points='1433.8,440.8 1520.8,465.4 1505.3,549.6 1384.4,570.5' fill='none'/%3E%3Cpolygon points='1520.8,465.4 1600.0,488.9 1505.3,549.6' fill='none'/%3E%3Cpolygon points='1600.0,488.9 1600.0,546.9 1505.3,549.6' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='0.0,581.1 68.3,539.9 76.3,643.0 0.0,639.1' fill='none'/%3E%3Cpolygon points='68.3,539.9 185.8,554.4 172.0,657.8 76.3,643.0' fill='none'/%3E%3Cpolygon points='185.8,554.4 257.8,535.1 267.0,658.5 172.0,657.8' fill='none'/%3E%3Cpolygon points='257.8,535.1 383.9,581.5 267.0,658.5' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='383.9,581.5 397.8,675.2 267.0,658.5' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='383.9,581.5 493.7,548.1 469.5,629.4 397.8,675.2' fill='none'/%3E%3Cpolygon points='493.7,548.1 555.8,577.4 469.5,629.4' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='555.8,577.4 577.8,621.2 469.5,629.4' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='555.8,577.4 655.8,581.0 577.8,621.2' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='655.8,581.0 686.5,665.2 577.8,621.2' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='655.8,581.0 778.0,533.8 725.7,671.2 686.5,665.2' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='778.0,533.8 820.0,534.9 871.8,662.1 725.7,671.2' fill='none'/%3E%3Cpolygon points='820.0,534.9 928.9,548.1 962.7,637.1 871.8,662.1' fill='none'/%3E%3Cpolygon points='928.9,548.1 1051.5,558.3 1030.1,669.3 962.7,637.1' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1051.5,558.3 1126.4,575.9 1030.1,669.3' fill='none'/%3E%3Cpolygon points='1126.4,575.9 1135.0,673.2 1030.1,669.3' fill='none'/%3E%3Cpolygon points='1126.4,575.9 1213.6,571.2 1236.0,634.0' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1126.4,575.9 1236.0,634.0 1135.0,673.2' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='1213.6,571.2 1333.7,566.1 1334.9,627.6 1236.0,634.0' fill='none'/%3E%3Cpolygon points='1333.7,566.1 1384.4,570.5 1431.0,636.6 1334.9,627.6' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1384.4,570.5 1505.3,549.6 1535.4,646.5 1431.0,636.6' fill='none'/%3E%3Cpolygon points='1505.3,549.6 1600.0,546.9 1600.0,646.6 1535.4,646.5' fill='none'/%3E%3Cpolygon points='0.0,639.1 76.3,643.0 0.0,717.3' fill='none'/%3E%3Cpolygon points='76.3,643.0 112.6,737.8 0.0,717.3' fill='none'/%3E%3Cpolygon points='76.3,643.0 172.0,657.8 112.6,737.8' fill='none'/%3E%3Cpolygon points='172.0,657.8 185.0,750.9 112.6,737.8' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='172.0,657.8 267.0,658.5 268.5,715.8 185.0,750.9' fill='none'/%3E%3Cpolygon points='267.0,658.5 397.8,675.2 390.3,725.8 268.5,715.8' fill='none'/%3E%3Cpolygon points='397.8,675.2 469.5,629.4 477.7,761.7 390.3,725.8' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='469.5,629.4 577.8,621.2 477.7,761.7' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='577.8,621.2 541.0,765.9 477.7,761.7' fill='none'/%3E%3Cpolygon points='577.8,621.2 686.5,665.2 541.0,765.9' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='686.5,665.2 644.4,724.5 541.0,765.9' fill='none'/%3E%3Cpolygon points='686.5,665.2 725.7,671.2 731.7,744.8' fill='none'/%3E%3Cpolygon points='686.5,665.2 731.7,744.8 644.4,724.5' fill='none'/%3E%3Cpolygon points='725.7,671.2 871.8,662.1 731.7,744.8' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='871.8,662.1 830.2,727.4 731.7,744.8' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='871.8,662.1 962.7,637.1 956.4,736.7 830.2,727.4' fill='none'/%3E%3Cpolygon points='962.7,637.1 1030.1,669.3 956.4,736.7' fill='none'/%3E%3Cpolygon points='1030.1,669.3 1060.9,731.7 956.4,736.7' fill='none'/%3E%3Cpolygon points='1030.1,669.3 1135.0,673.2 1139.7,767.0 1060.9,731.7' fill='none'/%3E%3Cpolygon points='1135.0,673.2 1236.0,634.0 1198.9,759.4 1139.7,767.0' fill='none'/%3E%3Cpolygon points='1236.0,634.0 1334.9,627.6 1305.6,717.6' fill='none'/%3E%3Cpolygon points='1236.0,634.0 1305.6,717.6 1198.9,759.4' fill='none'/%3E%3Cpolygon points='1334.9,627.6 1431.0,636.6 1305.6,717.6' fill='none'/%3E%3Cpolygon points='1431.0,636.6 1389.8,709.2 1305.6,717.6' fill='none'/%3E%3Cpolygon points='1431.0,636.6 1535.4,646.5 1502.2,724.2 1389.8,709.2' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='1535.4,646.5 1600.0,646.6 1502.2,724.2' fill='none'/%3E%3Cpolygon points='1600.0,646.6 1600.0,763.7 1502.2,724.2' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='0.0,717.3 112.6,737.8 0.0,856.8' fill='none'/%3E%3Cpolygon points='112.6,737.8 65.8,859.2 0.0,856.8' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='112.6,737.8 185.0,750.9 179.3,818.4 65.8,859.2' fill='none'/%3E%3Cpolygon points='185.0,750.9 268.5,715.8 264.9,827.5' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='185.0,750.9 264.9,827.5 179.3,818.4' fill='none'/%3E%3Cpolygon points='268.5,715.8 390.3,725.8 264.9,827.5' fill='none'/%3E%3Cpolygon points='390.3,725.8 357.6,840.9 264.9,827.5' fill='none'/%3E%3Cpolygon points='390.3,725.8 477.7,761.7 449.6,828.5 357.6,840.9' fill='none'/%3E%3Cpolygon points='477.7,761.7 541.0,765.9 574.2,810.2' fill='none'/%3E%3Cpolygon points='477.7,761.7 574.2,810.2 449.6,828.5' fill='none'/%3E%3Cpolygon points='541.0,765.9 644.4,724.5 574.2,810.2' fill='none'/%3E%3Cpolygon points='644.4,724.5 681.5,804.6 574.2,810.2' fill='none'/%3E%3Cpolygon points='644.4,724.5 731.7,744.8 681.5,804.6' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='731.7,744.8 745.8,804.4 681.5,804.6' fill='none'/%3E%3Cpolygon points='731.7,744.8 830.2,727.4 840.2,839.6 745.8,804.4' fill='none'/%3E%3Cpolygon points='830.2,727.4 956.4,736.7 947.7,841.2 840.2,839.6' fill='none'/%3E%3Cpolygon points='956.4,736.7 1060.9,731.7 1015.2,857.9' fill='none'/%3E%3Cpolygon points='956.4,736.7 1015.2,857.9 947.7,841.2' fill='none'/%3E%3Cpolygon points='1060.9,731.7 1139.7,767.0 1118.8,802.9 1015.2,857.9' fill='none'/%3E%3Cpolygon points='1139.7,767.0 1198.9,759.4 1230.0,843.7 1118.8,802.9' fill='none'/%3E%3Cpolygon points='1198.9,759.4 1305.6,717.6 1297.0,848.5 1230.0,843.7' fill='none'/%3E%3Cpolygon points='1305.6,717.6 1389.8,709.2 1430.1,810.1 1297.0,848.5' fill='none'/%3E%3Cpolygon points='1389.8,709.2 1502.2,724.2 1534.4,834.9' fill='none'/%3E%3Cpolygon points='1389.8,709.2 1534.4,834.9 1430.1,810.1' fill='none'/%3E%3Cpolygon points='1502.2,724.2 1600.0,763.7 1600.0,816.2 1534.4,834.9' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='0.0,856.8 65.8,859.2 98.7,897.9' fill='none'/%3E%3Cpolygon points='0.0,856.8 98.7,897.9 0.0,930.4' fill='none'/%3E%3Cpolygon points='65.8,859.2 179.3,818.4 188.5,894.9 98.7,897.9' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='179.3,818.4 264.9,827.5 188.5,894.9' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='264.9,827.5 273.2,914.3 188.5,894.9' fill='none'/%3E%3Cpolygon points='264.9,827.5 357.6,840.9 348.8,894.6 273.2,914.3' fill='none'/%3E%3Cpolygon points='357.6,840.9 449.6,828.5 479.8,951.2 348.8,894.6' fill='none'/%3E%3Cpolygon points='449.6,828.5 574.2,810.2 479.8,951.2' fill='none'/%3E%3Cpolygon points='574.2,810.2 554.6,924.2 479.8,951.2' fill='none'/%3E%3Cpolygon points='574.2,810.2 681.5,804.6 679.2,926.8' fill='none'/%3E%3Cpolygon points='574.2,810.2 679.2,926.8 554.6,924.2' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='681.5,804.6 745.8,804.4 679.2,926.8' fill='none'/%3E%3Cpolygon points='745.8,804.4 759.6,900.0 679.2,926.8' fill='none'/%3E%3Cpolygon points='745.8,804.4 840.2,839.6 818.8,926.4 759.6,900.0' fill='%23e06082' fill-opacity='0.04'/%3E%3Cpolygon points='840.2,839.6 947.7,841.2 919.9,948.5' fill='none'/%3E%3Cpolygon points='840.2,839.6 919.9,948.5 818.8,926.4' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='947.7,841.2 1015.2,857.9 919.9,948.5' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1015.2,857.9 1053.4,952.4 919.9,948.5' fill='none'/%3E%3Cpolygon points='1015.2,857.9 1118.8,802.9 1053.4,952.4' fill='none'/%3E%3Cpolygon points='1118.8,802.9 1100.6,902.2 1053.4,952.4' fill='none'/%3E%3Cpolygon points='1118.8,802.9 1230.0,843.7 1230.5,939.1 1100.6,902.2' fill='none'/%3E%3Cpolygon points='1230.0,843.7 1297.0,848.5 1288.9,945.5' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='1230.0,843.7 1288.9,945.5 1230.5,939.1' fill='none'/%3E%3Cpolygon points='1297.0,848.5 1430.1,810.1 1288.9,945.5' fill='none'/%3E%3Cpolygon points='1430.1,810.1 1384.1,935.1 1288.9,945.5' fill='none'/%3E%3Cpolygon points='1430.1,810.1 1534.4,834.9 1487.5,935.5' fill='none'/%3E%3Cpolygon points='1430.1,810.1 1487.5,935.5 1384.1,935.1' fill='none'/%3E%3Cpolygon points='1534.4,834.9 1600.0,816.2 1600.0,906.3 1487.5,935.5' fill='none'/%3E%3Cpolygon points='0.0,930.4 98.7,897.9 94.1,999.7 0.0,1014.5' fill='none'/%3E%3Cpolygon points='98.7,897.9 188.5,894.9 160.7,1019.8' fill='none'/%3E%3Cpolygon points='98.7,897.9 160.7,1019.8 94.1,999.7' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='188.5,894.9 273.2,914.3 160.7,1019.8' fill='none'/%3E%3Cpolygon points='273.2,914.3 261.5,1011.2 160.7,1019.8' fill='none'/%3E%3Cpolygon points='273.2,914.3 348.8,894.6 261.5,1011.2' fill='none'/%3E%3Cpolygon points='348.8,894.6 392.3,999.9 261.5,1011.2' fill='none'/%3E%3Cpolygon points='348.8,894.6 479.8,951.2 470.1,989.1 392.3,999.9' fill='none'/%3E%3Cpolygon points='479.8,951.2 554.6,924.2 588.8,1017.7 470.1,989.1' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='554.6,924.2 679.2,926.8 588.8,1017.7' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='679.2,926.8 653.7,998.0 588.8,1017.7' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='679.2,926.8 759.6,900.0 653.7,998.0' fill='none'/%3E%3Cpolygon points='759.6,900.0 764.9,998.4 653.7,998.0' fill='none'/%3E%3Cpolygon points='759.6,900.0 818.8,926.4 861.4,1005.9 764.9,998.4' fill='none'/%3E%3Cpolygon points='818.8,926.4 919.9,948.5 920.0,1014.1 861.4,1005.9' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='919.9,948.5 1053.4,952.4 1028.7,1001.1 920.0,1014.1' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='1053.4,952.4 1100.6,902.2 1157.4,990.4' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='1053.4,952.4 1157.4,990.4 1028.7,1001.1' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='1100.6,902.2 1230.5,939.1 1233.1,1004.1 1157.4,990.4' fill='none'/%3E%3Cpolygon points='1230.5,939.1 1288.9,945.5 1321.3,1043.1 1233.1,1004.1' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1288.9,945.5 1384.1,935.1 1424.6,992.5 1321.3,1043.1' fill='none'/%3E%3Cpolygon points='1384.1,935.1 1487.5,935.5 1424.6,992.5' fill='none'/%3E%3Cpolygon points='1487.5,935.5 1529.7,1010.4 1424.6,992.5' fill='none'/%3E%3Cpolygon points='1487.5,935.5 1600.0,906.3 1600.0,1004.9 1529.7,1010.4' fill='none'/%3E%3Cpolygon points='0.0,1014.5 94.1,999.7 83.1,1127.7' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='0.0,1014.5 83.1,1127.7 0.0,1123.4' fill='none'/%3E%3Cpolygon points='94.1,999.7 160.7,1019.8 83.1,1127.7' fill='none'/%3E%3Cpolygon points='160.7,1019.8 182.7,1125.6 83.1,1127.7' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='160.7,1019.8 261.5,1011.2 304.2,1130.4' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='160.7,1019.8 304.2,1130.4 182.7,1125.6' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='261.5,1011.2 392.3,999.9 351.1,1114.7 304.2,1130.4' fill='none'/%3E%3Cpolygon points='392.3,999.9 470.1,989.1 351.1,1114.7' fill='none'/%3E%3Cpolygon points='470.1,989.1 452.5,1088.3 351.1,1114.7' fill='none'/%3E%3Cpolygon points='470.1,989.1 588.8,1017.7 546.0,1135.9 452.5,1088.3' fill='none'/%3E%3Cpolygon points='588.8,1017.7 653.7,998.0 684.7,1088.3 546.0,1135.9' fill='none'/%3E%3Cpolygon points='653.7,998.0 764.9,998.4 684.7,1088.3' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='764.9,998.4 762.3,1107.9 684.7,1088.3' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='764.9,998.4 861.4,1005.9 870.9,1093.7 762.3,1107.9' fill='none'/%3E%3Cpolygon points='861.4,1005.9 920.0,1014.1 920.0,1135.3 870.9,1093.7' fill='none'/%3E%3Cpolygon points='920.0,1014.1 1028.7,1001.1 1038.4,1133.2 920.0,1135.3' fill='none'/%3E%3Cpolygon points='1028.7,1001.1 1157.4,990.4 1145.9,1094.5 1038.4,1133.2' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='1157.4,990.4 1233.1,1004.1 1228.3,1087.0 1145.9,1094.5' fill='none'/%3E%3Cpolygon points='1233.1,1004.1 1321.3,1043.1 1228.3,1087.0' fill='none'/%3E%3Cpolygon points='1321.3,1043.1 1335.7,1125.4 1228.3,1087.0' fill='none'/%3E%3Cpolygon points='1321.3,1043.1 1424.6,992.5 1426.0,1098.9' fill='none'/%3E%3Cpolygon points='1321.3,1043.1 1426.0,1098.9 1335.7,1125.4' fill='none'/%3E%3Cpolygon points='1424.6,992.5 1529.7,1010.4 1488.9,1087.4 1426.0,1098.9' fill='none'/%3E%3Cpolygon points='1529.7,1010.4 1600.0,1004.9 1600.0,1117.7' fill='none'/%3E%3Cpolygon points='1529.7,1010.4 1600.0,1117.7 1488.9,1087.4' fill='none'/%3E%3Cpolygon points='0.0,1123.4 83.1,1127.7 112.3,1200.0' fill='none'/%3E%3Cpolygon points='0.0,1123.4 112.3,1200.0 0.0,1200.0' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='83.1,1127.7 182.7,1125.6 112.3,1200.0' fill='none'/%3E%3Cpolygon points='182.7,1125.6 170.3,1200.0 112.3,1200.0' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='182.7,1125.6 304.2,1130.4 304.6,1200.0 170.3,1200.0' fill='%238a6ee4' fill-opacity='0.04'/%3E%3Cpolygon points='304.2,1130.4 351.1,1114.7 387.3,1200.0 304.6,1200.0' fill='none'/%3E%3Cpolygon points='351.1,1114.7 452.5,1088.3 463.3,1200.0 387.3,1200.0' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='452.5,1088.3 546.0,1135.9 572.0,1200.0' fill='none'/%3E%3Cpolygon points='452.5,1088.3 572.0,1200.0 463.3,1200.0' fill='none'/%3E%3Cpolygon points='546.0,1135.9 684.7,1088.3 572.0,1200.0' fill='%23f0b046' fill-opacity='0.04'/%3E%3Cpolygon points='684.7,1088.3 645.3,1200.0 572.0,1200.0' fill='none'/%3E%3Cpolygon points='684.7,1088.3 762.3,1107.9 761.8,1200.0 645.3,1200.0' fill='none'/%3E%3Cpolygon points='762.3,1107.9 870.9,1093.7 867.0,1200.0 761.8,1200.0' fill='%231ca89e' fill-opacity='0.04'/%3E%3Cpolygon points='870.9,1093.7 920.0,1135.3 925.1,1200.0 867.0,1200.0' fill='none'/%3E%3Cpolygon points='920.0,1135.3 1038.4,1133.2 1063.0,1200.0 925.1,1200.0' fill='none'/%3E%3Cpolygon points='1038.4,1133.2 1145.9,1094.5 1063.0,1200.0' fill='none'/%3E%3Cpolygon points='1145.9,1094.5 1103.4,1200.0 1063.0,1200.0' fill='%2338a6eb' fill-opacity='0.04'/%3E%3Cpolygon points='1145.9,1094.5 1228.3,1087.0 1235.6,1200.0 1103.4,1200.0' fill='none'/%3E%3Cpolygon points='1228.3,1087.0 1335.7,1125.4 1340.3,1200.0 1235.6,1200.0' fill='none'/%3E%3Cpolygon points='1335.7,1125.4 1426.0,1098.9 1428.0,1200.0 1340.3,1200.0' fill='none'/%3E%3Cpolygon points='1426.0,1098.9 1488.9,1087.4 1531.3,1200.0 1428.0,1200.0' fill='none'/%3E%3Cpolygon points='1488.9,1087.4 1600.0,1117.7 1600.0,1200.0' fill='none'/%3E%3Cpolygon points='1488.9,1087.4 1600.0,1200.0 1531.3,1200.0' fill='%238a6ee4' fill-opacity='0.04'/%3E%3C/g%3E%3C/svg%3E");
|
|
157
|
+
background-size: cover;
|
|
158
|
+
background-position: center;
|
|
159
|
+
-webkit-mask-image: var(--vitral-lattice-mask);
|
|
160
|
+
mask-image: var(--vitral-lattice-mask);
|
|
161
|
+
/* The near layer of the parallax, so the lead sits in front of the light. */
|
|
162
|
+
transform: translate3d(calc(var(--vitral-shift-x, 0) * -10px),
|
|
163
|
+
calc(var(--vitral-shift-y, 0) * -10px + var(--vitral-drift-near, 0px)), 0);
|
|
164
|
+
transition: transform .2s cubic-bezier(.2, .7, .3, 1);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* A narrow screen is all reading column, so the leading thins out everywhere
|
|
168
|
+
rather than changing shape at a breakpoint. */
|
|
169
|
+
@media (max-width: 64rem) {
|
|
170
|
+
:root { --vitral-lattice-mask: linear-gradient(90deg, rgba(0, 0, 0, 0.5) 0 100%); }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* The live lattice, drawn by the optional controller. Same place, same mask,
|
|
174
|
+
same job as the static one above, except that its nodes lean toward the
|
|
175
|
+
pointer. The static one stands down when this arrives. */
|
|
176
|
+
.vitral-lattice {
|
|
177
|
+
position: fixed;
|
|
178
|
+
left: 0;
|
|
179
|
+
right: 0;
|
|
180
|
+
top: 0;
|
|
181
|
+
z-index: -1;
|
|
182
|
+
pointer-events: none;
|
|
183
|
+
opacity: 0.8;
|
|
184
|
+
-webkit-mask-image: var(--vitral-lattice-mask);
|
|
185
|
+
mask-image: var(--vitral-lattice-mask);
|
|
186
|
+
transform: translate3d(calc(var(--vitral-shift-x, 0) * -10px),
|
|
187
|
+
calc(var(--vitral-shift-y, 0) * -10px + var(--vitral-drift-near, 0px)), 0);
|
|
188
|
+
transition: transform .2s cubic-bezier(.2, .7, .3, 1);
|
|
189
|
+
}
|
|
190
|
+
.vitral-live::after { display: none; }
|
|
191
|
+
|
|
192
|
+
/* One recipe for everything that is meant to read as a pane of glass.
|
|
193
|
+
.vitral-pane is public: put it on your own markup and the page around a
|
|
194
|
+
dashboard is made of the same window. */
|
|
195
|
+
.vitral-pane, .janela-frame > *, .janela-page > .janela-pane, .janela-card, .janela-flash, .janela-list-row {
|
|
196
|
+
position: relative;
|
|
197
|
+
padding: calc(var(--janela-space) * 4) calc(var(--janela-space) * 5);
|
|
198
|
+
background: linear-gradient(var(--vitral-glass), var(--vitral-glass)), var(--vitral-pane-1);
|
|
199
|
+
-webkit-backdrop-filter: var(--vitral-blur);
|
|
200
|
+
backdrop-filter: var(--vitral-blur);
|
|
201
|
+
border: 1px solid var(--vitral-came);
|
|
202
|
+
border-radius: var(--vitral-radius);
|
|
203
|
+
box-shadow: var(--vitral-shadow), inset 0 1px 0 rgba(255, 255, 255, 0.75);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/* A window worth looking at cycles its colours rather than repeating one, so a
|
|
207
|
+
pane takes its hue from where it sits. .vitral-panes on a container does the
|
|
208
|
+
same for your own markup. */
|
|
209
|
+
.vitral-panes > *:nth-child(5n + 2),
|
|
210
|
+
.janela-frame > *:nth-child(5n + 2),
|
|
211
|
+
.janela-page > .janela-pane:nth-child(5n + 2),
|
|
212
|
+
.janela-list-row:nth-child(5n + 2), .janela-card:nth-child(5n + 2) {
|
|
213
|
+
background: linear-gradient(var(--vitral-glass), var(--vitral-glass)), var(--vitral-pane-2);
|
|
214
|
+
}
|
|
215
|
+
.vitral-panes > *:nth-child(5n + 3),
|
|
216
|
+
.janela-frame > *:nth-child(5n + 3),
|
|
217
|
+
.janela-page > .janela-pane:nth-child(5n + 3),
|
|
218
|
+
.janela-list-row:nth-child(5n + 3), .janela-card:nth-child(5n + 3) {
|
|
219
|
+
background: linear-gradient(var(--vitral-glass), var(--vitral-glass)), var(--vitral-pane-3);
|
|
220
|
+
}
|
|
221
|
+
.vitral-panes > *:nth-child(5n + 4),
|
|
222
|
+
.janela-frame > *:nth-child(5n + 4),
|
|
223
|
+
.janela-page > .janela-pane:nth-child(5n + 4),
|
|
224
|
+
.janela-list-row:nth-child(5n + 4), .janela-card:nth-child(5n + 4) {
|
|
225
|
+
background: linear-gradient(var(--vitral-glass), var(--vitral-glass)), var(--vitral-pane-4);
|
|
226
|
+
}
|
|
227
|
+
.vitral-panes > *:nth-child(5n + 5),
|
|
228
|
+
.janela-frame > *:nth-child(5n + 5),
|
|
229
|
+
.janela-page > .janela-pane:nth-child(5n + 5),
|
|
230
|
+
.janela-list-row:nth-child(5n + 5), .janela-card:nth-child(5n + 5) {
|
|
231
|
+
background: linear-gradient(var(--vitral-glass), var(--vitral-glass)), var(--vitral-pane-5);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* The line of light along the top edge, where real glass catches it. A canvas
|
|
235
|
+
is a replaced element and takes no pseudo-element, so charts go without. */
|
|
236
|
+
.vitral-pane::before, .janela-frame > *::before,
|
|
237
|
+
.janela-card::before, .janela-list-row::before {
|
|
238
|
+
content: "";
|
|
239
|
+
position: absolute;
|
|
240
|
+
inset: 0 0 auto;
|
|
241
|
+
height: 1px;
|
|
242
|
+
border-radius: var(--vitral-radius) var(--vitral-radius) 0 0;
|
|
243
|
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.9), transparent);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* Inside a panel the pane is content: the grid item carries the glass, since
|
|
247
|
+
a table's caption renders outside the table's own border box and would
|
|
248
|
+
otherwise float above the colour. */
|
|
249
|
+
.janela-frame .janela-pane { background: none; border: 0; box-shadow: none; padding: 0; }
|
|
250
|
+
|
|
251
|
+
.vitral-pane::after, .janela-frame > *::after,
|
|
252
|
+
.janela-card::after, .janela-list-row::after {
|
|
253
|
+
content: "";
|
|
254
|
+
position: absolute;
|
|
255
|
+
inset: 0;
|
|
256
|
+
z-index: -1;
|
|
257
|
+
border-radius: inherit;
|
|
258
|
+
pointer-events: none;
|
|
259
|
+
background: linear-gradient(116deg,
|
|
260
|
+
rgba(255, 255, 255, 0.5) 0 10%,
|
|
261
|
+
rgba(255, 255, 255, 0.16) 24%,
|
|
262
|
+
rgba(255, 255, 255, 0) 44%);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* A collapsed table drops its corner radius, and glass without corners is a
|
|
266
|
+
pane of a different shape. */
|
|
267
|
+
table.janela-pane { border-collapse: separate; border-spacing: 0; }
|
|
268
|
+
table.janela-pane caption { padding-bottom: calc(var(--janela-space) * 2); }
|
|
269
|
+
table.janela-pane td:first-child { padding-left: 0; }
|
|
270
|
+
|
|
271
|
+
.janela-value-number { letter-spacing: -0.02em; }
|
|
272
|
+
.janela-value-label { opacity: 0.75; }
|
|
273
|
+
|
|
274
|
+
table.janela-pane button[aria-pressed="true"] {
|
|
275
|
+
background: var(--janela-accent);
|
|
276
|
+
color: white;
|
|
277
|
+
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.janela-empty { color: var(--vitral-muted); }
|
|
281
|
+
.janela-error { border-color: rgba(190, 60, 60, 0.5); }
|
|
282
|
+
|
|
283
|
+
/* Janela's own pages, which carry the theme themselves because the engine owns
|
|
284
|
+
that layout (ADR 011). */
|
|
285
|
+
.janela-page { padding-block: calc(var(--janela-space) * 10); }
|
|
286
|
+
.janela-heading { letter-spacing: -0.02em; }
|
|
287
|
+
.janela-list { display: grid; gap: calc(var(--janela-space) * 2); }
|
|
288
|
+
.janela-list-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
|
|
289
|
+
.janela-field input, .janela-field select {
|
|
290
|
+
background: var(--vitral-glass-strong);
|
|
291
|
+
border-color: var(--vitral-came-soft);
|
|
292
|
+
border-radius: 8px;
|
|
293
|
+
}
|
|
294
|
+
.janela-actions { display: flex; gap: calc(var(--janela-space) * 3); align-items: center; flex-wrap: wrap; }
|
|
295
|
+
|
|
296
|
+
/* A control made of the same glass. */
|
|
297
|
+
.vitral-button {
|
|
298
|
+
display: inline-block;
|
|
299
|
+
padding: .62rem 1.25rem;
|
|
300
|
+
font: inherit;
|
|
301
|
+
font-weight: 550;
|
|
302
|
+
color: var(--vitral-ink);
|
|
303
|
+
text-decoration: none;
|
|
304
|
+
cursor: pointer;
|
|
305
|
+
background: var(--vitral-glass-strong);
|
|
306
|
+
-webkit-backdrop-filter: var(--vitral-blur);
|
|
307
|
+
backdrop-filter: var(--vitral-blur);
|
|
308
|
+
border: 1px solid var(--vitral-came);
|
|
309
|
+
border-radius: 10px;
|
|
310
|
+
box-shadow: var(--vitral-shadow), inset 0 1px 0 rgba(255, 255, 255, 0.75);
|
|
311
|
+
}
|
|
312
|
+
.vitral-button:hover { border-color: rgba(26, 38, 54, 0.62); }
|
|
313
|
+
.vitral-button-primary {
|
|
314
|
+
background: linear-gradient(180deg, rgb(86, 180, 242), rgb(25, 112, 176));
|
|
315
|
+
color: white;
|
|
316
|
+
box-shadow: 0 10px 24px -12px rgba(25, 112, 176, 0.9), inset 0 1px 0 rgba(255, 255, 255, 0.45);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* Glass is an effect, not a requirement. Anyone who asked for less of it, or
|
|
320
|
+
whose browser cannot do it, gets the same window in plain panels. */
|
|
321
|
+
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
|
|
322
|
+
.vitral-pane, .janela-pane, .janela-card, .janela-flash, .janela-list-row, .vitral-button {
|
|
323
|
+
background: rgba(255, 255, 255, 0.94);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
|
|
328
|
+
.vitral-pane, .janela-pane, .janela-card, .janela-flash, .janela-list-row, .vitral-button {
|
|
329
|
+
background: #ffffff;
|
|
330
|
+
-webkit-backdrop-filter: none;
|
|
331
|
+
backdrop-filter: none;
|
|
332
|
+
border-color: var(--vitral-came-soft);
|
|
333
|
+
}
|
|
334
|
+
.vitral::before, .vitral::after, .janela-page::before, .janela-page::after { display: none; }
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
@media (prefers-reduced-motion: reduce) {
|
|
338
|
+
.vitral::before, .vitral::after, .vitral-lattice,
|
|
339
|
+
.janela-page::before, .janela-page::after {
|
|
340
|
+
transform: none;
|
|
341
|
+
transition: none;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
module Janela
|
|
2
2
|
class ApplicationController < Janela.parent_controller.constantize
|
|
3
|
+
# A host's own route helpers, so host code inherited from its
|
|
4
|
+
# ApplicationController works in here without knowing it is in an engine
|
|
5
|
+
# (ADR 022). Included before the engine's own helpers are used, and it
|
|
6
|
+
# defines none of their names, so nothing of Janela's is shadowed.
|
|
7
|
+
include Janela::HostRoutes
|
|
8
|
+
helper Janela::HostRoutes
|
|
9
|
+
|
|
3
10
|
# A frame request needs no layout, since Turbo keeps only the matching
|
|
4
11
|
# frame. A direct request gets Janela's own minimal layout, because a host
|
|
5
12
|
# layout's route helpers cannot resolve inside an isolated engine (ADR 011).
|
|
@@ -9,7 +9,8 @@ module Janela
|
|
|
9
9
|
def janela_frame(frame = nil, charts: true, &block)
|
|
10
10
|
return render("janela/frames/frame", frame: frame, filters: janela_page_filters, charts: charts) if frame
|
|
11
11
|
|
|
12
|
-
tag.div(data: { controller: "janela--frame",
|
|
12
|
+
tag.div(data: { controller: "janela--frame", action: "keydown.esc->janela--frame#clear",
|
|
13
|
+
janela__frame_filters_value: janela_page_filters.to_json }, &block)
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
def janela_pane(model, measure, by: nil, as: :table, granularity: nil, limit: nil)
|
data/app/models/janela/query.rb
CHANGED
|
@@ -103,11 +103,15 @@ module Janela
|
|
|
103
103
|
# The Ransack key and value a click on this label should toggle. A null
|
|
104
104
|
# group filters with the null predicate, not an empty string (ADR 009 has
|
|
105
105
|
# no say here; see issue #21).
|
|
106
|
+
# A click writes _in whether one value is selected or five, so there is one
|
|
107
|
+
# shape in the controller, the view and a stored snapshot. A hand written
|
|
108
|
+
# _eq link is still read, since a URL somebody already sent should not stop
|
|
109
|
+
# working to suit us (ADR 024).
|
|
106
110
|
def filter_params(label)
|
|
107
111
|
return [ nil, nil ] unless clickable?
|
|
108
112
|
return [ "#{ransack_name}_null", "1" ] if label.to_s == Dimension::NONE
|
|
109
113
|
|
|
110
|
-
[ "#{ransack_name}
|
|
114
|
+
[ "#{ransack_name}_in", label.to_s ]
|
|
111
115
|
end
|
|
112
116
|
|
|
113
117
|
# Every label in this pane paired with the filter it toggles, for a chart
|
|
@@ -120,11 +124,20 @@ module Janela
|
|
|
120
124
|
|
|
121
125
|
# The filter on this pane's own dimension is not applied to its query, but
|
|
122
126
|
# it is what the user clicked here, so the view highlights it.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
127
|
+
# Every value of this pane's own dimension the filters name. A set, because
|
|
128
|
+
# a dimension can hold more than one (ADR 024), and the null group is in it
|
|
129
|
+
# like any other label.
|
|
130
|
+
def selected_values
|
|
131
|
+
return [] unless clickable?
|
|
126
132
|
|
|
127
|
-
filter("#{ransack_name}_eq")
|
|
133
|
+
values = Array(filter("#{ransack_name}_in")) + Array(filter("#{ransack_name}_eq"))
|
|
134
|
+
values = values.map(&:to_s)
|
|
135
|
+
values << Dimension::NONE if filter("#{ransack_name}_null").present?
|
|
136
|
+
values
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def selected?(label)
|
|
140
|
+
selected_values.include?(label.to_s)
|
|
128
141
|
end
|
|
129
142
|
|
|
130
143
|
private
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<%= tag.div class: janela_frame_classes(frame),
|
|
2
|
-
data: { controller: "janela--frame",
|
|
2
|
+
data: { controller: "janela--frame", action: "keydown.esc->janela--frame#clear",
|
|
3
|
+
janela__frame_filters_value: filters.to_json } do %>
|
|
3
4
|
<%= render partial: "janela/frames/pane", collection: frame.panes, as: :pane, locals: { filters: filters, charts: charts } %>
|
|
4
5
|
<% end %>
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
data-action="janela--chart:toggle->janela--frame#toggle"
|
|
14
14
|
data-janela--chart-type-value="<%= query.renderer %>"
|
|
15
15
|
data-janela--chart-title-value="<%= query.title %>"
|
|
16
|
-
data-janela--chart-selected-value="<%= query.
|
|
16
|
+
data-janela--chart-selected-value="<%= query.selected_values.to_json %>"
|
|
17
17
|
data-janela--chart-labels-value="<%= result.keys.to_json %>"
|
|
18
18
|
data-janela--chart-values-value="<%= result.values.map(&:to_f).to_json %>"
|
|
19
19
|
data-janela--chart-formatted-value="<%= result.values.map { |measured| query.format(measured) }.to_json %>"
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<td>
|
|
30
30
|
<% if key %>
|
|
31
31
|
<button type="button"
|
|
32
|
-
aria-pressed="<%=
|
|
32
|
+
aria-pressed="<%= query.selected?(label) %>"
|
|
33
33
|
data-action="janela--frame#toggle"
|
|
34
34
|
data-janela--frame-key-param="<%= key %>"
|
|
35
35
|
data-janela--frame-value-param="<%= value %>">
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Deliberately minimal: the engine cannot know the host's asset names or
|
|
3
3
|
bundler, and a host layout's route helpers do not resolve inside an
|
|
4
4
|
isolated engine (ADR 011). It loads Janela's own stylesheet, which the gem
|
|
5
|
-
ships and can therefore name, and no JavaScript: Turbo and Stimulus come
|
|
5
|
+
ships and can therefore name, plus a theme if one is set, and no JavaScript: Turbo and Stimulus come
|
|
6
6
|
from the host, so these pages are static dashboards rather than broken
|
|
7
7
|
ones. A host can override this file, or point the controller at its own
|
|
8
8
|
layout. %>
|
|
@@ -15,9 +15,18 @@
|
|
|
15
15
|
<%= csrf_meta_tags %>
|
|
16
16
|
<%= csp_meta_tag %>
|
|
17
17
|
<%= stylesheet_link_tag "janela" %>
|
|
18
|
+
<%= stylesheet_link_tag Janela.theme if Janela.theme.present? %>
|
|
18
19
|
<%= yield :head %>
|
|
19
20
|
</head>
|
|
20
21
|
<body class="janela-page">
|
|
22
|
+
<%# Janela's own pages are reached from somewhere, and until ADR 022 the
|
|
23
|
+
engine could not name a host route to get back to. It can now, so the
|
|
24
|
+
layout stops being a dead end. Only when the host has a root, and
|
|
25
|
+
never a guess at what to call the place it goes. %>
|
|
26
|
+
<% if main_app.respond_to?(:root_path) %>
|
|
27
|
+
<p class="janela-exit"><%= link_to t("janela.actions.home"), main_app.root_path %></p>
|
|
28
|
+
<% end %>
|
|
29
|
+
|
|
21
30
|
<% flash.each do |_kind, message| %>
|
|
22
31
|
<p class="janela-flash"><%= message %></p>
|
|
23
32
|
<% end %>
|
data/config/importmap.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
pin "janela/frame_controller", to: "janela/frame_controller.js"
|
|
2
2
|
pin "janela/chart_controller", to: "janela/chart_controller.js"
|
|
3
|
+
pin "janela/vitral_controller", to: "janela/vitral_controller.js"
|
|
3
4
|
|
|
4
5
|
# Hosts that already pin their own Chart.js keep it.
|
|
5
6
|
pin "chart.js", to: "janela/vendor/chart.js" unless packages.key?("chart.js")
|