@nixweb/nixloc-ui 1.1.0 → 1.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nixweb/nixloc-ui",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Componentes UI",
5
5
  "author": "Fábio Ávila <fabio@nixweb.com.br>",
6
6
  "private": false,
@@ -41,6 +41,7 @@ export default {
41
41
  if (this.clicked) this.clicked(this.params);
42
42
  },
43
43
  },
44
+
44
45
  };
45
46
  </script>
46
47
  <style scoped>
@@ -33,7 +33,7 @@ export default {
33
33
  };
34
34
  </script>
35
35
 
36
- <style scoped>
36
+ <style scoped>
37
37
  .input {
38
38
  width: 90%;
39
39
  border: none;
@@ -3,7 +3,7 @@
3
3
  <vodal :duration="80" :show="modal.open" @hide="hide()" :width="width" :height="height" :closeOnEsc="closeOnEsc"
4
4
  :closeButton="closeButton" :closeOnClickMask="false">
5
5
  <div class="d-block text-left">
6
- <Messages v-if="!vodal.open" />
6
+ <Messages v-if="!vodal.open && showValidation" />
7
7
  <div class="title">{{ title }}</div>
8
8
  <hr class="hr" />
9
9
  <slot></slot>
@@ -33,6 +33,10 @@ export default {
33
33
  type: Boolean,
34
34
  default: true,
35
35
  },
36
+ showValidation: {
37
+ type: Boolean,
38
+ default: true,
39
+ },
36
40
  onHideModal: Function,
37
41
  },
38
42
  components: { Messages, Vodal },
