easycomponentstools 1.0.0-dev.1.1 → 1.0.0-dev.10

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,259 @@
1
+ import printJS from 'print-js'
2
+ import xlsx, { type IColumn, type IJsonSheet } from 'json-as-xlsx'
3
+ import pdfMake from 'pdfmake/build/pdfmake'
4
+ import pdfFonts from 'pdfmake/build/vfs_fonts'
5
+ import { ref, Ref } from 'vue'
6
+ import { AdvancedFiltersDTO, HardFiltersDTO, HeaderDTO, SortDTO } from '../types/table'
7
+
8
+ export default function useDataTableExport() {
9
+ const api: Ref<string> = ref('')
10
+ const sort: Ref<SortDTO | null> = ref(null)
11
+ const filters: Ref<AdvancedFiltersDTO[]> = ref([])
12
+ const hard_filters: Ref<HardFiltersDTO[]> = ref([])
13
+ const headers: Ref<HeaderDTO[]> = ref([])
14
+ const total: Ref<number> = ref(0)
15
+ const allPrintableItems: Ref<any[]> = ref([])
16
+ const loading: Ref<boolean> = ref(false)
17
+
18
+ const setApi = (newApi: string) => {
19
+ api.value = newApi
20
+ }
21
+ const setSort = (newSort: SortDTO) => {
22
+ sort.value = { ...newSort }
23
+ }
24
+ const setFilters = (newFilters: AdvancedFiltersDTO[]) => {
25
+ filters.value = [...newFilters]
26
+ }
27
+ const setHardFilters = (newHardFilters: HardFiltersDTO[]) => {
28
+ hard_filters.value = [...newHardFilters]
29
+ }
30
+ const setHeaders = (newHeaders: HeaderDTO[]) => {
31
+ headers.value = [...newHeaders]
32
+ }
33
+ const setTotal = (newTotal: number) => {
34
+ total.value = newTotal
35
+ }
36
+ const getAllItems = async () => {
37
+ try {
38
+ const params = new URLSearchParams({
39
+ sort: JSON.stringify(sort.value ?? {}),
40
+ filters: JSON.stringify(filters.value),
41
+ hard_filters: JSON.stringify(hard_filters.value),
42
+ page: '1',
43
+ limit: total.value.toString(),
44
+ })
45
+ const response: any = await fetch(`${api.value}?${params}`, { method: 'GET' })
46
+ if (!response.ok) {
47
+ throw new Error(`Error: ${response.status}`)
48
+ }
49
+ const data = response.json()
50
+ allPrintableItems.value = 'data' in data ? data.data : data
51
+ } catch (e) {
52
+ console.error('Failed Export Error', e)
53
+ }
54
+ }
55
+ const prepareExport = () => {
56
+ allPrintableItems.value.map((item: any, index: number) => {
57
+ for (const headKey in headers.value) {
58
+ if (
59
+ headers.value[headKey] &&
60
+ typeof headers.value[headKey].valueFormatter === 'function'
61
+ ) {
62
+ const render = headers.value[headKey].valueFormatter(
63
+ item[headers.value[headKey].value],
64
+ item
65
+ )
66
+ const cleanHtml = render ? render.replace(/(<([^>]+)>)/gi, '') : ''
67
+ allPrintableItems.value[index][headers.value[headKey].value] = cleanHtml.trim()
68
+ }
69
+ }
70
+ })
71
+ }
72
+ const exportPrint = () => {
73
+ const properties: any = headers.value
74
+ .map((head: HeaderDTO) => {
75
+ return head.value !== 'actions'
76
+ ? { field: head.value, displayName: head.title }
77
+ : ''
78
+ })
79
+ .filter((val) => val)
80
+ const itemsPrint = [...allPrintableItems.value]
81
+ itemsPrint.forEach((item: any, index: number) => {
82
+ Object.keys(item).forEach((key) => (itemsPrint[index][key] = item[key] ?? ''))
83
+ })
84
+ printJS({ printable: itemsPrint, properties, type: 'json' })
85
+ }
86
+ const exportXlsx = () => {
87
+ const settings = {
88
+ fileName: 'export', // Name of the resulting spreadsheet
89
+ extraLength: 3, // A bigger number means that columns will be wider
90
+ writeOptions: {}, // Style options from https://github.com/SheetJS/sheetjs#writing-options
91
+ }
92
+
93
+ const properties = headers.value.map((head: HeaderDTO) => {
94
+ return head.value !== 'actions' ? { value: head.value, label: head.title } : ''
95
+ })
96
+ const columns: IColumn[] | any[] = properties.filter((val) => val)
97
+
98
+ const data: IJsonSheet[] = [
99
+ {
100
+ sheet: 'export-excel',
101
+ columns: [...columns],
102
+ content: [...allPrintableItems.value],
103
+ },
104
+ ]
105
+
106
+ xlsx(data, settings)
107
+ }
108
+ const exportCsv = () => {
109
+ const properties = headers.value
110
+ .map((head: HeaderDTO) => {
111
+ return head.value !== 'actions' ? { value: head.value, label: head.title } : ''
112
+ })
113
+ .filter((val) => val)
114
+
115
+ const items = allPrintableItems.value
116
+ const replacer = (key: string, value: any) => (value === null ? '' : value)
117
+ const header = properties.map((head: any) => head.label)
118
+ const csv = [
119
+ header.join(','),
120
+ ...items.map((row: any) =>
121
+ properties
122
+ .map((fieldName: any) => JSON.stringify(row[fieldName.value], replacer))
123
+ .join(',')
124
+ ),
125
+ ].join('\r\n')
126
+
127
+ const blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8;' })
128
+ const url = URL.createObjectURL(blob)
129
+ const link = document.createElement('a')
130
+ link.setAttribute('href', url)
131
+ link.setAttribute('download', 'export.csv')
132
+ document.body.appendChild(link)
133
+
134
+ link.click()
135
+ }
136
+ const exportPDF = async () => {
137
+ const props: HeaderDTO[] = headers.value.filter(
138
+ (item: HeaderDTO) => item.value !== 'actions'
139
+ )
140
+ // Ορισμός γραμματοσειρών (απαραίτητο!)
141
+ const pdfMakeInstance = (pdfMake as any).default || pdfMake
142
+ // @ts-nocheck
143
+ pdfMakeInstance.vfs = pdfFonts.pdfMake
144
+ ? (pdfFonts.pdfMake as any).vfs
145
+ : (pdfFonts as any)?.vfs
146
+
147
+ const docDefinition = {
148
+ content: [
149
+ { text: 'Export PDF', style: 'header' },
150
+ {
151
+ table: {
152
+ widths: props.map(() => 70),
153
+ body: [
154
+ props.map((header: HeaderDTO) => header.title),
155
+ ...allPrintableItems.value.map((row) =>
156
+ props.map((header: HeaderDTO) => row[header.value])
157
+ ),
158
+ ],
159
+ },
160
+ },
161
+ ],
162
+ styles: {
163
+ header: {
164
+ fontSize: 18,
165
+ bold: true,
166
+ margin: [0, 0, 0, 10],
167
+ },
168
+ },
169
+ }
170
+
171
+ pdfMakeInstance.createPdf(docDefinition).download('export.pdf')
172
+ }
173
+ const exportWORD = () => {
174
+ const properties = headers.value
175
+ .filter((head: HeaderDTO) => head.value !== 'actions')
176
+ .map((head) => ({ value: head.value, label: head.title }))
177
+ const propertyLabels = properties.map((property) => property.label)
178
+
179
+ const items = allPrintableItems.value.map((row) =>
180
+ properties.map((property) => row[property.value] ?? '-')
181
+ )
182
+
183
+ const tableHtml = `<table><tr>${propertyLabels.map((label) => `<th>${label}</th>`).join('')}</tr>${items
184
+ .map((item) => `<tr>${item.map((value) => `<td>${value}</td>`).join('')}</tr>`)
185
+ .join('')}</table>`
186
+
187
+ const css = `<style>
188
+ @page WordSection1{size: 841.95pt 595.35pt;mso-page-orientation: landscape;}
189
+ div.WordSection1 {page: WordSection1;}
190
+ table {
191
+ min-width: 100%;
192
+ border-collapse: collapse;
193
+ }
194
+
195
+ th, td {
196
+ border: thin solid rgba(0,0,0,.12);
197
+ padding: 8px;
198
+ text-align: center;
199
+ }
200
+
201
+ th {
202
+ background-color: #f3f2f7;
203
+ color: rgba(0,0,0,.6);
204
+ }
205
+
206
+ tr:nth-child(even) {
207
+ background-color: #f2f2f2;
208
+ }
209
+ </style>
210
+ `
211
+
212
+ const blob = new Blob([css + tableHtml], { type: 'application/msword' })
213
+
214
+ const url = URL.createObjectURL(blob)
215
+
216
+ const a = document.createElement('a')
217
+ a.href = url
218
+ a.download = 'export.docx'
219
+ a.click()
220
+
221
+ URL.revokeObjectURL(url)
222
+ }
223
+ const exportData = async (type: string) => {
224
+ loading.value = true
225
+ await getAllItems()
226
+ prepareExport()
227
+
228
+ if (type === 'xlsx') {
229
+ exportXlsx()
230
+ } else if (type === 'csv') {
231
+ exportCsv()
232
+ } else if (type === 'pdf') {
233
+ exportPDF()
234
+ } else if (type === 'word') {
235
+ exportWORD()
236
+ } else {
237
+ exportPrint()
238
+ }
239
+ loading.value = false
240
+ }
241
+
242
+ return {
243
+ api,
244
+ sort,
245
+ filters,
246
+ hard_filters,
247
+ headers,
248
+ total,
249
+ loading,
250
+
251
+ setApi,
252
+ setSort,
253
+ setFilters,
254
+ setHardFilters,
255
+ setHeaders,
256
+ setTotal,
257
+ exportData,
258
+ }
259
+ }
@@ -0,0 +1,49 @@
1
+ import { HeaderDTO, SavedHeaderDTO } from '../types/table'
2
+
3
+ export default function useHeadersStorage() {
4
+ const submitHeaders = (newHeaders: HeaderDTO[], idDataTable: string) => {
5
+ const dataTableHeadersStorage: SavedHeaderDTO[] = newHeaders.map((item, index) => {
6
+ return {
7
+ value: item.value,
8
+ hidden: !!item.hidden,
9
+ order: index,
10
+ }
11
+ })
12
+ window.localStorage.setItem(
13
+ `dataTableHeaders_${idDataTable}`,
14
+ JSON.stringify(dataTableHeadersStorage)
15
+ )
16
+ }
17
+ const loadHeaders = (headers: HeaderDTO[], idDataTable: string) => {
18
+ const savedHeadersStorage: string | null = window.localStorage.getItem(
19
+ `dataTableHeaders_${idDataTable}`
20
+ )
21
+ if (savedHeadersStorage) {
22
+ try {
23
+ const savedHeaders: SavedHeaderDTO[] = JSON.parse(savedHeadersStorage)
24
+ savedHeaders.forEach((header: SavedHeaderDTO) => {
25
+ const index: number = headers.findIndex(
26
+ (v: HeaderDTO) => v.value === header.value
27
+ )
28
+ if (index !== -1 && headers[index]) {
29
+ headers[index].hidden = header.hidden
30
+ Object.assign(headers[index], { order: header.order })
31
+ }
32
+ })
33
+ headers.sort((a: any, b: any) => a?.order - b?.order)
34
+ return headers
35
+ } catch (error) {
36
+ console.error(error)
37
+ window.localStorage.removeItem(`dataTableHeaders_${idDataTable}`)
38
+ return []
39
+ }
40
+ } else {
41
+ return []
42
+ }
43
+ }
44
+
45
+ return {
46
+ submitHeaders,
47
+ loadHeaders,
48
+ }
49
+ }
package/src/dev.ts ADDED
@@ -0,0 +1,114 @@
1
+ import { createApp } from 'vue'
2
+ import { createI18n } from 'vue-i18n'
3
+ import langEn from '../src/lang/en'
4
+ import langEl from '../src/lang/el'
5
+ import { useInfo } from './stores/useInfo'
6
+ import EasyForm from './components/Form/EasyForm.vue'
7
+ import 'vuetify/styles'
8
+ import './scss/app.scss'
9
+ import { createPinia } from 'pinia'
10
+ import { FormDTO } from './types/form'
11
+ import { createVuetify } from 'vuetify'
12
+ import * as components from 'vuetify/components'
13
+ import * as directives from 'vuetify/directives'
14
+ import { VFileUpload } from 'vuetify/labs/components'
15
+ import { en, el } from 'vuetify/locale'
16
+ import { removeMdiSet } from './composables/removeMdiSet'
17
+
18
+ const formData: FormDTO = {
19
+ title: 'test',
20
+ inputs: [
21
+ {
22
+ type: 'map',
23
+ key: 'test',
24
+ label: 'Test',
25
+ required: true,
26
+ validation: [(v: any) => !!v],
27
+ options: {
28
+ variant: 'underlined',
29
+ 'hide-details': true,
30
+ enableTime: true,
31
+ },
32
+ },
33
+ ],
34
+ binder: {
35
+ test: '2026-06-01T14:15:00',
36
+ },
37
+ submit: {
38
+ label: 'OK',
39
+ icon: 'icon-timer',
40
+ color: 'primary',
41
+ onSubmit: (data: any) => {
42
+ console.log(data)
43
+ },
44
+ },
45
+ menuButtons: {
46
+ label: 'Menu asdasdasd asdasd asd asd asd',
47
+ color: 'secondary',
48
+ items: [
49
+ {
50
+ label: 'select1 as das ',
51
+ onClick: () => {
52
+ console.log('select1')
53
+ },
54
+ },
55
+ {
56
+ label: 'select2 asd as ',
57
+ onClick: () => {
58
+ console.log('select2')
59
+ },
60
+ },
61
+ ],
62
+ },
63
+ }
64
+ const pinia = createPinia()
65
+ const app = createApp(EasyForm, { form: formData })
66
+ const vuetify = createVuetify({
67
+ components: {
68
+ ...components,
69
+ VFileUpload,
70
+ },
71
+ directives,
72
+ defaults: {},
73
+ theme: {
74
+ defaultTheme: 'testTheme',
75
+ themes: {
76
+ testTheme: {
77
+ colors: {
78
+ background: '#f1f1f1',
79
+ surface: '#f1f1f1',
80
+ primary: '#9a8209',
81
+ secondary: '#242424',
82
+ error: '#b71c1c',
83
+ grey: '#ACAAAAFF',
84
+ },
85
+ variables: {
86
+ 'font-family': '"Roboto", "Helvetica", "Arial", sans-serif',
87
+ },
88
+ },
89
+ },
90
+ },
91
+ locale: {
92
+ locale: 'en',
93
+ fallback: 'en',
94
+ messages: {
95
+ en: { ...en },
96
+ el: { ...el },
97
+ },
98
+ },
99
+ })
100
+ const i18n = createI18n({
101
+ legacy: false,
102
+ locale: 'en',
103
+ messages: {
104
+ en: langEn,
105
+ el: langEl,
106
+ },
107
+ })
108
+ app.use(i18n)
109
+ app.use(pinia)
110
+ app.use(vuetify)
111
+ const info = useInfo()
112
+ info.timeZone = 'UTC'
113
+ removeMdiSet().run()
114
+ app.mount('#app')
@@ -1,5 +1,7 @@
1
1
  import form from './form'
