✨ stou theme for keycloak
This commit is contained in:
commit
931dac3449
16
.editorconfig
Normal file
16
.editorconfig
Normal file
@ -0,0 +1,16 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
57
.gitignore
vendored
Normal file
57
.gitignore
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
/.nx/cache
|
||||
/.nx/workspace-data
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# VS Code devcontainers
|
||||
.devcontainer
|
||||
/.yarn
|
||||
/.yarnrc.yml
|
||||
|
||||
/dist_keycloak
|
||||
/build
|
||||
/storybook-static
|
||||
|
||||
/stories/assets/fonts/
|
||||
/build_storybook/
|
||||
/storybook-static/
|
||||
|
||||
*storybook.log
|
||||
|
||||
.nx
|
4
.prettierignore
Normal file
4
.prettierignore
Normal file
@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
/dist/
|
||||
/dist_keycloak/
|
||||
/public/keycloak-dev-resources/
|
39
.storybook/main.ts
Normal file
39
.storybook/main.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import type { StorybookConfig } from '@storybook/angular';
|
||||
import { StorybookConfigVite } from '@storybook/builder-vite';
|
||||
import { UserConfig } from 'vite';
|
||||
|
||||
const config: StorybookConfig & StorybookConfigVite = {
|
||||
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
|
||||
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
|
||||
framework: {
|
||||
name: '@storybook/angular',
|
||||
options: {},
|
||||
},
|
||||
core: {
|
||||
builder: {
|
||||
name: '@storybook/builder-vite',
|
||||
options: {
|
||||
viteConfigPath: undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
staticDirs: ['../public'],
|
||||
async viteFinal(config: UserConfig, { configType }) {
|
||||
// Merge custom configuration into the default config
|
||||
const { mergeConfig } = await import('vite');
|
||||
const { default: angular } = await import('@analogjs/vite-plugin-angular');
|
||||
|
||||
return mergeConfig(config, {
|
||||
// Add dependencies to pre-optimization
|
||||
optimizeDeps: {
|
||||
include: ['@storybook/angular', '@storybook/angular/dist', '@angular/compiler', '@storybook/blocks', 'tslib'],
|
||||
},
|
||||
define: {
|
||||
'process.env': {},
|
||||
'process.env.NODE_ENV': JSON.stringify(configType === 'PRODUCTION' ? 'production' : 'development'),
|
||||
},
|
||||
plugins: [angular({ jit: true, tsconfig: './.storybook/tsconfig.json' })],
|
||||
} satisfies UserConfig);
|
||||
},
|
||||
};
|
||||
export default config;
|
14
.storybook/preview.ts
Normal file
14
.storybook/preview.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import type { Preview } from '@storybook/angular';
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/i,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default preview;
|
10
.storybook/tsconfig.doc.json
Normal file
10
.storybook/tsconfig.doc.json
Normal file
@ -0,0 +1,10 @@
|
||||
// This tsconfig is used by Compodoc to generate the documentation for the project.
|
||||
// If Compodoc is not used, this file can be deleted.
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
// Exclude all files that are not needed for documentation generation.
|
||||
"exclude": ["../src/test.ts", "../src/**/*.spec.ts", "../src/**/*.stories.ts"],
|
||||
// Please make sure to include all files from which Compodoc should generate documentation.
|
||||
"include": ["../src/**/*"],
|
||||
"files": ["./typings.d.ts"]
|
||||
}
|
21
.storybook/tsconfig.json
Normal file
21
.storybook/tsconfig.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"extends": "../tsconfig.app.json",
|
||||
"compilerOptions": {
|
||||
"types": [
|
||||
"node",
|
||||
],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"exclude": [
|
||||
"../src/test.ts",
|
||||
"../src/**/*.spec.ts"
|
||||
],
|
||||
"include": [
|
||||
"../src/**/*.stories.*",
|
||||
"./preview.ts"
|
||||
],
|
||||
"files": [
|
||||
"./typings.d.ts"
|
||||
]
|
||||
}
|
4
.storybook/typings.d.ts
vendored
Normal file
4
.storybook/typings.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
declare module '*.md' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 GitHub user u/luca-peruzzo
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
51
README.md
Normal file
51
README.md
Normal file
@ -0,0 +1,51 @@
|
||||
<p align="center">
|
||||
<i>🚀 <a href="https://keycloakify.dev">Angular + Vite Keycloakify</a> v11 starter 🚀</i>
|
||||
<br/>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
This starter is based on Vite and Angular. There is also [a Webpack based starter](https://github.com/keycloakify/keycloakify-starter-angular).
|
||||
|
||||
# Quick start
|
||||
|
||||
```bash
|
||||
git clone https://github.com/keycloakify/keycloakify-starter-angular-vite
|
||||
cd keycloakify-starter-angular-vite
|
||||
yarn install # Or use an other package manager, just be sure to delete the yarn.lock if you use another package manager.
|
||||
```
|
||||
|
||||
# Testing the theme locally
|
||||
|
||||
[Documentation](https://docs.keycloakify.dev/testing-your-theme)
|
||||
|
||||
# How to customize the theme
|
||||
|
||||
[Documentation](https://docs.keycloakify.dev/customization-strategies)
|
||||
|
||||
# Building the theme
|
||||
|
||||
You need to have [Maven](https://maven.apache.org/) installed to build the theme (Maven >= 3.1.1, Java >= 7).
|
||||
The `mvn` command must be in the $PATH.
|
||||
|
||||
- On macOS: `brew install maven`
|
||||
- On Debian/Ubuntu: `sudo apt-get install maven`
|
||||
- On Windows: `choco install openjdk` and `choco install maven` (Or download from [here](https://maven.apache.org/download.cgi))
|
||||
|
||||
```bash
|
||||
npm run build-keycloak-theme
|
||||
```
|
||||
|
||||
Note that by default Keycloakify generates multiple .jar files for different versions of Keycloak.
|
||||
You can customize this behavior, see documentation [here](https://docs.keycloakify.dev/targeting-specific-keycloak-versions).
|
||||
|
||||
# Initializing the account theme
|
||||
|
||||
```bash
|
||||
npx keycloakify initialize-account-theme
|
||||
```
|
||||
|
||||
# Initializing the email theme
|
||||
|
||||
```bash
|
||||
npx keycloakify initialize-email-theme
|
||||
```
|
61
angular.json
Normal file
61
angular.json
Normal file
@ -0,0 +1,61 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"keycloakify-starter-angular-vite": {
|
||||
"projectType": "application",
|
||||
"root": ".",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "kc",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@analogjs/platform:vite",
|
||||
"options": {
|
||||
"configFile": "vite.config.ts",
|
||||
"main": "src/main.ts",
|
||||
"outputPath": "dist",
|
||||
"tsConfig": "tsconfig.app.json"
|
||||
},
|
||||
"defaultConfiguration": "production",
|
||||
"configurations": {
|
||||
"development": {
|
||||
"mode": "development"
|
||||
},
|
||||
"production": {
|
||||
"sourcemap": false,
|
||||
"mode": "production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@analogjs/platform:vite-dev-server",
|
||||
"defaultConfiguration": "development",
|
||||
"options": {
|
||||
"buildTarget": "keycloakify-starter-angular-vite:build",
|
||||
"port": 5173
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildTarget": "keycloakify-starter-angular-vite:build:development",
|
||||
"hmr": true
|
||||
},
|
||||
"production": {
|
||||
"buildTarget": "keycloakify-starter-angular-vite:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-eslint/builder:lint",
|
||||
"options": {
|
||||
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"analytics": false,
|
||||
"schematicCollections": ["angular-eslint"]
|
||||
}
|
||||
}
|
78
eslint.config.js
Normal file
78
eslint.config.js
Normal file
@ -0,0 +1,78 @@
|
||||
// @ts-check
|
||||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import angular from 'angular-eslint';
|
||||
import prettier from 'eslint-plugin-prettier';
|
||||
import storybook from 'eslint-plugin-storybook';
|
||||
|
||||
|
||||
export default tseslint.config(
|
||||
{
|
||||
files: ['*.stories.@(ts|tsx|js|jsx|mjs|cjs)', '*.story.@(ts|tsx|js|jsx|mjs|cjs)'],
|
||||
plugins: {
|
||||
prettier,
|
||||
storybook
|
||||
},
|
||||
rules: {
|
||||
'import/no-anonymous-default-export': 'off',
|
||||
'prettier/prettier': ['error', {}],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
plugins: {
|
||||
prettier,
|
||||
},
|
||||
extends: [eslint.configs.recommended, ...tseslint.configs.recommended, ...tseslint.configs.stylistic, ...angular.configs.tsRecommended],
|
||||
processor: angular.processInlineTemplates,
|
||||
rules: {
|
||||
'@typescript-eslint/consistent-type-definitions': 'off',
|
||||
'@typescript-eslint/no-inferrable-types': 'off',
|
||||
'@typescript-eslint/no-namespace': 'off',
|
||||
'@typescript-eslint/no-empty-object-type': 'off',
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'kc',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'kc',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{},
|
||||
{
|
||||
usePrettierrc: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
plugins: {
|
||||
prettier,
|
||||
},
|
||||
extends: [...angular.configs.templateRecommended, ...angular.configs.templateAccessibility],
|
||||
rules: {
|
||||
'@angular-eslint/template/interactive-supports-focus': 'off',
|
||||
'@angular-eslint/template/click-events-have-key-events': 'off',
|
||||
'@angular-eslint/template/label-has-associated-control': 'off',
|
||||
'@angular-eslint/template/no-autofocus': 'off',
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{ parser: 'angular' },
|
||||
{
|
||||
usePrettierrc: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
);
|
23
index.html
Normal file
23
index.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<base href="/" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href="/favicon-32x32.png"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<kc-root></kc-root>
|
||||
<script
|
||||
type="module"
|
||||
src="/src/main.ts"
|
||||
></script>
|
||||
</body>
|
||||
</html>
|
83
package.json
Normal file
83
package.json
Normal file
@ -0,0 +1,83 @@
|
||||
{
|
||||
"name": "keycloakify-stou",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"description": "Angular + Vite Starter for Keycloakify 11",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/keycloakify/keycloakify-starter-angular-vite.git"
|
||||
},
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"dev": "ng serve",
|
||||
"start": "yarn dev",
|
||||
"build": "ng build",
|
||||
"build-keycloak-theme": "npm run build && keycloakify build",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint --fix",
|
||||
"storybook": "storybook dev --port 4400"
|
||||
},
|
||||
"author": "u/luca-peruzzo, u/kathari00, u/garronej",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"angular",
|
||||
"keycloakify",
|
||||
"keycloak",
|
||||
"vite",
|
||||
"analog"
|
||||
],
|
||||
"dependencies": {
|
||||
"@angular/animations": "^19.1.2",
|
||||
"@angular/common": "^19.1.2",
|
||||
"@angular/compiler": "^19.1.2",
|
||||
"@angular/core": "^19.1.2",
|
||||
"@angular/forms": "^19.1.2",
|
||||
"@angular/platform-browser": "^19.1.2",
|
||||
"@angular/platform-browser-dynamic": "^19.1.2",
|
||||
"@keycloakify/angular": "^0.2.17",
|
||||
"front-matter": "^4.0.2",
|
||||
"keycloakify": "^11.8.9",
|
||||
"prismjs": "^1.29.0",
|
||||
"rxjs": "~7.8.1",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@analogjs/platform": "^1.12.1",
|
||||
"@analogjs/vite-plugin-angular": "^1.12.1",
|
||||
"@analogjs/vitest-angular": "^1.12.1",
|
||||
"@angular-devkit/architect": "^0.1901.3",
|
||||
"@angular-devkit/build-angular": "^19.1.3",
|
||||
"@angular/build": "^19.1.3",
|
||||
"@angular/cli": "^19.1.3",
|
||||
"@angular/compiler-cli": "^19.1.2",
|
||||
"@nx/angular": "^20.3.2",
|
||||
"@nx/devkit": "^20.3.2",
|
||||
"@nx/vite": "^20.3.2",
|
||||
"@storybook/addon-essentials": "^8.5.0",
|
||||
"@storybook/addon-interactions": "^8.5.0",
|
||||
"@storybook/angular": "^8.5.0",
|
||||
"@storybook/blocks": "^8.5.0",
|
||||
"@storybook/builder-vite": "^8.5.0",
|
||||
"@storybook/test": "^8.5.0",
|
||||
"@types/node": "^22.10.7",
|
||||
"@typescript-eslint/types": "^8.21.0",
|
||||
"@typescript-eslint/utils": "^8.21.0",
|
||||
"angular-eslint": "^19.0.2",
|
||||
"eslint": "^9.18.0",
|
||||
"eslint-plugin-prettier": "^5.2.3",
|
||||
"eslint-plugin-storybook": "^0.11.2",
|
||||
"jsdom": "^26.0.0",
|
||||
"npm-check-updates": "^17.1.14",
|
||||
"prettier": "^3.4.2",
|
||||
"storybook": "^8.5.0",
|
||||
"typescript": "~5.6.3",
|
||||
"typescript-eslint": "^8.21.0",
|
||||
"vite": "^5.4.12",
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
"vitest": "^3.0.3",
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
}
|
25
prettier.config.js
Normal file
25
prettier.config.js
Normal file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @see https://prettier.io/docs/en/configuration.html
|
||||
* @type {import("prettier").Config}
|
||||
*/
|
||||
const config = {
|
||||
printWidth: 120,
|
||||
singleQuote: true,
|
||||
trailingComma: "all",
|
||||
tabWidth: 2,
|
||||
useTabs: false,
|
||||
semi: true,
|
||||
bracketSpacing: true,
|
||||
arrowParens: "always",
|
||||
singleAttributePerLine: true,
|
||||
overrides: [
|
||||
{
|
||||
files: "*.html",
|
||||
options: {
|
||||
parser: "angular"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default config;
|
0
public/.gitkeep
Normal file
0
public/.gitkeep
Normal file
BIN
public/favicon-32x32.png
Normal file
BIN
public/favicon-32x32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.6 KiB |
6
src/app.config.ts
Normal file
6
src/app.config.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import type { ApplicationConfig } from '@angular/core';
|
||||
import { provideExperimentalZonelessChangeDetection } from '@angular/core';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideExperimentalZonelessChangeDetection()],
|
||||
};
|
BIN
src/assets/font/Sarabun-Light.ttf
Normal file
BIN
src/assets/font/Sarabun-Light.ttf
Normal file
Binary file not shown.
BIN
src/assets/image/logo.jpg
Normal file
BIN
src/assets/image/logo.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 194 KiB |
86
src/kc.gen.ts
Normal file
86
src/kc.gen.ts
Normal file
@ -0,0 +1,86 @@
|
||||
// This file is auto-generated by keycloakify. Do not edit it manually.
|
||||
// Hash: 60fd6e41f85a18c23f8cb579b9476ceff3620dda085139def3e4c0137b4d47f1
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
// @ts-nocheck
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
|
||||
import type { ComponentRef, EnvironmentProviders, Type } from '@angular/core';
|
||||
|
||||
export type ThemeName = 'keycloakify-stou';
|
||||
|
||||
export const themeNames: ThemeName[] = ['keycloakify-stou'];
|
||||
|
||||
export type KcEnvName = never;
|
||||
|
||||
export const kcEnvNames: KcEnvName[] = [];
|
||||
|
||||
export const kcEnvDefaults: Record<KcEnvName, string> = {};
|
||||
|
||||
export type KcContext = import('./login/KcContext').KcContext;
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
kcContext?: KcContext;
|
||||
}
|
||||
}
|
||||
|
||||
type ApplicationRefLike = {
|
||||
components: ComponentRef<any>[];
|
||||
};
|
||||
|
||||
export async function bootstrapKcApplication(params: {
|
||||
kcContext: KcContext;
|
||||
bootstrapApplication: (params: {
|
||||
KcRootComponent: Type<unknown>;
|
||||
kcProvider: EnvironmentProviders;
|
||||
}) => Promise<ApplicationRefLike>;
|
||||
}) {
|
||||
const { kcContext, bootstrapApplication } = params;
|
||||
|
||||
switch (kcContext.themeType) {
|
||||
case 'login':
|
||||
{
|
||||
const [
|
||||
{ provideKeycloakifyAngular },
|
||||
{ getI18n },
|
||||
{
|
||||
PageComponent,
|
||||
TemplateComponent,
|
||||
doUseDefaultCss,
|
||||
classes,
|
||||
UserProfileFormFieldsComponent,
|
||||
doMakeUserConfirmPassword,
|
||||
},
|
||||
] = await Promise.all([
|
||||
import('@keycloakify/angular/login/providers/keycloakify-angular'),
|
||||
import('./login/i18n'),
|
||||
import('./login/KcPage').then(({ getKcPage }) => getKcPage(kcContext.pageId)),
|
||||
] as const);
|
||||
|
||||
const appRef = await bootstrapApplication({
|
||||
KcRootComponent: TemplateComponent,
|
||||
kcProvider: provideKeycloakifyAngular({
|
||||
kcContext,
|
||||
classes,
|
||||
getI18n,
|
||||
doUseDefaultCss,
|
||||
doMakeUserConfirmPassword,
|
||||
}),
|
||||
});
|
||||
|
||||
appRef.components.forEach((componentRef) => {
|
||||
// page must be defined first
|
||||
if ('page' in componentRef.instance) {
|
||||
componentRef.setInput('page', PageComponent);
|
||||
}
|
||||
if ('userProfileFormFields' in componentRef.instance) {
|
||||
componentRef.setInput('userProfileFormFields', UserProfileFormFieldsComponent);
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
14
src/login/KcContext.ts
Normal file
14
src/login/KcContext.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import type { ExtendKcContext } from 'keycloakify/login';
|
||||
import type { KcEnvName, ThemeName } from '../kc.gen';
|
||||
|
||||
export type KcContextExtension = {
|
||||
themeName: ThemeName;
|
||||
properties: Record<KcEnvName, string> & {};
|
||||
};
|
||||
|
||||
export type KcContextExtensionPerPage = {
|
||||
// Here you can declare additional properties on the KcContext
|
||||
// See: https://docs.keycloakify.dev/faq-and-help/some-values-you-need-are-missing-from-in-kccontext
|
||||
};
|
||||
|
||||
export type KcContext = ExtendKcContext<KcContextExtension, KcContextExtensionPerPage>;
|
24
src/login/KcPage.ts
Normal file
24
src/login/KcPage.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import "./main.css";
|
||||
import { getDefaultPageComponent, type KcPage } from '@keycloakify/angular/login';
|
||||
import { UserProfileFormFieldsComponent } from '@keycloakify/angular/login/components/user-profile-form-fields';
|
||||
import { TemplateComponent } from '@keycloakify/angular/login/template';
|
||||
import type { ClassKey } from 'keycloakify/login';
|
||||
import type { KcContext } from './KcContext';
|
||||
|
||||
export const classes = {} satisfies Partial<Record<ClassKey, string>>;
|
||||
export const doUseDefaultCss = true;
|
||||
export const doMakeUserConfirmPassword = true;
|
||||
|
||||
export async function getKcPage(pageId: KcContext['pageId']): Promise<KcPage> {
|
||||
switch (pageId) {
|
||||
default:
|
||||
return {
|
||||
PageComponent: await getDefaultPageComponent(pageId),
|
||||
TemplateComponent,
|
||||
UserProfileFormFieldsComponent,
|
||||
doMakeUserConfirmPassword,
|
||||
doUseDefaultCss,
|
||||
classes,
|
||||
};
|
||||
}
|
||||
}
|
70
src/login/KcPageStory.ts
Normal file
70
src/login/KcPageStory.ts
Normal file
@ -0,0 +1,70 @@
|
||||
/* eslint-disable @angular-eslint/component-class-suffix */
|
||||
import { Component, inject, OnInit, Type } from '@angular/core';
|
||||
import { provideKeycloakifyAngular } from '@keycloakify/angular/login/providers/keycloakify-angular';
|
||||
import { TemplateComponent } from '@keycloakify/angular/login/template';
|
||||
import { getKcPage } from './KcPage';
|
||||
import { getI18n } from './i18n';
|
||||
import { KC_LOGIN_CONTEXT } from '@keycloakify/angular/login/tokens/kc-context';
|
||||
import { createGetKcContextMock } from 'keycloakify/login/KcContext';
|
||||
import { kcEnvDefaults, themeNames } from '../kc.gen';
|
||||
import type { KcContextExtension, KcContextExtensionPerPage } from './KcContext';
|
||||
import { classes, doMakeUserConfirmPassword, doUseDefaultCss } from './KcPage';
|
||||
const kcContextExtension: KcContextExtension = {
|
||||
themeName: themeNames[0],
|
||||
properties: {
|
||||
...kcEnvDefaults,
|
||||
},
|
||||
};
|
||||
const kcContextExtensionPerPage: KcContextExtensionPerPage = {};
|
||||
|
||||
export const { getKcContextMock } = createGetKcContextMock({
|
||||
kcContextExtension,
|
||||
kcContextExtensionPerPage,
|
||||
overrides: {},
|
||||
overridesPerPage: {},
|
||||
});
|
||||
|
||||
type StoryContextLike = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
globals: Record<string, any>;
|
||||
};
|
||||
|
||||
export const decorators = (_: unknown, context: StoryContextLike) => ({
|
||||
applicationConfig: {
|
||||
providers: [
|
||||
provideKeycloakifyAngular({
|
||||
doMakeUserConfirmPassword: doMakeUserConfirmPassword,
|
||||
doUseDefaultCss: doUseDefaultCss,
|
||||
classes: classes,
|
||||
kcContext: getKcContextMock({
|
||||
pageId: context.globals['pageId'],
|
||||
overrides: context.globals['kcContext'],
|
||||
}),
|
||||
getI18n: getI18n,
|
||||
}),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
@Component({
|
||||
selector: 'kc-page-story',
|
||||
template: `@if (pageComponent) {
|
||||
<kc-root
|
||||
[page]="pageComponent"
|
||||
[userProfileFormFields]="userProfileFormFieldsComponent"
|
||||
></kc-root>
|
||||
}`,
|
||||
standalone: true,
|
||||
imports: [TemplateComponent],
|
||||
})
|
||||
export class KcPageStory implements OnInit {
|
||||
pageComponent: Type<unknown> | undefined;
|
||||
kcContext = inject(KC_LOGIN_CONTEXT);
|
||||
userProfileFormFieldsComponent: Type<unknown> | undefined;
|
||||
ngOnInit() {
|
||||
getKcPage(this.kcContext.pageId).then((kcPage) => {
|
||||
this.pageComponent = kcPage.PageComponent;
|
||||
this.userProfileFormFieldsComponent = kcPage.UserProfileFormFieldsComponent;
|
||||
});
|
||||
}
|
||||
}
|
10
src/login/i18n.ts
Normal file
10
src/login/i18n.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { i18nBuilder } from '@keycloakify/angular/login';
|
||||
import type { ThemeName } from '../kc.gen';
|
||||
|
||||
/** @see: https://docs.keycloakify.dev/features/i18n */
|
||||
const { getI18n, ofTypeI18n } = i18nBuilder.withThemeName<ThemeName>().build();
|
||||
|
||||
type I18n = typeof ofTypeI18n;
|
||||
|
||||
export { getI18n, type I18n };
|
159
src/login/main.css
Normal file
159
src/login/main.css
Normal file
@ -0,0 +1,159 @@
|
||||
@font-face{
|
||||
font-family: "Sarabun-Light";
|
||||
src: url(../assets/font/Sarabun-Light.ttf);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-family);
|
||||
}
|
||||
|
||||
.login-pf a {
|
||||
color: #1e3a8a;
|
||||
}
|
||||
.login-pf a:hover {
|
||||
text-decoration: underline;
|
||||
color: #1e3a8a;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
color: #1e3a8a;
|
||||
}
|
||||
|
||||
body.kcBodyClass {
|
||||
background: none;
|
||||
}
|
||||
#kc-header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
#kc-header-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding: 20px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
background-image: url(../assets/image/logo.jpg); /* Replace with your logo file */
|
||||
background-repeat: no-repeat;
|
||||
background-size: auto 150px; /* Maintain aspect ratio, height = 100px */
|
||||
background-position: center top; /* Position logo above text */
|
||||
padding-top: 160px; /* Adjust space so text is not covered */
|
||||
letter-spacing: normal;
|
||||
font-family: inherit;
|
||||
font-size: 20px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
|
||||
.kcLabelClass.pf-c-form__label.pf-c-form__label-text {
|
||||
color: #333;
|
||||
font-size: calc(var(--textFontSize) + 0.2em);
|
||||
}
|
||||
|
||||
.login-pf-page .login-pf-header h1 {
|
||||
font-weight: bold;
|
||||
font-size: calc(var(--textFontSize) + 0.5em);
|
||||
}
|
||||
|
||||
h1#kc-page-title {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
#kc-header-wrapper {
|
||||
font-size: 20px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Login form styling */
|
||||
/* .kcFormCardClass.card-pf {
|
||||
background-color: white;
|
||||
padding: 10px 20px 20px 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 350px;
|
||||
height: auto;
|
||||
} */
|
||||
|
||||
div.kcFormGroupClass.form-group {
|
||||
margin-bottom: 1.5em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input.kcInputClass.pf-c-form-control {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border-radius: 0;
|
||||
font-size: var(--textFontSize);
|
||||
height: auto;
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
line-height: inherit;
|
||||
font-feature-settings: var(--font-feature-settings, normal);
|
||||
color: #4b5563;
|
||||
background: #ffffff;
|
||||
border: 1px solid #d1d5db;
|
||||
transition: background-color 0.2s, color 0.2s, border-color 0.2s, box-shadow 0.2s;
|
||||
appearance: none;
|
||||
font-weight: normal;
|
||||
outline: none;
|
||||
}
|
||||
input.kcInputClass.pf-c-form-control:hover {
|
||||
border-color: #3B82F6;
|
||||
}
|
||||
input.kcInputClass.pf-c-form-control:focus {
|
||||
border-color: #3B82F6;
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.5);
|
||||
}
|
||||
.p-inputtext {
|
||||
font-size: var(--textFontSize);
|
||||
padding: 0.25rem 0.25rem 0.25rem 0.35rem;
|
||||
height: auto;
|
||||
}
|
||||
.kcInputGroup.pf-c-input-group {
|
||||
position: relative;
|
||||
}
|
||||
/* merge with input.kcInputClass.pf-c-form-control */
|
||||
.kcFormPasswordVisibilityButtonClass {
|
||||
position: absolute;
|
||||
margin-bottom: 1.5em;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
border-color: rgba(230, 230, 230, 0.5);
|
||||
}
|
||||
button.kcFormPasswordVisibilityButtonClass.pf-c-button.pf-m-control {
|
||||
--pf-c-button--BorderRadius: none;
|
||||
--pf-c-button--disabled--BackgroundColor: none;
|
||||
--pf-c-button--after--BorderWidth: none;
|
||||
--pf-c-button--after--BorderColor: none;
|
||||
/* color: transparent; */
|
||||
background-color: transparent;
|
||||
/* made it on top even the input is focused */
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.pf-c-button.pf-m-primary {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background-color: #1e3a8a;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.pf-c-button.pf-m-primary:hover {
|
||||
background-color: #3b5998;
|
||||
}
|
||||
|
||||
/* custom variable */
|
||||
:root {
|
||||
--textFontSize: 1em;
|
||||
--font-family: 'Sarabun-Light', sans-serif;
|
||||
--font-feature-settings: "cv02" "cv03" "cv04" "cv11";
|
||||
}
|
306
src/login/pages/login/login.stories.ts
Normal file
306
src/login/pages/login/login.stories.ts
Normal file
@ -0,0 +1,306 @@
|
||||
import { Meta, StoryObj } from '@storybook/angular';
|
||||
import { decorators, KcPageStory } from '../../KcPageStory';
|
||||
|
||||
const meta: Meta<KcPageStory> = {
|
||||
title: 'login/login.ftl',
|
||||
component: KcPageStory,
|
||||
decorators: decorators,
|
||||
globals: {
|
||||
pageId: 'login.ftl',
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<KcPageStory>;
|
||||
|
||||
export const Default: Story = {
|
||||
globals: {
|
||||
kcContext: {
|
||||
locale: {
|
||||
currentLanguageTag: "th"
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
export const WithInvalidCredential: Story = {
|
||||
globals: {
|
||||
kcContext: {
|
||||
locale: {
|
||||
currentLanguageTag: "th"
|
||||
},
|
||||
login: {
|
||||
username: 'johndoe',
|
||||
},
|
||||
messagesPerField: {
|
||||
existsError: (fieldName: string, ...otherFieldNames: string[]) => {
|
||||
const fieldNames = [fieldName, ...otherFieldNames];
|
||||
return fieldNames.includes('username') || fieldNames.includes('password');
|
||||
},
|
||||
get: (fieldName: string) => {
|
||||
return fieldName === 'username' || fieldName === 'password' ? 'Invalid username or password.' : '';
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithoutRegistration: Story = {
|
||||
globals: {
|
||||
kcContext: {
|
||||
locale: {
|
||||
currentLanguageTag: "th"
|
||||
},
|
||||
realm: { registrationAllowed: false },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithoutRememberMe: Story = {
|
||||
globals: {
|
||||
kcContext: {
|
||||
locale: {
|
||||
currentLanguageTag: "th"
|
||||
},
|
||||
realm: { rememberMe: false },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithoutPasswordReset: Story = {
|
||||
globals: {
|
||||
kcContext: {
|
||||
locale: {
|
||||
currentLanguageTag: "th"
|
||||
},
|
||||
realm: { resetPasswordAllowed: false },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithEmailAsUsername: Story = {
|
||||
globals: {
|
||||
kcContext: {
|
||||
locale: {
|
||||
currentLanguageTag: "th"
|
||||
},
|
||||
realm: { loginWithEmailAllowed: false },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithPresetUsername: Story = {
|
||||
globals: {
|
||||
kcContext: {
|
||||
locale: {
|
||||
currentLanguageTag: "th"
|
||||
},
|
||||
login: { username: 'max.mustermann@mail.com' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithImmutablePresetUsername: Story = {
|
||||
globals: {
|
||||
kcContext: {
|
||||
locale: {
|
||||
currentLanguageTag: "th"
|
||||
},
|
||||
auth: {
|
||||
attemptedUsername: 'max.mustermann@mail.com',
|
||||
showUsername: true,
|
||||
},
|
||||
usernameHidden: true,
|
||||
message: {
|
||||
type: 'info',
|
||||
summary: 'Please re-authenticate to continue',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithSocialProviders: Story = {
|
||||
globals: {
|
||||
kcContext: {
|
||||
locale: {
|
||||
currentLanguageTag: "th"
|
||||
},
|
||||
social: {
|
||||
displayInfo: true,
|
||||
providers: [
|
||||
{
|
||||
loginUrl: 'google',
|
||||
alias: 'google',
|
||||
providerId: 'google',
|
||||
displayName: 'Google',
|
||||
iconClasses: 'fa fa-google',
|
||||
},
|
||||
{
|
||||
loginUrl: 'microsoft',
|
||||
alias: 'microsoft',
|
||||
providerId: 'microsoft',
|
||||
displayName: 'Microsoft',
|
||||
iconClasses: 'fa fa-windows',
|
||||
},
|
||||
{
|
||||
loginUrl: 'facebook',
|
||||
alias: 'facebook',
|
||||
providerId: 'facebook',
|
||||
displayName: 'Facebook',
|
||||
iconClasses: 'fa fa-facebook',
|
||||
},
|
||||
{
|
||||
loginUrl: 'github',
|
||||
alias: 'github',
|
||||
providerId: 'github',
|
||||
displayName: 'Github',
|
||||
iconClasses: 'fa fa-github',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithoutPasswordField: Story = {
|
||||
globals: {
|
||||
kcContext: {
|
||||
locale: {
|
||||
currentLanguageTag: "th"
|
||||
},
|
||||
realm: { password: false },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithErrorMessage: Story = {
|
||||
globals: {
|
||||
kcContext: {
|
||||
locale: {
|
||||
currentLanguageTag: "th"
|
||||
},
|
||||
message: {
|
||||
summary:
|
||||
'The time allotted for the connection has elapsed.<br/>The login process will restart from the beginning.',
|
||||
type: 'error',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithOneSocialProvider: Story = {
|
||||
globals: {
|
||||
kcContext: {
|
||||
locale: {
|
||||
currentLanguageTag: "th"
|
||||
},
|
||||
social: {
|
||||
displayInfo: true,
|
||||
providers: [
|
||||
{
|
||||
loginUrl: 'google',
|
||||
alias: 'google',
|
||||
providerId: 'google',
|
||||
displayName: 'Google',
|
||||
iconClasses: 'fa fa-google',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithTwoSocialProviders: Story = {
|
||||
globals: {
|
||||
kcContext: {
|
||||
locale: {
|
||||
currentLanguageTag: "th"
|
||||
},
|
||||
social: {
|
||||
displayInfo: true,
|
||||
providers: [
|
||||
{
|
||||
loginUrl: 'google',
|
||||
alias: 'google',
|
||||
providerId: 'google',
|
||||
displayName: 'Google',
|
||||
iconClasses: 'fa fa-google',
|
||||
},
|
||||
{
|
||||
loginUrl: 'microsoft',
|
||||
alias: 'microsoft',
|
||||
providerId: 'microsoft',
|
||||
displayName: 'Microsoft',
|
||||
iconClasses: 'fa fa-windows',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithMoreThanTwoSocialProviders: Story = {
|
||||
globals: {
|
||||
kcContext: {
|
||||
locale: {
|
||||
currentLanguageTag: "th"
|
||||
},
|
||||
social: {
|
||||
displayInfo: true,
|
||||
providers: [
|
||||
{
|
||||
loginUrl: 'google',
|
||||
alias: 'google',
|
||||
providerId: 'google',
|
||||
displayName: 'Google',
|
||||
iconClasses: 'fa fa-google',
|
||||
},
|
||||
{
|
||||
loginUrl: 'microsoft',
|
||||
alias: 'microsoft',
|
||||
providerId: 'microsoft',
|
||||
displayName: 'Microsoft',
|
||||
iconClasses: 'fa fa-windows',
|
||||
},
|
||||
{
|
||||
loginUrl: 'facebook',
|
||||
alias: 'facebook',
|
||||
providerId: 'facebook',
|
||||
displayName: 'Facebook',
|
||||
iconClasses: 'fa fa-facebook',
|
||||
},
|
||||
{
|
||||
loginUrl: 'twitter',
|
||||
alias: 'twitter',
|
||||
providerId: 'twitter',
|
||||
displayName: 'Twitter',
|
||||
iconClasses: 'fa fa-twitter',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const WithSocialProvidersAndWithoutRememberMe: Story = {
|
||||
globals: {
|
||||
kcContext: {
|
||||
locale: {
|
||||
currentLanguageTag: "th"
|
||||
},
|
||||
social: {
|
||||
displayInfo: true,
|
||||
providers: [
|
||||
{
|
||||
loginUrl: 'google',
|
||||
alias: 'google',
|
||||
providerId: 'google',
|
||||
displayName: 'Google',
|
||||
iconClasses: 'fa fa-google',
|
||||
},
|
||||
],
|
||||
},
|
||||
realm: { rememberMe: false },
|
||||
},
|
||||
},
|
||||
};
|
34
src/main.ts
Normal file
34
src/main.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { bootstrapKcApplication } from './kc.gen';
|
||||
import { appConfig } from './app.config';
|
||||
|
||||
// The following block can be uncommented to test a specific page with `yarn dev`
|
||||
// Don't forget to comment back or your bundle size will increase
|
||||
/*
|
||||
import { getKcContextMock } from './login/KcPageStory';
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
window.kcContext = getKcContextMock({
|
||||
pageId: 'login.ftl',
|
||||
overrides: {},
|
||||
});
|
||||
}
|
||||
*/
|
||||
(async () => {
|
||||
if (window.kcContext === undefined) {
|
||||
const { NoContextComponent } = await import('./no-context.component');
|
||||
|
||||
bootstrapApplication(NoContextComponent, appConfig);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bootstrapKcApplication({
|
||||
kcContext: window.kcContext,
|
||||
bootstrapApplication: ({ KcRootComponent, kcProvider }) =>
|
||||
bootstrapApplication(KcRootComponent, {
|
||||
...appConfig,
|
||||
providers: [...appConfig.providers, kcProvider],
|
||||
}),
|
||||
});
|
||||
})();
|
8
src/no-context.component.ts
Normal file
8
src/no-context.component.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'kc-root',
|
||||
standalone: true,
|
||||
template: `<h1>No Keycloak Context</h1>`,
|
||||
})
|
||||
export class NoContextComponent {}
|
6
src/test-setup.ts
Normal file
6
src/test-setup.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import '@analogjs/vitest-angular/setup-snapshots';
|
||||
|
||||
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
|
||||
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
|
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
11
tsconfig.app.json
Normal file
11
tsconfig.app.json
Normal file
@ -0,0 +1,11 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"files": [],
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
32
tsconfig.json
Normal file
32
tsconfig.json
Normal file
@ -0,0 +1,32 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist/out-tsc",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"downlevelIteration": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"isolatedModules": true,
|
||||
"importHelpers": true,
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"lib": ["ES2022", "dom"],
|
||||
"useDefineForClassFields": false,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
10
tsconfig.spec.json
Normal file
10
tsconfig.spec.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"target": "es2016",
|
||||
"types": ["vitest/globals", "node"]
|
||||
},
|
||||
"files": ["src/test-setup.ts"],
|
||||
"include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
|
||||
}
|
31
vite.config.ts
Normal file
31
vite.config.ts
Normal file
@ -0,0 +1,31 @@
|
||||
/// <reference types="vitest" />
|
||||
|
||||
import { defineConfig } from 'vite';
|
||||
import angular from '@analogjs/vite-plugin-angular';
|
||||
import { keycloakify } from 'keycloakify/vite-plugin';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(({ mode }) => ({
|
||||
build: {
|
||||
target: ['es2022'],
|
||||
},
|
||||
resolve: {
|
||||
mainFields: ['module'],
|
||||
},
|
||||
plugins: [
|
||||
angular(),
|
||||
keycloakify({
|
||||
accountThemeImplementation: 'none',
|
||||
}),
|
||||
],
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
setupFiles: ['src/test-setup.ts'],
|
||||
include: ['**/*.spec.ts'],
|
||||
reporters: ['default'],
|
||||
},
|
||||
define: {
|
||||
'import.meta.vitest': mode !== 'production',
|
||||
},
|
||||
}));
|
Loading…
x
Reference in New Issue
Block a user