@omnitend/dashboard-for-laravel 0.44.0 → 0.46.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.
Files changed (42) hide show
  1. package/dist/assets/IBMPlexSans-Bold-Latin1-914f1400.woff2 +0 -0
  2. package/dist/assets/IBMPlexSans-Medium-Latin1-b5610af0.woff2 +0 -0
  3. package/dist/assets/IBMPlexSans-Regular-Latin1-b5ad7bd3.woff2 +0 -0
  4. package/dist/assets/IBMPlexSans-SemiBold-Latin1-fff0ab3a.woff2 +0 -0
  5. package/dist/components/base/DFormFile.vue.d.ts +17 -0
  6. package/dist/components/base/DFormValidFeedback.vue.d.ts +12 -0
  7. package/dist/components/extended/DXDashboard.vue.d.ts +95 -111
  8. package/dist/components/extended/DXDashboardNavbar.vue.d.ts +2 -36
  9. package/dist/components/extended/DXDashboardSidebar.vue.d.ts +60 -79
  10. package/dist/components/extended/DXLoading.vue.d.ts +3 -0
  11. package/dist/components/extended/DXTable.vue.d.ts +10 -1
  12. package/dist/dashboard-for-laravel.js +9783 -9564
  13. package/dist/dashboard-for-laravel.js.map +1 -1
  14. package/dist/dashboard-for-laravel.umd.cjs +7 -7
  15. package/dist/dashboard-for-laravel.umd.cjs.map +1 -1
  16. package/dist/index.d.ts +3 -1
  17. package/dist/style.css +2 -2
  18. package/dist/types/navigation.d.ts +153 -3
  19. package/dist/utils/statCardFit.d.ts +19 -0
  20. package/docs/public/api-reference.json +50 -8
  21. package/docs/public/docs-map.md +4 -2
  22. package/docs/public/llms.txt +4 -2
  23. package/package.json +3 -2
  24. package/resources/css/theme.scss +384 -4
  25. package/resources/fonts/ibm-plex-sans/IBMPlexSans-Bold-Latin1.woff2 +0 -0
  26. package/resources/fonts/ibm-plex-sans/IBMPlexSans-Medium-Latin1.woff2 +0 -0
  27. package/resources/fonts/ibm-plex-sans/IBMPlexSans-Regular-Latin1.woff2 +0 -0
  28. package/resources/fonts/ibm-plex-sans/IBMPlexSans-SemiBold-Latin1.woff2 +0 -0
  29. package/resources/fonts/ibm-plex-sans/OFL.txt +93 -0
  30. package/resources/fonts/ibm-plex-sans/SOURCE.txt +18 -0
  31. package/resources/js/components/base/DFormFile.vue +103 -0
  32. package/resources/js/components/base/DFormValidFeedback.vue +16 -0
  33. package/resources/js/components/extended/DXDashboard.vue +41 -18
  34. package/resources/js/components/extended/DXDashboardNavbar.vue +9 -1
  35. package/resources/js/components/extended/DXDashboardSidebar.vue +25 -3
  36. package/resources/js/components/extended/DXField.vue +36 -8
  37. package/resources/js/components/extended/DXLoading.vue +18 -11
  38. package/resources/js/components/extended/DXStatCard.vue +154 -11
  39. package/resources/js/components/extended/DXTable.vue +60 -10
  40. package/resources/js/index.ts +9 -0
  41. package/resources/js/types/navigation.ts +175 -3
  42. package/resources/js/utils/statCardFit.ts +210 -0
@@ -53,8 +53,9 @@ $color-contrast-light: #fff; // Light text color (for dark backgrounds)
53
53
  // Two bundled typefaces (SIL OFL 1.1; woff2 + licence in resources/fonts/):
54
54
  // MAVEN PRO for body text, and POPPINS as the DISPLAY face, published as
55
55
  // `--dx-font-family-display`. The theme applies the display face to h1–h4 and
56
- // nothing else; consumers use the token for things like product names. Both
57
- // are declared with `@font-face` below, so they render on every machine, not
56
+ // nothing else; consumers use the token for things like product names. A
57
+ // third, IBM PLEX SANS, supplies DIGITS ONLY for tabular figures (#215; see
58
+ // "Tabular figures" below). All are declared with `@font-face` below, so they render on every machine, not
58
59
  // only where the font happens to be installed (the theme used to name Poppins
59
60
  // and ship no face for it, so most visitors got the generic sans-serif).
60
61
  $font-family-sans-serif: "Maven Pro", system-ui, -apple-system, "Segoe UI", Roboto,