2
+ import table from './table'
2
3
 
3
4
  export default {
4
5
  form: { ...form },
6
+ table: { ...table },
5
7
  }
@@ -0,0 +1,14 @@
1
+ export default {
2
+ columns: 'Στήλες',
3
+ cancel: 'Άκυρο',
4
+ submit: 'Υποβολή',
5
+ print: 'Εκτύπωση',
6
+ filters: 'Φίλτρα',
7
+ more: 'Περισσότερα',
8
+ create: 'Δημιουργία',
9
+ delete: 'Διαγραφή',
10
+ deletion_description: 'Είστε σίγουροι για την διαγραφή;',
11
+ multiple_deletion_description: 'Είστε σίγουροι για την πολλαπλή διαγραφή;',
12
+ yes: 'Ναι',
13
+ no: 'Όχι',
14
+ }
@@ -1,5 +1,7 @@
1
1
  import form from './form'
2
+ import table from './table'
2
3
 
3
4
  export default {
4
5
  form: { ...form },
6
+ table: { ...table },
5
7
  }
@@ -0,0 +1,14 @@
1
+ export default {
2
+ columns: 'Columns',
3
+ cancel: 'Cancel',
4
+ submit: 'Submit',
5
+ print: 'Print',
6
+ filters: 'Filters',
7
+ more: 'More',
8
+ create: 'Create',
9
+ delete: 'Delete',
10
+ deletion_description: 'Are you sure for the deletion?',
11
+ multiple_deletion_description: 'Are you sure for the multiple deletion?',
12
+ yes: 'Yes',
13
+ no: 'No',
14
+ }
package/src/main.ts CHANGED
@@ -1,48 +1,25 @@
1
1
  import { App } from 'vue'
