@1024pix/pix-ui 55.9.0 → 55.11.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.
- package/addon/components/pix-app-layout.hbs +10 -3
- package/addon/components/pix-app-layout.js +26 -1
- package/addon/components/pix-gauge.hbs +98 -0
- package/addon/components/pix-gauge.js +96 -0
- package/addon/components/pix-navigation.js +1 -0
- package/addon/styles/_pix-app-layout.scss +54 -11
- package/addon/styles/_pix-gauge.scss +69 -0
- package/addon/styles/_pix-navigation.scss +2 -12
- package/addon/styles/addon.scss +1 -1
- package/app/components/pix-gauge.js +4 -0
- package/app/modifiers/on-window-resize.js +12 -0
- package/package.json +1 -1
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
<div class={{this.classNames}} ...attributes>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
<section
|
|
3
|
+
class="pix-app-layout__banner"
|
|
4
|
+
id="pix-layout-banner-container"
|
|
5
|
+
{{on-window-resize this.handleMarginContainerNavigation}}
|
|
6
|
+
>
|
|
7
|
+
{{yield to="banner"}}
|
|
8
|
+
</section>
|
|
9
|
+
<div class="pix-app-layout__navigation">{{yield to="navigation"}}</div>
|
|
10
|
+
<div class="pix-app-layout__main">{{yield to="main"}}</div>
|
|
11
|
+
<div class="pix-app-layout__footer">{{yield to="footer"}}</div>
|
|
5
12
|
</div>
|
|
@@ -1,8 +1,33 @@
|
|
|
1
1
|
import { VARIANTS } from '@1024pix/pix-ui/helpers/variants';
|
|
2
2
|
import { warn } from '@ember/debug';
|
|
3
|
+
import { service } from '@ember/service';
|
|
3
4
|
import Component from '@glimmer/component';
|
|
4
|
-
|
|
5
5
|
export default class PixAppLayout extends Component {
|
|
6
|
+
@service elementHelper;
|
|
7
|
+
|
|
8
|
+
constructor(...args) {
|
|
9
|
+
super(...args);
|
|
10
|
+
|
|
11
|
+
this.elementHelper.waitForElement('pix-layout-banner-container').then((elementList) => {
|
|
12
|
+
this.#computeMarginTopElement(elementList);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
#computeMarginTopElement(bannerContainer) {
|
|
17
|
+
const baseFontRemRatio = Number(
|
|
18
|
+
getComputedStyle(document.querySelector('html')).fontSize.match(/\d+(\.\d+)?/)[0],
|
|
19
|
+
);
|
|
20
|
+
const bannerHeight = bannerContainer.getBoundingClientRect().height;
|
|
21
|
+
const top = bannerHeight / baseFontRemRatio;
|
|
22
|
+
|
|
23
|
+
const layoutElement = document.querySelector('.pix-app-layout');
|
|
24
|
+
layoutElement.style.setProperty('--pix-app-layout-top', `${top}rem`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
handleMarginContainerNavigation = (element) => {
|
|
28
|
+
this.#computeMarginTopElement(element);
|
|
29
|
+
};
|
|
30
|
+
|
|
6
31
|
get variant() {
|
|
7
32
|
const value = this.args.variant ?? 'primary';
|
|
8
33
|
warn(
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<svg
|
|
2
|
+
xmlns:svg="http://www.w3.org/2000/svg"
|
|
3
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
4
|
+
viewBox={{this.viewBox}}
|
|
5
|
+
version="1.1"
|
|
6
|
+
preserveAspectRatio="none"
|
|
7
|
+
class="result-level-gauge {{if @isSmall 'result-level-gauge__small'}}"
|
|
8
|
+
role="progressbar"
|
|
9
|
+
aria-valuenow={{this.formatNumber this.reachedLevel}}
|
|
10
|
+
aria-valuemin={{0}}
|
|
11
|
+
aria-valuemax={{this.formatNumber this.maxLevel}}
|
|
12
|
+
aria-valuetext={{this.label}}
|
|
13
|
+
aria-label={{this.label}}
|
|
14
|
+
>
|
|
15
|
+
<title {{this.label}}></title>
|
|
16
|
+
|
|
17
|
+
{{! gauge grey background}}
|
|
18
|
+
<rect
|
|
19
|
+
y={{22}}
|
|
20
|
+
width="100%"
|
|
21
|
+
height={{this.backgroundHeight}}
|
|
22
|
+
rx={{this.greyGaugeRx}}
|
|
23
|
+
class="result-level-gauge__background"
|
|
24
|
+
/>
|
|
25
|
+
|
|
26
|
+
<g transform="translate(4, 0)">
|
|
27
|
+
{{! template-lint-disable no-inline-styles style-concatenation }}
|
|
28
|
+
{{! gauge white max level}}
|
|
29
|
+
<rect
|
|
30
|
+
y={{26}}
|
|
31
|
+
width="calc(calc(100% - 8px) * {{this.maxLevelPourcentage}})"
|
|
32
|
+
height={{this.whiteAndPurpleGaugesHeight}}
|
|
33
|
+
rx={{this.whiteAndPurpleGaugeRx}}
|
|
34
|
+
class="result-level-gauge__max-bar"
|
|
35
|
+
/>
|
|
36
|
+
{{#unless this.hideValues}}
|
|
37
|
+
<g style="transform: translate(calc(calc(100% - 8px) * {{this.maxLevelPourcentage}}))">
|
|
38
|
+
<text
|
|
39
|
+
y={{this.statsFontHeight}}
|
|
40
|
+
x="0"
|
|
41
|
+
dx={{-10}}
|
|
42
|
+
dy={{26}}
|
|
43
|
+
class="result-level-gauge__max-value"
|
|
44
|
+
aria-hidden={{this.hideValues}}
|
|
45
|
+
>{{this.formatNumber @maxLevel}}</text>
|
|
46
|
+
</g>
|
|
47
|
+
{{/unless}}
|
|
48
|
+
{{! mean purple level }}
|
|
49
|
+
<rect
|
|
50
|
+
y={{26}}
|
|
51
|
+
width="max(calc(calc(100% - 8px) * {{this.reachedLevelPercentage}}), 44px)"
|
|
52
|
+
height={{this.whiteAndPurpleGaugesHeight}}
|
|
53
|
+
rx={{this.whiteAndPurpleGaugeRx}}
|
|
54
|
+
class="result-level-gauge__mean-bar"
|
|
55
|
+
/>
|
|
56
|
+
{{#unless this.hideValues}}
|
|
57
|
+
<g
|
|
58
|
+
style="transform: translate(max(calc(calc(100% - 8px) * {{this.reachedLevelPercentage}}), 44px))"
|
|
59
|
+
>
|
|
60
|
+
<text
|
|
61
|
+
y={{this.statsFontHeight}}
|
|
62
|
+
x="0"
|
|
63
|
+
dx={{-10}}
|
|
64
|
+
dy={{26}}
|
|
65
|
+
class="result-level-gauge__mean-value"
|
|
66
|
+
aria-hidden={{this.hideValues}}
|
|
67
|
+
>{{this.formatNumber @reachedLevel}}</text>
|
|
68
|
+
</g>
|
|
69
|
+
{{/unless}}
|
|
70
|
+
{{! level labels }}
|
|
71
|
+
{{#unless @isSmall}}
|
|
72
|
+
{{#each @stepLabels as |stepLabel index|}}
|
|
73
|
+
<g style="transform: translate(calc(calc(100% - 8px) * {{this.stepLabelX index}}))">
|
|
74
|
+
<text
|
|
75
|
+
y={{15}}
|
|
76
|
+
class="result-level-gauge__rank
|
|
77
|
+
{{if (this.isLevelActive index) 'result-level-gauge__rank--active'}}"
|
|
78
|
+
>{{stepLabel}}</text>
|
|
79
|
+
</g>
|
|
80
|
+
{{#unless (eq index 0)}}
|
|
81
|
+
<g style="transform: translate(calc(100% * {{this.stepLineX index}}))">
|
|
82
|
+
<line
|
|
83
|
+
x1="0"
|
|
84
|
+
y1={{7}}
|
|
85
|
+
x2="0"
|
|
86
|
+
y2="74.6641"
|
|
87
|
+
stroke-width={{2}}
|
|
88
|
+
stroke-linecap="round"
|
|
89
|
+
stroke-dasharray="2 8"
|
|
90
|
+
class="result-level-gauge__separator"
|
|
91
|
+
role="separator"
|
|
92
|
+
/>
|
|
93
|
+
</g>
|
|
94
|
+
{{/unless}}
|
|
95
|
+
{{/each}}
|
|
96
|
+
{{/unless}}
|
|
97
|
+
</g>
|
|
98
|
+
</svg>
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { warn } from '@ember/debug';
|
|
2
|
+
import Component from '@glimmer/component';
|
|
3
|
+
export default class PixGauge extends Component {
|
|
4
|
+
get label() {
|
|
5
|
+
warn('PixGauge: @label must be defined', !this.args.label, {
|
|
6
|
+
id: 'pix-ui.gauge.label.not-defined',
|
|
7
|
+
});
|
|
8
|
+
return this.args.label;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
get reachedLevel() {
|
|
12
|
+
warn(
|
|
13
|
+
'PixGauge: @reachedLevel must be between 0 and 8',
|
|
14
|
+
this.args.reachedLevel <= 8 || this.args.reachedLevel >= 0,
|
|
15
|
+
{
|
|
16
|
+
id: 'pix-ui.gauge.reachedLevel.not-defined',
|
|
17
|
+
},
|
|
18
|
+
);
|
|
19
|
+
return this.args.reachedLevel;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get maxLevel() {
|
|
23
|
+
warn(
|
|
24
|
+
'PixGauge: @maxLevel must be between 1 and 8',
|
|
25
|
+
this.args.maxLevel <= 8 || this.args.maxLevel >= 1,
|
|
26
|
+
{
|
|
27
|
+
id: 'pix-ui.gauge.maxLevel.not-defined',
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
return this.args.maxLevel;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get hideValues() {
|
|
34
|
+
return this.args.hideValues ?? false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get maxLevelPourcentage() {
|
|
38
|
+
return this.maxLevel / 8;
|
|
39
|
+
}
|
|
40
|
+
get viewBox() {
|
|
41
|
+
if (this.args.isSmall) {
|
|
42
|
+
return '0 22 200 32';
|
|
43
|
+
}
|
|
44
|
+
return '0 0 100% 70';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get reachedLevelPercentage() {
|
|
48
|
+
return this.reachedLevel / 8;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get whiteAndPurpleGaugeRx() {
|
|
52
|
+
return this.args.isSmall ? 12 : 20;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get whiteAndPurpleGaugesHeight() {
|
|
56
|
+
return this.args.isSmall ? 24 : 40;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// background
|
|
60
|
+
get greyGaugeRx() {
|
|
61
|
+
return this.args.isSmall ? 16 : 24;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get backgroundHeight() {
|
|
65
|
+
return this.args.isSmall ? 32 : 48;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get statsFontHeight() {
|
|
69
|
+
return this.args.isSmall ? 18 : 26;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
isLevelActive = (index) => {
|
|
73
|
+
if (this.reachedLevel === 0 && index === 0) return true;
|
|
74
|
+
const step = 8 / this.args.stepLabels.length;
|
|
75
|
+
return index * step < this.reachedLevel && this.reachedLevel <= (index + 1) * step;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
stepLabelX = (index) => {
|
|
79
|
+
const stepStart = 1 / this.args.stepLabels.length;
|
|
80
|
+
return `${stepStart * index + (1 / 2) * stepStart}`;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
stepLineX = (index) => {
|
|
84
|
+
const stepStart = 1 / this.args.stepLabels.length;
|
|
85
|
+
return `${stepStart * index}`;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
formatNumber = (str) => {
|
|
89
|
+
const num = Number(str);
|
|
90
|
+
const oneDigitNum = num.toFixed(1);
|
|
91
|
+
if (oneDigitNum.toString().endsWith('0')) {
|
|
92
|
+
return Math.ceil(oneDigitNum);
|
|
93
|
+
}
|
|
94
|
+
return oneDigitNum;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -3,30 +3,73 @@
|
|
|
3
3
|
.pix-app-layout {
|
|
4
4
|
display: grid;
|
|
5
5
|
grid-template-areas:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
"banner banner"
|
|
7
|
+
"navigation main"
|
|
8
|
+
"navigation footer";
|
|
9
|
+
grid-template-rows: min-content 1fr;
|
|
9
10
|
grid-template-columns: min-content 1fr;
|
|
10
|
-
gap: var(--pix-spacing-6x);
|
|
11
11
|
min-height: 100dvh;
|
|
12
|
-
padding: var(--pix-spacing-6x);
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
@include breakpoints.device-is('mobile') {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
&__banner {
|
|
19
|
+
position: sticky;
|
|
20
|
+
top: 0;
|
|
21
|
+
z-index: 3;
|
|
22
|
+
grid-area: banner;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
&__navigation {
|
|
26
|
+
position: sticky;
|
|
27
|
+
bottom: var(--pix-spacing-6x);
|
|
28
|
+
z-index: 1;
|
|
15
29
|
grid-area: navigation;
|
|
30
|
+
min-height: calc(100vh - var(--pix-spacing-6x));
|
|
31
|
+
margin: auto 0 0;
|
|
32
|
+
padding: calc(var(--pix-app-layout-top) + var(--pix-spacing-6x)) 0 0 var(--pix-spacing-6x);
|
|
33
|
+
|
|
34
|
+
@include breakpoints.device-is('mobile') {
|
|
35
|
+
position: unset;
|
|
36
|
+
z-index: auto;
|
|
37
|
+
min-height: auto;
|
|
38
|
+
padding: var(--pix-spacing-2x) var(--pix-spacing-2x) 0 var(--pix-spacing-2x);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
&__main, &__footer {
|
|
43
|
+
width: 100%;
|
|
44
|
+
max-width: 93rem;
|
|
45
|
+
margin: 0 auto;
|
|
46
|
+
padding: 0 var(--pix-spacing-6x);
|
|
47
|
+
|
|
48
|
+
@include breakpoints.device-is('mobile') {
|
|
49
|
+
padding: 0 var(--pix-spacing-2x);
|
|
50
|
+
}
|
|
16
51
|
}
|
|
17
52
|
|
|
18
|
-
&
|
|
53
|
+
&__main {
|
|
19
54
|
grid-area: main;
|
|
55
|
+
padding-top: var(--pix-spacing-6x);
|
|
56
|
+
|
|
57
|
+
@include breakpoints.device-is('mobile') {
|
|
58
|
+
padding-top: var(--pix-spacing-2x);
|
|
59
|
+
}
|
|
20
60
|
}
|
|
21
61
|
|
|
22
|
-
&
|
|
62
|
+
&__footer {
|
|
23
63
|
grid-area: footer;
|
|
24
|
-
|
|
64
|
+
padding-bottom: var(--pix-spacing-6x);
|
|
25
65
|
|
|
26
|
-
|
|
27
|
-
|
|
66
|
+
@include breakpoints.device-is('mobile') {
|
|
67
|
+
padding-bottom: var(--pix-spacing-2x);
|
|
68
|
+
}
|
|
28
69
|
}
|
|
29
70
|
|
|
71
|
+
|
|
72
|
+
|
|
30
73
|
&--admin {
|
|
31
74
|
background-color: var(--pix-primary-10);
|
|
32
75
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
@use "pix-design-tokens/fonts";
|
|
2
|
+
@use "pix-design-tokens/typography";
|
|
3
|
+
|
|
4
|
+
.result-level-gauge {
|
|
5
|
+
display: block;
|
|
6
|
+
width: 100%;
|
|
7
|
+
height: 4.375rem;
|
|
8
|
+
margin-bottom: var(--pix-spacing-2x);
|
|
9
|
+
|
|
10
|
+
&__rank {
|
|
11
|
+
font-size: .625rem;
|
|
12
|
+
font-family: fonts.$font-roboto;
|
|
13
|
+
text-transform: uppercase;
|
|
14
|
+
fill: var(--pix-neutral-900);
|
|
15
|
+
text-anchor: middle;
|
|
16
|
+
|
|
17
|
+
&--active {
|
|
18
|
+
font-weight: var(--pix-font-bold);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
&__mean-bar {
|
|
23
|
+
fill: var(--pix-primary-300);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&__max-bar {
|
|
27
|
+
fill: var(--pix-neutral-0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
&__background {
|
|
31
|
+
fill: rgba(var(--pix-neutral-900-inline), 0.1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&__mean-value {
|
|
35
|
+
@extend %pix-body-l;
|
|
36
|
+
|
|
37
|
+
font-weight: var(--pix-font-bold);
|
|
38
|
+
fill: var(--pix-primary-10);
|
|
39
|
+
text-anchor: end;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
&__max-value {
|
|
43
|
+
@extend %pix-body-l;
|
|
44
|
+
|
|
45
|
+
fill: var(--pix-neutral-800);
|
|
46
|
+
text-anchor: end;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&__separator {
|
|
50
|
+
transform: translate(-4px, 0);
|
|
51
|
+
stroke: var(--pix-primary-100);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Modifier for small version of the gauge
|
|
55
|
+
&__small {
|
|
56
|
+
width: 200px;
|
|
57
|
+
height: 2rem;
|
|
58
|
+
|
|
59
|
+
&__mean-value {
|
|
60
|
+
font-weight: var(--pix-font-bold);
|
|
61
|
+
|
|
62
|
+
@extend %pix-body-s;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
&__max-value {
|
|
66
|
+
@extend %pix-body-s;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -6,34 +6,25 @@
|
|
|
6
6
|
--bg-color-active: rgb(var(--pix-neutral-100-inline), 30%);
|
|
7
7
|
--pix-navigation-width: 15rem;
|
|
8
8
|
|
|
9
|
-
position: sticky;
|
|
10
|
-
bottom: var(--pix-spacing-6x);
|
|
11
|
-
z-index: 1;
|
|
12
9
|
display: flex;
|
|
13
10
|
flex-direction: column;
|
|
14
11
|
gap:var(--pix-spacing-6x);
|
|
15
12
|
align-items: stretch;
|
|
16
13
|
justify-content: space-between;
|
|
17
14
|
width: var(--pix-navigation-width);
|
|
18
|
-
height:
|
|
19
|
-
min-height: calc(100vh - var(--pix-spacing-6x) * 2);
|
|
20
|
-
|
|
21
|
-
// using a margin top / bottom allow user to discover rapidly
|
|
22
|
-
// the part of sidebar that is above the fold
|
|
23
|
-
margin-top: auto;
|
|
15
|
+
min-height: calc(100vh - var(--pix-app-layout-top) - 2 * var(--pix-spacing-6x));
|
|
24
16
|
padding: var(--pix-spacing-6x) var(--pix-spacing-4x);
|
|
25
17
|
color: var(--pix-navigation-text-color);
|
|
26
18
|
background-color: var(--pix-navigation-color);
|
|
27
19
|
border-radius: var(--pix-spacing-6x);
|
|
28
20
|
|
|
29
|
-
|
|
30
21
|
@include breakpoints.device-is('mobile') {
|
|
31
|
-
position: static;
|
|
32
22
|
flex-direction: column;
|
|
33
23
|
align-items: stretch;
|
|
34
24
|
width: 100%;
|
|
35
25
|
max-width: none;
|
|
36
26
|
min-height: auto;
|
|
27
|
+
margin-top: 0;
|
|
37
28
|
padding: 0;
|
|
38
29
|
|
|
39
30
|
&--opened {
|
|
@@ -49,7 +40,6 @@
|
|
|
49
40
|
|
|
50
41
|
img {
|
|
51
42
|
display: block;
|
|
52
|
-
width: 132px;
|
|
53
43
|
height: 48px;
|
|
54
44
|
margin:0 auto;
|
|
55
45
|
}
|
package/addon/styles/addon.scss
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { modifier } from 'ember-modifier';
|
|
2
|
+
|
|
3
|
+
export default modifier(function onWindowResize(element, [action]) {
|
|
4
|
+
const actionWithElement = () => action(element);
|
|
5
|
+
|
|
6
|
+
window.addEventListener('resize', actionWithElement);
|
|
7
|
+
actionWithElement();
|
|
8
|
+
|
|
9
|
+
return () => {
|
|
10
|
+
window.removeEventListener('resize', actionWithElement);
|
|
11
|
+
};
|
|
12
|
+
});
|