@@ -0,0 +1,193 @@
1
+ <template>
2
+ <transition name="slide-up">
3
+ <section v-if="isOpen" class="bab-container">
4
+ <header class="bab-header">
5
+ <div class="bab-left">
6
+ <div class="bab-icon-wrapper">
7
+ <i class="fas fa-list-check bab-icon"></i>
8
+ <span class="bab-count">{{ selected.length }}</span>
9
+ </div>
10
+ <strong class="bab-title title">
11
+ <span>selecionados</span>
12
+ </strong>
13
+ </div>
14
+
15
+ <div class="bab-actions">
16
+ <button type="button" class="bab-btn-close" @click="close">
17
+ <i class="fas fa-times"></i>
18
+ </button>
19
+ </div>
20
+ </header>
21
+ <main class="bab-content">
22
+ <slot />
23
+ </main>
24
+ </section>
25
+ </transition>
26
+ </template>
27
+
28
+ <script>
29
+
30
+ import { mapMutations } from "vuex";
31
+
32
+ export default {
33
+ name: "BottomActionsBar",
34
+ props: {
35
+ selected: { type: Array, default: () => [] }
36
+ },
37
+ computed: {
38
+ isOpen() {
39
+ return Array.isArray(this.selected) && this.selected.length > 0;
40
+ }
41
+ },
42
+ mounted() {
43
+ document.addEventListener("keydown", this.onKeydown);
44
+ },
45
+ beforeDestroy() {
46
+ document.removeEventListener("keydown", this.onKeydown);
47
+ },
48
+ methods: {
49
+ ...mapMutations("generic", ["addEvent"]),
50
+ close() {
51
+ this.$emit("close");
52
+ this.addEvent({ name: "deselectAll" });
53
+ },
54
+ onKeydown(e) {
55
+ if (e.key === "Escape" && this.isOpen) this.close();
56
+ }
57
+ }
58
+ };
59
+ </script>
60
+
61
+ <style>
62
+ .bab-container {
63
+ position: fixed;
64
+ left: 0;
65
+ right: 0;
66
+ bottom: 0;
67
+ height: 200px;
68
+
69
+ border-top-left-radius: 18px;
70
+ border-top-right-radius: 18px;
71
+ box-shadow: 0 -8px 30px rgba(0, 0, 0, 0.25);
72
+ z-index: 9999;
73
+ display: flex;
74
+ flex-direction: column;
75
+ animation: elevate 0.2s ease-in-out;
76
+ }
77
+
78
+ .bab-header {
79
+ display: flex;
80
+ align-items: center;
81
+ justify-content: space-between;
82
+ padding: 12px 16px;
83
+ border-bottom: 1px solid #eef2f7;
84
+ }
85
+
86
+ .bab-left {
87
+ display: flex;
88
+ align-items: center;
89
+ gap: 10px;
90
+ }
91
+
92
+ .bab-icon-wrapper {
93
+ position: relative;
94
+ width: 38px;
95
+ height: 38px;
96
+ border-radius: 50%;
97
+ background: #007bff;
98
+ display: flex;
99
+ align-items: center;
100
+ justify-content: center;
101
+ color: #fff;
102
+ }
103
+
104
+ .bab-icon {
105
+ font-size: 16px;
106
+ }
107
+
108
+ .bab-count {
109
+ position: absolute;
110
+ top: -5px;
111
+ right: -5px;
112
+ background: #fff;
113
+ color: #007bff;
114
+ font-size: 11px;
115
+ font-weight: bold;
116
+ border-radius: 50%;
117
+ width: 18px;
118
+ height: 18px;
119
+ display: flex;
120
+ align-items: center;
121
+ justify-content: center;
122
+ border: 2px solid #007bff;
123
+ }
124
+
125
+ .bab-title {
126
+ font-size: 14px;
127
+ font-weight: 500;
128
+ color: #0f172a;
129
+ }
130
+
131
+ .bab-actions {
132
+ display: flex;
133
+ align-items: center;
134
+ }
135
+
136
+ .bab-btn-close {
137
+ background: transparent;
138
+ border: none;
139
+ color: #9ca3af;
140
+ font-size: 18px;
141
+ cursor: pointer;
142
+ transition: color 0.2s ease;
143
+ }
144
+
145
+ .bab-btn-close:hover {
146
+ color: #6b7280;
147
+ }
148
+
149
+ .bab-content {
150
+ flex: 1;
151
+ padding: 12px 16px;
152
+ overflow: auto;
153
+ display: grid;
154
+ grid-auto-flow: column;
155
+ grid-auto-columns: minmax(220px, 1fr);
156
+ gap: 12px;
157
+ }
158
+
159
+ .slide-up-enter-active,
160
+ .slide-up-leave-active {
161
+ transition: transform 200ms ease-in-out, opacity 200ms ease-in-out;
162
+ }
163
+
164
+ .slide-up-enter,
165
+ .slide-up-leave-to {
166
+ transform: translateY(16px);
167
+ opacity: 0;
168
+ }
169
+
170
+ @keyframes elevate {
171
+ from {
172
+ transform: translateY(20px);
173
+ opacity: 0.5;
174
+ }
175
+
176
+ to {
177
+ transform: translateY(0);
178
+ opacity: 1;
179
+ }
180
+ }
181
+
182
+ @media (max-width: 640px) {
183
+ .bab-container {
184
+ height: 220px;
185
+ border-top-left-radius: 14px;
186
+ border-top-right-radius: 14px;
187
+ }
188
+
189
+ .bab-content {
190
+ grid-auto-columns: minmax(160px, 1fr);
191
+ }
192
+ }
193
+ </style>
@@ -0,0 +1,175 @@
1
+ <template>
2
+ <div class="color-picker" ref="root">
3
+ <div v-if="title" class="color-picker__title">{{ title }}</div>
4
+
5
+ <div class="color-picker__trigger" :style="{ backgroundColor: innerValue }" role="button" tabindex="0"
6
+ @click="toggle()" @keydown.enter.prevent="toggle()" @keydown.space.prevent="toggle()"
7
+ aria-haspopup="listbox" :aria-expanded="open ? 'true' : 'false'"></div>
8
+
9
+ <transition name="color-picker--fade">
10
+ <div v-show="open" class="color-picker__menu" role="listbox" :aria-label="title || 'Escolher cor'">
11
+ <div class="color-picker__grid">
12
+ <div v-for="c in colors" :key="c" class="color-picker__item" :style="{ backgroundColor: c }"
13
+ role="option" :aria-selected="c === innerValue ? 'true' : 'false'" tabindex="0"
14
+ @click="choose(c)" @keydown.enter.prevent="choose(c)" @keydown.space.prevent="choose(c)">
15
+ <span v-if="c === innerValue" class="color-picker__check"></span>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ </transition>
20
+ </div>
21
+ </template>
22
+
23
+ <script>
24
+ export default {
25
+ name: "ColorPicker",
26
+ model: { prop: "value", event: "input" },
27
+ props: {
28
+ value: { type: String, default: "" },
29
+ defaultColor: { type: String, default: "#64B5F6" },
30
+ title: { type: String, default: "" },
31
+ colors: {
32
+ type: Array,
33
+ default: function () {
34
+ return [
35
+ "#E57373", "#F06292", "#FF6F61",
36
+ "#BA68C8", "#9575CD", "#8E24AA",
37
+ "#64B5F6", "#4FC3F7", "#3949AB",
38
+ "#039BE5", "#1976D2", "#1E88E5",
39
+ "#4DB6AC", "#81C784", "#AED581",
40
+ "#26A69A", "#2E7D32", "#0097A7",
41
+ "#7CB342", "#C0CA33", "#9CCC65",
42
+ "#FFD54F", "#FFB74D", "#FDD835",
43
+ "#FB8C00", "#FF8A65", "#F4511E",
44
+ "#A1887F", "#6D4C41", "#795548",
45
+ "#90A4AE", "#546E7A", "#9E9E9E",
46
+ "#000000", "#FFFFFF", "#B0BEC5"
47
+ ];
48
+ },
49
+ },
50
+ },
51
+ data() {
52
+ return { innerValue: "", open: false };
53
+ },
54
+ created() {
55
+ this.initialize();
56
+ },
57
+ watch: {
58
+ value(v) {
59
+ if (this.isEmpty(v)) {
60
+ const fallback = this.normalizeHex(this.defaultColor);
61
+ this.innerValue = fallback;
62
+ this.$emit("input", fallback);
63
+ } else if (v !== this.innerValue) {
64
+ this.innerValue = this.normalizeHex(v);
65
+ }
66
+ }
67
+ },
68
+ mounted() {
69
+ document.addEventListener("click", this.onClickOutside, { passive: true });
70
+ },
71
+ beforeDestroy() {
72
+ document.removeEventListener("click", this.onClickOutside);
73
+ },
74
+ methods: {
75
+ initialize() {
76
+ const start = this.isEmpty(this.value)
77
+ ? this.normalizeHex(this.defaultColor)
78
+ : this.normalizeHex(this.value);
79
+ this.innerValue = start;
80
+ if (this.isEmpty(this.value)) this.$emit("input", start);
81
+ },
82
+ isEmpty(v) {
83
+ return v === null || v === undefined || String(v).trim() === "";
84
+ },
85
+ normalizeHex(v) {
86
+ const s = String(v || "").trim().toUpperCase();
87
+ const hex = /^#([0-9A-F]{6}|[0-9A-F]{3})$/i;
88
+ return hex.test(s) ? s : "#64B5F6";
89
+ },
90
+ toggle() { this.open = !this.open; },
91
+ close() { this.open = false; },
92
+ choose(c) {
93
+ const val = this.normalizeHex(c);
94
+ this.innerValue = val;
95
+ this.$emit("input", val);
96
+ this.close();
97
+ },
98
+ onClickOutside(e) {
99
+ const root = this.$refs.root;
100
+ if (this.open && root && !root.contains(e.target)) this.close();
101
+ },
102
+ },
103
+ };
104
+ </script>
105
+
106
+ <style scoped>
107
+ .color-picker {
108
+ position: relative;
109
+ display: inline-grid;
110
+ gap: .375rem;
111
+ }
112
+
113
+ .color-picker__title {
114
+ font-weight: 500;
115
+ }
116
+
117
+ .color-picker__trigger {
118
+ width: 22px;
119
+ height: 22px;
120
+ border-radius: 50%;
121
+ cursor: pointer;
122
+ user-select: none;
123
+ }
124
+
125
+ .color-picker__menu {
126
+ position: absolute;
127
+ z-index: 40;
128
+ top: calc(100% + 8px);
129
+ left: 0;
130
+ min-width: 400px;
131
+ max-width: 90vw;
132
+ background: #fff;
133
+ border: 1px solid #e5e7eb;
134
+ border-radius: 10px;
135
+ box-shadow: 0 10px 24px rgba(0, 0, 0, .12);
136
+ padding: .5rem;
137
+ }
138
+
139
+ .color-picker__grid {
140
+ display: grid;
141
+ grid-template-columns: repeat(12, 28px);
142
+ gap: .5rem;
143
+ }
144
+
145
+ .color-picker__item {
146
+ width: 28px;
147
+ height: 28px;
148
+ border-radius: 50%;
149
+ cursor: pointer;
150
+ position: relative;
151
+ border: 1px solid rgba(0, 0, 0, .1);
152
+ }
153
+
154
+ .color-picker__check {
155
+ position: absolute;
156
+ width: 10px;
157
+ height: 10px;
158
+ background: white;
159
+ border-radius: 50%;
160
+ top: 50%;
161
+ left: 50%;
162
+ transform: translate(-50%, -50%);
163
+ }
164
+
165
+ .color-picker--fade-enter-active,
166
+ .color-picker--fade-leave-active {
167
+ transition: all .14s ease;
168
+ }
169
+
170
+ .color-picker--fade-enter,
171
+ .color-picker--fade-leave-to {
172
+ opacity: 0;
173
+ transform: translateY(-4px) scale(.98);
174
+ }
175
+ </style>
@@ -0,0 +1,161 @@
1
+ <template>
2
+ <div class="icon-picker" ref="root">
3
+ <div v-if="title" class="icon-picker__title">{{ title }}</div>
4
+
5
+ <div class="icon-picker__trigger" :style="{ backgroundColor: color }" role="button" tabindex="0"
6
+ @click="toggle()" @keydown.enter.prevent="toggle()" @keydown.space.prevent="toggle()"
7
+ aria-haspopup="listbox" :aria-expanded="open ? 'true' : 'false'">
8
+ <i :class="innerValue || 'far fa-square'"></i>
9
+ </div>
10
+
11
+ <transition name="icon-picker--fade">
12
+ <div v-show="open" class="icon-picker__menu" role="listbox" :aria-label="title || 'Escolher ícone'">
13
+ <div class="icon-picker__grid">
14
+ <div v-for="ic in icons" :key="ic" class="icon-picker__item"
15
+ :class="{ 'icon-picker__item--selected': ic === innerValue }" role="option"
16
+ :aria-selected="ic === innerValue ? 'true' : 'false'" tabindex="0" @click="choose(ic)"
17
+ @keydown.enter.prevent="choose(ic)" @keydown.space.prevent="choose(ic)"
18
+ :style="ic === innerValue ? { backgroundColor: color } : {}">
19
+ <i :class="ic"></i>
20
+ </div>
21
+ </div>
22
+ </div>
23
+ </transition>
24
+ </div>
25
+ </template>
26
+
27
+ <script>
28
+ export default {
29
+ name: "IconPicker",
30
+ model: { prop: "value", event: "input" },
31
+ props: {
32
+ value: { type: String, default: "fas fa-wallet" },
33
+ title: { type: String, default: "" },
34
+ color: { type: String, default: "#64B5F6" },
35
+ icons: {
36
+ type: Array,
37
+ default: function () {
38
+ return [
39
+ "fas fa-wallet", "fas fa-cash-register", "fas fa-shopping-cart", "fas fa-receipt", "fas fa-credit-card", "fas fa-money-bill", "fas fa-coins", "fas fa-percentage", "fas fa-dollar-sign", "fas fa-file-invoice", "fas fa-chart-line",
40
+ "fas fa-box-open", "fas fa-box", "fas fa-truck", "fas fa-warehouse", "fas fa-shipping-fast", "fas fa-dolly", "fas fa-pallet", "fas fa-trailer", "fas fa-clipboard-list",
41
+ "fas fa-building", "fas fa-city", "fas fa-home", "fas fa-briefcase", "fas fa-sitemap", "fas fa-industry", "fas fa-store", "fas fa-store-alt", "fas fa-landmark", "fas fa-hotel",
42
+ "fas fa-users", "fas fa-user-tie", "fas fa-user", "fas fa-user-friends", "fas fa-user-cog", "fas fa-user-shield", "fas fa-user-circle", "fas fa-id-badge", "fas fa-id-card",
43
+ "fas fa-cogs", "fas fa-tools", "fas fa-desktop", "fas fa-laptop", "fas fa-server", "fas fa-database", "fas fa-cloud", "fas fa-network-wired", "fas fa-microchip", "fas fa-robot",
44
+ "fas fa-shield-alt", "fas fa-lock", "fas fa-balance-scale", "fas fa-file-contract", "fas fa-gavel", "fas fa-passport", "fas fa-clipboard-check",
45
+ "fas fa-bullhorn", "fas fa-ad", "fas fa-mail-bulk", "fas fa-envelope", "fas fa-comments", "fas fa-headset", "fas fa-phone-alt", "fas fa-share-alt", "fas fa-at",
46
+ "fas fa-graduation-cap", "fas fa-book", "fas fa-book-open", "fas fa-chalkboard-teacher", "fas fa-lightbulb", "fas fa-brain", "fas fa-project-diagram",
47
+ "fas fa-car", "fas fa-gas-pump", "fas fa-utensils", "fas fa-coffee", "fas fa-gift", "fas fa-tag", "fas fa-leaf", "fas fa-seedling", "fas fa-heart", "fas fa-music", "fas fa-paw", "fas fa-plane"
48
+ ];
49
+ },
50
+ },
51
+ },
52
+ data() {
53
+ return {
54
+ innerValue: this.value || "",
55
+ open: false,
56
+ };
57
+ },
58
+ watch: {
59
+ value(v) { this.innerValue = v; }
60
+ },
61
+ mounted() {
62
+ document.addEventListener("click", this.onClickOutside, { passive: true });
63
+ },
64
+ beforeDestroy() {
65
+ document.removeEventListener("click", this.onClickOutside);
66
+ },
67
+ methods: {
68
+ toggle() { this.open = !this.open; },
69
+ close() { this.open = false; },
70
+ choose(ic) {
71
+ this.innerValue = ic;
72
+ this.$emit("input", ic);
73
+ this.close();
74
+ },
75
+ onClickOutside(e) {
76
+ const root = this.$refs.root;
77
+ if (this.open && root && !root.contains(e.target)) this.close();
78
+ }
79
+ }
80
+ };
81
+ </script>
82
+
83
+ <style scoped>
84
+ .icon-picker {
85
+ position: relative;
86
+ display: inline-grid;
87
+ gap: .375rem;
88
+ margin-bottom: 10px;
89
+ }
90
+
91
+ .icon-picker__title {
92
+ font-weight: 500;
93
+ }
94
+
95
+ .icon-picker__trigger {
96
+ width: 56px;
97
+ height: 56px;
98
+ border-radius: 50%;
99
+ display: grid;
100
+ place-items: center;
101
+ cursor: pointer;
102
+ user-select: none;
103
+ }
104
+
105
+ .icon-picker__trigger i {
106
+ font-size: 22px;
107
+ color: #fff;
108
+ }
109
+
110
+ .icon-picker__menu {
111
+ position: absolute;
112
+ z-index: 40;
113
+ top: calc(100% + 8px);
114
+ left: 0;
115
+ width: 540px;
116
+ height: 320px;
117
+ overflow-y: auto;
118
+ background: #fff;
119
+ border: 1px solid #e5e7eb;
120
+ border-radius: 10px;
121
+ box-shadow: 0 10px 24px rgba(0, 0, 0, .12);
122
+ padding: .5rem .5rem .6rem;
123
+ }
124
+
125
+ .icon-picker__grid {
126
+ display: grid;
127
+ grid-template-columns: repeat(12, 36px);
128
+ gap: .5rem;
129
+ justify-content: start;
130
+ }
131
+
132
+ .icon-picker__item {
133
+ width: 36px;
134
+ height: 36px;
135
+ border-radius: 50%;
136
+ background: #f3f4f6;
137
+ display: grid;
138
+ place-items: center;
139
+ cursor: pointer;
140
+ }
141
+
142
+ .icon-picker__item i {
143
+ font-size: 14px;
144
+ color: #6b7280;
145
+ }
146
+
147
+ .icon-picker__item--selected i {
148
+ color: #fff;
149
+ }
150
+
151
+ .icon-picker--fade-enter-active,
152
+ .icon-picker--fade-leave-active {
153
+ transition: all .14s ease;
154
+ }
155
+
156
+ .icon-picker--fade-enter,
157
+ .icon-picker--fade-leave-to {
158
+ opacity: 0;
159
+ transform: translateY(-4px) scale(.98);
160
+ }
161
+ </style>
@@ -10,7 +10,7 @@
10
10
  </span>
