@block-auth.io/angular 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +37 -0
- package/.turbo/turbo-lint.log +4 -0
- package/.turbo/turbo-test.log +82 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/CHANGELOG.md +10 -0
- package/ng-package.json +6 -0
- package/package.json +50 -0
- package/scripts/css-to-ts.mjs +14 -0
- package/src/lib/__tests__/integration.test.ts +45 -0
- package/src/lib/block-auth.service.test.ts +69 -0
- package/src/lib/block-auth.service.ts +81 -0
- package/src/lib/components/btns/btn-base-provider.component.test.ts +22 -0
- package/src/lib/components/btns/btn-base-provider.component.ts +55 -0
- package/src/lib/components/btns/btn-block-auth.component.test.ts +79 -0
- package/src/lib/components/btns/btn-block-auth.component.ts +104 -0
- package/src/lib/components/btns/btn-gmail.component.test.ts +16 -0
- package/src/lib/components/btns/btn-gmail.component.ts +41 -0
- package/src/lib/components/btns/btn-google-base.component.test.ts +16 -0
- package/src/lib/components/btns/btn-google-base.component.ts +73 -0
- package/src/lib/components/btns/btn-metamask.component.test.ts +20 -0
- package/src/lib/components/btns/btn-metamask.component.ts +98 -0
- package/src/lib/components/btns/btn-microsoft.component.test.ts +16 -0
- package/src/lib/components/btns/btn-microsoft.component.ts +77 -0
- package/src/lib/components/btns/btn-phantom.component.test.ts +20 -0
- package/src/lib/components/btns/btn-phantom.component.ts +93 -0
- package/src/lib/components/btns/btn-signin.component.test.ts +55 -0
- package/src/lib/components/btns/btn-signin.component.ts +108 -0
- package/src/lib/components/btns/btn-wallet-connect.component.test.ts +20 -0
- package/src/lib/components/btns/btn-wallet-connect.component.ts +109 -0
- package/src/lib/components/btns/index.ts +11 -0
- package/src/lib/components/contents/content-signin.component.test.ts +33 -0
- package/src/lib/components/contents/content-signin.component.ts +68 -0
- package/src/lib/components/contents/content-signup.component.test.ts +78 -0
- package/src/lib/components/contents/content-signup.component.ts +225 -0
- package/src/lib/components/dds/change-account.component.test.ts +66 -0
- package/src/lib/components/dds/change-account.component.ts +112 -0
- package/src/lib/components/dds/dropdown-profile-btn.component.test.ts +66 -0
- package/src/lib/components/dds/dropdown-profile-btn.component.ts +185 -0
- package/src/lib/components/dds/dropdown-profile-item.component.test.ts +75 -0
- package/src/lib/components/dds/dropdown-profile-item.component.ts +74 -0
- package/src/lib/components/dds/dropdown-profile.component.test.ts +98 -0
- package/src/lib/components/dds/dropdown-profile.component.ts +258 -0
- package/src/lib/components/flow-block-auth.component.test.ts +93 -0
- package/src/lib/components/flow-block-auth.component.ts +226 -0
- package/src/lib/components/modals/modal-overlay.component.test.ts +81 -0
- package/src/lib/components/modals/modal-overlay.component.ts +43 -0
- package/src/lib/components/modals/modal-restoration-expired.component.test.ts +71 -0
- package/src/lib/components/modals/modal-restoration-expired.component.ts +83 -0
- package/src/lib/components/modals/modal-restore.component.test.ts +74 -0
- package/src/lib/components/modals/modal-restore.component.ts +104 -0
- package/src/lib/components/modals/modal-signin.component.test.ts +66 -0
- package/src/lib/components/modals/modal-signin.component.ts +44 -0
- package/src/lib/components/modals/modal-signup.component.test.ts +56 -0
- package/src/lib/components/modals/modal-signup.component.ts +40 -0
- package/src/lib/components/sections/footer-section.component.test.ts +19 -0
- package/src/lib/components/sections/footer-section.component.ts +49 -0
- package/src/lib/components/sections/internals/providers-buttons.component.test.ts +27 -0
- package/src/lib/components/sections/internals/providers-buttons.component.ts +290 -0
- package/src/lib/components/sections/internals/providers-filters.component.test.ts +25 -0
- package/src/lib/components/sections/internals/providers-filters.component.ts +139 -0
- package/src/lib/components/sections/internals/selected-button.component.test.ts +36 -0
- package/src/lib/components/sections/internals/selected-button.component.ts +92 -0
- package/src/lib/components/sections/login-section.component.test.ts +49 -0
- package/src/lib/components/sections/login-section.component.ts +120 -0
- package/src/lib/components/sections/providers-section.component.test.ts +22 -0
- package/src/lib/components/sections/providers-section.component.ts +187 -0
- package/src/lib/components/txns/internals/permissions-required-modal.component.test.ts +14 -0
- package/src/lib/components/txns/internals/permissions-required-modal.component.ts +35 -0
- package/src/lib/components/txns/internals/solicited-data-modal.component.test.ts +72 -0
- package/src/lib/components/txns/internals/solicited-data-modal.component.ts +187 -0
- package/src/lib/components/txns/internals/txn-error.component.test.ts +100 -0
- package/src/lib/components/txns/internals/txn-error.component.ts +121 -0
- package/src/lib/components/txns/internals/txn-type.component.test.ts +52 -0
- package/src/lib/components/txns/internals/txn-type.component.ts +51 -0
- package/src/lib/components/txns/internals/txn.component.test.ts +188 -0
- package/src/lib/components/txns/internals/txn.component.ts +298 -0
- package/src/lib/components/txns/txns-overview.component.test.ts +107 -0
- package/src/lib/components/txns/txns-overview.component.ts +229 -0
- package/src/lib/icons/ai-outline-disconnect.component.ts +21 -0
- package/src/lib/icons/bi-logo-gmail.component.ts +21 -0
- package/src/lib/icons/cg-profile.component.ts +31 -0
- package/src/lib/icons/check-icon.component.ts +27 -0
- package/src/lib/icons/ci-clock2.component.ts +31 -0
- package/src/lib/icons/fa-arrow-right.component.ts +21 -0
- package/src/lib/icons/fa-times.component.ts +21 -0
- package/src/lib/icons/fa-undo.component.ts +21 -0
- package/src/lib/icons/icons.test.ts +25 -0
- package/src/lib/icons/index.ts +22 -0
- package/src/lib/icons/io-ios-arrow-back.component.ts +21 -0
- package/src/lib/icons/io-ios-arrow-forward.component.ts +21 -0
- package/src/lib/icons/io-md-arrow-back.component.ts +19 -0
- package/src/lib/icons/io-md-arrow-forward.component.ts +19 -0
- package/src/lib/icons/pi-warning-fill.component.ts +21 -0
- package/src/lib/icons/tb-arrows-exchange2.component.ts +30 -0
- package/src/lib/icons/tb-copy-check-filled.component.ts +21 -0
- package/src/lib/icons/tb-copy.component.ts +34 -0
- package/src/lib/icons/ti-vendor-microsoft.component.ts +21 -0
- package/src/lib/inject-store.test.ts +26 -0
- package/src/lib/inject-store.ts +16 -0
- package/src/lib/primitives/arrow-icon.component.test.ts +12 -0
- package/src/lib/primitives/arrow-icon.component.ts +38 -0
- package/src/lib/primitives/btn-arrow-dir.component.test.ts +22 -0
- package/src/lib/primitives/btn-arrow-dir.component.ts +65 -0
- package/src/lib/primitives/btn-base.component.test.ts +38 -0
- package/src/lib/primitives/btn-base.component.ts +140 -0
- package/src/lib/primitives/divider-base.component.test.ts +12 -0
- package/src/lib/primitives/divider-base.component.ts +74 -0
- package/src/lib/primitives/dropdown-arrow.component.test.ts +12 -0
- package/src/lib/primitives/dropdown-arrow.component.ts +46 -0
- package/src/lib/primitives/dropdown-base.component.test.ts +49 -0
- package/src/lib/primitives/dropdown-base.component.ts +329 -0
- package/src/lib/primitives/helpers.ts +151 -0
- package/src/lib/primitives/horizontal-loader.component.test.ts +19 -0
- package/src/lib/primitives/horizontal-loader.component.ts +70 -0
- package/src/lib/primitives/icon-base.component.test.ts +19 -0
- package/src/lib/primitives/icon-base.component.ts +58 -0
- package/src/lib/primitives/img-loader.component.test.ts +12 -0
- package/src/lib/primitives/img-loader.component.ts +118 -0
- package/src/lib/primitives/index.ts +29 -0
- package/src/lib/primitives/input-base.component.test.ts +20 -0
- package/src/lib/primitives/input-base.component.ts +210 -0
- package/src/lib/primitives/lifetime-slider-base.component.test.ts +88 -0
- package/src/lib/primitives/lifetime-slider-base.component.ts +75 -0
- package/src/lib/primitives/oval-spinner.component.test.ts +9 -0
- package/src/lib/primitives/oval-spinner.component.ts +158 -0
- package/src/lib/primitives/pickers/dropdown-picker-animated-base.component.ts +220 -0
- package/src/lib/primitives/pickers/picker-button.component.ts +74 -0
- package/src/lib/primitives/pickers/time-picker-dropdown-selector.component.ts +188 -0
- package/src/lib/primitives/pickers/time-picker.component.ts +124 -0
- package/src/lib/primitives/pickers/time-unit-selector.component.ts +82 -0
- package/src/lib/primitives/progressbar.component.test.ts +18 -0
- package/src/lib/primitives/progressbar.component.ts +93 -0
- package/src/lib/primitives/providersStyles.ts +268 -0
- package/src/lib/primitives/slider-base.component.ts +56 -0
- package/src/lib/provide-block-auth.test.ts +43 -0
- package/src/lib/provide-block-auth.ts +43 -0
- package/src/lib/runtime/block-auth-runtime.directive.test.ts +89 -0
- package/src/lib/runtime/block-auth-runtime.directive.ts +68 -0
- package/src/lib/teleport-body.directive.test.ts +29 -0
- package/src/lib/teleport-body.directive.ts +21 -0
- package/src/lib/testing.ts +15 -0
- package/src/lib/transitions.css +23 -0
- package/src/public-api.ts +85 -0
- package/src/vitest.setup.ts +8 -0
- package/tailwind.config.cjs +6 -0
- package/tsconfig.json +15 -0
- package/tsconfig.lib.json +9 -0
- package/vite.config.mts +44 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, input, signal } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
// El anillo giratorio de framer-motion
|
|
4
|
+
// (`motion.span` con animate={{rotate:360}} infinito) pasa a ser una
|
|
5
|
+
// animación CSS local. La <img> hace fade-in cuando termina de cargar
|
|
6
|
+
// (`(load)`) aplicando las clases `bas-fade-enter-active`/`bas-fade-enter-from`
|
|
7
|
+
// de transitions.css (equivalente al `<Transition name="bas-fade">` +
|
|
8
|
+
// `v-show` del port Vue): se mantiene siempre montada (para que el navegador
|
|
9
|
+
// pueda disparar `load`) y se alterna la clase `-enter-from` según `loaded()`.
|
|
10
|
+
type ImgLoaderSize = 'small' | 'medium' | 'large' | 'big';
|
|
11
|
+
|
|
12
|
+
const SIZES_CONTAINER: Record<ImgLoaderSize, string> = {
|
|
13
|
+
small: 'bas-h-[82px] bas-w-[82px]',
|
|
14
|
+
medium: 'bas-h-[90px] bas-w-[90px]',
|
|
15
|
+
large: 'bas-h-[120px] bas-w-[120px]',
|
|
16
|
+
big: 'bas-h-[200px] bas-w-[200px]',
|
|
17
|
+
};
|
|
18
|
+
const SIZES_ICON_CONTAINER: Record<ImgLoaderSize, string> = {
|
|
19
|
+
small: 'bas-h-[72px] bas-w-[72px]',
|
|
20
|
+
medium: 'bas-h-20 bas-w-20',
|
|
21
|
+
large: 'bas-h-[108px] bas-w-[108px]',
|
|
22
|
+
big: 'bas-h-[180px] bas-w-[180px]',
|
|
23
|
+
};
|
|
24
|
+
const SIZES_ICON: Record<ImgLoaderSize, string> = {
|
|
25
|
+
small: 'bas-h-16 bas-w-16',
|
|
26
|
+
medium: 'bas-h-[80px] bas-w-[80px]',
|
|
27
|
+
large: 'bas-h-[100px] bas-w-[100px]',
|
|
28
|
+
big: 'bas-h-[166px] bas-w-[166px]',
|
|
29
|
+
};
|
|
30
|
+
const MOTION_SIZES: Record<ImgLoaderSize, string> = {
|
|
31
|
+
small: 'bas-h-[112px] bas-w-[112px]',
|
|
32
|
+
medium: 'bas-h-[124px] bas-w-[124px]',
|
|
33
|
+
large: 'bas-h-[164px] bas-w-[164px]',
|
|
34
|
+
big: 'bas-h-[272px] bas-w-[272px]',
|
|
35
|
+
};
|
|
36
|
+
const MOTION_SPAN_SIZES: Record<ImgLoaderSize, string> = {
|
|
37
|
+
small: 'bas-border-[22px]',
|
|
38
|
+
medium: 'bas-border-[24px]',
|
|
39
|
+
large: 'bas-border-[28px]',
|
|
40
|
+
big: 'bas-border-[46px]',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
@Component({
|
|
44
|
+
selector: 'ba-img-loader',
|
|
45
|
+
standalone: true,
|
|
46
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
47
|
+
template: `
|
|
48
|
+
<div
|
|
49
|
+
[class]="
|
|
50
|
+
'bas-relative bas-flex ' +
|
|
51
|
+
SIZES_CONTAINER[size()] +
|
|
52
|
+
' bas-items-center bas-justify-center bas-overflow-hidden bas-rounded-lg'
|
|
53
|
+
"
|
|
54
|
+
>
|
|
55
|
+
<div
|
|
56
|
+
[class]="
|
|
57
|
+
'bas-z-10 bas-flex ' +
|
|
58
|
+
SIZES_ICON_CONTAINER[size()] +
|
|
59
|
+
' bas-items-center bas-justify-center bas-rounded-md bas-bg-white'
|
|
60
|
+
"
|
|
61
|
+
>
|
|
62
|
+
<img
|
|
63
|
+
[src]="imgSrc()"
|
|
64
|
+
[alt]="imgAlt()"
|
|
65
|
+
[class]="
|
|
66
|
+
'bal-ba-transform-base bal-ba-transition-2 bas-rounded-md ' +
|
|
67
|
+
SIZES_ICON[size()] +
|
|
68
|
+
' ' +
|
|
69
|
+
(scaleImgWhenHovered() ? 'bal-ba-hover-scale-110' : '') +
|
|
70
|
+
' bas-fade-enter-active ' +
|
|
71
|
+
(loaded() ? '' : 'bas-fade-enter-from')
|
|
72
|
+
"
|
|
73
|
+
[style.background-color]="imgBgColor()"
|
|
74
|
+
(load)="loaded.set(true)"
|
|
75
|
+
/>
|
|
76
|
+
</div>
|
|
77
|
+
<div [class]="'bas-absolute ' + MOTION_SIZES[size()]">
|
|
78
|
+
<span
|
|
79
|
+
[id]="loaderId()"
|
|
80
|
+
[class]="
|
|
81
|
+
'bas-img-loader-ring bas-left-0 bas-top-0 bas-box-border bas-block bas-h-full bas-w-full bas-rounded-full bas-border-solid bas-border-transparent ' +
|
|
82
|
+
MOTION_SPAN_SIZES[size()]
|
|
83
|
+
"
|
|
84
|
+
[style.border-top-color]="loaderColor()"
|
|
85
|
+
></span>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
`,
|
|
89
|
+
styles: [
|
|
90
|
+
`
|
|
91
|
+
@keyframes bas-img-loader-spin {
|
|
92
|
+
to {
|
|
93
|
+
transform: rotate(360deg);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
.bas-img-loader-ring {
|
|
97
|
+
animation: bas-img-loader-spin 1.25s linear infinite;
|
|
98
|
+
}
|
|
99
|
+
`,
|
|
100
|
+
],
|
|
101
|
+
})
|
|
102
|
+
export class ImgLoaderComponent {
|
|
103
|
+
readonly SIZES_CONTAINER = SIZES_CONTAINER;
|
|
104
|
+
readonly SIZES_ICON_CONTAINER = SIZES_ICON_CONTAINER;
|
|
105
|
+
readonly SIZES_ICON = SIZES_ICON;
|
|
106
|
+
readonly MOTION_SIZES = MOTION_SIZES;
|
|
107
|
+
readonly MOTION_SPAN_SIZES = MOTION_SPAN_SIZES;
|
|
108
|
+
|
|
109
|
+
loaderId = input('img-fm-loader');
|
|
110
|
+
imgSrc = input('');
|
|
111
|
+
imgAlt = input('');
|
|
112
|
+
imgBgColor = input('');
|
|
113
|
+
loaderColor = input('');
|
|
114
|
+
scaleImgWhenHovered = input(false);
|
|
115
|
+
size = input<ImgLoaderSize>('small');
|
|
116
|
+
|
|
117
|
+
loaded = signal(false);
|
|
118
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Barrel de las primitivas de UI de @block-auth.io/angular.
|
|
2
|
+
// Excluidas (fuera de scope, igual que en el port Vue): Switcher.
|
|
3
|
+
export { ArrowIconComponent } from './arrow-icon.component';
|
|
4
|
+
export { BtnArrowDirComponent } from './btn-arrow-dir.component';
|
|
5
|
+
export { BtnBaseComponent } from './btn-base.component';
|
|
6
|
+
export { DividerBaseComponent } from './divider-base.component';
|
|
7
|
+
export { DropdownArrowComponent } from './dropdown-arrow.component';
|
|
8
|
+
export { DropdownBaseComponent, type DropdownItem, defaultItems } from './dropdown-base.component';
|
|
9
|
+
export { HorizontalLoaderComponent } from './horizontal-loader.component';
|
|
10
|
+
export { IconBaseComponent } from './icon-base.component';
|
|
11
|
+
export { ImgLoaderComponent } from './img-loader.component';
|
|
12
|
+
export { InputBaseComponent } from './input-base.component';
|
|
13
|
+
export { LifetimeSliderBaseComponent } from './lifetime-slider-base.component';
|
|
14
|
+
export { OvalSpinnerComponent } from './oval-spinner.component';
|
|
15
|
+
export { ProgressbarComponent } from './progressbar.component';
|
|
16
|
+
export { SliderBaseComponent } from './slider-base.component';
|
|
17
|
+
|
|
18
|
+
// pickers/* — port de packages/react/src/primitives/pickers.
|
|
19
|
+
export { DropdownPickerAnimatedBaseComponent } from './pickers/dropdown-picker-animated-base.component';
|
|
20
|
+
export { PickerButtonComponent } from './pickers/picker-button.component';
|
|
21
|
+
export { TimePickerComponent } from './pickers/time-picker.component';
|
|
22
|
+
export {
|
|
23
|
+
TimePickerDropdownSelectorComponent,
|
|
24
|
+
type TimeArrays,
|
|
25
|
+
} from './pickers/time-picker-dropdown-selector.component';
|
|
26
|
+
export { TimeUnitSelectorComponent } from './pickers/time-unit-selector.component';
|
|
27
|
+
|
|
28
|
+
export { providersStyles, type ProviderStyle } from './providersStyles';
|
|
29
|
+
export * from './helpers';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { Component } from '@angular/core';
|
|
3
|
+
import { render, screen, fireEvent } from '@testing-library/angular';
|
|
4
|
+
import { InputBaseComponent } from './input-base.component';
|
|
5
|
+
describe('InputBaseComponent', () => {
|
|
6
|
+
it('renderiza el label', async () => {
|
|
7
|
+
@Component({ standalone: true, imports: [InputBaseComponent], template: `<ba-input-base labelText="Correo" inputId="e" />` })
|
|
8
|
+
class Host {}
|
|
9
|
+
await render(Host);
|
|
10
|
+
expect(screen.getByText('Correo')).toBeTruthy();
|
|
11
|
+
});
|
|
12
|
+
it('emite changedInputValue en input cuando isInputValueManaged', async () => {
|
|
13
|
+
const onChange = vi.fn();
|
|
14
|
+
@Component({ standalone: true, imports: [InputBaseComponent], template: `<ba-input-base [isInputValueManaged]="true" inputTestId="in" (changedInputValue)="f($event)" />` })
|
|
15
|
+
class Host { f = onChange; }
|
|
16
|
+
await render(Host);
|
|
17
|
+
await fireEvent.input(screen.getByTestId('in'), { target: { value: 'hola' } });
|
|
18
|
+
expect(onChange).toHaveBeenCalledWith('hola');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
// `register` (react-hook-form) no tiene
|
|
4
|
+
// equivalente y se elimina; `onChangedInputValue` -> emit `changedInputValue`.
|
|
5
|
+
// `min`/`max`/`step` se dejan sin default (undefined) para no introducir un
|
|
6
|
+
// clamp que react nunca tuvo por defecto (misma decisión que InputBase.vue).
|
|
7
|
+
type InputSize = 'xxs' | 'xs' | 'small' | 'base' | 'medium';
|
|
8
|
+
|
|
9
|
+
@Component({
|
|
10
|
+
selector: 'ba-input-base',
|
|
11
|
+
standalone: true,
|
|
12
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
13
|
+
template: `
|
|
14
|
+
<div
|
|
15
|
+
[class]="
|
|
16
|
+
(enableError() ? displayClassNames() + ' ' + widthClassNames() + ' bas-items-center bas-gap-1.5' : '') +
|
|
17
|
+
' ' + outerHeightGrid() + ' ' + className()
|
|
18
|
+
"
|
|
19
|
+
>
|
|
20
|
+
@if ((enableError() || info()) && enableWhiteSpaces()) {
|
|
21
|
+
<p class="bal-ba-error-text-invisible bas-m-0 bas-h-[18px]">white space</p>
|
|
22
|
+
}
|
|
23
|
+
<div [class]="'bas-relative bas-min-w-0 ' + innerHeightGrid()">
|
|
24
|
+
@if (isTextArea()) {
|
|
25
|
+
<textarea
|
|
26
|
+
[id]="inputId()"
|
|
27
|
+
[class]="inputClassNames()"
|
|
28
|
+
placeholder=""
|
|
29
|
+
[disabled]="isDisabled()"
|
|
30
|
+
[readonly]="isReadOnly()"
|
|
31
|
+
autocomplete="off"
|
|
32
|
+
[value]="isInputValueManaged() ? inputValue() : ''"
|
|
33
|
+
(input)="isInputValueManaged() && onInput($event)"
|
|
34
|
+
></textarea>
|
|
35
|
+
} @else {
|
|
36
|
+
<input
|
|
37
|
+
[type]="type()"
|
|
38
|
+
[id]="inputId()"
|
|
39
|
+
[attr.data-testid]="inputTestId()"
|
|
40
|
+
[class]="inputClassNames()"
|
|
41
|
+
placeholder=""
|
|
42
|
+
[disabled]="isDisabled()"
|
|
43
|
+
[readonly]="isReadOnly()"
|
|
44
|
+
autocomplete="off"
|
|
45
|
+
[attr.min]="min()"
|
|
46
|
+
[attr.max]="max()"
|
|
47
|
+
[attr.step]="step()"
|
|
48
|
+
[value]="isInputValueManaged() ? inputValue() : ''"
|
|
49
|
+
(input)="isInputValueManaged() && onInput($event)"
|
|
50
|
+
/>
|
|
51
|
+
}
|
|
52
|
+
<label [for]="inputId()" [class]="floatingLabelClassNames()">
|
|
53
|
+
<p [class]="'bas-m-0 ' + (isDisabled() ? 'bas-cursor-not-allowed' : 'bas-cursor-text')">
|
|
54
|
+
{{ labelText() }}
|
|
55
|
+
</p>
|
|
56
|
+
</label>
|
|
57
|
+
</div>
|
|
58
|
+
@if (enableError() || info()) {
|
|
59
|
+
<p
|
|
60
|
+
[class]="
|
|
61
|
+
'bas-m-0 bas-h-[18px] ' +
|
|
62
|
+
(error() || info() ? 'bal-ba-error-text' : 'bal-ba-error-text-invisible') + ' ' +
|
|
63
|
+
(info() ? (blueStyles() ? 'bal-ba-info-text-blue-styles' : 'bal-ba-info-text') : '')
|
|
64
|
+
"
|
|
65
|
+
>
|
|
66
|
+
{{ error() ? errorMessage() : '' }}{{ info() ? infoMessage() : '' }}
|
|
67
|
+
</p>
|
|
68
|
+
}
|
|
69
|
+
</div>
|
|
70
|
+
`,
|
|
71
|
+
})
|
|
72
|
+
export class InputBaseComponent {
|
|
73
|
+
type = input('text');
|
|
74
|
+
inputId = input('floating_outlined');
|
|
75
|
+
isTextArea = input(false);
|
|
76
|
+
labelText = input('Floating outlined');
|
|
77
|
+
size = input<InputSize>('base');
|
|
78
|
+
isDisabled = input(false);
|
|
79
|
+
isReadOnly = input(false);
|
|
80
|
+
enableError = input(true);
|
|
81
|
+
enableWhiteSpaces = input(true);
|
|
82
|
+
error = input(false);
|
|
83
|
+
errorMessage = input('Error message');
|
|
84
|
+
info = input(false);
|
|
85
|
+
infoMessage = input('Info message');
|
|
86
|
+
isBgRedWhenError = input(false);
|
|
87
|
+
bgColor = input('bas-bg-transparent');
|
|
88
|
+
bgColorWhenError = input('bas-bg-red-50');
|
|
89
|
+
isInputValueManaged = input(false);
|
|
90
|
+
inputValue = input('');
|
|
91
|
+
floatingLabelTextStyles = input('bas-text-sm');
|
|
92
|
+
floatingLabelBgColor = input('bas-bg-white');
|
|
93
|
+
borderRadiusClassNames = input('bas-rounded-lg');
|
|
94
|
+
displayClassNames = input('bas-grid');
|
|
95
|
+
className = input('');
|
|
96
|
+
widthClassNames = input('bas-w-full');
|
|
97
|
+
blueStyles = input(false);
|
|
98
|
+
min = input<number | undefined>(undefined);
|
|
99
|
+
max = input<number | undefined>(undefined);
|
|
100
|
+
step = input<number | undefined>(undefined);
|
|
101
|
+
inputTestId = input<string | undefined>(undefined);
|
|
102
|
+
|
|
103
|
+
/** Equivalente al emit `changedInputValue` de InputBase.vue. */
|
|
104
|
+
changedInputValue = output<string>();
|
|
105
|
+
|
|
106
|
+
onInput(e: Event): void {
|
|
107
|
+
this.changedInputValue.emit((e.target as HTMLInputElement | HTMLTextAreaElement).value);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private heightGrid(enableError: boolean, enableWhiteSpaces: boolean): string {
|
|
111
|
+
if (!this.isTextArea()) {
|
|
112
|
+
return {
|
|
113
|
+
xxs: enableError
|
|
114
|
+
? enableWhiteSpaces ? 'bas-h-[82px] bas-grid-rows-[18px_34px_18px]' : 'bas-h-[58px] bas-grid-rows-[34px_18px]'
|
|
115
|
+
: 'bas-h-[34px]',
|
|
116
|
+
xs: enableError
|
|
117
|
+
? enableWhiteSpaces ? 'bas-h-[86px] bas-grid-rows-[18px_38px_18px]' : 'bas-h-[62px] bas-grid-rows-[38px_18px]'
|
|
118
|
+
: 'bas-h-[38px]',
|
|
119
|
+
small: enableError
|
|
120
|
+
? enableWhiteSpaces ? 'bas-h-[90px] bas-grid-rows-[18px_42px_18px]' : 'bas-h-[66px] bas-grid-rows-[42px_18px]'
|
|
121
|
+
: 'bas-h-[42px]',
|
|
122
|
+
base: enableError
|
|
123
|
+
? enableWhiteSpaces ? 'bas-h-[94px] bas-grid-rows-[18px_46px_18px]' : 'bas-h-[70px] bas-grid-rows-[46px_18px]'
|
|
124
|
+
: 'bas-h-[46px]',
|
|
125
|
+
medium: enableError
|
|
126
|
+
? enableWhiteSpaces ? 'bas-h-[102px] bas-grid-rows-[18px_54px_18px]' : 'bas-h-[78px] bas-grid-rows-[54px_18px]'
|
|
127
|
+
: 'bas-h-[54px]',
|
|
128
|
+
}[this.size()];
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
xxs: enableError
|
|
132
|
+
? enableWhiteSpaces ? 'bas-h-[96px] bas-grid-rows-[18px_48px_18px]' : 'bas-h-[72px] bas-grid-rows-[48px_18px]'
|
|
133
|
+
: 'bas-h-[48px]',
|
|
134
|
+
xs: enableError
|
|
135
|
+
? enableWhiteSpaces ? 'bas-h-[112px] bas-grid-rows-[18px_64px_18px]' : 'bas-h-[88px] bas-grid-rows-[64px_18px]'
|
|
136
|
+
: 'bas-h-[64px]',
|
|
137
|
+
small: enableError
|
|
138
|
+
? enableWhiteSpaces ? 'bas-h-[128px] bas-grid-rows-[18px_80px_18px]' : 'bas-h-[104px] bas-grid-rows-[80px_18px]'
|
|
139
|
+
: 'bas-h-[80px]',
|
|
140
|
+
base: enableError
|
|
141
|
+
? enableWhiteSpaces ? 'bas-h-[144px] bas-grid-rows-[18px_96px_18px]' : 'bas-h-[120px] bas-grid-rows-[96px_18px]'
|
|
142
|
+
: 'bas-h-[96px]',
|
|
143
|
+
medium: enableError
|
|
144
|
+
? enableWhiteSpaces ? 'bas-h-[160px] bas-grid-rows-[18px_112px_18px]' : 'bas-h-[136px] bas-grid-rows-[112px_18px]'
|
|
145
|
+
: 'bas-h-[112px]',
|
|
146
|
+
}[this.size()];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
outerHeightGrid = computed(() => this.heightGrid(this.enableError(), this.enableWhiteSpaces()));
|
|
150
|
+
innerHeightGrid = computed(() => this.heightGrid(false, false));
|
|
151
|
+
|
|
152
|
+
private inputHeightClassNames = computed(() => {
|
|
153
|
+
if (!this.isTextArea()) {
|
|
154
|
+
return {
|
|
155
|
+
xxs: 'bas-h-[34px] bas-pb-0.5 bas-pt-1.5',
|
|
156
|
+
xs: 'bas-h-[38px] bas-pb-0.5 bas-pt-2',
|
|
157
|
+
small: 'bas-h-[42px] bas-pb-1 bas-pt-2.5',
|
|
158
|
+
base: 'bas-h-[46px] bas-pb-1.5 bas-pt-3',
|
|
159
|
+
medium: 'bas-h-[54px] bas-pb-2.5 bas-pt-4',
|
|
160
|
+
}[this.size()];
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
xxs: 'bas-h-[48px] bas-pb-0.5 bas-pt-1.5',
|
|
164
|
+
xs: 'bas-h-[64px] bas-pb-0.5 bas-pt-2',
|
|
165
|
+
small: 'bas-h-[80px] bas-pb-1 bas-pt-2.5',
|
|
166
|
+
base: 'bas-h-[96px] bas-pb-1.5 bas-pt-3',
|
|
167
|
+
medium: 'bas-h-[112px] bas-pb-2.5 bas-pt-4',
|
|
168
|
+
}[this.size()];
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
inputClassNames = computed(
|
|
172
|
+
() => `bal-ba-floating-input-base bal-ba-floating-input-outlined
|
|
173
|
+
bas-peer bas-box-border bas-block bas-w-full bas-appearance-none bas-border-2
|
|
174
|
+
bal-ba-transition-150 bas-border-solid bas-px-2.5 bas-text-sm bal-ba-outline-none
|
|
175
|
+
bal-ba-shadow-base bal-ba-focus-shadow-none disabled:bas-cursor-not-allowed
|
|
176
|
+
disabled:bas-border-gray-200 disabled:bas-text-gray-500
|
|
177
|
+
${this.size() !== 'xxs' ? 'xsm1:bas-text-base' : ''} ${this.isTextArea() ? 'bal-ba-scroll-base bas-resize-none' : ''}
|
|
178
|
+
${this.borderRadiusClassNames()} ${this.inputHeightClassNames()} ${
|
|
179
|
+
!this.error()
|
|
180
|
+
? `focus:bas-border-[var(--bal-primary-100)] focus:hover:bas-border-[var(--bal-primary-300)] ${
|
|
181
|
+
this.blueStyles()
|
|
182
|
+
? 'bas-text-[#004e98] hover:bas-text-[#00296b] bas-border-[var(--bal-primary-100)] hover:bas-border-[var(--bal-primary-300)]'
|
|
183
|
+
: `bas-text-gray-800 hover:bas-text-gray-900 bas-border-gray-300 hover:bas-border-gray-400
|
|
184
|
+
focus:bas-text-[#004e98] focus:hover:bas-text-[#00296b]`
|
|
185
|
+
}`
|
|
186
|
+
: 'bas-border-red-500 bas-text-red-500 focus:bas-border-red-600'
|
|
187
|
+
} ${this.isBgRedWhenError() && this.error() ? this.bgColorWhenError() : this.bgColor()}`,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
floatingLabelClassNames = computed(
|
|
191
|
+
() => `bal-ba-floating-label-base bal-ba-floating-label-outlined
|
|
192
|
+
bal-ba-transform-base bal-ba-transition-2 bas-absolute
|
|
193
|
+
bas-start-1 bas-z-10 bas-origin-[0] bas-cursor-text bas-px-2
|
|
194
|
+
peer-placeholder-shown:bas-top-1/2 peer-focus:bas-px-2
|
|
195
|
+
rtl:peer-focus:bas-left-auto ${
|
|
196
|
+
this.size() === 'small'
|
|
197
|
+
? 'bal-ba-floating-label-small-outlined bas-top-1 peer-focus:bas-top-1'
|
|
198
|
+
: 'bal-ba-floating-label-base-medium-outlined bas-top-2 peer-focus:bas-top-2'
|
|
199
|
+
} ${
|
|
200
|
+
this.isDisabled()
|
|
201
|
+
? 'bas-cursor-not-allowed bas-text-gray-400'
|
|
202
|
+
: !this.error()
|
|
203
|
+
? this.blueStyles()
|
|
204
|
+
? 'bas-text-[#004e98] peer-hover:bas-text-[#00296b]'
|
|
205
|
+
: `bas-text-gray-500 peer-hover:bas-text-gray-600
|
|
206
|
+
peer-focus:bas-text-[#004e98] peer-focus:hover:bas-text-[#00296b]`
|
|
207
|
+
: 'bas-text-red-500 peer-focus:bas-text-red-500'
|
|
208
|
+
} ${this.isBgRedWhenError() && this.error() ? this.bgColorWhenError() : this.floatingLabelBgColor()} ${this.floatingLabelTextStyles()}`,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { Component } from '@angular/core';
|
|
3
|
+
import { render, fireEvent } from '@testing-library/angular';
|
|
4
|
+
import { LifetimeSliderBaseComponent } from './lifetime-slider-base.component';
|
|
5
|
+
|
|
6
|
+
// Espejo de packages/vue/src/primitives/LifetimeSliderBase.test.ts. Cubre el
|
|
7
|
+
// árbol completo del port (LifetimeSliderBase -> TimePicker ->
|
|
8
|
+
// DropdownPickerAnimatedBase + TimePickerDropdownSelector + TimeUnitSelector +
|
|
9
|
+
// SliderBase), en particular la reconciliación de rangos y el commit en
|
|
10
|
+
// segundos de TimePickerDropdownSelector, que es la parte con más estado.
|
|
11
|
+
describe('LifetimeSliderBaseComponent', () => {
|
|
12
|
+
const TEMPLATE = `
|
|
13
|
+
<ba-lifetime-slider-base
|
|
14
|
+
id="lt"
|
|
15
|
+
[sliderValue]="sliderValue"
|
|
16
|
+
[min]="10"
|
|
17
|
+
[max]="29100"
|
|
18
|
+
[timeInputValue]="timeInputValue"
|
|
19
|
+
[isOpen]="isOpen"
|
|
20
|
+
(sliderValueChanged)="onSlider($event)"
|
|
21
|
+
(timeChanged)="onTime($event)"
|
|
22
|
+
(toggleTimerDropdown)="onToggle()"
|
|
23
|
+
/>
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
function makeHost(overrides: Partial<Record<string, unknown>> = {}) {
|
|
27
|
+
@Component({ standalone: true, imports: [LifetimeSliderBaseComponent], template: TEMPLATE })
|
|
28
|
+
class Host {
|
|
29
|
+
sliderValue = 3600;
|
|
30
|
+
timeInputValue = '01:00:00';
|
|
31
|
+
isOpen = false;
|
|
32
|
+
onSlider = vi.fn();
|
|
33
|
+
onTime = vi.fn();
|
|
34
|
+
onToggle = vi.fn();
|
|
35
|
+
constructor() {
|
|
36
|
+
Object.assign(this, overrides);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return Host;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
it('renderiza el slider (SliderBase) y el input del TimePicker con el valor HH:MM:SS', async () => {
|
|
43
|
+
const { container } = await render(makeHost());
|
|
44
|
+
const slider = container.querySelector('#lt-slider') as HTMLInputElement;
|
|
45
|
+
expect(slider).not.toBeNull();
|
|
46
|
+
expect(slider.type).toBe('range');
|
|
47
|
+
expect(slider.value).toBe('3600');
|
|
48
|
+
const timeInput = container.querySelector('#lt-time-picker-time-input') as HTMLInputElement;
|
|
49
|
+
expect(timeInput.value).toBe('01:00:00');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('emite toggleTimerDropdown al pulsar el botón del picker', async () => {
|
|
53
|
+
const onToggle = vi.fn();
|
|
54
|
+
const { container } = await render(makeHost({ onToggle }));
|
|
55
|
+
await fireEvent.click(container.querySelector('#lt-time-picker-btn') as HTMLButtonElement);
|
|
56
|
+
expect(onToggle).toHaveBeenCalledOnce();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('abierto: muestra las 3 columnas del picker (horas/minutos/segundos) y el botón Save', async () => {
|
|
60
|
+
const { container } = await render(makeHost({ isOpen: true }));
|
|
61
|
+
expect(container.querySelector('#lt-time-picker-hours-selector-container')).not.toBeNull();
|
|
62
|
+
expect(container.querySelector('#lt-time-picker-minutes-selector-container')).not.toBeNull();
|
|
63
|
+
expect(container.querySelector('#lt-time-picker-seconds-selector-container')).not.toBeNull();
|
|
64
|
+
expect(container.querySelector('#lt-time-picker-btn-save')).not.toBeNull();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('elegir una hora y guardar emite timeChanged en segundos', async () => {
|
|
68
|
+
const onTime = vi.fn();
|
|
69
|
+
const onToggle = vi.fn();
|
|
70
|
+
const { container } = await render(makeHost({ isOpen: true, onTime, onToggle }));
|
|
71
|
+
// Selecciona 02 horas (id `${id}-time-picker-hours-02`).
|
|
72
|
+
await fireEvent.click(container.querySelector('#lt-time-picker-hours-02') as HTMLElement);
|
|
73
|
+
const save = container.querySelector('#lt-time-picker-btn-save') as HTMLButtonElement;
|
|
74
|
+
expect(save.disabled).toBe(false); // cambió respecto del valor inicial
|
|
75
|
+
await fireEvent.click(save);
|
|
76
|
+
// 02:00:00 -> 7200s.
|
|
77
|
+
expect(onTime).toHaveBeenCalledWith(7200);
|
|
78
|
+
expect(onToggle).toHaveBeenCalledOnce();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('mover el slider emite sliderValueChanged en segundos', async () => {
|
|
82
|
+
const onSlider = vi.fn();
|
|
83
|
+
const { container } = await render(makeHost({ onSlider }));
|
|
84
|
+
const slider = container.querySelector('#lt-slider') as HTMLInputElement;
|
|
85
|
+
await fireEvent.input(slider, { target: { value: '7200' } });
|
|
86
|
+
expect(onSlider).toHaveBeenCalledWith(7200);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, input, output } from '@angular/core';
|
|
2
|
+
import { TimePickerComponent } from './pickers/time-picker.component';
|
|
3
|
+
import { SliderBaseComponent } from './slider-base.component';
|
|
4
|
+
|
|
5
|
+
// Control de lifetime de sesión: un `<ba-time-picker>` `HH:MM:SS` junto a un
|
|
6
|
+
// `<ba-slider-base>`, ambos manejando el mismo valor. El padre guarda el valor
|
|
7
|
+
// en segundos y lo pasa como `sliderValue` (número) y `timeInputValue` (el mismo
|
|
8
|
+
// valor formateado `HH:MM:SS`); tanto `sliderValueChanged` como `timeChanged`
|
|
9
|
+
// reportan de vuelta en segundos, así el padre mantiene ambas vistas en sync.
|
|
10
|
+
// `:host { display: contents }`: el grid raíz es el hijo real del contenedor.
|
|
11
|
+
@Component({
|
|
12
|
+
selector: 'ba-lifetime-slider-base',
|
|
13
|
+
standalone: true,
|
|
14
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
15
|
+
host: { style: 'display: contents' },
|
|
16
|
+
imports: [TimePickerComponent, SliderBaseComponent],
|
|
17
|
+
template: `
|
|
18
|
+
<div [class]="classNames()">
|
|
19
|
+
<div [class]="timePickerContainerClassNames()">
|
|
20
|
+
<ba-time-picker
|
|
21
|
+
[id]="id() + '-time-picker'"
|
|
22
|
+
size="small"
|
|
23
|
+
[error]="timeInputError()"
|
|
24
|
+
[errorMessage]="timeInputErrorMessage()"
|
|
25
|
+
[isOpen]="isOpen()"
|
|
26
|
+
[min]="min()"
|
|
27
|
+
[max]="max()"
|
|
28
|
+
[btnSaveText]="btnSaveText()"
|
|
29
|
+
[timeInputValue]="timeInputValue()"
|
|
30
|
+
[isInsideModal]="isTimePickerInsideModal()"
|
|
31
|
+
[modalId]="modalId()"
|
|
32
|
+
[dropdownDirection]="timePickerDropdownDirection()"
|
|
33
|
+
(isOpenChange)="isOpenChange.emit($event)"
|
|
34
|
+
(toggleTimerDropdown)="toggleTimerDropdown.emit()"
|
|
35
|
+
(timeChanged)="timeChanged.emit($event)"
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
<ba-slider-base
|
|
39
|
+
[id]="id() + '-slider'"
|
|
40
|
+
[sliderValue]="sliderValue()"
|
|
41
|
+
[step]="step()"
|
|
42
|
+
[min]="min()"
|
|
43
|
+
[max]="max()"
|
|
44
|
+
[classNames]="sliderContainerClassNames()"
|
|
45
|
+
(sliderValueChanged)="sliderValueChanged.emit($event)"
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
48
|
+
`,
|
|
49
|
+
})
|
|
50
|
+
export class LifetimeSliderBaseComponent {
|
|
51
|
+
id = input('lifetime-slider');
|
|
52
|
+
sliderValue = input(150);
|
|
53
|
+
step = input(1);
|
|
54
|
+
min = input(0);
|
|
55
|
+
max = input(100);
|
|
56
|
+
btnSaveText = input('Save');
|
|
57
|
+
timeInputError = input(false);
|
|
58
|
+
timeInputErrorMessage = input('');
|
|
59
|
+
timeInputValue = input('08:05:00');
|
|
60
|
+
isOpen = input(false);
|
|
61
|
+
isTimePickerInsideModal = input(false);
|
|
62
|
+
modalId = input('txns-overview');
|
|
63
|
+
timePickerDropdownDirection = input<'up' | 'down'>('down');
|
|
64
|
+
classNames = input(
|
|
65
|
+
'bas-grid bas-h-auto bas-w-full bas-grid-rows-[auto_auto] bas-items-center bas-gap-2 ' +
|
|
66
|
+
'2xsm1:bas-grid-cols-[152px_1fr] 2xsm1:bas-grid-rows-1 1xl:bas-gap-4 2xl:bas-gap-8 bas-mb-6 bas-mt-2',
|
|
67
|
+
);
|
|
68
|
+
timePickerContainerClassNames = input('bas-flex bas-w-full bas-items-center bas-justify-center');
|
|
69
|
+
sliderContainerClassNames = input('');
|
|
70
|
+
|
|
71
|
+
sliderValueChanged = output<number>();
|
|
72
|
+
timeChanged = output<number>();
|
|
73
|
+
isOpenChange = output<boolean>();
|
|
74
|
+
toggleTimerDropdown = output<void>();
|
|
75
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { render } from '@testing-library/angular';
|
|
3
|
+
import { OvalSpinnerComponent } from './oval-spinner.component';
|
|
4
|
+
describe('OvalSpinnerComponent', () => {
|
|
5
|
+
it('renderiza un svg con la clase de spin', async () => {
|
|
6
|
+
const { container } = await render(OvalSpinnerComponent);
|
|
7
|
+
expect(container.querySelector('.bas-oval-spin')).toBeTruthy();
|
|
8
|
+
});
|
|
9
|
+
});
|