@@ -62,6 +63,12 @@ $font-family-sans-serif: "Maven Pro", system-ui, -apple-system, "Segoe UI", Robo
62
63
  $dx-font-family-display: "Poppins", system-ui, -apple-system, "Segoe UI", Roboto,
63
64
  "Helvetica Neue", Arial, sans-serif;
64
65
  $headings-font-family: var(--dx-font-family-display);
66
+ // The face(s) tabular figures are set in, published as
67
+ // `--dx-font-family-figures`. Only the figures face goes here: the theme
68
+ // appends the surrounding face as the fallback (the body face, or the display
69
+ // face in h1–h4), which is where `£`, `,`, `.` and letters come from. See
70
+ // "Tabular figures" below.
71
+ $dx-font-family-figures: "DX Figures" !default;
65
72
  $font-size-base: 1rem; // 16px — the default tier. Legacy omnitend rendered body
66
73
  // and table text at 16px; the earlier 0.875rem (14px) shrank every ported page's
67
74
  // text ~12% while rem-fixed chrome stayed put, so controls read oversized and
@@ -128,7 +135,11 @@ $input-padding-y-lg: $btn-padding-y-lg;
128
135
  $input-border-radius: $border-radius;
129
136
  $input-border-color: #d1d5db; // Gray-300
130
137
  $input-focus-border-color: $primary;
131
- $input-focus-box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
138
+ // The focus halo is the brand navy, faint, outside the navy focus border. (It
139
+ // was a leftover indigo from before the navy brand.) Selects take the same
140
+ // ring; Bootstrap gives them their own 0.25rem one at 25%.
141
+ $input-focus-box-shadow: 0 0 0 3px rgba($primary, 0.1);
142
+ $form-select-focus-box-shadow: $input-focus-box-shadow;
132
143
 
133
144
  // Check/radio inputs default their border to `$border-color` (Gray-200), not
134
145
  // `$input-border-color` — so a bare checkbox looks faint beside the (darker,
@@ -441,6 +452,29 @@ $utilities-border-subtle: map.merge(
441
452
  ("pending-subtle": var(--bs-pending-border-subtle))
442
453
  );
443
454
 
455
+ // An invalid control's focus ring is the crimson form-error colour, like its
456
+ // border. Bootstrap builds it from `--bs-danger-rgb`, which is the magenta
457
+ // badge hue here. Its geometry and opacity stay Bootstrap's. The
458
+ // `.invalid-tooltip` background likewise (Bootstrap's is `var(--bs-danger)`):
459
+ // white on #c8102e is 5.9:1. A solid Sass colour, not
460
+ // `--bs-form-invalid-color`, which is a light pink in dark mode where the
461
+ // white text would fail. (The `.form-range` thumb's red is bootstrap-vue-next's
462
+ // own and is left alone.) The map only exists once Bootstrap's variables are
463
+ // read, hence here.
464
+ $form-validation-states: map.merge(
465
+ $form-validation-states,
466
+ (
467
+ "invalid": map.merge(
468
+ map.get($form-validation-states, "invalid"),
469
+ (
470
+ "focus-box-shadow": 0 0 $input-btn-focus-blur $input-focus-width
471
+ rgba($dx-form-error, $input-btn-focus-color-opacity),
472
+ "tooltip-bg-color": $dx-form-error,
473
+ )
474
+ ),
475
+ )
476
+ );
477
+
444
478
  @import "bootstrap/scss/bootstrap";
445
479
 
446
480
  // ----------------------------------------------------------------------------
@@ -506,8 +540,42 @@ $dx-bundled-fonts: (
506
540
  }
507
541
  }
508
542
 
543
+ // DX Figures: IBM Plex Sans's DIGITS, for tabular figures (#215). Maven Pro
544
+ // and Poppins have no `tnum` feature, so `tabular-nums` did nothing in them;
545
+ // Plex Sans's figures are tabular by default. `unicode-range` limits each face
546
+ // to U+0030–0039, so only the digits come from Plex and everything else in a
547
+ // tabular run falls through to the next family in the stack. `size-adjust`
548
+ // matches Plex's figure height (0.710em) to Maven Pro's (0.673em), so a digit
549
+ // sits beside Maven Pro text at the same size. 400 for body text and inputs,
550
+ // 500 for table headers and `.fw-medium`, 600 for `.fw-semibold` (without it
551
+ // font matching takes the 700 face for a 600 request), 700 for DXStatCard's
552
+ // value and `.fw-bold`. The files are IBM's own Latin1 split woff2s, byte-identical to
553
+ // the upstream package (resources/fonts/ibm-plex-sans/SOURCE.txt): "Plex" is a
554
+ // Reserved Font Name, so they must never be subset or re-encoded. Extracted to
555
+ // `dist/assets/` like the faces above, and fetched only where a tabular digit
556
+ // renders.
557
+ $dx-figures-fonts: (
558
+ (400, "ibm-plex-sans/IBMPlexSans-Regular-Latin1"),
559
+ (500, "ibm-plex-sans/IBMPlexSans-Medium-Latin1"),
560
+ (600, "ibm-plex-sans/IBMPlexSans-SemiBold-Latin1"),
561
+ (700, "ibm-plex-sans/IBMPlexSans-Bold-Latin1"),
562
+ );
563
+
564
+ @each $weight, $file in $dx-figures-fonts {
565
+ @font-face {
566
+ font-family: "DX Figures";
567
+ font-style: normal;
568
+ font-weight: $weight;
569
+ font-display: swap;
570
+ src: url("../fonts/#{$file}.woff2") format("woff2");
571
+ unicode-range: U+0030-0039;
572
+ size-adjust: 94.8%;
573
+ }
574
+ }
575
+
509
576
  :root {
510
577
  --dx-font-family-display: #{$dx-font-family-display};
578
+ --dx-font-family-figures: #{$dx-font-family-figures};
511
579
  }