11
11
  <span v-else>
12
12
  <div>
13
- <span class="title">
13
+ <span class="title title-pagination">
14
14
  <span v-if="totalPages == 0">0</span>
15
15
  <span v-else>{{ currentPage }}</span> de {{ totalPages }}</span>
16
16
  </div>
@@ -105,4 +105,9 @@ export default {
105
105
  opacity: 0.5;
106
106
  cursor: not-allowed;
107
107
  }
108
+
109
+ .title-pagination {
110
+ font-size: 15px;
111
+ font-weight: 400;
112
+ }
108
113
  </style>
@@ -1,8 +1,17 @@
1
1
  <template>
2
2
  <div>
3
- <div class="icon-print" v-print="'#printTable'">
4
- <i title="Imprimir" class="fas fa-print"></i>
3
+ <div class="header-actions">
4
+ <div class="icon-print" v-print="'#printTable'">
5
+ <i title="Imprimir" class="fas fa-print"></i>
6
+ </div>
7
+
8
+ <div class="div-total-records text-center">
9
+ Total <b>{{ totalRecords }} </b>
10
+ <span v-if="totalRecords > 1">registros</span>
11
+ <span v-else>registro</span>
12
+ </div>
5
13
  </div>
14
+
6
15
  <table id="printTable" class="table table-responsive-xs">
