@1024pix/pix-ui 48.1.0 → 48.2.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.
@@ -0,0 +1,5 @@
1
+ <div class="pix-toast-container">
2
+ {{#each this.pixToast.content as |toast|}}
3
+ <PixToast @toast={{toast}} @closeButtonAriaLabel={{@closeButtonAriaLabel}} />
4
+ {{/each}}
5
+ </div>
@@ -0,0 +1,6 @@
1
+ import Component from '@glimmer/component';
2
+ import { service } from '@ember/service';
3
+
4
+ export default class PixToastContainer extends Component {
5
+ @service pixToast;
6
+ }
@@ -0,0 +1,17 @@
1
+ <div class="pix-toast {{concat 'pix-toast--' this.type}}" role="alert" ...attributes>
2
+ <div class="pix-toast__icon {{concat 'pix-toast__icon--' this.type}}">
3
+ <PixIcon @name={{this.iconClass}} @ariaHidden={{true}} @plainIcon={{true}} />
4
+ </div>
5
+ <p class="pix-toast__content">
6
+ {{@toast.message}}
7
+ </p>
8
+ <div class="pix-toast__close-button-container">
9
+ <PixIconButton
10
+ @ariaLabel={{@closeButtonAriaLabel}}
11
+ @iconName="close"
12
+ @size="small"
13
+ @triggerAction={{this.removeNotification}}
14
+ class="{{concat 'pix-toast__close-button--' this.type}}"
15
+ />
16
+ </div>
17
+ </div>
@@ -0,0 +1,37 @@
1
+ import Component from '@glimmer/component';
2
+ import { service } from '@ember/service';
3
+ import { action } from '@ember/object';
4
+ import { warn } from '@ember/debug';
5
+ const TYPE_SUCCESS = 'success';
6
+ const TYPE_ERROR = 'error';
7
+ const TYPE_INFORMATION = 'information';
8
+ const TYPE_WARNING = 'warning';
9
+
10
+ export default class PixToast extends Component {
11
+ @service pixToast;
12
+
13
+ get type() {
14
+ const correctTypes = [TYPE_SUCCESS, TYPE_ERROR, TYPE_INFORMATION, TYPE_WARNING];
15
+ warn('PixToast: you need to provide a type', correctTypes.includes(this.args.toast?.type), {
16
+ id: 'pix-ui.toast.type.not-provided',
17
+ });
18
+ return this.args.toast?.type ?? 'success';
19
+ }
20
+
21
+ get iconClass() {
22
+ const classes = {
23
+ [TYPE_INFORMATION]: 'info',
24
+ [TYPE_SUCCESS]: 'checkCircle',
25
+ [TYPE_WARNING]: 'warning',
26
+ [TYPE_ERROR]: 'error',
27
+ };
28
+ return classes[this.type];
29
+ }
30
+
31
+ @action
32
+ removeNotification(event) {
33
+ event.preventDefault();
34
+ event.stopPropagation();
35
+ this.pixToast.removeNotification(this.args.toast);
36
+ }
37
+ }
@@ -0,0 +1,7 @@
1
+ <div class="toast-example">
2
+ <PixButton @size="small" @triggerAction={{this.displayNotification}}>
3
+ Afficher une notification
4
+ </PixButton>
5
+
6
+ <PixToastContainer @closeButtonAriaLabel="Fermer la notification" />
7
+ </div>
@@ -0,0 +1,24 @@
1
+ import Component from '@glimmer/component';
2
+ import { service } from '@ember/service';
3
+ import { action } from '@ember/object';
4
+
5
+ export default class ToastExample extends Component {
6
+ @service pixToast;
7
+
8
+ @action displayNotification() {
9
+ const types = ['error', 'information', 'success', 'warning'];
10
+ const messages = [
11
+ 'ALERTE !',
12
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer consectetur ultricies volutpat. Integer at massa id odio tristique lacinia quis in metus.',
13
+ "L'action a été un succès !",
14
+ 'Attention, cette action peut avoir des conséquences...',
15
+ ];
16
+
17
+ const randomNumber = Math.floor(Math.random() * 4);
18
+
19
+ this.pixToast.addNotification({
20
+ message: messages[randomNumber],
21
+ type: types[randomNumber],
22
+ });
23
+ }
24
+ }
@@ -0,0 +1,108 @@
1
+ import Service from '@ember/service';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { warn } from '@ember/debug';
4
+
5
+ export default class ToastService extends Service {
6
+ @tracked content = [];
7
+
8
+ /**
9
+ * Creates and returns a new toast notification.
10
+ * @param{string} message content to display
11
+ * @param{'error' | 'information' | 'success' | 'warning'} type type of toast notification
12
+ * @returns {EmberObject | void}
13
+ */
14
+ addNotification({ message, type }) {
15
+ const isToastValid = Boolean(message);
16
+
17
+ warn('Message mandatory attribute is missing', isToastValid, {
18
+ id: 'pix-ui.toast.not-message',
19
+ });
20
+
21
+ if (!isToastValid) return;
22
+
23
+ const toast = {
24
+ message,
25
+ type: type || 'success',
26
+ };
27
+
28
+ if (this.exists(toast)) {
29
+ this.removeNotification(toast);
30
+ }
31
+
32
+ this.content = [...this.content, toast];
33
+
34
+ return toast;
35
+ }
36
+
37
+ /**
38
+ * Check if toast already exist in content array
39
+ * @param{{message: string, type: string}} toast
40
+ * @returns {boolean}
41
+ */
42
+ exists(toast) {
43
+ return Boolean(
44
+ this.content.find((value) => toast.message === value.message && toast.type === value.type),
45
+ );
46
+ }
47
+
48
+ /**
49
+ * Creates and returns a new error toast notification.
50
+ * @param{string} message content to display
51
+ * @returns {EmberObject | void}
52
+ */
53
+ sendErrorNotification({ message }) {
54
+ return this.addNotification({
55
+ message,
56
+ type: 'error',
57
+ });
58
+ }
59
+
60
+ /**
61
+ * Creates and returns a new success toast notification.
62
+ * @param{string} message content to display
63
+ * @returns {EmberObject | void}
64
+ */
65
+ sendSuccessNotification({ message }) {
66
+ return this.addNotification({
67
+ message,
68
+ type: 'success',
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Creates and returns a new information toast notification.
74
+ * @param{string} message content to display
75
+ * @returns {EmberObject | void}
76
+ */
77
+ sendInformationNotification({ message }) {
78
+ return this.addNotification({
79
+ message,
80
+ type: 'information',
81
+ });
82
+ }
83
+
84
+ /**
85
+ * Creates and returns a new warning toast notification.
86
+ * @param{string} message content to display
87
+ * @returns {EmberObject | void}
88
+ */
89
+ sendWarningNotification({ message }) {
90
+ return this.addNotification({
91
+ message,
92
+ type: 'warning',
93
+ });
94
+ }
95
+
96
+ /**
97
+ * Remove toast notification from content list
98
+ * @param{{message: string, type: string}} toast toast to remove
99
+ * @returns {EmberObject | void}
100
+ */
101
+ removeNotification(toast) {
102
+ if (!toast) return;
103
+
104
+ this.content = this.content.filter(
105
+ (value) => toast.message !== value.message || toast.type !== value.type,
106
+ );
107
+ }
108
+ }
@@ -0,0 +1,150 @@
1
+ .pix-toast {
2
+ display: flex;
3
+ width: 400px;
4
+ margin-bottom: var(--pix-spacing-2x);
5
+ border-radius: 5px;
6
+ transition: all 0.3s ease-in-out;
7
+ animation: notification-show 180ms cubic-bezier(.175, .885, .32, 1.2750);
8
+ animation-fill-mode: forwards;
9
+
10
+ &--error {
11
+ color: var(--pix-error-700);
12
+ background-color: var(--pix-error-50);
13
+ border: 1.5px solid var(--pix-error-700);
14
+ }
15
+
16
+ &--success {
17
+ color: var(--pix-success-700);
18
+ background-color: var(--pix-success-50);
19
+ border: 1.5px solid var(--pix-success-700);
20
+ }
21
+
22
+ &--information {
23
+ color: var(--pix-info-700);
24
+ background-color: var(--pix-info-50);
25
+ border: 1.5px solid var(--pix-info-700);
26
+ }
27
+
28
+ &--warning {
29
+ color: var(--pix-warning-700);
30
+ background-color: var(--pix-warning-50);
31
+ border: 1.5px solid var(--pix-warning-700);
32
+ }
33
+
34
+ > p {
35
+ @extend %pix-body-m;
36
+
37
+ display: flex;
38
+ align-items: center;
39
+ width: 100%;
40
+ }
41
+ }
42
+
43
+ .pix-toast__icon, .pix-toast__close-button-container {
44
+ display: flex;
45
+ flex-shrink: 0;
46
+ align-items: center;
47
+ justify-content: center;
48
+ width: 40px;
49
+ }
50
+
51
+ .pix-toast__icon {
52
+ fill: var(--pix-neutral-0);
53
+
54
+ &--error {
55
+ background-color: var(--pix-error-700);
56
+ }
57
+
58
+ &--success {
59
+ background-color: var(--pix-success-700);
60
+ }
61
+
62
+ &--information {
63
+ background-color: var(--pix-info-700);
64
+ }
65
+
66
+ &--warning {
67
+ background-color: var(--pix-warning-700);
68
+ }
69
+ }
70
+
71
+ .pix-toast__content {
72
+ padding: var(--pix-spacing-2x) var(--pix-spacing-3x);
73
+ }
74
+
75
+ .pix-toast__close-button {
76
+ &--error {
77
+ fill: var(--pix-error-700);
78
+
79
+ &:hover:enabled,
80
+ &:active:enabled, {
81
+ background-color: var(--pix-error-700);
82
+ fill: var(--pix-neutral-0);
83
+ }
84
+
85
+ &:focus:enabled {
86
+ background-color: var(--pix-error-900);
87
+ }
88
+ }
89
+
90
+ &--success {
91
+ fill: var(--pix-success-700);
92
+
93
+ &:hover:enabled,
94
+ &:active:enabled, {
95
+ background-color: var(--pix-success-700);
96
+ fill: var(--pix-neutral-0);
97
+ }
98
+
99
+ &:focus:enabled {
100
+ background-color: var(--pix-success-900);
101
+ }
102
+ }
103
+
104
+ &--information {
105
+ fill: var(--pix-info-700);
106
+
107
+ &:hover:enabled,
108
+ &:active:enabled, {
109
+ background-color: var(--pix-info-700);
110
+ fill: var(--pix-neutral-0);
111
+ }
112
+
113
+ &:focus:enabled {
114
+ background-color: var(--pix-info-900);
115
+ }
116
+ }
117
+
118
+ &--warning {
119
+ fill: var(--pix-warning-700);
120
+
121
+ &:hover:enabled,
122
+ &:active:enabled, {
123
+ background-color: var(--pix-warning-700);
124
+ fill: var(--pix-neutral-0);
125
+ }
126
+
127
+ &:focus:enabled {
128
+ background-color: var(--pix-warning-900);
129
+ }
130
+ }
131
+ }
132
+
133
+ @keyframes notification-show {
134
+ 0% {
135
+ transform: perspective(450px) translate(0, -30px) rotateX(90deg);
136
+ opacity: 0;
137
+ }
138
+
139
+ 100% {
140
+ transform: perspective(450px) translate(0, 0) rotateX(0deg);
141
+ opacity: 1;
142
+ }
143
+ }
144
+
145
+ .pix-toast-container {
146
+ position: fixed;
147
+ right: var(--pix-spacing-2x);
148
+ bottom: var(--pix-spacing-8x);
149
+ z-index: 1000;
150
+ }
@@ -0,0 +1,10 @@
1
+ .toast-example {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 1rem;
5
+ align-items: center;
6
+
7
+ .pix-toast-container {
8
+ position: unset;
9
+ }
10
+ }
@@ -35,6 +35,9 @@
35
35
  @import 'pix-indicator-card';
36
36
  @import 'trap-focus';
37
37
  @import 'pix-search-input';
38
+ @import 'pix-toast';
39
+ @import 'toast-example';
40
+
38
41
 
39
42
 
40
43
  // at the end so it can override it's children scss
@@ -0,0 +1 @@
1
+ export { default } from '@1024pix/pix-ui/components/pix-toast-container';
@@ -0,0 +1 @@
1
+ export { default } from '@1024pix/pix-ui/components/pix-toast';
@@ -0,0 +1 @@
1
+ export { default } from '@1024pix/pix-ui/services/pix-toast';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1024pix/pix-ui",
3
- "version": "48.1.0",
3
+ "version": "48.2.0",
4
4
  "description": "Pix-UI is the implementation of Pix design principles and guidelines for its products.",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -78,17 +78,17 @@
78
78
  "@glimmer/component": "^1.1.2",
79
79
  "@glimmer/syntax": "^0.92.0",
80
80
  "@glimmer/tracking": "^1.1.2",
81
- "@storybook/addon-a11y": "^8.0.0",
82
- "@storybook/addon-controls": "^8.0.0",
83
- "@storybook/addon-docs": "^8.0.0",
84
- "@storybook/addon-essentials": "^8.0.0",
81
+ "@storybook/addon-a11y": "^8.4.2",
82
+ "@storybook/addon-controls": "^8.4.2",
83
+ "@storybook/addon-docs": "^8.4.2",
84
+ "@storybook/addon-essentials": "^8.4.2",
85
85
  "@storybook/addon-webpack5-compiler-babel": "^3.0.3",
86
- "@storybook/blocks": "^8.0.0",
87
- "@storybook/builder-webpack5": "^8.0.0",
88
- "@storybook/ember": "^8.0.0",
86
+ "@storybook/blocks": "^8.4.2",
87
+ "@storybook/builder-webpack5": "^8.4.2",
88
+ "@storybook/ember": "^8.4.2",
89
89
  "@storybook/ember-cli-storybook": "^0.6.1",
90
90
  "@storybook/storybook-deployer": "^2.8.16",
91
- "@storybook/theming": "^8.0.0",
91
+ "@storybook/theming": "^8.4.2",
92
92
  "@testing-library/dom": "^10.0.0",
93
93
  "@testing-library/user-event": "^14.4.3",
94
94
  "chromatic": "^11.0.0",
@@ -125,7 +125,7 @@
125
125
  "qunit": "^2.19.3",
126
126
  "qunit-dom": "^3.0.0",
127
127
  "sass": "^1.56.1",
128
- "storybook": "^8.0.0",
128
+ "storybook": "^8.4.2",
129
129
  "stylelint": "^16.0.2",
130
130
  "svg-sprite": "^2.0.4",
131
131
  "svgo": "^3.3.2",
@@ -141,7 +141,7 @@
141
141
  },
142
142
  "files": [
143
143
  "addon/",
144
- "app/components",
144
+ "app/components/pix-*.js",
145
145
  "app/modifiers",
146
146
  "app/services",
147
147
  "config/icons.js",