@fleetbase/ember-ui 0.3.9 → 0.3.11

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.
Files changed (51) hide show
  1. package/SCHEDULING_COMPONENTS.md +228 -0
  2. package/addon/components/availability-editor.js +81 -0
  3. package/addon/components/custom-field/input.hbs +1 -1
  4. package/addon/components/custom-field/input.js +61 -24
  5. package/addon/components/custom-field/options-input.hbs +1 -1
  6. package/addon/components/file.hbs +52 -49
  7. package/addon/components/file.js +5 -36
  8. package/addon/components/filter/multi-input.hbs +16 -0
  9. package/addon/components/filter/multi-input.js +68 -0
  10. package/addon/components/filter/range.hbs +52 -0
  11. package/addon/components/filter/range.js +74 -0
  12. package/addon/components/filters-picker.js +13 -6
  13. package/addon/components/layout/resource/panel.js +22 -12
  14. package/addon/components/layout/resource/tabular.hbs +2 -0
  15. package/addon/components/layout/resource/tabular.js +28 -0
  16. package/addon/components/modals/query-builder-computed-column-editor.hbs +80 -0
  17. package/addon/components/modals/query-builder-computed-column-editor.js +156 -0
  18. package/addon/components/query-builder/computed-columns.hbs +59 -0
  19. package/addon/components/query-builder/computed-columns.js +106 -0
  20. package/addon/components/query-builder.hbs +10 -0
  21. package/addon/components/query-builder.js +5 -0
  22. package/addon/components/report-builder.hbs +1 -0
  23. package/addon/components/schedule-calendar.hbs +37 -0
  24. package/addon/components/schedule-calendar.js +166 -0
  25. package/addon/components/schedule-item-card.hbs +39 -0
  26. package/addon/components/schedule-item-card.js +74 -0
  27. package/addon/components/table/cell/dropdown.hbs +2 -0
  28. package/addon/components/table/cell/dropdown.js +11 -0
  29. package/addon/components/table/cell/resizer.js +1 -1
  30. package/addon/components/table/cell.hbs +15 -2
  31. package/addon/components/table/cell.js +24 -0
  32. package/addon/components/table/foot.js +1 -1
  33. package/addon/components/table/td.hbs +2 -2
  34. package/addon/components/table/td.js +66 -1
  35. package/addon/components/table/th.hbs +15 -1
  36. package/addon/components/table/th.js +84 -0
  37. package/addon/components/table.hbs +2 -2
  38. package/addon/components/table.js +288 -0
  39. package/addon/styles/components/badge.css +8 -0
  40. package/addon/styles/components/file.css +32 -18
  41. package/addon/styles/components/input.css +8 -0
  42. package/addon/styles/components/table.css +126 -0
  43. package/addon/styles/layout/next.css +249 -28
  44. package/addon/utils/is-image-file.js +101 -0
  45. package/app/components/filter/multi-input.js +1 -0
  46. package/app/components/filter/range.js +1 -0
  47. package/app/components/modals/query-builder-computed-column-editor.js +1 -0
  48. package/app/components/query-builder/computed-columns.js +1 -0
  49. package/app/components/schedule-calendar.js +1 -0
  50. package/app/utils/is-image-file.js +1 -0
  51. package/package.json +1 -1
@@ -695,33 +695,33 @@ body[data-theme='dark'] .next-map-container.next-map-container-layout-map .next-
695
695
  @apply mb-0;
696
696
  }
697
697
 