512
580
 
513
581
  // Bootstrap gives `$headings-font-family` to h1–h6; the display face is for
@@ -733,6 +801,147 @@ $dx-display-only-row: '.dx-form--horizontal .row:has(> .col-form-label):not(:has
733
801
  flex-wrap: nowrap;
734
802
  }
735
803
 
804
+ // DFormFile: bvn's BFormFile draws its own control (`.b-form-file-control`, a
805
+ // div around a Browse button and the file-name text), not a `.form-control`,
806
+ // so none of the theme's input variables reach it. Give it the input's border
807
+ // colour, padding (so its height) and focus ring, and the theme's crimson invalid
808
+ // state (bvn hardcodes Bootstrap's red icon, and the navy focus border took
809
+ // over an invalid control). The state rings come from Bootstrap's own
810
+ // `$form-validation-states`, so they stay identical to `.form-control`'s. Global:
811
+ // the control is inside a bvn root (see CLAUDE.md, scope-id trap).
812
+ .b-form-file-control {
813
+ border-color: var(--d-input-border-color);
814
+
815
+ .b-form-file-text,
816
+ .b-form-file-button {
817
+ padding: $input-padding-y $input-padding-x;
818
+ }
819
+
820
+ &.form-control-sm .b-form-file-text,
821
+ &.form-control-sm .b-form-file-button {
822
+ padding: $input-padding-y-sm $input-padding-x-sm;
823
+ }
824
+
825
+ &.form-control-lg .b-form-file-text,
826
+ &.form-control-lg .b-form-file-button {
827
+ padding: $input-padding-y-lg $input-padding-x-lg;
828
+ }
829
+
830
+ .b-form-file-button {
831
+ border-right-color: var(--d-input-border-color);
832
+ }
833
+
834
+ &:focus-within:not([aria-disabled="true"]) {
835
+ border-color: $input-focus-border-color;
836
+ box-shadow: $input-focus-box-shadow;
837
+ }
838
+
839
+ &.is-invalid {
840
+ border-color: var(--bs-form-invalid-border-color);
841
+ background-image: escape-svg($form-feedback-icon-invalid);
842
+
843
+ &:focus-within:not([aria-disabled="true"]) {
844
+ border-color: var(--bs-form-invalid-border-color);
845
+ box-shadow: map-get(map-get($form-validation-states, "invalid"), "focus-box-shadow");
846
+ }
847
+ }
848
+
849
+ &.is-valid {
850
+ border-color: var(--bs-form-valid-border-color);
851
+ background-image: escape-svg($form-feedback-icon-valid);
852
+
853
+ &:focus-within:not([aria-disabled="true"]) {
854
+ border-color: var(--bs-form-valid-border-color);
855
+ box-shadow: map-get(map-get($form-validation-states, "valid"), "focus-box-shadow");
856
+ }
857
+ }
858
+ }
859
+
860
+ // A disabled fieldset (any disabled DFormGroup: it always renders one) disables
861
+ // the button and the native input but not bvn's `disabled` prop, so give the
862
+ // control the same look as `disabled` (bvn's `[aria-disabled=true]` rule).
863
+ fieldset:disabled .b-form-file-control {
864
+ background-color: var(--bs-secondary-bg);
865
+ opacity: 0.65;
866
+ cursor: not-allowed;
867
+
868
+ .b-form-file-text,
869
+ .b-form-file-button {
870
+ pointer-events: none;
871
+ }
872
+ }
873
+
874
+ // The state class sits on `.b-form-file-control` (or the plain input), deep
875
+ // inside `.b-form-file-root`, where Bootstrap's `.is-invalid ~ .invalid-feedback`
876
+ // cannot see it; a sibling DFormInvalidFeedback/DFormValidFeedback stayed hidden.
877
+ // `:has()` covers the custom control and `plain` without touching attrs.
878
+ .b-form-file-root:has(> .b-form-file-wrapper > .b-form-file-control.is-invalid, > input.is-invalid) {
879
+ ~ .invalid-feedback,
880
+ ~ .invalid-tooltip {
881
+ display: block;
882
+ }
883
+ }
884
+
885
+ .b-form-file-root:has(> .b-form-file-wrapper > .b-form-file-control.is-valid, > input.is-valid) {
886
+ ~ .valid-feedback,
887
+ ~ .valid-tooltip {
888
+ display: block;
889
+ }
890
+ }
891
+
892
+ // In a DInputGroup Bootstrap squares the corners of the radius-less root, not
893
+ // the control inside it, so the control kept a rounded edge against the addon.
894
+ .input-group > .b-form-file-root {
895
+ position: relative;
896
+
897
+ &:focus-within {
898
+ z-index: 5;
899
+ }
900
+
901
+ &:not(:first-child) {
902
+ .b-form-file-control,
903
+ .b-form-file-button {
904
+ border-top-left-radius: 0;
905
+ border-bottom-left-radius: 0;
906
+ }
907
+ }
908
+ }
909
+
910
+ .input-group:not(.has-validation) > .b-form-file-root:not(:last-child),
911
+ .input-group.has-validation > .b-form-file-root:nth-last-child(n + 3) {
912
+ .b-form-file-control {
913
+ border-top-right-radius: 0;
914
+ border-bottom-right-radius: 0;
915
+ }
916
+ }
917
+
918
+ // Drag state. bvn colours the overlay text, its dashed border and the control's
919
+ // border with `--bs-primary` (the brand navy), which is about 1.07:1 on the
920
+ // dark-mode overlay, so the accent is a token that becomes the emphasis colour
921
+ // in dark mode. The overlay's white is hardcoded in bvn; follow the page
922
+ // background.
923
+ :root,
924
+ [data-bs-theme="light"] {
925
+ --dx-form-file-drag-color: var(--bs-primary);
926
+ }
927
+
928
+ [data-bs-theme="dark"] {
929
+ --dx-form-file-drag-color: var(--bs-emphasis-color);
930
+ }
931
+
932
+ .b-form-file-dragging .b-form-file-control {
933
+ border-color: var(--dx-form-file-drag-color);
934
+ }
935
+
936
+ .b-form-file-drag-overlay {
937
+ background-color: rgba(var(--bs-body-bg-rgb), 0.95);
938
+ border-color: var(--dx-form-file-drag-color);
939
+ }
940
+
941
+ .b-form-file-drag-text {
942
+ color: var(--dx-form-file-drag-color);
943
+ }
944
+
736
945
  // DXNumberStepper: `[ − ][ 12 ][ + ]`. The buttons are DButton icon-only
