@7365admin1/layer-common 3.2.2-staging.141 → 3.2.2-staging.143
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/assets/css/screens.css
CHANGED
|
@@ -807,3 +807,170 @@
|
|
|
807
807
|
.screen-back:hover {
|
|
808
808
|
color: var(--accent);
|
|
809
809
|
}
|
|
810
|
+
|
|
811
|
+
/* ------------------------------------------------------------------ */
|
|
812
|
+
/* 9. The row kebab and the dropdown it opens. */
|
|
813
|
+
/* */
|
|
814
|
+
/* Both halves are LITERAL, and both were previously left as Vuetify */
|
|
815
|
+
/* draws them - `Dialog/UpdateMoreAction.vue` says so in a comment, */
|
|
816
|
+
/* because a menu teleports out of the measured container and could */
|
|
817
|
+
/* not be render-verified at the time. It can now (force-shot). */
|
|
818
|
+
/* */
|
|
819
|
+
/* The kebab: `iService365 Redesign.dc.html:494`, drawn identically at */
|
|
820
|
+
/* 494, 547, 625 and 660 - a 30px bordered square, 8px radius, */
|
|
821
|
+
/* transparent fill, `--muted` ink, 17px glyph, `--hover` on hover. */
|
|
822
|
+
/* 8px is not one of the radius tokens (the set is 7/9/10/12/14/16), */
|
|
823
|
+
/* so it is written literally rather than snapped to a near token. */
|
|
824
|
+
/* */
|
|
825
|
+
/* The panel: `iService365 Redesign.dc.html:79-90`, the profile */
|
|
826
|
+
/* dropdown - the only dropdown the handoff draws. 240px, `--card` on */
|
|
827
|
+
/* a 1px `--border`, 14px radius, `--shadow-menu`, 8px padding; rows */
|
|
828
|
+
/* are 13px/700 `--text2` at 10px 12px with a 10px radius, and a */
|
|
829
|
+
/* destructive row is `--err` ink with an `--err-bg` hover (line 89), */
|
|
830
|
+
/* which is ink and not a filled button. */
|
|
831
|
+
/* ------------------------------------------------------------------ */
|
|
832
|
+
.v-btn.screen-kebab {
|
|
833
|
+
--v-btn-height: 30px;
|
|
834
|
+
height: 30px;
|
|
835
|
+
width: 30px;
|
|
836
|
+
min-width: 30px;
|
|
837
|
+
padding: 0;
|
|
838
|
+
border: 1px solid var(--border);
|
|
839
|
+
border-radius: 8px;
|
|
840
|
+
background: transparent;
|
|
841
|
+
color: var(--muted);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
.screen-kebab .v-btn__overlay {
|
|
845
|
+
opacity: 0;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
.screen-kebab:hover {
|
|
849
|
+
background: var(--hover);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
.screen-kebab .v-icon {
|
|
853
|
+
font-size: 17px;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
.v-overlay__content > .v-list.screen-menu {
|
|
857
|
+
width: 240px;
|
|
858
|
+
padding: 8px;
|
|
859
|
+
border: 1px solid var(--border);
|
|
860
|
+
border-radius: var(--r-menu);
|
|
861
|
+
background: var(--card);
|
|
862
|
+
box-shadow: var(--shadow-menu);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/* Four classes, to beat Vuetify's own `.v-list .v-list-item--density-*` */
|
|
866
|
+
/* block on specificity rather than on load order - a three-class rule ties */
|
|
867
|
+
/* with it and loses, which is how the drawer's 10px gap never applied. */
|
|
868
|
+
.v-overlay__content > .v-list.screen-menu > .screen-menu__item.v-list-item {
|
|
869
|
+
min-height: 0;
|
|
870
|
+
padding: 10px 12px;
|
|
871
|
+
/* MEASURED, not assumed: without `!important` this read back 0px. Vuetify
|
|
872
|
+
puts the `rounded-0` UTILITY on every `v-list-item` inside a list with no
|
|
873
|
+
`rounded` prop, and utilities carry `!important`, so a four-class rule
|
|
874
|
+
still loses to it. Same trap `NavigationItem.vue` hit in batch Q. */
|
|
875
|
+
border-radius: var(--r-inner) !important;
|
|
876
|
+
color: var(--text2);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
.screen-menu__item .v-list-item-title {
|
|
880
|
+
font-family: var(--font-sans);
|
|
881
|
+
font-size: var(--fs-nav);
|
|
882
|
+
font-weight: var(--fw-btn);
|
|
883
|
+
letter-spacing: 0;
|
|
884
|
+
line-height: 1.3;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
.screen-menu__item .v-list-item__prepend > .v-icon {
|
|
888
|
+
font-size: 18px;
|
|
889
|
+
color: var(--muted);
|
|
890
|
+
opacity: 1;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/* The design's row gap is 10px; Vuetify's spacer is wider, and the selector */
|
|
894
|
+
/* below matches its own four-class shape for the same reason as above. */
|
|
895
|
+
.screen-menu__item .v-list-item__prepend > .v-icon ~ .v-list-item__spacer {
|
|
896
|
+
width: 10px;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
.screen-menu__item:hover > .v-list-item__overlay {
|
|
900
|
+
opacity: 0;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
.screen-menu__item:hover {
|
|
904
|
+
background: var(--hover);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
.v-overlay__content > .v-list.screen-menu > .screen-menu__item--danger.v-list-item {
|
|
908
|
+
color: var(--err);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
.screen-menu__item--danger .v-list-item__prepend > .v-icon {
|
|
912
|
+
color: var(--err);
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
.screen-menu__item--danger:hover {
|
|
916
|
+
background: var(--err-bg);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/* ------------------------------------------------------------------ */
|
|
920
|
+
/* SIGNATURE PAD - `SignaturePad.vue`. */
|
|
921
|
+
/* */
|
|
922
|
+
/* DESIGN GAP, STATED: the handoff draws no signature pad and contains */
|
|
923
|
+
/* no dashed border anywhere, so everything below is the established */
|
|
924
|
+
/* primitives and tokens, not a handoff literal. The one value taken */
|
|
925
|
+
/* from the handoff is the caption pair at line 659 - 12.5px / 600. */
|
|
926
|
+
/* */
|
|
927
|
+
/* THE PAD IS PAPER AND STAYS LIGHT IN BOTH THEMES, DELIBERATELY. */
|
|
928
|
+
/* The canvas ink is `#111` and `stop()` exports the drawing as a PNG */
|
|
929
|
+
/* that callers render back on light surfaces and print. A `--card` */
|
|
930
|
+
/* pad would be dark in the dark theme, so the signature would be */
|
|
931
|
+
/* black-on-black on the pad AND the exported PNG would be a black */
|
|
932
|
+
/* scrawl on transparency. The three literals here are the LIGHT theme */
|
|
933
|
+
/* values of `--card`, `--border` and `--muted`, named so nobody has */
|
|
934
|
+
/* to guess where they came from. */
|
|
935
|
+
/* ------------------------------------------------------------------ */
|
|
936
|
+
.screen-signpad {
|
|
937
|
+
position: relative;
|
|
938
|
+
overflow: hidden;
|
|
939
|
+
/* Light `--muted` at 55% - NOT `--border`. A drop zone's dashed edge is an
|
|
940
|
+
affordance and has to be seen; `--border` (rgb 232,232,225) on a white pad
|
|
941
|
+
is all but invisible, which the first render showed. This lands on the same
|
|
942
|
+
weight as the `#bbb` it replaces, from a token value rather than a hex. */
|
|
943
|
+
border: 1.5px dashed rgba(139, 143, 152, 0.55);
|
|
944
|
+
border-radius: var(--r-inner);
|
|
945
|
+
background: rgb(255, 255, 255); /* light `--card` */
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
.screen-signpad__canvas {
|
|
949
|
+
display: block;
|
|
950
|
+
width: 100%;
|
|
951
|
+
height: 130px;
|
|
952
|
+
cursor: crosshair;
|
|
953
|
+
touch-action: none;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
.screen-signpad__hint {
|
|
957
|
+
position: absolute;
|
|
958
|
+
inset: 0;
|
|
959
|
+
display: flex;
|
|
960
|
+
align-items: center;
|
|
961
|
+
justify-content: center;
|
|
962
|
+
pointer-events: none;
|
|
963
|
+
font-family: var(--font-sans);
|
|
964
|
+
font-size: 12.5px;
|
|
965
|
+
font-weight: 600;
|
|
966
|
+
letter-spacing: 0;
|
|
967
|
+
color: rgb(139, 143, 152); /* light `--muted` - the pad is paper */
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
.screen-signpad__label {
|
|
971
|
+
font-family: var(--font-sans);
|
|
972
|
+
font-size: 12.5px;
|
|
973
|
+
font-weight: 600;
|
|
974
|
+
letter-spacing: 0;
|
|
975
|
+
color: var(--muted);
|
|
976
|
+
}
|
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
<template>
|
|
2
|
+
<!-- The per-row kebab and its dropdown. Both were Vuetify defaults: a text
|
|
3
|
+
icon button with no border, and a plain `v-list` panel at Vuetify's 4px
|
|
4
|
+
radius with no border at all. The design draws both - the kebab at
|
|
5
|
+
`iService365 Redesign.dc.html:494`, the dropdown at lines 79-90 - and
|
|
6
|
+
`.screen-kebab` / `.screen-menu` in `screens.css` carry those values. -->
|
|
2
7
|
<v-menu location="bottom end">
|
|
3
8
|
<template #activator="{ props }">
|
|
4
9
|
<v-btn
|
|
5
10
|
v-bind="props"
|
|
6
11
|
icon="mdi-dots-vertical"
|
|
7
12
|
variant="text"
|
|
8
|
-
|
|
13
|
+
class="screen-kebab"
|
|
9
14
|
/>
|
|
10
15
|
</template>
|
|
11
16
|
|
|
12
|
-
<v-list density="compact">
|
|
17
|
+
<v-list class="screen-menu" density="compact">
|
|
13
18
|
<v-list-item
|
|
19
|
+
class="screen-menu__item"
|
|
14
20
|
prepend-icon="mdi-pencil-outline"
|
|
15
21
|
title="Edit plan"
|
|
16
22
|
@click="$emit('edit')"
|
|
17
23
|
/>
|
|
18
24
|
|
|
25
|
+
<!-- Deactivating a plan is the destructive action, so it takes the
|
|
26
|
+
handoff's destructive row treatment (line 89): `--err` ink over an
|
|
27
|
+
`--err-bg` hover, not a filled red button. -->
|
|
19
28
|
<v-list-item
|
|
29
|
+
class="screen-menu__item screen-menu__item--danger"
|
|
20
30
|
prepend-icon="mdi-close-circle-outline"
|
|
21
31
|
title="Deactivate"
|
|
22
32
|
@click="$emit('deactivate')"
|
|
@@ -1,25 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
|
-
<div
|
|
4
|
-
style="
|
|
5
|
-
position: relative;
|
|
6
|
-
border: 1.5px dashed #bbb;
|
|
7
|
-
border-radius: 6px;
|
|
8
|
-
background: #fafafa;
|
|
9
|
-
overflow: hidden;
|
|
10
|
-
"
|
|
11
|
-
>
|
|
3
|
+
<div class="screen-signpad">
|
|
12
4
|
<canvas
|
|
13
5
|
ref="canvasRef"
|
|
14
6
|
:width="W"
|
|
15
7
|
:height="H"
|
|
16
|
-
|
|
17
|
-
display: block;
|
|
18
|
-
width: 100%;
|
|
19
|
-
height: 130px;
|
|
20
|
-
cursor: crosshair;
|
|
21
|
-
touch-action: none;
|
|
22
|
-
"
|
|
8
|
+
class="screen-signpad__canvas"
|
|
23
9
|
@mousedown="startMouse"
|
|
24
10
|
@mousemove="moveMouse"
|
|
25
11
|
@mouseup="stop"
|
|
@@ -28,23 +14,13 @@
|
|
|
28
14
|
@touchmove.prevent="moveTouch"
|
|
29
15
|
@touchend="stop"
|
|
30
16
|
/>
|
|
31
|
-
<div
|
|
32
|
-
|
|
33
|
-
style="
|
|
34
|
-
position: absolute;
|
|
35
|
-
inset: 0;
|
|
36
|
-
display: flex;
|
|
37
|
-
align-items: center;
|
|
38
|
-
justify-content: center;
|
|
39
|
-
pointer-events: none;
|
|
40
|
-
"
|
|
41
|
-
>
|
|
42
|
-
<span class="text-caption text-disabled">Draw your signature here</span>
|
|
17
|
+
<div v-if="isEmpty" class="screen-signpad__hint">
|
|
18
|
+
Draw your signature here
|
|
43
19
|
</div>
|
|
44
20
|
</div>
|
|
45
21
|
|
|
46
22
|
<v-row no-gutters align="center" class="mt-1" style="min-height: 28px">
|
|
47
|
-
<span class="
|
|
23
|
+
<span class="screen-signpad__label">{{ label }}</span>
|
|
48
24
|
<v-spacer />
|
|
49
25
|
<v-btn
|
|
50
26
|
v-if="!isEmpty"
|
|
@@ -1,27 +1,48 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
The summary a visitor search draws under its result. Every one of the four
|
|
3
|
+
cells was `text-subtitle-1` - Vuetify's 16px/400 Roboto - so the LABEL that
|
|
4
|
+
names a value ("Name:", "NRIC:") was drawn at exactly the same size, weight
|
|
5
|
+
and ink as the value itself, and neither was the product font.
|
|
6
|
+
|
|
7
|
+
DESIGN GAP, STATED: the handoff draws no visitor-search panel. What it does
|
|
8
|
+
draw is a label sitting next to the value it names, at
|
|
9
|
+
`iService365 Redesign.dc.html:659` - `color:var(--muted);font-size:12.5px` -
|
|
10
|
+
inside a row whose own type is 13.5px/600 with the name at 700 (line 658).
|
|
11
|
+
Those are the values below: a muted 12.5px label over a 13.5px/700 value, the
|
|
12
|
+
same label-quieter-than-value relationship `ListItem.vue` already uses.
|
|
13
|
+
|
|
14
|
+
The trailing colons are dropped: the design separates a label from its value
|
|
15
|
+
by weight and ink, not by punctuation.
|
|
16
|
+
-->
|
|
1
17
|
<template>
|
|
2
18
|
<div class="w-100 py-2">
|
|
3
|
-
<div class="d-flex align-center mb-3 justify-space-between ga-4 w-100">
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<div>
|
|
10
|
-
|
|
11
|
-
|
|
19
|
+
<div class="d-flex flex-wrap align-center mb-3 justify-space-between ga-4 w-100">
|
|
20
|
+
<!-- These were `<span>` wrappers with `<div>` children - a block inside
|
|
21
|
+
an inline element, which the parser closes early. Now both are
|
|
22
|
+
blocks, which is also what makes the 50% min-width behave. -->
|
|
23
|
+
<div class="visitor-summary__cell">
|
|
24
|
+
<div class="visitor-summary__label">Name</div>
|
|
25
|
+
<div class="visitor-summary__value">{{ displayName }}</div>
|
|
26
|
+
</div>
|
|
27
|
+
<div class="visitor-summary__cell">
|
|
28
|
+
<div class="visitor-summary__label">NRIC</div>
|
|
29
|
+
<div class="visitor-summary__value">{{ item?.nric || "N/A" }}</div>
|
|
30
|
+
</div>
|
|
12
31
|
</div>
|
|
13
32
|
|
|
14
33
|
<div
|
|
15
34
|
v-if="contacts.length < 2 && plateNumbers.length < 2"
|
|
16
|
-
class="d-flex align-center justify-space-between ga-4 w-100"
|
|
35
|
+
class="d-flex flex-wrap align-center justify-space-between ga-4 w-100"
|
|
17
36
|
>
|
|
18
|
-
<div class="
|
|
19
|
-
<div>Contact
|
|
20
|
-
<div>{{ contacts[0] || "N/A" }}</div>
|
|
37
|
+
<div class="visitor-summary__cell">
|
|
38
|
+
<div class="visitor-summary__label">Contact</div>
|
|
39
|
+
<div class="visitor-summary__value">{{ contacts[0] || "N/A" }}</div>
|
|
21
40
|
</div>
|
|
22
|
-
<div class="
|
|
23
|
-
<div>Plate
|
|
24
|
-
<div class="text-uppercase">
|
|
41
|
+
<div class="visitor-summary__cell">
|
|
42
|
+
<div class="visitor-summary__label">Plate</div>
|
|
43
|
+
<div class="visitor-summary__value text-uppercase">
|
|
44
|
+
{{ plateNumbers[0] || "N/A" }}
|
|
45
|
+
</div>
|
|
25
46
|
</div>
|
|
26
47
|
</div>
|
|
27
48
|
</div>
|
|
@@ -84,3 +105,26 @@ const plateNumbers = computed(() => {
|
|
|
84
105
|
);
|
|
85
106
|
});
|
|
86
107
|
</script>
|
|
108
|
+
|
|
109
|
+
<style scoped>
|
|
110
|
+
.visitor-summary__cell {
|
|
111
|
+
min-width: 50%;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.visitor-summary__label {
|
|
115
|
+
font-family: var(--font-sans);
|
|
116
|
+
font-size: 12.5px;
|
|
117
|
+
font-weight: var(--fw-cell);
|
|
118
|
+
letter-spacing: 0;
|
|
119
|
+
color: var(--muted);
|
|
120
|
+
margin-bottom: 2px;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.visitor-summary__value {
|
|
124
|
+
font-family: var(--font-sans);
|
|
125
|
+
font-size: var(--fs-cell);
|
|
126
|
+
font-weight: var(--fw-btn);
|
|
127
|
+
letter-spacing: 0;
|
|
128
|
+
color: var(--text);
|
|
129
|
+
}
|
|
130
|
+
</style>
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@7365admin1/layer-common",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "3.2.2-staging.
|
|
5
|
+
"version": "3.2.2-staging.143",
|
|
6
6
|
"author": "7365admin1",
|
|
7
7
|
"main": "./nuxt.config.ts",
|
|
8
8
|
"//files": "What a consumer extending this layer actually loads. Without this npm ships the whole working tree - the changesets, the CI workflows, the render harness in tools/ and any scratch directory that happened to exist at publish time. Nuxt resolves a layer by directory, so every runtime directory below has to stay listed; adding a new top-level runtime directory means adding it here too.",
|