@gitlab/ui 137.3.1 → 138.0.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.
Files changed (79) hide show
  1. package/dist/components/base/alert/alert.js +0 -1
  2. package/dist/components/base/drawer/drawer.js +0 -1
  3. package/dist/components/base/filtered_search/filtered_search_token_segment.js +0 -1
  4. package/dist/components/base/form/form_group/form_group.js +0 -1
  5. package/dist/components/base/modal/modal.js +1 -5
  6. package/dist/components/base/new_dropdowns/base_dropdown/dropdown_container.js +1 -1
  7. package/dist/components/base/new_dropdowns/disclosure/disclosure_dropdown.js +1 -3
  8. package/dist/components/base/toast/index.js +1 -1
  9. package/dist/components/base/toggle/toggle.js +0 -1
  10. package/dist/components/mixins/tooltip_mixin.js +1 -1
  11. package/dist/index.css +2 -2
  12. package/dist/index.css.map +1 -1
  13. package/dist/tailwind.css +1 -1
  14. package/dist/tailwind.css.map +1 -1
  15. package/dist/tokens/build/js/tokens.dark.js +299 -299
  16. package/dist/tokens/build/js/tokens.js +302 -302
  17. package/dist/utils/confetti/confetti.js +189 -0
  18. package/dist/utils/confetti/constants.js +36 -0
  19. package/dist/utils/confetti/field.js +114 -0
  20. package/dist/utils/confetti/palette.js +6 -0
  21. package/dist/utils/confetti/particles.js +92 -0
  22. package/dist/utils/matrix/columns.js +54 -0
  23. package/dist/utils/matrix/constants.js +19 -0
  24. package/dist/utils/matrix/matrix.js +157 -0
  25. package/dist/utils/matrix/palette.js +9 -0
  26. package/dist/utils.js +2 -0
  27. package/dist/vendor/bootstrap-vue/src/components/popover/helpers/bv-popover-template.js +4 -2
  28. package/dist/vendor/bootstrap-vue/src/components/tooltip/helpers/bv-tooltip-template.js +3 -1
  29. package/package.json +3 -2
  30. package/src/components/base/alert/alert.vue +0 -1
  31. package/src/components/base/drawer/drawer.vue +0 -1
  32. package/src/components/base/filtered_search/filtered_search_token_segment.vue +0 -1
  33. package/src/components/base/form/form_group/form_group.vue +0 -2
  34. package/src/components/base/form/form_select/form_select.vue +0 -1
  35. package/src/components/base/modal/modal.vue +0 -3
  36. package/src/components/base/new_dropdowns/base_dropdown/dropdown_container.js +1 -1
  37. package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.vue +0 -1
  38. package/src/components/base/tabs/tabs/scrollable_tabs.vue +0 -1
  39. package/src/components/base/tabs/tabs/tabs.vue +0 -1
  40. package/src/components/base/toast/index.js +1 -1
  41. package/src/components/base/toggle/toggle.vue +0 -1
  42. package/src/components/charts/heatmap/index.js +1 -1
  43. package/src/components/mixins/tooltip_mixin.js +1 -1
  44. package/src/tokens/build/css/tokens.css +74 -74
  45. package/src/tokens/build/css/tokens.dark.css +70 -70
  46. package/src/tokens/build/docs/tokens-tailwind-docs.dark.json +303 -303
  47. package/src/tokens/build/docs/tokens-tailwind-docs.json +323 -323
  48. package/src/tokens/build/figma/constants.dark.json +207 -207
  49. package/src/tokens/build/figma/constants.json +207 -207
  50. package/src/tokens/build/figma/mode.dark.json +37 -37
  51. package/src/tokens/build/figma/mode.json +35 -35
  52. package/src/tokens/build/js/tokens.dark.js +299 -300
  53. package/src/tokens/build/js/tokens.js +302 -303
  54. package/src/tokens/build/json/tokens.dark.json +369 -369
  55. package/src/tokens/build/json/tokens.json +376 -376
  56. package/src/tokens/build/scss/_tokens.dark.scss +70 -70
  57. package/src/tokens/build/scss/_tokens.scss +74 -74
  58. package/src/tokens/constant/color.blue.tokens.json +39 -39
  59. package/src/tokens/constant/color.green.tokens.json +41 -41
  60. package/src/tokens/constant/color.orange.tokens.json +43 -43
  61. package/src/tokens/constant/color.purple.tokens.json +40 -40
  62. package/src/tokens/constant/color.red.tokens.json +44 -44
  63. package/src/tokens/contextual/avatar.tokens.json +13 -13
  64. package/src/tokens/contextual/button.tokens.json +22 -22
  65. package/src/tokens/contextual/chart.tokens.json +3 -3
  66. package/src/tokens/semantic/action.tokens.json +18 -18
  67. package/src/utils/confetti/confetti.js +191 -0
  68. package/src/utils/confetti/constants.js +42 -0
  69. package/src/utils/confetti/field.js +134 -0
  70. package/src/utils/confetti/palette.js +15 -0
  71. package/src/utils/confetti/particles.js +98 -0
  72. package/src/utils/matrix/columns.js +53 -0
  73. package/src/utils/matrix/constants.js +19 -0
  74. package/src/utils/matrix/matrix.js +185 -0
  75. package/src/utils/matrix/palette.js +18 -0
  76. package/src/utils.js +2 -0
  77. package/src/vendor/bootstrap-vue/src/components/popover/helpers/bv-popover-template.js +7 -2
  78. package/src/vendor/bootstrap-vue/src/components/tooltip/helpers/bv-tooltip-template.js +2 -1
  79. package/translations.js +1 -1
