sprintify-ui 0.6.82 → 0.6.83

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.
@@ -0,0 +1,32 @@
1
+ .base-json-reader {
2
+ ul {
3
+ list-style-type: disc !important;
4
+ padding-left: 20px !important;
5
+ }
6
+
7
+ li {
8
+ margin-bottom: 5px !important;
9
+ }
10
+
11
+ .chevron {
12
+ cursor: pointer;
13
+ }
14
+
15
+ .heroicons--chevron-right-16-solid,
16
+ .heroicons--chevron-down-16-solid {
17
+ display: inline-block;
18
+ background-color: currentColor;
19
+ -webkit-mask-image: var(--svg);
20
+ mask-image: var(--svg);
21
+ -webkit-mask-repeat: no-repeat;
22
+ mask-repeat: no-repeat;
23
+ -webkit-mask-size: 100% 100%;
24
+ mask-size: 100% 100%;
25
+ }
26
+ .heroicons--chevron-right-16-solid {
27
+ --svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M6.22 4.22a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06l-3.25 3.25a.75.75 0 0 1-1.06-1.06L8.94 8L6.22 5.28a.75.75 0 0 1 0-1.06' clip-rule='evenodd'/%3E%3C/svg%3E");
28
+ }
29
+ .heroicons--chevron-down-16-solid {
30
+ --svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06' clip-rule='evenodd'/%3E%3C/svg%3E");
31
+ }
32
+ }
@@ -6,6 +6,7 @@
6
6
  @import "./base-table.css";
7
7
  @import "./base-tabs.css";
8
8
  @import "./base-spinner.css";
9
+ @import "./base-json-reader";
9
10
 
10
11
  /** BaseAddressForm */
11
12
  @import "./google-pac.css";
@@ -125,7 +125,7 @@
125
125
  <tbody class="bg-white">
126
126
  <template
127
127
  v-for="(row, index) in data"
128
- :key="getRowIndex(row, index)"
128
+ :key="getRowKey(row)"
129
129
  >
130
130
  <tr class="item-row">
131
131
  <td
@@ -220,7 +220,7 @@
220
220
  <transition :name="detailTransition">
221
221
  <tr
222
222
  v-if="isActiveDetailRow(row)"
223
- :key="getRowIndex(row, index) + 'detail'"
223
+ :key="getRowKey(row) + 'detail'"
224
224
  >
225
225
  <td
226
226
  :colspan="columnCount"
@@ -272,27 +272,26 @@
272
272
  </div>
273
273
  </template>
274
274
 
275
- <script lang="ts">
276
- export default {
277
- name: 'BaseDataTableTemplate',
278
- inheritAttrs: false,
279
- };
280
- </script>
281
-
282
275
  <script lang="ts" setup>
283
- import { PropType, ref } from 'vue';
276
+ import { PropType, StyleValue, ref } from 'vue';
284
277
  import { BaseTableColumn, Row } from '@/types';
285
278
  import SlotComponent from './SlotComponent';
286
279
  import { useResizeObserver, useScroll } from '@vueuse/core';
287
280
  import { debounce, isArray } from 'lodash';
288
281
  import BaseSpinnerLarge from '../svg/BaseSpinnerLarge.vue';
289
282
  import { Size } from '@/utils/sizes';
283
+ import objectHash from 'object-hash';
290
284
 
291
285
  const checkboxStyle =
292
286
  'disabled:bg-slate-100 group-hover:shadow-md disabled:border-slate-300 disabled:cursor-not-allowed duration-300 cursor-pointer focus:ring-blue-300 border border-slate-300 shadow h-[18px] w-[18px] rounded';
293
287
  const DETAIL_ROW_WIDTH = 36;
294
288
  const CHECK_ROW_WIDTH = 36;
295
289
 
290
+ defineOptions({
291
+ name: 'BaseDataTableTemplate',
292
+ inheritAttrs: false,
293
+ });
294
+
296
295
  provide('table', getCurrentInstance());
297
296
 
