@ti-engine/web-framework 1.19.0 → 1.20.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/.env +4 -4
- package/CHANGELOG.md +384 -353
- package/README.md +73 -73
- package/bin/build/post-install.js +18 -18
- package/bin/localization/web-server-labels.json +27 -27
- package/bin/static/.well-known/appspecific/com.chrome.devtools.json +5 -5
- package/bin/static/fragments/components/component-notification-bar.html +21 -21
- package/bin/static/fragments/components/component-sidebar.html +33 -33
- package/bin/static/fragments/components/component-tooltip.html +10 -10
- package/bin/static/fragments/components/component-topbar.html +5 -5
- package/bin/static/fragments/frame-administration.html +2 -2
- package/bin/static/fragments/frame-application.html +18 -18
- package/bin/static/fragments/frame-dashboard.html +2 -2
- package/bin/static/fragments/frame-login.html +119 -119
- package/bin/static/fragments/frame-not-found.html +2 -2
- package/bin/static/fragments/frame-profile.html +2 -2
- package/bin/static/index.html +22 -22
- package/bin/static/scripts/ti-charts.js +1591 -1591
- package/bin/static/scripts/ti-framework.css +3194 -3194
- package/bin/static/scripts/ti-framework.js +1427 -1427
- package/bin/static/scripts/ti-theme-black-glass.css +216 -216
- package/bin/static/scripts/ti-theme-daylight.css +87 -87
- package/bin/web-app-manager.js +660 -663
- package/bin/web-server.js +936 -937
- package/bin/web-server.json +48 -48
- package/components/admin-config-handlers.js +95 -92
- package/components/auth-manager.js +438 -442
- package/components/authorization.js +135 -135
- package/components/config-change-notifier.js +98 -98
- package/components/config-registry.js +257 -260
- package/components/config-service.js +363 -360
- package/components/config-store.js +244 -246
- package/components/definitions.types.js +28 -26
- package/components/session-store.js +113 -110
- package/components/user.js +134 -132
- package/components/web-config-env.js +85 -85
- package/components/web-handlers.js +803 -800
- package/package.json +139 -67
- package/types/bin/web-app-manager.d.ts +194 -0
- package/types/bin/web-server.d.ts +373 -0
- package/types/components/admin-config-handlers.d.ts +11 -0
- package/types/components/auth-manager.d.ts +125 -0
- package/types/components/authorization.d.ts +54 -0
- package/types/components/config-change-notifier.d.ts +73 -0
- package/types/components/config-registry.d.ts +149 -0
- package/types/components/config-service.d.ts +218 -0
- package/types/components/config-store.d.ts +128 -0
- package/types/components/definitions.types.d.ts +31 -0
- package/types/components/session-store.d.ts +56 -0
- package/types/components/user.d.ts +83 -0
- package/types/components/web-config-env.d.ts +17 -0
- package/types/components/web-handlers.d.ts +23 -0
package/README.md
CHANGED
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
# ti-engine web framework
|
|
2
|
-
|
|
3
|
-