@@ -0,0 +1,189 @@
1
+ import { COVERAGE_RAMP } from './palette';
2
+ import { createPieces, advancePiece } from './particles';
3
+ import { createField } from './field';
4
+ import { CANVAS_Z_INDEX, DEFAULT_DURATION, FADE_OUT_DURATION, MAX_FRAME_DELTA } from './constants';
5
+
6
+ const noop = () => {};
7
+ const RUNNING_ATTRIBUTE = 'data-confetti';
8
+ const measureStage = boundary => {
9
+ const rect = boundary instanceof Element ? boundary.getBoundingClientRect() : {
10
+ left: 0,
11
+ top: 0,
12
+ width: window.innerWidth,
13
+ height: window.innerHeight
14
+ };
15
+ return {
16
+ x: rect.left,
17
+ y: rect.top,
18
+ width: rect.width,
19
+ height: rect.height
20
+ };
21
+ };
22
+ const stagePoint = (element, stage) => {
23
+ if (!(element instanceof Element)) {
24
+ return null;
25
+ }
26
+ const rect = element.getBoundingClientRect();
27
+ return {
28
+ x: rect.left + rect.width / 2 - stage.x,
29
+ y: rect.top + rect.height / 2 - stage.y
30
+ };
31
+ };
32
+ const createCanvas = () => {
33
+ const canvas = document.createElement('canvas');
34
+ canvas.setAttribute('aria-hidden', 'true');
35
+ canvas.setAttribute(RUNNING_ATTRIBUTE, '');
36
+ Object.assign(canvas.style, {
37
+ position: 'fixed',
38
+ inset: '0',
39
+ width: '100%',
40
+ height: '100%',
41
+ pointerEvents: 'none',
42
+ zIndex: String(CANVAS_Z_INDEX)
43
+ });
44
+ return canvas;
45
+ };
46
+
47
+ /**
48
+ * Bursts a dithered field of pixel blocks, lets it fall, then cleans up after itself.
49
+ *
50
+ * Every option has a default, so `confetti()` on its own fires from the bottom corners of the
51
+ * viewport. The blocks shrink as they fall and as they fade, and the ones furthest back start
52
+ * smaller, which is what gives the field its depth.
53
+ *
54
+ * Only one burst runs at a time: a call made while another is still on screen does nothing and
55
+ * returns a no-op, so a double click cannot stack two canvases.
56
+ *
57
+ * Does nothing when the user prefers reduced motion, or outside a browser.
58
+ *
59
+ * The canvas is `aria-hidden`, so nobody using a screen reader will know it fired. When the
60
+ * confetti is the only sign that something worked, the caller has to convey that success in the
61
+ * accessibility tree as well.
62
+ *
63
+ * @param {Object} [options]
64
+ * @param {Number} [options.count] Number of pieces. Defaults to a count scaled to the area it
65
+ * plays in, so a small `boundary` gets proportionally fewer.
66
+ * @param {Number} [options.duration=3000] How long the animation runs, in milliseconds.
67
+ * @param {String[]} [options.colors] CSS colors for the coverage ramp, sparse to dense: the first
68
+ * paints the thinnest edge of a cluster and the last its densest center. Any length works, and the
69
+ * first four double as the chromatic fringe colors. Defaults to the brand purple, red and orange
70
+ * color tokens.
71
+ * @param {Element} [options.origin] Element to burst from. Defaults to the bottom corners.
72
+ * @param {Element} [options.boundary] Element to confine the confetti to. Defaults to the viewport.
73
+ * @example
74
+ * const stop = confetti({ origin: button, colors: ['#7759c2', '#a989f5', '#fca326'] });
75
+ * @returns {Function} Stops the animation and removes the canvas early.
76
+ */
77
+ const confetti = function () {
78
+ var _window$matchMedia, _window;
79
+ let _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
80
+ count = _ref.count,
81
+ _ref$duration = _ref.duration,
82
+ duration = _ref$duration === void 0 ? DEFAULT_DURATION : _ref$duration,
83
+ colors = _ref.colors,
84
+ origin = _ref.origin,
85
+ boundary = _ref.boundary;
86
+ if (typeof window === 'undefined' || (_window$matchMedia = (_window = window).matchMedia) !== null && _window$matchMedia !== void 0 && _window$matchMedia.call(_window, '(prefers-reduced-motion: reduce)').matches || document.querySelector(`[${RUNNING_ATTRIBUTE}]`)) {
87
+ return noop;
88
+ }
89
+ const canvas = createCanvas();
90
+ const context = canvas.getContext('2d');
91
+ if (!context) {
92
+ return noop;
93
+ }
94
+ const stage = measureStage(boundary);
95
+ const resize = () => {
96
+ const ratio = window.devicePixelRatio || 1;
97
+ canvas.width = window.innerWidth * ratio;
98
+ canvas.height = window.innerHeight * ratio;
99
+ context.setTransform(ratio, 0, 0, ratio, 0, 0);
100
+ };
101
+ const fadeOutDuration = Math.min(FADE_OUT_DURATION, duration);
102
+ const field = createField({
103
+ ramp: colors !== null && colors !== void 0 && colors.length ? colors : COVERAGE_RAMP,
104
+ duration
105
+ });
106
+ const cleared = {
107
+ x: 0,
108
+ y: 0,
109
+ width: 0,
110
+ height: 0
111
+ };
112
+ let lastColor = null;
113
+ const drawBlock = _ref2 => {
114
+ let x = _ref2.x,
115
+ y = _ref2.y,
116
+ size = _ref2.size,
117
+ color = _ref2.color;
118
+ if (color !== lastColor) {
119
+ lastColor = color;
120
+ context.fillStyle = color;
121
+ }
122
+ context.fillRect(x, y, size, size);
123
+ };
124
+ const clearStage = () => {
125
+ const moved = stage.x !== cleared.x || stage.y !== cleared.y || stage.width !== cleared.width || stage.height !== cleared.height;
126
+ if (moved) {
127
+ context.clearRect(0, 0, window.innerWidth, window.innerHeight);
128
+ Object.assign(cleared, stage);
129
+ } else {
130
+ context.clearRect(stage.x, stage.y, stage.width, stage.height);
131
+ }
132
+ };
133
+ let pieces = createPieces({
134
+ stage,
135
+ origin: stagePoint(origin, stage),
136
+ count
137
+ });
138
+ let frame = null;
139
+ let startedAt = null;
140
+ let previousAt = null;
141
+ const stop = () => {
142
+ if (frame !== null) {
143
+ window.cancelAnimationFrame(frame);
144
+ frame = null;
145
+ }
146
+ window.removeEventListener('resize', resize);
147
+ canvas.remove();
148
+ };
149
+ const render = now => {
150
+ var _startedAt, _previousAt;
151
+ (_startedAt = startedAt) !== null && _startedAt !== void 0 ? _startedAt : startedAt = now;
152
+ const elapsed = now - startedAt;
153
+ const delta = Math.min(now - ((_previousAt = previousAt) !== null && _previousAt !== void 0 ? _previousAt : now), MAX_FRAME_DELTA);
154
+ previousAt = now;
155
+ if (elapsed >= duration) {
156
+ stop();
157
+ return;
158
+ }
159
+ Object.assign(stage, measureStage(boundary));
160
+ clearStage();
161
+ lastColor = null;
162
+ context.globalAlpha = Math.min(1, (duration - elapsed) / fadeOutDuration);
163
+ context.save();
164
+ context.beginPath();
165
+ context.rect(stage.x, stage.y, stage.width, stage.height);
166
+ context.clip();
167
+ context.translate(stage.x, stage.y);
168
+ pieces = pieces.map(piece => advancePiece(piece, {
169
+ elapsed,
170
+ delta
171
+ }));
172
+ field.resize(stage);
173
+ pieces.forEach(piece => {
174
+ if (elapsed >= piece.delay) {
175
+ field.stamp(piece, elapsed);
176
+ }
177
+ });
178
+ field.paint(drawBlock);
179
+ context.restore();
180
+ frame = window.requestAnimationFrame(render);
181
+ };
182
+ resize();
183
+ window.addEventListener('resize', resize);
184
+ document.body.append(canvas);
185
+ frame = window.requestAnimationFrame(render);
186
+ return stop;
187
+ };
188
+
189
+ export { confetti };
@@ -0,0 +1,36 @@
1
+ const DEFAULT_COUNT = 168;
2
+ const MIN_AUTO_COUNT = 36;
3
+ const AREA_EXPONENT = 0.4;
4
+ const DEFAULT_DURATION = 3000;
5
+ const FADE_OUT_DURATION = 600;
6
+ const CANVAS_Z_INDEX = 9999;
7
+ const MAX_FRAME_DELTA = 50;
8
+ const EDGE_ANGLE = 54 * Math.PI / 180;
9
+ const POINT_ANGLE = Math.PI / 2;
10
+ const POINT_POWER = 0.8;
11
+ const MIN_REACH = 0.04;
12
+ const REACH_HEADROOM = 1.3;
13
+ const BAYER = [[0, 8, 2, 10], [12, 4, 14, 6], [3, 11, 1, 9], [15, 7, 13, 5]];
14
+ const CONFIG = {
15
+ burstWindow: 220,
16
+ edgeSpread: 46,
17
+ pointSpread: 75,
18
+ aimJitter: 5,
19
+ drag: 0.0022,
20
+ gravity: 0.0005,
21
+ cell: 10,
22
+ fringeCells: 2,
23
+ blobCells: 1.4,
24
+ blobCellRange: 2,
25
+ stretch: 1.6,
26
+ stretchRange: 1.4,
27
+ peak: 0.55,
28
+ peakRange: 0.45,
29
+ falloff: 1.3,
30
+ decayMin: 0.45,
31
+ pixelFloor: 0.5,
32
+ pixelFade: 0.6,
33
+ pixelMin: 2
34
+ };
35
+
36
+ export { AREA_EXPONENT, BAYER, CANVAS_Z_INDEX, CONFIG, DEFAULT_COUNT, DEFAULT_DURATION, EDGE_ANGLE, FADE_OUT_DURATION, MAX_FRAME_DELTA, MIN_AUTO_COUNT, MIN_REACH, POINT_ANGLE, POINT_POWER, REACH_HEADROOM };
@@ -0,0 +1,114 @@
1
+ import { BAYER, CONFIG } from './constants';
2
+ import { FRINGE_KEYS } from './palette';
3
+
4
+ const SPAN = BAYER.length;
5
+ const LEVELS = SPAN * SPAN;
6
+ const createField = _ref => {
7
+ let ramp = _ref.ramp,
8
+ duration = _ref.duration;
9
+ const last = ramp.length - 1;
10
+ let columns = 0;
11
+ let rows = 0;
12
+ let coverage = new Float32Array(0);
13
+ let sizes = new Float32Array(0);
14
+ let fromColumn = 0;
15
+ let toColumn = -1;
16
+ let fromRow = 0;
17
+ let toRow = -1;
18
+ const valueAt = (column, row) => column < 0 || column >= columns ? 0 : coverage[row * columns + column];
19
+ const thresholdAt = (column, row) => BAYER[row % SPAN][column % SPAN] / LEVELS;
20
+ const fringeKey = (column, row, threshold) => (valueAt(column - CONFIG.fringeCells, row) > threshold ? 1 : 0) + (valueAt(column, row) > threshold ? 2 : 0) + (valueAt(column + CONFIG.fringeCells, row) > threshold ? 4 : 0);
21
+ const fringeColor = (column, row, threshold) => {
22
+ const index = FRINGE_KEYS[fringeKey(column, row, threshold)];
23
+ return index === null ? null : ramp[Math.min(last, index)];
24
+ };
25
+ const coverageColor = value => ramp[Math.min(last, Math.floor(value * ramp.length))];
26
+ const pixelSize = index => Math.max(CONFIG.pixelMin, Math.round(CONFIG.cell * sizes[index]));
27
+ const resize = stage => {
28
+ const nextColumns = Math.ceil(stage.width / CONFIG.cell) + 1;
29
+ const nextRows = Math.ceil(stage.height / CONFIG.cell) + 1;
30
+ if (nextColumns !== columns || nextRows !== rows) {
31
+ columns = nextColumns;
32
+ rows = nextRows;
33
+ coverage = new Float32Array(columns * rows);
34
+ sizes = new Float32Array(columns * rows);
35
+ } else {
36
+ coverage.fill(0);
37
+ }
38
+ fromColumn = columns;
39
+ toColumn = -1;
40
+ fromRow = rows;
41
+ toRow = -1;
42
+ };
43
+ const stamp = (piece, elapsed) => {
44
+ const age = (elapsed - piece.delay) / (duration * piece.decayFrom);
45
+ const strength = piece.peak * Math.max(0, 1 - age);
46
+ if (strength <= 0) {
47
+ return;
48
+ }
49
+ const fade = 1 - CONFIG.pixelFade * age;
50
+ const near = CONFIG.pixelFloor + piece.depth * (1 - CONFIG.pixelFloor);
51
+ const size = near * fade;
52
+ const radius = piece.radius * fade;
53
+ const stretch = piece.stretch;
54
+ const centerX = piece.x / CONFIG.cell;
55
+ const centerY = piece.y / CONFIG.cell;
56
+ const speed = Math.hypot(piece.velocityX, piece.velocityY) || 1;
57
+ const alongX = piece.velocityX / speed;
58
+ const alongY = piece.velocityY / speed;
59
+ const reach = radius * stretch;
60
+ const fromX = Math.max(0, Math.ceil(centerX - reach));
61
+ const toX = Math.min(columns - 1, Math.floor(centerX + reach));
62
+ const fromY = Math.max(0, Math.ceil(centerY - reach));
63
+ const toY = Math.min(rows - 1, Math.floor(centerY + reach));
64
+ fromColumn = Math.min(fromColumn, fromX);
65
+ toColumn = Math.max(toColumn, toX);
66
+ fromRow = Math.min(fromRow, fromY);
67
+ toRow = Math.max(toRow, toY);
68
+ for (let row = fromY; row <= toY; row += 1) {
69
+ const deltaY = row - centerY;
70
+ for (let column = fromX; column <= toX; column += 1) {
71
+ const deltaX = column - centerX;
72
+ const along = (deltaX * alongX + deltaY * alongY) / stretch;
73
+ const across = deltaX * alongY - deltaY * alongX;
74
+ const distance = Math.hypot(along, across) / radius;
75
+ if (distance < 1) {
76
+ const index = row * columns + column;
77
+ const value = strength * (1 - distance) ** CONFIG.falloff;
78
+ if (value > coverage[index]) {
79
+ coverage[index] = value;
80
+ sizes[index] = size;
81
+ }
82
+ }
83
+ }
84
+ }
85
+ };
86
+ const paint = drawBlock => {
87
+ for (let row = fromRow; row <= toRow; row += 1) {
88
+ for (let column = fromColumn; column <= toColumn; column += 1) {
89
+ var _fringeColor;
90
+ const index = row * columns + column;
91
+ const threshold = thresholdAt(column, row);
92
+ const value = coverage[index];
93
+ if (value <= threshold) {
94
+ continue;
95
+ }
96
+ const size = pixelSize(index);
97
+ const inset = Math.round((CONFIG.cell - size) / 2);
98
+ drawBlock({
99
+ x: column * CONFIG.cell + inset,
100
+ y: row * CONFIG.cell + inset,
101
+ size,
102
+ color: (_fringeColor = fringeColor(column, row, threshold)) !== null && _fringeColor !== void 0 ? _fringeColor : coverageColor(value)
103
+ });
104
+ }
105
+ }
106
+ };
107
+ return {
108
+ resize,
109
+ stamp,
110
+ paint
111
+ };
112
+ };
113
+
114
+ export { createField };
@@ -0,0 +1,6 @@
1
+ import { GL_COLOR_BRAND_PURPLE_02P, GL_COLOR_RED_500, GL_COLOR_BRAND_ORANGE_02P, GL_COLOR_BRAND_ORANGE_01P } from '../../tokens/build/js/tokens';
2
+
3
+ const COVERAGE_RAMP = [GL_COLOR_BRAND_PURPLE_02P, GL_COLOR_RED_500, GL_COLOR_BRAND_ORANGE_02P, GL_COLOR_BRAND_ORANGE_01P];
4
+ const FRINGE_KEYS = [null, null, 0, 1, null, 2, 3, null];
5
+
6
+ export { COVERAGE_RAMP, FRINGE_KEYS };
@@ -0,0 +1,92 @@
1
+ import { REACH_HEADROOM, CONFIG, MIN_AUTO_COUNT, DEFAULT_COUNT, AREA_EXPONENT, EDGE_ANGLE, POINT_ANGLE, POINT_POWER, MIN_REACH } from './constants';
2
+
3
+ const randomBetween = (min, max) => min + Math.random() * (max - min);
4
+ const degrees = value => value * Math.PI / 180;
5
+ const aimJitter = () => randomBetween(-degrees(CONFIG.aimJitter), degrees(CONFIG.aimJitter));
6
+ const countFor = stage => {
7
+ const share = stage.width * stage.height / (window.innerWidth * window.innerHeight);
8
+ return Math.round(Math.max(MIN_AUTO_COUNT, DEFAULT_COUNT * share ** AREA_EXPONENT));
9
+ };
10
+ const launchFromEdges = (stage, index) => {
11
+ const fromLeft = index % 2 === 0;
12
+ return {
13
+ x: fromLeft ? -10 : stage.width + 10,
14
+ y: stage.height + 10,
15
+ aim: Math.max(EDGE_ANGLE, Math.atan2(stage.height, stage.width)),
16
+ spread: degrees(CONFIG.edgeSpread),
17
+ direction: fromLeft ? 1 : -1,
18
+ power: 1
19
+ };
20
+ };
21
+ const launchFromPoint = origin => ({
22
+ x: origin.x,
23
+ y: origin.y,
24
+ aim: POINT_ANGLE,
25
+ spread: degrees(CONFIG.pointSpread),
26
+ direction: 1,
27
+ power: POINT_POWER
28
+ });
29
+ const createPiece = _ref => {
30
+ let stage = _ref.stage,
31
+ origin = _ref.origin,
32
+ index = _ref.index,
33
+ jitter = _ref.jitter,
34
+ maxSpeed = _ref.maxSpeed;
35
+ const launch = origin ? launchFromPoint(origin) : launchFromEdges(stage, index);
36
+ const depth = Math.random();
37
+ const reach = Math.sqrt(randomBetween(MIN_REACH, 1));
38
+ const speed = maxSpeed * launch.power * reach * (0.85 + depth * 0.2);
39
+ const angle = launch.aim + jitter + randomBetween(-launch.spread, launch.spread);
40
+ return {
41
+ depth,
42
+ x: launch.x,
43
+ y: launch.y,
44
+ delay: randomBetween(0, CONFIG.burstWindow),
45
+ radius: CONFIG.blobCells + depth * CONFIG.blobCellRange,
46
+ stretch: CONFIG.stretch + Math.random() * CONFIG.stretchRange,
47
+ peak: CONFIG.peak + depth * CONFIG.peakRange,
48
+ decayFrom: randomBetween(CONFIG.decayMin, 1),
49
+ velocityX: Math.cos(angle) * speed * launch.direction,
50
+ velocityY: -Math.sin(angle) * speed,
51
+ swayAmplitude: randomBetween(0.02, 0.08) * (1.3 - depth * 0.5),
52
+ swayFrequency: randomBetween(0.001, 0.003),
53
+ swayPhase: Math.random() * Math.PI * 2
54
+ };
55
+ };
56
+ const createPieces = _ref2 => {
57
+ let stage = _ref2.stage,
58
+ origin = _ref2.origin,
59
+ count = _ref2.count;
60
+ const travel = Math.max(Math.hypot(stage.width, stage.height), stage.height * REACH_HEADROOM);
61
+ const maxSpeed = travel * CONFIG.drag;
62
+ const jitter = aimJitter();
63
+ return Array.from({
64
+ length: count !== null && count !== void 0 ? count : countFor(stage)
65
+ }, (value, index) => createPiece({
66
+ stage,
67
+ origin,
68
+ index,
69
+ jitter,
70
+ maxSpeed
71
+ }));
72
+ };
73
+ const advancePiece = (piece, _ref3) => {
74
+ let elapsed = _ref3.elapsed,
75
+ delta = _ref3.delta;
76
+ if (elapsed < piece.delay) {
77
+ return piece;
78
+ }
79
+ const drag = Math.exp(-CONFIG.drag * delta);
80
+ const velocityX = piece.velocityX * drag;
81
+ const velocityY = (piece.velocityY + CONFIG.gravity * delta) * drag;
82
+ const sway = Math.sin(piece.swayPhase + elapsed * piece.swayFrequency) * piece.swayAmplitude;
83
+ return {
84
+ ...piece,
85
+ velocityX,
86
+ velocityY,
87
+ x: piece.x + (velocityX + sway) * delta,
88
+ y: piece.y + velocityY * delta
89
+ };
90
+ };
91
+
92
+ export { advancePiece, createPieces };
@@ -0,0 +1,54 @@
1
+ import { CONFIG, GLYPHS } from './constants';
2
+
3
+ const randomBetween = (min, max) => min + Math.random() * (max - min);
4
+ const sample = values => values[Math.floor(Math.random() * values.length)];
5
+ const glyph = () => sample(GLYPHS);
6
+ const measureGrid = _ref => {
7
+ let width = _ref.width,
8
+ height = _ref.height;
9
+ return {
10
+ across: Math.ceil(width / CONFIG.cell),
11
+ rows: Math.ceil(height / CONFIG.cell)
12
+ };
13
+ };
14
+ const createColumns = (stage, colors) => {
15
+ const _measureGrid = measureGrid(stage),
16
+ across = _measureGrid.across,
17
+ rows = _measureGrid.rows;
18
+ return Array.from({
19
+ length: across
20
+ }, (value, column) => ({
21
+ column,
22
+ rows,
23
+ row: -randomBetween(0, CONFIG.startSpread),
24
+ speed: randomBetween(CONFIG.minSpeed, CONFIG.maxSpeed),
25
+ color: sample(colors),
26
+ head: null,
27
+ steps: []
28
+ })).filter(() => Math.random() < CONFIG.columnShare);
29
+ };
30
+ const advanceColumn = (column, delta) => {
31
+ if (column.row > column.rows + CONFIG.restartAfter) {
32
+ return {
33
+ ...column,
34
+ row: -randomBetween(0, CONFIG.restartSpread),
35
+ head: null,
36
+ steps: []
37
+ };
38
+ }
39
+ const row = column.row + column.speed * delta;
40
+ const from = Math.max(0, Math.ceil(column.row));
41
+ const to = Math.min(column.rows, Math.floor(row));
42
+ const steps = [];
43
+ for (let step = from; step <= to; step += 1) {
44
+ steps.push(step);
45
+ }
46
+ return {
47
+ ...column,
48
+ row,
49
+ head: steps.length ? steps.at(-1) : column.head,
50
+ steps
51
+ };
52
+ };
53
+
54
+ export { advanceColumn, createColumns, glyph, measureGrid };
@@ -0,0 +1,19 @@
1
+ const GLYPHS = ['G', 'L'];
2
+ const FONT = 'ui-monospace, SFMono-Regular, Menlo, monospace';
3
+ const DEFAULT_DURATION = 6000;
4
+ const FADE_OUT_DURATION = 900;
5
+ const CANVAS_Z_INDEX = 9999;
6
+ const MAX_FRAME_DELTA = 50;
7
+ const RUNNING_ATTRIBUTE = 'data-matrix';
8
+ const CONFIG = {
9
+ cell: 16,
10
+ trail: 0.0016,
11
+ minSpeed: 0.004,
12
+ maxSpeed: 0.014,
13
+ columnShare: 0.72,
14
+ startSpread: 30,
15
+ restartSpread: 10,
16
+ restartAfter: 3
17
+ };
18
+
19
+ export { CANVAS_Z_INDEX, CONFIG, DEFAULT_DURATION, FADE_OUT_DURATION, FONT, GLYPHS, MAX_FRAME_DELTA, RUNNING_ATTRIBUTE };
@@ -0,0 +1,157 @@
1
+ import { resolveColors } from './palette';
2
+ import { createColumns, measureGrid, advanceColumn, glyph } from './columns';
3
+ import { RUNNING_ATTRIBUTE, CANVAS_Z_INDEX, FADE_OUT_DURATION, CONFIG, FONT, DEFAULT_DURATION, MAX_FRAME_DELTA } from './constants';
4
+
5
+ const noop = () => {};
6
+ const measureStage = boundary => {
7
+ const rect = boundary instanceof Element ? boundary.getBoundingClientRect() : {
8
+ left: 0,
9
+ top: 0,
10
+ width: window.innerWidth,
11
+ height: window.innerHeight
12
+ };
13
+ return {
14
+ x: rect.left,
15
+ y: rect.top,
16
+ width: rect.width,
17
+ height: rect.height
18
+ };
19
+ };
20
+ const createCanvas = () => {
21
+ const canvas = document.createElement('canvas');
22
+ canvas.setAttribute('aria-hidden', 'true');
23
+ canvas.setAttribute(RUNNING_ATTRIBUTE, '');
24
+ Object.assign(canvas.style, {
25
+ position: 'fixed',
26
+ inset: '0',
27
+ width: '100%',
28
+ height: '100%',
29
+ pointerEvents: 'none',
30
+ zIndex: String(CANVAS_Z_INDEX)
31
+ });
32
+ return canvas;
33
+ };
34
+
35
+ /**
36
+ * Rains `G` and `L` glyphs down the screen, then cleans up after itself.
37
+ *
38
+ * Every option has a default, so `matrix()` on its own rains over the whole viewport. Pass
39
+ * `boundary` to keep the rain inside an element. Each column falls at its own speed in one color
40
+ * from the brand ramp, with a head glyph in the default text color and a trail that fades behind it.
41
+ *
42
+ * Only one run happens at a time: a call made while another is still on screen does nothing and
43
+ * returns a no-op.
44
+ *
45
+ * Does nothing when the user prefers reduced motion, or outside a browser.
46
+ *
47
+ * The canvas is `aria-hidden`, so nobody using a screen reader will know it ran. When the rain is
48
+ * the only sign that something happened, the caller has to convey that in the accessibility tree
49
+ * as well.
50
+ *
51
+ * @param {Object} [options]
52
+ * @param {Number} [options.duration=6000] How long the rain runs, in milliseconds.
53
+ * @param {String[]} [options.colors] CSS colors for the columns, picked at random one per column.
54
+ * Defaults to the brand orange and purple and the red color tokens. The head glyph's text color
55
+ * resolves against `boundary` when given, so a `.gl-dark-scope` ancestor is honored.
56
+ * @param {Element} [options.boundary] Element to confine the rain to. Defaults to the viewport.
57
+ * @example
58
+ * const stop = matrix({ boundary: terminal, duration: 4000 });
59
+ * @returns {Function} Stops the rain and removes the canvas early.
60
+ */
61
+ const matrix = function () {
62
+ var _window$matchMedia, _window;
63
+ let _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
64
+ _ref$duration = _ref.duration,
65
+ duration = _ref$duration === void 0 ? DEFAULT_DURATION : _ref$duration,
66
+ colors = _ref.colors,
67
+ boundary = _ref.boundary;
68
+ if (typeof window === 'undefined' || (_window$matchMedia = (_window = window).matchMedia) !== null && _window$matchMedia !== void 0 && _window$matchMedia.call(_window, '(prefers-reduced-motion: reduce)').matches || document.querySelector(`[${RUNNING_ATTRIBUTE}]`)) {
69
+ return noop;
70
+ }
71
+ const canvas = createCanvas();
72
+ const context = canvas.getContext('2d');
73
+ if (!context) {
74
+ return noop;
75
+ }
76
+ const stage = measureStage(boundary);
77
+ const resize = () => {
78
+ const ratio = window.devicePixelRatio || 1;
79
+ canvas.width = window.innerWidth * ratio;
80
+ canvas.height = window.innerHeight * ratio;
81
+ context.setTransform(ratio, 0, 0, ratio, 0, 0);
82
+ };
83
+ const _resolveColors = resolveColors(colors, boundary instanceof Element ? boundary : document.documentElement),
84
+ head = _resolveColors.head,
85
+ rain = _resolveColors.rain;
86
+ let columns = createColumns(stage, rain);
87
+ const erase = delta => {
88
+ context.globalCompositeOperation = 'destination-out';
89
+ context.fillStyle = `rgba(0, 0, 0, ${1 - Math.exp(-CONFIG.trail * delta)})`;
90
+ context.fillRect(0, 0, stage.width, stage.height);
91
+ context.globalCompositeOperation = 'source-over';
92
+ };
93
+ const paintColumn = (column, previousHead) => {
94
+ const x = column.column * CONFIG.cell;
95
+ if (previousHead !== null) {
96
+ context.fillStyle = column.color;
97
+ context.fillText(glyph(), x, previousHead * CONFIG.cell);
98
+ }
99
+ column.steps.forEach((row, index) => {
100
+ context.fillStyle = index === column.steps.length - 1 ? head : column.color;
101
+ context.fillText(glyph(), x, row * CONFIG.cell);
102
+ });
103
+ };
104
+ let frame = null;
105
+ let startedAt = null;
106
+ let previousAt = null;
107
+ const stop = () => {
108
+ if (frame !== null) {
109
+ window.cancelAnimationFrame(frame);
110
+ frame = null;
111
+ }
112
+ window.removeEventListener('resize', resize);
113
+ canvas.remove();
114
+ };
115
+ const render = now => {
116
+ var _startedAt, _previousAt;
117
+ (_startedAt = startedAt) !== null && _startedAt !== void 0 ? _startedAt : startedAt = now;
118
+ const elapsed = now - startedAt;
119
+ const delta = Math.min(now - ((_previousAt = previousAt) !== null && _previousAt !== void 0 ? _previousAt : now), MAX_FRAME_DELTA);
120
+ previousAt = now;
121
+ if (elapsed >= duration) {
122
+ stop();
123
+ return;
124
+ }
125
+ const grid = measureGrid(stage);
126
+ Object.assign(stage, measureStage(boundary));
127
+ const nextGrid = measureGrid(stage);
128
+ if (nextGrid.across !== grid.across || nextGrid.rows !== grid.rows) {
129
+ columns = createColumns(stage, rain);
130
+ }
131
+ canvas.style.opacity = String(Math.min(1, (duration - elapsed) / FADE_OUT_DURATION));
132
+ context.save();
133
+ context.beginPath();
134
+ context.rect(stage.x, stage.y, stage.width, stage.height);
135
+ context.clip();
136
+ context.translate(stage.x, stage.y);
137
+ erase(delta);
138
+ context.font = `${CONFIG.cell}px ${FONT}`;
139
+ context.textBaseline = 'top';
140
+ columns = columns.map(column => {
141
+ const next = advanceColumn(column, delta);
142
+ if (next.steps.length) {
143
+ paintColumn(next, column.head);
144
+ }
145
+ return next;
146
+ });
147
+ context.restore();
148
+ frame = window.requestAnimationFrame(render);
149
+ };
150
+ resize();
151
+ window.addEventListener('resize', resize);
152
+ document.body.append(canvas);
153
+ frame = window.requestAnimationFrame(render);
154
+ return stop;
155
+ };
156
+
157
+ export { matrix };
@@ -0,0 +1,9 @@
1
+ import { GL_COLOR_BRAND_ORANGE_02P, GL_COLOR_BRAND_ORANGE_01P, GL_COLOR_RED_500, GL_COLOR_BRAND_PURPLE_01P } from '../../tokens/build/js/tokens';
2
+
3
+ const MATRIX_COLORS = [GL_COLOR_BRAND_ORANGE_02P, GL_COLOR_BRAND_ORANGE_01P, GL_COLOR_RED_500, GL_COLOR_BRAND_PURPLE_01P];
4
+ const resolveColors = (colors, element) => ({
5
+ head: window.getComputedStyle(element).getPropertyValue('--gl-text-color-default').trim(),
6
+ rain: colors !== null && colors !== void 0 && colors.length ? colors : MATRIX_COLORS
7
+ });
8
+
9
+ export { resolveColors };
package/dist/utils.js CHANGED
@@ -3,3 +3,5 @@ import * as validators from './components/base/form/form_fields/validators';
3
3
  export { validators as formValidators };
4
4
  import * as mappers from './components/base/form/form_fields/mappers';
5
5
  export { mappers as formMappers };
6
+ export { matrix } from './utils/matrix/matrix';
7
+ export { confetti } from './utils/confetti/confetti';