2
- import { en, el } from 'vuetify/locale'
3
2
  import { pluginsPropertiesDTO } from './types/plugins'
4
- import { createVuetify } from 'vuetify/framework'
5
- import * as components from 'vuetify/components'
6
- import * as directives from 'vuetify/directives'
7
- import { VFileUpload } from 'vuetify/labs/components'
3
+ import { createVuetify } from 'vuetify'
8
4
  import { createI18n } from 'vue-i18n'
9
5
  import langEn from '../src/lang/en'
10
6
  import langEl from '../src/lang/el'
11
- import { createPinia } from 'pinia'
12
7
  import { useInfo } from './stores/useInfo'
13
8
  import EasyForm from './components/Form/EasyForm.vue'
14
9
  import './scss/app.scss'
10
+ import { createPinia, getActivePinia } from 'pinia'
11
+ import { removeMdiSet } from './composables/removeMdiSet'
12
+ import EasyTable from './components/Table/EasyTable.vue'
13
+ import EasyConfirmationModal from './components/Modal/EasyConfirmationModal.vue'
15
14
 
16
15
  export default {
17
16
  install(app: App, props: pluginsPropertiesDTO) {
18
- useInfo().timeZone = props.timeZone
19
- const vuetify = createVuetify({
20
- components: {
21
- ...components,
22
- VFileUpload,
23
- },
24
- directives,
25
- defaults: {},
26
- theme: {
27
- defaultTheme: 'customTheme',
28
- themes: {
29
- customTheme: {
30
- colors: props.colors,
31
- variables: {
32
- 'font-family': props.fontFamily,
33
- },
34
- },
35
- },
36
- },
37
- locale: {
38
- locale: props.locale,
39
- fallback: props.locale,
40
- messages: {
41
- en: { ...en },
42
- el: { ...el },
43
- },
44
- },
45
- })
17
+ if (!createVuetify) {
18
+ throw new Error(
19
+ '[EasyComponents] Vuetify is required. Please install it:\n' + 'npm install vuetify'
20
+ )
21
+ }
22
+
46
23
  const i18n = createI18n({
47
24
  legacy: false,
48
25
  locale: props.locale ?? 'en',
@@ -51,9 +28,16 @@ export default {
51
28
  el: langEl,
52
29
  },
53
30
  })
54
- app.use(createPinia())
55
- app.use(vuetify)
56
31
  app.use(i18n)
57
32
  app.component('EasyForm', EasyForm)
33
+ app.component('EasyTable', EasyTable)
34
+ app.component('EasyConfirmationModal', EasyConfirmationModal)
35
+ const pinia = getActivePinia() ?? createPinia()
36
+ app.use(pinia)
37
+ app.runWithContext(() => {
38
+ const info = useInfo()
39
+ info.timeZone = props.timeZone
40
+ removeMdiSet().run()
41
+ })
58
42
  },
59
43
  }