737
946
  // squares, sized by the input group. The number is centred between them, and
738
947
  // the browser's spin arrows are hidden (they duplicate the buttons and push the
@@ -1388,6 +1597,53 @@ aside.dashboard-sidebar--light {
1388
1597
  // wrapper's DIRECT child table is affected, so a table nested in a cell keeps
1389
1598
  // its own layout.
1390
1599
 
1600
+ // ----------------------------------------------------------------------------
1601
+ // Tabular figures (#215)
1602
+ // ----------------------------------------------------------------------------
1603
+ //
1604
+ // Every digit on the same advance, so amounts in a column (or a total that
1605
+ // updates in place) line up and do not jitter. Global, so consumer markup and
1606
+ // slot content can use it; DXTable's `tabular: true` field option adds it to
1607
+ // a column's cells, and DXStatCard's value and DXCurrencyInput's amount use
1608
+ // tabular figures by default.
1609
+ //
1610
+ // Maven Pro and Poppins have no `tnum` feature, so the digits come from the
1611
+ // `--dx-font-family-figures` token (DX Figures, IBM Plex Sans digits, above),
1612
+ // with the surrounding face as the fallback for everything else. `tabular-nums`
1613
+ // stays, so a consumer who points the token at a face with a `tnum` feature
1614
+ // (Inter, IBM Plex Mono) still gets tabular figures from it. Setting the token
1615
+ // to `initial` turns the face off: the declaration becomes invalid and the
1616
+ // element inherits its parent's family.
1617
+ //
1618
+ // Heading trap: h1–h4 are in the display face, and `.dx-tabular` outranks
1619
+ // `h1`, so in or on a heading the fallback must be the display face too, or
1620
+ // the heading's letters would turn into Maven Pro. The second rule does that.
1621
+ // Elsewhere the fallback is the body face: CSS cannot say "the inherited
1622
+ // face" inside a font list, so a `.dx-tabular` inside some other display-face
1623
+ // element (the sidebar) gets body-face letters. The same trap in or on
1624
+ // `code`, `kbd`, `samp` and `pre`: their fallback is the monospace face, so a
1625
+ // code sample keeps monospace letters (the third rule, which comes last so
1626
+ // code inside a heading stays monospace). Both context rules use `:where()`
1627
+ // so each has exactly `.dx-tabular`'s specificity and wins by source order.
1628
+ // Pinned by tabular-figures.test.ts and theme-fonts.test.ts.
1629
+ $dx-headings-1-4: "h1, h2, h3, h4, .h1, .h2, .h3, .h4";
1630
+ $dx-monospace-elements: "code, kbd, samp, pre";
1631
+
1632
+ .dx-tabular {
1633
+ font-family: var(--dx-font-family-figures), var(--bs-body-font-family);
1634
+ font-variant-numeric: tabular-nums;
1635
+ }
1636
+
1637
+ :where(#{$dx-headings-1-4}).dx-tabular,
1638
+ :where(#{$dx-headings-1-4}) .dx-tabular {
1639
+ font-family: var(--dx-font-family-figures), var(--dx-font-family-display);
1640
+ }
1641
+
1642
+ :where(#{$dx-monospace-elements}).dx-tabular,
1643
+ :where(#{$dx-monospace-elements}) .dx-tabular {
1644
+ font-family: var(--dx-font-family-figures), var(--bs-font-monospace);
1645
+ }
1646
+
1391
1647
  // ----------------------------------------------------------------------------
1392
1648
  // DXCurrencyInput — keep the amount usable in a narrow container
1393
1649
  // ----------------------------------------------------------------------------
@@ -1396,10 +1652,14 @@ aside.dashboard-sidebar--light {
1396
1652
  // ("10L Bag in Box") crushed the amount to ~32px. The amount keeps room for a
1397
1653
  // price, the group still does not wrap (a wrapped affix would land on its own line
1398
1654
  // with broken corners: the theme already makes every `.input-group` nowrap),
1399
- // and the affix text wraps onto two lines instead.
1655
+ // and the affix text wraps onto two lines instead. The amount uses tabular
1656
+ // figures (#215), like every money value the library renders (the figures
1657
+ // face plus `tabular-nums`, as `.dx-tabular` above).
1400
1658
  .dx-currency-input {
1401
1659
  > .form-control {
1402
1660
  min-width: 5.5rem;
1661
+ font-family: var(--dx-font-family-figures), var(--bs-body-font-family);
1662
+ font-variant-numeric: tabular-nums;
1403
1663
  }
1404
1664
 
1405
1665
  > .input-group-text {
@@ -1992,3 +2252,123 @@ $dx-chart-edge-dark: $body-bg-dark;
1992
2252
  thead tr:has(.dx-table-filter-cell) > td {
1993
2253
  padding: 0.5rem;
1994
2254
  }
2255
+
2256
+ // ————————————————— DXLoading (the soft loader)
2257
+ //
2258
+ // Modelled on Omni Tend's legacy loader: a small grey spinner beside the
2259
+ // text on one line, the text in the display face with a slow light sweep
2260
+ // across it, fading in after DXLoading's delay. Quieter than a navy spinner,
2261
+ // but the text keeps AA contrast (#6c757d, 4.69:1 on white; the legacy
2262
+ // gray-300 was 1.5:1). The sweep passes through a lighter grey, so a moment
2263
+ // of the text sits below AA while it moves. Global, not scoped: DXTable and
2264
+ // consumers render it, and its spinner is a bvn component.
2265
+ //
2266
+ // The `role="status"` box renders at once with its contents hidden, and
2267
+ // `.dx-loading--shown` reveals them after the delay. Two reasons: a live
2268
+ // region that is already in the page announces content added to it (one
2269
+ // inserted together with its text can go unannounced), and the space the
2270
+ // loader will take is held from the start, so nothing jumps when it appears.
2271
+ @mixin dx-loading-light-vars {
2272
+ --dx-loading-color: #{$gray-600};
2273
+ --dx-loading-sweep-color: #{$gray-500};
2274
+ --dx-loading-spinner-color: #{$gray-500};
2275
+ }
2276
+
2277
+ // `[data-bs-theme="light"]` too, so a light section inside a dark page gets
2278
+ // the light set back (as the chart tokens do).
2279
+ :root,
2280
+ [data-bs-theme="light"] {
2281
+ @include dx-loading-light-vars;
2282
+ // Slower than Bootstrap's 0.75 s a turn: a calmer spin to match the soft
2283
+ // look (1.1 s chosen against 1.5 s and 2 s side by side).
2284
+ --dx-loading-spinner-speed: 1.1s;
2285
+ }
2286
+
2287
+ [data-bs-theme="dark"] {
2288
+ --dx-loading-color: #{$gray-500};
2289
+ --dx-loading-sweep-color: #{$gray-600};
2290
+ --dx-loading-spinner-color: #{$gray-600};
2291
+ }
2292
+
2293
+ .dx-loading {
2294
+ display: flex;
2295
+ justify-content: center;
2296
+ align-items: center;
2297
+ gap: 1rem;
2298
+ color: var(--dx-loading-color);
2299
+
2300
+ // Hidden, not removed: `visibility` keeps the box's size and keeps the
2301
+ // contents out of the accessibility tree until they are revealed.
2302
+ > * {
2303
+ visibility: hidden;
2304
+ }
2305
+
2306
+ // Set on the spinner itself: Bootstrap declares its speed on
2307
+ // `.spinner-border`, so a value inherited from `.dx-loading` would lose.
2308
+ .spinner-border {
2309
+ flex: none;
2310
+ color: var(--dx-loading-spinner-color);
2311
+ --bs-spinner-animation-speed: var(--dx-loading-spinner-speed);
2312
+ }
2313
+ }
2314
+
2315
+ .dx-loading--shown {
2316
+ animation: dx-loading-fade-in 0.4s ease-out both;
2317
+
2318
+ > * {
2319
+ visibility: visible;
2320
+ }
2321
+ }
2322
+
2323
+ .dx-loading__text {
2324
+ margin: 0;
2325
+ font-family: var(--dx-font-family-display);
2326
+ font-weight: 500;
2327
+ font-size: $h2-font-size;
2328
+ line-height: $headings-line-height;
2329
+ background-image: linear-gradient(
2330
+ to right,
2331
+ var(--dx-loading-color) 20%,
2332
+ var(--dx-loading-color) 40%,
2333
+ var(--dx-loading-sweep-color) 60%,
2334
+ var(--dx-loading-sweep-color) 80%,
2335
+ var(--dx-loading-color) 99%
2336
+ );
2337
+ background-size: 200% auto;
2338
+ -webkit-background-clip: text;
2339
+ background-clip: text;
2340
+ -webkit-text-fill-color: transparent;
2341
+ animation: dx-loading-sweep 3s linear infinite;
2342
+ }
2343
+
2344
+ @keyframes dx-loading-fade-in {
2345
+ from {
2346
+ opacity: 0;
2347
+ }
2348
+ }
2349
+
2350
+ @keyframes dx-loading-sweep {
2351
+ to {
2352
+ background-position: 200% center;
2353
+ }
2354
+ }
2355
+
2356
+ // For people who asked for less motion: no sweep and no fade (the text is
2357
+ // plain grey and appears at once), and the spinner slows to double its turn,
2358
+ // as Bootstrap's own reduced-motion rule does for its spinners. That rule
2359
+ // cannot reach this one, because the speed above out-ranks it.
2360
+ @media (prefers-reduced-motion: reduce) {
2361
+ .dx-loading--shown,
2362
+ .dx-loading__text {
2363
+ animation: none;
2364
+ }
2365
+
2366
+ .dx-loading__text {
2367
+ background-image: none;
2368
+ -webkit-text-fill-color: currentColor;
2369
+ }
2370
+
2371
+ .dx-loading .spinner-border {
2372
+ --bs-spinner-animation-speed: calc(2 * var(--dx-loading-spinner-speed));
2373
+ }
2374
+ }
@@ -0,0 +1,93 @@
1
+ Copyright © 2017 IBM Corp. with Reserved Font Name "Plex"
2
+
3
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
+
5
+ This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
6
+
7
+
8
+ -----------------------------------------------------------
9
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10
+ -----------------------------------------------------------
11
+
12
+ PREAMBLE
13
+ The goals of the Open Font License (OFL) are to stimulate worldwide
14
+ development of collaborative font projects, to support the font creation
15
+ efforts of academic and linguistic communities, and to provide a free and
16
+ open framework in which fonts may be shared and improved in partnership
17
+ with others.
18
+
19
+ The OFL allows the licensed fonts to be used, studied, modified and
20
+ redistributed freely as long as they are not sold by themselves. The
21
+ fonts, including any derivative works, can be bundled, embedded,
22
+ redistributed and/or sold with any software provided that any reserved
23
+ names are not used by derivative works. The fonts and derivatives,
24
+ however, cannot be released under any other type of license. The
25
+ requirement for fonts to remain under this license does not apply
26
+ to any document created using the fonts or their derivatives.
27
+
28
+ DEFINITIONS
29
+ "Font Software" refers to the set of files released by the Copyright
30
+ Holder(s) under this license and clearly marked as such. This may
31
+ include source files, build scripts and documentation.
32
+
33
+ "Reserved Font Name" refers to any names specified as such after the
34
+ copyright statement(s).
35
+
36
+ "Original Version" refers to the collection of Font Software components as
37
+ distributed by the Copyright Holder(s).
38
+
39
+ "Modified Version" refers to any derivative made by adding to, deleting,
40
+ or substituting -- in part or in whole -- any of the components of the
41
+ Original Version, by changing formats or by porting the Font Software to a
42
+ new environment.
43
+
44
+ "Author" refers to any designer, engineer, programmer, technical
45
+ writer or other person who contributed to the Font Software.
46
+
47
+ PERMISSION & CONDITIONS
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
50
+ redistribute, and sell modified and unmodified copies of the Font
51
+ Software, subject to the following conditions:
52
+
53
+ 1) Neither the Font Software nor any of its individual components,
54
+ in Original or Modified Versions, may be sold by itself.
55
+
56
+ 2) Original or Modified Versions of the Font Software may be bundled,
57
+ redistributed and/or sold with any software, provided that each copy
58
+ contains the above copyright notice and this license. These can be
59
+ included either as stand-alone text files, human-readable headers or
60
+ in the appropriate machine-readable metadata fields within text or
61
+ binary files as long as those fields can be easily viewed by the user.
62
+
63
+ 3) No Modified Version of the Font Software may use the Reserved Font
64
+ Name(s) unless explicit written permission is granted by the corresponding
65
+ Copyright Holder. This restriction only applies to the primary font name as
66
+ presented to the users.
67
+
68
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69
+ Software shall not be used to promote, endorse or advertise any
70
+ Modified Version, except to acknowledge the contribution(s) of the
71
+ Copyright Holder(s) and the Author(s) or with their explicit written
72
+ permission.
73
+
74
+ 5) The Font Software, modified or unmodified, in part or in whole,
75
+ must be distributed entirely under this license, and must not be
76
+ distributed under any other license. The requirement for fonts to
77
+ remain under this license does not apply to any document created
78
+ using the Font Software.
79
+
80
+ TERMINATION
81
+ This license becomes null and void if any of the above conditions are
82
+ not met.
83
+
84
+ DISCLAIMER
85
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93
+ OTHER DEALINGS IN THE FONT SOFTWARE.
@@ -0,0 +1,18 @@
1
+ IBM Plex Sans, digits only, for the theme's "DX Figures" face (#215).
2
+
3
+ These are IBM's official Latin1 split woff2 files, copied unmodified from the
4
+ npm package @ibm/plex-sans 1.1.0 (fonts/split/woff2/), the upstream of
5
+ https://github.com/IBM/plex. The theme limits them to U+0030-0039 with
6
+ unicode-range; the files themselves are whole.
7
+
8
+ "Plex" is a Reserved Font Name under the SIL Open Font License (OFL.txt,
9
+ the package's LICENSE.txt, unmodified). A modified copy could not keep the
10
+ name, so these files must never be subset, re-encoded or otherwise changed.
11
+ To update them, replace them with newer upstream files and record the new
12
+ version and checksums here and in tests/bundle/theme-fonts.test.ts.
13
+
14
+ sha256:
15
+ b5ad7bd39f996144915f0ad9849a90183b27d8c28ad97ed98af5b1bebc51f6b1 IBMPlexSans-Regular-Latin1.woff2
16
+ b5610af04d0d4b5a14a621d96d974b993e945a065db1a8861918f69ef9321934 IBMPlexSans-Medium-Latin1.woff2
17
+ fff0ab3a88b0b4aa0b693e4f0201359a15183b08e3fa5696d1918d8f0ade8ad5 IBMPlexSans-SemiBold-Latin1.woff2
18
+ 914f1400f363e636b6f9cc7965aa807ff01e93586e1437617525cba0a62aa78d IBMPlexSans-Bold-Latin1.woff2
@@ -0,0 +1,103 @@
1
+ /**
2
+ * @component
3
+ * Type-safe wrapper around Bootstrap Vue Next BFormFile component.
4
+ * File picker with a browse button, file-name display and drag-and-drop
5
+ * zone. `v-model` is a `File` (single), `File[]` (`multiple`/`directory`)
6
+ * or `null`; setting it to `null` (or `[]`) also clears the native input.
7
+ * A disabled control (the prop, or a disabled fieldset) ignores dropped
8
+ * files. Slots: `label`, `placeholder`, `file-name` (`{ files, names }`)
9
+ * and `drop-placeholder`. Exposes BFormFile's `focus`, `blur` and `reset`.
10
+ */
11
+ <script setup lang="ts">
12
+ import { onBeforeUnmount, onMounted, ref, watch } from "vue";
13
+ import { BFormFile } from "bootstrap-vue-next";
14
+
15
+ defineOptions({
16
+ inheritAttrs: false,
17
+ });
18
+
19
+ const bFormFileRef = ref<InstanceType<typeof BFormFile> | null>(null);
20
+
21
+ const rootElement = (): HTMLElement | null => {
22
+ const element = bFormFileRef.value?.$el as unknown;
23
+ return element instanceof HTMLElement ? element : null;
24
+ };
25
+
26
+ const nativeInput = (): HTMLInputElement | null =>
27
+ rootElement()?.querySelector<HTMLInputElement>('input[type="file"]') ?? null;
28
+
29
+ // bvn's drop handler checks only `noDrop`, so a disabled control still took
30
+ // a dropped file. The native input is :disabled both for the `disabled` prop
31
+ // and inside a disabled fieldset (which the attrs cannot see), so capture
32
+ // the drag events on BFormFile's root (useDropZone listens on the drop zone
33
+ // inside it) and stop them while the input is :disabled. dragleave too, or
34
+ // useDropZone's enter/leave counter drifts and the overlay sticks once the
35
+ // control is enabled again. Divergence from bvn: DIVERGENCES.md.
36
+ const blockedDragEvents = ["dragenter", "dragover", "dragleave", "drop"] as const;
37
+ let listeningRoot: HTMLElement | null = null;
38
+
39
+ const stopDragWhileDisabled = (event: Event) => {
40
+ if (nativeInput()?.matches(":disabled")) {
41
+ event.stopPropagation();
42
+ // Without this the browser opens a dropped file in the tab.
43
+ event.preventDefault();
44
+ }
45
+ };
46
+
47
+ onMounted(() => {
48
+ listeningRoot = rootElement();
49
+ blockedDragEvents.forEach((type) =>
50
+ listeningRoot?.addEventListener(type, stopDragWhileDisabled, { capture: true })
51
+ );
52
+ });
53
+
54
+ onBeforeUnmount(() => {
55
+ blockedDragEvents.forEach((type) =>
56
+ listeningRoot?.removeEventListener(type, stopDragWhileDisabled, { capture: true })
57
+ );
58
+ listeningRoot = null;
59
+ });
60
+
61
+ // bvn clears its own file list when the model becomes null or [], but in the
62
+ // custom control it leaves the hidden native input holding the files (and
63
+ // `plain` keeps them on []), so a plain form submit (FormData) still posted
64
+ // them. Clear the native input directly: bvn's reset() would also set the
65
+ // model to null, echoing an extra update:modelValue to the parent.
66
+ // Read from BFormFile's own props: they are reactive and normalised
67
+ // (`modelValue` whether the parent wrote `v-model` or `:model-value`).
68
+ // Divergence from bvn: DIVERGENCES.md.
69
+ const isCleared = (value: unknown) =>
70
+ value === null || value === undefined || (Array.isArray(value) && value.length === 0);
71
+
72
+ watch(
73
+ () => bFormFileRef.value?.$props.modelValue,
74
+ (modelValue, previousModelValue) => {
75
+ if (isCleared(modelValue) && !isCleared(previousModelValue)) {
76
+ const input = nativeInput();
77
+ if (input) {
78
+ input.value = "";
79
+ }
80
+ }
81
+ }
82
+ );
83
+
84
+ // A template ref on the wrapper would otherwise stop at the wrapper, so
85
+ // `reset()` (the only way to clear the native input's selection) is passed on.
86
+ defineExpose({
87
+ focus: () => bFormFileRef.value?.focus(),
88
+ blur: () => bFormFileRef.value?.blur(),
89
+ reset: () => bFormFileRef.value?.reset(),
90
+ get element() {
91
+ return bFormFileRef.value?.element ?? null;
92
+ },
93
+ });
94
+ </script>
95
+
96
+ <template>
97
+ <BFormFile ref="bFormFileRef" v-bind="$attrs">
98
+ <!-- Dynamically pass through all named slots with their props -->
99
+ <template v-for="(_, name) in $slots" :key="name" #[name]="slotProps">
100
+ <slot :name="name" v-bind="slotProps" />
101
+ </template>
102
+ </BFormFile>
103
+ </template>