7
16
  <thead>
8
17
  <tr>
@@ -48,7 +57,7 @@ import print from "vue-print-nb";
48
57
 
49
58
  import draggable from "vuedraggable";
50
59
 
51
- import { mapState, mapMutations, mapActions } from "vuex";
60
+ import { mapState, mapMutations, mapActions, mapGetters } from "vuex";
52
61
 
53
62
  export default {
54
63
  components: { draggable, TableItem },
@@ -58,6 +67,7 @@ export default {
58
67
  props: {
59
68
  header: Array,
60
69
  data: Array,
70
+ totalRecords: Number,
61
71
  showChecks: {
62
72
  type: Boolean,
63
73
  default: true,
@@ -75,6 +85,7 @@ export default {
75
85
  },
76
86
  computed: {
77
87
  ...mapState("generic", ["selected"]),
88
+ ...mapGetters("generic", ["event"]),
78
89
  selected: {
79
90
  get() {
80
91
  return this.$store.state.generic.selected;
@@ -112,6 +123,16 @@ export default {
112
123
  beforeDestroy() {
113
124
  this.addSelected([]);
114
125
  },
126
+ watch: {
127
+ event: {
128
+ handler(event) {
129
+ if (event.name == "deselectAll") {
130
+ this.selectAll = false;
131
+ }
132
+ },
133
+ deep: true,
134
+ },
135
+ },
115
136
  };
116
137
  </script>
117
138
 
@@ -171,4 +192,41 @@ tr:hover {
171
192
  font-size: 13.8px;
172
193
  margin-left: 6px;
173
194
  }
195
+
196
+ .table thead th {
197
+ vertical-align: middle !important;
198
+ }
199
+
200
+ .td-checkbox {
201
+ width: 30px;
202
+ vertical-align: middle !important;
203
+ }
204
+
205
+ .td-checkbox .hide-print {
206
+ display: flex;
207
+ align-items: center;
208
+ justify-content: center;
209
+ height: 100%;
210
+ }
211
+
212
+ .td-checkbox .custom-control {
213
+ margin: 0;
214
+ }
215
+
216
+ .header-actions {
217
+ display: flex;
218
+ justify-content: space-between;
219
+ align-items: center;
220
+ }
221
+
222
+ .div-total-records {
223
+ background: #f3f4f6;
224
+ color: #374151;
225
+ border: 1px solid #e6e7eb;
226
+ padding: 3px 10px;
227
+ border-radius: 999px;
228
+ font-size: 12px;
229
+ width: 150px;
230
+ margin-bottom: 10px;
231
+ }
174
232
  </style>