package/src/scss/app.scss CHANGED
@@ -1,6 +1,5 @@
1
- @use 'vuetify/styles';
2
- @import "../../node_modules/lucide-static/font/lucide.css";
3
- @import "../../node_modules/@mdi/font/css/materialdesignicons.min.css";
1
+ @import "lucide-static/font/lucide.css";
2
+ @import "@mdi/font/css/materialdesignicons.min.css";
4
3
 
5
4
  .mainform {
6
5
  .required_field label:after{
@@ -1,15 +1,4 @@
1
- export type colorsDTO = {
2
- background: string
3
- surface: string
4
- primary: string
5
- secondary: string
6
- error: string
7
- grey: string
8
- }
9
-
10
1
  export type pluginsPropertiesDTO = {
11
- colors: colorsDTO
12
- fontFamily: string
13
- locale: 'el' | 'en'
2
+ locale: string
14
3
  timeZone: string
15
4
  }
package/vite.config.ts CHANGED
@@ -3,6 +3,11 @@ import vue from '@vitejs/plugin-vue'
3
3
  import dts from 'vite-plugin-dts'
4
4
 
5
5
  export default defineConfig({
6
+ resolve: {
7
+ alias: {
8
+ 'vue-i18n': 'vue-i18n/dist/vue-i18n.esm-bundler.js',
9
+ },
10
+ },
6
11
  plugins: [vue(), dts()],
7
12
  build: {
8
13
  cssCodeSplit: false,