diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..33c3bd7
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,41 @@
+{
+ "ignorePatterns": ["!**/*"],
+ "overrides": [
+ {
+ "files": ["*.ts"],
+ "extends": [
+ "plugin:@angular-eslint/recommended",
+ "plugin:@angular-eslint/template/process-inline-templates",
+ "plugin:prettier/recommended"
+ ],
+ "rules": {
+ "@angular-eslint/directive-selector": [
+ "error",
+ {
+ "type": "attribute",
+ "prefix": "gt",
+ "style": "camelCase"
+ }
+ ],
+ "@angular-eslint/component-selector": [
+ "error",
+ {
+ "type": "element",
+ "prefix": "angular-generic",
+ "style": "kebab-case"
+ }
+ ],
+ "prettier/prettier": "error"
+ }
+ },
+ {
+ "files": ["*.html"],
+ "rules": {}
+ }
+ ],
+ "plugins": [
+ "@angular-eslint",
+ "prettier",
+ "html"
+ ]
+}
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 7a5b9a8..f6d5265 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -15,7 +15,7 @@ jobs:
id: branch
- name: Install dependencies
- run: npm ci
+ run: npm ci --legacy-peer-deps
- name: Build library
run: npm run build
diff --git a/.prettierrc b/.prettierrc
index 8d3dfb0..4452522 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,8 +1,15 @@
{
- "printWidth": 120,
- "singleQuote": true,
- "useTabs": false,
- "tabWidth": 2,
- "semi": true,
- "bracketSpacing": true
+ "extends": ["prettier"],
+ "plugins": ["prettier"],
+ "rules": {
+ "prettier/prettier": "error",
+ "arrow-body-style": "off",
+ "prefer-arrow-callback": "off",
+ "printWidth": 120,
+ "singleQuote": true,
+ "useTabs": false,
+ "tabWidth": 2,
+ "semi": true,
+ "bracketSpacing": true
+ }
}
diff --git a/.storybook/assets/JetBrainsMono-Bold.woff2 b/.storybook/assets/JetBrainsMono-Bold.woff2
new file mode 100644
index 0000000..023512c
Binary files /dev/null and b/.storybook/assets/JetBrainsMono-Bold.woff2 differ
diff --git a/.storybook/assets/JetBrainsMono-BoldItalic.woff2 b/.storybook/assets/JetBrainsMono-BoldItalic.woff2
new file mode 100644
index 0000000..f3e87a3
Binary files /dev/null and b/.storybook/assets/JetBrainsMono-BoldItalic.woff2 differ
diff --git a/.storybook/assets/JetBrainsMono-ExtraBold.woff2 b/.storybook/assets/JetBrainsMono-ExtraBold.woff2
new file mode 100644
index 0000000..a8b78a9
Binary files /dev/null and b/.storybook/assets/JetBrainsMono-ExtraBold.woff2 differ
diff --git a/.storybook/assets/JetBrainsMono-ExtraBoldItalic.woff2 b/.storybook/assets/JetBrainsMono-ExtraBoldItalic.woff2
new file mode 100644
index 0000000..b54a2d5
Binary files /dev/null and b/.storybook/assets/JetBrainsMono-ExtraBoldItalic.woff2 differ
diff --git a/.storybook/assets/JetBrainsMono-ExtraLight.woff2 b/.storybook/assets/JetBrainsMono-ExtraLight.woff2
new file mode 100644
index 0000000..edd6a68
Binary files /dev/null and b/.storybook/assets/JetBrainsMono-ExtraLight.woff2 differ
diff --git a/.storybook/assets/JetBrainsMono-ExtraLightItalic.woff2 b/.storybook/assets/JetBrainsMono-ExtraLightItalic.woff2
new file mode 100644
index 0000000..2a02a18
Binary files /dev/null and b/.storybook/assets/JetBrainsMono-ExtraLightItalic.woff2 differ
diff --git a/.storybook/assets/JetBrainsMono-Italic.woff2 b/.storybook/assets/JetBrainsMono-Italic.woff2
new file mode 100644
index 0000000..e8eeb4b
Binary files /dev/null and b/.storybook/assets/JetBrainsMono-Italic.woff2 differ
diff --git a/.storybook/assets/JetBrainsMono-Light.woff2 b/.storybook/assets/JetBrainsMono-Light.woff2
new file mode 100644
index 0000000..459bacf
Binary files /dev/null and b/.storybook/assets/JetBrainsMono-Light.woff2 differ
diff --git a/.storybook/assets/JetBrainsMono-LightItalic.woff2 b/.storybook/assets/JetBrainsMono-LightItalic.woff2
new file mode 100644
index 0000000..352f5d9
Binary files /dev/null and b/.storybook/assets/JetBrainsMono-LightItalic.woff2 differ
diff --git a/.storybook/assets/JetBrainsMono-Medium.woff2 b/.storybook/assets/JetBrainsMono-Medium.woff2
new file mode 100644
index 0000000..484c9e6
Binary files /dev/null and b/.storybook/assets/JetBrainsMono-Medium.woff2 differ
diff --git a/.storybook/assets/JetBrainsMono-MediumItalic.woff2 b/.storybook/assets/JetBrainsMono-MediumItalic.woff2
new file mode 100644
index 0000000..e127994
Binary files /dev/null and b/.storybook/assets/JetBrainsMono-MediumItalic.woff2 differ
diff --git a/.storybook/assets/JetBrainsMono-Regular.woff2 b/.storybook/assets/JetBrainsMono-Regular.woff2
new file mode 100644
index 0000000..8c862e3
Binary files /dev/null and b/.storybook/assets/JetBrainsMono-Regular.woff2 differ
diff --git a/.storybook/assets/JetBrainsMono-SemiBold.woff2 b/.storybook/assets/JetBrainsMono-SemiBold.woff2
new file mode 100644
index 0000000..fce8cd8
Binary files /dev/null and b/.storybook/assets/JetBrainsMono-SemiBold.woff2 differ
diff --git a/.storybook/assets/JetBrainsMono-SemiBoldItalic.woff2 b/.storybook/assets/JetBrainsMono-SemiBoldItalic.woff2
new file mode 100644
index 0000000..a14851f
Binary files /dev/null and b/.storybook/assets/JetBrainsMono-SemiBoldItalic.woff2 differ
diff --git a/.storybook/assets/JetBrainsMono-Thin.woff2 b/.storybook/assets/JetBrainsMono-Thin.woff2
new file mode 100644
index 0000000..7c61278
Binary files /dev/null and b/.storybook/assets/JetBrainsMono-Thin.woff2 differ
diff --git a/.storybook/assets/JetBrainsMono-ThinItalic.woff2 b/.storybook/assets/JetBrainsMono-ThinItalic.woff2
new file mode 100644
index 0000000..0676ba8
Binary files /dev/null and b/.storybook/assets/JetBrainsMono-ThinItalic.woff2 differ
diff --git a/.storybook/assets/THICCCBOI-Black.woff2 b/.storybook/assets/THICCCBOI-Black.woff2
new file mode 100644
index 0000000..77c23a8
Binary files /dev/null and b/.storybook/assets/THICCCBOI-Black.woff2 differ
diff --git a/.storybook/assets/THICCCBOI-Bold.woff2 b/.storybook/assets/THICCCBOI-Bold.woff2
new file mode 100644
index 0000000..af3d442
Binary files /dev/null and b/.storybook/assets/THICCCBOI-Bold.woff2 differ
diff --git a/.storybook/assets/THICCCBOI-ExtraBold.woff2 b/.storybook/assets/THICCCBOI-ExtraBold.woff2
new file mode 100644
index 0000000..b43827c
Binary files /dev/null and b/.storybook/assets/THICCCBOI-ExtraBold.woff2 differ
diff --git a/.storybook/assets/THICCCBOI-Light.woff2 b/.storybook/assets/THICCCBOI-Light.woff2
new file mode 100644
index 0000000..e8b3568
Binary files /dev/null and b/.storybook/assets/THICCCBOI-Light.woff2 differ
diff --git a/.storybook/assets/THICCCBOI-Medium.woff2 b/.storybook/assets/THICCCBOI-Medium.woff2
new file mode 100644
index 0000000..5476e3e
Binary files /dev/null and b/.storybook/assets/THICCCBOI-Medium.woff2 differ
diff --git a/.storybook/assets/THICCCBOI-Regular.woff2 b/.storybook/assets/THICCCBOI-Regular.woff2
new file mode 100644
index 0000000..eac9d25
Binary files /dev/null and b/.storybook/assets/THICCCBOI-Regular.woff2 differ
diff --git a/.storybook/assets/THICCCBOI-SemiBold.woff2 b/.storybook/assets/THICCCBOI-SemiBold.woff2
new file mode 100644
index 0000000..b40921a
Binary files /dev/null and b/.storybook/assets/THICCCBOI-SemiBold.woff2 differ
diff --git a/.storybook/assets/THICCCBOI-ThicccAF.woff2 b/.storybook/assets/THICCCBOI-ThicccAF.woff2
new file mode 100644
index 0000000..aa95763
Binary files /dev/null and b/.storybook/assets/THICCCBOI-ThicccAF.woff2 differ
diff --git a/.storybook/assets/THICCCBOI-Thin.woff2 b/.storybook/assets/THICCCBOI-Thin.woff2
new file mode 100644
index 0000000..5786669
Binary files /dev/null and b/.storybook/assets/THICCCBOI-Thin.woff2 differ
diff --git a/.storybook/assets/logo.svg b/.storybook/assets/logo.svg
new file mode 100644
index 0000000..439f4cc
--- /dev/null
+++ b/.storybook/assets/logo.svg
@@ -0,0 +1,3 @@
+
diff --git a/.storybook/main.js b/.storybook/main.js
index 89e0074..e23d631 100644
--- a/.storybook/main.js
+++ b/.storybook/main.js
@@ -1,11 +1,17 @@
module.exports = {
stories: [
- '../projects/docs/src/stories/**/*.stories.mdx',
- '../projects/docs/src/stories/**/*.stories.@(js|jsx|ts|tsx)',
+ "../projects/docs/src/stories/**/*.stories.mdx",
+ "../projects/docs/src/stories/**/*.stories.@(js|jsx|ts|tsx)",
],
- addons: ['@storybook/addon-docs', '@storybook/addon-links', '@storybook/addon-essentials'],
- framework: '@storybook/angular',
+ addons: [
+ "@storybook/addon-docs",
+ "@storybook/addon-links",
+ "@storybook/addon-essentials",
+ "storybook-addon-themes",
+ ],
+ framework: "@storybook/angular",
+ staticDirs: ["../.storybook/assets"],
core: {
- builder: 'webpack5',
+ builder: "webpack5",
},
};
diff --git a/.storybook/manager.js b/.storybook/manager.js
new file mode 100644
index 0000000..a327562
--- /dev/null
+++ b/.storybook/manager.js
@@ -0,0 +1,12 @@
+// import scss using scss-loader file
+import "!style-loader!css-loader!postcss-loader!sass-loader!./manager.scss";
+
+// set default theme
+document.body.setAttribute("data-theme", "theme-bootstrap");
+
+import { addons } from "@storybook/addons";
+import theme from "./theme";
+
+addons.setConfig({
+ theme,
+});
diff --git a/.storybook/manager.scss b/.storybook/manager.scss
new file mode 100644
index 0000000..a0daffe
--- /dev/null
+++ b/.storybook/manager.scss
@@ -0,0 +1,23 @@
+// load global styles used by storybook
+@use "themes/swimbird";
+@use "themes/bootstrap/css-variables" as bootstrap;
+
+body[data-theme="theme-bootstrap"] {
+ @include bootstrap.add-variables;
+}
+
+body[data-theme="theme-sb-dark"] {
+ @include swimbird.dark-theme;
+}
+body[data-theme="theme-sb-light"] {
+ @include swimbird.light-theme;
+}
+@include swimbird.add-typography;
+
+body {
+ background: var(--sb-body-bg) !important;
+
+ div#storybook-explorer-tree * {
+ color: var(--sb-body-color);
+ }
+}
diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html
index e58d580..e9e5539 100644
--- a/.storybook/preview-head.html
+++ b/.storybook/preview-head.html
@@ -1,7 +1,5 @@
-
-
-
-
-
-
+
diff --git a/.storybook/preview.js b/.storybook/preview.js
index 8d0929d..d04c6a2 100644
--- a/.storybook/preview.js
+++ b/.storybook/preview.js
@@ -1,9 +1,10 @@
-import { setCompodocJson } from '@storybook/addon-docs/angular';
-import docJson from '../documentation.json';
+import { setCompodocJson } from "@storybook/addon-docs/angular";
+import docJson from "../documentation.json";
+
setCompodocJson(docJson);
export const parameters = {
- actions: { argTypesRegex: '^on[A-Z].*' },
+ actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
@@ -11,10 +12,21 @@ export const parameters = {
},
},
docs: { inlineStories: true },
+ themes: {
+ //default: "Swimbird - Dark",
+ default: "Bootstrap",
+ clearable: false,
+ list: [
+ //{ name: "Swimbird - Dark", class: "theme-sb-dark", color: "#00aced" },
+ //{ name: "Swimbird - Light", class: "theme-sb-light", color: "#00aced" },
+ {
+ name: "Bootstrap",
+ class: "theme-bootstrap",
+ color: "var(--bs-primary)",
+ },
+ ],
+ onChange: (theme) => {
+ document.body.setAttribute("data-theme", theme.class);
+ },
+ },
};
-
-// workaround for https://github.com/nrwl/nx/issues/7054
-import GlobalStyles from '!postcss-loader!sass-loader!./scss-loader.scss';
-const storybookStyles = document.createElement('style');
-storybookStyles.innerHTML = GlobalStyles;
-document.body.appendChild(storybookStyles);
diff --git a/.storybook/scss-loader.scss b/.storybook/scss-loader.scss
index f9d5bee..5d154f1 100644
--- a/.storybook/scss-loader.scss
+++ b/.storybook/scss-loader.scss
@@ -1,3 +1,49 @@
// load global styles used by storybook
+@use 'themes/swimbird' with (
+ $font-path: '/static/media/.storybook/assets/'
+);
+@use 'bootstrap/scss/bootstrap-grid' with (
+ $enable-negative-margins: true
+);
+
@use 'projects/core/src/lib/scss' as generic-table;
@include generic-table.styles;
+
+@include swimbird.add-typography;
+
+.theme-bootstrap {
+ @import 'themes/bootstrap';
+}
+
+.theme-sb-dark {
+ @include swimbird.dark-theme
+}
+
+.theme-sb-light {
+ @include swimbird.light-theme
+}
+//@import 'bootstrap/scss/tables';
+
+
+
+.sbdocs {
+ &.sbdocs-wrapper, &.sbdocs-h1, &.sbdocs-h2, &.sbdocs-h3, &.sbdocs-p, &.sbdocs-p > code {
+ background: var(--sb-body-bg);
+ color: var(--sb-body-color);
+ }
+}
+docs-tabs pre, .sbdocs-pre .prismjs {
+ background-image: linear-gradient(0deg, var(--sb-code-even) 25%, var(--sb-code-odd) 25%, var(--sb-code-odd) 50%, var(--sb-code-even) 50%, var(--sb-code-even) 75%, var(--sb-code-odd) 75%, var(--sb-code-odd) 100%) !important;
+ background-size: 84px 84px !important;
+ padding: 21px 1rem !important;
+ font-family: var(--bs-font-monospace);
+ font-size: 0.875em;
+}
+
+.sbdocs-pre .prismjs {
+ font-size: 14px;
+ line-height: 21px;
+ * {
+ font-family: var(--bs-font-monospace) !important;
+ }
+}
diff --git a/.storybook/theme.js b/.storybook/theme.js
new file mode 100644
index 0000000..c2e4d5b
--- /dev/null
+++ b/.storybook/theme.js
@@ -0,0 +1,9 @@
+import { create } from "@storybook/theming";
+
+export default create({
+ base: "light",
+ brandTitle: "Angular Generic Table",
+ brandUrl: "https://github.com/hjalmers/angular-generic-table",
+ brandImage: "logo.svg",
+ brandTarget: "_self",
+});
diff --git a/.storybook/themes/bootstrap/_css-variables.scss b/.storybook/themes/bootstrap/_css-variables.scss
new file mode 100644
index 0000000..ddf6f30
--- /dev/null
+++ b/.storybook/themes/bootstrap/_css-variables.scss
@@ -0,0 +1,62 @@
+@import "../../../node_modules/bootstrap/scss/functions";
+@import "../../../node_modules/bootstrap/scss/variables";
+@import "../../../node_modules/bootstrap/scss/maps";
+@import "../../../node_modules/bootstrap/scss/mixins";
+@import "../../../node_modules/bootstrap/scss/utilities";
+@mixin add-variables() {
+ --sb-code-even: #{$body-bg};
+ --sb-code-odd: #{$gray-100};
+
+ // Note: Custom variable values only support SassScript inside `#{}`.
+
+ // Colors
+ //
+ // Generate palettes for full colors, grays, and theme colors.
+
+ @each $color, $value in $colors {
+ --#{$variable-prefix}#{$color}: #{$value};
+ }
+
+ @each $color, $value in $grays {
+ --#{$variable-prefix}gray-#{$color}: #{$value};
+ }
+
+ @each $color, $value in $theme-colors {
+ --#{$variable-prefix}#{$color}: #{$value};
+ }
+
+ @each $color, $value in $theme-colors-rgb {
+ --#{$variable-prefix}#{$color}-rgb: #{$value};
+ }
+
+ --#{$variable-prefix}white-rgb: #{to-rgb($white)};
+ --#{$variable-prefix}black-rgb: #{to-rgb($black)};
+ --#{$variable-prefix}body-color-rgb: #{to-rgb($body-color)};
+ --#{$variable-prefix}body-bg-rgb: #{to-rgb($body-bg)};
+
+ // Fonts
+
+ // Note: Use `inspect` for lists so that quoted items keep the quotes.
+ // See https://github.com/sass/sass/issues/2383#issuecomment-336349172
+ --#{$variable-prefix}font-sans-serif: #{inspect($font-family-sans-serif)};
+ --#{$variable-prefix}font-monospace: #{inspect($font-family-monospace)};
+ --#{$variable-prefix}gradient: #{$gradient};
+
+ // Root and body
+ // stylelint-disable custom-property-empty-line-before
+ // scss-docs-start root-body-variables
+ @if $font-size-root != null {
+ --#{$variable-prefix}root-font-size: #{$font-size-root};
+ }
+ --#{$variable-prefix}body-font-family: #{$font-family-base};
+ --#{$variable-prefix}body-font-size: #{$font-size-base};
+ --#{$variable-prefix}body-font-weight: #{$font-weight-base};
+ --#{$variable-prefix}body-line-height: #{$line-height-base};
+ --#{$variable-prefix}body-color: #{$body-color};
+ @if $body-text-align != null {
+ --#{$variable-prefix}body-text-align: #{$body-text-align};
+ }
+ --#{$variable-prefix}body-bg: #{$body-bg};
+ // scss-docs-end root-body-variables
+ // stylelint-enable custom-property-empty-line-before
+}
diff --git a/.storybook/themes/bootstrap/index.scss b/.storybook/themes/bootstrap/index.scss
new file mode 100644
index 0000000..1896871
--- /dev/null
+++ b/.storybook/themes/bootstrap/index.scss
@@ -0,0 +1,73 @@
+@use '../../../node_modules/bootstrap/scss/bootstrap' with (
+ $enable-negative-margins: true
+);
+@use 'css-variables' as bootstrap-variables;
+@import "../../../node_modules/bootstrap/scss/functions";
+@import "../../../node_modules/bootstrap/scss/variables";
+@import "../../../node_modules/bootstrap/scss/maps";
+@import "../../../node_modules/bootstrap/scss/mixins";
+@import "../../../node_modules/bootstrap/scss/utilities";
+
+& {
+ @include bootstrap-variables.add-variables;
+}
+// stylelint-disable declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix
+
+
+// Reboot
+//
+// Normalization of HTML elements, manually forked from Normalize.css to remove
+// styles targeting irrelevant browsers while applying new styles.
+//
+// Normalize is licensed MIT. https://github.com/necolas/normalize.css
+
+
+// Document
+//
+// Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.
+
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
+
+// Root
+//
+// Ability to the value of the root font sizes, affecting the value of `rem`.
+// null by default, thus nothing is generated.
+
+& {
+ @if $font-size-root != null {
+ font-size: var(--#{$variable-prefix}root-font-size);
+ }
+
+ @if $enable-smooth-scroll {
+ @media (prefers-reduced-motion: no-preference) {
+ scroll-behavior: smooth;
+ }
+ }
+}
+
+
+// Body
+//
+// 1. Remove the margin in all browsers.
+// 2. As a best practice, apply a default `background-color`.
+// 3. Prevent adjustments of font size after orientation changes in iOS.
+// 4. Change the default tap highlight to be completely transparent in iOS.
+
+// scss-docs-start reboot-body-rules
+& {
+ margin: 0; // 1
+ font-family: var(--#{$variable-prefix}body-font-family);
+ @include font-size(var(--#{$variable-prefix}body-font-size));
+ font-weight: var(--#{$variable-prefix}body-font-weight);
+ line-height: var(--#{$variable-prefix}body-line-height);
+ color: var(--#{$variable-prefix}body-color);
+ text-align: var(--#{$variable-prefix}body-text-align);
+ background-color: var(--#{$variable-prefix}body-bg); // 2
+ -webkit-text-size-adjust: 100%; // 3
+ -webkit-tap-highlight-color: rgba($black, 0); // 4
+}
diff --git a/.storybook/themes/swimbird/_colors.scss b/.storybook/themes/swimbird/_colors.scss
new file mode 100644
index 0000000..3603e56
--- /dev/null
+++ b/.storybook/themes/swimbird/_colors.scss
@@ -0,0 +1,275 @@
+@use 'functions';
+@use 'sass:map';
+@use 'sass:math';
+@use 'sass:string';
+
+@function to-number($value) {
+ @if type-of($value) == 'number' {
+ @return $value;
+ } @else if type-of($value) != 'string' {
+ @error 'Value for `to-number` should be a number or a string.';
+ }
+
+ $result: 0;
+ $digits: 0;
+ $minus: str-slice($value, 1, 1) == '-';
+ $numbers: (
+ '0': 0,
+ '1': 1,
+ '2': 2,
+ '3': 3,
+ '4': 4,
+ '5': 5,
+ '6': 6,
+ '7': 7,
+ '8': 8,
+ '9': 9
+ );
+
+ @for $i from if($minus, 2, 1) through str-length($value) {
+ $character: str-slice($value, $i, $i);
+
+ @if (index(map-keys($numbers), $character) or $character == '.') {
+ @if $character == '.' {
+ $digits: 1;
+ } @else if $digits == 0 {
+ $result: $result * 10 + map.get($numbers, $character);
+ } @else {
+ $digits: $digits * 10;
+ $result: $result + math.div(map.get($numbers, $character),$digits);
+ }
+ }
+ }
+
+ @return if($minus, -$result, $result);
+}
+$grey: (
+ 0: rgb(20, 20, 20),
+ 1: rgb(30, 30, 30),
+ 2: rgb(40, 40, 40),
+ 3: rgb(80, 80, 80),
+ 4: rgb(100, 100, 100),
+ 5: rgb(120, 120, 120),
+ 6: rgb(140, 140, 140),
+ 7: rgb(160, 160, 160),
+ 8: rgb(180, 180, 180),
+ 9: rgb(200, 200, 200),
+ 10: rgb(240, 240, 240),
+ 11: rgb(247, 247, 247)
+);
+
+$black: #000;
+$white: #fff;
+
+$purple: #ac586f;
+//$teal: #40cda0;
+$new-teal: #038a91;
+
+// intention
+$info-color: #00989e;
+$critical-color: #b45869;
+$success-color: #0ad406;
+$warning-color: #ffb103;
+
+// from design
+$super-light-teal: #b5d7d8;
+$light-teal: #76b7bd;
+$teal: #00989e;
+$dark-teal: #028085;
+$super-light-petrol: #c6dbe1;
+$light-petrol: #80b4be;
+$petrol: #208a9a;
+$dark-petrol: #006371;
+$super-light-purple: #c0adbf;
+$light-purple: #ae94ac;
+$purple: #7e627d;
+$dark-purple: #614a61;
+$super-light-red: #e3bec1;
+$light-red: #c06d7a;
+$red: #b45869;
+$dark-red: #960338;
+$super-light-orange: #fcd6c0;
+$light-orange: #f6b088;
+$orange: #f08655;
+$dark-orange: #e8501d;
+$super-light-yellow: #ffe9c6;
+$light-yellow: #fed898;
+$yellow: #fbc75f;
+$dark-yellow: #f8af00;
+
+$colors: (
+ 0: (
+ 0: $super-light-teal,
+ 1: $light-teal,
+ 2: $teal,
+ 3: $dark-teal
+ ),
+ 1: (
+ 0: $super-light-petrol,
+ 1: $light-petrol,
+ 2: $petrol,
+ 3: $dark-petrol
+ ),
+ 2: (
+ 0: $super-light-purple,
+ 1: $light-purple,
+ 2: $purple,
+ 3: $dark-purple
+ ),
+ 3: (
+ 0: $super-light-red,
+ 1: $light-red,
+ 2: $red,
+ 3: $dark-red
+ ),
+ 4: (
+ 0: $super-light-orange,
+ 1: $light-orange,
+ 2: $orange,
+ 3: $dark-orange
+ ),
+ 5: (
+ 0: $super-light-yellow,
+ 1: $light-yellow,
+ 2: $yellow,
+ 3: $dark-yellow
+ )
+);
+
+$number-of-chart-shades: 10;
+
+/* use $count as number of child elements */
+@function tonal-transition($color-start, $color-end, $count) {
+ $map: ();
+ @for $i from 1 through $count {
+ $map: map-merge(
+ $map,
+ (#{$i}: mix($color-end, $color-start, 100% * math.div($i, $count)))
+ );
+ }
+ @return $map;
+}
+
+@function generate-color-maps($theme, $shades: $number-of-chart-shades) {
+ $map: ();
+ @each $key, $value in $theme {
+ $map: map-merge(
+ $map,
+ (
+ #{$key}:
+ tonal-transition(map.get($value, max), map.get($value, min), $shades)
+ )
+ );
+ }
+ @return $map;
+}
+
+$base-chart-colors: (
+ 'dark': (
+ 1: (
+ min: $super-light-petrol,
+ max: $dark-petrol
+ ),
+ 2: (
+ min: $super-light-purple,
+ max: $dark-purple
+ ),
+ 3: (
+ min: $super-light-red,
+ max: $dark-red
+ ),
+ 4: (
+ min: $super-light-orange,
+ max: $dark-orange
+ ),
+ 5: (
+ min: $super-light-yellow,
+ max: $dark-yellow
+ ),
+ 6: (
+ min: $super-light-teal,
+ max: $dark-teal
+ )
+ ),
+ 'light': (
+ 1: (
+ min: $super-light-petrol,
+ max: $dark-petrol
+ ),
+ 2: (
+ min: $super-light-purple,
+ max: $dark-purple
+ ),
+ 3: (
+ min: $super-light-red,
+ max: $dark-red
+ ),
+ 4: (
+ min: $super-light-orange,
+ max: $dark-orange
+ ),
+ 5: (
+ min: $super-light-yellow,
+ max: $dark-yellow
+ ),
+ 6: (
+ min: $super-light-teal,
+ max: $dark-teal
+ )
+ )
+);
+
+$generated-chart-colors: (
+ 'light': generate-color-maps(map.get($base-chart-colors, 'light')),
+ 'dark': generate-color-maps(map.get($base-chart-colors, 'dark'))
+);
+
+@mixin generate-chart-color-variables($generated-chart-colors) {
+ @each $series, $map in $generated-chart-colors {
+ @each $color, $value in $map {
+ --bb-chart-serie-#{$series}-color-#{$color}: #{$value};
+ --bb-chart-serie-#{$series}-color-contrast-#{$color}: #{functions.most-legible-color(
+ $value
+ )};
+ @if (to-number($color) == math.div(length($map), 2)) {
+ $next: #{(to-number($series) + 6)};
+ --bb-chart-serie-#{$next}-color-1: #{$value};
+ } @else if(to-number($color) == length($map)) {
+ $next: #{(to-number($series) + 12)};
+ --bb-chart-serie-#{$next}-color-1: #{$value};
+ }
+ }
+ }
+}
+
+@mixin generate-variables($map, $prefix: '') {
+ @if ($prefix != '') {
+ $prefix: #{$prefix}-;
+ }
+ @each $key, $value in $map {
+ --#{$prefix}#{$key}: #{$value};
+ }
+}
+
+@mixin generate-map-variables($maps, $prefix: '', $hsl: false) {
+ @if ($prefix != '') {
+ $prefix: #{$prefix}-;
+ }
+
+ @each $name, $map in $maps {
+ @if ($hsl) {
+ $map: functions.hsl-map($map);
+ }
+ @each $key, $value in $map {
+ --#{$prefix}#{$name}-#{$key}: #{$value};
+ }
+ }
+}
+
+@mixin generate-color-variables($map) {
+ @each $key, $value in $map {
+ --color-#{$key}-h: #{hue($value)};
+ --color-#{$key}-s: #{saturation($value)};
+ --color-#{$key}-l: #{lightness($value)};
+ }
+}
diff --git a/.storybook/themes/swimbird/_dark-theme.scss b/.storybook/themes/swimbird/_dark-theme.scss
new file mode 100644
index 0000000..fe0642f
--- /dev/null
+++ b/.storybook/themes/swimbird/_dark-theme.scss
@@ -0,0 +1,344 @@
+@use 'tokens';
+@use 'colors';
+@use 'functions';
+@use 'sass:map';
+@use 'sass:color';
+
+@mixin add(
+ // colors
+ $primary-color: tokens.$dark-teal,
+ $secondary-color: $primary-color,
+ $accent-color: $primary-color,
+ $colors: (primary: $primary-color, secondary: $secondary-color),
+ $purple: colors.$purple,
+ $bg-color: map.get(tokens.$grey, 0),
+ $bg-color-1: map.get(tokens.$grey, 1),
+ $bg-color-fadable: functions.stripped-rgb($bg-color),
+ $bg-image-color: transparentize(tokens.$black, 0.85),
+ $accent-color-contrast: tokens.$white,
+ //functions.most-legible-color($accent-color);
+ $accent-color-contrast-fadable: functions.stripped-rgb($accent-color-contrast),
+ // brand
+ $logo-sm-path: url('^./assets/images/swimbird-logo.svg'),
+ $logo-path: url('^./assets/images/swimbird-logo.svg'),
+ $login-logo-path: url('^./assets/images/swimbird-logo-horisontal-light.svg'),
+ $brand-text: 'SWIMBIRD',
+ $font-family: 'Thicccboi, sans-serif',
+ $border-radius: 1.5rem,
+ $box-shadow-sm: 0 0 0.5rem 0 rgba(0, 0, 0, 0.0675),
+ // links
+ $link-color: functions.a11y-color($primary-color, $bg-color-1),
+ $link-color-hover: functions.a11y-color(lighten($primary-color, 15%), $bg-color-1),
+ $link-color-visited: functions.a11y-color($purple, $bg-color-1),
+ $link-font-weight: bold,
+ // buttons
+ $button-border-radius: 1.5rem,
+ $button-primary-bg: $primary-color,
+ $button-primary-border: $primary-color,
+ $button-primary-color: functions.most-legible-color($button-primary-bg),
+ $button-primary-bg-hover: darken($button-primary-bg, 10.5%),
+ $button-primary-color-hover: functions.most-legible-color($button-primary-bg-hover),
+ $button-primary-bg-active: darken($button-primary-bg, 20.5%),
+ $button-primary-color-active: functions.most-legible-color($button-primary-bg-active),
+ $button-secondary-bg: transparent,
+ $button-secondary-border: darken($button-primary-bg, 10.5%),
+ //functions.most-legible-color($button-secondary-bg),
+ $button-secondary-color: $primary-color,
+ //functions.most-legible-color($button-secondary-bg),
+ $button-secondary-bg-hover: $button-primary-bg-hover,
+ $button-secondary-color-hover:
+ functions.most-legible-color($button-secondary-bg-hover),
+ $button-secondary-bg-active: $button-primary-bg-active,
+ $button-secondary-color-active:
+ functions.most-legible-color($button-secondary-bg-active),
+ $buttons: (
+ primary: (
+ color: $button-primary-color,
+ border: $button-primary-border,
+ background: $button-primary-bg,
+ color-hover: $button-primary-color-hover,
+ background-hover: $button-primary-bg-hover,
+ color-active: $button-primary-color-active,
+ background-active: $button-primary-bg-active
+ ),
+ secondary: (
+ color: $button-secondary-color,
+ border: $button-secondary-border,
+ background: $button-secondary-bg,
+ color-hover: $button-secondary-color-hover,
+ background-hover: $button-secondary-bg-hover,
+ color-active: $button-secondary-color-active,
+ background-active: $button-secondary-bg-active
+ )
+ ),
+ $footer-bg: map.get(tokens.$grey, 1),
+ $footer-color: functions.most-legible-color($footer-bg),
+ $focus-color:
+ hsla(var(--color-primary-h), var(--color-primary-s), var(--color-primary-l), 0.35),
+ // alerts
+ $alert-border-radius: 0.5rem,
+ //$border-radius,
+ $teal: #00989e,
+ $blue: #3f939e,
+ $green: #3f9e7e,
+ $petrol: #208a9a,
+ $yellow: #fbc75f,
+ $red: #a63d50,
+ $intent: (info: $blue, success: $green, warning: $yellow, critical: $red),
+ $alerts: (
+ info: (
+ text:
+ functions.a11y-color(map.get($intent, 'info'), $bg-color-1, $level: 'AAA'),
+ border-color: map.get($intent, 'info'),
+ background: map.get($intent, 'info'),
+ contrast:
+ functions.a11y-color(
+ #fff,
+ map.get($intent, 'info'),
+ $bold: true,
+ $graphic: true,
+ $force: 'lighten'
+ )
+ ),
+ success: (
+ text:
+ functions.a11y-color(
+ map.get($intent, 'success'),
+ $bg-color-1,
+ $level: 'AAA'
+ ),
+ border-color: map.get($intent, 'success'),
+ background: map.get($intent, 'success'),
+ contrast:
+ functions.a11y-color(
+ #fff,
+ map.get($intent, 'success'),
+ $bold: true,
+ $graphic: true,
+ $force: 'lighten'
+ )
+ ),
+ warning: (
+ text:
+ functions.a11y-color(
+ map.get($intent, 'warning'),
+ $bg-color-1,
+ $level: 'AAA'
+ ),
+ border-color: map.get($intent, 'warning'),
+ background: map.get($intent, 'warning'),
+ contrast:
+ functions.a11y-color(
+ #fff,
+ map.get($intent, 'warning'),
+ $bold: true,
+ $graphic: true,
+ $force: 'lighten'
+ )
+ ),
+ critical: (
+ text:
+ functions.a11y-color(
+ map.get($intent, 'critical'),
+ $bg-color-1,
+ $level: 'AAA'
+ ),
+ border-color: map.get($intent, 'critical'),
+ background: map.get($intent, 'critical'),
+ contrast:
+ functions.a11y-color(
+ #fff,
+ map.get($intent, 'critical'),
+ $bold: true,
+ $graphic: true,
+ $force: 'lighten'
+ )
+ )
+ ),
+ $badges: (
+ critical: (
+ background: map.get($intent, 'critical'),
+ contrast:
+ functions.a11y-color(
+ #fff,
+ map.get($intent, 'critical'),
+ $bold: true,
+ $graphic: true,
+ $force: 'lighten'
+ )
+ )
+ ),
+ $nav: (
+ bg: tokens.$navbar-bg,
+ box-shadow: tokens.$shadow-md,
+ menu-category-font-weight: tokens.$menu-category-font-weight,
+ menu-category-font-size: tokens.$font-size-md,
+ menu-category-text-decoration: none,
+ menu-category-color: tokens.$label-color,
+ menu-item-color: tokens.$menubar-item-color,
+ menu-item-color-hover: tokens.$menubar-item-color-hover,
+ menu-item-bg-hover: tokens.$menubar-item-bg-hover,
+ menu-item-font-weight: tokens.$menubar-item-font-weight,
+ menu-item-underline-color: tokens.$menubar-item-accent-color,
+ mega-menu-item-font-weight: tokens.$menu-item-font-weight,
+ mega-menu-item-color: tokens.$menu-item-color,
+ mega-menu-item-color-hover: tokens.$menu-item-color-hover,
+ mega-menu-item-bg-hover: tokens.$menu-item-bg-hover,
+ mega-menu-bg: tokens.$popover-bg,
+ mega-menu-box-shadow: tokens.$shadow-md
+ ),
+ $card: (
+ bg: tokens.$level-1-bg,
+ nested-bg: tokens.$level-2-bg,
+ nested-border: none,
+ border-radius: $border-radius,
+ mobile-border-radius: 0,
+ box-shadow: $box-shadow-sm,
+ box-shadow-hover: var(--shadow-md),
+ border: none
+ ),
+ $pills: (
+ border-radius: $border-radius,
+ padding: 0.75rem 1rem,
+ //color: $button-primary-color,
+ border: none,
+ background: transparent,
+ //color-hover: $button-primary-color-hover,
+ border-hover: none,
+ background-hover: $button-primary-bg-hover,
+ color-active: functions.most-legible-color(map.get(tokens.$grey, 3)),
+ border-active: none,
+ background-active: map.get(tokens.$grey, 3)
+ ),
+ $modal: (
+ box-shadow: tokens.$shadow-md,
+ color: tokens.$text-color,
+ bg: tokens.$level-1-bg,
+ border-radius: 1.5rem,
+ dialog-border-radius: 1.5rem,
+ aside-border-radius: 1rem 1rem 0 0,
+ aside-border-radius-sm: 1.5rem 0 0 1.5rem,
+ aside-left-border-radius-sm: 0 1.5rem 1.5rem 0
+ ),
+ $chart: (
+ fill-opacity: 0.25,
+ fill-opacity-hover: 1,
+ stroke-width: 2px,
+ line-stroke-width: 2px,
+ bubble-stroke-width: 2px,
+ bubble-stroke-opacity: 0.35,
+ bubble-fill-opacity: 0.1,
+ bubble-fill-opacity-hover: 0.4
+ ),
+ $chart-colors: (
+ 1: (
+ min: tokens.$super-light-petrol,
+ max: tokens.$dark-petrol
+ ),
+ 2: (
+ min: tokens.$super-light-purple,
+ max: tokens.$dark-purple
+ ),
+ 3: (
+ min: tokens.$super-light-red,
+ max: tokens.$dark-red
+ ),
+ 4: (
+ min: tokens.$super-light-orange,
+ max: tokens.$dark-orange
+ ),
+ 5: (
+ min: tokens.$super-light-yellow,
+ max: tokens.$dark-yellow
+ ),
+ 6: (
+ min: tokens.$super-light-teal,
+ max: tokens.$dark-teal
+ )
+ )
+) {
+ --border-radius: #{$border-radius};
+ --alert-border-radius: #{$alert-border-radius};
+ // card
+ @include colors.generate-variables($card, 'card');
+ // pills
+ @include colors.generate-variables($pills, 'pills');
+ --font-family: #{$font-family};
+ --focus-color: #{$focus-color};
+ --button-border-radius: #{$button-border-radius};
+ --bb-chart-colors: url('#{tokens.$petrol};#{tokens.$purple};#{tokens.$red};#{tokens.$orange};#{tokens.$yellow};#{tokens.$teal};#{tokens.$light-petrol};#{tokens.$light-purple};#{tokens.$light-red};#{tokens.$light-orange};#{tokens.$light-yellow};#{tokens.$light-teal}');
+ --bg-color: #{$bg-color};
+ --bg-border-color: #{map.get(tokens.$grey, 1)};
+ --bg-color-fadable: #{$bg-color-fadable};
+ --bg-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 261.46 193.49'%3E%3Cpath d='M216.3 96.87c7.33.77 14.67 1.42 22 2l2.45.18c1.85.14 3.7.28 5.55.4l.95.06q12.35.8 24.65 1.21-8.89-5.57-18-10.81l-4.48-2.53c-1.15-.65-2.31-1.29-3.46-1.92q-5.53-3-11.15-6A89.44 89.44 0 0 0 151 25.79a130.13 130.13 0 0 0-23.61-15.15 89.86 89.86 0 0 0-31.23 18.91l-3.52-.76-3-.6-2.88-.58q-15.21-3-30.38-5.14-23.07-3.25-46-4.53Q39.89 43.5 72 66.17l4.24 3v.06a89.37 89.37 0 0 0-5 9.68 87.08 87.08 0 0 0-3.08 8 89.36 89.36 0 0 0 66.51 115.68A74.33 74.33 0 0 0 217.23 161a89.62 89.62 0 0 1-27.17 17c-1.44.57-2.89 1.11-4.36 1.61a47.71 47.71 0 0 0 5.3-13.94c.26-1.26.46-2.52.62-3.78a89.39 89.39 0 0 1-92.91-19.33 47.61 47.61 0 0 1 55.41-33.23 80.52 80.52 0 0 0 44.09-3.13 81.08 81.08 0 0 0 18.14-9.33Z' transform='translate(-10.42 -10.64)' style='fill:#{functions.escape-hex-color(#{$bg-image-color})}' /%3E%3C/svg%3E");
+ --bg-color-hover: #{map.get(tokens.$grey, 2)};
+ --table-border-color: #{map.get(tokens.$grey, 2)};
+ --level-0-bg-color: #{$bg-color-1};
+ --level-0-color: #{tokens.$white};
+ --level-1-bg-color: #{map.get(tokens.$grey, 2)};
+ --level-1-color: #{tokens.$white};
+ --level-2-bg-color: #{map.get(tokens.$grey, 2)};
+ --label-color: #{map.get(tokens.$grey, 12)};
+ --text-color: #{tokens.$white};
+ --text-color-fadable: #{functions.stripped-rgb(tokens.$white)};
+ --text-positive-color: #{tokens.$teal};
+ --text-negative-color: #{tokens.$light-red};
+ --text-muted-color: #{map.get(tokens.$grey, 2)};
+ --text-color-inverted: #{tokens.$black};
+ // highchart colors start
+ --chart-color-0: #{tokens.$petrol};
+ --chart-color-1: #{tokens.$purple};
+ --chart-color-2: #{tokens.$red};
+ --chart-color-3: #{tokens.$orange};
+ --chart-color-4: #{tokens.$yellow};
+ --chart-color-5: #{tokens.$teal};
+ --chart-color-6: #{tokens.$light-petrol};
+ --chart-color-7: #{tokens.$light-purple};
+ --chart-color-8: #{tokens.$light-red};
+ --chart-color-9: #{tokens.$light-orange};
+ --chart-color-10: #{tokens.$light-yellow};
+ --chart-color-11: #{tokens.$light-teal};
+ // highchart colors end
+ --chart-benchmark: #{tokens.$yellow};
+ --accent-color: #{$accent-color};
+ --accent-color-contrast: #{$accent-color-contrast};
+ --accent-color-contrast-fadable: #{$accent-color-contrast-fadable};
+ --hr-color: #{transparentize(map.get(tokens.$grey, 10), 0.75)};
+ --link-color: #{$link-color};
+ --link-color-hover: #{$link-color-hover};
+ --link-color-visited: #{$link-color-visited};
+ --link-font-weight: #{$link-font-weight};
+ --scroll-thumb-color: #{map.get(tokens.$grey, 2)};
+ --footer-bg: #{$footer-bg};
+ --footer-color: #{$footer-color};
+ --shadow-sm: 0 0 0.5rem 0.25rem rgba(0, 0, 0, 0.25);
+ --shadow-md: 0 0 1rem 0.5rem rgba(0, 0, 0, 0.25);
+ --logo-sm-path: #{$logo-sm-path};
+ --logo-path: #{$logo-path};
+ --login-logo-path: #{$login-logo-path};
+ --brand-text: #{$brand-text};
+ --skeleton-loader-highlight-color: #333333;
+ --skeleton-loader-bg-color: #{map.get(tokens.$grey, 2)};
+ @include colors.generate-variables($chart, 'chart');
+ @include colors.generate-variables($nav, 'nav');
+ @include colors.generate-variables($modal, 'modal');
+ @include colors.generate-map-variables($buttons, 'button');
+ @include colors.generate-map-variables($alerts, 'alert');
+ @include colors.generate-map-variables($badges, 'badge');
+ @include colors.generate-color-variables($colors);
+ @include colors.generate-chart-color-variables(
+ colors.generate-color-maps($chart-colors)
+ );
+
+ @include colors.generate-map-variables($alerts, 'alert', true);
+}
+
+/* mixin for generating css variables from map with optional prefix */
+@mixin _generate-variables($map, $prefix: '') {
+ @if ($prefix != '') {
+ $prefix: #{$prefix}-;
+ }
+ @each $key, $value in $map {
+ --#{$prefix}#{$key}: #{$value};
+ }
+}
diff --git a/.storybook/themes/swimbird/buttons/index.scss b/.storybook/themes/swimbird/buttons/index.scss
new file mode 100644
index 0000000..4eea232
--- /dev/null
+++ b/.storybook/themes/swimbird/buttons/index.scss
@@ -0,0 +1,24 @@
+@use 'mixins';
+
+.btn {
+ @include mixins.add-base-button;
+}
+
+.btn-sm {
+ @include mixins.add-small-button-modifier;
+}
+
+.primary {
+ @include mixins.add-primary-button;
+}
+
+.secondary {
+ @include mixins.add-secondary-button;
+}
+
+.close {
+ @include mixins.close;
+}
+sb-pending-button-indicator {
+ @include mixins.loading;
+}
diff --git a/.storybook/themes/swimbird/buttons/mixins.scss b/.storybook/themes/swimbird/buttons/mixins.scss
new file mode 100644
index 0000000..b792289
--- /dev/null
+++ b/.storybook/themes/swimbird/buttons/mixins.scss
@@ -0,0 +1,296 @@
+//@use '../../tokens';
+//@use '../../functions';
+@use 'sass:math';
+@use 'sass:map';
+
+$btn-primary-color: var(--button-primary-color);
+$btn-primary-border: var(--button-primary-border);
+$btn-primary-bg: var(--button-primary-background);
+$btn-primary-color-hover: var(--button-primary-color-hover);
+$btn-primary-bg-hover: var(--button-primary-background-hover);
+$btn-primary-color-active: var(--button-primary-color-active);
+$btn-primary-bg-active: var(--button-primary-background-active);
+$btn-secondary-color: var(--button-secondary-color);
+$btn-secondary-border: var(--button-secondary-border);
+$btn-secondary-bg: var(--button-secondary-bg);
+$btn-secondary-color-hover: var(--button-secondary-color-hover);
+$btn-secondary-bg-hover: var(--button-secondary-background-hover);
+$btn-secondary-color-active: var(--button-secondary-color-active);
+$btn-secondary-bg-active: var(--button-secondary-background-active);
+$btn-border-radius: var(--button-border-radius);
+$focus-color: var(--focus-color);
+$disabled-opacity: 0.3;
+$font-family: var(--font-family);
+
+@mixin add-base-button() {
+ background: none;
+ border: none;
+ padding: 0.75rem 1rem;
+ border-radius: $btn-border-radius; // 1.5rem;
+ font-family: #{$font-family};
+ font-weight: bold;
+ cursor: pointer;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 300ms ease-in-out;
+ overflow: hidden;
+ position: relative;
+ z-index: 0;
+ line-height: normal;
+}
+
+@mixin add-small-button-modifier {
+ padding: 0.25rem 0.5rem;
+ font-size: 0.75rem;
+}
+@mixin add-primary-button() {
+ background-color: var(--background);
+ border: solid 1px $btn-primary-border;
+ color: var(--color);
+ --color: #{$btn-primary-color};
+ --background: #{$btn-primary-bg};
+ @include add-states(
+ $base: (
+ color: $btn-primary-color,
+ bg: $btn-primary-bg
+ ),
+ $hover: (
+ color: $btn-primary-color-hover,
+ border: $btn-primary-bg-hover,
+ bg: $btn-primary-bg-hover
+ ),
+ $active: (
+ color: $btn-primary-color-active,
+ bg: $btn-primary-bg-active
+ )
+ );
+}
+@mixin add-secondary-button() {
+ background-color: var(--background);
+ color: var(--color);
+ border: solid 1px $btn-secondary-border;
+ --background: #{$btn-secondary-bg};
+ --color: #{$btn-secondary-color};
+ @include add-states(
+ $base: (
+ color: $btn-secondary-color,
+ bg: $btn-secondary-bg
+ ),
+ $hover: (
+ color: $btn-secondary-color-hover,
+ border: $btn-secondary-bg-hover,
+ bg: $btn-secondary-bg-hover
+ ),
+ $active: (
+ color: $btn-secondary-color-active,
+ bg: $btn-secondary-bg-active
+ )
+ );
+}
+@mixin add-states(
+ $base: (
+ color: #ffffff,
+ bg: lighten(desaturate(#038a91, 50%), 15%)
+ ),
+ $hover: (
+ color: #ffffff,
+ border: #ffffff,
+ bg: darken(desaturate(#038a91, 50%), 5%)
+ ),
+ $active: (
+ color: fade-out(#ffffff, 0.2),
+ border: fade-out(#ffffff, 0.2),
+ bg: darken(desaturate(#038a91, 50%), 5%)
+ )
+) {
+ &::before {
+ opacity: 0;
+ content: '';
+ position: absolute;
+ display: block;
+ width: 100%;
+ height: 100%;
+ background-color: map.get($hover, bg); //lighten(desaturate(#038a91, 50%), 15%);
+ top: 0;
+ left: 0;
+ transition: all 300ms ease-in-out, outline 0s;
+ border-radius: inherit;
+ transform: scale3d(0.3, 0.8, 1);
+ z-index: -1;
+ }
+ &:hover:not([disabled], .disabled),
+ &:active:not([disabled], .disabled),
+ &.active:not([disabled], .disabled) {
+ --background: #{map.get($hover, bg)}; //darken(desaturate(#038a91, 50%), 5%);
+ border-color: map.get($hover, border);
+ &::before {
+ opacity: 1;
+ transform: scale3d(1.5, 2.5, 1);
+ }
+ --color: #{map.get($hover, color)};
+ }
+ &:active:not([disabled], .disabled),
+ &.active:not([disabled], .disabled) {
+ --background: #{map.get($active, bg)}; //darken(desaturate(#038a91, 50%), 5%);
+ border-color: map.get($active, border);
+
+ &::before {
+ opacity: 0.4;
+ }
+ --color: #{map.get($active, color)}; //fade-out($color, 0.2);
+ }
+ &:focus:not(:focus-visible) {
+ outline: 0;
+ }
+
+ &:focus-visible:not([disabled], .disabled) {
+ outline: $focus-color solid 3px;
+ }
+ &:disabled,
+ &.disabled {
+ opacity: $disabled-opacity;
+ cursor: not-allowed;
+ }
+}
+
+$close-height: 42px;
+$close-width: $close-height;
+$close-height-sm: 34px;
+$close-width-sm: $close-height-sm;
+$close-padding: 20px;
+$close-padding-sm: 16px;
+$close-thickness: 2px;
+$close-bg-hover: #8C8C8C;
+@mixin close {
+ color: var(--text-color);
+ background-color: transparent;
+ border: 0;
+ border-radius: 50%;
+ content: '';
+ cursor: pointer;
+ font-family: inherit;
+ height: $close-height;
+ position: relative;
+ width: $close-width;
+ font-size: 0;
+
+ &::after,
+ &::before {
+ background: var(--color);
+ content: '';
+ display: block;
+ height: $close-height - $close-padding;
+ left: calc(50% - #{math.div($close-thickness, 2)});
+ position: absolute;
+ top: math.div($close-padding, 2);
+ width: $close-thickness;
+ }
+
+ &::after {
+ transform: rotateZ(45deg);
+ }
+
+ &::before {
+ transform: rotateZ(-45deg);
+ }
+}
+
+@mixin loading() {
+ svg {
+ height: 18px;
+ g.loading {
+ animation: 2s linear infinite loading-animation;
+ transform-origin: center;
+ circle {
+ animation: 1.4s ease-in-out infinite both circle-animation;
+ display: block;
+ fill: transparent;
+ stroke: var(--color);
+ stroke-linecap: round;
+ stroke-dasharray: 283;
+ stroke-dashoffset: 280;
+ stroke-width: 12;
+ transform-origin: 50% 50%;
+ }
+ }
+
+ path.success {
+ fill: none;
+ stroke: var(--color);
+ stroke-width: 4;
+ stroke-miterlimit: 10;
+ transform: scale(3);
+ transform-origin: 26px 26px;
+ stroke-linejoin: miter;
+ stroke-linecap: butt;
+ stroke-dasharray: 36;
+ stroke-dashoffset: 0;
+ }
+ path.close {
+ fill: none;
+ stroke: var(--color);
+ stroke-width: 4;
+ stroke-miterlimit: 10;
+ transform: scale(3);
+ transform-origin: 24px 16px;
+ stroke-linejoin: miter;
+ stroke-linecap: butt;
+ stroke-dasharray: 29;
+ stroke-dashoffset: 0;
+ }
+ g.error {
+ path,
+ circle.outer {
+ fill: none;
+ stroke: var(--color);
+ stroke-width: 10;
+ stroke-miterlimit: 10;
+ stroke-linejoin: round;
+ }
+ path {
+ stroke-dasharray: 36;
+ stroke-dashoffset: 0;
+ }
+ circle.outer {
+ stroke-dasharray: 280;
+ stroke-dashoffset: 0;
+ stroke-width: 12;
+ }
+ circle.dot {
+ fill: var(--color);
+ stroke: none;
+ stroke-dashoffset: 0;
+ }
+ transform-origin: center;
+ }
+ }
+}
+
+@keyframes loading-animation {
+ 0% {
+ transform: rotateZ(0deg);
+ }
+ 100% {
+ transform: rotateZ(360deg);
+ }
+}
+
+@keyframes circle-animation {
+ 0%,
+ 25% {
+ stroke-dashoffset: 280;
+ transform: rotate(0);
+ }
+
+ 50%,
+ 75% {
+ stroke-dashoffset: 75;
+ transform: rotate(45deg);
+ }
+
+ 100% {
+ stroke-dashoffset: 280;
+ transform: rotate(360deg);
+ }
+}
diff --git a/.storybook/themes/swimbird/functions/index.scss b/.storybook/themes/swimbird/functions/index.scss
new file mode 100644
index 0000000..edfa793
--- /dev/null
+++ b/.storybook/themes/swimbird/functions/index.scss
@@ -0,0 +1,422 @@
+@use 'sass:math';
+@use 'sass:map';
+
+// Characters which are escaped by the escape-svg function
+$escaped-characters: (
+ ('<', '%3c'),
+ ('>', '%3e'),
+ ('#', '%23'),
+ ('(', '%28'),
+ (')', '%29')
+) !default;
+
+// Precomputed linear color channel values, for use in contrast calculations.
+// See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
+//
+// Algorithm, for c in 0 to 255:
+// f(c) {
+// c = c / 255;
+// return c < 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
+// }
+//
+// This lookup table is needed since there is no `pow` in SASS.
+$linear-channel-values: 0 0.0003035269835488375 0.000607053967097675 0.0009105809506465125
+ 0.00121410793419535 0.0015176349177441874 0.001821161901293025 0.0021246888848418626
+ 0.0024282158683907 0.0027317428519395373 0.003035269835488375 0.003346535763899161
+ 0.003676507324047436 0.004024717018496307 0.004391442037410293 0.004776953480693729
+ 0.005181516702338386 0.005605391624202723 0.006048833022857054 0.006512090792594475
+ 0.006995410187265387 0.007499032043226175 0.008023192985384994 0.008568125618069307
+ 0.009134058702220787 0.00972121732023785 0.010329823029626936 0.010960094006488246
+ 0.011612245179743885 0.012286488356915872 0.012983032342173012 0.013702083047289686
+ 0.014443843596092545 0.01520851442291271 0.01599629336550963 0.016807375752887384
+ 0.017641954488384078 0.018500220128379697 0.019382360956935723 0.0202885630566524
+ 0.021219010376003555 0.022173884793387385 0.02315336617811041 0.024157632448504756
+ 0.02518685962736163 0.026241221894849898 0.027320891639074894 0.028426039504420793
+ 0.0295568344378088 0.030713443732993635 0.03189603307301153 0.033104766570885055
+ 0.03433980680868217 0.03560131487502034 0.03688945040110004 0.0382043715953465
+ 0.03954623527673284 0.04091519690685319 0.042311410620809675 0.043735029256973465
+ 0.04518620438567554 0.046665086336880095 0.04817182422688942 0.04970656598412723
+ 0.05126945837404324 0.052860647023180246 0.05448027644244237 0.05612849004960009
+ 0.05780543019106723 0.0595112381629812 0.06124605423161761 0.06301001765316767
+ 0.06480326669290577 0.06662593864377289 0.06847816984440017 0.07036009569659588
+ 0.07227185068231748 0.07421356838014963 0.07618538148130785 0.07818742180518633
+ 0.08021982031446832 0.0822827071298148 0.08437621154414882 0.08650046203654976
+ 0.08865558628577294 0.09084171118340768 0.09305896284668745 0.0953074666309647
+ 0.09758734714186246 0.09989872824711389 0.10224173308810132 0.10461648409110419
+ 0.10702310297826761 0.10946171077829933 0.1119324278369056 0.11443537382697373
+ 0.11697066775851084 0.11953842798834562 0.12213877222960187 0.12477181756095049
+ 0.12743768043564743 0.1301364766903643 0.13286832155381798 0.13563332965520566
+ 0.13843161503245183 0.14126329114027164 0.14412847085805777 0.14702726649759498
+ 0.14995978981060856 0.15292615199615017 0.1559264637078274 0.1589608350608804
+ 0.162029375639111 0.1651321945016676 0.16826940018969075 0.1714411007328226
+ 0.17464740365558504 0.17788841598362912 0.18116424424986022 0.184474994500441
+ 0.18782077230067787 0.19120168274079138 0.1946178304415758 0.19806931955994886
+ 0.20155625379439707 0.20507873639031693 0.20863687014525575 0.21223075741405523
+ 0.21586050011389926 0.2195261997292692 0.2232279573168085 0.22696587351009836
+ 0.23074004852434915 0.23455058216100522 0.238397573812271 0.24228112246555486
+ 0.24620132670783548 0.25015828472995344 0.25415209433082675 0.2581828529215958
+ 0.26225065752969623 0.26635560480286247 0.2704977910130658 0.27467731206038465
+ 0.2788942634768104 0.2831487404299921 0.2874408377269175 0.29177064981753587
+ 0.2961382707983211 0.3005437944157765 0.3049873140698863 0.30946892281750854
+ 0.31398871337571754 0.31854677812509186 0.32314320911295075 0.3277780980565422
+ 0.33245153634617935 0.33716361504833037 0.3419144249086609 0.3467040563550296
+ 0.35153259950043936 0.3564001441459435 0.3613067797835095 0.3662525955988395
+ 0.3712376804741491 0.3762621229909065 0.38132601143253014 0.386429433787049
+ 0.39157247774972326 0.39675523072562685 0.4019777798321958 0.4072402119017367
+ 0.41254261348390375 0.4178850708481375 0.4232676699860717 0.4286904966139066
+ 0.43415363617474895 0.4396571738409188 0.44520119451622786 0.45078578283822346
+ 0.45641102318040466 0.4620769996544071 0.467783796112159 0.47353149614800955
+ 0.4793201831008268 0.4851499400560704 0.4910208498478356 0.4969329950608704
+ 0.5028864580325687 0.5088813208549338 0.5149176653765214 0.5209955732043543
+ 0.5271151257058131 0.5332764040105052 0.5394794890121072 0.5457244613701866
+ 0.5520114015120001 0.5583403896342679 0.5647115057049292 0.5711248294648731
+ 0.5775804404296506 0.5840784178911641 0.5906188409193369 0.5972017883637634
+ 0.6038273388553378 0.6104955708078648 0.6172065624196511 0.6239603916750761
+ 0.6307571363461468 0.6375968739940326 0.6444796819705821 0.6514056374198242
+ 0.6583748172794485 0.665387298282272 0.6724431569576875 0.6795424696330938
+ 0.6866853124353135 0.6938717612919899 0.7011018919329731 0.7083757798916868
+ 0.7156935005064807 0.7230551289219693 0.7304607400903537 0.7379104087727308
+ 0.7454042095403874 0.7529422167760779 0.7605245046752924 0.768151147247507
+ 0.7758222183174236 0.7835377915261935 0.7912979403326302 0.799102738014409
+ 0.8069522576692516 0.8148465722161012 0.8227857543962835 0.8307698767746546
+ 0.83879901174074 0.846873231509858 0.8549926081242338 0.8631572134541023
+ 0.8713671191987972 0.8796223968878317 0.8879231178819663 0.8962693533742664
+ 0.9046611743911496 0.9130986517934192 0.9215818562772946 0.9301108583754237
+ 0.938685728457888 0.9473065367331999 0.9559733532492861 0.9646862478944651
+ 0.9734452903984125 0.9822505503331171 0.9911020971138298 1;
+/**
+ * Calculate the luminance for a color.
+ * See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
+ */
+@function luminance($color) {
+ $red: nth($linear-channel-values, red($color) + 1);
+ $green: nth($linear-channel-values, green($color) + 1);
+ $blue: nth($linear-channel-values, blue($color) + 1);
+
+ //@return 0.2126 * $red + 0.7152 * $green + 0.0722 * $blue;
+ @return 0.2126 * $red + 0.7152 * $green + 0.0693 * $blue;
+}
+
+/**
+ * Calculate the contrast ratio between two colors.
+ * See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests
+ */
+@function color-contrast($fg, $bg) {
+ $luminance1: luminance($fg) + 0.05;
+ $luminance2: luminance($bg) + 0.05;
+ $ratio: math.div($luminance1, $luminance2);
+ @if $luminance2 > $luminance1 {
+ $ratio: math.div(1, $ratio);
+ }
+ @return $ratio;
+}
+
+// light-or-dark($color)
+// Helper: Use contrast against white or black to determine if a color is "light" or "dark"
+// Adapted from: https://medium.com/dev-channel/using-sass-to-automatically-pick-text-colors-4ba7645d2796
+// To be used in other functions or mixins — creates non-standard CSS output:
+// Usage:
+// .sample { light-or-dark: light-or-dark(#c00); }
+// Output:
+// .sample { light-or-dark: "light"; }
+//
+@function light-or-dark($color) {
+ $light-contrast: color-contrast($color, #fff);
+ $dark-contrast: color-contrast($color, #000);
+
+ @if $light-contrast > $dark-contrast {
+ // Contrast against white is higher than against black, so, this is a dark color
+ @return 'dark';
+ } @else {
+ @return 'light';
+ }
+}
+
+// most-legible-color($color)
+// Helper: In some cases, we simply want to use black or white text over a color, whichever is more appropriate
+// Usage:
+// .sample {
+// background-color: #c00;
+// color: most-legible-color(#c00);
+// }
+// Output:
+// .sample {
+// background-color: #c00;
+// color: #fff;
+// }
+@function most-legible-color($color) {
+ $color-lod: light-or-dark($color);
+
+ @if ($color-lod == 'dark') {
+ @return #fff;
+ } @else {
+ @return #000;
+ }
+}
+
+// get-ratio($level: 'AA', $size: 16, $bold: false, $graphic: false)
+// Helper: Determine the correct ratio value to use based on font-size and WCAG Level
+// To be used in other functions or mixins — creates non-standard CSS output:
+// Usage:
+// .sample { get-ratio: get-ratio('AAA', 19, true); }
+// Output:
+// .sample { get-ratio: 4.5; }
+//
+@function get-ratio($level: 'AA', $size: 16, $bold: false, $graphic: false) {
+ // Default ratio
+ $ratio: 4.5;
+ @if $level == 'AAA' {
+ $ratio: 7;
+ }
+
+ // Ratio for graphic and interface components
+ @if $graphic {
+ @return 3;
+ }
+
+ // Make sure the size is valid. If the value is not EM, REM, or PX (preferred), we can't help
+ $size: validate-font-size($size);
+
+ // Check font size
+ @if $size < 24 {
+ // Small text, use defaults
+ // But:
+ @if $size >= 19 and $bold == true {
+ // Special case: Small text but also bold
+ @if $level == 'AAA' {
+ $ratio: 4.5;
+ } @else {
+ $ratio: 3;
+ }
+ }
+ } @else {
+ // Larger than 24
+ $ratio: 3;
+ @if $level == 'AAA' {
+ $ratio: 4.5;
+ }
+ }
+ @return $ratio;
+}
+
+/// Remove the unit of a length
+/// @param {Number} $number - Number to remove unit from
+/// @return {Number} - Unitless number
+@function strip-unit($number) {
+ @if type-of($number) == 'number' and not unitless($number) {
+ @return math.div($number, ($number * 0 + 1));
+ }
+
+ @return $number;
+}
+
+// validate-font-size($size)
+// Helper: Depending on the unit recalculate a font size value into pixels if possible
+// To be used in other functions or mixins — creates non-standard CSS output:
+// Usage:
+// .sample { validate-font-size: validate-font-size(1em); }
+// Output:
+// .sample { validate-font-size: 16; }
+//
+@function validate-font-size($size) {
+ @if unit($size) ==
+ 'em' or
+ unit($size) ==
+ 'rem' or
+ unit($size) ==
+ 'px' or
+ unit($size) ==
+ ''
+ {
+ // Check if a flexible unit
+ @if unit($size) == 'em' or unit($size) == 'rem' {
+ // Need to convert to a pixel value. Let's not overcomplicate it with possible EM inheritence scale factors
+ @return strip-unit($size * 16);
+ }
+ @if unit($size) == 'px' {
+ // We expect PX, so strip the value and return it
+ @return strip-unit($size);
+ }
+ @if unit($size) == '' {
+ @return $size;
+ }
+ } @else {
+ @error 'validate-font-size(): An unexpected font size unit was supplied.';
+ }
+}
+
+// a11y-color($fg, $bg, $level: 'AA', $size: 16, $bold: false, $graphic: false)
+// Goal: Return a color that passes for the chosen WCAG level without changing the Hue of the color
+// Usage:
+// .sample {
+// background-color: #000;
+// color: a11y-color(#c0c, #000);
+// }
+// Output:
+// .sample {
+// background-color: #000;
+// color: #d200d2;
+// }
+//
+@function a11y-color(
+ $fg,
+ $bg,
+ $level: 'AA',
+ $size: 16,
+ $bold: false,
+ $graphic: false,
+ $maxOffset: 1000%,
+ $darkMode: false,
+ $ratio: false,
+ $force: false
+) {
+ // Helper: make sure the font size value is acceptable
+ $font-size: validate-font-size($size);
+ // @debug 'step1' $fg;
+
+ @if not $ratio {
+ // Helper: With the level, font size, and bold boolean, return the proper target ratio. 3.0, 4.5, or 7.0 expected
+ $ratio: get-ratio($level, $font-size, $bold, $graphic);
+ }
+
+ // Calculate the first contrast ratio of the given pair
+ $original-contrast: color-contrast($fg, $bg);
+
+ @if $original-contrast >= $ratio {
+ // If we pass the ratio already, return the original color
+ @return $fg;
+ } @else {
+ // Doesn't pass. Time to get to work
+ // Should the color be lightened or darkened?
+ $fg-lod: light-or-dark($fg);
+ $bg-lod: light-or-dark($bg);
+
+ // Set a "step" value to lighten or darken a color
+ // Note: Higher percentage steps means faster compile time, but we might overstep the required threshold too far with something higher than 5%
+ $step: 2%;
+
+ @if $force == 'lighten' {
+ $step: $step;
+ } @else if $force == 'darken' {
+ $step: -$step;
+ }
+ // Run through some cases where we want to darken, or use a negative step value
+ @else if $fg-lod == 'light' and $bg-lod == 'light' {
+ // Both are light colors, darken the fg
+ $step: -$step;
+ } @else if $fg-lod == 'dark' and $bg-lod == 'light' {
+ // bg is light, fg is dark but does not pass, darken more
+ $step: -$step;
+ }
+ // Keeping the rest of the logic here, but our default values do not change, so this logic is not needed
+ //@else if $fg-lod == 'light' and $bg-lod == 'dark' {
+ // // bg is dark, fg is light but does not pass, lighten further
+ // $step: $step;
+ //} @else if $fg-lod == 'dark' and $bg-lod == 'dark' {
+ // // Both are dark, so lighten the fg
+ // $step: $step;
+ //}
+
+ // The magic happens here
+ // Loop through with a @while statement until the color combination passes our required ratio. Scale the color by our step value until the expression is false
+ // This might loop 100 times or more depending on the colors
+ $offset: 0;
+ @while color-contrast($fg, $bg) < $ratio and $offset < $maxOffset {
+ $fg: scale-color($fg, $lightness: $step, $saturation: math.div($step, 4));
+ $offset: $offset + math.abs($step);
+ //@debug 'step' $step $original-contrast $fg color-contrast($fg, $bg) $offset;
+ }
+
+ @if color-contrast($fg, $bg) < $ratio {
+ $fg: most-legible-color($fg);
+ }
+ @return $fg;
+ }
+}
+
+@function stripped-rgb($color) {
+ @return red($color) + ', ' + green($color) + ', ' + blue($color);
+}
+
+// Replace `$search` with `$replace` in `$string`
+// Used on our SVG icon backgrounds for custom forms.
+//
+// @author Hugo Giraudel
+// @param {String} $string - Initial string
+// @param {String} $search - Substring to replace
+// @param {String} $replace ('') - New value
+// @return {String} - Updated string
+@function str-replace($string, $search, $replace: '') {
+ $index: str-index($string, $search);
+
+ @if $index {
+ @return str-slice($string, 1, $index - 1) + $replace +
+ str-replace(
+ str-slice($string, $index + str-length($search)),
+ $search,
+ $replace
+ );
+ }
+
+ @return $string;
+}
+
+// See https://codepen.io/kevinweber/pen/dXWoRw
+//
+// Requires the use of quotes around data URIs.
+
+@function escape-svg($string) {
+ @if str-index($string, 'data:image/svg+xml') {
+ @each $char, $encoded in $escaped-characters {
+ // Do not escape the url brackets
+ @if str-index($string, 'url(') == 1 {
+ $string: url('#{str-replace(str-slice($string, 6, -3), $char, $encoded)}');
+ } @else {
+ $string: str-replace($string, $char, $encoded);
+ }
+ }
+ }
+
+ @return $string;
+}
+
+// Replace `$search` with `$replace` in `$string`
+// Used on our SVG icon backgrounds for custom forms.
+//
+// @author Hugo Giraudel
+// @param {String} $string - Initial string
+// @param {String} $search - Substring to replace
+// @param {String} $replace ('') - New value
+// @return {String} - Updated string
+@function str-replace($string, $search, $replace: '') {
+ $index: str-index($string, $search);
+
+ @if $index {
+ @return str-slice($string, 1, $index - 1) + $replace +
+ str-replace(
+ str-slice($string, $index + str-length($search)),
+ $search,
+ $replace
+ );
+ }
+
+ @return $string;
+}
+
+@function escape-hex-color($string) {
+ @return str-replace($string, '#', '%23');
+}
+
+@function stripped-hsl($color) {
+ @return hue($color) + ', ' + saturation($color) + ', ' + lightness($color);
+}
+
+@function hsl-map($colors, $prefix: 'hsl-') {
+ // new map
+ $hsl-map: ();
+ // for each defined color
+ @each $name, $color in $colors {
+ $hsl-map: map.merge($hsl-map, ($prefix + $name: stripped-hsl($color)));
+ }
+ @return $hsl-map;
+}
diff --git a/.storybook/themes/swimbird/index.scss b/.storybook/themes/swimbird/index.scss
new file mode 100644
index 0000000..fd4a8da
--- /dev/null
+++ b/.storybook/themes/swimbird/index.scss
@@ -0,0 +1,409 @@
+$font-path: './assets/' !default;
+$background-color-hover: #282828;//rgba(tokens.$text-color-fadable, 0.1);
+$table-border-color: #282828;
+
+@use 'sass:map';
+@use 'buttons/mixins' as button;
+@mixin _add-theme($background: #141414, $color: #ffffff, $code-odd: #1e1e1e) {
+ background: $background;
+ --sb-body-bg: #{$background};
+ --sb-code-even: var(--sb-body-bg);
+ --sb-code-odd: #{$code-odd};
+ --sb-body-color: #{$color};
+ --sb-font-family: Thicccboi, sans-serif;
+ --bs-font-monospace: JetBrainsMono, monospace;
+
+ & {
+ font-family: var(--sb-font-family) !important;
+ }
+
+ pre, code {
+ font-family: var(--bs-font-monospace) !important;
+ font-size: 13px;
+ line-height: 21px;
+ }
+
+ h1,
+ .h1,
+ h2,
+ .h2,
+ h3,
+ .h3 {
+ font-family: inherit !important;
+ font-weight: 700 !important;
+ }
+
+ h2,
+ .h2 {
+ font-size: 2rem;
+ }
+
+ h3,
+ .h3 {
+ font-size: 1.25rem !important;
+ }
+ .table {
+ @include add-table;
+ }
+ .btn {
+ @include button.add-base-button;
+ }
+
+ .btn-sm {
+ @include button.add-small-button-modifier;
+ }
+
+ .primary {
+ @include button.add-primary-button;
+ }
+
+ .secondary {
+ @include button.add-secondary-button;
+ }
+
+ .close {
+ @include button.close;
+ }
+}
+
+@mixin add-table() {
+ width: 100%;
+ border-collapse: collapse;
+ //margin: 0 -0.5rem 1rem -0.5rem;
+ color: var(--sb-body-color);
+ th {
+ text-align: left;
+ &.sort {
+ transition: color 200ms ease-in-out;
+ &:hover {
+ color: var(--link-color);
+ }
+ cursor: pointer;
+ > span {
+ display: inline-flex;
+ align-items: center;
+ padding-right: 0;
+ transition: padding 200ms ease-out;
+ &::after {
+ content: '';
+ width: 0;
+ height: 0;
+ display: block;
+ border-bottom: solid var(--accent-color) 2px;
+ border-left: solid var(--accent-color) 2px;
+ opacity: 0;
+ transform: translate(75%, 2px) rotate3d(0, 0, 1, -45deg)
+ scale3d(-1, -1, 1);
+ transition: transform 400ms, opacity 400ms;
+ }
+ }
+ &.sort-desc > span,
+ &.sort-asc > span {
+ padding-right: 0.75rem;
+ &::after {
+ opacity: 1;
+ width: 8px;
+ height: 8px;
+ }
+ }
+ &.sort-desc > span::after {
+ transform: translate(75%, -2px) rotate3d(0, 0, 1, -45deg)
+ scale3d(1, 1, -1);
+ }
+
+ &.sort-asc > span::after {
+ transform: translate(75%, 2px) rotate3d(0, 0, 1, -45deg)
+ scale3d(-1, -1, 1);
+ }
+ }
+ }
+ tbody {
+ @media screen and (min-width: 576px) {
+ tr.active,
+ tr:hover {
+ background: $background-color-hover;
+ }
+ }
+
+ tr.link {
+ //@include mixins.link-style(td a);
+ cursor: pointer;
+ }
+ tr > td > img {
+ height: 5rem;
+ width: 7.5rem;
+ object-fit: cover;
+ vertical-align: middle;
+ border-radius: 0.25rem;
+ }
+ tr > td .image-placeholder > button {
+ height: 5rem;
+ width: 7.5rem;
+ border-radius: 0.25rem;
+ }
+ tr > td .badge {
+ position: absolute;
+ margin: 1.25rem 2rem;
+ z-index: 1;
+ ~ .image-placeholder .btn {
+ opacity: 0.1;
+ filter: grayscale(1);
+ }
+ ~ img {
+ filter: saturate(0.5);
+ opacity: 0.25;
+ }
+ }
+ }
+ th,
+ td {
+ white-space: nowrap;
+ border-bottom: solid 1px $table-border-color;
+ padding: 0.25rem 0.5rem;
+ }
+
+ .table-no-data {
+ margin: 0.5rem 0;
+ padding: 0.5rem;
+ background: $background-color-hover;
+ text-align: center;
+ }
+}
+
+@mixin add-mobile-table() {
+ width: 100%;
+ @media screen and (max-width: 576px) {
+ thead {
+ top: 50px;
+ border-bottom: 3px solid $table-border-color;
+ font-size: 1rem;
+ }
+ tr {
+ th,
+ td {
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ }
+ }
+ }
+}
+
+// TODO: refactor - pagination button should get active class
+// button states
+$base: (
+ color: red, // tokens.$white,
+ bg: lighten(desaturate(#038a91, 50%), 15%)
+);
+$hover: (
+ color: red, // tokens.$white,
+ border: red, // tokens.$white,
+ bg: darken(desaturate(#038a91, 50%), 5%)
+);
+$active: (
+ color: red, // fade-out(tokens.$white, 0.2),
+ border: red, // fade-out(tokens.$white, 0.2),
+ bg: darken(desaturate(#038a91, 50%), 5%)
+);
+
+@mixin add-table-pagination() {
+ margin: 1rem 0;
+ .btn.btn-link {
+ //@include button.add-secondary-button();
+ //@include button.add-small-button-modifier();
+ }
+ li + li {
+ margin-left: 0.5rem;
+ }
+
+ li {
+ &.active .btn:not([disabled], .disabled) {
+ --background: #{map.get($hover, bg)}; //darken(desaturate(#038a91, 50%), 5%);
+ border-color: map.get($hover, border);
+ &::before {
+ opacity: 1;
+ transform: scale3d(1.5, 2.5, 1);
+ }
+ --color: #{map.get($hover, color)};
+ }
+ &.active .btn:not([disabled], .disabled) {
+ --background: #{map.get($active, bg)}; //darken(desaturate(#038a91, 50%), 5%);
+ border-color: map.get($active, border);
+
+ &::before {
+ opacity: 0.4;
+ }
+ --color: #{map.get($active, color)}; //fade-out($color, 0.2);
+ }
+ }
+}
+
+
+@mixin add-typography() {
+ @font-face {
+ font-family: Thicccboi;
+ src: url('#{$font-path}THICCCBOI-Thin.woff2') format('woff2');
+ font-weight: 100;
+ }
+ @font-face {
+ font-family: Thicccboi;
+ src: url('#{$font-path}THICCCBOI-Light.woff2') format('woff2');
+ font-weight: 200;
+ }
+ @font-face {
+ font-family: Thicccboi;
+ src: url('#{$font-path}THICCCBOI-Regular.woff2') format('woff2');
+ font-weight: 300;
+ }
+ @font-face {
+ font-family: Thicccboi;
+ src: url('#{$font-path}THICCCBOI-Medium.woff2') format('woff2');
+ font-weight: 400;
+ }
+ @font-face {
+ font-family: Thicccboi;
+ src: url('#{$font-path}THICCCBOI-SemiBold.woff2') format('woff2');
+ font-weight: 500;
+ }
+ @font-face {
+ font-family: Thicccboi;
+ src: url('#{$font-path}THICCCBOI-Bold.woff2') format('woff2');
+ font-weight: 600;
+ }
+ @font-face {
+ font-family: Thicccboi;
+ src: url('#{$font-path}THICCCBOI-ExtraBold.woff2') format('woff2');
+ font-weight: 700;
+ }
+ @font-face {
+ font-family: Thicccboi;
+ src: url('#{$font-path}THICCCBOI-Black.woff2') format('woff2');
+ font-weight: 800;
+ }
+ @font-face {
+ font-family: Thicccboi;
+ src: url('#{$font-path}THICCCBOI-ThicccAF.woff2') format('woff2');
+ font-weight: 900;
+ }
+
+ // normal fonts
+
+ @font-face {
+ font-family: JetBrainsMono;
+ font-style: normal;
+ font-weight: 100;
+ src: url('#{$font-path}JetBrainsMono-Thin.woff2') format("woff2");
+ }
+ @font-face {
+ font-family: JetBrainsMono;
+ font-style: normal;
+ font-weight: 200;
+ src: url('#{$font-path}JetBrainsMono-ExtraLight.woff2')
+ format("woff2");
+ }
+ @font-face {
+ font-family: JetBrainsMono;
+ font-style: normal;
+ font-weight: 300;
+ src: url('#{$font-path}JetBrainsMono-Light.woff2') format("woff2");
+ }
+ @font-face {
+ font-family: JetBrainsMono;
+ font-style: normal;
+ font-weight: 400;
+ src: url('#{$font-path}JetBrainsMono-Regular.woff2')
+ format("woff2");
+ }
+ @font-face {
+ font-family: JetBrainsMono;
+ font-style: normal;
+ font-weight: 500;
+ src: url('#{$font-path}JetBrainsMono-Medium.woff2') format("woff2");
+ }
+ @font-face {
+ font-family: JetBrainsMono;
+ font-style: normal;
+ font-weight: 600;
+ src: url('#{$font-path}JetBrainsMono-SemiBold.woff2')
+ format("woff2");
+ }
+ @font-face {
+ font-family: JetBrainsMono;
+ font-style: normal;
+ font-weight: 700;
+ src: url('#{$font-path}JetBrainsMono-Bold.woff2') format("woff2");
+ }
+ @font-face {
+ font-family: JetBrainsMono;
+ font-style: normal;
+ font-weight: 800;
+ src: url('#{$font-path}JetBrainsMono-ExtraBold.woff2')
+ format("woff2");
+ }
+
+ // italic fonts
+
+ @font-face {
+ font-family: JetBrainsMono;
+ font-style: italic;
+ font-weight: 100;
+ src: url('#{$font-path}JetBrainsMono-ThinItalic.woff2')
+ format("woff2");
+ }
+ @font-face {
+ font-family: JetBrainsMono;
+ font-style: italic;
+ font-weight: 200;
+ src: url('#{$font-path}JetBrainsMono-ExtraLightItalic.woff2')
+ format("woff2");
+ }
+ @font-face {
+ font-family: JetBrainsMono;
+ font-style: italic;
+ font-weight: 300;
+ src: url('#{$font-path}JetBrainsMono-LightItalic.woff2')
+ format("woff2");
+ }
+ @font-face {
+ font-family: JetBrainsMono;
+ font-style: italic;
+ font-weight: 400;
+ src: url('#{$font-path}JetBrainsMono-Italic.woff2') format("woff2");
+ }
+ @font-face {
+ font-family: JetBrainsMono;
+ font-style: italic;
+ font-weight: 500;
+ src: url('#{$font-path}JetBrainsMono-MediumItalic.woff2')
+ format("woff2");
+ }
+ @font-face {
+ font-family: JetBrainsMono;
+ font-style: italic;
+ font-weight: 600;
+ src: url('#{$font-path}JetBrainsMono-SemiBoldItalic.woff2')
+ format("woff2");
+ }
+ @font-face {
+ font-family: JetBrainsMono;
+ font-style: italic;
+ font-weight: 700;
+ src: url('#{$font-path}JetBrainsMono-BoldItalic.woff2')
+ format("woff2");
+ }
+ @font-face {
+ font-family: JetBrainsMono;
+ font-style: italic;
+ font-weight: 800;
+ src: url('#{$font-path}JetBrainsMono-ExtraBoldItalic.woff2')
+ format("woff2");
+ }
+}
+
+
+@mixin dark-theme() {
+ @include _add-theme();
+}
+
+@mixin light-theme() {
+ @include _add-theme();
+}
diff --git a/.storybook/themes/swimbird/tokens.scss b/.storybook/themes/swimbird/tokens.scss
new file mode 100644
index 0000000..89fab91
--- /dev/null
+++ b/.storybook/themes/swimbird/tokens.scss
@@ -0,0 +1,111 @@
+@use 'sass:map';
+@use 'colors';
+@forward 'functions';
+@forward 'colors';
+
+// logo
+$logo-path: var(--logo-path);
+$logo-sm-path: var(--logo-sm-path);
+$brand-text: var(--brand-text);
+
+$bg-color: var(--bg-color);
+$bg-border-color: var(--bg-border-color);
+$bg-color-fadable: var(--bg-color-fadable);
+$bg-image: var(--bg-image);
+$bg-color-hover: var(--bg-color-hover);
+
+$text-color-fadable: var(--text-color-fadable);
+
+$label-color: var(--text-color);
+
+// levels
+$level-0-bg: var(--bg-color);
+$level-0-color: var(--level-0-color);
+
+$level-1-bg: var(--level-0-bg-color);
+$level-1-color: var(--level-0-color);
+
+$level-2-bg: var(--level-1-bg-color);
+$level-2-color: var(--level-1-color);
+
+$accent-color: var(--accent-color);
+$accent-color-contrast: var(--accent-color-contrast);
+$link-color: var(--link-color);
+$link-color-hover: var(--link-color-hover);
+
+$label-color: var(--text-color);
+
+$text-color: var(--text-color);
+$text-secondary-color: rgba(var(--text-color-fadable), 0.3);
+$text-positive-color: var(--text-positive-color);
+$text-negative-color: var(--text-negative-color);
+$text-muted-color: var(--text-muted-color);
+
+$heading-color: $text-color;
+$label-color: $label-color;
+
+$hr-color: var(--hr-color);
+$chart-colors: var(--chart-colors);
+$chart-benchmark-color: var(--chart-benchmark);
+
+$shadow-sm: var(--shadow-sm);
+$shadow-md: var(--shadow-md);
+
+$dark: map.get(colors.$grey, 0);
+$light: map.get(colors.$grey, 8);
+
+// table
+$table-border-color: var(--table-border-color);
+
+// skeleton-loader
+$skeleton-loader-highlight-color: var(--skeleton-loader-highlight-color);
+
+// popover
+$popover-bg: var(--level-1-bg-color);
+
+$font-size-lg: 2rem;
+$font-size-md: 1rem;
+$font-size-sm: 0.75rem;
+
+// footer
+$footer-color: var(--footer-color);
+$footer-bg: var(--footer-bg);
+
+// navbar
+$navbar-bg: var(--level-0-bg-color);
+
+// dropdown
+$dropdown-item-color: $label-color;
+$dropdown-item-bg: transparent;
+$dropdown-item-hover-color: $accent-color-contrast;
+$dropdown-item-hover-bg: $accent-color;
+
+$menubar-item-color: $label-color; //black !default;
+$menubar-item-color-hover: $link-color-hover;
+$menubar-item-bg-hover: var(--bg-color-hover);
+$menubar-item-font-weight: 600;
+$menubar-item-accent-color: $accent-color; // #d05378;
+
+$menu-category-font-weight: $menubar-item-font-weight;
+
+$menu-item-color: $link-color;
+$menu-item-color-hover: $link-color-hover;
+$menu-item-bg-hover: transparent;
+$menu-item-font-weight: $menubar-item-font-weight;
+
+$grid-breakpoints: (
+ xs: 0,
+ sm: 576px,
+ md: 768px,
+ lg: 992px,
+ xl: 1200px,
+ xxl: 1400px
+);
+
+// Scrollbar
+$scrollbar-track-bg: $bg-color; //$gray-100;
+$scrollbar-thumb-bg: var(--scroll-thumb-color); //$gray-400;
+$scrollbar-thumb-active-bg: $accent-color;
+$scrollbar-thickness: 10px;
+
+$border-radius: 5px;
diff --git a/README.md b/README.md
index 944d246..92ba040 100644
--- a/README.md
+++ b/README.md
@@ -1,27 +1,84 @@
-# MyWorkspace
+# Angular Generic Table
-This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 11.0.6.
+Generic table version 5 has been rebuilt to be more efficient and to make it easier to customize and add new features. It still relies on native html tables for layout and rendering. Follow the instructions below to get started with the latest release candidate.
-## Development server
-Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
+## Install
-## Code scaffolding
+```
+npm install @angular-generic-table/core@rc --save
+```
-Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
+## Import module
+We recommend import `GenericTableCoreModule` into a shared module eg. `SharedModule` that can be imported into other, preferably lazy loaded modules when needed.
+```ts
+import { GenericTableCoreModule } from '@angular-generic-table/core';
-## Build
+@NgModule({
+ declarations: [...],
+ imports: [
+ ...
+ GenericTableCoreModule
+ ],
+ exports: [
+ GenericTableCoreModule
+ ]
+})
+export class SharedModule {}
+```
-Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
+## Import styling
+We recommend setting up your Angular project to use scss (SASS) for css preprocessing.
-## Running unit tests
+Once configured to use scss, it's just a matter of including the scss to your main styles file, typically it would be `styles.scss` located at the root of the `src` folder unless you've changed it.
-Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
+### Add scss
+`{project}/src/styles.scss`
-## Running end-to-end tests
+```scss
+// import base styles from angular-generic-table/core
+@use '~@angular-generic-table/core/scss' as generic-table-styles;
+@include generic-table-styles.styles(); // all styles
-Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
+// @include generic-table-styles.search-style(); // search (highlight) style
+// @include generic-table-styles.mobile-style(); // mobile layout style
+// @include generic-table-styles.pagination-style(); // pagination styles
+```
-## Further help
-To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
+### Override scss variables
+It's possible to override the scss variables used by generic table by passing them when importing the scss.
+`{project}/src/styles.scss`
+
+```scss
+// import base styles from angular-generic-table/core and override scss variables
+@use '~@angular-generic-table/core/scss' as generic-table-styles with (
+ $highlight-background-color: purple,
+ $mobile-style-max-width: 375px
+);
+@include generic-table-styles.styles();
+```
+
+**SCSS Variables**
+
+|Name|Default value|
+|:--|:--|
+|$highlight-background-color: | #ffdd00; |
+|$mobile-style-selector: | 'table.table-mobile'; |
+|$mobile-style-max-width: | 576px; |
+|$mobile-style-header-font-weight: | 500; |
+|$mobile-style-header-background-color: | #fff; |
+|$mobile-style-button-selector: | '.btn-sm'; |
+|$mobile-style-border-bottom: | solid 1px #dedede; |
+|$mobile-style-button-font-size: | 1rem; |
+|$mobile-style-button-padding: | 0.5625rem 1rem; |
+|$pagination-ellipsis-content: | '...'; |
+|$pagination-active-color: | #000; |
+|$pagination-justify-content: | center; |
+
+### Have other needs?
+More examples and use cases coming soon! In the meantime create an [issue over at github](https://github.com/hjalmers/angular-generic-table/issues)
+
+## Sponsored by
+
+Angular Generic Table is sponsored by [Swimbird](https://www.swimbird.com/) - Portfolio Management.
diff --git a/angular.json b/angular.json
index 02ff386..6d89935 100644
--- a/angular.json
+++ b/angular.json
@@ -21,12 +21,13 @@
}
}
},
- "test": {
- "builder": "@angular-devkit/build-angular:karma",
+ "lint": {
+ "builder": "@angular-eslint/builder:lint",
"options": {
- "main": "projects/core/src/test.ts",
- "tsConfig": "projects/core/tsconfig.spec.json",
- "karmaConfig": "projects/core/karma.conf.js"
+ "lintFilePatterns": [
+ "projects/core/src/**/*.ts",
+ "projects/core/src/**/*.html"
+ ]
}
}
}
@@ -108,28 +109,20 @@
"browserTarget": "docs:build"
}
},
- "test": {
- "builder": "@angular-devkit/build-angular:karma",
+ "storybook": {
+ "builder": "@storybook/angular:start-storybook",
"options": {
- "main": "projects/docs/src/test.ts",
- "polyfills": "projects/docs/src/polyfills.ts",
- "tsConfig": "projects/docs/tsconfig.spec.json",
- "karmaConfig": "projects/docs/karma.conf.js",
- "assets": ["projects/docs/src/favicon.ico", "projects/docs/src/assets"],
- "styles": ["projects/docs/src/styles.scss"],
- "scripts": []
+ "browserTarget": "docs:build",
+ "port": 4400,
+ "styles": [".storybook/scss-loader.scss"]
}
},
- "e2e": {
- "builder": "@angular-devkit/build-angular:protractor",
+ "build-storybook": {
+ "builder": "@storybook/angular:build-storybook",
"options": {
- "protractorConfig": "projects/docs/e2e/protractor.conf.js",
- "devServerTarget": "docs:serve"
- },
- "configurations": {
- "production": {
- "devServerTarget": "docs:serve:production"
- }
+ "browserTarget": "docs:build",
+ "outputDir": "dist/storybook",
+ "styles": [".storybook/scss-loader.scss"]
}
}
}
diff --git a/documentation.json b/documentation.json
index ba1ef1a..5280501 100644
--- a/documentation.json
+++ b/documentation.json
@@ -45,7 +45,7 @@
},
{
"name": "DashCasePipe",
- "id": "pipe-DashCasePipe-4017df98b719591cde460a3f250768183ac280628c3d59cb9f2441f8d1e3376547b6b5aedff3bc8af8b7d97f863f066536eb699f88c8b2b3cdec7c3f350a4f8b",
+ "id": "pipe-DashCasePipe-67844698d38f46a703c8055efff81a2825551f27d8f2c5403c4fb2b9865d5d0daf5bbdf34c9ece4087820abd4e96301019762eeb2dfca2fefd2fa95a6c28a776",
"file": "projects/core/src/lib/pipes/dash-case.pipe.ts",
"type": "pipe",
"deprecated": false,
@@ -84,7 +84,7 @@
}
],
"ngname": "dashCase",
- "sourceCode": "import { Pipe, PipeTransform } from '@angular/core';\nimport { dashed } from '../utilities/utilities';\n\n@Pipe({\n name: 'dashCase'\n})\nexport class DashCasePipe implements PipeTransform {\n transform(s: string): any {\n return dashed(s);\n }\n}\n"
+ "sourceCode": "import { Pipe, PipeTransform } from '@angular/core';\nimport { dashed } from '../utilities/utilities';\n\n@Pipe({\n name: 'dashCase',\n})\nexport class DashCasePipe implements PipeTransform {\n transform(s: string): any {\n return dashed(s);\n }\n}\n"
},
{
"name": "DynamicPipe",
@@ -161,7 +161,7 @@
},
{
"name": "GenderPipe",
- "id": "pipe-GenderPipe-111e4b7be7fd951cf467a92d422596ee0ec208b3ec73a27e69c2d89a7431d1acfed41e359a1da0b97f878fb4a9143d8cb411bcba402270e6bc7c301787328562",
+ "id": "pipe-GenderPipe-2ccdf200e7b7f3afa39e0e6fdca8c7ea0f53af37d1eb3722378cbcaba073ec2e8a214f10e4485e4464b0ea63b3d083009815564424e37fbd8206a83200f4ed92",
"file": "projects/docs/src/app/examples/mobile-layout/mobile-layout.component.ts",
"type": "pipe",
"deprecated": false,
@@ -183,7 +183,7 @@
"optional": false,
"returnType": "string",
"typeParameters": [],
- "line": 13,
+ "line": 23,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
@@ -200,11 +200,11 @@
}
],
"ngname": "genderPipe",
- "sourceCode": "import {Component, Pipe, PipeTransform, TemplateRef, ViewChild, ViewEncapsulation} from '@angular/core';\nimport {Story} from \"@storybook/angular/types-6-0\";\nimport {TableColumn, TableConfig, TableRow} from \"@angular-generic-table/core\";\nimport {BehaviorSubject, Observable} from \"rxjs\";\nimport {map} from \"rxjs/operators\";\nimport {MOBILE_LAYOUT_SNIPPETS} from \"./mobileLayout.snippets\";\n\n\n@Pipe({\n name: 'genderPipe',\n})\nexport class GenderPipe implements PipeTransform {\n transform(gender: 'male' | 'female'): string {\n return gender === 'male' ? '👨' : '👩';\n }\n}\n\n\n@Component({\n selector: 'docs-mobile-layout',\n template: `\n
\n {{clicked}} \n
\n \n \n \n \n \n `,\n styles: [`\n .table th {\n white-space: nowrap;\n }\n `],\n encapsulation: ViewEncapsulation.None\n})\nexport class MobileLayoutComponent {\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n clicked = '';\n\n mobileLayout$ = new BehaviorSubject(true);\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config$: Observable = this.mobileLayout$.pipe(\n map(mobileLayout => ({\n mobileLayout,\n columns: {\n firstName: {\n mobileHeader: true,\n sortable: true\n },\n lastName: {\n mobileHeader: true,\n sortable: true\n },\n gender: {\n mobileHeader: true,\n transform: {\n pipe: GenderPipe\n }\n },\n favoriteFood: {\n mobileHeader: true\n },\n action: {\n mobileHeader: false,\n header: false,\n templateRef: this.actions,\n },\n },\n }))\n );\n\n SNIPPETS = MOBILE_LAYOUT_SNIPPETS;\n\n toggleLayout = (): void => {\n this.mobileLayout$.next(!this.mobileLayout$.getValue());\n }\n clickAction(row: TableRow, column: { key: string; value: TableColumn }, index: number): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = `Clicked row number: ${index}`;\n }\n}\n\nexport const Mobile: Story = (args: MobileLayoutComponent) => ({\n props: args,\n component: MobileLayoutComponent,\n});\n"
+ "sourceCode": "import {\n Component,\n Pipe,\n PipeTransform,\n TemplateRef,\n ViewChild,\n ViewEncapsulation,\n} from '@angular/core';\nimport { Story } from '@storybook/angular/types-6-0';\nimport {\n TableColumn,\n TableConfig,\n TableRow,\n} from '@angular-generic-table/core';\nimport { BehaviorSubject, Observable } from 'rxjs';\nimport { map } from 'rxjs/operators';\nimport { MOBILE_LAYOUT_SNIPPETS } from './mobileLayout.snippets';\n\n@Pipe({\n name: 'genderPipe',\n})\nexport class GenderPipe implements PipeTransform {\n transform(gender: 'male' | 'female'): string {\n return gender === 'male' ? '👨' : '👩';\n }\n}\n\n@Component({\n selector: 'docs-mobile-layout',\n template: `\n \n {{ clicked }}\n \n
\n \n \n \n \n \n `,\n styles: [\n `\n .table th {\n white-space: nowrap;\n }\n `,\n ],\n encapsulation: ViewEncapsulation.None,\n})\nexport class MobileLayoutComponent {\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n clicked = '';\n\n mobileLayout$ = new BehaviorSubject(true);\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config$: Observable = this.mobileLayout$.pipe(\n map((mobileLayout) => ({\n mobileLayout,\n columns: {\n firstName: {\n mobileHeader: true,\n sortable: true,\n },\n lastName: {\n mobileHeader: true,\n sortable: true,\n },\n gender: {\n mobileHeader: true,\n transform: {\n pipe: GenderPipe,\n },\n },\n favoriteFood: {\n mobileHeader: true,\n },\n action: {\n mobileHeader: false,\n header: false,\n templateRef: this.actions,\n },\n },\n }))\n );\n\n SNIPPETS = MOBILE_LAYOUT_SNIPPETS;\n\n toggleLayout = (): void => {\n this.mobileLayout$.next(!this.mobileLayout$.getValue());\n };\n clickAction(\n row: TableRow,\n column: { key: string; value: TableColumn },\n index: number\n ): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = `Clicked row number: ${index}`;\n }\n}\n\nexport const Mobile: Story = (\n args: MobileLayoutComponent\n) => ({\n props: args,\n component: MobileLayoutComponent,\n});\n"
},
{
"name": "HighlightPipe",
- "id": "pipe-HighlightPipe-d185bb2c2498b91f6e3251bb88ffa81628dbe70cbe8e0dc4a5609fd9b0a18e39af77bdb5baa556d0f6da386f68346a900472b8c4f248a760b0c994e72804eaa8",
+ "id": "pipe-HighlightPipe-170b3fde5f924488434a54204079f9bc641bc6b1d553bfc3b0db6b86024c027a9e715dce4cdbc029dc645ae8bcc80db5288a7f91dd34db27fb7015f58690bfc6",
"file": "projects/core/src/lib/pipes/highlight.pipe.ts",
"type": "pipe",
"deprecated": false,
@@ -258,11 +258,11 @@
}
],
"ngname": "highlight",
- "sourceCode": "import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({\n name: 'highlight'\n})\nexport class HighlightPipe implements PipeTransform {\n transform(text: any, searchTerm: string | null): string {\n if (!searchTerm) {\n return text;\n }\n const haystackAlwaysString = text + '';\n let highlightedText = haystackAlwaysString; // fallback\n\n let searchPattern;\n try {\n searchPattern = new RegExp(\n '(' +\n // @ts-ignore\n searchTerm\n .toLowerCase()\n .match(/\".*?\"|[^ ]+/g) // extract words\n .map(\n needle => needle.replace(/\"(.*?)\"/, '$1') // strip away '\"'\n )\n .join('|') + // combine words\n ')',\n 'ig'\n );\n } catch (error) {\n return highlightedText;\n }\n\n const containsTagPattern = /(<.*?>)(.*)(<\\/.*?>)/gi;\n const containsTagMatches = containsTagPattern.exec(haystackAlwaysString);\n\n if (containsTagMatches) {\n // tag exists in haystack\n highlightedText =\n containsTagMatches[1] +\n containsTagMatches[2].replace(searchPattern, '$1') +\n containsTagMatches[3];\n } else {\n highlightedText = haystackAlwaysString.replace(searchPattern, '$1');\n }\n\n return highlightedText;\n }\n}\n"
+ "sourceCode": "import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({\n name: 'highlight',\n})\nexport class HighlightPipe implements PipeTransform {\n transform(text: any, searchTerm: string | null): string {\n if (!searchTerm) {\n return text;\n }\n const haystackAlwaysString = text + '';\n let highlightedText = haystackAlwaysString; // fallback\n\n let searchPattern;\n try {\n searchPattern = new RegExp(\n '(' +\n // @ts-ignore\n searchTerm\n .toLowerCase()\n .match(/\".*?\"|[^ ]+/g) // extract words\n .map(\n (needle) => needle.replace(/\"(.*?)\"/, '$1') // strip away '\"'\n )\n .join('|') + // combine words\n ')',\n 'ig'\n );\n } catch (error) {\n return highlightedText;\n }\n\n const containsTagPattern = /(<.*?>)(.*)(<\\/.*?>)/gi;\n const containsTagMatches = containsTagPattern.exec(haystackAlwaysString);\n\n if (containsTagMatches) {\n // tag exists in haystack\n highlightedText =\n containsTagMatches[1] +\n containsTagMatches[2].replace(\n searchPattern,\n '$1'\n ) +\n containsTagMatches[3];\n } else {\n highlightedText = haystackAlwaysString.replace(\n searchPattern,\n '$1'\n );\n }\n\n return highlightedText;\n }\n}\n"
},
{
"name": "SortClassPipe",
- "id": "pipe-SortClassPipe-a53ebcf3312bb7ead898b3a803cfdde788b6cc165558b4ef475c279f136271ecaf729125c9d24b4f7acded1bf62a8503538e1a2284316507fe28d3b33f2cf3f7",
+ "id": "pipe-SortClassPipe-a55105d02f9568c517c39af2393d1378995a5ccddc579f68199abd1ca1ff9656fbe7ac800e67ed2e8098953ea641b08e67b8f5168c76ef2ba04ca38117e51c7c",
"file": "projects/core/src/lib/pipes/sort-class.pipe.ts",
"type": "pipe",
"deprecated": false,
@@ -285,10 +285,17 @@
"type": "string",
"deprecated": false,
"deprecationMessage": ""
+ },
+ {
+ "name": "aria",
+ "type": "",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "defaultValue": "false"
}
],
"optional": false,
- "returnType": "string",
+ "returnType": "string | null",
"typeParameters": [],
"line": 8,
"deprecated": false,
@@ -311,15 +318,107 @@
"tagName": {
"text": "param"
}
+ },
+ {
+ "name": "aria",
+ "type": "",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "defaultValue": "false",
+ "tagName": {
+ "text": "param"
+ }
}
]
}
],
"ngname": "sortClass",
- "sourceCode": "import { Pipe, PipeTransform } from '@angular/core';\nimport { Order } from '../enums/order.enum';\n\n@Pipe({\n name: 'sortClass',\n})\nexport class SortClassPipe implements PipeTransform {\n transform(selection: { sortBy: string; sortByOrder: Order } | any, property: string): string {\n return selection?.sortBy === property ? 'sort-' + selection.sortByOrder : '';\n }\n}\n"
+ "sourceCode": "import { Pipe, PipeTransform } from '@angular/core';\nimport { Order } from '../enums/order.enum';\n\n@Pipe({\n name: 'sortClass',\n})\nexport class SortClassPipe implements PipeTransform {\n transform(\n selection: { sortBy: string; sortByOrder: Order } | any,\n property: string,\n aria = false\n ): string | null {\n return selection?.sortBy === property\n ? !aria\n ? 'gt-sort-' + selection.sortByOrder\n : selection.sortByOrder + 'ending'\n : !aria\n ? ''\n : null;\n }\n}\n"
}
],
"interfaces": [
+ {
+ "name": "GtPaginationAriaLabels",
+ "id": "interface-GtPaginationAriaLabels-b61886aff7ec6266e2fecc3c4d070bf12d450c8547655bfc4fd7bd41551f50cc5f3a7db5503444c874878cd722de11168ea1d52dcd9ec8bb741183c7431eda7c",
+ "file": "projects/core/src/lib/models/gt-pagination.ts",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "interface",
+ "sourceCode": "export interface GtPaginationClasses {\n nav?: string;\n ul?: string;\n li?: string;\n button?: string;\n}\n\nexport interface GtPaginationAriaLabels {\n nav: string;\n button: string;\n}\n",
+ "properties": [
+ {
+ "name": "button",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "string",
+ "optional": false,
+ "description": "",
+ "line": 10
+ },
+ {
+ "name": "nav",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "string",
+ "optional": false,
+ "description": "",
+ "line": 9
+ }
+ ],
+ "indexSignatures": [],
+ "kind": 165,
+ "methods": []
+ },
+ {
+ "name": "GtPaginationClasses",
+ "id": "interface-GtPaginationClasses-b61886aff7ec6266e2fecc3c4d070bf12d450c8547655bfc4fd7bd41551f50cc5f3a7db5503444c874878cd722de11168ea1d52dcd9ec8bb741183c7431eda7c",
+ "file": "projects/core/src/lib/models/gt-pagination.ts",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "interface",
+ "sourceCode": "export interface GtPaginationClasses {\n nav?: string;\n ul?: string;\n li?: string;\n button?: string;\n}\n\nexport interface GtPaginationAriaLabels {\n nav: string;\n button: string;\n}\n",
+ "properties": [
+ {
+ "name": "button",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "string",
+ "optional": true,
+ "description": "",
+ "line": 5
+ },
+ {
+ "name": "li",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "string",
+ "optional": true,
+ "description": "",
+ "line": 4
+ },
+ {
+ "name": "nav",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "string",
+ "optional": true,
+ "description": "",
+ "line": 2
+ },
+ {
+ "name": "ul",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "string",
+ "optional": true,
+ "description": "",
+ "line": 3
+ }
+ ],
+ "indexSignatures": [],
+ "kind": 165,
+ "methods": []
+ },
{
"name": "TableColumn",
"id": "interface-TableColumn-a65576dcb9f7d8edc456fb2f7fa6d5940986eee2c3514b61483ea402b172603d4da7f8a5731ce48383796274c45f570cf5a71396249baa6cefa8fc7d52523278",
@@ -426,12 +525,12 @@
},
{
"name": "TableConfig",
- "id": "interface-TableConfig-6534ef0eb4a666473333a27b8065655b36de439b20774419736946cfe083cbfbd156b9029ab88e2d9fcca4a7294ff2a66524544321b970108ef1bc19d13c4308",
+ "id": "interface-TableConfig-32524bf68aaea97195a74f13e2142d2eb9f08760bed89d291988324e5ee4a60a498de31b5af9c955929266d01e94f9e9da93e53c9f9b24ab02143b5585ce41e0",
"file": "projects/core/src/lib/models/table-config.interface.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
- "sourceCode": "import { TableColumn } from './table-column.interface';\n\nexport interface TableConfig {\n hidden?: boolean;\n mobileLayout?: boolean;\n class?: string;\n rows?: {\n [key: string]: TableColumn;\n };\n columns?: {\n [key: string]: TableColumn;\n };\n pagination?: {\n length?: number;\n };\n}\n",
+ "sourceCode": "import { TableColumn } from './table-column.interface';\n\nexport interface TableConfig {\n hidden?: boolean;\n mobileLayout?: boolean;\n stickyHeaders?: {\n row?: boolean;\n column?: boolean;\n };\n class?: string;\n rows?: {\n [key: string]: TableColumn;\n };\n columns?: {\n [key: string]: TableColumn;\n };\n pagination?: {\n length?: number;\n };\n}\n",
"properties": [
{
"name": "class",
@@ -440,7 +539,7 @@
"type": "string",
"optional": true,
"description": "",
- "line": 6
+ "line": 10
},
{
"name": "columns",
@@ -449,7 +548,7 @@
"type": "literal type",
"optional": true,
"description": "",
- "line": 10
+ "line": 14
},
{
"name": "hidden",
@@ -476,7 +575,7 @@
"type": "literal type",
"optional": true,
"description": "",
- "line": 13
+ "line": 17
},
{
"name": "rows",
@@ -485,7 +584,16 @@
"type": "literal type",
"optional": true,
"description": "",
- "line": 7
+ "line": 11
+ },
+ {
+ "name": "stickyHeaders",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "literal type",
+ "optional": true,
+ "description": "",
+ "line": 6
}
],
"indexSignatures": [],
@@ -630,7 +738,7 @@
"injectables": [
{
"name": "CoreService",
- "id": "injectable-CoreService-2e409702024a2b887f15a8cd19af33f959c7625cae438df9b419f13003dc36e8faec3fc4efa935a7c4aa55ab5635e1062f115d8be6c6dd2b10b5d851382c810b",
+ "id": "injectable-CoreService-aa6c97a9fa8aaeb04ef59acbab00bca3734e79841c357f3bf212f24c045414c295ec1cd3b16188e237ce977a4ffec26961195586d7de5f10277a1cb0f8bf2896",
"file": "projects/core/src/lib/core.service.ts",
"properties": [],
"methods": [],
@@ -638,7 +746,7 @@
"deprecationMessage": "",
"description": "",
"rawdescription": "\n",
- "sourceCode": "import { Injectable } from '@angular/core';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class CoreService {\n constructor() {}\n}\n",
+ "sourceCode": "import { Injectable } from '@angular/core';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class CoreService {\n constructor() {}\n}\n",
"constructorObj": {
"name": "constructor",
"description": "",
@@ -657,7 +765,7 @@
"components": [
{
"name": "AdvancedComponent",
- "id": "component-AdvancedComponent-08b75b2ba2da34adc9176ef5fffbd79f68876d561626ab61db62d34e13f51675d13dc790e233983b67442a985e3ad7b6b42ae90b34ae41664674f38f45852909",
+ "id": "component-AdvancedComponent-89ff734ac3a472f0ccb2b658902b20b84fe1d1d33c8885b4ee7c7b56355553499c3414f9529410b0b4bdb7565753c1682761468dbc0411d0e67df780f83c67c2",
"file": "projects/docs/src/app/examples/advanced/advanced.component.ts",
"encapsulation": [],
"entryComponents": [],
@@ -682,7 +790,7 @@
"type": "",
"optional": false,
"description": "",
- "line": 48,
+ "line": 53,
"modifierKind": [
121
]
@@ -694,7 +802,7 @@
"type": "TemplateRef | undefined",
"optional": false,
"description": "",
- "line": 23,
+ "line": 27,
"decorators": [
{
"name": "ViewChild",
@@ -710,7 +818,7 @@
"type": "string",
"optional": false,
"description": "",
- "line": 50
+ "line": 55
},
{
"name": "color",
@@ -719,7 +827,7 @@
"type": "TemplateRef | undefined",
"optional": false,
"description": "",
- "line": 24,
+ "line": 28,
"decorators": [
{
"name": "ViewChild",
@@ -735,7 +843,7 @@
"type": "[]",
"optional": false,
"description": "",
- "line": 55
+ "line": 76
},
{
"name": "data$",
@@ -745,7 +853,7 @@
"type": "BehaviorSubject",
"optional": false,
"description": "",
- "line": 31
+ "line": 36
},
{
"name": "femaleFirstNames",
@@ -755,27 +863,27 @@
"type": "[]",
"optional": false,
"description": "",
- "line": 52
+ "line": 57
},
{
"name": "foods",
- "defaultValue": "['Pizza', 'Pasta', 'Hamburger', 'Pancakes', 'Tacos', 'Lasagna', 'Meatloaf']",
+ "defaultValue": "[\n 'Pizza',\n 'Pasta',\n 'Hamburger',\n 'Pancakes',\n 'Tacos',\n 'Lasagna',\n 'Meatloaf',\n ]",
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
"optional": false,
"description": "",
- "line": 54
+ "line": 67
},
{
"name": "lastNames",
- "defaultValue": "['Andersson', 'Smith', 'Parker', 'Kent', 'Rogers', 'Lane', 'Jackson']",
+ "defaultValue": "[\n 'Andersson',\n 'Smith',\n 'Parker',\n 'Kent',\n 'Rogers',\n 'Lane',\n 'Jackson',\n ]",
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
"optional": false,
"description": "",
- "line": 53
+ "line": 58
},
{
"name": "loading$",
@@ -785,7 +893,7 @@
"type": "",
"optional": false,
"description": "",
- "line": 30
+ "line": 35
},
{
"name": "maleFirstNames",
@@ -795,7 +903,7 @@
"type": "[]",
"optional": false,
"description": "",
- "line": 51
+ "line": 56
},
{
"name": "next",
@@ -805,7 +913,7 @@
"type": "",
"optional": false,
"description": "",
- "line": 95
+ "line": 126
},
{
"name": "paginationForm",
@@ -815,7 +923,7 @@
"type": "",
"optional": false,
"description": "",
- "line": 25
+ "line": 29
},
{
"name": "prev",
@@ -825,17 +933,17 @@
"type": "",
"optional": false,
"description": "",
- "line": 98
+ "line": 129
},
{
"name": "search$",
- "defaultValue": "this.paginationForm.get('search')?.valueChanges as Observable",
+ "defaultValue": "this.paginationForm.get('search')\n ?.valueChanges as Observable",
"deprecated": false,
"deprecationMessage": "",
"type": "",
"optional": false,
"description": "",
- "line": 29
+ "line": 33
},
{
"name": "SNIPPETS",
@@ -845,7 +953,7 @@
"type": "",
"optional": false,
"description": "",
- "line": 58
+ "line": 79
},
{
"name": "tableConfig$",
@@ -855,7 +963,7 @@
"type": "ReplaySubject",
"optional": false,
"description": "",
- "line": 57
+ "line": 78
}
],
"methodsClass": [
@@ -865,7 +973,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 60,
+ "line": 81,
"deprecated": false,
"deprecationMessage": ""
},
@@ -894,7 +1002,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 74,
+ "line": 95,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
@@ -933,7 +1041,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 102,
+ "line": 133,
"deprecated": false,
"deprecationMessage": ""
},
@@ -943,7 +1051,7 @@
"optional": false,
"returnType": "TableRow",
"typeParameters": [],
- "line": 79,
+ "line": 104,
"deprecated": false,
"deprecationMessage": ""
},
@@ -953,7 +1061,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 64,
+ "line": 85,
"deprecated": false,
"deprecationMessage": ""
},
@@ -963,7 +1071,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 68,
+ "line": 89,
"deprecated": false,
"deprecationMessage": ""
}
@@ -975,7 +1083,7 @@
"description": "",
"rawdescription": "\n",
"type": "component",
- "sourceCode": "import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { FormBuilder } from '@angular/forms';\nimport { TableConfig, TableRow, TableColumn } from '@angular-generic-table/core';\nimport { withLatestFrom } from 'rxjs/operators';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { ADVANCED_DOCS } from './advanced.snippets';\n\n@Component({\n selector: 'docs-advanced',\n templateUrl: './advanced.component.html',\n styles: [],\n})\nexport class AdvancedComponent implements OnInit {\n get currentPage$(): Observable {\n return this._currentPage$.asObservable();\n }\n\n set currentPage(value: number) {\n this._currentPage$.next(value);\n }\n constructor(private fb: FormBuilder) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n length: [10],\n search: [''],\n });\n search$ = this.paginationForm.get('search')?.valueChanges as Observable;\n loading$ = new BehaviorSubject(true);\n data$: BehaviorSubject = new BehaviorSubject([\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteColor: '#26BFAF',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteColor: '#0f0',\n favoriteFood: 'Pizza',\n },\n ]);\n\n private _currentPage$ = new BehaviorSubject(0);\n\n clicked: string = '';\n maleFirstNames = ['Peter', 'Clark', 'Ruben', 'John', 'Jack', 'Roscoe'];\n femaleFirstNames = ['Mary Jane', 'Kim', 'Sarah', 'Michelle', 'Ann'];\n lastNames = ['Andersson', 'Smith', 'Parker', 'Kent', 'Rogers', 'Lane', 'Jackson'];\n foods = ['Pizza', 'Pasta', 'Hamburger', 'Pancakes', 'Tacos', 'Lasagna', 'Meatloaf'];\n colors = ['#33d60b', '#dcafff', '#3fc9ff', '#ff1600', '#5238b1', '#fff'];\n\n tableConfig$: ReplaySubject = new ReplaySubject(1);\n SNIPPETS = ADVANCED_DOCS;\n\n addData(): void {\n this.data$.next([...this.data$.getValue(), this.randomRecord()]);\n }\n\n removeData(): void {\n this.data$.next([]);\n }\n\n simulateLoad(): void {\n this.loading$.next(true);\n // set loading state to false after 2 seconds\n setTimeout(() => this.loading$.next(false), 2000);\n }\n\n clickAction(row: TableRow, column: { key: string; value: TableColumn }, index: number): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = `clicked row number: ${index}`;\n }\n\n randomRecord(): TableRow {\n const random = Math.floor(Math.random() * 2);\n const newRecord = {\n firstName: random\n ? this.maleFirstNames[Math.floor(Math.random() * this.maleFirstNames.length)]\n : this.femaleFirstNames[Math.floor(Math.random() * this.femaleFirstNames.length)],\n lastName: this.lastNames[Math.floor(Math.random() * this.lastNames.length)],\n gender: random ? 'male' : 'female',\n favoriteColor: this.colors[Math.floor(Math.random() * this.colors.length)],\n favoriteFood: this.foods[Math.floor(Math.random() * this.foods.length)],\n };\n console.log('added new random record:', newRecord);\n\n return newRecord;\n }\n\n next = () => {\n this.currentPage = this._currentPage$.value + 1;\n };\n prev = () => {\n this.currentPage = this._currentPage$.value - 1;\n };\n\n ngOnInit(): void {\n this.simulateLoad();\n this.paginationForm\n .get('length')\n ?.valueChanges.pipe(withLatestFrom(this.tableConfig$))\n .subscribe(([length, config]) => {\n length = +length;\n this.tableConfig$.next({\n ...config,\n pagination: { ...config.pagination, length },\n });\n });\n this.tableConfig$.next({\n class: 'table table-mobile text-nowrap mb-0',\n columns: {\n firstName: {\n header: 'First name',\n mobileHeader: true,\n sortable: true,\n order: 0,\n },\n lastName: {\n header: 'Last name',\n mobileHeader: true,\n hidden: false,\n sortable: true,\n },\n gender: {\n mobileHeader: 'Sex',\n sortable: true,\n order: 1,\n },\n favoriteColor: {\n header: 'Favorite color',\n mobileHeader: true,\n templateRef: this.color,\n sortable: false,\n order: 2,\n search: false,\n class: 'custom-class',\n },\n favoriteFood: {\n mobileHeader: true,\n header: 'Favorite food',\n hidden: false,\n sortable: true,\n order: 0,\n },\n action: {\n mobileHeader: false,\n header: false,\n templateRef: this.actions,\n order: 6,\n },\n },\n pagination: {\n length: this.paginationForm.get('length')?.value || 0,\n },\n });\n }\n}\n\nexport const Advanced: Story = (args: AdvancedComponent) => ({\n props: args,\n component: AdvancedComponent,\n});\n",
+ "sourceCode": "import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { FormBuilder } from '@angular/forms';\nimport {\n TableConfig,\n TableRow,\n TableColumn,\n} from '@angular-generic-table/core';\nimport { withLatestFrom } from 'rxjs/operators';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { ADVANCED_DOCS } from './advanced.snippets';\n\n@Component({\n selector: 'docs-advanced',\n templateUrl: './advanced.component.html',\n styles: [],\n})\nexport class AdvancedComponent implements OnInit {\n get currentPage$(): Observable {\n return this._currentPage$.asObservable();\n }\n\n set currentPage(value: number) {\n this._currentPage$.next(value);\n }\n constructor(private fb: FormBuilder) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n length: [10],\n search: [''],\n });\n search$ = this.paginationForm.get('search')\n ?.valueChanges as Observable;\n loading$ = new BehaviorSubject(true);\n data$: BehaviorSubject = new BehaviorSubject([\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteColor: '#26BFAF',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteColor: '#0f0',\n favoriteFood: 'Pizza',\n },\n ]);\n\n private _currentPage$ = new BehaviorSubject(0);\n\n clicked: string = '';\n maleFirstNames = ['Peter', 'Clark', 'Ruben', 'John', 'Jack', 'Roscoe'];\n femaleFirstNames = ['Mary Jane', 'Kim', 'Sarah', 'Michelle', 'Ann'];\n lastNames = [\n 'Andersson',\n 'Smith',\n 'Parker',\n 'Kent',\n 'Rogers',\n 'Lane',\n 'Jackson',\n ];\n foods = [\n 'Pizza',\n 'Pasta',\n 'Hamburger',\n 'Pancakes',\n 'Tacos',\n 'Lasagna',\n 'Meatloaf',\n ];\n colors = ['#33d60b', '#dcafff', '#3fc9ff', '#ff1600', '#5238b1', '#fff'];\n\n tableConfig$: ReplaySubject = new ReplaySubject(1);\n SNIPPETS = ADVANCED_DOCS;\n\n addData(): void {\n this.data$.next([...this.data$.getValue(), this.randomRecord()]);\n }\n\n removeData(): void {\n this.data$.next([]);\n }\n\n simulateLoad(): void {\n this.loading$.next(true);\n // set loading state to false after 2 seconds\n setTimeout(() => this.loading$.next(false), 2000);\n }\n\n clickAction(\n row: TableRow,\n column: { key: string; value: TableColumn },\n index: number\n ): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = `clicked row number: ${index}`;\n }\n\n randomRecord(): TableRow {\n const random = Math.floor(Math.random() * 2);\n const newRecord = {\n firstName: random\n ? this.maleFirstNames[\n Math.floor(Math.random() * this.maleFirstNames.length)\n ]\n : this.femaleFirstNames[\n Math.floor(Math.random() * this.femaleFirstNames.length)\n ],\n lastName:\n this.lastNames[Math.floor(Math.random() * this.lastNames.length)],\n gender: random ? 'male' : 'female',\n favoriteColor:\n this.colors[Math.floor(Math.random() * this.colors.length)],\n favoriteFood: this.foods[Math.floor(Math.random() * this.foods.length)],\n };\n console.log('added new random record:', newRecord);\n\n return newRecord;\n }\n\n next = () => {\n this.currentPage = this._currentPage$.value + 1;\n };\n prev = () => {\n this.currentPage = this._currentPage$.value - 1;\n };\n\n ngOnInit(): void {\n this.simulateLoad();\n this.paginationForm\n .get('length')\n ?.valueChanges.pipe(withLatestFrom(this.tableConfig$))\n .subscribe(([length, config]) => {\n length = +length;\n this.tableConfig$.next({\n ...config,\n pagination: { ...config.pagination, length },\n });\n });\n this.tableConfig$.next({\n class: 'table text-nowrap mb-0',\n mobileLayout: true,\n columns: {\n firstName: {\n header: 'First name',\n mobileHeader: true,\n sortable: true,\n order: 0,\n },\n lastName: {\n header: 'Last name',\n mobileHeader: true,\n hidden: false,\n sortable: true,\n },\n gender: {\n mobileHeader: 'Sex',\n sortable: true,\n order: 1,\n },\n favoriteColor: {\n header: 'Favorite color',\n mobileHeader: true,\n templateRef: this.color,\n sortable: false,\n order: 2,\n search: false,\n class: 'custom-class',\n },\n favoriteFood: {\n mobileHeader: true,\n header: 'Favorite food',\n hidden: false,\n sortable: true,\n order: 0,\n },\n action: {\n mobileHeader: false,\n header: false,\n templateRef: this.actions,\n order: 6,\n class: 'py-1 text-end',\n },\n },\n pagination: {\n length: this.paginationForm.get('length')?.value || 0,\n },\n });\n }\n}\n\nexport const Advanced: Story = (\n args: AdvancedComponent\n) => ({\n props: args,\n component: AdvancedComponent,\n});\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
@@ -992,7 +1100,7 @@
"deprecationMessage": ""
}
],
- "line": 21,
+ "line": 25,
"jsdoctags": [
{
"name": "fb",
@@ -1015,7 +1123,7 @@
"name": "currentPage$",
"type": "",
"returnType": "Observable",
- "line": 15
+ "line": 19
}
},
"currentPage": {
@@ -1034,7 +1142,7 @@
}
],
"returnType": "void",
- "line": 19,
+ "line": 23,
"jsdoctags": [
{
"name": "value",
@@ -1049,7 +1157,7 @@
}
}
},
- "templateData": "\n\n\n
\n \n \n Table is empty\n
\n \n
\n\n
\n \n
\n
\n \n
\n
Current page: {{pagination.current +1}}
\n
Total pages: {{pagination.total}}
\n
\n Records: {{(data$ | async).length}}\n
\n
\n {{clicked}}\n
\n
\n\n \n\n\n \n\n\n"
+ "templateData": "\n
\n \n
\n
\n \n
\n
\n \n
\n
\n\n\n\n
\n \n
\n
\n \n
\n
Current page: {{ pagination.current + 1 }}
\n
Total pages: {{ pagination.total }}
\n
Records: {{ (data$ | async).length }}
\n
\n {{ clicked }}\n
\n
\n\n \n\n\n \n\n\n"
},
{
"name": "AppComponent",
@@ -1443,11 +1551,11 @@
}
}
},
- "templateData": "\n\n"
+ "templateData": "\n\n"
},
{
"name": "CoreComponent",
- "id": "component-CoreComponent-4daf7b76a76f4e7766237b09cd3b5e0285951ef0ae64e967e9361f455c0d3df06f41d268334111b251b5c7a3dddff06e9ea3e8240a5c6e152d37d2f59b6ab314",
+ "id": "component-CoreComponent-be078ad00c8399508b5b1def0abf650b2cd1b5df6b58c4bba5fa185afbbe03772d9cd2b6cff637bad5e5958433aa6a455cdedbb878713d828a76625d5002d6f8",
"file": "projects/core/src/lib/core.component.ts",
"changeDetection": "ChangeDetectionStrategy.OnPush",
"encapsulation": [],
@@ -1467,31 +1575,31 @@
"name": "config",
"deprecated": false,
"deprecationMessage": "",
- "line": 34,
- "type": "",
+ "line": 49,
+ "type": "Observable | TableConfig",
"decorators": []
},
{
"name": "data",
"deprecated": false,
"deprecationMessage": "",
- "line": 39,
- "type": "",
+ "line": 54,
+ "type": "Observable | Array",
"decorators": []
},
{
"name": "loading",
"deprecated": false,
"deprecationMessage": "",
- "line": 20,
- "type": "",
+ "line": 35,
+ "type": "Observable | boolean",
"decorators": []
},
{
"name": "page",
"deprecated": false,
"deprecationMessage": "",
- "line": 24,
+ "line": 39,
"type": "number",
"decorators": []
},
@@ -1499,8 +1607,8 @@
"name": "search",
"deprecated": false,
"deprecationMessage": "",
- "line": 29,
- "type": "",
+ "line": 44,
+ "type": "Observable | string | null",
"decorators": []
}
],
@@ -1514,7 +1622,7 @@
"type": "BehaviorSubject",
"optional": false,
"description": "",
- "line": 142,
+ "line": 190,
"modifierKind": [
121
]
@@ -1527,7 +1635,7 @@
"type": "ReplaySubject",
"optional": false,
"description": "",
- "line": 74,
+ "line": 92,
"modifierKind": [
121
]
@@ -1540,7 +1648,7 @@
"type": "ReplaySubject",
"optional": false,
"description": "",
- "line": 52,
+ "line": 67,
"modifierKind": [
121
]
@@ -1553,7 +1661,7 @@
"type": "ReplaySubject",
"optional": false,
"description": "",
- "line": 58,
+ "line": 74,
"modifierKind": [
121
]
@@ -1565,7 +1673,7 @@
"type": "TableSort | undefined",
"optional": false,
"description": "",
- "line": 55,
+ "line": 71,
"modifierKind": [
121
]
@@ -1578,20 +1686,20 @@
"type": "ReplaySubject",
"optional": false,
"description": "",
- "line": 67,
+ "line": 84,
"modifierKind": [
121
]
},
{
"name": "colspan$",
- "defaultValue": "this.tableConfig$.pipe(\n switchMap((config) =>\n config.columns\n ? of(Object.values(config.columns || config.rows || {}).filter((value) => value.hidden !== true).length)\n : this.data$.pipe(map((data) => data.length + 1))\n )\n )",
+ "defaultValue": "this.tableConfig$.pipe(\n switchMap((config) =>\n config.columns\n ? of(\n Object.values(config.columns || config.rows || {}).filter(\n (value) => value.hidden !== true\n ).length\n )\n : this.data$.pipe(map((data) => data.length + 1))\n )\n )",
"deprecated": false,
"deprecationMessage": "",
"type": "",
"optional": false,
"description": "",
- "line": 153
+ "line": 205
},
{
"name": "columnOrder",
@@ -1601,27 +1709,27 @@
"type": "",
"optional": false,
"description": "",
- "line": 174
+ "line": 232
},
{
"name": "currentPage$",
- "defaultValue": "combineLatest([this._currentPage$, this.table$]).pipe(\n map(([page, table]: any) => {\n // determine last page\n const lastPage = Math.ceil(table.info.records / (table.config?.pagination?.length || table.info.records)) - 1;\n // determine max/min position\n return +page < 0 ? 0 : +page > lastPage ? lastPage : +page;\n }),\n shareReplay(1)\n )",
+ "defaultValue": "combineLatest([this._currentPage$, this.table$]).pipe(\n map(([page, table]: any) => {\n // determine last page\n const lastPage =\n Math.ceil(\n table.info.records /\n (table.config?.pagination?.length || table.info.records)\n ) - 1;\n // determine max/min position\n return +page < 0 ? 0 : +page > lastPage ? lastPage : +page;\n }),\n shareReplay(1)\n )",
"deprecated": false,
"deprecationMessage": "",
"type": "",
"optional": false,
"description": "",
- "line": 143
+ "line": 191
},
{
"name": "data$",
- "defaultValue": "this._data$.pipe(\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => combineLatest([obs])),\n withLatestFrom(this.tableConfig$),\n map(([[data], config]) => {\n // if columns or rows contains config for mapTo...\n if (config.columns && !!Object.values(config.columns).find(column => !!column.mapTo) ||\n (config.rows && !!Object.values(config.rows).find(column => !!column.mapTo))) {\n // ...map data to new keys on row...\n data = data.map(row => {\n const newKeys = Object.entries(config.columns || config.rows || [])\n .filter(([key, value]) => !!value.mapTo) // add keys for columns with mapTo config...\n .reduce((previousValue, currentValue) => ({\n ...previousValue,\n // tslint:disable-next-line:no-non-null-assertion\n [currentValue[0]]: this.nestedValue(row, currentValue[1].mapTo!.path, currentValue[1].mapTo?.missingValue)\n }), {});\n return {...row, ...newKeys}\n });\n }\n return {data, config}\n }),\n switchMap(obs => combineLatest([of(obs), this.sortBy$.pipe(startWith(EMPTY)), this.searchBy$])),\n map(([table, sortBy, searchBy]) => {\n // create a new array reference and sort new array (prevent mutating existing state)\n table.data = [...table.data];\n return !sortBy\n ? searchBy\n ? search(searchBy, false, table.data, table.config)\n : table.data\n : (searchBy ? search(searchBy, false, table.data, table.config) : table.data)?.sort((a, b) => {\n // TODO: improve logic\n const typed = sortBy as TableSort;\n return a[typed.sortBy] > b[typed.sortBy]\n ? typed.sortByOrder === Order.ASC\n ? 1\n : -1\n : b[typed.sortBy] > a[typed.sortBy]\n ? typed.sortByOrder === Order.ASC\n ? -1\n : 1\n : 0;\n });\n }),\n shareReplay(1)\n )",
+ "defaultValue": "this._data$.pipe(\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => combineLatest([obs])),\n withLatestFrom(this.tableConfig$),\n map(([[data], config]) => {\n // if columns or rows contains config for mapTo...\n if (\n (config.columns &&\n !!Object.values(config.columns).find((column) => !!column.mapTo)) ||\n (config.rows &&\n !!Object.values(config.rows).find((column) => !!column.mapTo))\n ) {\n // ...map data to new keys on row...\n data = data.map((row) => {\n const newKeys = Object.entries(config.columns || config.rows || [])\n .filter(([key, value]) => !!value.mapTo) // add keys for columns with mapTo config...\n .reduce(\n (previousValue, currentValue) => ({\n ...previousValue,\n // tslint:disable-next-line:no-non-null-assertion\n [currentValue[0]]: this.nestedValue(\n row,\n currentValue[1].mapTo!.path,\n currentValue[1].mapTo?.missingValue\n ),\n }),\n {}\n );\n return { ...row, ...newKeys };\n });\n }\n return { data, config };\n }),\n switchMap((obs) =>\n combineLatest([\n of(obs),\n this.sortBy$.pipe(startWith(EMPTY)),\n this.searchBy$,\n ])\n ),\n map(([table, sortBy, searchBy]) => {\n // create a new array reference and sort new array (prevent mutating existing state)\n table.data = [...table.data];\n return !sortBy\n ? searchBy\n ? search(searchBy, false, table.data, table.config)\n : table.data\n : (searchBy\n ? search(searchBy, false, table.data, table.config)\n : table.data\n )?.sort((a, b) => {\n // TODO: improve logic\n const typed = sortBy as TableSort;\n return a[typed.sortBy] > b[typed.sortBy]\n ? typed.sortByOrder === Order.ASC\n ? 1\n : -1\n : b[typed.sortBy] > a[typed.sortBy]\n ? typed.sortByOrder === Order.ASC\n ? -1\n : 1\n : 0;\n });\n }),\n shareReplay(1)\n )",
"deprecated": false,
"deprecationMessage": "",
"type": "Observable>",
"optional": false,
"description": "",
- "line": 75
+ "line": 94
},
{
"name": "searchBy$",
@@ -1631,7 +1739,7 @@
"type": "Observable",
"optional": false,
"description": "",
- "line": 59
+ "line": 76
},
{
"name": "sortBy$",
@@ -1641,17 +1749,17 @@
"type": "Subject",
"optional": false,
"description": "",
- "line": 53
+ "line": 69
},
{
"name": "table$",
- "defaultValue": "combineLatest([this.data$, this.tableConfig$]).pipe(\n map(([sorted, config]) => {\n // if pagination is disabled...\n if (!config.pagination || config.pagination.length === 0) {\n // ...return unaltered array\n return { data: [sorted], config, info: { records: sorted.length, pageTotal: 1 } };\n }\n // return record set\n return {\n data: chunk(sorted, +(config.pagination.length || 0)),\n config,\n info: {\n records: sorted.length,\n pageTotal: Math.ceil(sorted.length / +(config.pagination.length || 0)),\n },\n };\n }),\n shareReplay(1)\n )",
+ "defaultValue": "combineLatest([\n this.data$,\n this.tableConfig$,\n ]).pipe(\n map(([sorted, config]) => {\n // if pagination is disabled...\n if (!config.pagination || config.pagination.length === 0) {\n // ...return unaltered array\n return {\n data: [sorted],\n config,\n info: { records: sorted.length, pageTotal: 1 },\n };\n }\n // return record set\n return {\n data: chunk(sorted, +(config.pagination.length || 0)),\n config,\n info: {\n records: sorted.length,\n pageTotal: Math.ceil(\n sorted.length / +(config.pagination.length || 0)\n ),\n },\n };\n }),\n shareReplay(1)\n )",
"deprecated": false,
"deprecationMessage": "",
"type": "Observable",
"optional": false,
"description": "",
- "line": 122
+ "line": 161
},
{
"name": "tableConfig$",
@@ -1661,7 +1769,7 @@
"type": "Observable",
"optional": false,
"description": "",
- "line": 68
+ "line": 86
}
],
"methodsClass": [
@@ -1691,7 +1799,7 @@
"optional": false,
"returnType": "",
"typeParameters": [],
- "line": 178,
+ "line": 239,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
@@ -1738,7 +1846,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 161,
+ "line": 217,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
@@ -1761,7 +1869,7 @@
"description": "",
"rawdescription": "\n",
"type": "component",
- "sourceCode": "import {ChangeDetectionStrategy, Component, Input} from '@angular/core';\nimport {BehaviorSubject, combineLatest, EMPTY, isObservable, Observable, of, ReplaySubject, Subject} from 'rxjs';\nimport {TableConfig} from './models/table-config.interface';\nimport {KeyValue} from '@angular/common';\nimport {map, shareReplay, startWith, switchMap, withLatestFrom} from 'rxjs/operators';\nimport {TableColumn} from './models/table-column.interface';\nimport {Order} from './enums/order.enum';\nimport {chunk, search} from './utilities/utilities';\nimport {TableRow} from './models/table-row.interface';\nimport {TableSort} from './models/table-sort.interface';\nimport {TableMeta} from './models/table-meta.interface';\n\n@Component({\n selector: 'angular-generic-table',\n templateUrl: './core.component.html',\n styles: [],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class CoreComponent {\n @Input() set loading(value: Observable | boolean) {\n this._loading$.next(value);\n }\n @Input()\n set page(value: number) {\n this._currentPage$.next(value);\n }\n\n @Input()\n set search(value: Observable | string | null) {\n this._searchBy$.next(value);\n }\n\n @Input()\n set config(value: Observable | TableConfig) {\n this._tableConfig$.next(value);\n }\n\n @Input()\n set data(value: Observable> | Array) {\n this._data$.next(value);\n }\n\n get loading$(): Observable {\n return this._loading$.pipe(\n startWith(false),\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => obs),\n shareReplay(1)\n );\n }\n\n private _loading$: ReplaySubject | boolean> = new ReplaySubject(1);\n sortBy$: Subject = new Subject();\n // tslint:disable-next-line:variable-name\n private _sortBy: TableSort | undefined;\n\n // tslint:disable-next-line:variable-name\n private _searchBy$: ReplaySubject | string | null> = new ReplaySubject(1);\n searchBy$: Observable = this._searchBy$.pipe(\n startWith(''),\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => obs),\n shareReplay(1)\n );\n\n // tslint:disable-next-line:variable-name\n private _tableConfig$: ReplaySubject> = new ReplaySubject(1);\n tableConfig$: Observable = this._tableConfig$.pipe(\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => obs),\n shareReplay(1)\n );\n\n private _data$: ReplaySubject | Observable>> = new ReplaySubject(1);\n data$: Observable> = this._data$.pipe(\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => combineLatest([obs])),\n withLatestFrom(this.tableConfig$),\n map(([[data], config]) => {\n // if columns or rows contains config for mapTo...\n if (config.columns && !!Object.values(config.columns).find(column => !!column.mapTo) ||\n (config.rows && !!Object.values(config.rows).find(column => !!column.mapTo))) {\n // ...map data to new keys on row...\n data = data.map(row => {\n const newKeys = Object.entries(config.columns || config.rows || [])\n .filter(([key, value]) => !!value.mapTo) // add keys for columns with mapTo config...\n .reduce((previousValue, currentValue) => ({\n ...previousValue,\n // tslint:disable-next-line:no-non-null-assertion\n [currentValue[0]]: this.nestedValue(row, currentValue[1].mapTo!.path, currentValue[1].mapTo?.missingValue)\n }), {});\n return {...row, ...newKeys}\n });\n }\n return {data, config}\n }),\n switchMap(obs => combineLatest([of(obs), this.sortBy$.pipe(startWith(EMPTY)), this.searchBy$])),\n map(([table, sortBy, searchBy]) => {\n // create a new array reference and sort new array (prevent mutating existing state)\n table.data = [...table.data];\n return !sortBy\n ? searchBy\n ? search(searchBy, false, table.data, table.config)\n : table.data\n : (searchBy ? search(searchBy, false, table.data, table.config) : table.data)?.sort((a, b) => {\n // TODO: improve logic\n const typed = sortBy as TableSort;\n return a[typed.sortBy] > b[typed.sortBy]\n ? typed.sortByOrder === Order.ASC\n ? 1\n : -1\n : b[typed.sortBy] > a[typed.sortBy]\n ? typed.sortByOrder === Order.ASC\n ? -1\n : 1\n : 0;\n });\n }),\n shareReplay(1)\n );\n\n table$: Observable = combineLatest([this.data$, this.tableConfig$]).pipe(\n map(([sorted, config]) => {\n // if pagination is disabled...\n if (!config.pagination || config.pagination.length === 0) {\n // ...return unaltered array\n return { data: [sorted], config, info: { records: sorted.length, pageTotal: 1 } };\n }\n // return record set\n return {\n data: chunk(sorted, +(config.pagination.length || 0)),\n config,\n info: {\n records: sorted.length,\n pageTotal: Math.ceil(sorted.length / +(config.pagination.length || 0)),\n },\n };\n }),\n shareReplay(1)\n );\n\n private _currentPage$: BehaviorSubject = new BehaviorSubject(0);\n currentPage$ = combineLatest([this._currentPage$, this.table$]).pipe(\n map(([page, table]: any) => {\n // determine last page\n const lastPage = Math.ceil(table.info.records / (table.config?.pagination?.length || table.info.records)) - 1;\n // determine max/min position\n return +page < 0 ? 0 : +page > lastPage ? lastPage : +page;\n }),\n shareReplay(1)\n );\n\n colspan$ = this.tableConfig$.pipe(\n switchMap((config) =>\n config.columns\n ? of(Object.values(config.columns || config.rows || {}).filter((value) => value.hidden !== true).length)\n : this.data$.pipe(map((data) => data.length + 1))\n )\n );\n\n sort(property: string): void {\n const newSortOrder =\n this._sortBy?.sortBy !== property || this._sortBy?.sortByOrder === Order.DESC || !this._sortBy.sortByOrder\n ? Order.ASC\n : Order.DESC;\n const newSortBy = {\n sortBy: property,\n sortByOrder: newSortOrder,\n };\n this.sortBy$.next(newSortBy);\n this._sortBy = newSortBy;\n }\n\n columnOrder = (a: KeyValue, b: KeyValue): number => {\n return (a.value.order || 0) - (b.value.order || 0);\n };\n\n nestedValue(object: any, mapTo: string, missingValue: string | number | null = null): unknown {\n const levels = mapTo\n .split('.');\n return levels.reduce((previousValue, currentValue, index) => (previousValue)[currentValue] || (index === levels.length - 1 ? missingValue : {}), object);\n }\n}\n",
+ "sourceCode": "import { ChangeDetectionStrategy, Component, Input } from '@angular/core';\nimport {\n BehaviorSubject,\n combineLatest,\n EMPTY,\n isObservable,\n Observable,\n of,\n ReplaySubject,\n Subject,\n} from 'rxjs';\nimport { TableConfig } from './models/table-config.interface';\nimport { KeyValue } from '@angular/common';\nimport {\n map,\n shareReplay,\n startWith,\n switchMap,\n withLatestFrom,\n} from 'rxjs/operators';\nimport { TableColumn } from './models/table-column.interface';\nimport { Order } from './enums/order.enum';\nimport { chunk, search } from './utilities/utilities';\nimport { TableRow } from './models/table-row.interface';\nimport { TableSort } from './models/table-sort.interface';\nimport { TableMeta } from './models/table-meta.interface';\n\n@Component({\n selector: 'angular-generic-table',\n templateUrl: './core.component.html',\n styles: [],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class CoreComponent {\n @Input() set loading(isLoading: Observable | boolean) {\n this._loading$.next(isLoading);\n }\n @Input()\n set page(page: number) {\n this._currentPage$.next(page);\n }\n\n @Input()\n set search(string: Observable | string | null) {\n this._searchBy$.next(string);\n }\n\n @Input()\n set config(config: Observable | TableConfig) {\n this._tableConfig$.next(config);\n }\n\n @Input()\n set data(data: Observable> | Array) {\n this._data$.next(data);\n }\n\n get loading$(): Observable {\n return this._loading$.pipe(\n startWith(false),\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => obs),\n shareReplay(1)\n );\n }\n\n private _loading$: ReplaySubject | boolean> =\n new ReplaySubject(1);\n sortBy$: Subject = new Subject();\n // tslint:disable-next-line:variable-name\n private _sortBy: TableSort | undefined;\n\n // tslint:disable-next-line:variable-name\n private _searchBy$: ReplaySubject | string | null> =\n new ReplaySubject(1);\n searchBy$: Observable = this._searchBy$.pipe(\n startWith(''),\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => obs),\n shareReplay(1)\n );\n\n // tslint:disable-next-line:variable-name\n private _tableConfig$: ReplaySubject> =\n new ReplaySubject(1);\n tableConfig$: Observable = this._tableConfig$.pipe(\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => obs),\n shareReplay(1)\n );\n\n private _data$: ReplaySubject | Observable>> =\n new ReplaySubject(1);\n data$: Observable> = this._data$.pipe(\n map((value) => (isObservable(value) ? value : of(value))),\n switchMap((obs) => combineLatest([obs])),\n withLatestFrom(this.tableConfig$),\n map(([[data], config]) => {\n // if columns or rows contains config for mapTo...\n if (\n (config.columns &&\n !!Object.values(config.columns).find((column) => !!column.mapTo)) ||\n (config.rows &&\n !!Object.values(config.rows).find((column) => !!column.mapTo))\n ) {\n // ...map data to new keys on row...\n data = data.map((row) => {\n const newKeys = Object.entries(config.columns || config.rows || [])\n .filter(([key, value]) => !!value.mapTo) // add keys for columns with mapTo config...\n .reduce(\n (previousValue, currentValue) => ({\n ...previousValue,\n // tslint:disable-next-line:no-non-null-assertion\n [currentValue[0]]: this.nestedValue(\n row,\n currentValue[1].mapTo!.path,\n currentValue[1].mapTo?.missingValue\n ),\n }),\n {}\n );\n return { ...row, ...newKeys };\n });\n }\n return { data, config };\n }),\n switchMap((obs) =>\n combineLatest([\n of(obs),\n this.sortBy$.pipe(startWith(EMPTY)),\n this.searchBy$,\n ])\n ),\n map(([table, sortBy, searchBy]) => {\n // create a new array reference and sort new array (prevent mutating existing state)\n table.data = [...table.data];\n return !sortBy\n ? searchBy\n ? search(searchBy, false, table.data, table.config)\n : table.data\n : (searchBy\n ? search(searchBy, false, table.data, table.config)\n : table.data\n )?.sort((a, b) => {\n // TODO: improve logic\n const typed = sortBy as TableSort;\n return a[typed.sortBy] > b[typed.sortBy]\n ? typed.sortByOrder === Order.ASC\n ? 1\n : -1\n : b[typed.sortBy] > a[typed.sortBy]\n ? typed.sortByOrder === Order.ASC\n ? -1\n : 1\n : 0;\n });\n }),\n shareReplay(1)\n );\n\n table$: Observable = combineLatest([\n this.data$,\n this.tableConfig$,\n ]).pipe(\n map(([sorted, config]) => {\n // if pagination is disabled...\n if (!config.pagination || config.pagination.length === 0) {\n // ...return unaltered array\n return {\n data: [sorted],\n config,\n info: { records: sorted.length, pageTotal: 1 },\n };\n }\n // return record set\n return {\n data: chunk(sorted, +(config.pagination.length || 0)),\n config,\n info: {\n records: sorted.length,\n pageTotal: Math.ceil(\n sorted.length / +(config.pagination.length || 0)\n ),\n },\n };\n }),\n shareReplay(1)\n );\n\n private _currentPage$: BehaviorSubject = new BehaviorSubject(0);\n currentPage$ = combineLatest([this._currentPage$, this.table$]).pipe(\n map(([page, table]: any) => {\n // determine last page\n const lastPage =\n Math.ceil(\n table.info.records /\n (table.config?.pagination?.length || table.info.records)\n ) - 1;\n // determine max/min position\n return +page < 0 ? 0 : +page > lastPage ? lastPage : +page;\n }),\n shareReplay(1)\n );\n\n colspan$ = this.tableConfig$.pipe(\n switchMap((config) =>\n config.columns\n ? of(\n Object.values(config.columns || config.rows || {}).filter(\n (value) => value.hidden !== true\n ).length\n )\n : this.data$.pipe(map((data) => data.length + 1))\n )\n );\n\n sort(property: string): void {\n const newSortOrder =\n this._sortBy?.sortBy !== property ||\n this._sortBy?.sortByOrder === Order.DESC ||\n !this._sortBy.sortByOrder\n ? Order.ASC\n : Order.DESC;\n const newSortBy = {\n sortBy: property,\n sortByOrder: newSortOrder,\n };\n this.sortBy$.next(newSortBy);\n this._sortBy = newSortBy;\n }\n\n columnOrder = (\n a: KeyValue,\n b: KeyValue\n ): number => {\n return (a.value.order || 0) - (b.value.order || 0);\n };\n\n nestedValue(\n object: any,\n mapTo: string,\n missingValue: string | number | null = null\n ): unknown {\n const levels = mapTo.split('.');\n return levels.reduce(\n (previousValue, currentValue, index) =>\n previousValue[currentValue] ||\n (index === levels.length - 1 ? missingValue : {}),\n object\n );\n }\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
@@ -1775,17 +1883,17 @@
"deprecationMessage": "",
"args": [
{
- "name": "value",
+ "name": "isLoading",
"type": "Observable | boolean",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "void",
- "line": 20,
+ "line": 35,
"jsdoctags": [
{
- "name": "value",
+ "name": "isLoading",
"type": "Observable | boolean",
"deprecated": false,
"deprecationMessage": "",
@@ -1805,17 +1913,17 @@
"deprecationMessage": "",
"args": [
{
- "name": "value",
+ "name": "page",
"type": "number",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "void",
- "line": 24,
+ "line": 39,
"jsdoctags": [
{
- "name": "value",
+ "name": "page",
"type": "number",
"deprecated": false,
"deprecationMessage": "",
@@ -1835,17 +1943,17 @@
"deprecationMessage": "",
"args": [
{
- "name": "value",
+ "name": "string",
"type": "Observable | string | null",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "void",
- "line": 29,
+ "line": 44,
"jsdoctags": [
{
- "name": "value",
+ "name": "string",
"type": "Observable | string | null",
"deprecated": false,
"deprecationMessage": "",
@@ -1865,17 +1973,17 @@
"deprecationMessage": "",
"args": [
{
- "name": "value",
+ "name": "config",
"type": "Observable | TableConfig",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "void",
- "line": 34,
+ "line": 49,
"jsdoctags": [
{
- "name": "value",
+ "name": "config",
"type": "Observable | TableConfig",
"deprecated": false,
"deprecationMessage": "",
@@ -1895,17 +2003,17 @@
"deprecationMessage": "",
"args": [
{
- "name": "value",
+ "name": "data",
"type": "Observable> | Array",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "void",
- "line": 39,
+ "line": 54,
"jsdoctags": [
{
- "name": "value",
+ "name": "data",
"type": "Observable> | Array",
"deprecated": false,
"deprecationMessage": "",
@@ -1922,15 +2030,15 @@
"name": "loading$",
"type": "",
"returnType": "Observable",
- "line": 43
+ "line": 58
}
}
},
- "templateData": "\n \n \n \n \n {{ column.value?.header || column.key | capitalCase }}\n | \n \n \n \n \n \n \n | \n \n
\n \n \n \n \n \n | \n
\n \n
\n\n \n 0; else noData\">\n \n \n \n \n \n | \n \n
\n \n \n \n \n \n \n \n \n | \n
\n \n \n \n \n\n\n \n \n \n \n | \n
\n \n\n\n \n\n\n {{row[column.key]}}\n\n\n {{row[column.key] | dynamicPipe:transform.pipe:transform?.args}}\n\n\n \n\n"
+ "templateData": "\n \n \n \n \n \n {{ column.value?.header || column.key | capitalCase }}\n | \n \n \n \n \n \n \n | \n \n
\n \n \n \n \n \n | \n
\n \n
\n\n \n 0; else noData\">\n \n \n \n \n \n | \n \n
\n \n \n \n \n \n \n \n \n | \n
\n \n \n \n \n\n\n \n \n \n \n | \n
\n \n\n\n \n\n\n {{ row[column.key] }}\n\n\n {{ row[column.key] | dynamicPipe: transform.pipe:transform?.args }}\n\n\n \n\n"
},
{
"name": "CustomTemplatesComponent",
- "id": "component-CustomTemplatesComponent-dbd9a8a5b28512709cbc1977a2a06e4bef0fb6ceeedc2df0a2b0942088b7cb24fda05526d0803efc2630b6f3be4e341730862c40f5747e3afdb6aceae52be7a6",
+ "id": "component-CustomTemplatesComponent-6cecf532b8a2e4c2eaf23ea4201513aedce4fd037994d2ffc381a761e281dfc1b925f4a7d1c7439b0d14f4d6ea855e4ddb4d7152151bee330f72ba15f3c570b3",
"file": "projects/docs/src/app/examples/custom-templates/custom-templates.component.ts",
"encapsulation": [],
"entryComponents": [],
@@ -1940,7 +2048,7 @@
"selector": "docs-custom-templates",
"styleUrls": [],
"styles": [],
- "template": "\n\n \n\n\n \n\n{{ clicked }}\n\n",
+ "template": "\n\n \n\n\n \n\n{{ clicked }}\n\n",
"templateUrl": [],
"viewProviders": [],
"inputsClass": [],
@@ -1953,7 +2061,7 @@
"type": "TemplateRef | undefined",
"optional": false,
"description": "",
- "line": 22,
+ "line": 39,
"decorators": [
{
"name": "ViewChild",
@@ -1969,7 +2077,7 @@
"type": "string",
"optional": false,
"description": "",
- "line": 24
+ "line": 41
},
{
"name": "color",
@@ -1978,7 +2086,7 @@
"type": "TemplateRef | undefined",
"optional": false,
"description": "",
- "line": 23,
+ "line": 40,
"decorators": [
{
"name": "ViewChild",
@@ -1994,7 +2102,7 @@
"type": "ReplaySubject",
"optional": false,
"description": "",
- "line": 42
+ "line": 59
},
{
"name": "data",
@@ -2004,7 +2112,7 @@
"type": "[]",
"optional": false,
"description": "",
- "line": 26
+ "line": 43
},
{
"name": "SNIPPETS",
@@ -2014,7 +2122,7 @@
"type": "",
"optional": false,
"description": "",
- "line": 44
+ "line": 61
}
],
"methodsClass": [
@@ -2043,7 +2151,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 61,
+ "line": 78,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
@@ -2082,7 +2190,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 45,
+ "line": 62,
"deprecated": false,
"deprecationMessage": ""
}
@@ -2094,7 +2202,7 @@
"description": "",
"rawdescription": "\n",
"type": "component",
- "sourceCode": "import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { TableConfig, TableRow, TableColumn } from '@angular-generic-table/core';\nimport { ReplaySubject } from 'rxjs';\nimport { CUSTOM_TEMPLATES_DOCS } from './custom-templates.snippets';\n\n@Component({\n selector: 'docs-custom-templates',\n template: `\n \n \n \n \n \n \n \n {{ clicked }}\n \n `,\n})\nexport class CustomTemplatesComponent implements OnInit {\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n clicked = '';\n\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteColor: '#26BFAF',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteColor: '#0f0',\n favoriteFood: 'Pizza',\n },\n ];\n config$: ReplaySubject = new ReplaySubject(1);\n\n SNIPPETS = CUSTOM_TEMPLATES_DOCS;\n ngOnInit(): void {\n this.config$.next({\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteColor: {\n templateRef: this.color,\n },\n favoriteFood: {},\n action: {\n templateRef: this.actions,\n },\n },\n });\n }\n clickAction(row: TableRow, column: { key: string; value: TableColumn }, index: number): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = `clicked row number: ${index}`;\n }\n}\n\nexport const CustomTemplates: Story = (args: CustomTemplatesComponent) => ({\n props: args,\n component: CustomTemplatesComponent,\n});\n",
+ "sourceCode": "import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { Story } from '@storybook/angular/types-6-0';\nimport {\n TableConfig,\n TableRow,\n TableColumn,\n} from '@angular-generic-table/core';\nimport { ReplaySubject } from 'rxjs';\nimport { CUSTOM_TEMPLATES_DOCS } from './custom-templates.snippets';\n\n@Component({\n selector: 'docs-custom-templates',\n template: `\n \n \n \n \n \n \n \n {{ clicked }}\n \n `,\n})\nexport class CustomTemplatesComponent implements OnInit {\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n clicked = '';\n\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteColor: '#26BFAF',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteColor: '#0f0',\n favoriteFood: 'Pizza',\n },\n ];\n config$: ReplaySubject = new ReplaySubject(1);\n\n SNIPPETS = CUSTOM_TEMPLATES_DOCS;\n ngOnInit(): void {\n this.config$.next({\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteColor: {\n templateRef: this.color,\n },\n favoriteFood: {},\n action: {\n templateRef: this.actions,\n },\n },\n });\n }\n clickAction(\n row: TableRow,\n column: { key: string; value: TableColumn },\n index: number\n ): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = `clicked row number: ${index}`;\n }\n}\n\nexport const CustomTemplates: Story = (\n args: CustomTemplatesComponent\n) => ({\n props: args,\n component: CustomTemplatesComponent,\n});\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
@@ -2103,105 +2211,392 @@
]
},
{
- "name": "MobileLayoutComponent",
- "id": "component-MobileLayoutComponent-111e4b7be7fd951cf467a92d422596ee0ec208b3ec73a27e69c2d89a7431d1acfed41e359a1da0b97f878fb4a9143d8cb411bcba402270e6bc7c301787328562",
- "file": "projects/docs/src/app/examples/mobile-layout/mobile-layout.component.ts",
- "encapsulation": [
- "ViewEncapsulation.None"
- ],
+ "name": "GtDeltaComponent",
+ "id": "component-GtDeltaComponent-6fd03daa2704a9ff5639b8b9d1ea3620aed7018176b68fe45a400dc853b7ac73502336be442783ff224e41587dd572a86ef3357383260f206b4e3a4509c43e47",
+ "file": "projects/core/src/lib/gt-delta/gt-delta.component.ts",
+ "changeDetection": "ChangeDetectionStrategy.OnPush",
+ "encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
- "selector": "docs-mobile-layout",
+ "selector": "gt-delta",
"styleUrls": [],
"styles": [
- "\n .table th {\n white-space: nowrap;\n }\n "
+ "\n :host {\n display: inline-block;\n }\n "
],
- "template": "\n {{clicked}} \n
\n\n\n \n\n\n",
+ "template": " 0\n ? classes.positive\n : classes.negative\n ]\"\n [class.gt-delta-positive]=\"delta.value > 0 && Number.isFinite(delta.value)\"\n [class.gt-delta-negative]=\"delta.value < 0\"\n >{{\n Number.isFinite(delta.value)\n ? (delta.value | percent)\n : delta.value === initialValue\n ? initialValue\n : notApplicableValue\n }}",
"templateUrl": [],
"viewProviders": [],
- "inputsClass": [],
- "outputsClass": [],
- "propertiesClass": [
+ "inputsClass": [
{
- "name": "actions",
+ "name": "baseIndex",
"deprecated": false,
"deprecationMessage": "",
- "type": "TemplateRef | undefined",
- "optional": false,
- "description": "",
- "line": 41,
- "decorators": [
- {
- "name": "ViewChild",
- "stringifiedArguments": "'actions', {static: true}"
- }
- ]
+ "line": 54,
+ "type": "number",
+ "decorators": []
},
{
- "name": "clicked",
- "defaultValue": "''",
+ "name": "classes",
+ "defaultValue": "{\n span: 'gt-delta',\n positive: 'text-success',\n negative: 'text-danger',\n }",
"deprecated": false,
"deprecationMessage": "",
- "type": "string",
- "optional": false,
- "description": "",
- "line": 42
+ "line": 55,
+ "type": "{ span: string; positive: string; negative: string; }",
+ "decorators": []
},
{
- "name": "config$",
- "defaultValue": "this.mobileLayout$.pipe(\n map(mobileLayout => ({\n mobileLayout,\n columns: {\n firstName: {\n mobileHeader: true,\n sortable: true\n },\n lastName: {\n mobileHeader: true,\n sortable: true\n },\n gender: {\n mobileHeader: true,\n transform: {\n pipe: GenderPipe\n }\n },\n favoriteFood: {\n mobileHeader: true\n },\n action: {\n mobileHeader: false,\n header: false,\n templateRef: this.actions,\n },\n },\n }))\n )",
+ "name": "data",
+ "defaultValue": "[]",
"deprecated": false,
"deprecationMessage": "",
- "type": "Observable",
- "optional": false,
- "description": "",
- "line": 59
+ "line": 52,
+ "type": "TableRows",
+ "decorators": []
},
{
- "name": "data",
- "defaultValue": "[\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ]",
+ "name": "index",
+ "defaultValue": "0",
"deprecated": false,
"deprecationMessage": "",
- "type": "[]",
- "optional": false,
- "description": "",
- "line": 45
+ "line": 53,
+ "type": "number",
+ "decorators": []
},
{
- "name": "mobileLayout$",
- "defaultValue": "new BehaviorSubject(true)",
+ "name": "initialValue",
+ "defaultValue": "'-'",
"deprecated": false,
"deprecationMessage": "",
- "type": "",
- "optional": false,
- "description": "",
- "line": 44
+ "line": 62,
+ "type": "string",
+ "decorators": []
},
{
- "name": "SNIPPETS",
- "defaultValue": "MOBILE_LAYOUT_SNIPPETS",
+ "name": "key",
+ "defaultValue": "'value'",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "line": 60,
+ "type": "string",
+ "decorators": []
+ },
+ {
+ "name": "notApplicableValue",
+ "defaultValue": "'n/a'",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "line": 61,
+ "type": "string",
+ "decorators": []
+ }
+ ],
+ "outputsClass": [],
+ "propertiesClass": [
+ {
+ "name": "Math",
+ "defaultValue": "Math",
"deprecated": false,
"deprecationMessage": "",
"type": "",
"optional": false,
"description": "",
- "line": 89
+ "line": 50
},
{
- "name": "toggleLayout",
- "defaultValue": "() => {...}",
+ "name": "Number",
+ "defaultValue": "Number",
"deprecated": false,
"deprecationMessage": "",
"type": "",
"optional": false,
"description": "",
- "line": 91
+ "line": 51
}
],
- "methodsClass": [
- {
+ "methodsClass": [],
+ "deprecated": false,
+ "deprecationMessage": "",
+ "hostBindings": [],
+ "hostListeners": [],
+ "description": "",
+ "rawdescription": "\n",
+ "type": "component",
+ "sourceCode": "import { ChangeDetectionStrategy, Component, Input } from '@angular/core';\nimport { TableRows } from '../models/table-row.interface';\n\n@Component({\n selector: 'gt-delta',\n template: ` 0\n ? classes.positive\n : classes.negative\n ]\"\n [class.gt-delta-positive]=\"delta.value > 0 && Number.isFinite(delta.value)\"\n [class.gt-delta-negative]=\"delta.value < 0\"\n >{{\n Number.isFinite(delta.value)\n ? (delta.value | percent)\n : delta.value === initialValue\n ? initialValue\n : notApplicableValue\n }}`,\n styles: [\n `\n :host {\n display: inline-block;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class GtDeltaComponent {\n constructor() {}\n Math = Math;\n Number = Number;\n @Input() data: TableRows = [];\n @Input() index: number = 0;\n @Input() baseIndex?: number;\n @Input() classes = {\n span: 'gt-delta',\n positive: 'text-success',\n negative: 'text-danger',\n };\n @Input() key: string = 'value';\n @Input() notApplicableValue: string = 'n/a';\n @Input() initialValue: string = '-';\n}\n",
+ "assetsDirs": [],
+ "styleUrlsData": "",
+ "stylesData": "\n :host {\n display: inline-block;\n }\n \n",
+ "constructorObj": {
+ "name": "constructor",
+ "description": "",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "args": [],
+ "line": 48
+ }
+ },
+ {
+ "name": "HorizontalTableComponent",
+ "id": "component-HorizontalTableComponent-c908ce0b9358de7e6185b2abf3560e1f8ecd6bd235001c0639bee6d8162114188bb0c3932d8f9cf57f96ce1245d0d0729ff19b8045b758f8a9032c496d1d91a8",
+ "file": "projects/docs/src/app/examples/horizontal-table/horizontal-table.component.ts",
+ "encapsulation": [],
+ "entryComponents": [],
+ "inputs": [],
+ "outputs": [],
+ "providers": [],
+ "selector": "docs-horizontal-table",
+ "styleUrls": [],
+ "styles": [],
+ "template": "\n\n\n\n
\n \n Table is empty
\n \n
\n\n \n 😀\n 🙂\n 😐\n 😭\n
\n\n\n \n\n\n \n\n\n",
+ "templateUrl": [],
+ "viewProviders": [],
+ "inputsClass": [],
+ "outputsClass": [],
+ "propertiesClass": [
+ {
+ "name": "config",
+ "defaultValue": "{}",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "TableConfig",
+ "optional": false,
+ "description": "",
+ "line": 58
+ },
+ {
+ "name": "data",
+ "defaultValue": "[]",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "TableRows",
+ "optional": false,
+ "description": "",
+ "line": 59
+ },
+ {
+ "name": "delta",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "TemplateRef | undefined",
+ "optional": false,
+ "description": "",
+ "line": 51,
+ "decorators": [
+ {
+ "name": "ViewChild",
+ "stringifiedArguments": "'delta', {static: true}"
+ }
+ ]
+ },
+ {
+ "name": "deltaIndex",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "TemplateRef | undefined",
+ "optional": false,
+ "description": "",
+ "line": 54,
+ "decorators": [
+ {
+ "name": "ViewChild",
+ "stringifiedArguments": "'deltaIndex', {static: true}"
+ }
+ ]
+ },
+ {
+ "name": "feelings",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "TemplateRef | undefined",
+ "optional": false,
+ "description": "",
+ "line": 48,
+ "decorators": [
+ {
+ "name": "ViewChild",
+ "stringifiedArguments": "'feelings', {static: true}"
+ }
+ ]
+ },
+ {
+ "name": "loading$",
+ "defaultValue": "new BehaviorSubject(false)",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "",
+ "optional": false,
+ "description": "",
+ "line": 57
+ },
+ {
+ "name": "SNIPPETS",
+ "defaultValue": "HORIZONTAL_TABLE_SNIPPETS",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "",
+ "optional": false,
+ "description": "",
+ "line": 131
+ }
+ ],
+ "methodsClass": [
+ {
+ "name": "empty",
+ "args": [],
+ "optional": false,
+ "returnType": "void",
+ "typeParameters": [],
+ "line": 98,
+ "deprecated": false,
+ "deprecationMessage": ""
+ },
+ {
+ "name": "load",
+ "args": [],
+ "optional": false,
+ "returnType": "void",
+ "typeParameters": [],
+ "line": 101,
+ "deprecated": false,
+ "deprecationMessage": ""
+ },
+ {
+ "name": "ngOnInit",
+ "args": [],
+ "optional": false,
+ "returnType": "void",
+ "typeParameters": [],
+ "line": 61,
+ "deprecated": false,
+ "deprecationMessage": ""
+ },
+ {
+ "name": "simulateLoad",
+ "args": [],
+ "optional": false,
+ "returnType": "void",
+ "typeParameters": [],
+ "line": 93,
+ "deprecated": false,
+ "deprecationMessage": ""
+ }
+ ],
+ "deprecated": false,
+ "deprecationMessage": "",
+ "hostBindings": [],
+ "hostListeners": [],
+ "description": "",
+ "rawdescription": "\n",
+ "type": "component",
+ "sourceCode": "import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { HORIZONTAL_TABLE_SNIPPETS } from './horizontal-table.snippets';\nimport {\n GtDeltaComponent,\n TableConfig,\n TableRows,\n} from '@angular-generic-table/core';\nimport { BehaviorSubject } from 'rxjs';\n\n@Component({\n selector: 'docs-horizontal-table',\n template: `\n \n \n \n \n
\n \n Table is empty
\n \n
\n \n \n 😀\n 🙂\n 😐\n 😭\n
\n \n \n \n \n \n \n \n \n `,\n styles: [],\n})\nexport class HorizontalTableComponent implements OnInit {\n @ViewChild('feelings', { static: true }) feelings:\n | TemplateRef\n | undefined;\n @ViewChild('delta', { static: true }) delta:\n | TemplateRef\n | undefined;\n @ViewChild('deltaIndex', { static: true }) deltaIndex:\n | TemplateRef\n | undefined;\n loading$ = new BehaviorSubject(false);\n config: TableConfig = {};\n data: TableRows = [];\n\n ngOnInit(): void {\n this.config = {\n stickyHeaders: {\n row: true,\n },\n mobileLayout: true,\n rows: {\n year: {\n class: 'text-end',\n header: false,\n },\n value: {\n class: 'text-end',\n },\n delta: {\n header: 'Delta %',\n templateRef: this.delta,\n class: 'text-end',\n },\n deltaIndex: {\n header: 'Since inception %',\n templateRef: this.deltaIndex,\n class: 'text-end',\n },\n feeling: {\n templateRef: this.feelings,\n class: 'text-end',\n },\n },\n };\n this.load();\n }\n simulateLoad(): void {\n this.loading$.next(true);\n // set loading state to false after 2 seconds\n setTimeout(() => this.loading$.next(false), 2000);\n }\n empty(): void {\n this.data = [];\n }\n load(): void {\n this.data = [\n {\n year: '2017',\n value: 50,\n feeling: 'neutral',\n },\n {\n year: '2018',\n value: 75,\n feeling: 'positive',\n },\n {\n year: '2019',\n value: 100,\n feeling: 'thrilled',\n },\n {\n year: '2020',\n value: 250,\n feeling: 'thrilled',\n },\n {\n year: '2021',\n value: 50,\n feeling: 'negative',\n },\n ];\n }\n\n SNIPPETS = HORIZONTAL_TABLE_SNIPPETS;\n}\n\nexport const Horizontal: Story = (\n args: HorizontalTableComponent\n) => ({\n props: args,\n component: HorizontalTableComponent,\n});\n",
+ "assetsDirs": [],
+ "styleUrlsData": "",
+ "stylesData": "",
+ "implements": [
+ "OnInit"
+ ]
+ },
+ {
+ "name": "MobileLayoutComponent",
+ "id": "component-MobileLayoutComponent-2ccdf200e7b7f3afa39e0e6fdca8c7ea0f53af37d1eb3722378cbcaba073ec2e8a214f10e4485e4464b0ea63b3d083009815564424e37fbd8206a83200f4ed92",
+ "file": "projects/docs/src/app/examples/mobile-layout/mobile-layout.component.ts",
+ "encapsulation": [
+ "ViewEncapsulation.None"
+ ],
+ "entryComponents": [],
+ "inputs": [],
+ "outputs": [],
+ "providers": [],
+ "selector": "docs-mobile-layout",
+ "styleUrls": [],
+ "styles": [
+ "\n .table th {\n white-space: nowrap;\n }\n "
+ ],
+ "template": "\n {{ clicked }}\n \n
\n\n\n \n\n\n",
+ "templateUrl": [],
+ "viewProviders": [],
+ "inputsClass": [],
+ "outputsClass": [],
+ "propertiesClass": [
+ {
+ "name": "actions",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "TemplateRef | undefined",
+ "optional": false,
+ "description": "",
+ "line": 63,
+ "decorators": [
+ {
+ "name": "ViewChild",
+ "stringifiedArguments": "'actions', {static: true}"
+ }
+ ]
+ },
+ {
+ "name": "clicked",
+ "defaultValue": "''",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "string",
+ "optional": false,
+ "description": "",
+ "line": 64
+ },
+ {
+ "name": "config$",
+ "defaultValue": "this.mobileLayout$.pipe(\n map((mobileLayout) => ({\n mobileLayout,\n columns: {\n firstName: {\n mobileHeader: true,\n sortable: true,\n },\n lastName: {\n mobileHeader: true,\n sortable: true,\n },\n gender: {\n mobileHeader: true,\n transform: {\n pipe: GenderPipe,\n },\n },\n favoriteFood: {\n mobileHeader: true,\n },\n action: {\n mobileHeader: false,\n header: false,\n templateRef: this.actions,\n },\n },\n }))\n )",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "Observable",
+ "optional": false,
+ "description": "",
+ "line": 81
+ },
+ {
+ "name": "data",
+ "defaultValue": "[\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ]",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "[]",
+ "optional": false,
+ "description": "",
+ "line": 67
+ },
+ {
+ "name": "mobileLayout$",
+ "defaultValue": "new BehaviorSubject(true)",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "",
+ "optional": false,
+ "description": "",
+ "line": 66
+ },
+ {
+ "name": "SNIPPETS",
+ "defaultValue": "MOBILE_LAYOUT_SNIPPETS",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "",
+ "optional": false,
+ "description": "",
+ "line": 111
+ },
+ {
+ "name": "toggleLayout",
+ "defaultValue": "() => {...}",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "",
+ "optional": false,
+ "description": "",
+ "line": 113
+ }
+ ],
+ "methodsClass": [
+ {
"name": "clickAction",
"args": [
{
@@ -2226,7 +2621,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 94,
+ "line": 116,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
@@ -2267,14 +2662,14 @@
"description": "",
"rawdescription": "\n",
"type": "component",
- "sourceCode": "import {Component, Pipe, PipeTransform, TemplateRef, ViewChild, ViewEncapsulation} from '@angular/core';\nimport {Story} from \"@storybook/angular/types-6-0\";\nimport {TableColumn, TableConfig, TableRow} from \"@angular-generic-table/core\";\nimport {BehaviorSubject, Observable} from \"rxjs\";\nimport {map} from \"rxjs/operators\";\nimport {MOBILE_LAYOUT_SNIPPETS} from \"./mobileLayout.snippets\";\n\n\n@Pipe({\n name: 'genderPipe',\n})\nexport class GenderPipe implements PipeTransform {\n transform(gender: 'male' | 'female'): string {\n return gender === 'male' ? '👨' : '👩';\n }\n}\n\n\n@Component({\n selector: 'docs-mobile-layout',\n template: `\n \n {{clicked}} \n
\n \n \n \n \n \n `,\n styles: [`\n .table th {\n white-space: nowrap;\n }\n `],\n encapsulation: ViewEncapsulation.None\n})\nexport class MobileLayoutComponent {\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n clicked = '';\n\n mobileLayout$ = new BehaviorSubject(true);\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config$: Observable = this.mobileLayout$.pipe(\n map(mobileLayout => ({\n mobileLayout,\n columns: {\n firstName: {\n mobileHeader: true,\n sortable: true\n },\n lastName: {\n mobileHeader: true,\n sortable: true\n },\n gender: {\n mobileHeader: true,\n transform: {\n pipe: GenderPipe\n }\n },\n favoriteFood: {\n mobileHeader: true\n },\n action: {\n mobileHeader: false,\n header: false,\n templateRef: this.actions,\n },\n },\n }))\n );\n\n SNIPPETS = MOBILE_LAYOUT_SNIPPETS;\n\n toggleLayout = (): void => {\n this.mobileLayout$.next(!this.mobileLayout$.getValue());\n }\n clickAction(row: TableRow, column: { key: string; value: TableColumn }, index: number): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = `Clicked row number: ${index}`;\n }\n}\n\nexport const Mobile: Story = (args: MobileLayoutComponent) => ({\n props: args,\n component: MobileLayoutComponent,\n});\n",
+ "sourceCode": "import {\n Component,\n Pipe,\n PipeTransform,\n TemplateRef,\n ViewChild,\n ViewEncapsulation,\n} from '@angular/core';\nimport { Story } from '@storybook/angular/types-6-0';\nimport {\n TableColumn,\n TableConfig,\n TableRow,\n} from '@angular-generic-table/core';\nimport { BehaviorSubject, Observable } from 'rxjs';\nimport { map } from 'rxjs/operators';\nimport { MOBILE_LAYOUT_SNIPPETS } from './mobileLayout.snippets';\n\n@Pipe({\n name: 'genderPipe',\n})\nexport class GenderPipe implements PipeTransform {\n transform(gender: 'male' | 'female'): string {\n return gender === 'male' ? '👨' : '👩';\n }\n}\n\n@Component({\n selector: 'docs-mobile-layout',\n template: `\n \n {{ clicked }}\n \n
\n \n \n \n \n \n `,\n styles: [\n `\n .table th {\n white-space: nowrap;\n }\n `,\n ],\n encapsulation: ViewEncapsulation.None,\n})\nexport class MobileLayoutComponent {\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n clicked = '';\n\n mobileLayout$ = new BehaviorSubject(true);\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config$: Observable = this.mobileLayout$.pipe(\n map((mobileLayout) => ({\n mobileLayout,\n columns: {\n firstName: {\n mobileHeader: true,\n sortable: true,\n },\n lastName: {\n mobileHeader: true,\n sortable: true,\n },\n gender: {\n mobileHeader: true,\n transform: {\n pipe: GenderPipe,\n },\n },\n favoriteFood: {\n mobileHeader: true,\n },\n action: {\n mobileHeader: false,\n header: false,\n templateRef: this.actions,\n },\n },\n }))\n );\n\n SNIPPETS = MOBILE_LAYOUT_SNIPPETS;\n\n toggleLayout = (): void => {\n this.mobileLayout$.next(!this.mobileLayout$.getValue());\n };\n clickAction(\n row: TableRow,\n column: { key: string; value: TableColumn },\n index: number\n ): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = `Clicked row number: ${index}`;\n }\n}\n\nexport const Mobile: Story = (\n args: MobileLayoutComponent\n) => ({\n props: args,\n component: MobileLayoutComponent,\n});\n",
"assetsDirs": [],
"styleUrlsData": "",
- "stylesData": "\n .table th {\n white-space: nowrap;\n }\n \n"
+ "stylesData": "\n .table th {\n white-space: nowrap;\n }\n \n"
},
{
"name": "NestedDataComponent",
- "id": "component-NestedDataComponent-0368a30b14184803ea2d1e5c288b29d0fc90b2f4e6f1baf73ed33d59c8693c26bc54bc02b63091925149ae215c60fec485a68e38f44094b271b15b7942b1f79e",
+ "id": "component-NestedDataComponent-ba03f02a98cc4ad9bde4815796062b9ac0dc528bfcc1f5e8e29aae1ae510ea9141e119673eb1865b1875aecc73e3ba4743d1923e1577dad3670a8ad69c4d374a",
"file": "projects/docs/src/app/examples/nested-data/nested-data.component.ts",
"encapsulation": [],
"entryComponents": [],
@@ -2284,7 +2679,7 @@
"selector": "nested-data",
"styleUrls": [],
"styles": [],
- "template": "\n\n\n\n \n ♂️\n ♀️\n
\n\n",
+ "template": "\n
\n \n
\n
\n \n
\n
\n\n\n\n \n ♂️\n ♀️\n
\n\n",
"templateUrl": [],
"viewProviders": [],
"inputsClass": [],
@@ -2298,7 +2693,7 @@
"type": "TableConfig",
"optional": false,
"description": "",
- "line": 24
+ "line": 40
},
{
"name": "data",
@@ -2308,7 +2703,7 @@
"type": "TableRows",
"optional": false,
"description": "",
- "line": 25
+ "line": 41
},
{
"name": "gender",
@@ -2317,7 +2712,7 @@
"type": "TemplateRef | undefined",
"optional": false,
"description": "",
- "line": 22,
+ "line": 38,
"decorators": [
{
"name": "ViewChild",
@@ -2333,7 +2728,7 @@
"type": "",
"optional": false,
"description": "",
- "line": 113
+ "line": 132
}
],
"methodsClass": [
@@ -2343,7 +2738,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 77,
+ "line": 96,
"deprecated": false,
"deprecationMessage": ""
},
@@ -2353,7 +2748,17 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 26,
+ "line": 42,
+ "deprecated": false,
+ "deprecationMessage": ""
+ },
+ {
+ "name": "resetData",
+ "args": [],
+ "optional": false,
+ "returnType": "void",
+ "typeParameters": [],
+ "line": 67,
"deprecated": false,
"deprecationMessage": ""
}
@@ -2365,7 +2770,7 @@
"description": "",
"rawdescription": "\n",
"type": "component",
- "sourceCode": "import {NESTED_SNIPPETS} from \"./nested.snippets\";\nimport { Story } from '@storybook/angular/types-6-0';\nimport {Component, OnInit, TemplateRef, ViewChild} from '@angular/core';\nimport {TableConfig, TableRows} from \"@angular-generic-table/core\";\n\n@Component({\n selector: 'nested-data',\n template: `\n \n \n \n \n \n ♂️\n ♀️\n
\n \n `,\n styles: [],\n})\nexport class NestedDataComponent implements OnInit {\n @ViewChild('gender', { static: true }) gender: TemplateRef | undefined;\n\n config: TableConfig = {};\n data: TableRows = [];\n ngOnInit(): void {\n this.data = [\n {\n name: {\n first: 'Peter',\n last: 'Parker',\n },\n data: {\n details: {\n gender: 'male',\n favoriteFood: 'Pasta',\n }\n },\n },\n {\n name: {\n first: 'Mary Jane',\n last: 'Watson',\n },\n data: {\n details: {\n gender: 'female',\n favoriteFood: 'Pizza',\n }\n }\n },\n ];\n this.config = {\n columns: {\n firstName: {\n sortable: true,\n mapTo: { path: 'name.first' }\n },\n lastName: {\n mapTo: { path: 'name.last' }\n },\n gender: {\n mapTo: { path: 'data.details.gender' },\n templateRef: this.gender,\n\n },\n favoriteFood: {\n mapTo: { path: 'data.details.favoriteFood', missingValue: 'n/a' }\n },\n missing: {\n mapTo: { path: 'data.missingKey.noMatch', missingValue: 'n/a' }\n },\n },\n };\n }\n\n loadData(): void {\n this.data = [\n {\n name: {\n first: 'John',\n last: 'Doe',\n },\n data: {\n details: {\n gender: 'male',\n favoriteFood: 'Pasta',\n }\n },\n },\n {\n name: {\n first: 'Jane',\n last: 'Doe',\n },\n data: {\n details: {\n gender: 'female',\n favoriteFood: 'Pizza',\n }\n }\n },\n {\n name: {\n first: 'Foo',\n last: 'Bar',\n },\n data: {}\n },\n ];\n }\n\n SNIPPETS = NESTED_SNIPPETS;\n}\n\nexport const Nested: Story = (args: NestedDataComponent) => ({\n props: args,\n component: NestedDataComponent,\n});\n",
+ "sourceCode": "import { NESTED_SNIPPETS } from './nested.snippets';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { TableConfig, TableRows } from '@angular-generic-table/core';\n\n@Component({\n selector: 'nested-data',\n template: `\n \n
\n \n
\n
\n \n
\n
\n \n \n \n \n ♂️\n ♀️\n
\n \n `,\n styles: [],\n})\nexport class NestedDataComponent implements OnInit {\n @ViewChild('gender', { static: true }) gender: TemplateRef | undefined;\n\n config: TableConfig = {};\n data: TableRows = [];\n ngOnInit(): void {\n this.resetData();\n this.config = {\n columns: {\n firstName: {\n sortable: true,\n mapTo: { path: 'name.first' },\n },\n lastName: {\n mapTo: { path: 'name.last' },\n },\n gender: {\n mapTo: { path: 'data.details.gender' },\n templateRef: this.gender,\n },\n favoriteFood: {\n mapTo: { path: 'data.details.favoriteFood', missingValue: 'n/a' },\n },\n missing: {\n mapTo: { path: 'data.missingKey.noMatch', missingValue: 'n/a' },\n },\n },\n };\n }\n\n resetData() {\n this.data = [\n {\n name: {\n first: 'Peter',\n last: 'Parker',\n },\n data: {\n details: {\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n },\n },\n {\n name: {\n first: 'Mary Jane',\n last: 'Watson',\n },\n data: {\n details: {\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n },\n },\n ];\n }\n\n loadData(): void {\n this.data = [\n {\n name: {\n first: 'John',\n last: 'Doe',\n },\n data: {\n details: {\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n },\n },\n {\n name: {\n first: 'Jane',\n last: 'Doe',\n },\n data: {\n details: {\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n },\n },\n {\n name: {\n first: 'Foo',\n last: 'Bar',\n },\n data: {},\n },\n ];\n }\n\n SNIPPETS = NESTED_SNIPPETS;\n}\n\nexport const Nested: Story = (\n args: NestedDataComponent\n) => ({\n props: args,\n component: NestedDataComponent,\n});\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
@@ -2375,7 +2780,7 @@
},
{
"name": "PaginationComponent",
- "id": "component-PaginationComponent-585222b5a5e9d0ae9a2d1590a9cee3123ca9f582b54ee5bd868d9b7e85f1a240e87ba893889466694fc8d57d541b63631401e9cd4ae63674f5dd890f36378caf",
+ "id": "component-PaginationComponent-a357a4c0fd2e8981242f212925300a40d0bbac4d08ab558c2fe5121b33f4911eb4637651c0bcc7e1b21d07b487f52a6b70633335c229c178f61ce2ff6185bd01",
"file": "projects/core/src/lib/pagination/pagination.component.ts",
"changeDetection": "ChangeDetectionStrategy.OnPush",
"encapsulation": [],
@@ -2391,17 +2796,80 @@
],
"viewProviders": [],
"inputsClass": [
+ {
+ "name": "ariaLabels",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "line": 34,
+ "type": "GtPaginationAriaLabels",
+ "decorators": []
+ },
+ {
+ "name": "classes",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "line": 27,
+ "type": "GtPaginationClasses",
+ "decorators": []
+ },
+ {
+ "name": "paginationLength",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "line": 20,
+ "type": "number",
+ "decorators": []
+ },
{
"name": "table",
"deprecated": false,
"deprecationMessage": "",
- "line": 15,
+ "line": 40,
"type": "any",
"decorators": []
}
],
"outputsClass": [],
"propertiesClass": [
+ {
+ "name": "_ariaLabels",
+ "defaultValue": "{\n nav: 'Table pagination',\n button: 'Go to page ',\n }",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "GtPaginationAriaLabels",
+ "optional": false,
+ "description": "",
+ "line": 47,
+ "modifierKind": [
+ 121
+ ]
+ },
+ {
+ "name": "_classes",
+ "defaultValue": "{\n ul: 'pagination',\n li: 'page-item',\n button: 'page-link',\n }",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "GtPaginationClasses",
+ "optional": false,
+ "description": "",
+ "line": 51,
+ "modifierKind": [
+ 121
+ ]
+ },
+ {
+ "name": "_paginationLength",
+ "defaultValue": "5",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "number",
+ "optional": false,
+ "description": "",
+ "line": 56,
+ "modifierKind": [
+ 121
+ ]
+ },
{
"name": "_table",
"deprecated": false,
@@ -2409,20 +2877,20 @@
"type": "CoreComponent | undefined",
"optional": false,
"description": "",
- "line": 21,
+ "line": 46,
"modifierKind": [
121
]
},
{
"name": "pagination$",
- "defaultValue": "this.table$.pipe(\n switchMap((core) => combineLatest([core?.table$.pipe(pluck('info')), core?.currentPage$])),\n map(([info, currentPage]) => this.generateList(info.pageTotal, currentPage))\n )",
+ "defaultValue": "this.table$.pipe(\n switchMap((core) =>\n combineLatest([core?.table$.pipe(pluck('info')), core?.currentPage$])\n ),\n map(([info, currentPage]) => this.generateList(info.pageTotal, currentPage))\n )",
"deprecated": false,
"deprecationMessage": "",
"type": "",
"optional": false,
"description": "",
- "line": 22
+ "line": 57
},
{
"name": "table$",
@@ -2432,7 +2900,7 @@
"type": "ReplaySubject",
"optional": false,
"description": "",
- "line": 20
+ "line": 45
}
],
"methodsClass": [
@@ -2455,7 +2923,7 @@
"optional": false,
"returnType": "Array",
"typeParameters": [],
- "line": 27,
+ "line": 64,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
@@ -2492,7 +2960,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 53,
+ "line": 96,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
@@ -2504,22 +2972,130 @@
"tagName": {
"text": "param"
}
- }
- ]
- }
- ],
- "deprecated": false,
- "deprecationMessage": "",
- "hostBindings": [],
- "hostListeners": [],
- "description": "",
- "rawdescription": "\n",
- "type": "component",
- "sourceCode": "import { ChangeDetectionStrategy, Component, Input } from '@angular/core';\nimport { combineLatest, ReplaySubject } from 'rxjs';\nimport { map, pluck, switchMap } from 'rxjs/operators';\nimport { CoreComponent } from '../core.component';\n\n@Component({\n selector: 'angular-generic-table-pagination',\n templateUrl: './pagination.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class PaginationComponent {\n get table(): CoreComponent | undefined {\n return this._table;\n }\n @Input() set table(value: any) {\n this._table = value;\n this.table$.next(value);\n }\n\n table$: ReplaySubject = new ReplaySubject(1);\n private _table: CoreComponent | undefined;\n pagination$ = this.table$.pipe(\n switchMap((core) => combineLatest([core?.table$.pipe(pluck('info')), core?.currentPage$])),\n map(([info, currentPage]) => this.generateList(info.pageTotal, currentPage))\n );\n\n generateList(pages: number, currentPosition: number): Array {\n const paginationLength: 5 | 7 = 5;\n const middle = Math.floor(paginationLength / 2);\n const length = pages < paginationLength ? pages : paginationLength;\n\n return Array.from({ length }, (_, i) => {\n if (i === 0) {\n return 1;\n } else if (pages < paginationLength) {\n return i + 1;\n } else if (i + 1 === length) {\n return pages;\n } else if (currentPosition > middle && currentPosition < pages - middle) {\n return i + currentPosition - (middle - 1);\n } else if (currentPosition > middle && currentPosition < pages - (middle - 1)) {\n return i + currentPosition - middle;\n } else if (currentPosition > middle && currentPosition === pages - (middle - 1)) {\n return i + currentPosition - (middle + 1);\n } else if (currentPosition > middle && currentPosition === pages - 1) {\n return i + currentPosition - (middle + 2);\n } else {\n return i + 1;\n }\n });\n }\n\n goto(page: number): void {\n if (this.table) {\n this.table.page = page - 1;\n }\n }\n}\n",
- "assetsDirs": [],
- "styleUrlsData": "",
- "stylesData": "",
- "accessors": {
+ }
+ ]
+ }
+ ],
+ "deprecated": false,
+ "deprecationMessage": "",
+ "hostBindings": [],
+ "hostListeners": [],
+ "description": "",
+ "rawdescription": "\n",
+ "type": "component",
+ "sourceCode": "import { ChangeDetectionStrategy, Component, Input } from '@angular/core';\nimport { combineLatest, ReplaySubject } from 'rxjs';\nimport { map, pluck, switchMap } from 'rxjs/operators';\nimport { CoreComponent } from '../core.component';\nimport {\n GtPaginationAriaLabels,\n GtPaginationClasses,\n} from '../models/gt-pagination';\n\n@Component({\n selector: 'angular-generic-table-pagination',\n templateUrl: './pagination.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class PaginationComponent {\n get paginationLength(): number {\n return this._paginationLength;\n }\n\n @Input() set paginationLength(value: number) {\n this._paginationLength = +value;\n }\n get classes(): GtPaginationClasses {\n return this._classes;\n }\n\n @Input() set classes(value: GtPaginationClasses) {\n this._classes = value;\n }\n get ariaLabels(): GtPaginationAriaLabels {\n return this._ariaLabels;\n }\n\n @Input() set ariaLabels(value: GtPaginationAriaLabels) {\n this._ariaLabels = value;\n }\n get table(): CoreComponent | undefined {\n return this._table;\n }\n @Input() set table(value: any) {\n this._table = value;\n this.table$.next(value);\n }\n\n table$: ReplaySubject = new ReplaySubject(1);\n private _table: CoreComponent | undefined;\n private _ariaLabels: GtPaginationAriaLabels = {\n nav: 'Table pagination',\n button: 'Go to page ',\n };\n private _classes: GtPaginationClasses = {\n ul: 'pagination',\n li: 'page-item',\n button: 'page-link',\n };\n private _paginationLength: number = 5;\n pagination$ = this.table$.pipe(\n switchMap((core) =>\n combineLatest([core?.table$.pipe(pluck('info')), core?.currentPage$])\n ),\n map(([info, currentPage]) => this.generateList(info.pageTotal, currentPage))\n );\n\n generateList(pages: number, currentPosition: number): Array {\n const middle = Math.floor(this.paginationLength / 2);\n const length =\n pages < this.paginationLength ? pages : this.paginationLength;\n\n return Array.from({ length }, (_, i) => {\n if (i === 0) {\n return 1;\n } else if (pages < this.paginationLength) {\n return i + 1;\n } else if (i + 1 === length) {\n return pages;\n } else if (currentPosition > middle && currentPosition < pages - middle) {\n return i + currentPosition - (middle - 1);\n } else if (\n currentPosition > middle &&\n currentPosition < pages - (middle - 1)\n ) {\n return i + currentPosition - middle;\n } else if (\n currentPosition > middle &&\n currentPosition === pages - (middle - 1)\n ) {\n return i + currentPosition - (middle + 1);\n } else if (currentPosition > middle && currentPosition === pages - 1) {\n return i + currentPosition - (middle + 2);\n } else {\n return i + 1;\n }\n });\n }\n\n goto(page: number): void {\n if (this.table) {\n this.table.page = page - 1;\n }\n }\n}\n",
+ "assetsDirs": [],
+ "styleUrlsData": "",
+ "stylesData": "",
+ "accessors": {
+ "paginationLength": {
+ "name": "paginationLength",
+ "setSignature": {
+ "name": "paginationLength",
+ "type": "void",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "args": [
+ {
+ "name": "value",
+ "type": "number",
+ "deprecated": false,
+ "deprecationMessage": ""
+ }
+ ],
+ "returnType": "void",
+ "line": 20,
+ "jsdoctags": [
+ {
+ "name": "value",
+ "type": "number",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "tagName": {
+ "text": "param"
+ }
+ }
+ ]
+ },
+ "getSignature": {
+ "name": "paginationLength",
+ "type": "number",
+ "returnType": "number",
+ "line": 16
+ }
+ },
+ "classes": {
+ "name": "classes",
+ "setSignature": {
+ "name": "classes",
+ "type": "void",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "args": [
+ {
+ "name": "value",
+ "type": "GtPaginationClasses",
+ "deprecated": false,
+ "deprecationMessage": ""
+ }
+ ],
+ "returnType": "void",
+ "line": 27,
+ "jsdoctags": [
+ {
+ "name": "value",
+ "type": "GtPaginationClasses",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "tagName": {
+ "text": "param"
+ }
+ }
+ ]
+ },
+ "getSignature": {
+ "name": "classes",
+ "type": "",
+ "returnType": "GtPaginationClasses",
+ "line": 23
+ }
+ },
+ "ariaLabels": {
+ "name": "ariaLabels",
+ "setSignature": {
+ "name": "ariaLabels",
+ "type": "void",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "args": [
+ {
+ "name": "value",
+ "type": "GtPaginationAriaLabels",
+ "deprecated": false,
+ "deprecationMessage": ""
+ }
+ ],
+ "returnType": "void",
+ "line": 34,
+ "jsdoctags": [
+ {
+ "name": "value",
+ "type": "GtPaginationAriaLabels",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "tagName": {
+ "text": "param"
+ }
+ }
+ ]
+ },
+ "getSignature": {
+ "name": "ariaLabels",
+ "type": "",
+ "returnType": "GtPaginationAriaLabels",
+ "line": 30
+ }
+ },
"table": {
"name": "table",
"setSignature": {
@@ -2536,7 +3112,7 @@
}
],
"returnType": "void",
- "line": 15,
+ "line": 40,
"jsdoctags": [
{
"name": "value",
@@ -2553,15 +3129,15 @@
"name": "table",
"type": "",
"returnType": "CoreComponent | undefined",
- "line": 12
+ "line": 37
}
}
},
- "templateData": "\n \n\n"
+ "templateData": "\n \n\n"
},
{
"name": "PaginationComponent",
- "id": "component-PaginationComponent-92b860c43fc328eac415387a0a2200a4cedc154aae46901fb84a8828f7cf50e62e173c95d499f21b004ccf64ebf24894cad0b61ec32ff0fab6a44ac4db2ecfe8-1",
+ "id": "component-PaginationComponent-46d9b689fa8fc058795acf4372840786e973f255d8c34ef86c27fee53e2eaf54a19a2143a297960b44f4dcf5a158e812f51a4660dd3dff831f02af02c6610c4e-1",
"file": "projects/docs/src/app/examples/pagination/pagination.component.ts",
"encapsulation": [],
"entryComponents": [],
@@ -2610,13 +3186,13 @@
},
{
"name": "data$",
- "defaultValue": "this.http.get('https://private-730c61-generictable.apiary-mock.com/data').pipe(\n pluck('data'),\n tap((_) => this.loading$.next(false))\n )",
+ "defaultValue": "this.http\n .get('https://private-730c61-generictable.apiary-mock.com/data')\n .pipe(\n pluck('data'),\n tap((_) => this.loading$.next(false))\n )",
"deprecated": false,
"deprecationMessage": "",
"type": "Observable",
"optional": false,
"description": "",
- "line": 26
+ "line": 27
},
{
"name": "loading$",
@@ -2626,7 +3202,7 @@
"type": "",
"optional": false,
"description": "",
- "line": 25
+ "line": 26
},
{
"name": "paginationForm",
@@ -2640,7 +3216,7 @@
},
{
"name": "search$",
- "defaultValue": "this.paginationForm.get('search')?.valueChanges as Observable",
+ "defaultValue": "this.paginationForm.get('search')\n ?.valueChanges as Observable",
"deprecated": false,
"deprecationMessage": "",
"type": "",
@@ -2656,7 +3232,7 @@
"type": "",
"optional": false,
"description": "",
- "line": 32
+ "line": 35
},
{
"name": "tableConfig$",
@@ -2666,7 +3242,7 @@
"type": "ReplaySubject",
"optional": false,
"description": "",
- "line": 31
+ "line": 34
}
],
"methodsClass": [
@@ -2676,7 +3252,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 34,
+ "line": 37,
"deprecated": false,
"deprecationMessage": ""
}
@@ -2688,7 +3264,7 @@
"description": "",
"rawdescription": "\n",
"type": "component",
- "sourceCode": "import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { FormBuilder } from '@angular/forms';\nimport { TableConfig } from '@angular-generic-table/core';\nimport { pluck, tap, withLatestFrom } from 'rxjs/operators';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { ADVANCED_DOCS } from './pagination.snippets';\nimport { HttpClient } from '@angular/common/http';\nimport { DatePipe } from '@angular/common';\n\n@Component({\n selector: 'docs-pagination',\n templateUrl: './pagination.component.html',\n styles: [],\n})\nexport class PaginationComponent implements OnInit {\n constructor(private fb: FormBuilder, private http: HttpClient) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n length: [10],\n search: [''],\n });\n search$ = this.paginationForm.get('search')?.valueChanges as Observable;\n loading$ = new BehaviorSubject(true);\n data$: Observable = this.http.get('https://private-730c61-generictable.apiary-mock.com/data').pipe(\n pluck('data'),\n tap((_) => this.loading$.next(false))\n );\n\n tableConfig$: ReplaySubject = new ReplaySubject(1);\n SNIPPETS = ADVANCED_DOCS;\n\n ngOnInit(): void {\n this.paginationForm\n .get('length')\n ?.valueChanges.pipe(withLatestFrom(this.tableConfig$))\n .subscribe(([length, config]) => {\n length = +length;\n this.tableConfig$.next({\n ...config,\n pagination: { ...config.pagination, length },\n });\n });\n this.tableConfig$.next({\n class: 'table table-mobile text-nowrap mb-0',\n columns: {\n first_name: {\n mobileHeader: true,\n sortable: true,\n },\n last_name: {\n mobileHeader: true,\n sortable: true,\n },\n gender: {\n mobileHeader: 'Sex',\n sortable: true,\n },\n birthday: {\n mobileHeader: true,\n sortable: true,\n class: 'text-right',\n transform: {\n pipe: DatePipe,\n args: ['longDate'],\n },\n },\n },\n pagination: {\n length: this.paginationForm.get('length')?.value || 0,\n },\n });\n }\n}\n\nexport const Pagination: Story = (args: PaginationComponent) => ({\n props: args,\n component: PaginationComponent,\n});\n",
+ "sourceCode": "import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { FormBuilder } from '@angular/forms';\nimport { TableConfig } from '@angular-generic-table/core';\nimport { pluck, tap, withLatestFrom } from 'rxjs/operators';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { ADVANCED_DOCS } from './pagination.snippets';\nimport { HttpClient } from '@angular/common/http';\nimport { DatePipe } from '@angular/common';\n\n@Component({\n selector: 'docs-pagination',\n templateUrl: './pagination.component.html',\n styles: [],\n})\nexport class PaginationComponent implements OnInit {\n constructor(private fb: FormBuilder, private http: HttpClient) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n length: [10],\n search: [''],\n });\n search$ = this.paginationForm.get('search')\n ?.valueChanges as Observable;\n loading$ = new BehaviorSubject(true);\n data$: Observable = this.http\n .get('https://private-730c61-generictable.apiary-mock.com/data')\n .pipe(\n pluck('data'),\n tap((_) => this.loading$.next(false))\n );\n\n tableConfig$: ReplaySubject = new ReplaySubject(1);\n SNIPPETS = ADVANCED_DOCS;\n\n ngOnInit(): void {\n this.paginationForm\n .get('length')\n ?.valueChanges.pipe(withLatestFrom(this.tableConfig$))\n .subscribe(([length, config]) => {\n length = +length;\n this.tableConfig$.next({\n ...config,\n pagination: { ...config.pagination, length },\n });\n });\n this.tableConfig$.next({\n class: 'table text-nowrap',\n columns: {\n first_name: {\n sortable: true,\n },\n last_name: {\n sortable: true,\n },\n gender: {\n sortable: true,\n },\n birthday: {\n sortable: true,\n class: 'text-end',\n transform: {\n pipe: DatePipe,\n args: ['longDate'],\n },\n },\n },\n pagination: {\n length: this.paginationForm.get('length')?.value || 0,\n },\n });\n }\n}\n\nexport const Pagination: Story = (\n args: PaginationComponent\n) => ({\n props: args,\n component: PaginationComponent,\n});\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
@@ -2739,11 +3315,11 @@
"isDuplicate": true,
"duplicateId": 1,
"duplicateName": "PaginationComponent-1",
- "templateData": "\n\n
\n \n \n Table is empty\n
\n \n
\n\n\n\n"
+ "templateData": "\n\n
\n \n Table is empty
\n \n
\n\n\n\n"
},
{
"name": "SimpleComponent",
- "id": "component-SimpleComponent-9a2df61af38ece6922c6d524ac4719931bcb31cb03d85bb4c7320e43b3d784141036b2a0bb4ac89b4cd0beacaf1b42cf78a02b55d91071b3f16356337ad44de0",
+ "id": "component-SimpleComponent-0b5dd4ea6bbba305fe50e9c4b02fd7024d3188e43c876f0e50e70daa720c81241199fe599856f3b690d9d26233f5595f0f5dc4eb9c83b21836b42afcebe76819",
"file": "projects/docs/src/app/examples/simple/simple.component.ts",
"encapsulation": [],
"entryComponents": [],
@@ -2753,7 +3329,7 @@
"selector": "docs-simple",
"styleUrls": [],
"styles": [],
- "template": "\n\n",
+ "template": "\n\n",
"templateUrl": [],
"viewProviders": [],
"inputsClass": [],
@@ -2767,7 +3343,7 @@
"type": "object",
"optional": false,
"description": "",
- "line": 28
+ "line": 33
},
{
"name": "data",
@@ -2777,7 +3353,7 @@
"type": "[]",
"optional": false,
"description": "",
- "line": 14
+ "line": 19
},
{
"name": "SNIPPETS",
@@ -2787,7 +3363,7 @@
"type": "",
"optional": false,
"description": "",
- "line": 37
+ "line": 42
}
],
"methodsClass": [],
@@ -2798,14 +3374,14 @@
"description": "",
"rawdescription": "\n",
"type": "component",
- "sourceCode": "import { Component } from '@angular/core';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { SIMPLE_SNIPPETS } from './simple.snippets';\n\n@Component({\n selector: 'docs-simple',\n template: `\n \n \n `,\n styles: [],\n})\nexport class SimpleComponent {\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config = {\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteFood: {},\n },\n };\n\n SNIPPETS = SIMPLE_SNIPPETS;\n}\n\nexport const Simple: Story = (args: SimpleComponent) => ({\n props: args,\n component: SimpleComponent,\n});\n",
+ "sourceCode": "import { Component } from '@angular/core';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { SIMPLE_SNIPPETS } from './simple.snippets';\n\n@Component({\n selector: 'docs-simple',\n template: `\n \n \n `,\n styles: [],\n})\nexport class SimpleComponent {\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config = {\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteFood: {},\n },\n };\n\n SNIPPETS = SIMPLE_SNIPPETS;\n}\n\nexport const Simple: Story = (args: SimpleComponent) => ({\n props: args,\n component: SimpleComponent,\n});\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": ""
},
{
"name": "TabsComponent",
- "id": "component-TabsComponent-b2d5a1f55fca258698b72f20e5f92f798f4ff6cb550606e1f95d5203f453e8cec878eada8b10c775bce81c8dd533ff57731437d3b928317ee6b4f080f6171577",
+ "id": "component-TabsComponent-131f2b0e18f1436b6da2d042a7674c3bca41a9b2821d858a001d8873585a47d68d9414129ece9918196f171b9c45bdc8e4e00d03967e23b35d316ddbcc4b6563",
"file": "projects/docs/src/app/components/tabs/tabs.component.ts",
"encapsulation": [],
"entryComponents": [],
@@ -2817,7 +3393,7 @@
"./tabs.component.scss"
],
"styles": [],
- "template": "\n - \n \n
\n
\n\n
\n\n",
+ "template": "\n - \n \n
\n
\n\n
\n\n",
"templateUrl": [],
"viewProviders": [],
"inputsClass": [
@@ -2825,7 +3401,7 @@
"name": "content",
"deprecated": false,
"deprecationMessage": "",
- "line": 35,
+ "line": 39,
"type": "any",
"decorators": []
}
@@ -2840,7 +3416,7 @@
"type": "Array",
"optional": false,
"description": "",
- "line": 42,
+ "line": 46,
"modifierKind": [
121
]
@@ -2852,7 +3428,7 @@
"type": "HighlightResult | undefined",
"optional": false,
"description": "",
- "line": 40
+ "line": 44
},
{
"name": "activeIndex",
@@ -2862,7 +3438,7 @@
"type": "number",
"optional": false,
"description": "",
- "line": 39
+ "line": 43
}
],
"methodsClass": [
@@ -2872,7 +3448,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 43,
+ "line": 47,
"deprecated": false,
"deprecationMessage": ""
},
@@ -2889,7 +3465,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 49,
+ "line": 53,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
@@ -2912,11 +3488,11 @@
"description": "",
"rawdescription": "\n",
"type": "component",
- "sourceCode": "import { Component, Input, OnInit } from '@angular/core';\nimport { Story } from '@storybook/angular/types-6-0';\nimport hljs from 'highlight.js/lib/core';\nimport { HighlightResult } from 'highlight.js';\n\nimport typescript from 'highlight.js/lib/languages/typescript';\nimport xml from 'highlight.js/lib/languages/xml';\nimport scss from 'highlight.js/lib/languages/scss';\n\nhljs.registerLanguage('typescript', typescript);\nhljs.registerLanguage('scss', scss);\nhljs.registerLanguage('xml', xml);\n\n@Component({\n selector: 'docs-tabs',\n template: `\n \n - \n \n
\n
\n \n
\n \n `,\n styleUrls: ['./tabs.component.scss'],\n})\nexport class TabsComponent implements OnInit {\n get content(): any {\n return this._content;\n }\n\n @Input() set content(value: any) {\n this._content = value;\n }\n constructor() {}\n activeIndex = 0;\n activeContent: HighlightResult | undefined;\n\n private _content: Array<{}> = [];\n ngOnInit(): void {\n this.activeContent = hljs.highlight(this.content[this.activeIndex].code, {\n language: this.content[this.activeIndex].language,\n });\n }\n\n view(index: number): void {\n this.activeIndex = index;\n this.activeContent = hljs.highlight(this.content[index].code, { language: this.content[index].language });\n }\n}\n\nexport const Tabs: Story = (args: TabsComponent) => ({\n props: args,\n component: TabsComponent,\n});\n",
+ "sourceCode": "import { Component, Input, OnInit } from '@angular/core';\nimport { Story } from '@storybook/angular/types-6-0';\nimport hljs from 'highlight.js/lib/core';\nimport { HighlightResult } from 'highlight.js';\n\nimport typescript from 'highlight.js/lib/languages/typescript';\nimport xml from 'highlight.js/lib/languages/xml';\nimport scss from 'highlight.js/lib/languages/scss';\n\nhljs.registerLanguage('typescript', typescript);\nhljs.registerLanguage('scss', scss);\nhljs.registerLanguage('xml', xml);\n\n@Component({\n selector: 'docs-tabs',\n template: `\n \n - \n \n
\n
\n \n
\n \n `,\n styleUrls: ['./tabs.component.scss'],\n})\nexport class TabsComponent implements OnInit {\n get content(): any {\n return this._content;\n }\n\n @Input() set content(value: any) {\n this._content = value;\n }\n constructor() {}\n activeIndex = 0;\n activeContent: HighlightResult | undefined;\n\n private _content: Array<{}> = [];\n ngOnInit(): void {\n this.activeContent = hljs.highlight(this.content[this.activeIndex].code, {\n language: this.content[this.activeIndex].language,\n });\n }\n\n view(index: number): void {\n this.activeIndex = index;\n this.activeContent = hljs.highlight(this.content[index].code, {\n language: this.content[index].language,\n });\n }\n}\n\nexport const Tabs: Story = (args: TabsComponent) => ({\n props: args,\n component: TabsComponent,\n});\n",
"assetsDirs": [],
"styleUrlsData": [
{
- "data": "pre {\n background-image: linear-gradient(0deg, #f8f8f8 25%, #ffffff 25%, #ffffff 50%, #f8f8f8 50%, #f8f8f8 75%, #ffffff 75%, #ffffff 100%);\n background-size: 84.00px 84.00px;\n padding: 21px 1rem;\n}\n",
+ "data": "pre {\n background-image: linear-gradient(0deg, #f8f8f8 25%, #ffffff 25%, #ffffff 50%, #f8f8f8 50%, #f8f8f8 75%, #ffffff 75%, #ffffff 100%);\n background-size: 84.00px 84.00px;\n padding: 21px 1rem;\n}\nul {\n &::-webkit-scrollbar {\n display: none;\n }\n -ms-overflow-style: none; /* IE and Edge */\n scrollbar-width: none; /* Firefox */\n}\n",
"styleUrl": "./tabs.component.scss"
}
],
@@ -2927,7 +3503,7 @@
"deprecated": false,
"deprecationMessage": "",
"args": [],
- "line": 37
+ "line": 41
},
"implements": [
"OnInit"
@@ -2949,7 +3525,7 @@
}
],
"returnType": "void",
- "line": 35,
+ "line": 39,
"jsdoctags": [
{
"name": "value",
@@ -2966,14 +3542,14 @@
"name": "content",
"type": "any",
"returnType": "any",
- "line": 31
+ "line": 35
}
}
}
},
{
"name": "TransposeComponent",
- "id": "component-TransposeComponent-242be5f01c510d9ba9e1eb3afc5690137f27a753a3f55b1379dfff902d289ecd6af60851b26a5d5e8786425cbf7b492c09e0a0b1ba4f2d4d3e62c23d4debb98d",
+ "id": "component-TransposeComponent-5c484b1c03b3a68cc9049f5911de2a9a6592a5f67ce934a34fba7d66acdf6cd43a90d88750f22ef97abb04510dea0f05a5f8340cf2f1f24a4b231618fd1e05e9",
"file": "projects/docs/src/app/examples/transpose/transpose.component.ts",
"encapsulation": [],
"entryComponents": [],
@@ -2983,21 +3559,26 @@
"selector": "docs-transpose",
"styleUrls": [],
"styles": [],
- "template": "\n\n\n\n \n Table is empty
\n\n\n \n 😀\n 🙂\n 😐\n 😭\n
\n\n\n",
+ "template": "\n",
"templateUrl": [],
"viewProviders": [],
"inputsClass": [],
"outputsClass": [],
"propertiesClass": [
{
- "name": "config",
- "defaultValue": "{}",
+ "name": "combined",
"deprecated": false,
"deprecationMessage": "",
- "type": "TableConfig",
+ "type": "TemplateRef | undefined",
"optional": false,
"description": "",
- "line": 33
+ "line": 104,
+ "decorators": [
+ {
+ "name": "ViewChild",
+ "stringifiedArguments": "'combined', {static: true}"
+ }
+ ]
},
{
"name": "data",
@@ -3007,20 +3588,35 @@
"type": "TableRows",
"optional": false,
"description": "",
- "line": 34
+ "line": 116
},
{
- "name": "feelings",
+ "name": "delta",
"deprecated": false,
"deprecationMessage": "",
- "type": "TemplateRef | undefined",
+ "type": "TemplateRef | undefined",
"optional": false,
"description": "",
- "line": 31,
+ "line": 98,
"decorators": [
{
"name": "ViewChild",
- "stringifiedArguments": "'feelings', {static: true}"
+ "stringifiedArguments": "'delta', {static: true}"
+ }
+ ]
+ },
+ {
+ "name": "deltaIndex",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "TemplateRef | undefined",
+ "optional": false,
+ "description": "",
+ "line": 101,
+ "decorators": [
+ {
+ "name": "ViewChild",
+ "stringifiedArguments": "'deltaIndex', {static: true}"
}
]
},
@@ -3032,7 +3628,27 @@
"type": "",
"optional": false,
"description": "",
- "line": 32
+ "line": 107
+ },
+ {
+ "name": "reactiveForm",
+ "defaultValue": "this.fb.group({\n length: [10],\n search: [''],\n })",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "",
+ "optional": false,
+ "description": "",
+ "line": 108
+ },
+ {
+ "name": "search$",
+ "defaultValue": "this.reactiveForm.get('search')?.valueChanges as Observable",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "",
+ "optional": false,
+ "description": "",
+ "line": 112
},
{
"name": "SNIPPETS",
@@ -3042,7 +3658,17 @@
"type": "",
"optional": false,
"description": "",
- "line": 105
+ "line": 261
+ },
+ {
+ "name": "tableConfig$",
+ "defaultValue": "new BehaviorSubject(\n {}\n )",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "BehaviorSubject",
+ "optional": false,
+ "description": "",
+ "line": 113
}
],
"methodsClass": [
@@ -3052,7 +3678,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 67,
+ "line": 140,
"deprecated": false,
"deprecationMessage": ""
},
@@ -3062,7 +3688,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 70,
+ "line": 143,
"deprecated": false,
"deprecationMessage": ""
},
@@ -3072,7 +3698,7 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 36,
+ "line": 120,
"deprecated": false,
"deprecationMessage": ""
},
@@ -3082,7 +3708,17 @@
"optional": false,
"returnType": "void",
"typeParameters": [],
- "line": 62,
+ "line": 135,
+ "deprecated": false,
+ "deprecationMessage": ""
+ },
+ {
+ "name": "transpose",
+ "args": [],
+ "optional": false,
+ "returnType": "void",
+ "typeParameters": [],
+ "line": 195,
"deprecated": false,
"deprecationMessage": ""
}
@@ -3094,10 +3730,36 @@
"description": "",
"rawdescription": "\n",
"type": "component",
- "sourceCode": "import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { TRANSPOSE_SNIPPETS } from './transpose.snippets';\nimport { PercentPipe } from '@angular/common';\nimport { TableConfig, TableRows } from '@angular-generic-table/core';\nimport { BehaviorSubject } from 'rxjs';\n\n@Component({\n selector: 'docs-transpose',\n template: `\n \n \n \n \n \n Table is empty
\n \n \n \n 😀\n 🙂\n 😐\n 😭\n
\n \n \n `,\n styles: [],\n})\nexport class TransposeComponent implements OnInit {\n @ViewChild('feelings', { static: true }) feelings: TemplateRef | undefined;\n loading$ = new BehaviorSubject(false);\n config: TableConfig = {};\n data: TableRows = [];\n\n ngOnInit(): void {\n this.config = {\n rows: {\n year: {\n class: 'text-right',\n header: false,\n },\n value: {\n class: 'text-right',\n },\n change: {\n header: 'Change %',\n transform: {\n pipe: PercentPipe,\n args: [],\n },\n class: 'text-right',\n },\n feeling: {\n templateRef: this.feelings,\n class: 'text-right',\n },\n },\n };\n this.load();\n }\n simulateLoad(): void {\n this.loading$.next(true);\n // set loading state to false after 2 seconds\n setTimeout(() => this.loading$.next(false), 2000);\n }\n empty(): void {\n this.data = [];\n }\n load(): void {\n this.data = [\n {\n year: '2017',\n value: 50,\n change: 0.5,\n feeling: 'thrilled',\n },\n {\n year: '2018',\n value: 75,\n change: 0.33,\n feeling: 'positive',\n },\n {\n year: '2019',\n value: 100,\n change: 1.5,\n feeling: 'thrilled',\n },\n {\n year: '2020',\n value: 250,\n change: -0.8,\n feeling: 'negative',\n },\n {\n year: '2021',\n value: 50,\n change: null,\n feeling: 'neutral',\n },\n ];\n }\n\n SNIPPETS = TRANSPOSE_SNIPPETS;\n}\n\nexport const Horizontal: Story = (args: TransposeComponent) => ({\n props: args,\n component: TransposeComponent,\n});\n",
+ "sourceCode": "import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { BehaviorSubject, Observable } from 'rxjs';\nimport {\n TableConfig,\n TableRows,\n GtDeltaComponent,\n} from '@angular-generic-table/core';\nimport { FormBuilder } from '@angular/forms';\nimport { withLatestFrom } from 'rxjs/operators';\nimport { Story } from '@storybook/angular/types-6-0';\nimport { TRANSPOSE_SNIPPETS } from './transpose.snippets';\n\n@Component({\n selector: 'docs-transpose',\n template: `\n \n `,\n styles: [],\n})\nexport class TransposeComponent implements OnInit {\n @ViewChild('delta', { static: true }) delta:\n | TemplateRef\n | undefined;\n @ViewChild('deltaIndex', { static: true }) deltaIndex:\n | TemplateRef\n | undefined;\n @ViewChild('combined', { static: true }) combined:\n | TemplateRef\n | undefined;\n loading$ = new BehaviorSubject(false);\n reactiveForm = this.fb.group({\n length: [10],\n search: [''],\n });\n search$ = this.reactiveForm.get('search')?.valueChanges as Observable;\n tableConfig$: BehaviorSubject = new BehaviorSubject(\n {}\n );\n data: TableRows = [];\n\n constructor(private fb: FormBuilder) {}\n\n ngOnInit(): void {\n this.transpose();\n this.load();\n this.reactiveForm\n .get('length')\n ?.valueChanges.pipe(withLatestFrom(this.tableConfig$))\n .subscribe(([length, config]) => {\n length = +length;\n this.tableConfig$.next({\n ...config,\n pagination: { ...config.pagination, length },\n });\n });\n }\n\n simulateLoad(): void {\n this.loading$.next(true);\n // set loading state to false after 2 seconds\n setTimeout(() => this.loading$.next(false), 2000);\n }\n empty(): void {\n this.data = [];\n }\n load(): void {\n this.data = [\n {\n year: '2010',\n value: 15,\n },\n {\n year: '2011',\n value: 30,\n },\n {\n year: '2012',\n value: 25,\n },\n {\n year: '2013',\n value: 0,\n },\n {\n year: '2014',\n value: 40,\n },\n {\n year: '2015',\n value: 60,\n },\n {\n year: '2016',\n value: -5,\n },\n {\n year: '2018',\n value: 75,\n },\n {\n year: '2019',\n value: 100,\n },\n {\n year: '2020',\n value: 250,\n },\n {\n year: '2021',\n value: 50,\n },\n {\n year: '2022',\n value: 60,\n },\n ];\n }\n transpose(): void {\n if (this.tableConfig$.value.columns) {\n this.tableConfig$.next({\n stickyHeaders: {\n row: true,\n column: true,\n },\n rows: {\n year: {\n sortable: true,\n header: false,\n class: 'text-end',\n },\n value: {\n class: 'text-end',\n },\n delta: {\n header: 'Delta %',\n templateRef: this.delta,\n class: 'text-end',\n },\n deltaIndex: {\n header: 'Since inception %',\n templateRef: this.deltaIndex,\n class: 'text-end',\n },\n combined: {\n header: 'Value with change',\n templateRef: this.combined,\n class: 'text-end text-nowrap',\n },\n },\n });\n } else {\n this.tableConfig$.next({\n stickyHeaders: {\n row: true,\n column: true,\n },\n columns: {\n year: {\n sortable: true,\n },\n value: {\n class: 'text-end',\n },\n delta: {\n header: 'Delta %',\n templateRef: this.delta,\n class: 'text-end',\n },\n deltaIndex: {\n header: 'Since inception %',\n templateRef: this.deltaIndex,\n class: 'text-end',\n },\n combined: {\n header: 'Value with change',\n templateRef: this.combined,\n class: 'text-end text-nowrap',\n },\n },\n pagination: { length: this.reactiveForm.get('length')?.value },\n });\n }\n }\n SNIPPETS = TRANSPOSE_SNIPPETS;\n}\n\nexport const Transpose: Story = (\n args: TransposeComponent\n) => ({\n props: args,\n component: TransposeComponent,\n});\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
+ "constructorObj": {
+ "name": "constructor",
+ "description": "",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "args": [
+ {
+ "name": "fb",
+ "type": "FormBuilder",
+ "deprecated": false,
+ "deprecationMessage": ""
+ }
+ ],
+ "line": 116,
+ "jsdoctags": [
+ {
+ "name": "fb",
+ "type": "FormBuilder",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "tagName": {
+ "text": "param"
+ }
+ }
+ ]
+ },
"implements": [
"OnInit"
]
@@ -3106,13 +3768,13 @@
"modules": [
{
"name": "AppModule",
- "id": "module-AppModule-7e12e11d8c344b12211dd592c3ec6e776f26efbdd5a29f0f56813b4867e2b34c9be1dedaaf395369cf2be26f8d5ed19bd128814e2ff231c5c3f21d6a5ce4ce0b",
+ "id": "module-AppModule-89f7a91051436709d9b7d773d883e71f72b875e31bdd56d24331cf0f2c214695f802c5b4a2de3f4eb30cd92f0fcf61f01974f3e855b3a513d673f32a8b0e5c44",
"description": "",
"deprecationMessage": "",
"deprecated": false,
"file": "projects/docs/src/app/app.module.ts",
"methods": [],
- "sourceCode": "import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { AppRoutingModule } from './app-routing.module';\nimport { AppComponent } from './app.component';\nimport { GenericTableCoreModule, GenericTablePaginationModule } from '@angular-generic-table/core';\nimport { ReactiveFormsModule } from '@angular/forms';\nimport { AdvancedComponent } from './examples/advanced/advanced.component';\nimport { SimpleComponent } from './examples/simple/simple.component';\nimport { TabsComponent } from './components/tabs/tabs.component';\nimport { CustomTemplatesComponent } from './examples/custom-templates/custom-templates.component';\nimport { PaginationComponent } from './examples/pagination/pagination.component';\nimport { CommonModule } from '@angular/common';\nimport { HttpClientModule } from '@angular/common/http';\nimport { TransposeComponent } from './examples/transpose/transpose.component';\nimport { MobileLayoutComponent } from './examples/mobile-layout/mobile-layout.component';\nimport { NestedDataComponent } from './examples/nested-data/nested-data.component';\n\n@NgModule({\n declarations: [\n AppComponent,\n AdvancedComponent,\n SimpleComponent,\n TransposeComponent,\n PaginationComponent,\n TabsComponent,\n CustomTemplatesComponent,\n MobileLayoutComponent,\n NestedDataComponent,\n ],\n imports: [\n BrowserModule,\n CommonModule,\n AppRoutingModule,\n GenericTableCoreModule,\n GenericTablePaginationModule,\n ReactiveFormsModule,\n HttpClientModule,\n ],\n providers: [],\n bootstrap: [AppComponent],\n})\nexport class AppModule {}\n",
+ "sourceCode": "import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { AppRoutingModule } from './app-routing.module';\nimport { AppComponent } from './app.component';\nimport {\n GenericTableCoreModule,\n GenericTablePaginationModule,\n} from '@angular-generic-table/core';\nimport { ReactiveFormsModule } from '@angular/forms';\nimport { AdvancedComponent } from './examples/advanced/advanced.component';\nimport { SimpleComponent } from './examples/simple/simple.component';\nimport { TabsComponent } from './components/tabs/tabs.component';\nimport { CustomTemplatesComponent } from './examples/custom-templates/custom-templates.component';\nimport { PaginationComponent } from './examples/pagination/pagination.component';\nimport { CommonModule } from '@angular/common';\nimport { HttpClientModule } from '@angular/common/http';\nimport { HorizontalTableComponent } from './examples/horizontal-table/horizontal-table.component';\nimport { MobileLayoutComponent } from './examples/mobile-layout/mobile-layout.component';\nimport { NestedDataComponent } from './examples/nested-data/nested-data.component';\nimport { TransposeComponent } from './examples/transpose/transpose.component';\n\n@NgModule({\n declarations: [\n AppComponent,\n AdvancedComponent,\n SimpleComponent,\n HorizontalTableComponent,\n PaginationComponent,\n TabsComponent,\n CustomTemplatesComponent,\n MobileLayoutComponent,\n NestedDataComponent,\n TransposeComponent,\n ],\n imports: [\n BrowserModule,\n CommonModule,\n AppRoutingModule,\n GenericTableCoreModule,\n GenericTablePaginationModule,\n ReactiveFormsModule,\n HttpClientModule,\n ],\n providers: [],\n bootstrap: [AppComponent],\n})\nexport class AppModule {}\n",
"children": [
{
"type": "providers",
@@ -3130,6 +3792,9 @@
{
"name": "CustomTemplatesComponent"
},
+ {
+ "name": "HorizontalTableComponent"
+ },
{
"name": "MobileLayoutComponent"
},
@@ -3181,13 +3846,13 @@
},
{
"name": "AppRoutingModule",
- "id": "module-AppRoutingModule-c01360d1b4ecda84c76f872f835d27819af7ddb3fe980fabb585483b795c6c0e3cfc87642d3b4a3c0d8bed3de486b6468aa2600c58780f42fbe30721f36ac788",
+ "id": "module-AppRoutingModule-eb45b2ac4ab3c78daaada47c136f608a009dd6215be163817bf37fa978e49db3729ac78473211a053c7450fe92772998aa972259265779327ad289ede4dfd448",
"description": "",
"deprecationMessage": "",
"deprecated": false,
"file": "projects/docs/src/app/app-routing.module.ts",
"methods": [],
- "sourceCode": "import { NgModule } from '@angular/core';\nimport { Routes, RouterModule } from '@angular/router';\nimport { AdvancedComponent } from './examples/advanced/advanced.component';\nimport { SimpleComponent } from './examples/simple/simple.component';\nimport { CustomTemplatesComponent } from './examples/custom-templates/custom-templates.component';\nimport { PaginationComponent } from './examples/pagination/pagination.component';\nimport { TransposeComponent } from './examples/transpose/transpose.component';\nimport {MobileLayoutComponent} from \"./examples/mobile-layout/mobile-layout.component\";\nimport {NestedDataComponent} from \"./examples/nested-data/nested-data.component\";\n\nconst routes: Routes = [\n {\n path: 'advanced',\n component: AdvancedComponent,\n },\n {\n path: 'pagination',\n component: PaginationComponent,\n },\n {\n path: 'simple',\n component: SimpleComponent,\n },\n {\n path: 'transpose',\n component: TransposeComponent,\n },\n {\n path: 'custom-templates',\n component: CustomTemplatesComponent,\n },\n {\n path: 'mobile-layout',\n component: MobileLayoutComponent,\n },\n {\n path: 'nested',\n component: NestedDataComponent,\n },\n];\n\n@NgModule({\n imports: [RouterModule.forRoot(routes)],\n exports: [RouterModule],\n})\nexport class AppRoutingModule {}\n",
+ "sourceCode": "import { NgModule } from '@angular/core';\nimport { Routes, RouterModule } from '@angular/router';\nimport { AdvancedComponent } from './examples/advanced/advanced.component';\nimport { SimpleComponent } from './examples/simple/simple.component';\nimport { CustomTemplatesComponent } from './examples/custom-templates/custom-templates.component';\nimport { PaginationComponent } from './examples/pagination/pagination.component';\nimport { HorizontalTableComponent } from './examples/horizontal-table/horizontal-table.component';\nimport { MobileLayoutComponent } from './examples/mobile-layout/mobile-layout.component';\nimport { NestedDataComponent } from './examples/nested-data/nested-data.component';\nimport { TransposeComponent } from './examples/transpose/transpose.component';\n\nconst routes: Routes = [\n {\n path: 'advanced',\n component: AdvancedComponent,\n },\n {\n path: 'pagination',\n component: PaginationComponent,\n },\n {\n path: 'simple',\n component: SimpleComponent,\n },\n {\n path: 'horizontal-table',\n component: HorizontalTableComponent,\n },\n {\n path: 'custom-templates',\n component: CustomTemplatesComponent,\n },\n {\n path: 'mobile-layout',\n component: MobileLayoutComponent,\n },\n {\n path: 'nested',\n component: NestedDataComponent,\n },\n {\n path: 'transpose',\n component: TransposeComponent,\n },\n];\n\n@NgModule({\n imports: [RouterModule.forRoot(routes)],\n exports: [RouterModule],\n})\nexport class AppRoutingModule {}\n",
"children": [
{
"type": "providers",
@@ -3217,13 +3882,13 @@
},
{
"name": "GenericTableCoreModule",
- "id": "module-GenericTableCoreModule-f48995d25178a40996b4dd9bd48332aeaa61ea9cca67f19a2d0e27a2832273fa74dee140f635991aee277c438cb426c90e020536a2ae4d56ead6d459f0119b40",
+ "id": "module-GenericTableCoreModule-412838afb3fc8bd33204e72942b4202f97e8a2240e4cfecd65853725d6c8a7bcf4004af04049e6ade1d2a83bf866d462a2c7b1d2d977ff4a5b8f3a66b46e4ca7",
"description": "",
"deprecationMessage": "",
"deprecated": false,
"file": "projects/core/src/lib/core.module.ts",
"methods": [],
- "sourceCode": "import { NgModule } from '@angular/core';\nimport { CoreComponent } from './core.component';\nimport { CommonModule } from '@angular/common';\nimport { SortClassPipe } from './pipes/sort-class.pipe';\nimport { DashCasePipe } from './pipes/dash-case.pipe';\nimport { HighlightPipe } from './pipes/highlight.pipe';\nimport { CapitalCasePipe } from './pipes/capital-case.pipe';\nimport { DynamicPipe } from './pipes/dynamic.pipe';\n\n@NgModule({\n declarations: [CoreComponent, SortClassPipe, DashCasePipe, HighlightPipe, CapitalCasePipe, CapitalCasePipe, DynamicPipe],\n imports: [CommonModule],\n exports: [CoreComponent],\n})\nexport class GenericTableCoreModule {}\n",
+ "sourceCode": "import { NgModule } from '@angular/core';\nimport { CoreComponent } from './core.component';\nimport { CommonModule } from '@angular/common';\nimport { SortClassPipe } from './pipes/sort-class.pipe';\nimport { DashCasePipe } from './pipes/dash-case.pipe';\nimport { HighlightPipe } from './pipes/highlight.pipe';\nimport { CapitalCasePipe } from './pipes/capital-case.pipe';\nimport { DynamicPipe } from './pipes/dynamic.pipe';\nimport { GtDeltaComponent } from './gt-delta/gt-delta.component';\n\n@NgModule({\n declarations: [\n CoreComponent,\n SortClassPipe,\n DashCasePipe,\n HighlightPipe,\n CapitalCasePipe,\n CapitalCasePipe,\n DynamicPipe,\n GtDeltaComponent,\n ],\n imports: [CommonModule],\n exports: [CoreComponent, GtDeltaComponent],\n})\nexport class GenericTableCoreModule {}\n",
"children": [
{
"type": "providers",
@@ -3247,6 +3912,9 @@
{
"name": "DynamicPipe"
},
+ {
+ "name": "GtDeltaComponent"
+ },
{
"name": "HighlightPipe"
},
@@ -3264,6 +3932,9 @@
"elements": [
{
"name": "CoreComponent"
+ },
+ {
+ "name": "GtDeltaComponent"
}
]
},
@@ -3342,7 +4013,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "Story",
- "defaultValue": "(args: AdvancedComponent) => ({\n props: args,\n component: AdvancedComponent,\n})"
+ "defaultValue": "(\n args: AdvancedComponent\n) => ({\n props: args,\n component: AdvancedComponent,\n})"
},
{
"name": "ADVANCED_DOCS",
@@ -3352,7 +4023,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
- "defaultValue": "[\n {\n name: 'advanced.component.html',\n code: `\n\n\n
\n \n \n Table is empty\n
\n \n
\n\n
\n \n
\n
\n \n
\n
Current page: {{pagination.current +1}}
\n
Total pages: {{pagination.total}}
\n
\n Records: {{(data$ | async).length}}\n
\n
\n {{clicked}}\n
\n
\n\n \n\n\n \n`,\n language: 'xml',\n },\n {\n name: 'advanced.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { FormBuilder } from '@angular/forms';\nimport { TableColumn, TableConfig, TableRow } from '@angular-generic-table/core';\nimport { withLatestFrom } from 'rxjs/operators';\n\n@Component({\n selector: 'docs-advanced',\n templateUrl: './advanced.component.html',\n styles: [],\n})\nexport class AdvancedComponent implements OnInit {\n get currentPage$(): Observable {\n return this._currentPage$.asObservable();\n }\n\n set currentPage(value: number) {\n this._currentPage$.next(value);\n }\n constructor(private fb: FormBuilder) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n length: [10],\n search: [''],\n });\n search$ = this.paginationForm.get('search')?.valueChanges as Observable;\n loading$ = new BehaviorSubject(true);\n data$: BehaviorSubject = new BehaviorSubject([\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteColor: '#26BFAF',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteColor: '#0f0',\n favoriteFood: 'Pizza',\n },\n ]);\n\n private _currentPage$ = new BehaviorSubject(0);\n\n clicked: string = '';\n maleFirstNames = ['Peter', 'Clark', 'Ruben', 'John', 'Jack', 'Roscoe'];\n femaleFirstNames = ['Mary Jane', 'Kim', 'Sarah', 'Michelle', 'Ann'];\n lastNames = ['Andersson', 'Smith', 'Parker', 'Kent', 'Rogers', 'Lane', 'Jackson'];\n foods = ['Pizza', 'Pasta', 'Hamburger', 'Pancakes', 'Tacos', 'Lasagna', 'Meatloaf'];\n colors = ['#33d60b', '#dcafff', '#3fc9ff', '#ff1600', '#5238b1', '#fff'];\n\n tableConfig$: ReplaySubject = new ReplaySubject(1);\n\n addData(): void {\n this.data$.next([...this.data$.getValue(), this.randomRecord()]);\n }\n\n removeData(): void {\n this.data$.next([]);\n }\n\n simulateLoad(): void {\n this.loading$.next(true);\n // set loading state to false after 2 seconds\n setTimeout(() => this.loading$.next(false), 2000);\n }\n\n clickAction(row: TableRow, column: { key: string; value: TableColumn }, index: number): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = \\`clicked row number: \\${index}\\`;\n }\n\n randomRecord(): TableRow {\n const random = Math.floor(Math.random() * 2);\n const newRecord = {\n firstName: random\n ? this.maleFirstNames[Math.floor(Math.random() * this.maleFirstNames.length)]\n : this.femaleFirstNames[Math.floor(Math.random() * this.femaleFirstNames.length)],\n lastName: this.lastNames[Math.floor(Math.random() * this.lastNames.length)],\n gender: random ? 'male' : 'female',\n favoriteColor: this.colors[Math.floor(Math.random() * this.colors.length)],\n favoriteFood: this.foods[Math.floor(Math.random() * this.foods.length)],\n };\n console.log('added new random record:', newRecord);\n\n return newRecord;\n }\n\n next = () => {\n this.currentPage = this._currentPage$.value + 1;\n };\n prev = () => {\n this.currentPage = this._currentPage$.value - 1;\n };\n\n ngOnInit(): void {\n this.simulateLoad();\n this.paginationForm\n .get('length')\n ?.valueChanges.pipe(withLatestFrom(this.tableConfig$))\n .subscribe(([length, config]) => {\n length = +length;\n this.tableConfig$.next({\n ...config,\n pagination: { ...config.pagination, length },\n });\n });\n this.tableConfig$.next({\n class: 'table table-mobile text-nowrap mb-0',\n columns: {\n firstName: {\n header: 'First name',\n mobileHeader: true,\n sortable: true,\n order: 0,\n },\n lastName: {\n header: 'Last name',\n mobileHeader: true,\n hidden: false,\n sortable: true,\n },\n gender: {\n mobileHeader: 'Sex',\n sortable: true,\n order: 1,\n },\n favoriteColor: {\n header: 'Favorite color',\n mobileHeader: true,\n templateRef: this.color,\n sortable: false,\n order: 2,\n search: false,\n class: 'custom-class',\n },\n favoriteFood: {\n mobileHeader: true,\n header: 'Favorite food',\n hidden: false,\n sortable: true,\n order: 0,\n },\n action: {\n mobileHeader: false,\n header: false,\n templateRef: this.actions,\n order: 6,\n },\n },\n pagination: {\n length: this.paginationForm.get('length')?.value || 0,\n },\n });\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { ReactiveFormsModule } from '@angular/forms';\n\nimport { AdvancedComponent } from './advanced.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [AdvancedComponent],\n imports: [BrowserModule, ReactiveFormsModule, GenericTableCoreModule],\n bootstrap: [AdvancedComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
+ "defaultValue": "[\n {\n name: 'advanced.component.html',\n code: `\n
\n \n
\n
\n \n
\n
\n \n
\n
\n\n\n
\n \n \n Table is empty\n
\n \n
\n\n
\n \n
\n
\n \n
\n
Current page: {{pagination.current +1}}
\n
Total pages: {{pagination.total}}
\n
\n Records: {{(data$ | async).length}}\n
\n
\n {{clicked}}\n
\n
\n\n \n\n\n \n`,\n language: 'xml',\n },\n {\n name: 'advanced.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { FormBuilder } from '@angular/forms';\nimport { TableColumn, TableConfig, TableRow } from '@angular-generic-table/core';\nimport { withLatestFrom } from 'rxjs/operators';\n\n@Component({\n selector: 'docs-advanced',\n templateUrl: './advanced.component.html',\n styles: [],\n})\nexport class AdvancedComponent implements OnInit {\n get currentPage$(): Observable {\n return this._currentPage$.asObservable();\n }\n\n set currentPage(value: number) {\n this._currentPage$.next(value);\n }\n constructor(private fb: FormBuilder) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n length: [10],\n search: [''],\n });\n search$ = this.paginationForm.get('search')?.valueChanges as Observable;\n loading$ = new BehaviorSubject(true);\n data$: BehaviorSubject = new BehaviorSubject([\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteColor: '#26BFAF',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteColor: '#0f0',\n favoriteFood: 'Pizza',\n },\n ]);\n\n private _currentPage$ = new BehaviorSubject(0);\n\n clicked: string = '';\n maleFirstNames = ['Peter', 'Clark', 'Ruben', 'John', 'Jack', 'Roscoe'];\n femaleFirstNames = ['Mary Jane', 'Kim', 'Sarah', 'Michelle', 'Ann'];\n lastNames = ['Andersson', 'Smith', 'Parker', 'Kent', 'Rogers', 'Lane', 'Jackson'];\n foods = ['Pizza', 'Pasta', 'Hamburger', 'Pancakes', 'Tacos', 'Lasagna', 'Meatloaf'];\n colors = ['#33d60b', '#dcafff', '#3fc9ff', '#ff1600', '#5238b1', '#fff'];\n\n tableConfig$: ReplaySubject = new ReplaySubject(1);\n\n addData(): void {\n this.data$.next([...this.data$.getValue(), this.randomRecord()]);\n }\n\n removeData(): void {\n this.data$.next([]);\n }\n\n simulateLoad(): void {\n this.loading$.next(true);\n // set loading state to false after 2 seconds\n setTimeout(() => this.loading$.next(false), 2000);\n }\n\n clickAction(row: TableRow, column: { key: string; value: TableColumn }, index: number): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = \\`clicked row number: \\${index}\\`;\n }\n\n randomRecord(): TableRow {\n const random = Math.floor(Math.random() * 2);\n const newRecord = {\n firstName: random\n ? this.maleFirstNames[Math.floor(Math.random() * this.maleFirstNames.length)]\n : this.femaleFirstNames[Math.floor(Math.random() * this.femaleFirstNames.length)],\n lastName: this.lastNames[Math.floor(Math.random() * this.lastNames.length)],\n gender: random ? 'male' : 'female',\n favoriteColor: this.colors[Math.floor(Math.random() * this.colors.length)],\n favoriteFood: this.foods[Math.floor(Math.random() * this.foods.length)],\n };\n console.log('added new random record:', newRecord);\n\n return newRecord;\n }\n\n next = () => {\n this.currentPage = this._currentPage$.value + 1;\n };\n prev = () => {\n this.currentPage = this._currentPage$.value - 1;\n };\n\n ngOnInit(): void {\n this.simulateLoad();\n this.paginationForm\n .get('length')\n ?.valueChanges.pipe(withLatestFrom(this.tableConfig$))\n .subscribe(([length, config]) => {\n length = +length;\n this.tableConfig$.next({\n ...config,\n pagination: { ...config.pagination, length },\n });\n });\n this.tableConfig$.next({\n class: 'table table-mobile text-nowrap mb-0',\n columns: {\n firstName: {\n header: 'First name',\n mobileHeader: true,\n sortable: true,\n order: 0,\n },\n lastName: {\n header: 'Last name',\n mobileHeader: true,\n hidden: false,\n sortable: true,\n },\n gender: {\n mobileHeader: 'Sex',\n sortable: true,\n order: 1,\n },\n favoriteColor: {\n header: 'Favorite color',\n mobileHeader: true,\n templateRef: this.color,\n sortable: false,\n order: 2,\n search: false,\n class: 'custom-class',\n },\n favoriteFood: {\n mobileHeader: true,\n header: 'Favorite food',\n hidden: false,\n sortable: true,\n order: 0,\n },\n action: {\n mobileHeader: false,\n header: false,\n templateRef: this.actions,\n order: 6,\n },\n },\n pagination: {\n length: this.paginationForm.get('length')?.value || 0,\n },\n });\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { ReactiveFormsModule } from '@angular/forms';\n\nimport { AdvancedComponent } from './advanced.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [AdvancedComponent],\n imports: [BrowserModule, ReactiveFormsModule, GenericTableCoreModule],\n bootstrap: [AdvancedComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
},
{
"name": "ADVANCED_DOCS",
@@ -3362,7 +4033,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
- "defaultValue": "[\n {\n name: 'pagination.component.html',\n code: `\n\n
\n \n \n Table is empty\n
\n \n
\n\n`,\n language: 'xml',\n },\n {\n name: 'pagination.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { FormBuilder } from '@angular/forms';\nimport { TableConfig } from '@angular-generic-table/core';\nimport { pluck, tap, withLatestFrom } from 'rxjs/operators';\nimport { HttpClient } from '@angular/common/http';\nimport { DatePipe } from '@angular/common';\n\n@Component({\n selector: 'docs-pagination',\n templateUrl: './pagination.component.html',\n styles: [],\n})\nexport class PaginationComponent implements OnInit {\n constructor(private fb: FormBuilder, private http: HttpClient) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n length: [10],\n search: [''],\n });\n search$ = this.paginationForm.get('search')?.valueChanges as Observable;\n loading$ = new BehaviorSubject(true);\n data$: Observable = this.http.get('https://private-730c61-generictable.apiary-mock.com/data').pipe(\n pluck('data'),\n tap((_) => this.loading$.next(false))\n );\n\n tableConfig$: ReplaySubject = new ReplaySubject(1);\n\n ngOnInit(): void {\n this.paginationForm\n .get('length')\n ?.valueChanges.pipe(withLatestFrom(this.tableConfig$))\n .subscribe(([length, config]) => {\n length = +length;\n this.tableConfig$.next({\n ...config,\n pagination: { ...config.pagination, length },\n });\n });\n this.tableConfig$.next({\n class: 'table table-mobile text-nowrap mb-0',\n columns: {\n first_name: {\n mobileHeader: true,\n sortable: true,\n },\n last_name: {\n mobileHeader: true,\n sortable: true,\n },\n gender: {\n mobileHeader: 'Sex',\n sortable: true,\n },\n birthday: {\n mobileHeader: true,\n sortable: true,\n class: 'text-right',\n transform: {\n pipe: DatePipe,\n args: ['longDate'],\n },\n },\n },\n pagination: {\n length: this.paginationForm.get('length')?.value || 0,\n },\n });\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { HttpClientModule } from '@angular/common/http';\nimport { ReactiveFormsModule } from '@angular/forms';\n\nimport { PaginationComponent } from './pagination.component';\nimport { GenericTableCoreModule, GenericTablePaginationModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [PaginationComponent],\n imports: [\n BrowserModule,\n GenericTableCoreModule,\n GenericTablePaginationModule,\n ReactiveFormsModule,\n HttpClientModule\n ],\n bootstrap: [PaginationComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
+ "defaultValue": "[\n {\n name: 'pagination.component.html',\n code: `\n\n
\n \n \n Table is empty\n
\n \n
\n\n`,\n language: 'xml',\n },\n {\n name: 'pagination.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { FormBuilder } from '@angular/forms';\nimport { TableConfig } from '@angular-generic-table/core';\nimport { pluck, tap, withLatestFrom } from 'rxjs/operators';\nimport { HttpClient } from '@angular/common/http';\nimport { DatePipe } from '@angular/common';\n\n@Component({\n selector: 'docs-pagination',\n templateUrl: './pagination.component.html',\n styles: [],\n})\nexport class PaginationComponent implements OnInit {\n constructor(private fb: FormBuilder, private http: HttpClient) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n length: [10],\n search: [''],\n });\n search$ = this.paginationForm.get('search')?.valueChanges as Observable;\n loading$ = new BehaviorSubject(true);\n data$: Observable = this.http.get('https://private-730c61-generictable.apiary-mock.com/data').pipe(\n pluck('data'),\n tap((_) => this.loading$.next(false))\n );\n\n tableConfig$: ReplaySubject = new ReplaySubject(1);\n\n ngOnInit(): void {\n this.paginationForm\n .get('length')\n ?.valueChanges.pipe(withLatestFrom(this.tableConfig$))\n .subscribe(([length, config]) => {\n length = +length;\n this.tableConfig$.next({\n ...config,\n pagination: { ...config.pagination, length },\n });\n });\n this.tableConfig$.next({\n class: 'table text-nowrap',\n columns: {\n first_name: {\n sortable: true,\n },\n last_name: {\n sortable: true,\n },\n gender: {\n sortable: true,\n },\n birthday: {\n sortable: true,\n class: 'text-end',\n transform: {\n pipe: DatePipe,\n args: ['longDate'],\n },\n },\n },\n pagination: {\n length: this.paginationForm.get('length')?.value || 0,\n },\n });\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { HttpClientModule } from '@angular/common/http';\nimport { ReactiveFormsModule } from '@angular/forms';\n\nimport { PaginationComponent } from './pagination.component';\nimport { GenericTableCoreModule, GenericTablePaginationModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [PaginationComponent],\n imports: [\n BrowserModule,\n GenericTableCoreModule,\n GenericTablePaginationModule,\n ReactiveFormsModule,\n HttpClientModule\n ],\n bootstrap: [PaginationComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
},
{
"name": "capitalize",
@@ -3386,7 +4057,7 @@
"name": "context",
"ctype": "miscellaneous",
"subtype": "variable",
- "file": "projects/docs/src/test.ts",
+ "file": "projects/core/src/test.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "",
@@ -3396,7 +4067,7 @@
"name": "context",
"ctype": "miscellaneous",
"subtype": "variable",
- "file": "projects/core/src/test.ts",
+ "file": "projects/docs/src/test.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "",
@@ -3410,7 +4081,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
- "defaultValue": "[\n {\n name: 'custom-templates.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { TableColumn, TableConfig, TableRow } from '@angular-generic-table/core';\nimport { ReplaySubject } from 'rxjs';\n\n@Component({\n selector: 'custom-templates',\n templateUrl: \\`./custom-templates.component.html\\`\n})\nexport class CustomTemplatesComponent implements OnInit {\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n clicked = '';\n\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteColor: '#26BFAF',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteColor: '#0f0',\n favoriteFood: 'Pizza',\n },\n ];\n config$: ReplaySubject = new ReplaySubject(1);\n ngOnInit(): void {\n this.config$.next({\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteColor: {\n templateRef: this.color,\n },\n favoriteFood: {},\n action: {\n templateRef: this.actions,\n },\n },\n });\n }\n clickAction(row: TableRow, column: { key: string; value: TableColumn }, index: number): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = \\`clicked row number: \\${index}\\`;\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { CustomTemplatesComponent } from './custom-templates-table.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [CustomTemplatesComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [CustomTemplatesComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n {\n name: 'custom-templates.component.html',\n code: `\n\n \n\n\n \n\n{{ clicked }}`,\n language: 'xml',\n },\n]"
+ "defaultValue": "[\n {\n name: 'custom-templates.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { TableColumn, TableConfig, TableRow } from '@angular-generic-table/core';\nimport { ReplaySubject } from 'rxjs';\n\n@Component({\n selector: 'custom-templates',\n templateUrl: \\`./custom-templates.component.html\\`\n})\nexport class CustomTemplatesComponent implements OnInit {\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n clicked = '';\n\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteColor: '#26BFAF',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteColor: '#0f0',\n favoriteFood: 'Pizza',\n },\n ];\n config$: ReplaySubject = new ReplaySubject(1);\n ngOnInit(): void {\n this.config$.next({\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteColor: {\n templateRef: this.color,\n },\n favoriteFood: {},\n action: {\n templateRef: this.actions,\n },\n },\n });\n }\n clickAction(row: TableRow, column: { key: string; value: TableColumn }, index: number): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = \\`clicked row number: \\${index}\\`;\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { CustomTemplatesComponent } from './custom-templates-table.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [CustomTemplatesComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [CustomTemplatesComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n {\n name: 'custom-templates.component.html',\n code: `\n\n \n\n\n \n\n{{ clicked }}`,\n language: 'xml',\n },\n]"
},
{
"name": "CustomTemplates",
@@ -3420,7 +4091,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "Story",
- "defaultValue": "(args: CustomTemplatesComponent) => ({\n props: args,\n component: CustomTemplatesComponent,\n})"
+ "defaultValue": "(\n args: CustomTemplatesComponent\n) => ({\n props: args,\n component: CustomTemplatesComponent,\n})"
},
{
"name": "dashed",
@@ -3455,11 +4126,21 @@
"name": "Horizontal",
"ctype": "miscellaneous",
"subtype": "variable",
- "file": "projects/docs/src/app/examples/transpose/transpose.component.ts",
+ "file": "projects/docs/src/app/examples/horizontal-table/horizontal-table.component.ts",
"deprecated": false,
"deprecationMessage": "",
- "type": "Story",
- "defaultValue": "(args: TransposeComponent) => ({\n props: args,\n component: TransposeComponent,\n})"
+ "type": "Story",
+ "defaultValue": "(\n args: HorizontalTableComponent\n) => ({\n props: args,\n component: HorizontalTableComponent,\n})"
+ },
+ {
+ "name": "HORIZONTAL_TABLE_SNIPPETS",
+ "ctype": "miscellaneous",
+ "subtype": "variable",
+ "file": "projects/docs/src/app/examples/horizontal-table/horizontal-table.snippets.ts",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "[]",
+ "defaultValue": "[\n {\n name: 'horizontal-table.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { GtDeltaComponent, TableConfig, TableRows } from '@angular-generic-table/core';\nimport { BehaviorSubject } from 'rxjs';\n\n@Component({\n selector: 'docs-horizontal',\n templateUrl: './horizontal-table.component.html',\n styles: [],\n})\nexport class TransposeComponent implements OnInit {\n @ViewChild('feelings', { static: true }) feelings:\n | TemplateRef\n | undefined;\n @ViewChild('delta', { static: true }) delta: TemplateRef | undefined;\n @ViewChild('deltaIndex', { static: true }) deltaIndex:\n | TemplateRef\n | undefined;\n loading$ = new BehaviorSubject(false);\n config: TableConfig = {};\n data: TableRows = [];\n\n ngOnInit(): void {\n this.config = {\n stickyHeaders: {\n row: true,\n },\n mobileLayout: true,\n rows: {\n year: {\n class: 'text-end',\n header: false\n },\n value: {\n class: 'text-end'\n },\n delta: {\n header: 'Delta %',\n templateRef: this.delta,\n class: 'text-end'\n },\n deltaIndex: {\n header: 'Since inception %',\n templateRef: this.deltaIndex,\n class: 'text-end'\n },\n feeling: {\n templateRef: this.feelings,\n class: 'text-end'\n },\n },\n };\n this.load();\n }\n simulateLoad(): void {\n this.loading$.next(true);\n // set loading state to false after 2 seconds\n setTimeout(() => this.loading$.next(false), 2000);\n }\n empty(): void {\n this.data = [];\n }\n load(): void {\n this.data = [\n {\n year: '2017',\n value: 50,\n feeling: 'neutral'\n },\n {\n year: '2018',\n value: 75,\n feeling: 'positive'\n },\n {\n year: '2019',\n value: 100,\n feeling: 'thrilled'\n },\n {\n year: '2020',\n value: 250,\n feeling: 'thrilled'\n },\n {\n year: '2021',\n value: 50,\n feeling: 'negative'\n }\n ];\n }\n}\n`,\n language: 'typescript',\n },\n {\n name: 'horizontal-table.component.html',\n code: `\n\n\n\n
\n \n Table is empty
\n \n
\n\n \n 😀\n 🙂\n 😐\n 😭\n
\n\n\n \n\n\n \n`,\n language: 'xml',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { HorizontalTableComponent } from './horizontal-table.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [HorizontalTableComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [HorizontalTableComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
},
{
"name": "Mobile",
@@ -3469,7 +4150,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "Story",
- "defaultValue": "(args: MobileLayoutComponent) => ({\n props: args,\n component: MobileLayoutComponent,\n})"
+ "defaultValue": "(\n args: MobileLayoutComponent\n) => ({\n props: args,\n component: MobileLayoutComponent,\n})"
},
{
"name": "MOBILE_LAYOUT_SNIPPETS",
@@ -3479,7 +4160,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
- "defaultValue": "[\n {\n name: 'mobile-layout.component.ts',\n code: `import {Component, Pipe, PipeTransform, TemplateRef, ViewChild, ViewEncapsulation} from '@angular/core';\nimport {TableColumn, TableConfig, TableRow} from \"@angular-generic-table/core\";\nimport {BehaviorSubject, Observable} from \"rxjs\";\nimport {map} from \"rxjs/operators\";\n\n@Pipe({\n name: 'genderPipe',\n})\nexport class GenderPipe implements PipeTransform {\n transform(gender: 'male' | 'female'): string {\n return gender === 'male' ? '👨' : '👩';\n }\n}\n\n@Component({\n selector: 'docs-mobile-layout',\n template: \\`\n \n {{clicked}} \n
\n \n \n \n \n \\`,\n styles: [\\`\n .table th {\n white-space: nowrap;\n }\n \\`],\n encapsulation: ViewEncapsulation.None\n})\nexport class MobileLayoutComponent {\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n clicked = '';\n\n mobileLayout$ = new BehaviorSubject(true);\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config$: Observable = this.mobileLayout$.pipe(\n map(mobileLayout => ({\n mobileLayout,\n columns: {\n firstName: {\n mobileHeader: true,\n sortable: true\n },\n lastName: {\n mobileHeader: true,\n sortable: true\n },\n gender: {\n mobileHeader: true,\n transform: {\n pipe: GenderPipe\n }\n },\n favoriteFood: {\n mobileHeader: true\n },\n action: {\n mobileHeader: false,\n header: false,\n templateRef: this.actions,\n },\n },\n }))\n );\n\n toggleLayout = (): void => {\n this.mobileLayout$.next(!this.mobileLayout$.getValue());\n }\n clickAction(row: TableRow, column: { key: string; value: TableColumn }, index: number): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = \\`Clicked row number: \\${index}\\`;\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { MobileLayoutComponent } from './mobile-layout.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [MobileLayoutComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [MobileLayoutComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
+ "defaultValue": "[\n {\n name: 'mobile-layout.component.ts',\n code: `import {Component, Pipe, PipeTransform, TemplateRef, ViewChild, ViewEncapsulation} from '@angular/core';\nimport {TableColumn, TableConfig, TableRow} from \"@angular-generic-table/core\";\nimport {BehaviorSubject, Observable} from \"rxjs\";\nimport {map} from \"rxjs/operators\";\n\n@Pipe({\n name: 'genderPipe',\n})\nexport class GenderPipe implements PipeTransform {\n transform(gender: 'male' | 'female'): string {\n return gender === 'male' ? '👨' : '👩';\n }\n}\n\n@Component({\n selector: 'docs-mobile-layout',\n template: \\`\n \n {{clicked}} \n
\n \n \n \n \n \\`,\n styles: [\\`\n .table th {\n white-space: nowrap;\n }\n \\`],\n encapsulation: ViewEncapsulation.None\n})\nexport class MobileLayoutComponent {\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n clicked = '';\n\n mobileLayout$ = new BehaviorSubject(true);\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config$: Observable = this.mobileLayout$.pipe(\n map(mobileLayout => ({\n mobileLayout,\n columns: {\n firstName: {\n mobileHeader: true,\n sortable: true\n },\n lastName: {\n mobileHeader: true,\n sortable: true\n },\n gender: {\n mobileHeader: true,\n transform: {\n pipe: GenderPipe\n }\n },\n favoriteFood: {\n mobileHeader: true\n },\n action: {\n mobileHeader: false,\n header: false,\n templateRef: this.actions,\n },\n },\n }))\n );\n\n toggleLayout = (): void => {\n this.mobileLayout$.next(!this.mobileLayout$.getValue());\n }\n clickAction(row: TableRow, column: { key: string; value: TableColumn }, index: number): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = \\`Clicked row number: \\${index}\\`;\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { MobileLayoutComponent } from './mobile-layout.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [MobileLayoutComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [MobileLayoutComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
},
{
"name": "Nested",
@@ -3489,7 +4170,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "Story",
- "defaultValue": "(args: NestedDataComponent) => ({\n props: args,\n component: NestedDataComponent,\n})"
+ "defaultValue": "(\n args: NestedDataComponent\n) => ({\n props: args,\n component: NestedDataComponent,\n})"
},
{
"name": "NESTED_SNIPPETS",
@@ -3499,7 +4180,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
- "defaultValue": "[\n {\n name: 'nested-data.component.ts',\n code: `import {Component, OnInit, TemplateRef, ViewChild} from '@angular/core';\nimport {TableConfig, TableRows} from \"@angular-generic-table/core\";\n\n@Component({\n selector: 'nested-data',\n template: \\`\n \n \n \n \n \n ♂️\n ♀️\n
\n \n \\`,\n styles: [],\n})\nexport class NestedDataComponent implements OnInit {\n @ViewChild(/'gender/', { static: true }) gender: TemplateRef | undefined;\n\n config: TableConfig = {};\n data: TableRows = [];\n ngOnInit(): void {\n this.data = [\n {\n name: {\n first: 'Peter',\n last: 'Parker',\n },\n data: {\n details: {\n gender: 'male',\n favoriteFood: 'Pasta',\n }\n },\n },\n {\n name: {\n first: 'Mary Jane',\n last: 'Watson',\n },\n data: {\n details: {\n gender: 'female',\n favoriteFood: 'Pizza',\n }\n }\n },\n ];\n this.config = {\n columns: {\n firstName: {\n sortable: true,\n mapTo: { path: 'name.first' }\n },\n lastName: {\n mapTo: { path: 'name.last' }\n },\n gender: {\n mapTo: { path: 'data.details.gender' },\n templateRef: this.gender,\n\n },\n favoriteFood: {\n mapTo: { path: 'data.details.favoriteFood' }\n },\n missing: {\n mapTo: { path: 'data.missingKey.noMatch', missingValue: 'n/a' }\n },\n },\n };\n }\n\n loadData(): void {\n this.data = [\n {\n name: {\n first: 'John',\n last: 'Doe',\n },\n data: {\n details: {\n gender: 'male',\n favoriteFood: 'Pasta',\n }\n },\n },\n {\n name: {\n first: 'Jane',\n last: 'Doe',\n },\n data: {\n details: {\n gender: 'female',\n favoriteFood: 'Pizza',\n }\n }\n },\n {\n name: {\n first: 'Foo',\n last: 'Bar',\n },\n data: {}\n },\n ];\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { NestedDataComponent } from './nested-data.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [NestedDataComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [NestedDataComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
+ "defaultValue": "[\n {\n name: 'nested-data.component.ts',\n code: `import {Component, OnInit, TemplateRef, ViewChild} from '@angular/core';\nimport {TableConfig, TableRows} from \"@angular-generic-table/core\";\n\n@Component({\n selector: 'nested-data',\n template: \\`\n \n
\n \n
\n
\n \n
\n
\n \n \n \n \n ♂️\n ♀️\n
\n \n \\`,\n styles: [],\n})\nexport class NestedDataComponent implements OnInit {\n @ViewChild('gender', { static: true }) gender: TemplateRef | undefined;\n\n config: TableConfig = {};\n data: TableRows = [];\n ngOnInit(): void {\n this.resetData();\n this.config = {\n columns: {\n firstName: {\n sortable: true,\n mapTo: { path: 'name.first' },\n },\n lastName: {\n mapTo: { path: 'name.last' },\n },\n gender: {\n mapTo: { path: 'data.details.gender' },\n templateRef: this.gender,\n },\n favoriteFood: {\n mapTo: { path: 'data.details.favoriteFood', missingValue: 'n/a' },\n },\n missing: {\n mapTo: { path: 'data.missingKey.noMatch', missingValue: 'n/a' },\n },\n },\n };\n }\n\n resetData() {\n this.data = [\n {\n name: {\n first: 'Peter',\n last: 'Parker',\n },\n data: {\n details: {\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n },\n },\n {\n name: {\n first: 'Mary Jane',\n last: 'Watson',\n },\n data: {\n details: {\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n },\n },\n ];\n }\n\n loadData(): void {\n this.data = [\n {\n name: {\n first: 'John',\n last: 'Doe',\n },\n data: {\n details: {\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n },\n },\n {\n name: {\n first: 'Jane',\n last: 'Doe',\n },\n data: {\n details: {\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n },\n },\n {\n name: {\n first: 'Foo',\n last: 'Bar',\n },\n data: {},\n },\n ];\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { NestedDataComponent } from './nested-data.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [NestedDataComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [NestedDataComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
},
{
"name": "Pagination",
@@ -3509,13 +4190,13 @@
"deprecated": false,
"deprecationMessage": "",
"type": "Story",
- "defaultValue": "(args: PaginationComponent) => ({\n props: args,\n component: PaginationComponent,\n})"
+ "defaultValue": "(\n args: PaginationComponent\n) => ({\n props: args,\n component: PaginationComponent,\n})"
},
{
"name": "require",
"ctype": "miscellaneous",
"subtype": "variable",
- "file": "projects/docs/src/test.ts",
+ "file": "projects/core/src/test.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "literal type"
@@ -3524,7 +4205,7 @@
"name": "require",
"ctype": "miscellaneous",
"subtype": "variable",
- "file": "projects/core/src/test.ts",
+ "file": "projects/docs/src/test.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "literal type"
@@ -3568,6 +4249,16 @@
"type": "Story",
"defaultValue": "(args: TabsComponent) => ({\n props: args,\n component: TabsComponent,\n})"
},
+ {
+ "name": "Transpose",
+ "ctype": "miscellaneous",
+ "subtype": "variable",
+ "file": "projects/docs/src/app/examples/transpose/transpose.component.ts",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "Story",
+ "defaultValue": "(\n args: TransposeComponent\n) => ({\n props: args,\n component: TransposeComponent,\n})"
+ },
{
"name": "TRANSPOSE_SNIPPETS",
"ctype": "miscellaneous",
@@ -3576,7 +4267,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
- "defaultValue": "[\n {\n name: 'horizontal-table.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { PercentPipe } from '@angular/common';\nimport { TableConfig, TableRows } from '@angular-generic-table/core';\nimport { BehaviorSubject } from 'rxjs';\n\n@Component({\n selector: 'docs-horizontal',\n templateUrl: './horizontal-table.component.html',\n styles: [],\n})\nexport class TransposeComponent implements OnInit {\n @ViewChild('feelings', { static: true }) feelings: TemplateRef | undefined;\n loading$ = new BehaviorSubject(false);\n config: TableConfig = {};\n data: TableRows = [];\n\n ngOnInit(): void {\n this.config = {\n rows: {\n year: {\n class: 'text-right',\n header: false,\n },\n value: {\n class: 'text-right',\n },\n change: {\n header: 'Change %',\n transform: {\n pipe: PercentPipe,\n args: [],\n },\n class: 'text-right',\n },\n feeling: {\n templateRef: this.feelings,\n class: 'text-right',\n },\n },\n };\n this.load();\n }\n simulateLoad(): void {\n this.loading$.next(true);\n // set loading state to false after 2 seconds\n setTimeout(() => this.loading$.next(false), 2000);\n }\n empty(): void {\n this.data = [];\n }\n load(): void {\n this.data = [\n {\n year: '2017',\n value: 50,\n change: 0.5,\n feeling: 'thrilled',\n },\n {\n year: '2018',\n value: 75,\n change: 0.33,\n feeling: 'positive',\n },\n {\n year: '2019',\n value: 100,\n change: 1.5,\n feeling: 'thrilled',\n },\n {\n year: '2020',\n value: 250,\n change: -0.8,\n feeling: 'negative',\n },\n {\n year: '2021',\n value: 50,\n change: null,\n feeling: 'neutral',\n },\n ];\n }\n}\n`,\n language: 'typescript',\n },\n {\n name: 'horizontal-table.component.html',\n code: `\n\n\n\n \n Table is empty
\n\n\n \n 😀\n 🙂\n 😐\n 😭\n
\n`,\n language: 'xml',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { HorizontalTableComponent } from './horizontal-table.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [HorizontalTableComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [HorizontalTableComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
+ "defaultValue": "[\n {\n name: 'transpose.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { BehaviorSubject, Observable } from 'rxjs';\nimport { TableConfig, TableRows, GtDeltaComponent } from '@angular-generic-table/core';\nimport { FormBuilder } from '@angular/forms';\nimport { withLatestFrom } from 'rxjs/operators';\n\n@Component({\n selector: 'docs-transpose',\n templateUrl: './transpose.component.html',\n styles: []\n})\nexport class TransposeComponent implements OnInit {\n @ViewChild('delta', { static: true }) delta:\n | TemplateRef\n | undefined;\n @ViewChild('deltaIndex', { static: true }) deltaIndex:\n | TemplateRef\n | undefined;\n @ViewChild('combined', { static: true }) combined:\n | TemplateRef\n | undefined;\n loading$ = new BehaviorSubject(false);\n reactiveForm = this.fb.group({\n length: [10],\n search: [''],\n });\n search$ = this.reactiveForm.get('search')?.valueChanges as Observable;\n tableConfig$: BehaviorSubject = new BehaviorSubject(\n {}\n );\n data: TableRows = [];\n\n constructor(private fb: FormBuilder) {}\n\n ngOnInit(): void {\n this.transpose();\n this.load();\n this.reactiveForm\n .get('length')\n ?.valueChanges.pipe(withLatestFrom(this.tableConfig$))\n .subscribe(([length, config]) => {\n length = +length;\n this.tableConfig$.next({\n ...config,\n pagination: { ...config.pagination, length },\n });\n });\n }\n\n simulateLoad(): void {\n this.loading$.next(true);\n // set loading state to false after 2 seconds\n setTimeout(() => this.loading$.next(false), 2000);\n }\n empty(): void {\n this.data = [];\n }\n load(): void {\n this.data = [\n {\n year: '2010',\n value: 15,\n },\n {\n year: '2011',\n value: 30,\n },\n {\n year: '2012',\n value: 25,\n },\n {\n year: '2013',\n value: 0,\n },\n {\n year: '2014',\n value: 40,\n },\n {\n year: '2015',\n value: 60,\n },\n {\n year: '2016',\n value: -5,\n },\n {\n year: '2018',\n value: 75,\n },\n {\n year: '2019',\n value: 100,\n },\n {\n year: '2020',\n value: 250,\n },\n {\n year: '2021',\n value: 50,\n },\n {\n year: '2022',\n value: 60,\n },\n ];\n }\n transpose(): void {\n if (this.tableConfig$.value.columns) {\n this.tableConfig$.next({\n stickyHeaders: {\n row: true,\n column: true,\n },\n rows: {\n year: {\n sortable: true,\n header: false,\n class: 'text-end',\n },\n value: {\n class: 'text-end',\n },\n delta: {\n header: 'Delta %',\n templateRef: this.delta,\n class: 'text-end',\n },\n deltaIndex: {\n header: 'Since inception %',\n templateRef: this.deltaIndex,\n class: 'text-end',\n },\n combined: {\n header: 'Value with change',\n templateRef: this.combined,\n class: 'text-end text-nowrap',\n },\n },\n });\n } else {\n this.tableConfig$.next({\n stickyHeaders: {\n row: true,\n column: true,\n },\n columns: {\n year: {\n sortable: true,\n },\n value: {\n class: 'text-end',\n },\n delta: {\n header: 'Delta %',\n templateRef: this.delta,\n class: 'text-end',\n },\n deltaIndex: {\n header: 'Since inception %',\n templateRef: this.deltaIndex,\n class: 'text-end',\n },\n combined: {\n header: 'Value with change',\n templateRef: this.combined,\n class: 'text-end text-nowrap',\n },\n },\n pagination: { length: this.reactiveForm.get('length')?.value },\n });\n }\n }\n}\n`,\n language: 'typescript',\n },\n {\n name: 'horizontal-table.component.html',\n code: ``,\n language: 'xml',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { HorizontalTableComponent } from './horizontal-table.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [HorizontalTableComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [HorizontalTableComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
}
],
"functions": [],
@@ -3640,7 +4331,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "Story",
- "defaultValue": "(args: AdvancedComponent) => ({\n props: args,\n component: AdvancedComponent,\n})"
+ "defaultValue": "(\n args: AdvancedComponent\n) => ({\n props: args,\n component: AdvancedComponent,\n})"
}
],
"projects/docs/src/app/examples/advanced/advanced.snippets.ts": [
@@ -3652,7 +4343,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
- "defaultValue": "[\n {\n name: 'advanced.component.html',\n code: `\n\n\n
\n \n \n Table is empty\n
\n \n
\n\n
\n \n
\n
\n \n
\n
Current page: {{pagination.current +1}}
\n
Total pages: {{pagination.total}}
\n
\n Records: {{(data$ | async).length}}\n
\n
\n {{clicked}}\n
\n
\n\n \n\n\n \n`,\n language: 'xml',\n },\n {\n name: 'advanced.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { FormBuilder } from '@angular/forms';\nimport { TableColumn, TableConfig, TableRow } from '@angular-generic-table/core';\nimport { withLatestFrom } from 'rxjs/operators';\n\n@Component({\n selector: 'docs-advanced',\n templateUrl: './advanced.component.html',\n styles: [],\n})\nexport class AdvancedComponent implements OnInit {\n get currentPage$(): Observable {\n return this._currentPage$.asObservable();\n }\n\n set currentPage(value: number) {\n this._currentPage$.next(value);\n }\n constructor(private fb: FormBuilder) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n length: [10],\n search: [''],\n });\n search$ = this.paginationForm.get('search')?.valueChanges as Observable;\n loading$ = new BehaviorSubject(true);\n data$: BehaviorSubject = new BehaviorSubject([\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteColor: '#26BFAF',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteColor: '#0f0',\n favoriteFood: 'Pizza',\n },\n ]);\n\n private _currentPage$ = new BehaviorSubject(0);\n\n clicked: string = '';\n maleFirstNames = ['Peter', 'Clark', 'Ruben', 'John', 'Jack', 'Roscoe'];\n femaleFirstNames = ['Mary Jane', 'Kim', 'Sarah', 'Michelle', 'Ann'];\n lastNames = ['Andersson', 'Smith', 'Parker', 'Kent', 'Rogers', 'Lane', 'Jackson'];\n foods = ['Pizza', 'Pasta', 'Hamburger', 'Pancakes', 'Tacos', 'Lasagna', 'Meatloaf'];\n colors = ['#33d60b', '#dcafff', '#3fc9ff', '#ff1600', '#5238b1', '#fff'];\n\n tableConfig$: ReplaySubject = new ReplaySubject(1);\n\n addData(): void {\n this.data$.next([...this.data$.getValue(), this.randomRecord()]);\n }\n\n removeData(): void {\n this.data$.next([]);\n }\n\n simulateLoad(): void {\n this.loading$.next(true);\n // set loading state to false after 2 seconds\n setTimeout(() => this.loading$.next(false), 2000);\n }\n\n clickAction(row: TableRow, column: { key: string; value: TableColumn }, index: number): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = \\`clicked row number: \\${index}\\`;\n }\n\n randomRecord(): TableRow {\n const random = Math.floor(Math.random() * 2);\n const newRecord = {\n firstName: random\n ? this.maleFirstNames[Math.floor(Math.random() * this.maleFirstNames.length)]\n : this.femaleFirstNames[Math.floor(Math.random() * this.femaleFirstNames.length)],\n lastName: this.lastNames[Math.floor(Math.random() * this.lastNames.length)],\n gender: random ? 'male' : 'female',\n favoriteColor: this.colors[Math.floor(Math.random() * this.colors.length)],\n favoriteFood: this.foods[Math.floor(Math.random() * this.foods.length)],\n };\n console.log('added new random record:', newRecord);\n\n return newRecord;\n }\n\n next = () => {\n this.currentPage = this._currentPage$.value + 1;\n };\n prev = () => {\n this.currentPage = this._currentPage$.value - 1;\n };\n\n ngOnInit(): void {\n this.simulateLoad();\n this.paginationForm\n .get('length')\n ?.valueChanges.pipe(withLatestFrom(this.tableConfig$))\n .subscribe(([length, config]) => {\n length = +length;\n this.tableConfig$.next({\n ...config,\n pagination: { ...config.pagination, length },\n });\n });\n this.tableConfig$.next({\n class: 'table table-mobile text-nowrap mb-0',\n columns: {\n firstName: {\n header: 'First name',\n mobileHeader: true,\n sortable: true,\n order: 0,\n },\n lastName: {\n header: 'Last name',\n mobileHeader: true,\n hidden: false,\n sortable: true,\n },\n gender: {\n mobileHeader: 'Sex',\n sortable: true,\n order: 1,\n },\n favoriteColor: {\n header: 'Favorite color',\n mobileHeader: true,\n templateRef: this.color,\n sortable: false,\n order: 2,\n search: false,\n class: 'custom-class',\n },\n favoriteFood: {\n mobileHeader: true,\n header: 'Favorite food',\n hidden: false,\n sortable: true,\n order: 0,\n },\n action: {\n mobileHeader: false,\n header: false,\n templateRef: this.actions,\n order: 6,\n },\n },\n pagination: {\n length: this.paginationForm.get('length')?.value || 0,\n },\n });\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { ReactiveFormsModule } from '@angular/forms';\n\nimport { AdvancedComponent } from './advanced.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [AdvancedComponent],\n imports: [BrowserModule, ReactiveFormsModule, GenericTableCoreModule],\n bootstrap: [AdvancedComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
+ "defaultValue": "[\n {\n name: 'advanced.component.html',\n code: `\n
\n \n
\n
\n \n
\n
\n \n
\n
\n\n\n
\n \n \n Table is empty\n
\n \n
\n\n
\n \n
\n
\n \n
\n
Current page: {{pagination.current +1}}
\n
Total pages: {{pagination.total}}
\n
\n Records: {{(data$ | async).length}}\n
\n
\n {{clicked}}\n
\n
\n\n \n\n\n \n`,\n language: 'xml',\n },\n {\n name: 'advanced.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { FormBuilder } from '@angular/forms';\nimport { TableColumn, TableConfig, TableRow } from '@angular-generic-table/core';\nimport { withLatestFrom } from 'rxjs/operators';\n\n@Component({\n selector: 'docs-advanced',\n templateUrl: './advanced.component.html',\n styles: [],\n})\nexport class AdvancedComponent implements OnInit {\n get currentPage$(): Observable {\n return this._currentPage$.asObservable();\n }\n\n set currentPage(value: number) {\n this._currentPage$.next(value);\n }\n constructor(private fb: FormBuilder) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n length: [10],\n search: [''],\n });\n search$ = this.paginationForm.get('search')?.valueChanges as Observable;\n loading$ = new BehaviorSubject(true);\n data$: BehaviorSubject = new BehaviorSubject([\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteColor: '#26BFAF',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteColor: '#0f0',\n favoriteFood: 'Pizza',\n },\n ]);\n\n private _currentPage$ = new BehaviorSubject(0);\n\n clicked: string = '';\n maleFirstNames = ['Peter', 'Clark', 'Ruben', 'John', 'Jack', 'Roscoe'];\n femaleFirstNames = ['Mary Jane', 'Kim', 'Sarah', 'Michelle', 'Ann'];\n lastNames = ['Andersson', 'Smith', 'Parker', 'Kent', 'Rogers', 'Lane', 'Jackson'];\n foods = ['Pizza', 'Pasta', 'Hamburger', 'Pancakes', 'Tacos', 'Lasagna', 'Meatloaf'];\n colors = ['#33d60b', '#dcafff', '#3fc9ff', '#ff1600', '#5238b1', '#fff'];\n\n tableConfig$: ReplaySubject = new ReplaySubject(1);\n\n addData(): void {\n this.data$.next([...this.data$.getValue(), this.randomRecord()]);\n }\n\n removeData(): void {\n this.data$.next([]);\n }\n\n simulateLoad(): void {\n this.loading$.next(true);\n // set loading state to false after 2 seconds\n setTimeout(() => this.loading$.next(false), 2000);\n }\n\n clickAction(row: TableRow, column: { key: string; value: TableColumn }, index: number): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = \\`clicked row number: \\${index}\\`;\n }\n\n randomRecord(): TableRow {\n const random = Math.floor(Math.random() * 2);\n const newRecord = {\n firstName: random\n ? this.maleFirstNames[Math.floor(Math.random() * this.maleFirstNames.length)]\n : this.femaleFirstNames[Math.floor(Math.random() * this.femaleFirstNames.length)],\n lastName: this.lastNames[Math.floor(Math.random() * this.lastNames.length)],\n gender: random ? 'male' : 'female',\n favoriteColor: this.colors[Math.floor(Math.random() * this.colors.length)],\n favoriteFood: this.foods[Math.floor(Math.random() * this.foods.length)],\n };\n console.log('added new random record:', newRecord);\n\n return newRecord;\n }\n\n next = () => {\n this.currentPage = this._currentPage$.value + 1;\n };\n prev = () => {\n this.currentPage = this._currentPage$.value - 1;\n };\n\n ngOnInit(): void {\n this.simulateLoad();\n this.paginationForm\n .get('length')\n ?.valueChanges.pipe(withLatestFrom(this.tableConfig$))\n .subscribe(([length, config]) => {\n length = +length;\n this.tableConfig$.next({\n ...config,\n pagination: { ...config.pagination, length },\n });\n });\n this.tableConfig$.next({\n class: 'table table-mobile text-nowrap mb-0',\n columns: {\n firstName: {\n header: 'First name',\n mobileHeader: true,\n sortable: true,\n order: 0,\n },\n lastName: {\n header: 'Last name',\n mobileHeader: true,\n hidden: false,\n sortable: true,\n },\n gender: {\n mobileHeader: 'Sex',\n sortable: true,\n order: 1,\n },\n favoriteColor: {\n header: 'Favorite color',\n mobileHeader: true,\n templateRef: this.color,\n sortable: false,\n order: 2,\n search: false,\n class: 'custom-class',\n },\n favoriteFood: {\n mobileHeader: true,\n header: 'Favorite food',\n hidden: false,\n sortable: true,\n order: 0,\n },\n action: {\n mobileHeader: false,\n header: false,\n templateRef: this.actions,\n order: 6,\n },\n },\n pagination: {\n length: this.paginationForm.get('length')?.value || 0,\n },\n });\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { ReactiveFormsModule } from '@angular/forms';\n\nimport { AdvancedComponent } from './advanced.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [AdvancedComponent],\n imports: [BrowserModule, ReactiveFormsModule, GenericTableCoreModule],\n bootstrap: [AdvancedComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
}
],
"projects/docs/src/app/examples/pagination/pagination.snippets.ts": [
@@ -3664,7 +4355,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
- "defaultValue": "[\n {\n name: 'pagination.component.html',\n code: `\n\n
\n \n \n Table is empty\n
\n \n
\n\n`,\n language: 'xml',\n },\n {\n name: 'pagination.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { FormBuilder } from '@angular/forms';\nimport { TableConfig } from '@angular-generic-table/core';\nimport { pluck, tap, withLatestFrom } from 'rxjs/operators';\nimport { HttpClient } from '@angular/common/http';\nimport { DatePipe } from '@angular/common';\n\n@Component({\n selector: 'docs-pagination',\n templateUrl: './pagination.component.html',\n styles: [],\n})\nexport class PaginationComponent implements OnInit {\n constructor(private fb: FormBuilder, private http: HttpClient) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n length: [10],\n search: [''],\n });\n search$ = this.paginationForm.get('search')?.valueChanges as Observable;\n loading$ = new BehaviorSubject(true);\n data$: Observable = this.http.get('https://private-730c61-generictable.apiary-mock.com/data').pipe(\n pluck('data'),\n tap((_) => this.loading$.next(false))\n );\n\n tableConfig$: ReplaySubject = new ReplaySubject(1);\n\n ngOnInit(): void {\n this.paginationForm\n .get('length')\n ?.valueChanges.pipe(withLatestFrom(this.tableConfig$))\n .subscribe(([length, config]) => {\n length = +length;\n this.tableConfig$.next({\n ...config,\n pagination: { ...config.pagination, length },\n });\n });\n this.tableConfig$.next({\n class: 'table table-mobile text-nowrap mb-0',\n columns: {\n first_name: {\n mobileHeader: true,\n sortable: true,\n },\n last_name: {\n mobileHeader: true,\n sortable: true,\n },\n gender: {\n mobileHeader: 'Sex',\n sortable: true,\n },\n birthday: {\n mobileHeader: true,\n sortable: true,\n class: 'text-right',\n transform: {\n pipe: DatePipe,\n args: ['longDate'],\n },\n },\n },\n pagination: {\n length: this.paginationForm.get('length')?.value || 0,\n },\n });\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { HttpClientModule } from '@angular/common/http';\nimport { ReactiveFormsModule } from '@angular/forms';\n\nimport { PaginationComponent } from './pagination.component';\nimport { GenericTableCoreModule, GenericTablePaginationModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [PaginationComponent],\n imports: [\n BrowserModule,\n GenericTableCoreModule,\n GenericTablePaginationModule,\n ReactiveFormsModule,\n HttpClientModule\n ],\n bootstrap: [PaginationComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
+ "defaultValue": "[\n {\n name: 'pagination.component.html',\n code: `\n\n
\n \n \n Table is empty\n
\n \n
\n\n`,\n language: 'xml',\n },\n {\n name: 'pagination.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { BehaviorSubject, Observable, ReplaySubject } from 'rxjs';\nimport { FormBuilder } from '@angular/forms';\nimport { TableConfig } from '@angular-generic-table/core';\nimport { pluck, tap, withLatestFrom } from 'rxjs/operators';\nimport { HttpClient } from '@angular/common/http';\nimport { DatePipe } from '@angular/common';\n\n@Component({\n selector: 'docs-pagination',\n templateUrl: './pagination.component.html',\n styles: [],\n})\nexport class PaginationComponent implements OnInit {\n constructor(private fb: FormBuilder, private http: HttpClient) {}\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n paginationForm = this.fb.group({\n length: [10],\n search: [''],\n });\n search$ = this.paginationForm.get('search')?.valueChanges as Observable;\n loading$ = new BehaviorSubject(true);\n data$: Observable = this.http.get('https://private-730c61-generictable.apiary-mock.com/data').pipe(\n pluck('data'),\n tap((_) => this.loading$.next(false))\n );\n\n tableConfig$: ReplaySubject = new ReplaySubject(1);\n\n ngOnInit(): void {\n this.paginationForm\n .get('length')\n ?.valueChanges.pipe(withLatestFrom(this.tableConfig$))\n .subscribe(([length, config]) => {\n length = +length;\n this.tableConfig$.next({\n ...config,\n pagination: { ...config.pagination, length },\n });\n });\n this.tableConfig$.next({\n class: 'table text-nowrap',\n columns: {\n first_name: {\n sortable: true,\n },\n last_name: {\n sortable: true,\n },\n gender: {\n sortable: true,\n },\n birthday: {\n sortable: true,\n class: 'text-end',\n transform: {\n pipe: DatePipe,\n args: ['longDate'],\n },\n },\n },\n pagination: {\n length: this.paginationForm.get('length')?.value || 0,\n },\n });\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { HttpClientModule } from '@angular/common/http';\nimport { ReactiveFormsModule } from '@angular/forms';\n\nimport { PaginationComponent } from './pagination.component';\nimport { GenericTableCoreModule, GenericTablePaginationModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [PaginationComponent],\n imports: [\n BrowserModule,\n GenericTableCoreModule,\n GenericTablePaginationModule,\n ReactiveFormsModule,\n HttpClientModule\n ],\n bootstrap: [PaginationComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
}
],
"projects/core/src/lib/utilities/utilities.ts": [
@@ -3705,12 +4396,12 @@
"type": "TableRow[]"
}
],
- "projects/docs/src/test.ts": [
+ "projects/core/src/test.ts": [
{
"name": "context",
"ctype": "miscellaneous",
"subtype": "variable",
- "file": "projects/docs/src/test.ts",
+ "file": "projects/core/src/test.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "",
@@ -3720,18 +4411,18 @@
"name": "require",
"ctype": "miscellaneous",
"subtype": "variable",
- "file": "projects/docs/src/test.ts",
+ "file": "projects/core/src/test.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "literal type"
}
],
- "projects/core/src/test.ts": [
+ "projects/docs/src/test.ts": [
{
"name": "context",
"ctype": "miscellaneous",
"subtype": "variable",
- "file": "projects/core/src/test.ts",
+ "file": "projects/docs/src/test.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "",
@@ -3741,7 +4432,7 @@
"name": "require",
"ctype": "miscellaneous",
"subtype": "variable",
- "file": "projects/core/src/test.ts",
+ "file": "projects/docs/src/test.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "literal type"
@@ -3756,7 +4447,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
- "defaultValue": "[\n {\n name: 'custom-templates.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { TableColumn, TableConfig, TableRow } from '@angular-generic-table/core';\nimport { ReplaySubject } from 'rxjs';\n\n@Component({\n selector: 'custom-templates',\n templateUrl: \\`./custom-templates.component.html\\`\n})\nexport class CustomTemplatesComponent implements OnInit {\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n clicked = '';\n\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteColor: '#26BFAF',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteColor: '#0f0',\n favoriteFood: 'Pizza',\n },\n ];\n config$: ReplaySubject = new ReplaySubject(1);\n ngOnInit(): void {\n this.config$.next({\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteColor: {\n templateRef: this.color,\n },\n favoriteFood: {},\n action: {\n templateRef: this.actions,\n },\n },\n });\n }\n clickAction(row: TableRow, column: { key: string; value: TableColumn }, index: number): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = \\`clicked row number: \\${index}\\`;\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { CustomTemplatesComponent } from './custom-templates-table.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [CustomTemplatesComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [CustomTemplatesComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n {\n name: 'custom-templates.component.html',\n code: `\n\n \n\n\n \n\n{{ clicked }}`,\n language: 'xml',\n },\n]"
+ "defaultValue": "[\n {\n name: 'custom-templates.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { TableColumn, TableConfig, TableRow } from '@angular-generic-table/core';\nimport { ReplaySubject } from 'rxjs';\n\n@Component({\n selector: 'custom-templates',\n templateUrl: \\`./custom-templates.component.html\\`\n})\nexport class CustomTemplatesComponent implements OnInit {\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n @ViewChild('color', { static: true }) color: TemplateRef | undefined;\n clicked = '';\n\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteColor: '#26BFAF',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteColor: '#0f0',\n favoriteFood: 'Pizza',\n },\n ];\n config$: ReplaySubject = new ReplaySubject(1);\n ngOnInit(): void {\n this.config$.next({\n columns: {\n firstName: {},\n lastName: {},\n gender: {},\n favoriteColor: {\n templateRef: this.color,\n },\n favoriteFood: {},\n action: {\n templateRef: this.actions,\n },\n },\n });\n }\n clickAction(row: TableRow, column: { key: string; value: TableColumn }, index: number): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = \\`clicked row number: \\${index}\\`;\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { CustomTemplatesComponent } from './custom-templates-table.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [CustomTemplatesComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [CustomTemplatesComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n {\n name: 'custom-templates.component.html',\n code: `\n\n \n\n\n \n\n{{ clicked }}`,\n language: 'xml',\n },\n]"
}
],
"projects/docs/src/app/examples/custom-templates/custom-templates.component.ts": [
@@ -3768,7 +4459,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "Story",
- "defaultValue": "(args: CustomTemplatesComponent) => ({\n props: args,\n component: CustomTemplatesComponent,\n})"
+ "defaultValue": "(\n args: CustomTemplatesComponent\n) => ({\n props: args,\n component: CustomTemplatesComponent,\n})"
}
],
"projects/docs/src/environments/environment.prod.ts": [
@@ -3795,16 +4486,28 @@
"defaultValue": "{\n production: false\n}"
}
],
- "projects/docs/src/app/examples/transpose/transpose.component.ts": [
+ "projects/docs/src/app/examples/horizontal-table/horizontal-table.component.ts": [
{
"name": "Horizontal",
"ctype": "miscellaneous",
"subtype": "variable",
- "file": "projects/docs/src/app/examples/transpose/transpose.component.ts",
+ "file": "projects/docs/src/app/examples/horizontal-table/horizontal-table.component.ts",
"deprecated": false,
"deprecationMessage": "",
- "type": "Story",
- "defaultValue": "(args: TransposeComponent) => ({\n props: args,\n component: TransposeComponent,\n})"
+ "type": "Story",
+ "defaultValue": "(\n args: HorizontalTableComponent\n) => ({\n props: args,\n component: HorizontalTableComponent,\n})"
+ }
+ ],
+ "projects/docs/src/app/examples/horizontal-table/horizontal-table.snippets.ts": [
+ {
+ "name": "HORIZONTAL_TABLE_SNIPPETS",
+ "ctype": "miscellaneous",
+ "subtype": "variable",
+ "file": "projects/docs/src/app/examples/horizontal-table/horizontal-table.snippets.ts",
+ "deprecated": false,
+ "deprecationMessage": "",
+ "type": "[]",
+ "defaultValue": "[\n {\n name: 'horizontal-table.component.ts',\n code: `import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { GtDeltaComponent, TableConfig, TableRows } from '@angular-generic-table/core';\nimport { BehaviorSubject } from 'rxjs';\n\n@Component({\n selector: 'docs-horizontal',\n templateUrl: './horizontal-table.component.html',\n styles: [],\n})\nexport class TransposeComponent implements OnInit {\n @ViewChild('feelings', { static: true }) feelings:\n | TemplateRef\n | undefined;\n @ViewChild('delta', { static: true }) delta: TemplateRef | undefined;\n @ViewChild('deltaIndex', { static: true }) deltaIndex:\n | TemplateRef\n | undefined;\n loading$ = new BehaviorSubject(false);\n config: TableConfig = {};\n data: TableRows = [];\n\n ngOnInit(): void {\n this.config = {\n stickyHeaders: {\n row: true,\n },\n mobileLayout: true,\n rows: {\n year: {\n class: 'text-end',\n header: false\n },\n value: {\n class: 'text-end'\n },\n delta: {\n header: 'Delta %',\n templateRef: this.delta,\n class: 'text-end'\n },\n deltaIndex: {\n header: 'Since inception %',\n templateRef: this.deltaIndex,\n class: 'text-end'\n },\n feeling: {\n templateRef: this.feelings,\n class: 'text-end'\n },\n },\n };\n this.load();\n }\n simulateLoad(): void {\n this.loading$.next(true);\n // set loading state to false after 2 seconds\n setTimeout(() => this.loading$.next(false), 2000);\n }\n empty(): void {\n this.data = [];\n }\n load(): void {\n this.data = [\n {\n year: '2017',\n value: 50,\n feeling: 'neutral'\n },\n {\n year: '2018',\n value: 75,\n feeling: 'positive'\n },\n {\n year: '2019',\n value: 100,\n feeling: 'thrilled'\n },\n {\n year: '2020',\n value: 250,\n feeling: 'thrilled'\n },\n {\n year: '2021',\n value: 50,\n feeling: 'negative'\n }\n ];\n }\n}\n`,\n language: 'typescript',\n },\n {\n name: 'horizontal-table.component.html',\n code: `\n\n\n\n
\n \n Table is empty
\n \n
\n\n \n 😀\n 🙂\n 😐\n 😭\n
\n\n\n \n\n\n \n`,\n language: 'xml',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { HorizontalTableComponent } from './horizontal-table.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [HorizontalTableComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [HorizontalTableComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
}
],
"projects/docs/src/app/examples/mobile-layout/mobile-layout.component.ts": [
@@ -3816,7 +4519,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "Story",
- "defaultValue": "(args: MobileLayoutComponent) => ({\n props: args,\n component: MobileLayoutComponent,\n})"
+ "defaultValue": "(\n args: MobileLayoutComponent\n) => ({\n props: args,\n component: MobileLayoutComponent,\n})"
}
],
"projects/docs/src/app/examples/mobile-layout/mobileLayout.snippets.ts": [
@@ -3828,7 +4531,7 @@
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
- "defaultValue": "[\n {\n name: 'mobile-layout.component.ts',\n code: `import {Component, Pipe, PipeTransform, TemplateRef, ViewChild, ViewEncapsulation} from '@angular/core';\nimport {TableColumn, TableConfig, TableRow} from \"@angular-generic-table/core\";\nimport {BehaviorSubject, Observable} from \"rxjs\";\nimport {map} from \"rxjs/operators\";\n\n@Pipe({\n name: 'genderPipe',\n})\nexport class GenderPipe implements PipeTransform {\n transform(gender: 'male' | 'female'): string {\n return gender === 'male' ? '👨' : '👩';\n }\n}\n\n@Component({\n selector: 'docs-mobile-layout',\n template: \\`\n \n {{clicked}} \n
\n \n \n \n \n \\`,\n styles: [\\`\n .table th {\n white-space: nowrap;\n }\n \\`],\n encapsulation: ViewEncapsulation.None\n})\nexport class MobileLayoutComponent {\n @ViewChild('actions', { static: true }) actions: TemplateRef | undefined;\n clicked = '';\n\n mobileLayout$ = new BehaviorSubject(true);\n data = [\n {\n firstName: 'Peter',\n lastName: 'Parker',\n gender: 'male',\n favoriteFood: 'Pasta',\n },\n {\n firstName: 'Mary Jane',\n lastName: 'Watson',\n gender: 'female',\n favoriteFood: 'Pizza',\n },\n ];\n config$: Observable = this.mobileLayout$.pipe(\n map(mobileLayout => ({\n mobileLayout,\n columns: {\n firstName: {\n mobileHeader: true,\n sortable: true\n },\n lastName: {\n mobileHeader: true,\n sortable: true\n },\n gender: {\n mobileHeader: true,\n transform: {\n pipe: GenderPipe\n }\n },\n favoriteFood: {\n mobileHeader: true\n },\n action: {\n mobileHeader: false,\n header: false,\n templateRef: this.actions,\n },\n },\n }))\n );\n\n toggleLayout = (): void => {\n this.mobileLayout$.next(!this.mobileLayout$.getValue());\n }\n clickAction(row: TableRow, column: { key: string; value: TableColumn }, index: number): void {\n console.log('clicked row:', row, 'col:', column);\n this.clicked = \\`Clicked row number: \\${index}\\`;\n }\n}`,\n language: 'typescript',\n },\n {\n name: 'app.module.ts',\n code: `import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { MobileLayoutComponent } from './mobile-layout.component';\nimport { GenericTableCoreModule } from '@angular-generic-table/core';\n\n@NgModule({\n declarations: [MobileLayoutComponent],\n imports: [BrowserModule, GenericTableCoreModule],\n bootstrap: [MobileLayoutComponent]\n})\nexport class AppModule {}`,\n language: 'typescript',\n },\n]"
+ "defaultValue": "[\n {\n name: 'mobile-layout.component.ts',\n code: `import {Component, Pipe, PipeTransform, TemplateRef, ViewChild, ViewEncapsulation} from '@angular/core';\nimport {TableColumn, TableConfig, TableRow} from \"@angular-generic-table/core\";\nimport {BehaviorSubject, Observable} from \"rxjs\";\nimport {map} from \"rxjs/operators\";\n\n@Pipe({\n name: 'genderPipe',\n})\nexport class GenderPipe implements PipeTransform {\n transform(gender: 'male' | 'female'): string {\n return gender === 'male' ? '👨' : '👩';\n }\n}\n\n@Component({\n selector: 'docs-mobile-layout',\n template: \\`\n