698
- .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-control-zoom.leaflet-bar.leaflet-control > a[role="button"].leaflet-control-zoom-out {
698
+ .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-control-zoom.leaflet-bar.leaflet-control > a[role='button'].leaflet-control-zoom-out {
699
699
  @apply mb-0;
700
700
  }
701
701
 
702
- .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-bar.leaflet-control > a[role="button"] {
702
+ .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-bar.leaflet-control > a[role='button'] {
703
703
  @apply bg-transparent flex items-center justify-center rounded-full w-8 h-8 m-auto mb-2 border-0 outline-0;
704
704
  }
705
705
 
706
- .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-bar.leaflet-control > a[role="button"].is-action:hover {
706
+ .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-bar.leaflet-control > a[role='button'].is-action:hover {
707
707
  @apply bg-blue-500;
708
708
  }
709
709
 
710
- .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-bar.leaflet-control > a[role="button"]:focus,
711
- .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-bar.leaflet-control > a[role="button"]:active,
712
- .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-bar.leaflet-control > a[role="button"]:hover {
710
+ .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-bar.leaflet-control > a[role='button']:focus,
711
+ .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-bar.leaflet-control > a[role='button']:active,
712
+ .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-bar.leaflet-control > a[role='button']:hover {
713
713
  @apply outline-none;
714
714
  }
715
715
 
716
- .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-bar.leaflet-control > a[role="button"]:hover {
716
+ .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-bar.leaflet-control > a[role='button']:hover {
717
717
  @apply bg-gray-600;
718
718
  }
719
719
 
720
- body[data-theme='light'] .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-bar.leaflet-control > a[role="button"] svg {
720
+ body[data-theme='light'] .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-bar.leaflet-control > a[role='button'] svg {
721
721
  @apply text-gray-800;
722
722
  }
723
723
 
724
- body[data-theme='light'] .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-bar.leaflet-control > a[role="button"]:hover {
724
+ body[data-theme='light'] .fleetbase-leaflet-map-container .leaflet-control-container .leaflet-bar.leaflet-control > a[role='button']:hover {
725
725
  @apply bg-gray-200;
726
726
  }
727
727
 
@@ -1058,7 +1058,7 @@ body[data-theme='dark'] .next-view-header-item.is-open {
1058
1058
  @apply rounded-md;
1059
1059
  }
1060
1060
 
1061
- body[data-theme="dark"] .next-content-panel-wrapper.bordered .next-content-panel-container .next-content-panel > .next-content-panel-header {
1061
+ body[data-theme='dark'] .next-content-panel-wrapper.bordered .next-content-panel-container .next-content-panel > .next-content-panel-header {
1062
1062
  @apply shadow-sm text-gray-200;
1063
1063
  }
1064
1064
 
@@ -1067,7 +1067,7 @@ body[data-theme="dark"] .next-content-panel-wrapper.bordered .next-content-panel
1067
1067
  @apply border rounded-lg bg-gray-50 dark:bg-gray-900 border-gray-300 dark:border-gray-700 px-3 mb-2;
1068
1068
  }
1069
1069
 
1070
- body[data-theme="dark"] .next-content-panel-wrapper.bordered .next-content-panel-container .next-content-panel > .next-content-panel-header {
1070
+ body[data-theme='dark'] .next-content-panel-wrapper.bordered .next-content-panel-container .next-content-panel > .next-content-panel-header {
1071
1071
  @apply shadow-sm text-gray-200;
1072
1072
  }
1073
1073
 
@@ -1084,7 +1084,7 @@ body[data-theme="dark"] .next-content-panel-wrapper.bordered .next-content-panel
1084
1084
  @apply border-t-0;
1085
1085
  }
1086
1086
 
1087
- body[data-theme="dark"] .next-content-panel-wrapper.bordered-inline .next-content-panel-container .next-content-panel > .next-content-panel-header {
1087
+ body[data-theme='dark'] .next-content-panel-wrapper.bordered-inline .next-content-panel-container .next-content-panel > .next-content-panel-header {
1088
1088
  @apply shadow-sm text-gray-200;
1089
1089
  }
1090
1090
 
@@ -1101,7 +1101,7 @@ body[data-theme="dark"] .next-content-panel-wrapper.bordered-inline .next-conten
1101
1101
  @apply border-t-0;
1102
1102
  }
1103
1103
 
1104
- body[data-theme="dark"] .next-content-panel-wrapper.bordered-top .next-content-panel-container .next-content-panel > .next-content-panel-header {
1104
+ body[data-theme='dark'] .next-content-panel-wrapper.bordered-top .next-content-panel-container .next-content-panel > .next-content-panel-header {
1105
1105
  @apply text-gray-200;
1106
1106
  }
1107
1107
 
@@ -1129,15 +1129,27 @@ body[data-theme="dark"] .next-content-panel-wrapper.bordered-top .next-content-p
1129
1129
  @apply w-5 h-full text-xs;
1130
1130
  }
1131
1131
 
1132
- .next-content-panel-wrapper .next-content-panel-container .next-content-panel > .next-content-panel-header .next-content-panel-header-left > .next-content-panel-title-container .panel-title {
1132
+ .next-content-panel-wrapper
1133
+ .next-content-panel-container
1134
+ .next-content-panel
1135
+ > .next-content-panel-header
1136
+ .next-content-panel-header-left
1137
+ > .next-content-panel-title-container
1138
+ .panel-title {
1133
1139
  @apply text-sm font-semibold uppercase;
1134
1140
  }
1135
1141
 
1136
- .next-content-panel-wrapper .next-content-panel-container .next-content-panel > .next-content-panel-header.bordered .next-content-panel-header-left > .next-content-panel-title-container .panel-title {
1142
+ .next-content-panel-wrapper
1143
+ .next-content-panel-container
1144
+ .next-content-panel
1145
+ > .next-content-panel-header.bordered
1146
+ .next-content-panel-header-left
1147
+ > .next-content-panel-title-container
1148
+ .panel-title {
1137
1149
  @apply font-normal;
1138
1150
  }
1139
1151
 
1140
- body[data-theme="light"] .details-wrapper .next-content-panel-wrapper .next-content-panel-container .next-content-panel > .next-content-panel-header {
1152
+ body[data-theme='light'] .details-wrapper .next-content-panel-wrapper .next-content-panel-container .next-content-panel > .next-content-panel-header {
1141
1153
  @apply bg-white;
1142
1154
  }
1143
1155
 
@@ -1449,8 +1461,21 @@ body[data-theme='light'] .next-sidebar .next-content-panel-wrapper .next-content
1449
1461
  @apply flex flex-row items-center justify-between px-3 py-0.5 rounded-md text-sm mb-1 border border-transparent;
1450
1462
  }
1451
1463
 
1452
- .next-sidebar .next-content-panel-wrapper .next-content-panel-container .next-content-panel > .next-content-panel-header > .next-content-panel-header-left > .next-content-panel-title-container,
1453
- .next-sidebar .next-content-panel-wrapper .next-content-panel-container .next-content-panel > .next-content-panel-header > .next-content-panel-header-left > .next-content-panel-title-container > .panel-title {
1464
+ .next-sidebar
1465
+ .next-content-panel-wrapper
1466
+ .next-content-panel-container
1467
+ .next-content-panel
1468
+ > .next-content-panel-header
1469
+ > .next-content-panel-header-left
1470
+ > .next-content-panel-title-container,
1471
+ .next-sidebar
1472
+ .next-content-panel-wrapper
1473
+ .next-content-panel-container
1474
+ .next-content-panel
1475
+ > .next-content-panel-header
1476
+ > .next-content-panel-header-left
1477
+ > .next-content-panel-title-container
1478
+ > .panel-title {
1454
1479
  @apply text-sm font-normal normal-case;
1455
1480
  }
1456
1481
 
@@ -1463,7 +1488,7 @@ body[data-theme='light'] .next-sidebar .next-content-panel-wrapper .next-content
1463
1488
  }
1464
1489
 
1465
1490
  .next-sidebar .next-sidebar-panel-container > .next-sidebar-panel > .next-content-panel > .next-sidebar-panel-toggle > .next-content-panel-header-left {
1466
- padding: 0;
1491
+ padding: 0;
1467
1492
  margin: 0;
1468
1493
  }
1469
1494
 
@@ -1691,6 +1716,10 @@ body[data-theme='dark']
1691
1716
  @apply text-sm flex flex-row items-center px-3 py-0.5 rounded-md my-1;
1692
1717
  }
1693
1718
 
1719
+ .next-dd-menu .next-dd-item.xs-text {
1720
+ @apply text-xs;
1721
+ }
1722
+
1694
1723
  .next-dd-menu .next-dd-item > .next-nav-item-icon-container {
1695
1724
  width: 1rem;
1696
1725
  }
@@ -1785,15 +1814,18 @@ section.next-view-section {
1785
1814
  }
1786
1815
 
1787
1816
  .next-table-wrapper {
1788
- overflow: scroll;
1817
+ overflow: auto;
1818
+ overflow-x: scroll; /* Always show horizontal scrollbar */
1819
+ overflow-y: scroll;
1789
1820
  height: 100vh;
1790
1821
  @apply grid grid-cols-1;
1791
1822
  }
1792
1823
 
1793
1824
  .next-table-wrapper > table {
1794
- @apply table-fixed w-full h-screen;
1825
+ @apply table-auto min-w-full h-screen;
1795
1826
  border-collapse: separate;
1796
1827
  border-spacing: 0;
1828
+ width: max-content;
1797
1829
  }
1798
1830
 
1799
1831
  .next-table-wrapper table > tbody:after {
@@ -1804,8 +1836,8 @@ section.next-view-section {
1804
1836
  }
1805
1837
 
1806
1838
  .next-table-wrapper table > thead {
1807
- position: sticky;
1808
- top: 0;
1839
+ /* Removed position: sticky to avoid conflict with sticky th elements */
1840
+ /* Individual th elements handle their own sticky positioning */
1809
1841
  z-index: 12;
1810
1842
  }
1811
1843
 
@@ -1836,7 +1868,7 @@ section.next-view-section {
1836
1868
  margin: 0 0.5rem;
1837
1869
  }
1838
1870
 
1839
- .next-table-wrapper table tbody tr td .cell-dropdown-button span.btn-wrapper > button.btn,
1871
+ .next-table-wrapper table tbody tr td .cell-dropdown-button span.btn-wrapper > button.btn,
1840
1872
  .next-table-wrapper table tbody tr td span.btn-wrapper > button.btn {
1841
1873
  height: 21px;
1842
1874
  cursor: default;
@@ -1867,6 +1899,9 @@ body[data-theme='dark'] .next-table-wrapper .tfoot-row > td {
1867
1899
  height: 53px;
1868
1900
  min-width: 100%;
1869
1901
  max-width: 100%;
1902
+ position: sticky;
1903
+ left: 0;
1904
+ right: 0;
1870
1905
  }
1871
1906
 
1872
1907
  .next-table-wrapper
@@ -1895,7 +1930,7 @@ body[data-theme='dark'] .next-table-wrapper .tfoot-row > td {
1895
1930
  }
1896
1931
 
1897
1932
  .next-table-wrapper table thead tr th {
1898
- @apply px-4 py-2 text-xs text-left font-medium leading-4 tracking-wider text-gray-900 whitespace-nowrap uppercase border-b border-gray-200 bg-white;
1933
+ @apply px-2 py-2.5 text-xs text-left font-medium leading-4 tracking-wider text-gray-900 whitespace-nowrap uppercase border-b border-gray-200 bg-white;
1899
1934
  text-overflow: ellipsis;
1900
1935
  }
1901
1936
 
@@ -1935,9 +1970,13 @@ body[data-theme='dark'] .next-table-wrapper .tfoot-row > td {
1935
1970
  max-height: 40px;
1936
1971
  }
1937
1972
 
1973
+ .next-table-wrapper table tfoot {
1974
+ z-index: 30;
1975
+ }
1976
+
1938
1977
  .next-table-wrapper table tfoot tr td,
1939
1978
  .next-table-wrapper table tbody tr td {
1940
- @apply px-4 py-1 text-sm font-medium leading-4 tracking-wider text-gray-800 truncate whitespace-nowrap select-none border-b border-gray-200;
1979
+ @apply px-2 py-1 text-sm font-medium leading-4 tracking-wider text-gray-800 truncate whitespace-nowrap select-none border-b border-gray-200;
1941
1980
  }
1942
1981
 
1943
1982
  .next-table-wrapper table tfoot tr td span a,
@@ -1960,6 +1999,93 @@ body[data-theme='dark'] .next-table-wrapper .tfoot-row > td {
1960
1999
  border-top: 0;
1961
2000
  }
1962
2001
 
2002
+ /* Sticky Column Styles */
2003
+ .next-table-wrapper table thead th.is-sticky,
2004
+ .next-table-wrapper table tbody td.is-sticky {
2005
+ position: sticky;
2006
+ @apply bg-white;
2007
+ }
2008
+
2009
+ .next-table-wrapper table thead th.is-sticky {
2010
+ z-index: 20;
2011
+ top: 0;
2012
+ }
2013
+
2014
+ .next-table-wrapper table tbody td.is-sticky {
2015
+ z-index: 15;
2016
+ }
2017
+
2018
+ .next-table-wrapper table thead th.is-sticky.sticky-left {
2019
+ left: 0;
2020
+ top: 0;
2021
+ box-shadow:
2022
+ 4px 0 8px -2px rgba(0, 0, 0, 0.15),
2023
+ 2px 0 4px -1px rgba(0, 0, 0, 0.1);
2024
+ }
2025
+
2026
+ .next-table-wrapper table tbody td.is-sticky.sticky-left {
2027
+ left: 0;
2028
+ box-shadow:
2029
+ 4px 0 8px -2px rgba(0, 0, 0, 0.15),
2030
+ 2px 0 4px -1px rgba(0, 0, 0, 0.1);
2031
+ }
2032
+
2033
+ .next-table-wrapper table thead th.is-sticky.sticky-right {
2034
+ right: 0;
2035
+ top: 0;
2036
+ box-shadow:
2037
+ -4px 0 8px -2px rgba(0, 0, 0, 0.15),
2038
+ -2px 0 4px -1px rgba(0, 0, 0, 0.1);
2039
+ }
2040
+
2041
+ .next-table-wrapper table tbody td.is-sticky.sticky-right {
2042
+ right: 0;
2043
+ box-shadow:
2044
+ -4px 0 8px -2px rgba(0, 0, 0, 0.15),
2045
+ -2px 0 4px -1px rgba(0, 0, 0, 0.1);
2046
+ }
2047
+
2048
+ /* Hide shadows when sticky columns are at their natural position */
2049
+ .next-table-wrapper table thead th.is-sticky.at-natural-position,
2050
+ .next-table-wrapper table tbody td.is-sticky.at-natural-position {
2051
+ box-shadow: none !important;
2052
+ }
2053
+
2054
+ /* Ensure sticky columns have proper background on hover */
2055
+ .next-table-wrapper table tbody tr:hover td.is-sticky {
2056
+ @apply bg-white;
2057
+ }
2058
+
2059
+ /* Dark theme support for sticky columns */
2060
+ body[data-theme='dark'] .next-table-wrapper table thead th.is-sticky,
2061
+ body[data-theme='dark'] .next-table-wrapper table tbody td.is-sticky {
2062
+ @apply bg-gray-900;
2063
+ }
2064
+
2065
+ body[data-theme='dark'] .next-table-wrapper table tbody tr:hover td.is-sticky {
2066
+ @apply bg-gray-900;
2067
+ }
2068
+
2069
+ body[data-theme='dark'] .next-table-wrapper table thead th.is-sticky.sticky-left,
2070
+ body[data-theme='dark'] .next-table-wrapper table tbody td.is-sticky.sticky-left {
2071
+ box-shadow:
2072
+ 4px 0 8px -2px rgba(0, 0, 0, 0.4),
2073
+ 2px 0 4px -1px rgba(0, 0, 0, 0.3);
2074
+ }
2075
+
2076
+ body[data-theme='dark'] .next-table-wrapper table thead th.is-sticky.sticky-right,
2077
+ body[data-theme='dark'] .next-table-wrapper table tbody td.is-sticky.sticky-right {
2078
+ box-shadow:
2079
+ -4px 0 8px -2px rgba(0, 0, 0, 0.4),
2080
+ -2px 0 4px -1px rgba(0, 0, 0, 0.3);
2081
+ }
2082
+
2083
+ /* Dark theme - hide shadows when at natural position */
2084
+ body[data-theme='dark'] .next-table-wrapper table thead th.is-sticky.at-natural-position,
2085
+ body[data-theme='dark'] .next-table-wrapper table tbody td.is-sticky.at-natural-position {
2086
+ box-shadow: none !important;
2087
+ }
2088
+
1963
2089
  body[data-theme='dark'] .next-table-wrapper table tfoot tr td {
1964
2090
  @apply bg-gray-900;
1965
2091
  }
@@ -2041,7 +2167,7 @@ body[data-theme='light'] .next-table-wrapper table tfoot tr td {
2041
2167
  }
2042
2168
 
2043
2169
  .field-info-container.field-vertical-container + .field-info-container.field-vertical-container {
2044
- @apply mt-1.5;
2170
+ @apply mt-1.5;
2045
2171
  }
2046
2172
 
2047
2173
  .field-info-container.field-vertical-container > .field-name {
@@ -2328,6 +2454,101 @@ a.text-danger:hover {
2328
2454
  @apply flex flex-row items-center;
2329
2455
  }
2330
2456
 
2457
+ .filter-multi-input {
2458
+ @apply flex flex-col relative;
2459
+ height: 34px;
2460
+ }
2461
+
2462
+ .filter-multi-input > .clear-button {
2463
+ @apply absolute top-0 right-0 dark:text-gray-500 text-gray-700 pr-3 pt-2;
2464
+ }
2465
+
2466
+ .filter-multi-input > .clear-button:hover {
2467
+ @apply opacity-50;
2468
+ }
2469
+
2470
+ .filter-range {
2471
+ @apply flex flex-col relative;
2472
+ }
2473
+
2474
+ .filter-range-inputs {
2475
+ @apply flex items-center gap-2 mb-2;
2476
+ }
2477
+
2478
+ .filter-range-input-group {
2479
+ @apply flex flex-col flex-1;
2480
+ }
2481
+
2482
+ .filter-range-label {
2483
+ @apply text-xs font-medium text-gray-600 dark:text-gray-400 mb-1;
2484
+ }
2485
+
2486
+ .filter-range-separator {
2487
+ @apply text-gray-500 dark:text-gray-400 font-semibold mt-5;
2488
+ }
2489
+
2490
+ .filter-range-sliders {
2491
+ @apply relative mb-2;
2492
+ }
2493
+
2494
+ .filter-range-slider {
2495
+ @apply absolute w-full h-2 appearance-none bg-transparent pointer-events-none;
2496
+ }
2497
+
2498
+ .filter-range-slider::-webkit-slider-thumb {
2499
+ @apply appearance-none w-4 h-4 rounded-full bg-blue-500 cursor-pointer pointer-events-auto;
2500
+ }
2501
+
2502
+ .filter-range-slider::-moz-range-thumb {
2503
+ @apply w-4 h-4 rounded-full bg-blue-500 cursor-pointer pointer-events-auto border-0;
2504
+ }
2505
+
2506
+ .filter-range-slider::-webkit-slider-runnable-track {
2507
+ @apply w-full h-1 bg-gray-200 dark:bg-gray-700 rounded-full;
2508
+ }
2509
+
2510
+ .filter-range-slider::-moz-range-track {
2511
+ @apply w-full h-1 bg-gray-200 dark:bg-gray-700 rounded-full;
2512
+ }
2513
+
2514
+ .filter-range-slider-min::-webkit-slider-thumb {
2515
+ @apply bg-blue-500;
2516
+ z-index: 50;
2517
+ position: relative;
2518
+ top: -6px;
2519
+ }
2520
+
2521
+ .filter-range-slider-max::-webkit-slider-thumb {
2522
+ @apply bg-blue-600;
2523
+ z-index: 50;
2524
+ position: relative;
2525
+ right: 0;
2526
+ top: -6px;
2527
+ }
2528
+
2529
+ .filter-range-slider-min::-moz-range-thumb {
2530
+ @apply bg-blue-500;
2531
+ z-index: 50;
2532
+ position: relative;
2533
+ top: -6px;
2534
+ }
2535
+
2536
+ .filter-range-slider-max::-moz-range-thumb {
2537
+ @apply bg-blue-600;
2538
+ z-index: 50;
2539
+ position: relative;
2540
+ right: 0;
2541
+ top: -6px;
2542
+ }
2543
+
2544
+ .filter-range > .clear-button {
2545
+ @apply absolute top-0 right-0 dark:text-gray-500 text-gray-700 pr-1;
2546
+ }
2547
+
2548
+ .filter-range > .clear-button:hover {
2549
+ @apply opacity-50;
2550
+ }
2551
+
2331
2552
  .fleetbase-loader-wrapper {
2332
2553
  padding: 0;
2333
2554
  margin-left: 0;
@@ -2477,4 +2698,4 @@ body[data-theme='dark'] .app-version-in-nav {
2477
2698
  .details-wrapper > :not([hidden], .is-closed) ~ :not([hidden], .is-closed) {
2478
2699
  margin-top: calc(1rem * (1 - var(--tw-space-y-reverse)));
2479
2700
  margin-bottom: calc(1rem * var(--tw-space-y-reverse));
2480
- }
2701
+ }
@@ -0,0 +1,101 @@
1
+ export default function isImageFile(file) {
2
+ if (!file) return false;
3
+
4
+ // MIME (most reliable if provided)
5
+ const mime = (file.content_type || file.type || '').toLowerCase();
6
+ if (mime.startsWith('image/')) return true; // e.g., "image/png"
7
+ // Some services mislabel images as octet-stream; fall through if so.
8
+
9
+ // data: URL (base64 inline)
10
+ const url = file.url || file.path || '';
11
+ if (typeof url === 'string' && url.startsWith('data:image/')) return true;
12
+
13
+ // Extension check (strip query/fragment, pick last segment)
14
+ const name = file.original_filename || file.path || file.url || '';
15
+ const ext = getFileExtension(name);
16
+ if (ext) {
17
+ const IMAGE_EXTS = new Set(['jpg', 'jpeg', 'jfif', 'png', 'gif', 'bmp', 'tif', 'tiff', 'webp', 'svg', 'heic', 'heif', 'avif', 'ico', 'cur']);
18
+ if (IMAGE_EXTS.has(ext)) return true;
19
+ }
20
+
21
+ // Signature sniffing if you have bytes/base64
22
+ // This helps when MIME & ext are missing/misleading.
23
+ const bytes = file.bytes || file.arrayBuffer || file.buffer;
24
+ const base64 = file.base64;
25
+ if (bytes || base64) {
26
+ /* eslint-disable no-empty */
27
+ try {
28
+ const u8 = toUint8(bytes, base64);
29
+ if (u8) return looksLikeImageBytes(u8);
30
+ } catch (_) {}
31
+ }
32
+
33
+ return false;
34
+ }
35
+
36
+ export function getFileExtension(str) {
37
+ if (!str || typeof str !== 'string') return '';
38
+ // Fast path for data URLs
39
+ if (str.startsWith('data:image/')) return 'dataurl';
40
+ // Strip query/fragment
41
+ const clean = str.split('?')[0].split('#')[0];
42
+ // Handle full URLs safely if possible
43
+ try {
44
+ const u = new URL(clean);
45
+ str = u.pathname;
46
+ } catch (_) {
47
+ str = clean;
48
+ }
49
+ const last = str.split('/').pop() || '';
50
+ const dot = last.lastIndexOf('.');
51
+ if (dot <= 0 || dot === last.length - 1) return '';
52
+ return last.slice(dot + 1).toLowerCase();
53
+ }
54
+
55
+ // convert provided bytes/base64 into Uint8Array
56
+ export function toUint8(bytes, base64) {
57
+ if (bytes instanceof Uint8Array) return bytes;
58
+ if (bytes && bytes.byteLength != null) return new Uint8Array(bytes);
59
+ if (typeof base64 === 'string') {
60
+ if (base64.startsWith('data:')) base64 = base64.split(',')[1] || '';
61
+ const bin = atob(base64);
62
+ const u8 = new Uint8Array(bin.length);
63
+ for (let i = 0; i < bin.length; i++) u8[i] = bin.charCodeAt(i);
64
+ return u8;
65
+ }
66
+ return null;
67
+ }
68
+
69
+ // quick magic-number checks for common formats
70
+ export function looksLikeImageBytes(u8) {
71
+ if (u8.length < 12) return false;
72
+
73
+ // JPEG: FF D8 FF
74
+ if (u8[0] === 0xff && u8[1] === 0xd8 && u8[2] === 0xff) return true;
75
+
76
+ // PNG: 89 50 4E 47 0D 0A 1A 0A
77
+ if (u8[0] === 0x89 && u8[1] === 0x50 && u8[2] === 0x4e && u8[3] === 0x47 && u8[4] === 0x0d && u8[5] === 0x0a && u8[6] === 0x1a && u8[7] === 0x0a) return true;
78
+
79
+ // GIF: "GIF87a"/"GIF89a"
80
+ if (u8[0] === 0x47 && u8[1] === 0x49 && u8[2] === 0x46 && u8[3] === 0x38 && (u8[4] === 0x37 || u8[4] === 0x39) && u8[5] === 0x61) return true;
81
+
82
+ // WEBP: "RIFF" .... "WEBP"
83
+ if (u8[0] === 0x52 && u8[1] === 0x49 && u8[2] === 0x46 && u8[3] === 0x46 && u8[8] === 0x57 && u8[9] === 0x45 && u8[10] === 0x42 && u8[11] === 0x50) return true;
84
+
85
+ // BMP: "BM"
86
+ if (u8[0] === 0x42 && u8[1] === 0x4d) return true;
87
+
88
+ // TIFF: "II*" or "MM*"
89
+ if ((u8[0] === 0x49 && u8[1] === 0x49 && u8[2] === 0x2a && u8[3] === 0x00) || (u8[0] === 0x4d && u8[1] === 0x4d && u8[2] === 0x00 && u8[3] === 0x2a)) return true;
90
+
91
+ // HEIC/AVIF (ISOBMFF): "ftyp" box with known brands
92
+ // bytes 4..7: 'ftyp'
93
+ const isFtyp = u8[4] === 0x66 && u8[5] === 0x74 && u8[6] === 0x79 && u8[7] === 0x70;
94
+ if (isFtyp) {
95
+ const brand = String.fromCharCode(u8[8], u8[9], u8[10], u8[11]).toLowerCase();
96
+ if (['heic', 'heix', 'mif1', 'hevc', 'avif', 'avis'].includes(brand)) return true;
97
+ }
98
+
99
+ // SVG can’t be reliably sniffed with bytes (it’s XML). Rely on MIME/extension.
100
+ return false;
101
+ }
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/ember-ui/components/filter/multi-input';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/ember-ui/components/filter/range';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/ember-ui/components/modals/query-builder-computed-column-editor';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/ember-ui/components/query-builder/computed-columns';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/ember-ui/components/schedule-calendar';
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/ember-ui/utils/is-image-file';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fleetbase/ember-ui",
3
- "version": "0.3.9",
3
+ "version": "0.3.11",
4
4
  "description": "Fleetbase UI provides all the interface components, helpers, services and utilities for building a Fleetbase extension into the Console.",
5
5
  "keywords": [
6
6
  "fleetbase-ui",