@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.
- package/dist/assets/IBMPlexSans-Bold-Latin1-914f1400.woff2 +0 -0
- package/dist/assets/IBMPlexSans-Medium-Latin1-b5610af0.woff2 +0 -0
- package/dist/assets/IBMPlexSans-Regular-Latin1-b5ad7bd3.woff2 +0 -0
- package/dist/assets/IBMPlexSans-SemiBold-Latin1-fff0ab3a.woff2 +0 -0
- package/dist/components/base/DFormFile.vue.d.ts +17 -0
- package/dist/components/base/DFormValidFeedback.vue.d.ts +12 -0
- package/dist/components/extended/DXDashboard.vue.d.ts +95 -111
- package/dist/components/extended/DXDashboardNavbar.vue.d.ts +2 -36
- package/dist/components/extended/DXDashboardSidebar.vue.d.ts +60 -79
- package/dist/components/extended/DXLoading.vue.d.ts +3 -0
- package/dist/components/extended/DXTable.vue.d.ts +10 -1
- package/dist/dashboard-for-laravel.js +9783 -9564
- package/dist/dashboard-for-laravel.js.map +1 -1
- package/dist/dashboard-for-laravel.umd.cjs +7 -7
- package/dist/dashboard-for-laravel.umd.cjs.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/style.css +2 -2
- package/dist/types/navigation.d.ts +153 -3
- package/dist/utils/statCardFit.d.ts +19 -0
- package/docs/public/api-reference.json +50 -8
- package/docs/public/docs-map.md +4 -2
- package/docs/public/llms.txt +4 -2
- package/package.json +3 -2
- package/resources/css/theme.scss +384 -4
- package/resources/fonts/ibm-plex-sans/IBMPlexSans-Bold-Latin1.woff2 +0 -0
- package/resources/fonts/ibm-plex-sans/IBMPlexSans-Medium-Latin1.woff2 +0 -0
- package/resources/fonts/ibm-plex-sans/IBMPlexSans-Regular-Latin1.woff2 +0 -0
- package/resources/fonts/ibm-plex-sans/IBMPlexSans-SemiBold-Latin1.woff2 +0 -0
- package/resources/fonts/ibm-plex-sans/OFL.txt +93 -0
- package/resources/fonts/ibm-plex-sans/SOURCE.txt +18 -0
- package/resources/js/components/base/DFormFile.vue +103 -0
- package/resources/js/components/base/DFormValidFeedback.vue +16 -0
- package/resources/js/components/extended/DXDashboard.vue +41 -18
- package/resources/js/components/extended/DXDashboardNavbar.vue +9 -1
- package/resources/js/components/extended/DXDashboardSidebar.vue +25 -3
- package/resources/js/components/extended/DXField.vue +36 -8
- package/resources/js/components/extended/DXLoading.vue +18 -11
- package/resources/js/components/extended/DXStatCard.vue +154 -11
- package/resources/js/components/extended/DXTable.vue +60 -10
- package/resources/js/index.ts +9 -0
- package/resources/js/types/navigation.ts +175 -3
- package/resources/js/utils/statCardFit.ts +210 -0
package/resources/css/theme.scss
CHANGED
|
@@ -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.
|
|
57
|
-
//
|
|
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
|
-
|
|
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
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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>
|