@foxeltech/angular-ui 0.0.4 → 0.0.6
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/README.md +2 -2
- package/fesm2022/foxeltech-angular-ui.mjs +176 -19
- package/fesm2022/foxeltech-angular-ui.mjs.map +1 -1
- package/index.d.ts +61 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @foxeltech/angular-ui
|
|
2
2
|
|
|
3
|
-
Common UI & Service Library
|
|
3
|
+
Common UI & Service Library
|
|
@@ -310,9 +310,9 @@ class FxUtils {
|
|
|
310
310
|
return 'Session timeout. Please login again.';
|
|
311
311
|
}
|
|
312
312
|
if (this.isStringEmpty(_.get(err, 'error.code'))) {
|
|
313
|
-
return _.get(err, 'error.
|
|
313
|
+
return _.get(err, 'error.errors', 'Unknown Error');
|
|
314
314
|
}
|
|
315
|
-
return `${_.get(err, 'error.code')}: ${_.get(err, 'error.
|
|
315
|
+
return `${_.get(err, 'error.code')}: ${_.get(err, 'error.errors', 'Unknown Error')}`;
|
|
316
316
|
}
|
|
317
317
|
static convertColorFromVariable(name, alpha = 1) {
|
|
318
318
|
const value = getComputedStyle(document.body)
|
|
@@ -363,10 +363,6 @@ class FxUtils {
|
|
|
363
363
|
const isEmail = emailRegex.test(value);
|
|
364
364
|
return isEmail;
|
|
365
365
|
}
|
|
366
|
-
static isStrongPassword(password) {
|
|
367
|
-
const regex = /^(?=.{6,}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^A-Za-z0-9]).*$/;
|
|
368
|
-
return regex.test(password);
|
|
369
|
-
}
|
|
370
366
|
static formatSizeUnits(bytes) {
|
|
371
367
|
if (bytes >= 1073741824) {
|
|
372
368
|
return (bytes / 1073741824).toFixed(2) + ' GB';
|
|
@@ -513,6 +509,9 @@ class BaseTableComponent extends BaseComponent {
|
|
|
513
509
|
}
|
|
514
510
|
async refresh() {
|
|
515
511
|
this.page = 0;
|
|
512
|
+
if (this.paginator) {
|
|
513
|
+
this.paginator.pageIndex = 0;
|
|
514
|
+
}
|
|
516
515
|
await this.fetch();
|
|
517
516
|
}
|
|
518
517
|
setDataSource(result) {
|
|
@@ -890,6 +889,122 @@ class BaseResolver {
|
|
|
890
889
|
}
|
|
891
890
|
}
|
|
892
891
|
|
|
892
|
+
class FxValidators {
|
|
893
|
+
/**
|
|
894
|
+
* Name fields: firstName, lastName, groupName, projectName, displayName
|
|
895
|
+
* Allows: letters (Unicode), spaces, hyphens, apostrophes, periods
|
|
896
|
+
*/
|
|
897
|
+
static validateName(value) {
|
|
898
|
+
if (!value || value.trim() === '')
|
|
899
|
+
return null;
|
|
900
|
+
const regex = /^[\p{L}\s'\-.]{1,100}$/u;
|
|
901
|
+
return regex.test(value) ? null : { invalidName: 'Only letters, spaces, hyphens, apostrophes, and periods are allowed (max 100 characters)' };
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Description fields
|
|
905
|
+
* Allows: letters, digits, spaces, basic punctuation
|
|
906
|
+
*/
|
|
907
|
+
static validateDescription(value) {
|
|
908
|
+
if (!value || value.trim() === '')
|
|
909
|
+
return null;
|
|
910
|
+
const regex = /^[\p{L}\d\s.,;:!?'/()"&\-\n\r]{1,500}$/u;
|
|
911
|
+
return regex.test(value) ? null : { invalidDescription: 'Contains disallowed characters (max 500 characters)' };
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Email fields
|
|
915
|
+
*/
|
|
916
|
+
static validateEmail(value) {
|
|
917
|
+
if (!value || value.trim() === '')
|
|
918
|
+
return null;
|
|
919
|
+
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
920
|
+
return regex.test(value) ? null : { invalidEmail: 'Please enter a valid email address' };
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Phone fields
|
|
924
|
+
* Allows: digits, optional leading +, spaces, hyphens, parentheses (7-15 digits)
|
|
925
|
+
*/
|
|
926
|
+
static validatePhone(value) {
|
|
927
|
+
if (!value || value.trim() === '')
|
|
928
|
+
return null;
|
|
929
|
+
const regex = /^\+?[\d\s\-()]{7,20}$/;
|
|
930
|
+
const digitCount = value.replace(/\D/g, '').length;
|
|
931
|
+
if (!regex.test(value) || digitCount < 7 || digitCount > 15) {
|
|
932
|
+
return { invalidPhone: 'Please enter a valid phone number' };
|
|
933
|
+
}
|
|
934
|
+
return null;
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Code/identifier fields: server code, template code, templateId
|
|
938
|
+
* Allows: alphanumeric, hyphens, underscores
|
|
939
|
+
*/
|
|
940
|
+
static validateCode(value) {
|
|
941
|
+
if (!value || value.trim() === '')
|
|
942
|
+
return null;
|
|
943
|
+
const regex = /^[a-zA-Z0-9_\-]{1,50}$/;
|
|
944
|
+
return regex.test(value) ? null : { invalidCode: 'Only letters, numbers, hyphens, and underscores are allowed (max 50 characters)' };
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Domain fields
|
|
948
|
+
* Standard domain pattern with optional port
|
|
949
|
+
*/
|
|
950
|
+
static validateDomain(value) {
|
|
951
|
+
if (!value || value.trim() === '')
|
|
952
|
+
return null;
|
|
953
|
+
const regex = /^([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}(:\d{1,5})?$/;
|
|
954
|
+
return regex.test(value) ? null : { invalidDomain: 'Please enter a valid domain (e.g., mail.example.com)' };
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Subject fields (email subject)
|
|
958
|
+
* Allows: letters, digits, spaces, common punctuation, template placeholders {{}}
|
|
959
|
+
*/
|
|
960
|
+
static validateSubject(value) {
|
|
961
|
+
if (!value || value.trim() === '')
|
|
962
|
+
return null;
|
|
963
|
+
const regex = /^[\p{L}\d\s.,;:!?'/()"&\-{}]{1,200}$/u;
|
|
964
|
+
return regex.test(value) ? null : { invalidSubject: 'Contains disallowed characters (max 200 characters)' };
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Tag fields
|
|
968
|
+
* Allows: alphanumeric, hyphens, underscores, dots, spaces
|
|
969
|
+
*/
|
|
970
|
+
static validateTag(value) {
|
|
971
|
+
if (!value || value.trim() === '')
|
|
972
|
+
return null;
|
|
973
|
+
const regex = /^[a-zA-Z0-9_\-.\s]{1,50}$/;
|
|
974
|
+
return regex.test(value) ? null : { invalidTag: 'Only letters, numbers, hyphens, underscores, dots, and spaces are allowed' };
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* URL fields: proxy, masterUrl
|
|
978
|
+
* Standard URL pattern
|
|
979
|
+
*/
|
|
980
|
+
static validateUrl(value) {
|
|
981
|
+
if (!value || value.trim() === '')
|
|
982
|
+
return null;
|
|
983
|
+
const regex = /^https?:\/\/[a-zA-Z0-9\-._~:/?#[\]@!$&'()*+,;=%]{1,2000}$/;
|
|
984
|
+
return regex.test(value) ? null : { invalidUrl: 'Please enter a valid URL (http:// or https://)' };
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* Numeric string fields: timeout, memoryLimit, cpuCores
|
|
988
|
+
* Allows: digits, optional decimal point
|
|
989
|
+
*/
|
|
990
|
+
static validateNumeric(value) {
|
|
991
|
+
if (!value || value.toString().trim() === '')
|
|
992
|
+
return null;
|
|
993
|
+
const regex = /^\d+(\.\d+)?$/;
|
|
994
|
+
return regex.test(value.toString()) ? null : { invalidNumeric: 'Please enter a valid number' };
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* Version fields (semver-like)
|
|
998
|
+
* Allows: digits, dots, hyphens, plus, letters
|
|
999
|
+
*/
|
|
1000
|
+
static validateVersion(value) {
|
|
1001
|
+
if (!value || value.trim() === '')
|
|
1002
|
+
return null;
|
|
1003
|
+
const regex = /^[a-zA-Z0-9.\-+]{1,50}$/;
|
|
1004
|
+
return regex.test(value) ? null : { invalidVersion: 'Please enter a valid version (e.g., 1.0.0)' };
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
893
1008
|
function HttpLoaderFactory(http) {
|
|
894
1009
|
return new TranslateHttpLoader(http, '/assets/i18n/', '.json');
|
|
895
1010
|
}
|
|
@@ -1510,6 +1625,7 @@ class ChartComponent {
|
|
|
1510
1625
|
intersectionObserver;
|
|
1511
1626
|
canInit = false;
|
|
1512
1627
|
isMobile = false;
|
|
1628
|
+
containerWidth = 0;
|
|
1513
1629
|
constructor(ref, cdr) {
|
|
1514
1630
|
this.ref = ref;
|
|
1515
1631
|
this.cdr = cdr;
|
|
@@ -1563,6 +1679,7 @@ class ChartComponent {
|
|
|
1563
1679
|
updateResponsiveState() {
|
|
1564
1680
|
const { width } = this.ref.nativeElement.getBoundingClientRect();
|
|
1565
1681
|
if (width > 0) {
|
|
1682
|
+
this.containerWidth = width;
|
|
1566
1683
|
this.isMobile = width < this.mobileWidth;
|
|
1567
1684
|
}
|
|
1568
1685
|
}
|
|
@@ -1574,7 +1691,7 @@ class ChartComponent {
|
|
|
1574
1691
|
requestAnimationFrame(() => this.forceMediaRecalc(retry + 1));
|
|
1575
1692
|
return;
|
|
1576
1693
|
}
|
|
1577
|
-
this.
|
|
1694
|
+
this.rebuildChart();
|
|
1578
1695
|
this.chartInstance.resize();
|
|
1579
1696
|
}
|
|
1580
1697
|
rebuildChart() {
|
|
@@ -1646,20 +1763,20 @@ class ChartComponent {
|
|
|
1646
1763
|
}),
|
|
1647
1764
|
pie: (data, colors) => {
|
|
1648
1765
|
const total = data.reduce((sum, d) => sum + d.value, 0);
|
|
1649
|
-
const
|
|
1766
|
+
const layout = this.calcPieLayout(data);
|
|
1650
1767
|
return {
|
|
1651
1768
|
color: colors,
|
|
1652
1769
|
tooltip: { trigger: 'item' },
|
|
1653
1770
|
legend: {
|
|
1654
|
-
|
|
1771
|
+
type: 'scroll',
|
|
1772
|
+
orient: layout.legendOrient,
|
|
1655
1773
|
icon: 'circle',
|
|
1656
1774
|
itemGap: 12,
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
bottom: isMobile ? 0 : undefined,
|
|
1775
|
+
right: layout.legendRight,
|
|
1776
|
+
top: layout.legendTop,
|
|
1777
|
+
left: layout.legendLeft,
|
|
1778
|
+
bottom: layout.legendBottom,
|
|
1779
|
+
width: layout.legendWidth,
|
|
1663
1780
|
formatter: (name) => {
|
|
1664
1781
|
const item = data.find((d) => d.name === name);
|
|
1665
1782
|
return item ? `${name}: ${item.value}` : name;
|
|
@@ -1676,7 +1793,7 @@ class ChartComponent {
|
|
|
1676
1793
|
{
|
|
1677
1794
|
type: 'pie',
|
|
1678
1795
|
radius: ['40%', '70%'],
|
|
1679
|
-
center:
|
|
1796
|
+
center: layout.pieCenter,
|
|
1680
1797
|
data,
|
|
1681
1798
|
label: {
|
|
1682
1799
|
show: this.showPieLabel,
|
|
@@ -1697,8 +1814,8 @@ class ChartComponent {
|
|
|
1697
1814
|
],
|
|
1698
1815
|
graphic: {
|
|
1699
1816
|
type: 'group',
|
|
1700
|
-
left:
|
|
1701
|
-
top:
|
|
1817
|
+
left: layout.graphicLeft,
|
|
1818
|
+
top: layout.graphicTop,
|
|
1702
1819
|
bounding: 'raw',
|
|
1703
1820
|
children: [
|
|
1704
1821
|
{
|
|
@@ -1740,6 +1857,46 @@ class ChartComponent {
|
|
|
1740
1857
|
],
|
|
1741
1858
|
}),
|
|
1742
1859
|
};
|
|
1860
|
+
/* ================= PIE LAYOUT ================= */
|
|
1861
|
+
calcPieLayout(data) {
|
|
1862
|
+
const cw = this.containerWidth || 400;
|
|
1863
|
+
if (this.isMobile) {
|
|
1864
|
+
return {
|
|
1865
|
+
legendOrient: 'horizontal',
|
|
1866
|
+
legendLeft: 'center',
|
|
1867
|
+
legendRight: undefined,
|
|
1868
|
+
legendTop: undefined,
|
|
1869
|
+
legendBottom: 0,
|
|
1870
|
+
legendWidth: undefined,
|
|
1871
|
+
pieCenter: ['50%', '40%'],
|
|
1872
|
+
graphicLeft: '50%',
|
|
1873
|
+
graphicTop: '40%',
|
|
1874
|
+
};
|
|
1875
|
+
}
|
|
1876
|
+
// Estimate legend pixel width from longest formatted label
|
|
1877
|
+
const CHAR_WIDTH = 7.5; // approx px per char at fontSize 13
|
|
1878
|
+
const ICON_GAP = 30; // legend icon + padding
|
|
1879
|
+
const longestLabel = data.reduce((max, d) => {
|
|
1880
|
+
return Math.max(max, `${d.name}: ${d.value}`.length);
|
|
1881
|
+
}, 0);
|
|
1882
|
+
const rawLegendPx = longestLabel * CHAR_WIDTH + ICON_GAP;
|
|
1883
|
+
// Clamp: min 100px, max 45% of container
|
|
1884
|
+
const legendPx = Math.max(100, Math.min(rawLegendPx, cw * 0.45));
|
|
1885
|
+
const legendPct = (legendPx / cw) * 100;
|
|
1886
|
+
// Pie center: middle of the remaining space after legend
|
|
1887
|
+
const pieCenterX = Math.round((100 - legendPct) / 2);
|
|
1888
|
+
return {
|
|
1889
|
+
legendOrient: 'vertical',
|
|
1890
|
+
legendLeft: undefined,
|
|
1891
|
+
legendRight: '2%',
|
|
1892
|
+
legendTop: 'middle',
|
|
1893
|
+
legendBottom: undefined,
|
|
1894
|
+
legendWidth: Math.round(legendPx),
|
|
1895
|
+
pieCenter: [`${pieCenterX}%`, '50%'],
|
|
1896
|
+
graphicLeft: `${pieCenterX}%`,
|
|
1897
|
+
graphicTop: '50%',
|
|
1898
|
+
};
|
|
1899
|
+
}
|
|
1743
1900
|
/* ================= BUILD ================= */
|
|
1744
1901
|
detectType() {
|
|
1745
1902
|
if (this.type !== 'auto')
|
|
@@ -3449,5 +3606,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
|
|
|
3449
3606
|
* Generated bundle index. Do not edit.
|
|
3450
3607
|
*/
|
|
3451
3608
|
|
|
3452
|
-
export { AuthInterceptor, AuthStateService, BaseComponent, BaseDialogComponent, BaseResolver, BaseTableComponent, BreadcrumbService, ButtonComponent, ChartComponent, CheckboxComponent, CircleProgressBar, ConfirmationDialogComponent, DatetimePicker, DndUploadComponent, DrawerComponent, FlowConnection, FxLoadingService, FxStorageService, FxToastrService, FxUtils, HasPermissionDirective, HeroIconComponent, HttpLoaderFactory, HttpWrapper, InputComponent, KanbanBoardComponent, LoadingPanel, MY_MOMENT_FORMATS, NotificationService, PermissionGuard, PermissionService, QuillStyleLoaderService, RadioButtonComponent, RadioButtonToggleComponent, RichTextAreaComponent, SearchBarComponent, SelectComponent, SkeletonTableLoadingComponent, SliderComponent, SwitchComponent, TabComponent, TabGroupComponent, TableCell, TagComponent, ThemeService, ToastComponent, ToastContainerComponent, TranslationModule, TranslationService, TreeDiagram, TrimOnBlurDirective, UiModule };
|
|
3609
|
+
export { AuthInterceptor, AuthStateService, BaseComponent, BaseDialogComponent, BaseResolver, BaseTableComponent, BreadcrumbService, ButtonComponent, ChartComponent, CheckboxComponent, CircleProgressBar, ConfirmationDialogComponent, DatetimePicker, DndUploadComponent, DrawerComponent, FlowConnection, FxLoadingService, FxStorageService, FxToastrService, FxUtils, FxValidators, HasPermissionDirective, HeroIconComponent, HttpLoaderFactory, HttpWrapper, InputComponent, KanbanBoardComponent, LoadingPanel, MY_MOMENT_FORMATS, NotificationService, PermissionGuard, PermissionService, QuillStyleLoaderService, RadioButtonComponent, RadioButtonToggleComponent, RichTextAreaComponent, SearchBarComponent, SelectComponent, SkeletonTableLoadingComponent, SliderComponent, SwitchComponent, TabComponent, TabGroupComponent, TableCell, TagComponent, ThemeService, ToastComponent, ToastContainerComponent, TranslationModule, TranslationService, TreeDiagram, TrimOnBlurDirective, UiModule };
|
|
3453
3610
|
//# sourceMappingURL=foxeltech-angular-ui.mjs.map
|