@esportsplus/ui 0.38.1 → 0.39.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,22 @@
1
+ import { Response } from '@esportsplus/action';
2
+ import { Attributes, Renderable } from '@esportsplus/template';
3
+ import './scss/index.scss';
4
+ declare function deactivate(): void;
5
+ declare const _default: {
6
+ content: (attributes: Attributes & {
7
+ close?: Attributes;
8
+ message?: Attributes;
9
+ }, { check, close, error }: {
10
+ check: string;
11
+ close: string;
12
+ error: string;
13
+ }) => Node;
14
+ deactivate: typeof deactivate;
15
+ error: {
16
+ (messages: Renderable<any>, seconds?: number): void;
17
+ response(response: Response<any>): void;
18
+ };
19
+ info: (messages: Renderable<any>, seconds?: number) => void;
20
+ success: (messages: Renderable<any>, seconds?: number) => void;
21
+ };
22
+ export default _default;
@@ -0,0 +1,120 @@
1
+ import { reactive } from '@esportsplus/reactivity';
2
+ import { html, svg } from '@esportsplus/template';
3
+ import { omit } from '@esportsplus/utilities';
4
+ import { icon } from '@esportsplus/ui';
5
+ import './scss/index.scss';
6
+ const OMIT = ['close', 'message'];
7
+ let modifiers = {
8
+ error: 'red',
9
+ info: 'black',
10
+ success: 'green'
11
+ }, state = reactive({
12
+ active: false,
13
+ messages: new Set,
14
+ type: ''
15
+ }), timeout = 250;
16
+ function activate(key, messages, seconds = 0) {
17
+ if (!Array.isArray(messages)) {
18
+ messages = [messages];
19
+ }
20
+ if (!messages.length) {
21
+ return;
22
+ }
23
+ if (state.type !== key) {
24
+ state.messages.clear();
25
+ }
26
+ else {
27
+ state.type = '';
28
+ }
29
+ for (let message of messages) {
30
+ state.messages.add(message);
31
+ }
32
+ if (state.active) {
33
+ state.active = false;
34
+ setTimeout(() => {
35
+ state.active = true;
36
+ state.type = key;
37
+ if (seconds) {
38
+ setTimeout(deactivate, 500 * seconds);
39
+ }
40
+ }, timeout);
41
+ }
42
+ else {
43
+ setTimeout(() => {
44
+ state.active = true;
45
+ state.type = key;
46
+ if (seconds) {
47
+ setTimeout(deactivate, 500 * seconds);
48
+ }
49
+ }, timeout);
50
+ }
51
+ }
52
+ function deactivate() {
53
+ state.active = false;
54
+ setTimeout(() => {
55
+ state.messages.clear();
56
+ }, timeout);
57
+ }
58
+ const error = (messages, seconds = 0) => activate('error', messages, seconds);
59
+ error.response = (response) => {
60
+ if (response.ok) {
61
+ return;
62
+ }
63
+ error(response.errors.map(({ message, path }) => {
64
+ if (!path) {
65
+ return message;
66
+ }
67
+ return `${String(path).split('.').join(' ')} ${message}`;
68
+ }), 5);
69
+ };
70
+ const info = (messages, seconds = 0) => activate('info', messages, seconds);
71
+ const success = (messages, seconds = 0) => activate('success', messages, seconds);
72
+ const content = (attributes, { check, close, error }) => {
73
+ return html `
74
+ <div
75
+ class='alert anchor anchor--n card --flex-fill --flex-row --flex-vertical ${() => state.active && '--active'}'
76
+ style='--max-width: 640px;'
77
+ ${omit(attributes, OMIT)}
78
+ >
79
+ ${() => {
80
+ let type = state.type;
81
+ return html `
82
+ <div class='--flex-vertical' style='${`--color: var(--color-${modifiers[type]}-400);`}'>
83
+ ${icon({ class: '--margin-right --margin-600 --size-500' }, type === 'error' ? error : check)}
84
+ </div>
85
+ `;
86
+ }}
87
+
88
+ <div class='--flex-fill --flex-column --gap-100 --padding-right --padding-800'>
89
+ ${() => {
90
+ let message = attributes.message;
91
+ return state.type && [...state.messages].map((content, i) => {
92
+ if (typeof content === 'string') {
93
+ return html `
94
+ <p class='${i === 0 && '--text-crop-top'}' ${message}>
95
+ ${content}
96
+ </p>
97
+ `;
98
+ }
99
+ return html `
100
+ <div class='--flex-start'>
101
+ ${content}
102
+ </div>
103
+ `;
104
+ });
105
+ }}
106
+ </div>
107
+
108
+ <div
109
+ class='alert-close button --padding-300'
110
+ onclick='${deactivate}'
111
+ ${attributes.close}
112
+ >
113
+ <div class="icon" style='--size: 14px;'>
114
+ ${svg.sprite(close)}
115
+ </div>
116
+ </div>
117
+ </div>
118
+ `;
119
+ };
120
+ export default { content, deactivate, error, info, success };
@@ -0,0 +1,2 @@
1
+ @layer components {.anchor{--margin-horizontal:var(--size-400);--margin-vertical:var(--size-400);max-height:calc(var(--max-height,100%) - var(--margin-vertical)*2);max-width:calc(var(--max-width,100%) - var(--margin-horizontal)*2);transition:opacity var(--transition-duration)ease-in-out,transform var(--transition-duration)ease-in-out;z-index:9;position:absolute}.anchor:not(.--active){opacity:0}.anchor:not(.--active),.anchor:not(.--active) *{pointer-events:none}.anchor--n,.anchor--s{right:50%;transform:translate(50%)}.anchor--ne,.anchor--nw{top:var(--margin-vertical)}.anchor--se,.anchor--sw{bottom:var(--margin-vertical)}.anchor--ne,.anchor--se{right:var(--margin-horizontal)}.anchor--nw,.anchor--sw{left:var(--margin-horizontal)}.anchor--n{top:var(--margin-vertical)}.anchor--s{bottom:var(--margin-vertical)}.alert{--max-width:480px;max-width:var(--max-width);width:calc(100% - var(--margin-horizontal)*2);z-index:11;position:fixed}.alert.anchor--n:not(.--active){transform:translate(50%,-100%)}.alert.anchor--ne:not(.--active){transform:translateY(-100%)}.alert-close{bottom:50%;right:var(--size-500);z-index:0;position:absolute;transform:translateY(50%)}.alert-message{opacity:0;transition:opacity var(--transition-duration)ease-in-out,transform var(--transition-duration)ease-in-out}.alert-message:not(.--active){pointer-events:none;position:absolute;top:0;left:0;transform:translate(-25%)}.alert-message.--active{opacity:1}}
2
+ /*$vite$:1*/
@@ -1,2 +1,2 @@
1
- @layer components {.anchor{--margin-horizontal:var(--size-400);--margin-vertical:var(--size-400);max-height:calc(var(--max-height,100%) - var(--margin-vertical)*2);max-width:calc(var(--max-width,100%) - var(--margin-horizontal)*2);transition:opacity var(--transition-duration)ease-in-out,transform var(--transition-duration)ease-in-out;z-index:9;position:absolute}.anchor:not(.--active){opacity:0}.anchor:not(.--active),.anchor:not(.--active) *{pointer-events:none}.anchor--ne,.anchor--nw{top:var(--margin-vertical)}.anchor--se,.anchor--sw{bottom:var(--margin-vertical)}.anchor--ne,.anchor--se{right:var(--margin-horizontal)}.anchor--nw,.anchor--sw{left:var(--margin-horizontal)}}
1
+ @layer components {.anchor{--margin-horizontal:var(--size-400);--margin-vertical:var(--size-400);max-height:calc(var(--max-height,100%) - var(--margin-vertical)*2);max-width:calc(var(--max-width,100%) - var(--margin-horizontal)*2);transition:opacity var(--transition-duration)ease-in-out,transform var(--transition-duration)ease-in-out;z-index:9;position:absolute}.anchor:not(.--active){opacity:0}.anchor:not(.--active),.anchor:not(.--active) *{pointer-events:none}.anchor--n,.anchor--s{right:50%;transform:translate(50%)}.anchor--ne,.anchor--nw{top:var(--margin-vertical)}.anchor--se,.anchor--sw{bottom:var(--margin-vertical)}.anchor--ne,.anchor--se{right:var(--margin-horizontal)}.anchor--nw,.anchor--sw{left:var(--margin-horizontal)}.anchor--n{top:var(--margin-vertical)}.anchor--s{bottom:var(--margin-vertical)}}
2
2
  /*$vite$:1*/