|
|
4
|
-
|
|
5
|
-
Flexible framework for the creation of microservices with node.js.
|
|
6
|
-
|
|
7
|
-
## Information
|
|
8
|
-
|
|
9
|
-
This is a customizable web framework based on the **ti-engine** framework. Currently under development.
|
|
10
|
-
|
|
11
|
-
## Environment variables
|
|
12
|
-
|
|
13
|
-
The web server configuration (host, port, TLS, cookies, etc.) is normally provided via the service configuration file merged in the `TiWebServer` constructor. The following environment variables can override individual values at runtime — useful for container/12-factor deployments where the same image is configured per environment:
|
|
14
|
-
|
|
15
|
-
* `TI_WEB_HOST` overrides the bind address (e.g. `0.0.0.0` in a container). Defaults to the value in the web server config.
|
|
16
|
-
* `TI_WEB_PORT` overrides the listen port.
|
|
17
|
-
* `TI_WEB_USE_TLS` (`true`/`false`) toggles in-app TLS. Set `false` when a reverse proxy / ingress terminates TLS.
|
|
18
|
-
* `TI_WEB_TLS_CERT_PATH` / `TI_WEB_TLS_KEY_PATH` override the TLS certificate/key paths (only used when TLS is enabled).
|
|
19
|
-
* `TI_WEB_COOKIE_SECRET` sets the session cookie signing secret. Set a stable, private value for durable sessions and multi-replica deployments (otherwise a random per-process value is used).
|
|
20
|
-
* `TI_WEB_AUTH_METHODS` (comma-separated) **replaces** the enabled authentication methods (`auth.enabledMethods`), e.g. `openid-google` or `local,openid-google`.
|
|
21
|
-
* `TI_WEB_AUTH_ADMINS` (comma-separated) **replaces** the admin allowlist (`auth.admins`). Entries are matched against the session user's user ID, username or email, so an OpenID deployment lists emails. An explicitly empty value means *no admins*.
|
|
22
|
-
* `TI_WEB_TRUSTED_ORIGINS` (comma-separated) **replaces** the trusted request origins (`trustedOrigins`) — needed behind proxies that do not present the real external origin.
|
|
23
|
-
* `TI_WEB_STATIC_MAX_AGE` (whole seconds) overrides `staticCache.maxAge`. See [Static asset caching](#static-asset-caching).
|
|
24
|
-
* `TI_WEB_STATIC_IMMUTABLE` (`true`/`false`) overrides `staticCache.immutable`.
|
|
25
|
-
* `TI_WEB_STATIC_IMMUTABLE_PATHS` (comma-separated) **replaces** `staticCache.immutablePaths`. An explicitly empty value means *no long-lived paths*.
|
|
26
|
-
|
|
27
|
-
OpenID Connect providers are configured with their own variables — `TI_AZURE_AUTH_CLIENT_ID` / `TI_AZURE_AUTH_CLIENT_SECRET` / `TI_AZURE_AUTH_CALLBACK_URL` / `TI_AZURE_AUTH_DISCOVERY_URL`, and the `TI_GCLOUD_AUTH_*` equivalents. A callback URL may be given either as the full absolute URL registered with the provider (`https://your-host/login/azure-callback`) or as a path (`/login/azure-callback`): the server always listens on the path, while the `redirect_uri` sent to the provider is the absolute value verbatim if one was configured, and otherwise assembled from the request's forwarded protocol/host.
|
|
28
|
-
|
|
29
|
-
## Static asset caching
|
|
30
|
-
|
|
31
|
-
Everything under `/static` is served with a `Cache-Control` policy configured by the `staticCache` block:
|
|
32
|
-
|
|
33
|
-
```json
|
|
34
|
-
{
|
|
35
|
-
"staticCache": {
|
|
36
|
-
"maxAge": 0,
|
|
37
|
-
"immutable": false,
|
|
38
|
-
"immutablePaths": [ "/fonts/" ]
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
* `maxAge` — the `max-age` in **whole seconds** (not an express-style `"1y"` duration string; one is rejected with a warning rather than reinterpreted as milliseconds). `0`, the default, emits `public, max-age=0, must-revalidate`.
|
|
44
|
-
* `immutable` — adds the `immutable` directive. Defaults to `false`, and is **ignored with a warning when `maxAge` is 0**, since a response that is stale on arrival cannot also promise never to change.
|
|
45
|
-
* `immutablePaths` — path prefixes under `/static` (matched case-sensitively, on a path-segment boundary) served `public, max-age=31536000, immutable` regardless of the two settings above. Defaults to `[ "/fonts/" ]`. This **replaces** rather than merges, so `[]` means no long-lived paths.
|
|
46
|
-
|
|
47
|
-
**The default revalidates, and that is deliberate.** `immutable` tells a browser the bytes behind a URL will never change, and browsers honour it so completely that not even a manual reload revalidates. On a stable filename — which is what the framework's own assets use (`/static/scripts/ti-framework.js`, the theme sheets) — the promise is false, and a deployed CSS or JS fix simply never reaches anyone who has already visited, for up to a year, with no way to tell them otherwise. Revalidating costs a conditional request per asset, answered with a `304` from the `ETag`/`Last-Modified` that `express.static` still attaches — headers, no body.
|
|
48
|
-
|
|
49
|
-
**Opt back into `immutable` once your filenames are content-addressed.** If your build emits `app.a1b2c3.css`, or your application appends a content hash to each asset URL, the promise becomes true and there is real value in making it:
|
|
50
|
-
|
|
51
|
-
```json
|
|
52
|
-
{
|
|
53
|
-
"staticCache": { "maxAge": 31536000, "immutable": true }
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Fonts are the default exception because a released `.woff2` is an artifact rather than something edited in place, and its filename already carries the family, weight and style. If that is not how a given deployment manages its fonts, clear the list.
|
|
58
|
-
|
|
59
|
-
## Configure HTTPS for development
|
|
60
|
-
|
|
61
|
-
Use the `mkcert` tool to create a certificate for development.
|
|
62
|
-
|
|
63
|
-
Step 1: Install the tool:
|
|
64
|
-
```text
|
|
65
|
-
choco install mkcert
|
|
66
|
-
```
|
|
67
|
-
Step 2: Install certificate authority:
|
|
68
|
-
```text
|
|
69
|
-
mkcert -install
|
|
70
|
-
```
|
|
71
|
-
Step 3: Generate certificate files for localhost:
|
|
72
|
-
```text
|
|
73
|
-
mkcert localhost 127.0.0.1 ::1
|
|
1
|
+
# ti-engine web framework
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Flexible framework for the creation of microservices with node.js.
|
|
6
|
+
|
|
7
|
+
## Information
|
|
8
|
+
|
|
9
|
+
This is a customizable web framework based on the **ti-engine** framework. Currently under development.
|
|
10
|
+
|
|
11
|
+
## Environment variables
|
|
12
|
+
|
|
13
|
+
The web server configuration (host, port, TLS, cookies, etc.) is normally provided via the service configuration file merged in the `TiWebServer` constructor. The following environment variables can override individual values at runtime — useful for container/12-factor deployments where the same image is configured per environment:
|
|
14
|
+
|
|
15
|
+
* `TI_WEB_HOST` overrides the bind address (e.g. `0.0.0.0` in a container). Defaults to the value in the web server config.
|
|
16
|
+
* `TI_WEB_PORT` overrides the listen port.
|
|
17
|
+
* `TI_WEB_USE_TLS` (`true`/`false`) toggles in-app TLS. Set `false` when a reverse proxy / ingress terminates TLS.
|
|
18
|
+
* `TI_WEB_TLS_CERT_PATH` / `TI_WEB_TLS_KEY_PATH` override the TLS certificate/key paths (only used when TLS is enabled).
|
|
19
|
+
* `TI_WEB_COOKIE_SECRET` sets the session cookie signing secret. Set a stable, private value for durable sessions and multi-replica deployments (otherwise a random per-process value is used).
|
|
20
|
+
* `TI_WEB_AUTH_METHODS` (comma-separated) **replaces** the enabled authentication methods (`auth.enabledMethods`), e.g. `openid-google` or `local,openid-google`.
|
|
21
|
+
* `TI_WEB_AUTH_ADMINS` (comma-separated) **replaces** the admin allowlist (`auth.admins`). Entries are matched against the session user's user ID, username or email, so an OpenID deployment lists emails. An explicitly empty value means *no admins*.
|
|
22
|
+
* `TI_WEB_TRUSTED_ORIGINS` (comma-separated) **replaces** the trusted request origins (`trustedOrigins`) — needed behind proxies that do not present the real external origin.
|
|
23
|
+
* `TI_WEB_STATIC_MAX_AGE` (whole seconds) overrides `staticCache.maxAge`. See [Static asset caching](#static-asset-caching).
|
|
24
|
+
* `TI_WEB_STATIC_IMMUTABLE` (`true`/`false`) overrides `staticCache.immutable`.
|
|
25
|
+
* `TI_WEB_STATIC_IMMUTABLE_PATHS` (comma-separated) **replaces** `staticCache.immutablePaths`. An explicitly empty value means *no long-lived paths*.
|
|
26
|
+
|
|
27
|
+
OpenID Connect providers are configured with their own variables — `TI_AZURE_AUTH_CLIENT_ID` / `TI_AZURE_AUTH_CLIENT_SECRET` / `TI_AZURE_AUTH_CALLBACK_URL` / `TI_AZURE_AUTH_DISCOVERY_URL`, and the `TI_GCLOUD_AUTH_*` equivalents. A callback URL may be given either as the full absolute URL registered with the provider (`https://your-host/login/azure-callback`) or as a path (`/login/azure-callback`): the server always listens on the path, while the `redirect_uri` sent to the provider is the absolute value verbatim if one was configured, and otherwise assembled from the request's forwarded protocol/host.
|
|
28
|
+
|
|
29
|
+
## Static asset caching
|
|
30
|
+
|
|
31
|
+
Everything under `/static` is served with a `Cache-Control` policy configured by the `staticCache` block:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"staticCache": {
|
|
36
|
+
"maxAge": 0,
|
|
37
|
+
"immutable": false,
|
|
38
|
+
"immutablePaths": [ "/fonts/" ]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
* `maxAge` — the `max-age` in **whole seconds** (not an express-style `"1y"` duration string; one is rejected with a warning rather than reinterpreted as milliseconds). `0`, the default, emits `public, max-age=0, must-revalidate`.
|
|
44
|
+
* `immutable` — adds the `immutable` directive. Defaults to `false`, and is **ignored with a warning when `maxAge` is 0**, since a response that is stale on arrival cannot also promise never to change.
|
|
45
|
+
* `immutablePaths` — path prefixes under `/static` (matched case-sensitively, on a path-segment boundary) served `public, max-age=31536000, immutable` regardless of the two settings above. Defaults to `[ "/fonts/" ]`. This **replaces** rather than merges, so `[]` means no long-lived paths.
|
|
46
|
+
|
|
47
|
+
**The default revalidates, and that is deliberate.** `immutable` tells a browser the bytes behind a URL will never change, and browsers honour it so completely that not even a manual reload revalidates. On a stable filename — which is what the framework's own assets use (`/static/scripts/ti-framework.js`, the theme sheets) — the promise is false, and a deployed CSS or JS fix simply never reaches anyone who has already visited, for up to a year, with no way to tell them otherwise. Revalidating costs a conditional request per asset, answered with a `304` from the `ETag`/`Last-Modified` that `express.static` still attaches — headers, no body.
|
|
48
|
+
|
|
49
|
+
**Opt back into `immutable` once your filenames are content-addressed.** If your build emits `app.a1b2c3.css`, or your application appends a content hash to each asset URL, the promise becomes true and there is real value in making it:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"staticCache": { "maxAge": 31536000, "immutable": true }
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Fonts are the default exception because a released `.woff2` is an artifact rather than something edited in place, and its filename already carries the family, weight and style. If that is not how a given deployment manages its fonts, clear the list.
|
|
58
|
+
|
|
59
|
+
## Configure HTTPS for development
|
|
60
|
+
|
|
61
|
+
Use the `mkcert` tool to create a certificate for development.
|
|
62
|
+
|
|
63
|
+
Step 1: Install the tool:
|
|
64
|
+
```text
|
|
65
|
+
choco install mkcert
|
|
66
|
+
```
|
|
67
|
+
Step 2: Install certificate authority:
|
|
68
|
+
```text
|
|
69
|
+
mkcert -install
|
|
70
|
+
```
|
|
71
|
+
Step 3: Generate certificate files for localhost:
|
|
72
|
+
```text
|
|
73
|
+
mkcert localhost 127.0.0.1 ::1
|
|
74
74
|
```
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
const fs = require( "node:fs" );
|
|
2
|
-
const path = require( "node:path" );
|
|
3
|
-
|
|
4
|
-
function copyLibrary( source, destination ) {
|
|
5
|
-
try {
|
|
6
|
-
fs.mkdirSync( path.dirname( destination ), { recursive: true } );
|
|
7
|
-
fs.copyFileSync( source, destination );
|
|
8
|
-
console.log( `Copied ${ source } -> ${ destination }` );
|
|
9
|
-
} catch ( error ) {
|
|
10
|
-
console.error( `Failed to copy ${ source } -> ${ destination }:`, error.message );
|
|
11
|
-
process.exit( 1 );
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const staticDir = path.join( __dirname, "..", "static", "scripts", "lib" );
|
|
16
|
-
|
|
17
|
-
copyLibrary( require.resolve( "htmx.org/dist/htmx.min.js" ), path.join( staticDir, "htmx.min.js" ) );
|
|
18
|
-
copyLibrary( require.resolve( "@alpinejs/csp/dist/cdn.min.js" ), path.join( staticDir, "alpinejs-csp.min.js" ) );
|
|
1
|
+
const fs = require( "node:fs" );
|
|
2
|
+
const path = require( "node:path" );
|
|
3
|
+
|
|
4
|
+
function copyLibrary( source, destination ) {
|
|
5
|
+
try {
|
|
6
|
+
fs.mkdirSync( path.dirname( destination ), { recursive: true } );
|
|
7
|
+
fs.copyFileSync( source, destination );
|
|
8
|
+
console.log( `Copied ${ source } -> ${ destination }` );
|
|
9
|
+
} catch ( error ) {
|
|
10
|
+
console.error( `Failed to copy ${ source } -> ${ destination }:`, error.message );
|
|
11
|
+
process.exit( 1 );
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const staticDir = path.join( __dirname, "..", "static", "scripts", "lib" );
|
|
16
|
+
|
|
17
|
+
copyLibrary( require.resolve( "htmx.org/dist/htmx.min.js" ), path.join( staticDir, "htmx.min.js" ) );
|
|
18
|
+
copyLibrary( require.resolve( "@alpinejs/csp/dist/cdn.min.js" ), path.join( staticDir, "alpinejs-csp.min.js" ) );
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
{
|
|
2
|
-
"interface": {
|
|
3
|
-
"default": {
|
|
4
|
-
"login": {
|
|
5
|
-
"password": {
|
|
6
|
-
"en": "Password",
|
|
7
|
-
"bg": "Парола"
|
|
8
|
-
},
|
|
9
|
-
"sign-in": {
|
|
10
|
-
"en": "Sign In",
|
|
11
|
-
"bg": "Вход"
|
|
12
|
-
},
|
|
13
|
-
"sign-in-azure": {
|
|
14
|
-
"en": "Sign In - Azure",
|
|
15
|
-
"bg": "Вход с Azure"
|
|
16
|
-
},
|
|
17
|
-
"sign-in-google": {
|
|
18
|
-
"en": "Sign In - Google",
|
|
19
|
-
"bg": "Вход с Google"
|
|
20
|
-
},
|
|
21
|
-
"username": {
|
|
22
|
-
"en": "Username",
|
|
23
|
-
"bg": "Потребител"
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"interface": {
|
|
3
|
+
"default": {
|
|
4
|
+
"login": {
|
|
5
|
+
"password": {
|
|
6
|
+
"en": "Password",
|
|
7
|
+
"bg": "Парола"
|
|
8
|
+
},
|
|
9
|
+
"sign-in": {
|
|
10
|
+
"en": "Sign In",
|
|
11
|
+
"bg": "Вход"
|
|
12
|
+
},
|
|
13
|
+
"sign-in-azure": {
|
|
14
|
+
"en": "Sign In - Azure",
|
|
15
|
+
"bg": "Вход с Azure"
|
|
16
|
+
},
|
|
17
|
+
"sign-in-google": {
|
|
18
|
+
"en": "Sign In - Google",
|
|
19
|
+
"bg": "Вход с Google"
|
|
20
|
+
},
|
|
21
|
+
"username": {
|
|
22
|
+
"en": "Username",
|
|
23
|
+
"bg": "Потребител"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
28
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
{
|
|
2
|
-
"workspace": {
|
|
3
|
-
"root": "packages/web-framework",
|
|
4
|
-
"uuid": "a6c9b71a-41c3-448d-a680-7c1f6c5b42b0"
|
|
5
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"workspace": {
|
|
3
|
+
"root": "packages/web-framework",
|
|
4
|
+
"uuid": "a6c9b71a-41c3-448d-a680-7c1f6c5b42b0"
|
|
5
|
+
}
|
|
6
6
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
<div id="ti-notifications" x-data="tiComponentNotificationBar" class="ti-toast-stack" aria-live="polite" aria-atomic="false">
|
|
2
|
-
<template x-for="n in notifications" x-bind:key="n.id">
|
|
3
|
-
<div class="ti-toast" x-bind:class="n.tone || 'info'" role="alert">
|
|
4
|
-
<div class="ti-toast-bar" aria-hidden="true"></div>
|
|
5
|
-
<div class="ti-toast-icon" aria-hidden="true">
|
|
6
|
-
<span class="ti-icon check md" aria-hidden="true" x-show="n.tone === 'success'"></span>
|
|
7
|
-
<span class="ti-icon close md" aria-hidden="true" x-show="n.tone === 'danger'"></span>
|
|
8
|
-
<span class="ti-icon warning-triangle md" aria-hidden="true" x-show="n.tone === 'warn'"></span>
|
|
9
|
-
<span class="ti-icon info-circle md" aria-hidden="true" x-show="!n.tone || n.tone === 'info'"></span>
|
|
10
|
-
</div>
|
|
11
|
-
<div class="ti-toast-body">
|
|
12
|
-
<div class="ti-toast-title" x-text="n.title || ''"></div>
|
|
13
|
-
<div class="ti-toast-msg" x-text="n.message || ''"></div>
|
|
14
|
-
<div class="ti-toast-details" x-show="n.details" x-text="n.details || ''"></div>
|
|
15
|
-
</div>
|
|
16
|
-
<button class="ti-toast-close" @click="remove(n.id)" aria-label="Dismiss notification" type="button">
|
|
17
|
-
<span class="ti-icon close sm" aria-hidden="true"></span>
|
|
18
|
-
</button>
|
|
19
|
-
</div>
|
|
20
|
-
</template>
|
|
21
|
-
</div>
|
|
1
|
+
<div id="ti-notifications" x-data="tiComponentNotificationBar" class="ti-toast-stack" aria-live="polite" aria-atomic="false">
|
|
2
|
+
<template x-for="n in notifications" x-bind:key="n.id">
|
|
3
|
+
<div class="ti-toast" x-bind:class="n.tone || 'info'" role="alert">
|
|
4
|
+
<div class="ti-toast-bar" aria-hidden="true"></div>
|
|
5
|
+
<div class="ti-toast-icon" aria-hidden="true">
|
|
6
|
+
<span class="ti-icon check md" aria-hidden="true" x-show="n.tone === 'success'"></span>
|
|
7
|
+
<span class="ti-icon close md" aria-hidden="true" x-show="n.tone === 'danger'"></span>
|
|
8
|
+
<span class="ti-icon warning-triangle md" aria-hidden="true" x-show="n.tone === 'warn'"></span>
|
|
9
|
+
<span class="ti-icon info-circle md" aria-hidden="true" x-show="!n.tone || n.tone === 'info'"></span>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="ti-toast-body">
|
|
12
|
+
<div class="ti-toast-title" x-text="n.title || ''"></div>
|
|
13
|
+
<div class="ti-toast-msg" x-text="n.message || ''"></div>
|
|
14
|
+
<div class="ti-toast-details" x-show="n.details" x-text="n.details || ''"></div>
|
|
15
|
+
</div>
|
|
16
|
+
<button class="ti-toast-close" @click="remove(n.id)" aria-label="Dismiss notification" type="button">
|
|
17
|
+
<span class="ti-icon close sm" aria-hidden="true"></span>
|
|
18
|
+
</button>
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
21
|
+
</div>
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
<nav class="ti-sidebar" x-data="tiComponentSidebarNav" aria-label="Main navigation">
|
|
2
|
-
<button class="ti-sidebar-collapse-btn" @click="toggleCollapse()" x-bind:aria-label="collapsed ? 'Expand sidebar' : 'Collapse sidebar'" type="button">
|
|
3
|
-
<span class="ti-icon chevron-left xs" aria-hidden="true"></span>
|
|
4
|
-
</button>
|
|
5
|
-
|
|
6
|
-
<!-- Brand slot — override in app-specific frame -->
|
|
7
|
-
<div class="ti-sidebar-brand">
|
|
8
|
-
<div class="ti-sidebar-brand-mark">T</div>
|
|
9
|
-
<div class="ti-sidebar-brand-text-block">
|
|
10
|
-
<span class="ti-sidebar-brand-text">ti-engine</span>
|
|
11
|
-
</div>
|
|
12
|
-
</div>
|
|
13
|
-
|
|
14
|
-
<!-- Navigation slot — override in app-specific frame -->
|
|
15
|
-
<div class="ti-sidebar-nav">
|
|
16
|
-
<a href="/" class="ti-sidebar-item" data-tip="Dashboard" aria-label="Dashboard">
|
|
17
|
-
<span class="ti-sidebar-item-icon">
|
|
18
|
-
<span class="ti-icon dashboard md" aria-hidden="true"></span>
|
|
19
|
-
</span>
|
|
20
|
-
<span class="ti-sidebar-item-label">Dashboard</span>
|
|
21
|
-
</a>
|
|
22
|
-
</div>
|
|
23
|
-
|
|
24
|
-
<!-- Footer -->
|
|
25
|
-
<div class="ti-sidebar-foot">
|
|
26
|
-
<button class="ti-sidebar-item" @click="toggleTheme()" data-tip="Toggle theme" aria-label="Toggle theme" type="button">
|
|
27
|
-
<span class="ti-sidebar-item-icon">
|
|
28
|
-
<span class="ti-icon sun md" aria-hidden="true"></span>
|
|
29
|
-
</span>
|
|
30
|
-
<span class="ti-sidebar-item-label" x-text="theme === 'daylight' ? 'Glass Theme' : 'Daylight Theme'">Toggle Theme</span>
|
|
31
|
-
</button>
|
|
32
|
-
</div>
|
|
33
|
-
</nav>
|
|
1
|
+
<nav class="ti-sidebar" x-data="tiComponentSidebarNav" aria-label="Main navigation">
|
|
2
|
+
<button class="ti-sidebar-collapse-btn" @click="toggleCollapse()" x-bind:aria-label="collapsed ? 'Expand sidebar' : 'Collapse sidebar'" type="button">
|
|
3
|
+
<span class="ti-icon chevron-left xs" aria-hidden="true"></span>
|
|
4
|
+
</button>
|
|
5
|
+
|
|
6
|
+
<!-- Brand slot — override in app-specific frame -->
|
|
7
|
+
<div class="ti-sidebar-brand">
|
|
8
|
+
<div class="ti-sidebar-brand-mark">T</div>
|
|
9
|
+
<div class="ti-sidebar-brand-text-block">
|
|
10
|
+
<span class="ti-sidebar-brand-text">ti-engine</span>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<!-- Navigation slot — override in app-specific frame -->
|
|
15
|
+
<div class="ti-sidebar-nav">
|
|
16
|
+
<a href="/" class="ti-sidebar-item" data-tip="Dashboard" aria-label="Dashboard">
|
|
17
|
+
<span class="ti-sidebar-item-icon">
|
|
18
|
+
<span class="ti-icon dashboard md" aria-hidden="true"></span>
|
|
19
|
+
</span>
|
|
20
|
+
<span class="ti-sidebar-item-label">Dashboard</span>
|
|
21
|
+
</a>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<!-- Footer -->
|
|
25
|
+
<div class="ti-sidebar-foot">
|
|
26
|
+
<button class="ti-sidebar-item" @click="toggleTheme()" data-tip="Toggle theme" aria-label="Toggle theme" type="button">
|
|
27
|
+
<span class="ti-sidebar-item-icon">
|
|
28
|
+
<span class="ti-icon sun md" aria-hidden="true"></span>
|
|
29
|
+
</span>
|
|
30
|
+
<span class="ti-sidebar-item-label" x-text="theme === 'daylight' ? 'Glass Theme' : 'Daylight Theme'">Toggle Theme</span>
|
|
31
|
+
</button>
|
|
32
|
+
</div>
|
|
33
|
+
</nav>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
<div x-data="tiComponentTooltip()"
|
|
2
|
-
x-on:mouseover="handleEnter($event)"
|
|
3
|
-
x-on:mouseout="handleLeave($event)"
|
|
4
|
-
x-on:focusin="handleEnter($event)"
|
|
5
|
-
x-on:focusout="handleLeave($event)"
|
|
6
|
-
class="ti-tooltip">
|
|
7
|
-
<div x-show="isVisible"
|
|
8
|
-
x-text="text"
|
|
9
|
-
x-transition
|
|
10
|
-
class="ti-tooltip-bubble"></div>
|
|
1
|
+
<div x-data="tiComponentTooltip()"
|
|
2
|
+
x-on:mouseover="handleEnter($event)"
|
|
3
|
+
x-on:mouseout="handleLeave($event)"
|
|
4
|
+
x-on:focusin="handleEnter($event)"
|
|
5
|
+
x-on:focusout="handleLeave($event)"
|
|
6
|
+
class="ti-tooltip">
|
|
7
|
+
<div x-show="isVisible"
|
|
8
|
+
x-text="text"
|
|
9
|
+
x-transition
|
|
10
|
+
class="ti-tooltip-bubble"></div>
|
|
11
11
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
<header id="ti-topbar" class="ti-topbar" x-data="tiComponentTopbar">
|
|
2
|
-
<div id="ti-topbar-info">
|
|
3
|
-
<div class="ti-topbar-title" x-text="screenTitle"></div>
|
|
4
|
-
<div class="ti-topbar-sub" x-show="$store.tiApplication.topbarSubtitle" x-text="$store.tiApplication.topbarSubtitle"></div>
|
|
5
|
-
</div>
|
|
1
|
+
<header id="ti-topbar" class="ti-topbar" x-data="tiComponentTopbar">
|
|
2
|
+
<div id="ti-topbar-info">
|
|
3
|
+
<div class="ti-topbar-title" x-text="screenTitle"></div>
|
|
4
|
+
<div class="ti-topbar-sub" x-show="$store.tiApplication.topbarSubtitle" x-text="$store.tiApplication.topbarSubtitle"></div>
|
|
5
|
+
</div>
|
|
6
6
|
</header>
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
<div>
|
|
2
|
-
<span>Administration</span>
|
|
1
|
+
<div>
|
|
2
|
+
<span>Administration</span>
|
|
3
3
|
</div>
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
<div class="ti-application" x-data="tiApplication" x-bind:class="{ collapsed: collapsed }">
|
|
2
|
-
<!-- Sidebar -->
|
|
3
|
-
<ti-component-sidebar-placeholder></ti-component-sidebar-placeholder>
|
|
4
|
-
|
|
5
|
-
<!-- Content -->
|
|
6
|
-
<div id="ti-content-wrapper" class="ti-content">
|
|
7
|
-
<!-- Topbar -->
|
|
8
|
-
<ti-component-topbar-placeholder></ti-component-topbar-placeholder>
|
|
9
|
-
|
|
10
|
-
<!-- Application Screen -->
|
|
11
|
-
<div id="ti-content" class="ti-page-scrollable">
|
|
12
|
-
<ti-nested-frame-placeholder hx-get="/app/dashboard" hx-trigger="load" hx-target="this" hx-swap="outerHTML"
|
|
13
|
-
hx-push-url="true"></ti-nested-frame-placeholder>
|
|
14
|
-
</div>
|
|
15
|
-
</div>
|
|
16
|
-
|
|
17
|
-
<!-- Toast notification stack -->
|
|
18
|
-
<ti-component-notification-bar-placeholder></ti-component-notification-bar-placeholder>
|
|
1
|
+
<div class="ti-application" x-data="tiApplication" x-bind:class="{ collapsed: collapsed }">
|
|
2
|
+
<!-- Sidebar -->
|
|
3
|
+
<ti-component-sidebar-placeholder></ti-component-sidebar-placeholder>
|
|
4
|
+
|
|
5
|
+
<!-- Content -->
|
|
6
|
+
<div id="ti-content-wrapper" class="ti-content">
|
|
7
|
+
<!-- Topbar -->
|
|
8
|
+
<ti-component-topbar-placeholder></ti-component-topbar-placeholder>
|
|
9
|
+
|
|
10
|
+
<!-- Application Screen -->
|
|
11
|
+
<div id="ti-content" class="ti-page-scrollable">
|
|
12
|
+
<ti-nested-frame-placeholder hx-get="/app/dashboard" hx-trigger="load" hx-target="this" hx-swap="outerHTML"
|
|
13
|
+
hx-push-url="true"></ti-nested-frame-placeholder>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<!-- Toast notification stack -->
|
|
18
|
+
<ti-component-notification-bar-placeholder></ti-component-notification-bar-placeholder>
|
|
19
19
|
</div>
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
<div>
|
|
2
|
-
<span>Dashboard</span>
|
|
1
|
+
<div>
|
|
2
|
+
<span>Dashboard</span>
|
|
3
3
|
</div>
|