298
297
  const props = defineProps({
@@ -356,11 +355,6 @@ const props = defineProps({
356
355
  type: Function,
357
356
  default: () => true,
358
357
  },
359
- /** Use a unique key of your data Object when use detailed or opened detailed. (id recommended) */
360
- rowKey: {
361
- type: String,
362
- default: 'id',
363
- },
364
358
  /* Transition name to use when toggling row details. */
365
359
  detailTransition: {
366
360
  type: String,
@@ -388,7 +382,7 @@ const emit = defineEmits([
388
382
  'cell-click',
389
383
  ]);
390
384
 
391
- const visibleDetailRows = ref<Row[]>([]);
385
+ const visibleDetailRows = ref<string[]>([]);
392
386
  // eslint-disable-next-line vue/no-setup-props-destructure
393
387
  const newCheckedRows = ref<Row[]>([...props.checkedRows]);
394
388
  const lastCheckedRowIndex = ref<number | null>(null);
@@ -521,10 +515,13 @@ function sort(column: BaseTableColumn, updatingData = false, event = null) {
521
515
  * Check if the row is checked (is added to the array).
522
516
  */
523
517
  function isRowChecked(row: Row): boolean {
524
- return (
525
- newCheckedRows.value.find((r) => r[props.rowKey] == row[props.rowKey]) !==
526
- undefined
527
- );
518
+ const found = newCheckedRows.value.find((r) => {
519
+ const key1 = getRowKey(r);
520
+ const key2 = getRowKey(row);
521
+ return key1 == key2
522
+ });
523
+
524
+ return found !== undefined;
528
525
  }
529
526
 
530
527
  /**
@@ -555,9 +552,13 @@ const isAllChecked = computed(() => {
555
552
  });
556
553
 
557
554
  function getCheckedRowIndex(row: Row) {
558
- return newCheckedRows.value.findIndex(
559
- (r) => r[props.rowKey] == row[props.rowKey]
560
- );
555
+ const foundIndex = newCheckedRows.value.findIndex((r) => {
556
+ const key1 = getRowKey(r);
557
+ const key2 = getRowKey(row);
558
+ return key1 == key2
559
+ });
560
+
561
+ return foundIndex;
561
562
  }
562
563
 
563
564
  /**
@@ -668,36 +669,27 @@ function toggleDetails(row: Row) {
668
669
  }
669
670
 
670
671
  function openDetailRow(row: Row) {
671
- const index = getDetailedIndex(row);
672
- visibleDetailRows.value.push(index);
672
+ const key = getRowKey(row);
673
+ visibleDetailRows.value.push(key);
673
674
  }
674
675
 
675
676
  function closeDetailRow(row: Row) {
676
- const index = getDetailedIndex(row);
677
- const i = visibleDetailRows.value.indexOf(index);
677
+ const key = getRowKey(row);
678
+ const i = visibleDetailRows.value.indexOf(key);
678
679
  if (i >= 0) {
679
680
  visibleDetailRows.value.splice(i, 1);
680
681
  }
681
682
  }
682
683
 
683
684
  function isVisibleDetailRow(row: Row) {
684
- const index = getDetailedIndex(row);
685
- return visibleDetailRows.value.indexOf(index) >= 0;
685
+ const key = getRowKey(row);
686
+ return visibleDetailRows.value.indexOf(key) >= 0;
686
687
  }
687
688
 
688
689
  function isActiveDetailRow(row: Row) {
689
690
  return props.detailed && isVisibleDetailRow(row);
690
691
  }
691
692
 
692
- /**
693
- * When the rowKey is defined we use the object[rowKey] as index.
694
- * If not, use the object reference by default.
695
- */
696
- function getDetailedIndex(row: Row) {
697
- const key = props.rowKey;
698
- return !key.length || !row ? row : row[key];
699
- }
700
-
701
693
  /**
702
694
  * Update sort state
703
695
  */
@@ -759,7 +751,7 @@ function removeColumn(column: BaseTableColumn) {
759
751
 
760
752
  const borderClasses = 'border-b border-slate-200';
761
753
 
762
- function borderBottomClasses(index: number, row: Record<string, any>): string {
754
+ function borderBottomClasses(index: number, row: Row): string {
763
755
  if (index < props.data.length - 1) {
764
756
  return borderClasses;
765
757
  }
@@ -795,7 +787,7 @@ function nextSequence() {
795
787
  return sequence.value++;
796
788
  }
797
789
 
798
- function getRowIndex(row: Row, index: number): string {
790
+ function getRowKey(row: Row): string {
799
791
  if (row.id) {
800
792
  return row.id;
801
793
  }
@@ -805,7 +797,7 @@ function getRowIndex(row: Row, index: number): string {
805
797
  if (row.uuid) {
806
798
  return row.uuid;
807
799
  }
808
- return index + '';
800
+ return objectHash(row);
809
801
  }
810
802
 
811
803
  // Sticky styles
@@ -825,7 +817,7 @@ function zIndex(th: boolean) {
825
817
  return 1;
826
818
  }
827
819
 
828
- function detailsStyles(th: boolean): any {
820
+ function detailsStyles(th: boolean): StyleValue {
829
821
  if (props.detailed) {
830
822
  return {
831
823
  zIndex: zIndex(th) + 1,
@@ -839,7 +831,7 @@ function detailsStyles(th: boolean): any {
839
831
  return {};
840
832
  }
841
833
 
842
- function checkStyles(th: boolean): any {
834
+ function checkStyles(th: boolean): StyleValue {
843
835
  if (props.checkable) {
844
836
  return {
845
837
  zIndex: zIndex(th) + 1,
@@ -853,7 +845,7 @@ function checkStyles(th: boolean): any {
853
845
  return {};
854
846
  }
855
847
 
856
- function firstColStyles(th: boolean): any {
848
+ function firstColStyles(th: boolean): StyleValue {
857
849
  let left = 0;
858
850
  if (props.checkable) {
859
851
  left += CHECK_ROW_WIDTH;
@@ -0,0 +1,128 @@
1
+ import BaseJsonReader from './BaseJsonReader.vue';
2
+ import ShowValue from '@/../.storybook/components/ShowValue.vue';
3
+
4
+ const sizes = ['xs', 'sm', 'md'];
5
+
6
+ export default {
7
+ title: 'Components/BaseJsonReader',
8
+ component: BaseJsonReader,
9
+ argTypes: {
10
+ size: {
11
+ control: { type: 'select' },
12
+ options: sizes,
13
+ }
14
+ },
15
+ };
16
+
17
+ const Template = (args) => ({
18
+ components: { BaseJsonReader, ShowValue },
19
+ setup() {
20
+ const object = {
21
+ "user": {
22
+ "name": "John Doe",
23
+ "email": "john@witify.io",
24
+ "permissions": [
25
+ "read",
26
+ "create",
27
+ "delete"
28
+ ],
29
+ comments: [
30
+ {
31
+ comment: "First comment",
32
+ replies: [
33
+ {
34
+ comment: "sub-comment 1 for comment 1",
35
+ replies: [
36
+ {
37
+ comment: "sub-sub-comment 1",
38
+ replies: [
39
+ {
40
+ comment: "sub-sub-sub-comment 1",
41
+ },
42
+ {
43
+ comment: {
44
+ name: "sub-sub-sub-comment 2",
45
+ desc: "test desc"
46
+ }
47
+ },
48
+ {
49
+ comment: {
50
+ name: "sub-sub-sub-comment 3",
51
+ desc: "test desc2"
52
+ }
53
+ },
54
+ ],
55
+ },
56
+ { comment: "sub-sub-comment 2" },
57
+ ],
58
+ },
59
+ { comment: "sub-comment 2 for comment 1" },
60
+ ],
61
+ },
62
+
63
+ {
64
+ comment: "Second comment",
65
+ replies: [
66
+ {
67
+ comment: "sub-comment 1 for comment 2",
68
+ replies: [
69
+ { comment: "sub-sub-comment 1" },
70
+ { comment: "sub-sub-comment 2" },
71
+ ],
72
+ },
73
+ { comment: "sub-comment 2 for comment 2" },
74
+ ],
75
+ },
76
+ ],
77
+ "category": {
78
+ "id": 1,
79
+ "name": "Other"
80
+ },
81
+ "tags": {
82
+ "id": [
83
+ "create",
84
+ "delete"
85
+ ],
86
+ "name": {
87
+ "id": {
88
+ "read": {
89
+ "id": {
90
+ "create": {
91
+ "id": {
92
+ "delete": {
93
+ "id": {
94
+ "update": {
95
+ "id": {
96
+ "name": "Read"
97
+ }
98
+ }
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+ },
106
+ "name": "Read"
107
+ },
108
+ }
109
+ },
110
+ "foo": "bar",
111
+ };
112
+
113
+ const value = ref(object);
114
+ return { value, args };
115
+ },
116
+ template: `
117
+ <BaseJsonReader v-model="value" v-bind="args">
118
+ </BaseJsonReader>
119
+ <ShowValue :value="value" />
120
+ `,
121
+ });
122
+
123
+ export const Demo = Template.bind({});
124
+
125
+ export const VariantCollapse = Template.bind({});
126
+ VariantCollapse.args = {
127
+ variant: 'collapse',
128
+ };
@@ -0,0 +1,199 @@
1
+ <template>
2
+ <div
3
+ class="base-json-reader"
4
+ :class="classes"
5
+ @click="handleClick"
6
+ >
7
+ <div
8
+ v-html="renderObject(modelValue)"
9
+ />
10
+ </div>
11
+ </template>
12
+
13
+ <script lang="ts" setup>
14
+ import { Size, sizes } from '@/utils/sizes';
15
+ import { twMerge } from 'tailwind-merge';
16
+ import { defineProps } from 'vue';
17
+
18
+ interface JsonData {
19
+ [key: string]: string | number | null | undefined | JsonData[];
20
+ }
21
+
22
+ const props = withDefaults(
23
+ defineProps<{
24
+ modelValue: JsonData;
25
+ size?: Size;
26
+ variant?: 'list' | 'collapse';
27
+ }>(),
28
+ {
29
+ size: 'sm',
30
+ variant: 'list',
31
+ }
32
+ );
33
+
34
+ const classes = computed(() => {
35
+ const base = 'bg-slate-100 p-2 rounded-md text-sm text-slate-800';
36
+ const sizeConfig = sizes[props.size];
37
+
38
+ return twMerge(
39
+ base,
40
+ sizeConfig.fontSize,
41
+ );
42
+ });
43
+
44
+ const classSizeChevron = computed(() => {
45
+ const chevronSizes = {
46
+ xs: {
47
+ width: 'w-[1em]',
48
+ height: 'h-[1em]',
49
+ marginBottom: 'mb-[-2.5px]'
50
+ },
51
+ sm: {
52
+ width: 'w-[1.1em]',
53
+ height: 'h-[1.1em]',
54
+ marginBottom: 'mb-[-3.5px]'
55
+ },
56
+ md: {
57
+ width: 'w-[1.2em]',
58
+ height: 'h-[1.2em]',
59
+ marginBottom: 'mb-[-5px]'
60
+ },
61
+ };
62
+
63
+ const sizeConfig = chevronSizes[props.size];
64
+
65
+ return twMerge(
66
+ sizeConfig.height,
67
+ sizeConfig.width,
68
+ sizeConfig.marginBottom
69
+ );
70
+ });
71
+
72
+ const expandedKeys = ref<string[]>([]);
73
+
74
+ function toggleExpand(key: string) {
75
+ if (isExpanded(key)) {
76
+ expandedKeys.value = expandedKeys.value.filter(k => k !== key);
77
+ } else {
78
+ expandedKeys.value.push(key);
79
+ }
80
+ }
81
+
82
+ function isExpanded(key: string): boolean {
83
+ return expandedKeys.value.includes(key);
84
+ }
85
+
86
+ // Expand when created
87
+ function recursiveExpand(data: JsonData | JsonData[] , parentKey = '') {
88
+ if (props.variant !== 'collapse') {
89
+ return;
90
+ }
91
+
92
+ if (Array.isArray(data)) {
93
+ data.forEach((item, index) => {
94
+ const uniqueKey = parentKey ? `${parentKey}[${index}]` : `[${index}]`;
95
+ expandedKeys.value.push(uniqueKey);
96
+ recursiveExpand(item, uniqueKey);
97
+ });
98
+ } else {
99
+ Object.entries(data).forEach(([key, value], index) => {
100
+ const uniqueKey = parentKey ? `${parentKey}[${index}]` : `[${index}]`;
101
+ expandedKeys.value.push(uniqueKey);
102
+
103
+ if (typeof value === 'object' && value !== null) {
104
+ recursiveExpand(value, uniqueKey);
105
+ }
106
+ });
107
+ }
108
+ }
109
+
110
+ recursiveExpand(props.modelValue);
111
+
112
+ function handleClick(event: MouseEvent) {
113
+ if (props.variant !== 'collapse') {
114
+ return;
115
+ }
116
+
117
+ const target = event.target as HTMLElement;
118
+ if (target.classList.contains('chevron')) {
119
+ const key = target.dataset.key;
120
+
121
+ if (key) {
122
+ toggleExpand(key);
123
+ }
124
+ }
125
+ }
126
+
127
+ function renderContent(data: JsonData | JsonData[], uniqueKey = ''): string {
128
+ if (Array.isArray(data)) {
129
+ return renderArray(data, uniqueKey);
130
+ }
131
+
132
+ else if (typeof data === 'object' && data !== null) {
133
+ return renderObject(data, uniqueKey);
134
+ }
135
+
136
+ return `<span>${data}</span>`;
137
+ }
138
+
139
+ // If data is an array
140
+ function renderArray(data: JsonData[], parentKey = ''): string {
141
+ let result = '<ul>';
142
+
143
+ data.forEach((item, index) => {
144
+ const uniqueKey = parentKey ? `${parentKey}[${index}]` : `[${index}]`;
145
+
146
+ if (typeof item === 'object' && item !== null) {
147
+ result += `${renderContent(item, uniqueKey)}`;
148
+ }
149
+ else {
150
+ result += `<li>${renderContent(item, uniqueKey)}</li>`;
151
+ }
152
+
153
+ });
154
+
155
+ result += '</ul>';
156
+
157
+ return result;
158
+ }
159
+
160
+ // If data is an object
161
+ function renderObject(data: JsonData, parentKey = ''): string {
162
+ let result = '<ul>';
163
+
164
+ Object.entries(data).forEach(([key, value], index) => {
165
+ const uniqueKey = parentKey ? `${parentKey}[${index}]` : `[${index}]`;
166
+ console.log('uniqueKey', uniqueKey, 'parentKey', parentKey, 'index', index);
167
+
168
+
169
+ if (props.variant === 'collapse') {
170
+ result += `<li> <b data-key="${uniqueKey}" class="${(typeof value === 'object' && value !== null) ? 'chevron' : ''}">${key}</b>`;
171
+ } else {
172
+ result += `<li> <b>${key}</b>`;
173
+ }
174
+
175
+ if (typeof value === 'object' && value !== null) {
176
+ if (props.variant === 'collapse') {
177
+ result += `<span data-key="${uniqueKey}" class="chevron ml-[-2px]"> ${renderChevron(uniqueKey)} </span>`;
178
+ }
179
+
180
+ if (isExpanded(uniqueKey) || props.variant !== 'collapse') {
181
+ result += renderContent(value, uniqueKey);
182
+ }
183
+ } else {
184
+ result += ` <span>${value}</span>`;
185
+ }
186
+
187
+ result += '</li>';
188
+ });
189
+
190
+ result += '</ul>';
191
+
192
+ return result;
193
+ }
194
+
195
+ function renderChevron(key: string): string {
196
+ return isExpanded(key) ? `<span class="chevron heroicons--chevron-right-16-solid w-1 h-1 ${classSizeChevron.value}" data-key="${key}"></span>`
197
+ : `<span class="chevron heroicons--chevron-down-16-solid ${classSizeChevron.value}" data-key="${key}"></span>`;
198
+ }
199
+ </script>
@@ -77,8 +77,6 @@ const scrollToSelectedValue = (
77
77
  behavior: 'auto' | 'instant' | 'smooth' | undefined = 'smooth'
78
78
  ) => {
79
79
 
80
- console.log('timeContainer', timeContainer, 'timeValue', timeValue.value);
81
-
82
80
  if (!timeContainer || !timeValue.value) {
83
81
  return;
84
82
  }
@@ -345,9 +345,6 @@ function ok(close: () => void) {
345
345
 
346
346
  emitUpdate(time);
347
347
 
348
- console.log('time', time);
349
-
350
-
351
348
  close();
352
349
  }
353
350