@@ -0,0 +1,7 @@
1
+ import { Attributes, Renderable } from '@esportsplus/template';
2
+ import './scss/index.scss';
3
+ declare const _default: ({ attributes, content }: {
4
+ attributes?: Attributes;
5
+ content?: Renderable<any>;
6
+ }) => Node;
7
+ export default _default;
@@ -0,0 +1,9 @@
1
+ import { html } from '@esportsplus/template';
2
+ import './scss/index.scss';
3
+ export default ({ attributes, content }) => {
4
+ return html `
5
+ <a class='back link --padding-0px --flex-vertical' ${attributes}>
6
+ ${content}
7
+ </a>
8
+ `;
9
+ };
@@ -0,0 +1,2 @@
1
+ @layer components {.back-arrow{transition:color var(--transition-duration)ease-in-out,transform var(--transition-duration)ease-in-out;transform:rotate(-90deg)}.back:not(.--active):hover .back-arrow{transform:rotate(-90deg)translateY(-2px)}}
2
+ /*$vite$:1*/
@@ -1,4 +1,6 @@
1
1
  export { default as accordion } from './accordion/index.js';
2
+ export { default as alert } from './alert/index.js';
3
+ export { default as back } from './back/index.js';
2
4
  export { default as button } from './button/index.js';
