@bagelink/vue 1.2.101 → 1.2.105
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/components/AddressSearch.vue.d.ts.map +1 -1
- package/dist/components/DragOver.vue.d.ts +27 -0
- package/dist/components/DragOver.vue.d.ts.map +1 -0
- package/dist/components/Dropdown.vue.d.ts.map +1 -1
- package/dist/components/ImportData.vue.d.ts +20 -0
- package/dist/components/ImportData.vue.d.ts.map +1 -0
- package/dist/components/calendar/views/CalendarPopover.vue.d.ts +2 -2
- package/dist/components/calendar/views/CalendarPopover.vue.d.ts.map +1 -1
- package/dist/components/form/FieldArray.vue.d.ts +3 -1
- package/dist/components/form/FieldArray.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/lightbox/Lightbox.vue.d.ts.map +1 -1
- package/dist/composables/index.d.ts +2 -1
- package/dist/composables/index.d.ts.map +1 -1
- package/dist/composables/useExcel.d.ts +25 -0
- package/dist/composables/useExcel.d.ts.map +1 -0
- package/dist/index.cjs +2562 -675
- package/dist/index.mjs +2563 -676
- package/dist/plugins/modalTypes.d.ts +1 -3
- package/dist/plugins/modalTypes.d.ts.map +1 -1
- package/dist/style.css +222 -186
- package/dist/types/BagelForm.d.ts +5 -4
- package/dist/types/BagelForm.d.ts.map +1 -1
- package/dist/types/timeago.d.ts +23 -0
- package/dist/types/timeago.d.ts.map +1 -0
- package/dist/utils/BagelFormUtils.d.ts +9 -4
- package/dist/utils/BagelFormUtils.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/DragOver.vue +112 -0
- package/src/components/ImportData.vue +1964 -0
- package/src/components/form/FieldArray.vue +4 -2
- package/src/components/form/inputs/RichText/utils/media.ts +2 -2
- package/src/components/index.ts +2 -0
- package/src/components/lightbox/Lightbox.vue +2 -14
- package/src/composables/index.ts +2 -1
- package/src/composables/useExcel.ts +220 -0
- package/src/plugins/modalTypes.ts +1 -1
- package/src/styles/buttons.css +79 -75
- package/src/types/BagelForm.ts +10 -10
- package/src/utils/BagelFormUtils.ts +18 -6
- package/src/utils/index.ts +21 -0
- package/dist/components/Carousel2.vue.d.ts +0 -89
- package/dist/components/Carousel2.vue.d.ts.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script lang="ts" setup generic="T, P extends Path<T>">
|
|
1
|
+
<script lang="ts" setup generic="T extends {[key: string]: any}, P extends Path<T>">
|
|
2
2
|
import type {
|
|
3
3
|
AttributeFn,
|
|
4
4
|
AttributeValue,
|
|
@@ -50,6 +50,8 @@ const props = withDefaults(
|
|
|
50
50
|
|
|
51
51
|
const emit = defineEmits(['update:modelValue'])
|
|
52
52
|
|
|
53
|
+
const BagelFormFA = BagelForm<T, P>
|
|
54
|
+
|
|
53
55
|
// State
|
|
54
56
|
const minimizedItems = ref<boolean[]>([])
|
|
55
57
|
const internalData = ref<any[]>(props.modelValue || [])
|
|
@@ -201,7 +203,7 @@ const showMinimizeButton = computed(() => {
|
|
|
201
203
|
</p>
|
|
202
204
|
|
|
203
205
|
<!-- Form View -->
|
|
204
|
-
<
|
|
206
|
+
<BagelFormFA
|
|
205
207
|
v-else
|
|
206
208
|
:model-value="isPrimitiveType ? { value: item } : item"
|
|
207
209
|
:schema="resolvedSchemaData"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BaseBagelField } from '@bagelink/vue'
|
|
2
2
|
import type { ModalApi } from '../../../../../plugins/modal'
|
|
3
3
|
|
|
4
4
|
import type { EditorState } from '../richTextTypes'
|
|
@@ -90,7 +90,7 @@ export function insertEmbed(modal: ModalApi, state: EditorState) {
|
|
|
90
90
|
bglFrmUtil.numField('width', 'Width', { min: 200, placeholder: '560' }),
|
|
91
91
|
bglFrmUtil.numField('height', 'Height', { min: 200, placeholder: '315' })
|
|
92
92
|
),
|
|
93
|
-
{ id: 'allowFullscreen', $el: 'check', label: 'Allow Fullscreen', value: true } as
|
|
93
|
+
{ id: 'allowFullscreen', $el: 'check', label: 'Allow Fullscreen', value: true } as BaseBagelField<InsertImbedModalData, 'allowFullscreen'>,
|
|
94
94
|
],
|
|
95
95
|
onSubmit: (data: { url: string, width?: number, height?: number, allowFullscreen?: boolean }) => {
|
|
96
96
|
if (!data.url) return
|
package/src/components/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ export { default as DataTable } from './dataTable/DataTable.vue'
|
|
|
15
15
|
/** @deprecated Use DataTable instead. TableSchema is an alias that will be removed in a future version. */
|
|
16
16
|
export { default as TableSchema } from './dataTable/DataTable.vue'
|
|
17
17
|
export { Draggable, useDraggable, vDraggable } from './draggable'
|
|
18
|
+
export { default as DragOver } from './DragOver.vue'
|
|
18
19
|
export { default as Dropdown } from './Dropdown.vue'
|
|
19
20
|
export { default as FieldSetVue } from './FieldSetVue.vue'
|
|
20
21
|
export { default as Flag } from './Flag.vue'
|
|
@@ -22,6 +23,7 @@ export * from './form'
|
|
|
22
23
|
export { default as Icon } from './Icon/Icon.vue'
|
|
23
24
|
export { default as IframeVue } from './IframeVue.vue'
|
|
24
25
|
export { default as Image } from './Image.vue'
|
|
26
|
+
export { default as ImportData } from './ImportData.vue'
|
|
25
27
|
export * from './layout'
|
|
26
28
|
export { default as ListItem } from './ListItem.vue'
|
|
27
29
|
export { default as ListView } from './ListView.vue'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { LightboxItem } from './lightbox.types'
|
|
3
3
|
|
|
4
|
-
import { BglVideo, Btn, Icon, Zoomer, Image, normalizeURL, Carousel } from '@bagelink/vue'
|
|
4
|
+
import { BglVideo, Btn, Icon, Zoomer, Image, normalizeURL, Carousel, downloadFile } from '@bagelink/vue'
|
|
5
5
|
import { watch } from 'vue'
|
|
6
6
|
|
|
7
7
|
let isOpen = $ref(false)
|
|
@@ -61,18 +61,6 @@ function clickOutside() {
|
|
|
61
61
|
if (zoom === 1) close()
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
const upgradeHeaders = (url: string) => url.replace(/http:\/\//, '//')
|
|
65
|
-
|
|
66
|
-
function downloadFile() {
|
|
67
|
-
const link = document.createElement('a')
|
|
68
|
-
const src = currentItem.src || ''
|
|
69
|
-
link.target = '_blank'
|
|
70
|
-
link.href = upgradeHeaders(src)
|
|
71
|
-
link.download = src ? src.split('/').pop() || 'download' : 'download'
|
|
72
|
-
document.body.appendChild(link)
|
|
73
|
-
link.click()
|
|
74
|
-
document.body.removeChild(link)
|
|
75
|
-
}
|
|
76
64
|
defineExpose({ open, close })
|
|
77
65
|
</script>
|
|
78
66
|
|
|
@@ -118,7 +106,7 @@ defineExpose({ open, close })
|
|
|
118
106
|
<Btn
|
|
119
107
|
v-if="currentItem?.download && currentItem?.src" class="color-white" round thin flat icon="download"
|
|
120
108
|
value="Download File"
|
|
121
|
-
@click="downloadFile"
|
|
109
|
+
@click="downloadFile(currentItem?.src)"
|
|
122
110
|
/>
|
|
123
111
|
<div v-if="!currentItem?.openFile && !currentItem?.download" />
|
|
124
112
|
</div>
|
package/src/composables/index.ts
CHANGED
|
@@ -5,8 +5,8 @@ import { getFallbackSchema } from '@bagelink/vue'
|
|
|
5
5
|
import { ref, toValue, watch } from 'vue'
|
|
6
6
|
|
|
7
7
|
export { useDevice } from './useDevice'
|
|
8
|
+
export { useExcel } from './useExcel'
|
|
8
9
|
export { usePolling } from './usePolling'
|
|
9
|
-
export { useValidateFieldValue } from './useValidateFieldValue'
|
|
10
10
|
interface UseBglSchemaParamsT<T> {
|
|
11
11
|
schema?: MaybeRefOrGetter<BglFormSchemaT<T>>
|
|
12
12
|
columns?: MaybeRefOrGetter<string[]>
|
|
@@ -47,3 +47,4 @@ export function localRef<T>(
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export const useLocalStorage = localRef
|
|
50
|
+
export { useValidateFieldValue } from './useValidateFieldValue'
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import type { MaybeRefOrGetter } from 'vue'
|
|
2
|
+
import type { BglFormSchemaT } from '../types'
|
|
3
|
+
import { ref, toValue } from 'vue'
|
|
4
|
+
import { appendScript, downloadFile, getNestedValue } from '../utils'
|
|
5
|
+
|
|
6
|
+
declare global {
|
|
7
|
+
interface Window {
|
|
8
|
+
XLSX: any
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface SheetData {
|
|
13
|
+
headers: string[]
|
|
14
|
+
data: Record<string, any>[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const objKeys = (object: object) => Object.keys(object)
|
|
18
|
+
const strToTitle = (str?: string) => str?.split(' ').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(' ')
|
|
19
|
+
|
|
20
|
+
function formatData<T>(data: T[], schema?: BglFormSchemaT<T>) {
|
|
21
|
+
return schema
|
|
22
|
+
? data.map((row) => {
|
|
23
|
+
const newRow: any = {}
|
|
24
|
+
schema.forEach(({ id, transform, label }) => {
|
|
25
|
+
if (id) {
|
|
26
|
+
// Support for dot notation to access nested properties
|
|
27
|
+
const value = getNestedValue(row, id)
|
|
28
|
+
const key = label || id
|
|
29
|
+
newRow[key] = transform?.(value, row) || value || ''
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
return newRow
|
|
33
|
+
})
|
|
34
|
+
: data
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function useExcel() {
|
|
38
|
+
const xlsxLoaded = ref(false)
|
|
39
|
+
|
|
40
|
+
async function ensureXLSXLoaded() {
|
|
41
|
+
if (!xlsxLoaded.value) {
|
|
42
|
+
await appendScript('https://cdn.jsdelivr.net/npm/xlsx/dist/xlsx.full.min.js')
|
|
43
|
+
xlsxLoaded.value = true
|
|
44
|
+
}
|
|
45
|
+
return window.XLSX
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function downloadExcel<T>(data: T[], fileName = 'data', schemaFn?: MaybeRefOrGetter<BglFormSchemaT<T>>) {
|
|
49
|
+
const schema = toValue(schemaFn) || []
|
|
50
|
+
const XLSX = await ensureXLSXLoaded()
|
|
51
|
+
|
|
52
|
+
const formattedData = formatData(data, schema)
|
|
53
|
+
const headers = schema.length > 0 ? schema.map(({ id, label }) => ({ v: label || id, t: 's' })) : []
|
|
54
|
+
const ws = XLSX.utils.json_to_sheet(formattedData)
|
|
55
|
+
|
|
56
|
+
if (headers.length > 0) {
|
|
57
|
+
XLSX.utils.sheet_add_aoa(ws, [headers.map(h => h.v)], { origin: 'A1' })
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const wb = XLSX.utils.book_new()
|
|
61
|
+
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1')
|
|
62
|
+
fileName = fileName.endsWith('.xlsx') ? fileName : `${fileName}.xlsx`
|
|
63
|
+
XLSX.writeFile(wb, fileName)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function downloadCSV(data: Record<string, any>[], fileName = 'data', schema?: BglFormSchemaT<any>) {
|
|
67
|
+
const keys = schema ? schema.map(({ id }) => id).filter(Boolean) : [...new Set(data.flatMap(objKeys))]
|
|
68
|
+
const columns = schema ? schema.map(({ label, id }) => label || strToTitle(id)) : keys.map(strToTitle)
|
|
69
|
+
|
|
70
|
+
const formattedData = formatData(data, schema)
|
|
71
|
+
const csv = [
|
|
72
|
+
columns.join(','),
|
|
73
|
+
...formattedData.map(row => keys.map((key) => {
|
|
74
|
+
const value = row[key as string] !== undefined ? row[key as string] : ''
|
|
75
|
+
return `"${String(value).replace(/"/g, '""')}"`
|
|
76
|
+
}).join(','))
|
|
77
|
+
].join('\n')
|
|
78
|
+
fileName = fileName.endsWith('.csv') ? fileName : `${fileName}.csv`
|
|
79
|
+
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' })
|
|
80
|
+
downloadFile(blob, fileName)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function getWorkbook(file: File) {
|
|
84
|
+
const XLSX = await ensureXLSXLoaded()
|
|
85
|
+
const data = await file.arrayBuffer()
|
|
86
|
+
return XLSX.read(data)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function getSheetNames(file: File): Promise<string[]> {
|
|
90
|
+
const workbook = await getWorkbook(file)
|
|
91
|
+
return workbook.SheetNames
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function readSheetData(file: File, sheetName: string, hasHeaders = true): Promise<SheetData> {
|
|
95
|
+
const XLSX = await ensureXLSXLoaded()
|
|
96
|
+
const workbook = await getWorkbook(file)
|
|
97
|
+
const worksheet = workbook.Sheets[sheetName]
|
|
98
|
+
|
|
99
|
+
// Read the raw sheet data as arrays
|
|
100
|
+
const rawSheetData = XLSX.utils.sheet_to_json(worksheet, { header: 1 }) as any[][]
|
|
101
|
+
|
|
102
|
+
if (rawSheetData.length === 0) {
|
|
103
|
+
return { headers: [], data: [] }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Extract headers based on hasHeaders setting
|
|
107
|
+
let headers: string[] = []
|
|
108
|
+
let startRow = 0
|
|
109
|
+
|
|
110
|
+
if (hasHeaders) {
|
|
111
|
+
// Use first row as headers
|
|
112
|
+
headers = rawSheetData[0].map(h => String(h || '').trim())
|
|
113
|
+
startRow = 1
|
|
114
|
+
} else {
|
|
115
|
+
// Generate column letter headers (A, B, C...)
|
|
116
|
+
const firstRow = rawSheetData[0]
|
|
117
|
+
headers = firstRow.map((_, i) => String.fromCharCode(65 + i))
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Convert the raw data into objects
|
|
121
|
+
const parsedData = []
|
|
122
|
+
for (let i = startRow; i < rawSheetData.length; i++) {
|
|
123
|
+
const row = rawSheetData[i]
|
|
124
|
+
const rowData: Record<string, any> = {}
|
|
125
|
+
for (let j = 0; j < headers.length; j++) {
|
|
126
|
+
if (j < row.length) {
|
|
127
|
+
rowData[headers[j]] = row[j]
|
|
128
|
+
} else {
|
|
129
|
+
rowData[headers[j]] = ''
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
parsedData.push(rowData)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
headers,
|
|
138
|
+
data: parsedData
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function readExcelFile(file: File): Promise<any[]> {
|
|
143
|
+
const XLSX = await ensureXLSXLoaded()
|
|
144
|
+
|
|
145
|
+
return new Promise((resolve, reject) => {
|
|
146
|
+
const reader = new FileReader()
|
|
147
|
+
|
|
148
|
+
reader.onload = (e) => {
|
|
149
|
+
try {
|
|
150
|
+
const data = new Uint8Array(e.target?.result as ArrayBuffer)
|
|
151
|
+
const workbook = XLSX.read(data, { type: 'array' })
|
|
152
|
+
|
|
153
|
+
const firstSheetName = workbook.SheetNames[0]
|
|
154
|
+
const worksheet = workbook.Sheets[firstSheetName]
|
|
155
|
+
const jsonData = XLSX.utils.sheet_to_json(worksheet)
|
|
156
|
+
|
|
157
|
+
resolve(jsonData)
|
|
158
|
+
} catch (error) {
|
|
159
|
+
reject(error)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
reader.onerror = () => {
|
|
164
|
+
reject(new Error('Failed to read file'))
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
reader.readAsArrayBuffer(file)
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Helper function for date detection and conversion
|
|
172
|
+
function isExcelSerialDate(value: any): boolean {
|
|
173
|
+
return (
|
|
174
|
+
typeof value === 'number'
|
|
175
|
+
&& Number.isInteger(value)
|
|
176
|
+
&& value >= 20000
|
|
177
|
+
&& value <= 50000
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function excelSerialDateToJSDate(serial: number): Date {
|
|
182
|
+
const excelEpoch = new Date(Date.UTC(1899, 11, 30)) // Excel counts from Dec 30, 1899
|
|
183
|
+
const msPerDay = 24 * 60 * 60 * 1000
|
|
184
|
+
return new Date(excelEpoch.getTime() + serial * msPerDay)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function formatDate(date: Date, includeTime = false): string {
|
|
188
|
+
if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
|
|
189
|
+
return ''
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const year = date.getFullYear()
|
|
193
|
+
const month = String(date.getMonth() + 1).padStart(2, '0')
|
|
194
|
+
const day = String(date.getDate()).padStart(2, '0')
|
|
195
|
+
|
|
196
|
+
if (!includeTime) {
|
|
197
|
+
return `${year}-${month}-${day}`
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const hours = String(date.getHours()).padStart(2, '0')
|
|
201
|
+
const minutes = String(date.getMinutes()).padStart(2, '0')
|
|
202
|
+
const seconds = String(date.getSeconds()).padStart(2, '0')
|
|
203
|
+
|
|
204
|
+
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
downloadExcel,
|
|
209
|
+
downloadCSV,
|
|
210
|
+
readExcelFile,
|
|
211
|
+
ensureXLSXLoaded,
|
|
212
|
+
getSheetNames,
|
|
213
|
+
readSheetData,
|
|
214
|
+
getWorkbook,
|
|
215
|
+
// Date helpers
|
|
216
|
+
isExcelSerialDate,
|
|
217
|
+
excelSerialDateToJSDate,
|
|
218
|
+
formatDate
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -37,7 +37,7 @@ export interface ModalFormComponentProps<T extends { [key: string]: any }> exten
|
|
|
37
37
|
modalOptions: ModalFormOptions<T>
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
export interface ModalFormOptions<T
|
|
40
|
+
export interface ModalFormOptions<T> {
|
|
41
41
|
'schema': MaybeRefOrGetter<BglFormSchemaT<T>>
|
|
42
42
|
'modelValue'?: T
|
|
43
43
|
'onUpdate:modelValue'?: (val: T) => void
|
package/src/styles/buttons.css
CHANGED
|
@@ -1,140 +1,144 @@
|
|
|
1
1
|
.bgl_btn,
|
|
2
2
|
.bgl_flatBtn,
|
|
3
3
|
.bgl_btn-icon {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
4
|
+
font-family: inherit;
|
|
5
|
+
white-space: nowrap;
|
|
6
|
+
cursor: pointer;
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
user-select: none;
|
|
9
|
+
border: none;
|
|
10
|
+
transition: var(--bgl-transition);
|
|
11
|
+
border-radius: var(--btn-border-radius);
|
|
12
|
+
line-height: var(--btn-height);
|
|
13
|
+
font-size: var(--input-font-size);
|
|
14
|
+
display: inline-block;
|
|
15
|
+
height: var(--btn-height);
|
|
16
|
+
padding: 0;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
.btn-close {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
20
|
+
margin-top: -20px;
|
|
21
|
+
margin-inline-end: -20px;
|
|
22
|
+
margin-inline-start: auto;
|
|
23
|
+
margin-bottom: 15px;
|
|
24
|
+
transition: var(--bgl-transition);
|
|
25
|
+
height: 30px;
|
|
26
|
+
width: 30px;
|
|
27
|
+
opacity: 0.6;
|
|
28
|
+
cursor: pointer;
|
|
29
|
+
border-radius: 100%;
|
|
30
|
+
outline: 2px solid transparent;
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
justify-content: center;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
.btn-close:hover {
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
background: var(--bgl-gray-light);
|
|
38
|
+
opacity: 1;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
.btn-close:active {
|
|
42
|
-
|
|
42
|
+
background: var(--bgl-gray);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
.btn-close::before {
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
content: 'close';
|
|
47
|
+
font-family: 'Material Symbols Outlined', serif;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
.bgl_btn.thin {
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
height: calc(var(--btn-height) * 0.7);
|
|
52
|
+
line-height: calc(var(--btn-height) * 0.7);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
.hover {
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
transition: all 400ms ease;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
.hover:hover {
|
|
61
|
-
|
|
61
|
+
filter: brightness(90%);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
.hover:active {
|
|
65
|
-
|
|
65
|
+
filter: brightness(80%);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
.border {
|
|
69
|
-
|
|
69
|
+
border: 1px solid var(--border-color);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
.border-primary {
|
|
73
|
-
|
|
73
|
+
border: 1px solid var(--bgl-primary);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
.outline {
|
|
77
|
-
|
|
77
|
+
outline: 1px solid var(--border-color);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.outline-dashed {
|
|
81
|
+
outline: 2px dashed var(--border-color);
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
.outline-primary {
|
|
81
|
-
|
|
85
|
+
outline: 1px solid var(--bgl-primary);
|
|
82
86
|
}
|
|
83
87
|
|
|
84
88
|
.rotate-270 {
|
|
85
|
-
|
|
89
|
+
transform: rotate(270deg);
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
.rotate-180 {
|
|
89
|
-
|
|
93
|
+
transform: rotate(180deg);
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
.rotate-90 {
|
|
93
|
-
|
|
97
|
+
transform: rotate(90deg);
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
.rotate-0 {
|
|
97
|
-
|
|
101
|
+
transform: rotate(0deg) !important;
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
@media screen and (max-width: 910px) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
.bgl_btn {
|
|
106
|
+
padding: 0 20px;
|
|
107
|
+
}
|
|
104
108
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
109
|
+
.m_border {
|
|
110
|
+
border: 1px solid var(--border-color);
|
|
111
|
+
}
|
|
108
112
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
.m_rotate-270 {
|
|
114
|
+
transform: rotate(270deg);
|
|
115
|
+
}
|
|
112
116
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
117
|
+
.m_rotate-180 {
|
|
118
|
+
transform: rotate(180deg);
|
|
119
|
+
}
|
|
116
120
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
121
|
+
.m_rotate-90 {
|
|
122
|
+
transform: rotate(90deg);
|
|
123
|
+
}
|
|
120
124
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
125
|
+
.m_rotate-0 {
|
|
126
|
+
transform: rotate(0deg) !important;
|
|
127
|
+
}
|
|
124
128
|
}
|
|
125
129
|
|
|
126
130
|
.ripple {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
position: absolute;
|
|
132
|
+
border-radius: 50%;
|
|
133
|
+
transform: scale(0);
|
|
134
|
+
background: rgba(0, 0, 0, 0.3);
|
|
135
|
+
pointer-events: none;
|
|
136
|
+
animation: rippleEffect 0.6s ease-out;
|
|
133
137
|
}
|
|
134
138
|
|
|
135
139
|
@keyframes rippleEffect {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
140
|
+
to {
|
|
141
|
+
transform: scale(4);
|
|
142
|
+
opacity: 0;
|
|
143
|
+
}
|
|
144
|
+
}
|
package/src/types/BagelForm.ts
CHANGED
|
@@ -100,22 +100,22 @@ export type BglFieldT<T> = Field<T>
|
|
|
100
100
|
|
|
101
101
|
export type BglFormSchemaT<T> = Field<T>[]
|
|
102
102
|
|
|
103
|
-
export type InputBagelField<T> = Field<T> & {
|
|
104
|
-
$el: 'text' | ComponentExposed<typeof TextInput>
|
|
105
|
-
type?: string
|
|
106
|
-
}
|
|
107
|
-
// export interface InputBagelField<T> extends BaseBagelField<T, Path<T>> {
|
|
103
|
+
// export type InputBagelField<T> = Field<T> & {
|
|
108
104
|
// $el: 'text' | ComponentExposed<typeof TextInput>
|
|
109
105
|
// type?: string
|
|
110
106
|
// }
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
107
|
+
export interface InputBagelField<T, P extends Path<T>> extends BaseBagelField<T, P> {
|
|
108
|
+
$el: 'text' | ComponentExposed<typeof TextInput>
|
|
109
|
+
type?: string
|
|
114
110
|
}
|
|
115
|
-
|
|
111
|
+
|
|
112
|
+
// export type SelectBagelField<T> = Field<T> & {
|
|
116
113
|
// $el: 'select' | ComponentExposed<typeof SelectInput>
|
|
117
|
-
// type?: string
|
|
118
114
|
// }
|
|
115
|
+
export interface SelectBagelField<T, P extends Path<T>> extends BaseBagelField<T, P> {
|
|
116
|
+
$el: 'select' | ComponentExposed<typeof SelectInput>
|
|
117
|
+
type?: string
|
|
118
|
+
}
|
|
119
119
|
|
|
120
120
|
export interface ValidateInputBaseT {
|
|
121
121
|
validate?: ValidationFn<{ [key: string]: unknown }, string>
|
|
@@ -75,7 +75,7 @@ export function txtField<T, P extends Path<T>>(
|
|
|
75
75
|
id: P,
|
|
76
76
|
label?: string,
|
|
77
77
|
options?: TextInputOptions<T, P>,
|
|
78
|
-
): BaseBagelField<T, P> | InputBagelField<T> {
|
|
78
|
+
): BaseBagelField<T, P> | InputBagelField<T, P> {
|
|
79
79
|
return {
|
|
80
80
|
$el: 'text',
|
|
81
81
|
id,
|
|
@@ -107,7 +107,7 @@ export function selectField<T, P extends Path<T>>(
|
|
|
107
107
|
label?: string,
|
|
108
108
|
options?: Option[] | (() => Option[]),
|
|
109
109
|
config?: SlctInputOptions<T, P>,
|
|
110
|
-
): BaseBagelField<T, P> | SelectBagelField<T> {
|
|
110
|
+
): BaseBagelField<T, P> | SelectBagelField<T, P> {
|
|
111
111
|
return {
|
|
112
112
|
$el: 'select',
|
|
113
113
|
id,
|
|
@@ -230,17 +230,29 @@ export function numField<T, P extends Path<T>>(
|
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
-
export function frmRow<
|
|
233
|
+
export function frmRow<
|
|
234
|
+
T,
|
|
235
|
+
P extends Path<T>
|
|
236
|
+
>(
|
|
237
|
+
...children: SchemaChild<T, P>[]
|
|
238
|
+
) {
|
|
234
239
|
return {
|
|
235
240
|
$el: 'div',
|
|
236
241
|
class: 'flex gap-1 m_block align-items-end',
|
|
237
242
|
children,
|
|
238
|
-
}
|
|
243
|
+
} satisfies BaseBagelField<T, P>
|
|
239
244
|
}
|
|
240
245
|
|
|
241
|
-
export
|
|
246
|
+
export interface UploadOptions<T, K extends Path<T>> extends UploadInputProps, InputOptions<T, K> {}
|
|
242
247
|
|
|
243
|
-
export function uploadField<
|
|
248
|
+
export function uploadField<
|
|
249
|
+
T,
|
|
250
|
+
P extends Path<T>
|
|
251
|
+
>(
|
|
252
|
+
id: P,
|
|
253
|
+
label?: string,
|
|
254
|
+
options?: UploadOptions<T, P>
|
|
255
|
+
): BaseBagelField<T, P> {
|
|
244
256
|
return {
|
|
245
257
|
$el: 'upload',
|
|
246
258
|
id,
|
package/src/utils/index.ts
CHANGED
|
@@ -232,3 +232,24 @@ export function getNestedValue(obj: any, path?: string, defaultValue: any = unde
|
|
|
232
232
|
|
|
233
233
|
return current ?? defaultValue
|
|
234
234
|
}
|
|
235
|
+
|
|
236
|
+
const upgradeHeaders = (url: string) => url.replace(/http:\/\//, '//')
|
|
237
|
+
|
|
238
|
+
export function downloadFile(source: string | Blob, fileName?: string) {
|
|
239
|
+
const link = document.createElement('a')
|
|
240
|
+
link.target = '_blank'
|
|
241
|
+
|
|
242
|
+
if (typeof source === 'string') {
|
|
243
|
+
link.href = upgradeHeaders(source)
|
|
244
|
+
link.download = fileName || source.split('/').pop() || 'download'
|
|
245
|
+
} else {
|
|
246
|
+
const url = URL.createObjectURL(source)
|
|
247
|
+
link.href = url
|
|
248
|
+
link.download = fileName || 'download'
|
|
249
|
+
URL.revokeObjectURL(url)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
document.body.appendChild(link)
|
|
253
|
+
link.click()
|
|
254
|
+
document.body.removeChild(link)
|
|
255
|
+
}
|