@luizleon/sf.prefeiturasp.vuecomponents 4.0.2 → 4.0.3
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/package.json +5 -2
- package/src/axios/axiosClient.ts +0 -207
- package/src/common/appResult.ts +0 -34
- package/src/components/button/Button.vue +0 -104
- package/src/components/content/Content.vue +0 -49
- package/src/components/drawer/Drawer.vue +0 -146
- package/src/components/icon/Icon.vue +0 -96
- package/src/components/internal/HeaderAvatar.vue +0 -73
- package/src/components/internal/LoadingCircle.vue +0 -16
- package/src/components/internal/MenuIcon.vue +0 -13
- package/src/components/internal/ScrollToTop.vue +0 -26
- package/src/components/internal/ThemeToggle.ts +0 -41
- package/src/components/internal/ThemeToggle.vue +0 -23
- package/src/components/internal/cssClassBuilder.ts +0 -44
- package/src/components/internal/getMaxZindex.ts +0 -15
- package/src/components/layout/Layout.d.ts +0 -38
- package/src/components/layout/Layout.vue +0 -58
- package/src/components/message/Message.vue +0 -65
- package/src/components/navmenu/NavMenu.vue +0 -109
- package/src/components/tabnavigation/TabNavigation.vue +0 -125
- package/src/components/tooltip/Tooltip.vue +0 -91
- package/src/index.ts +0 -53
- package/src/keycloak.d.ts +0 -671
- package/src/keycloak.js +0 -1731
- package/src/services/authService.ts +0 -118
- package/src/services/dialogService.ts +0 -109
- package/src/services/navMenuService.ts +0 -21
- package/src/style/componentes.scss +0 -20
- package/src/style/src/_functions.scss +0 -19
- package/src/style/src/_mixins.scss +0 -193
- package/src/style/src/_variables.scss +0 -12
- package/src/style/src/components/_button.scss +0 -104
- package/src/style/src/components/_content.scss +0 -57
- package/src/style/src/components/_drawer.scss +0 -89
- package/src/style/src/components/_headerAvatar.scss +0 -22
- package/src/style/src/components/_icon.scss +0 -119
- package/src/style/src/components/_layout.scss +0 -160
- package/src/style/src/components/_loading-circle.scss +0 -24
- package/src/style/src/components/_mask.scss +0 -35
- package/src/style/src/components/_message.scss +0 -47
- package/src/style/src/components/_navmenulink.scss +0 -31
- package/src/style/src/components/_scrollToTop.scss +0 -28
- package/src/style/src/components/_svg_icon.scss +0 -5
- package/src/style/src/components/_tab-navigation.scss +0 -93
- package/src/style/src/components/_themetoggle.scss +0 -52
- package/src/style/src/components/_tooltip.scss +0 -53
- package/src/style/src/sweetalert/_sweetalert.scss +0 -9
- package/src/style/src/sweetalert/scss/_animations.scss +0 -197
- package/src/style/src/sweetalert/scss/_body.scss +0 -45
- package/src/style/src/sweetalert/scss/_core.scss +0 -862
- package/src/style/src/sweetalert/scss/_mixins.scss +0 -16
- package/src/style/src/sweetalert/scss/_theming.scss +0 -8
- package/src/style/src/sweetalert/scss/_toasts-animations.scss +0 -83
- package/src/style/src/sweetalert/scss/_toasts-body.scss +0 -85
- package/src/style/src/sweetalert/scss/_toasts.scss +0 -203
- package/src/style/src/sweetalert/scss/_variables.scss +0 -271
- package/src/ts-helpers.d.ts +0 -44
- package/src/types/index.ts +0 -59
- package/src/vite-env.d.ts +0 -1
- package/tsconfig.json +0 -22
- package/vite.config.js +0 -29
- /package/dist/{src/axios → axios}/axiosClient.d.ts +0 -0
- /package/dist/{src/common → common}/appResult.d.ts +0 -0
- /package/dist/{src/components → components}/button/Button.d.ts +0 -0
- /package/dist/{src/components → components}/content/Content.d.ts +0 -0
- /package/dist/{src/components → components}/drawer/Drawer.d.ts +0 -0
- /package/dist/{src/components → components}/icon/Icon.d.ts +0 -0
- /package/dist/{src/components → components}/internal/HeaderAvatar.d.ts +0 -0
- /package/dist/{src/components → components}/internal/LoadingCircle.d.ts +0 -0
- /package/dist/{src/components → components}/internal/MenuIcon.d.ts +0 -0
- /package/dist/{src/components → components}/internal/ScrollToTop.d.ts +0 -0
- /package/dist/{src/components → components}/internal/ThemeToggle.d.ts +0 -0
- /package/dist/{src/components → components}/internal/cssClassBuilder.d.ts +0 -0
- /package/dist/{src/components → components}/internal/getMaxZindex.d.ts +0 -0
- /package/dist/{src/components → components}/layout/Layout.d.ts +0 -0
- /package/dist/{src/components → components}/message/Message.d.ts +0 -0
- /package/dist/{src/components → components}/navmenu/NavMenu.d.ts +0 -0
- /package/dist/{src/components → components}/tabnavigation/TabNavigation.d.ts +0 -0
- /package/dist/{src/components → components}/tooltip/Tooltip.d.ts +0 -0
- /package/dist/{src/index.d.ts → index.d.ts} +0 -0
- /package/dist/{src/keycloak.d.ts → keycloak.d.ts} +0 -0
- /package/dist/{src/services → services}/authService.d.ts +0 -0
- /package/dist/{src/services → services}/dialogService.d.ts +0 -0
- /package/dist/{src/services → services}/navMenuService.d.ts +0 -0
- /package/dist/{src/types → types}/index.d.ts +0 -0
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luizleon/sf.prefeiturasp.vuecomponents",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.3",
|
|
4
4
|
"type": "module",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist"
|
|
7
|
+
],
|
|
5
8
|
"main": "dist/sf.prefeiturasp.vuecomponents.umd.js",
|
|
6
9
|
"module": "dist/sf.prefeiturasp.vuecomponents.es.js",
|
|
7
|
-
"types": "dist/
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
8
11
|
"scripts": {
|
|
9
12
|
"build": "rimraf ./dist/ && vue-tsc --noEmit && vite build"
|
|
10
13
|
},
|
package/src/axios/axiosClient.ts
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import _axios, { AxiosInstance, AxiosResponse } from "axios";
|
|
2
|
-
import { AppResult } from "../common/appResult";
|
|
3
|
-
import Keycloak from "../keycloak";
|
|
4
|
-
|
|
5
|
-
class AxiosClient {
|
|
6
|
-
constructor(keycloak: Keycloak, baseUrl: string) {
|
|
7
|
-
this.keycloak = keycloak;
|
|
8
|
-
this.axios = _axios.create();
|
|
9
|
-
this.axios.defaults.baseURL = baseUrl;
|
|
10
|
-
this.axios.defaults.headers.common["content-type"] =
|
|
11
|
-
"application/json";
|
|
12
|
-
this.axios.defaults.timeout = 1000 * 60 * 5;
|
|
13
|
-
this.axios.interceptors.request.use(async (config) => {
|
|
14
|
-
await this.SilentLogin();
|
|
15
|
-
const token = this.keycloak.token;
|
|
16
|
-
config.headers.Authorization = `Bearer ${token}`;
|
|
17
|
-
return config;
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
keycloak: Keycloak;
|
|
21
|
-
axios: AxiosInstance;
|
|
22
|
-
private fileName: string = "silent-login.html";
|
|
23
|
-
|
|
24
|
-
private ParseAppResult(data: any): AppResult<any> | null {
|
|
25
|
-
try {
|
|
26
|
-
const check1 = typeof data.hasSuccess === "boolean";
|
|
27
|
-
const check2 = typeof data.hasError === "boolean";
|
|
28
|
-
const check3 = typeof data.httpStatusCode === "number";
|
|
29
|
-
const check4 = Array.isArray(data.errors);
|
|
30
|
-
if (check1 && check2 && check3 && check4) return data;
|
|
31
|
-
return null;
|
|
32
|
-
} catch {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Usar após 'then' (httpStatusCode 2xx) para obter o resultado da requisição.
|
|
39
|
-
* @param r
|
|
40
|
-
*/
|
|
41
|
-
HandleThen<T>(r: AxiosResponse<any, any>): AppResult<T> {
|
|
42
|
-
const result = new AppResult<T>();
|
|
43
|
-
const parsed = this.ParseAppResult(r.data);
|
|
44
|
-
if (parsed === null) {
|
|
45
|
-
// Foi recebido um httpStatusCode 2xx, mas não é um AppResult.
|
|
46
|
-
// Então, o resultado é o próprio 'data'.
|
|
47
|
-
// Por padrão, httpStatusCode é 200.
|
|
48
|
-
result.value = r.data;
|
|
49
|
-
return result;
|
|
50
|
-
}
|
|
51
|
-
if (parsed.hasError) {
|
|
52
|
-
// Foi recebido um httpStatusCode 2xx e é um AppResult.
|
|
53
|
-
// Porém, o resultado é um erro.
|
|
54
|
-
// Então, adiciona os erros e o httpStatusCode.
|
|
55
|
-
parsed.errors.forEach((e) => result.WithError(e));
|
|
56
|
-
result.httpStatusCode = parsed.httpStatusCode;
|
|
57
|
-
return result;
|
|
58
|
-
}
|
|
59
|
-
// Foi recebido um httpStatusCode 2xx e é um AppResult.
|
|
60
|
-
// O resultado é um sucesso.
|
|
61
|
-
// Então, adiciona o valor.
|
|
62
|
-
// Por padrão, httpStatusCode é 200.
|
|
63
|
-
result.value = r.data.value;
|
|
64
|
-
return result;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Usar após 'catch' (httpStatusCode 4xx ou 5xx) para obter o resultado da requisição.
|
|
69
|
-
* @param r
|
|
70
|
-
* @returns
|
|
71
|
-
*/
|
|
72
|
-
HandleCatch<T>(r: any): AppResult<T> {
|
|
73
|
-
const result = new AppResult<T>();
|
|
74
|
-
const parsed = this.ParseAppResult(r.response?.data);
|
|
75
|
-
if (parsed === null) {
|
|
76
|
-
// Foi recebido um httpStatusCode 4xx ou 5xx, mas não é um AppResult.
|
|
77
|
-
// Então só adiciona o erro.
|
|
78
|
-
result.WithError(r.message);
|
|
79
|
-
} else {
|
|
80
|
-
// Foi recebido um httpStatusCode 4xx ou 5xx e é um AppResult.
|
|
81
|
-
// Então, adiciona os erros e o httpStatusCode.
|
|
82
|
-
result.WithErrors(parsed.errors);
|
|
83
|
-
result.httpStatusCode = parsed.httpStatusCode;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return result;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
private async SilentLogin() {
|
|
90
|
-
return new Promise<boolean>(async (res) => {
|
|
91
|
-
if (!this.isTokenExpired) {
|
|
92
|
-
res(true);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
const iframe = this.GenerateIframe();
|
|
96
|
-
const fn = (e: MessageEvent) => {
|
|
97
|
-
document.body.removeChild(iframe);
|
|
98
|
-
window.removeEventListener("message", fn);
|
|
99
|
-
if (e.origin !== location.origin) {
|
|
100
|
-
res(false);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
const action = e.data.action;
|
|
104
|
-
const code = e.data.code;
|
|
105
|
-
const state = e.data.state;
|
|
106
|
-
const error = e.data.error;
|
|
107
|
-
if (
|
|
108
|
-
action !== "silent-login-iframe-result" ||
|
|
109
|
-
error === "login_required" ||
|
|
110
|
-
!code
|
|
111
|
-
) {
|
|
112
|
-
res(false);
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
const req = new XMLHttpRequest();
|
|
116
|
-
req.open("POST", this.tokenExchangeUrl, true);
|
|
117
|
-
req.setRequestHeader(
|
|
118
|
-
"Content-type",
|
|
119
|
-
"application/x-www-form-urlencoded"
|
|
120
|
-
);
|
|
121
|
-
req.onreadystatechange = () => {
|
|
122
|
-
if (req.readyState === 4) {
|
|
123
|
-
if (req.status === 200) {
|
|
124
|
-
const tokenResponse = JSON.parse(req.responseText);
|
|
125
|
-
this.keycloak.setToken(
|
|
126
|
-
tokenResponse.access_token,
|
|
127
|
-
null,
|
|
128
|
-
tokenResponse.id_token,
|
|
129
|
-
new Date().getTime()
|
|
130
|
-
);
|
|
131
|
-
res(true);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
res(false);
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
req.send(this.TokenExchangeParams(code, state));
|
|
138
|
-
};
|
|
139
|
-
window.addEventListener("message", fn);
|
|
140
|
-
}).then((r) => {
|
|
141
|
-
if (!r) {
|
|
142
|
-
this.keycloak.clearToken();
|
|
143
|
-
this.keycloak.login();
|
|
144
|
-
}
|
|
145
|
-
return r;
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
private GetCodeVerifierFromState(state: string) {
|
|
150
|
-
const key = `kc-callback-${state}`;
|
|
151
|
-
const stored = localStorage.getItem(key);
|
|
152
|
-
if (stored) {
|
|
153
|
-
localStorage.removeItem(key);
|
|
154
|
-
return JSON.parse(stored).pkceCodeVerifier;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
private TokenExchangeParams(code: string, state: string) {
|
|
159
|
-
let params = `code=${code}&grant_type=authorization_code`;
|
|
160
|
-
params += `&client_id=${encodeURIComponent(
|
|
161
|
-
this.keycloak.clientId!
|
|
162
|
-
)}`;
|
|
163
|
-
params += `&redirect_uri=${this.silentLoginRedirectUrl}`;
|
|
164
|
-
params += `&code_verifier=${this.GetCodeVerifierFromState(
|
|
165
|
-
state
|
|
166
|
-
)}`;
|
|
167
|
-
return params;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
private GenerateIframe() {
|
|
171
|
-
const iframe = document.createElement("iframe");
|
|
172
|
-
iframe.src =
|
|
173
|
-
this.keycloak.createLoginUrl({
|
|
174
|
-
prompt: "none",
|
|
175
|
-
redirectUri: this.silentLoginRedirectUrl,
|
|
176
|
-
}) ?? "";
|
|
177
|
-
document.body.appendChild(iframe);
|
|
178
|
-
return iframe;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
private get silentLoginRedirectUrl() {
|
|
182
|
-
return `${location.origin}/${this.fileName}`;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
private get tokenExchangeUrl() {
|
|
186
|
-
return `${this.keycloak.authServerUrl}/realms/${this.keycloak.realm}/protocol/openid-connect/token`;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
private get isTokenExpired() {
|
|
190
|
-
const token = this.keycloak.tokenParsed;
|
|
191
|
-
const exp = token ? token["exp"] : null;
|
|
192
|
-
|
|
193
|
-
if (!token || !exp) return true;
|
|
194
|
-
|
|
195
|
-
const expiresIn =
|
|
196
|
-
exp -
|
|
197
|
-
Math.ceil(new Date().getTime() / 1000) +
|
|
198
|
-
(this.keycloak.timeSkew ?? 0);
|
|
199
|
-
|
|
200
|
-
return expiresIn < 10;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const UseAxiosClient = (keycloak: Keycloak, baseURL: string) =>
|
|
205
|
-
new AxiosClient(keycloak, baseURL);
|
|
206
|
-
|
|
207
|
-
export { AxiosClient, UseAxiosClient };
|
package/src/common/appResult.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
export class AppResult<Value = null> {
|
|
2
|
-
constructor(value: Value | null = null) {
|
|
3
|
-
this._value = value;
|
|
4
|
-
this._httpStatusCode = 200;
|
|
5
|
-
}
|
|
6
|
-
errors: string[] = [];
|
|
7
|
-
private _value?: Value | null = null;
|
|
8
|
-
private _httpStatusCode: number;
|
|
9
|
-
get value() {
|
|
10
|
-
return this._value as Value;
|
|
11
|
-
}
|
|
12
|
-
set value(v: Value) {
|
|
13
|
-
this._value = v;
|
|
14
|
-
}
|
|
15
|
-
get hasSuccess(): boolean {
|
|
16
|
-
return !this.hasError;
|
|
17
|
-
}
|
|
18
|
-
get hasError(): boolean {
|
|
19
|
-
return this.errors.length > 0;
|
|
20
|
-
}
|
|
21
|
-
get httpStatusCode() {
|
|
22
|
-
return this._httpStatusCode;
|
|
23
|
-
}
|
|
24
|
-
set httpStatusCode(v: number) {
|
|
25
|
-
this._httpStatusCode = v;
|
|
26
|
-
}
|
|
27
|
-
WithError(error: string) {
|
|
28
|
-
this.errors.push(error);
|
|
29
|
-
if (this._httpStatusCode === 200) this._httpStatusCode = 400;
|
|
30
|
-
}
|
|
31
|
-
WithErrors(errors: string[]) {
|
|
32
|
-
errors.forEach((error) => this.WithError(error));
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { StyleValue } from "vue";
|
|
3
|
-
import Icon from "../icon/Icon.vue";
|
|
4
|
-
import { CssClassBuilder } from "../internal/cssClassBuilder";
|
|
5
|
-
|
|
6
|
-
export interface SfButtonProps {
|
|
7
|
-
icon?: string;
|
|
8
|
-
/**
|
|
9
|
-
* @default true
|
|
10
|
-
*/
|
|
11
|
-
visible?: boolean;
|
|
12
|
-
class?: any;
|
|
13
|
-
style?: StyleValue;
|
|
14
|
-
disabled?: boolean;
|
|
15
|
-
loading?: boolean;
|
|
16
|
-
/**
|
|
17
|
-
* @default 'primary'
|
|
18
|
-
*/
|
|
19
|
-
color?:
|
|
20
|
-
| "primary"
|
|
21
|
-
| "secondary"
|
|
22
|
-
| "info"
|
|
23
|
-
| "success"
|
|
24
|
-
| "help"
|
|
25
|
-
| "warn"
|
|
26
|
-
| "error"
|
|
27
|
-
| undefined;
|
|
28
|
-
/**
|
|
29
|
-
* @default 'md'
|
|
30
|
-
*/
|
|
31
|
-
size?: "sm" | "md" | "lg" | undefined;
|
|
32
|
-
/**
|
|
33
|
-
* @default 'filled'
|
|
34
|
-
*/
|
|
35
|
-
variant?: "filled" | "outlined" | "text" | undefined;
|
|
36
|
-
autofocus?: boolean;
|
|
37
|
-
form?: string;
|
|
38
|
-
name?: string;
|
|
39
|
-
id?: string;
|
|
40
|
-
/**
|
|
41
|
-
* @default 'button'
|
|
42
|
-
*/
|
|
43
|
-
type?: "submit" | "reset" | "button";
|
|
44
|
-
value?: string | string[] | number;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const props: SfButtonProps = withDefaults(
|
|
48
|
-
defineProps<SfButtonProps>(),
|
|
49
|
-
{
|
|
50
|
-
visible: true,
|
|
51
|
-
color: "primary",
|
|
52
|
-
size: "md",
|
|
53
|
-
type: "button",
|
|
54
|
-
variant: "filled",
|
|
55
|
-
}
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
function CssClass() {
|
|
59
|
-
return new CssClassBuilder(`${props.class ?? ""} sf-button`)
|
|
60
|
-
.AddClass("sf-component-loading", props.loading)
|
|
61
|
-
.AddClass("sf-component-disabled", props.disabled)
|
|
62
|
-
.AddClass("sf-button-small", props.size === "sm")
|
|
63
|
-
.AddClass("sf-button-medium", props.size === "md")
|
|
64
|
-
.AddClass("sf-button-large", props.size === "lg")
|
|
65
|
-
.AddClass("sf-button-filled", props.variant === "filled")
|
|
66
|
-
.AddClass("sf-button-text", props.variant === "text")
|
|
67
|
-
.AddClass("sf-button-outlined", props.variant === "outlined")
|
|
68
|
-
.AddClass("sf-button-with-icon", !!props.icon)
|
|
69
|
-
.AddClass("sf-ripple")
|
|
70
|
-
.Build();
|
|
71
|
-
}
|
|
72
|
-
</script>
|
|
73
|
-
|
|
74
|
-
<template>
|
|
75
|
-
<button
|
|
76
|
-
v-if="props.visible"
|
|
77
|
-
:id="props.id"
|
|
78
|
-
:name="props.name"
|
|
79
|
-
:class="CssClass()"
|
|
80
|
-
:style="props.style"
|
|
81
|
-
:disabled="props.disabled || props.loading"
|
|
82
|
-
:data-color="props.color"
|
|
83
|
-
:form="props.form"
|
|
84
|
-
:type="props.type"
|
|
85
|
-
>
|
|
86
|
-
<Icon
|
|
87
|
-
v-if="!!props.icon"
|
|
88
|
-
:icon="props.icon"
|
|
89
|
-
:loading="props.loading"
|
|
90
|
-
:size="props.size"
|
|
91
|
-
:color="undefined"
|
|
92
|
-
:button-props="{ tabindex: -1 }"
|
|
93
|
-
/>
|
|
94
|
-
<Icon
|
|
95
|
-
v-else-if="props.loading"
|
|
96
|
-
:loading="true"
|
|
97
|
-
:size="props.size"
|
|
98
|
-
:button-props="{ tabindex: -1 }"
|
|
99
|
-
/>
|
|
100
|
-
<span class="sf-button-label">
|
|
101
|
-
<slot name="default"></slot>
|
|
102
|
-
</span>
|
|
103
|
-
</button>
|
|
104
|
-
</template>
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { StyleValue } from "vue";
|
|
3
|
-
import ScrollToTop from "../internal/ScrollToTop.vue";
|
|
4
|
-
import { CssClassBuilder } from "../internal/cssClassBuilder";
|
|
5
|
-
|
|
6
|
-
export interface SfContentProps {
|
|
7
|
-
class?: any;
|
|
8
|
-
style?: StyleValue;
|
|
9
|
-
disableBodyPadding?: boolean;
|
|
10
|
-
disableHeaderPadding?: boolean;
|
|
11
|
-
disableFooterPadding?: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const props: SfContentProps = defineProps<SfContentProps>();
|
|
15
|
-
|
|
16
|
-
function Css() {
|
|
17
|
-
return new CssClassBuilder("sf-content")
|
|
18
|
-
.AddClass(props.class, !!props.class)
|
|
19
|
-
.Build();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function CssBody() {
|
|
23
|
-
return new CssClassBuilder("sf-content-body")
|
|
24
|
-
.AddClass("sf-content-no-padding", props.disableBodyPadding)
|
|
25
|
-
.Build();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function CssHeader() {
|
|
29
|
-
return new CssClassBuilder("sf-content-header")
|
|
30
|
-
.AddClass("sf-content-no-padding", props.disableHeaderPadding)
|
|
31
|
-
.Build();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function CssFooter() {
|
|
35
|
-
return new CssClassBuilder("sf-content-footer")
|
|
36
|
-
.AddClass("sf-content-no-padding", props.disableFooterPadding)
|
|
37
|
-
.Build();
|
|
38
|
-
}
|
|
39
|
-
</script>
|
|
40
|
-
<template>
|
|
41
|
-
<div :class="Css()" :style="props.style">
|
|
42
|
-
<div :class="CssHeader()"><slot name="header"></slot></div>
|
|
43
|
-
<div :class="CssBody()">
|
|
44
|
-
<slot name="default"></slot>
|
|
45
|
-
<ScrollToTop />
|
|
46
|
-
</div>
|
|
47
|
-
<div :class="CssFooter()"><slot name="footer"></slot></div>
|
|
48
|
-
</div>
|
|
49
|
-
</template>
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { StyleValue, ref, useSlots, watch } from "vue";
|
|
3
|
-
import { CssClassBuilder } from "../internal/cssClassBuilder";
|
|
4
|
-
import { GetMaxZindex } from "../internal/getMaxZindex";
|
|
5
|
-
import { nanoid } from "nanoid";
|
|
6
|
-
import SfIcon from "../icon/Icon.vue";
|
|
7
|
-
|
|
8
|
-
export interface SfDrawerProps {
|
|
9
|
-
visible: boolean;
|
|
10
|
-
/**
|
|
11
|
-
* @default 'left'
|
|
12
|
-
*/
|
|
13
|
-
position?: "left" | "right" | "bottom" | "full";
|
|
14
|
-
style?: StyleValue;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const props: SfDrawerProps = withDefaults(
|
|
18
|
-
defineProps<SfDrawerProps>(),
|
|
19
|
-
{
|
|
20
|
-
visible: false,
|
|
21
|
-
position: "left",
|
|
22
|
-
}
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
const emit = defineEmits<{
|
|
26
|
-
(e: "update:visible", v: boolean): void;
|
|
27
|
-
(e: "open"): void;
|
|
28
|
-
(e: "before-close"): void;
|
|
29
|
-
}>();
|
|
30
|
-
|
|
31
|
-
const slots = useSlots();
|
|
32
|
-
|
|
33
|
-
const visible = ref(props.visible);
|
|
34
|
-
|
|
35
|
-
const maskVisible = ref(false);
|
|
36
|
-
|
|
37
|
-
const zIndex = ref(0);
|
|
38
|
-
|
|
39
|
-
const id = nanoid();
|
|
40
|
-
|
|
41
|
-
function Close() {
|
|
42
|
-
visible.value = false;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function OnKeydown(ev: KeyboardEvent) {
|
|
46
|
-
if (ev.key === "Escape" || ev.code === "Escape") {
|
|
47
|
-
Close();
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function OnEnter() {
|
|
52
|
-
emit("open");
|
|
53
|
-
window.document.addEventListener("keydown", OnKeydown, true);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function OnBeforeEnter(el: Element) {
|
|
57
|
-
zIndex.value = GetMaxZindex();
|
|
58
|
-
(el as HTMLElement).style.zIndex = `${zIndex.value}`;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function OnBeforeLeave(el: Element) {
|
|
62
|
-
emit("before-close");
|
|
63
|
-
el?.parentElement?.classList.add("sf-mask-overlay-leave");
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function OnAfterLeave() {
|
|
67
|
-
window.document.removeEventListener("keydown", OnKeydown, true);
|
|
68
|
-
maskVisible.value = false;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function CssMask() {
|
|
72
|
-
return new CssClassBuilder("sf-mask sf-mask-overlay")
|
|
73
|
-
.AddClass("right", props.position === "right")
|
|
74
|
-
.AddClass("bottom", props.position === "bottom")
|
|
75
|
-
.AddClass("full", props.position === "full")
|
|
76
|
-
.AddClass("sf-mask-overlay-enter", props.visible === true)
|
|
77
|
-
.Build();
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
watch(
|
|
81
|
-
() => props.visible,
|
|
82
|
-
(v) => {
|
|
83
|
-
visible.value = v;
|
|
84
|
-
}
|
|
85
|
-
);
|
|
86
|
-
watch(
|
|
87
|
-
() => visible.value,
|
|
88
|
-
(v) => {
|
|
89
|
-
emit("update:visible", v);
|
|
90
|
-
if (v) {
|
|
91
|
-
maskVisible.value = true;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
);
|
|
95
|
-
</script>
|
|
96
|
-
|
|
97
|
-
<template>
|
|
98
|
-
<Teleport :to="'body'">
|
|
99
|
-
<div
|
|
100
|
-
v-if="maskVisible"
|
|
101
|
-
:id="id"
|
|
102
|
-
:class="CssMask()"
|
|
103
|
-
:style="{ zIndex: zIndex - 1 }"
|
|
104
|
-
@click.stop="Close"
|
|
105
|
-
>
|
|
106
|
-
<transition
|
|
107
|
-
name="sf-drawer"
|
|
108
|
-
appear
|
|
109
|
-
@before-enter="OnBeforeEnter"
|
|
110
|
-
@enter="OnEnter"
|
|
111
|
-
@before-leave="OnBeforeLeave"
|
|
112
|
-
@after-leave="OnAfterLeave"
|
|
113
|
-
>
|
|
114
|
-
<div
|
|
115
|
-
v-if="visible"
|
|
116
|
-
:class="[
|
|
117
|
-
'sf-drawer',
|
|
118
|
-
{
|
|
119
|
-
'sf-drawer-with-footer': !!slots.footer,
|
|
120
|
-
},
|
|
121
|
-
]"
|
|
122
|
-
:style="props.style"
|
|
123
|
-
role="dialog"
|
|
124
|
-
@click.stop="void"
|
|
125
|
-
>
|
|
126
|
-
<div class="sf-drawer-header">
|
|
127
|
-
<span class="sf-drawer-title">
|
|
128
|
-
<slot name="title"></slot>
|
|
129
|
-
</span>
|
|
130
|
-
<SfIcon
|
|
131
|
-
:button="true"
|
|
132
|
-
:icon="'close'"
|
|
133
|
-
@click.stop="Close"
|
|
134
|
-
/>
|
|
135
|
-
</div>
|
|
136
|
-
<div class="sf-drawer-content">
|
|
137
|
-
<slot name="default"></slot>
|
|
138
|
-
</div>
|
|
139
|
-
<div class="sf-drawer-footer" v-if="slots.footer">
|
|
140
|
-
<slot name="footer"></slot>
|
|
141
|
-
</div>
|
|
142
|
-
</div>
|
|
143
|
-
</transition>
|
|
144
|
-
</div>
|
|
145
|
-
</Teleport>
|
|
146
|
-
</template>
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { ButtonHTMLAttributes, StyleValue, computed } from "vue";
|
|
3
|
-
import LoadingCircle from "../internal/LoadingCircle.vue";
|
|
4
|
-
import { CssClassBuilder } from "../internal/cssClassBuilder";
|
|
5
|
-
import Tooltip from "../tooltip/Tooltip.vue";
|
|
6
|
-
import { Color, IconSize } from "../../types";
|
|
7
|
-
|
|
8
|
-
export interface SfIconProps {
|
|
9
|
-
icon?: string;
|
|
10
|
-
visible?: boolean;
|
|
11
|
-
class?: any;
|
|
12
|
-
style?: StyleValue;
|
|
13
|
-
disabled?: boolean;
|
|
14
|
-
loading?: boolean;
|
|
15
|
-
button?: boolean;
|
|
16
|
-
size?: IconSize;
|
|
17
|
-
dot?: boolean;
|
|
18
|
-
dotColor?: Color;
|
|
19
|
-
filled?: boolean;
|
|
20
|
-
buttonProps?: ButtonHTMLAttributes;
|
|
21
|
-
tooltip?: string;
|
|
22
|
-
color?: Color | "inherit";
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const props: SfIconProps = withDefaults(defineProps<SfIconProps>(), {
|
|
26
|
-
visible: true,
|
|
27
|
-
class: "",
|
|
28
|
-
style: "",
|
|
29
|
-
size: "md",
|
|
30
|
-
dotColor: "error",
|
|
31
|
-
color: "inherit",
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const dynamicComponent = computed(() =>
|
|
35
|
-
props.tooltip ? Tooltip : "div"
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
const componentClass = computed(() => {
|
|
39
|
-
return props.tooltip ? "" : "display-content";
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const cssClass = computed(() => {
|
|
43
|
-
return new CssClassBuilder(`${props.class} sf-icon`)
|
|
44
|
-
.AddClass("sf-component-loading", props.loading)
|
|
45
|
-
.AddClass("sf-component-disabled", props.disabled)
|
|
46
|
-
.AddClass("sf-icon-button", props.button)
|
|
47
|
-
.AddClass("sf-icon-small", props.size === "sm")
|
|
48
|
-
.AddClass("sf-icon-medium", props.size === "md")
|
|
49
|
-
.AddClass("sf-icon-large", props.size === "lg")
|
|
50
|
-
.AddClass("sf-icon-xlarge", props.size === "xl")
|
|
51
|
-
.AddClass("sf-icon-2xlarge", props.size === "2xl")
|
|
52
|
-
.AddClass("sf-icon-3xlarge", props.size === "3xl")
|
|
53
|
-
.AddClass("sf-ripple", props.button === true)
|
|
54
|
-
.AddClass(
|
|
55
|
-
`${props.color}-color`,
|
|
56
|
-
!!props.color && props.color !== "inherit"
|
|
57
|
-
)
|
|
58
|
-
.Build();
|
|
59
|
-
});
|
|
60
|
-
</script>
|
|
61
|
-
|
|
62
|
-
<template>
|
|
63
|
-
<component
|
|
64
|
-
:is="dynamicComponent"
|
|
65
|
-
:text="props.tooltip"
|
|
66
|
-
:class="componentClass"
|
|
67
|
-
>
|
|
68
|
-
<div
|
|
69
|
-
v-if="!!props.visible"
|
|
70
|
-
:style="props.style"
|
|
71
|
-
:class="cssClass"
|
|
72
|
-
>
|
|
73
|
-
<button
|
|
74
|
-
:class="[
|
|
75
|
-
'material-symbols-outlined',
|
|
76
|
-
{ 'icon-filled': !!props.filled },
|
|
77
|
-
]"
|
|
78
|
-
:disabled="props.disabled || props.loading"
|
|
79
|
-
v-bind="props.buttonProps"
|
|
80
|
-
:tabindex="!props.button ? -1 : undefined"
|
|
81
|
-
>
|
|
82
|
-
<template v-if="props.loading">
|
|
83
|
-
<LoadingCircle></LoadingCircle>
|
|
84
|
-
</template>
|
|
85
|
-
<template v-else>{{ props.icon }}</template>
|
|
86
|
-
</button>
|
|
87
|
-
<span v-if="props.dot" :data-color="props.dotColor"></span>
|
|
88
|
-
</div>
|
|
89
|
-
</component>
|
|
90
|
-
</template>
|
|
91
|
-
|
|
92
|
-
<style scoped>
|
|
93
|
-
.display-content {
|
|
94
|
-
display: contents;
|
|
95
|
-
}
|
|
96
|
-
</style>
|