3
5
  export { default as checkbox } from './checkbox/index.js';
4
6
  export { default as clipboard } from './clipboard/index.js';
@@ -1,4 +1,6 @@
1
1
  export { default as accordion } from './accordion/index.js';
2
+ export { default as alert } from './alert/index.js';
3
+ export { default as back } from './back/index.js';
2
4
  export { default as button } from './button/index.js';
3
5
  export { default as checkbox } from './checkbox/index.js';
4
6
  export { default as clipboard } from './clipboard/index.js';
package/package.json CHANGED
@@ -10,13 +10,13 @@
10
10
  },
11
11
  "devDependencies": {
12
12
  "@esportsplus/typescript": "^0.9.2",
13
- "@types/node": "^24.7.0",
13
+ "@types/node": "^24.9.1",
14
14
  "autoprefixer": "^10.4.21",
15
15
  "glob": "^11.0.3",
16
16
  "lightningcss": "^1.30.2",
17
17
  "npm-run-all": "^4.1.5",
18
18
  "sass": "^1.93.2",
19
- "vite": "^7.1.9"
19
+ "vite": "^7.1.12"
20
20
  },
21
21
  "exports": {
22
22
  "./css-utilities.scss": "./build/css-utilities/index.scss",
@@ -42,7 +42,7 @@
42
42
  "private": false,
43
43
  "sideEffects": false,
44
44
  "type": "module",
45
- "version": "0.38.1",
45
+ "version": "0.39.0",
46
46
  "scripts": {
47
47
  "build": "run-s build:vite build:ts",
48
48
  "build:ts": "tsc && tsc-alias",
@@ -0,0 +1,163 @@
1
+ import { Response } from '@esportsplus/action';
2
+ import { reactive } from '@esportsplus/reactivity';
3
+ import { html, svg, Attributes, Renderable } from '@esportsplus/template';
4
+ import { omit } from '@esportsplus/utilities';
5
+ import { icon } from '@esportsplus/ui';
6
+ import './scss/index.scss';
7
+
8
+
9
+ type Type = 'error' | 'info' | 'success';
10
+
11
+
12
+ const OMIT = ['close', 'message'];
13
+
14
+
15
+ let modifiers: Record<Type, string> = {
16
+ error: 'red',
17
+ info: 'black',
18
+ success: 'green'
19
+ },
20
+ state = reactive({
21
+ active: false,
22
+ messages: new Set as Set<Renderable<any>>,
23
+ type: '' as Type
24
+ }),
25
+ timeout = 250;
26
+
27
+
28
+ function activate(key: Type, messages: Renderable<any>, seconds: number = 0) {
29
+ if (!Array.isArray(messages)) {
30
+ messages = [messages];
31
+ }
32
+
33
+ if (!messages.length) {
34
+ return;
35
+ }
36
+
37
+ if (state.type !== key) {
38
+ state.messages.clear();
39
+ }
40
+ else {
41
+ // @ts-ignore
42
+ state.type = '';
43
+ }
44
+
45
+ for (let message of messages) {
46
+ state.messages.add(message);
47
+ }
48
+
49
+ if (state.active) {
50
+ state.active = false;
51
+
52
+ // Slide in animation needs time
53
+ setTimeout(() => {
54
+ state.active = true;
55
+ state.type = key;
56
+
57
+ if (seconds) {
58
+ setTimeout(deactivate, 500 * seconds);
59
+ }
60
+ }, timeout);
61
+ }
62
+ else {
63
+ setTimeout(() => {
64
+ state.active = true;
65
+ state.type = key;
66
+
67
+ if (seconds) {
68
+ setTimeout(deactivate, 500 * seconds);
69
+ }
70
+ }, timeout);
71
+ }
72
+ }
73
+
74
+ function deactivate() {
75
+ state.active = false;
76
+
77
+ setTimeout(() => {
78
+ state.messages.clear();
79
+ }, timeout);
80
+ }
81
+
82
+
83
+ const error = (messages: Renderable<any>, seconds: number = 0) => activate('error', messages, seconds);
84
+
85
+ error.response = (response: Response<any>) => {
86
+ if (response.ok) {
87
+ return;
88
+ }
89
+
90
+ error(
91
+ response.errors.map(({ message, path }) => {
92
+ if (!path) {
93
+ return message;
94
+ }
95
+
96
+ return `${String(path).split('.').join(' ')} ${message}`;
97
+ }),
98
+ 5
99
+ );
100
+ };
101
+
102
+ const info = (messages: Renderable<any>, seconds: number = 0) => activate('info', messages, seconds);
103
+
104
+ const success = (messages: Renderable<any>, seconds: number = 0) => activate('success', messages, seconds);
105
+
106
+
107
+ const content = (
108
+ attributes: Attributes & { close?: Attributes, message?: Attributes },
109
+ { check, close, error }: { check: string, close: string, error: string }
110
+ ) => {
111
+ return html`
112
+ <div
113
+ class='alert anchor anchor--n card --flex-fill --flex-row --flex-vertical ${() => state.active && '--active'}'
114
+ style='--max-width: 640px;'
115
+ ${omit(attributes, OMIT)}
116
+ >
117
+ ${() => {
118
+ let type = state.type;
119
+
120
+ return html`
121
+ <div class='--flex-vertical' style='${`--color: var(--color-${modifiers[type]}-400);`}'>
122
+ ${icon({ class: '--margin-right --margin-600 --size-500' }, type === 'error' ? error : check)}
123
+ </div>
124
+ `;
125
+ }}
126
+
127
+ <div class='--flex-fill --flex-column --gap-100 --padding-right --padding-800'>
128
+ ${() => {
129
+ let message = attributes.message;
130
+
131
+ return state.type && [...state.messages].map((content, i) => {
132
+ if (typeof content === 'string') {
133
+ return html`
134
+ <p class='${i === 0 && '--text-crop-top'}' ${message}>
135
+ ${content}
136
+ </p>
137
+ `;
138
+ }
139
+
140
+ return html`
141
+ <div class='--flex-start'>
142
+ ${content}
143
+ </div>
144
+ `;
145
+ });
146
+ }}
147
+ </div>
148
+
149
+ <div
150
+ class='alert-close button --padding-300'
151
+ onclick='${deactivate}'
152
+ ${attributes.close}
153
+ >
154
+ <div class="icon" style='--size: 14px;'>
155
+ ${svg.sprite(close)}
156
+ </div>
157
+ </div>
158
+ </div>
159
+ `;
160
+ };
161
+
162
+
163
+ export default { content, deactivate, error, info, success };
@@ -0,0 +1,50 @@
1
+ @use '../../anchor/scss';
2
+ @use '/lib';
3
+ @use '/tokens';
4
+ @use 'variables';
5
+
6
+ .alert {
7
+ max-width: var(--max-width);
8
+ position: fixed;
9
+ width: calc(100% - (var(--margin-horizontal) * 2));
10
+ z-index: 11;
11
+
12
+
13
+ &.anchor--n {
14
+ @include tokens.state(inactive) {
15
+ transform: translate(50%, -100%);
16
+ }
17
+ }
18
+
19
+ &.anchor--ne {
20
+ @include tokens.state(inactive) {
21
+ transform: translateY(-100%);
22
+ }
23
+ }
24
+
25
+
26
+ &-close {
27
+ @include lib.position(absolute, vertical);
28
+ right: var(--size-500);
29
+ z-index: 0;
30
+ }
31
+
32
+ &-message {
33
+ opacity: 0;
34
+ transition:
35
+ opacity var(--transition-duration) ease-in-out,
36
+ transform var(--transition-duration) ease-in-out;
37
+
38
+ @include tokens.state(inactive) {
39
+ pointer-events: none;
40
+ position: absolute;
41
+ transform: translateX(calc(25% * -1));
42
+ left: 0;
43
+ top: 0;
44
+ }
45
+
46
+ @include tokens.state(active) {
47
+ opacity: 1;
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,3 @@
1
+ .alert {
2
+ --max-width: 480px;
3
+ }
@@ -20,6 +20,12 @@
20
20
  }
21
21
 
22
22
 
23
+ &--n,
24
+ &--s {
25
+ right: 50%;
26
+ transform: translateX(50%);
27
+ }
28
+
23
29
  &--ne,
24
30
  &--nw {
25
31
  top: var(--margin-vertical);
@@ -39,4 +45,12 @@
39
45
  &--sw {
40
46
  left: var(--margin-horizontal);
41
47
  }
48
+
49
+ &--n {
50
+ top: var(--margin-vertical);
51
+ }
52
+
53
+ &--s {
54
+ bottom: var(--margin-vertical);
55
+ }
42
56
  }
@@ -0,0 +1,11 @@
1
+ import { html, Attributes, Renderable } from '@esportsplus/template';
2
+ import './scss/index.scss';
3
+
4
+
5
+ export default ({ attributes, content }: { attributes?: Attributes, content?: Renderable<any> }) => {
6
+ return html`
7
+ <a class='back link --padding-0px --flex-vertical' ${attributes}>
8
+ ${content}
9
+ </a>
10
+ `;
11
+ };
@@ -0,0 +1,17 @@
1
+ @use '@esportsplus/ui/tokens.scss';
2
+
3
+
4
+ .back {
5
+ &-arrow {
6
+ transform: rotate(-90deg);
7
+ transition:
8
+ color var(--transition-duration) ease-in-out,
9
+ transform var(--transition-duration) ease-in-out;
10
+ }
11
+
12
+ @include tokens.state(hover) {
13
+ .back-arrow {
14
+ transform: rotate(-90deg) translateY(-2px);
15
+ }
16
+ }
17
+ }
@@ -1,4 +1,6 @@
1
1
  export { default as accordion } from './accordion';
2
+ export { default as alert } from './alert';
3
+ export { default as back } from './back';
2
4
  export { default as button } from './button';
3
5
  export { default as checkbox } from './checkbox';
4
6
  export { default as clipboard } from './clipboard';