@bagelink/vue 1.6.43 → 1.6.49
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/bin/experimentalGenTypedRoutes.ts +18 -19
- package/bin/utils.ts +4 -4
- package/dist/components/AddressSearch.vue.d.ts.map +1 -1
- package/dist/components/Alert.vue.d.ts.map +1 -1
- package/dist/components/BglVideo.vue.d.ts.map +1 -1
- package/dist/components/Card.vue.d.ts.map +1 -1
- package/dist/components/Carousel.vue.d.ts +2 -2
- package/dist/components/Carousel.vue.d.ts.map +1 -1
- package/dist/components/Dropdown.vue.d.ts.map +1 -1
- package/dist/components/Flag.vue.d.ts.map +1 -1
- package/dist/components/IframeVue.vue.d.ts.map +1 -1
- package/dist/components/ListItem.vue.d.ts.map +1 -1
- package/dist/components/Loading.vue.d.ts.map +1 -1
- package/dist/components/Modal.vue.d.ts.map +1 -1
- package/dist/components/ModalForm.vue.d.ts.map +1 -1
- package/dist/components/NavBar.vue.d.ts +1 -1
- package/dist/components/Pill.vue.d.ts.map +1 -1
- package/dist/components/Swiper.vue.d.ts +12 -4
- package/dist/components/Swiper.vue.d.ts.map +1 -1
- package/dist/components/Zoomer.vue.d.ts.map +1 -1
- package/dist/components/analytics/LineChart.vue.d.ts.map +1 -1
- package/dist/components/analytics/PieChart.vue.d.ts +2 -1
- package/dist/components/analytics/PieChart.vue.d.ts.map +1 -1
- package/dist/components/analytics/index.d.ts +1 -1
- package/dist/components/analytics/index.d.ts.map +1 -1
- package/dist/components/calendar/CalendarPopover.vue.d.ts.map +1 -1
- package/dist/components/form/BglMultiStepForm.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/ColorInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/DateInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/PasswordInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RadioGroup.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RangeInput.vue.d.ts +11 -11
- package/dist/components/form/inputs/RichText/components/EditorToolbar.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/components/TableGridSelector.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/commands.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/TelInput.vue.d.ts.map +1 -1
- package/dist/components/layout/AppContent.vue.d.ts.map +1 -1
- package/dist/components/layout/AppSidebar.vue.d.ts +1 -0
- package/dist/components/layout/AppSidebar.vue.d.ts.map +1 -1
- package/dist/components/layout/Layout.vue.d.ts.map +1 -1
- package/dist/components/layout/Tabs.vue.d.ts.map +1 -1
- package/dist/components/layout/index.d.ts +3 -3
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/lightbox/Lightbox.vue.d.ts.map +1 -1
- package/dist/index.cjs +24 -15
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +1530 -1404
- package/dist/plugins/modalTypes.d.ts +1 -8
- package/dist/plugins/modalTypes.d.ts.map +1 -1
- package/dist/plugins/useModal.d.ts.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -2
- package/src/components/AccordionItem.vue +13 -13
- package/src/components/AddToCalendar.vue +1 -1
- package/src/components/AddressSearch.vue +9 -8
- package/src/components/Alert.vue +2 -1
- package/src/components/Badge.vue +5 -5
- package/src/components/BglVideo.vue +44 -45
- package/src/components/Btn.vue +15 -15
- package/src/components/Card.vue +10 -8
- package/src/components/Carousel.vue +159 -162
- package/src/components/DataPreview.vue +1 -1
- package/src/components/DragOver.vue +6 -6
- package/src/components/Dropdown.vue +39 -38
- package/src/components/Flag.vue +7 -6
- package/src/components/Icon/Icon.vue +22 -22
- package/src/components/IframeVue.vue +5 -5
- package/src/components/Image.vue +17 -17
- package/src/components/ImportData.vue +79 -79
- package/src/components/ListItem.vue +12 -11
- package/src/components/Loading.vue +10 -9
- package/src/components/MapEmbed/Index.vue +24 -24
- package/src/components/Modal.vue +11 -9
- package/src/components/ModalForm.vue +15 -11
- package/src/components/NavBar.vue +6 -6
- package/src/components/Pagination.vue +27 -27
- package/src/components/Pill.vue +11 -12
- package/src/components/Rating.vue +2 -2
- package/src/components/Slider.vue +75 -75
- package/src/components/Spreadsheet/Index.vue +34 -34
- package/src/components/Spreadsheet/SpreadsheetTable.vue +3 -3
- package/src/components/Swiper.vue +4 -4
- package/src/components/Zoomer.vue +282 -182
- package/src/components/analytics/BarChart.vue +6 -6
- package/src/components/analytics/KpiCard.vue +2 -2
- package/src/components/analytics/LineChart.vue +63 -61
- package/src/components/analytics/PieChart.vue +104 -90
- package/src/components/analytics/index.ts +2 -2
- package/src/components/calendar/CalendarPopover.vue +1 -1
- package/src/components/calendar/Index.vue +1 -1
- package/src/components/calendar/views/AgendaView.vue +3 -3
- package/src/components/calendar/views/DayView.vue +6 -6
- package/src/components/calendar/views/MonthView.vue +2 -2
- package/src/components/calendar/views/WeekView.vue +18 -18
- package/src/components/dataTable/DataTable.vue +4 -4
- package/src/components/dataTable/useSorting.ts +1 -1
- package/src/components/dataTable/useTableData.ts +15 -15
- package/src/components/dataTable/useTableSelection.ts +15 -15
- package/src/components/dataTable/useTableVirtualization.ts +1 -1
- package/src/components/draggable/useDraggable.ts +42 -42
- package/src/components/form/BagelForm.vue +15 -15
- package/src/components/form/BglFieldSet.vue +5 -3
- package/src/components/form/BglMultiStepForm.vue +20 -21
- package/src/components/form/inputs/CheckInput.vue +2 -2
- package/src/components/form/inputs/CodeEditor/format.ts +7 -7
- package/src/components/form/inputs/CodeEditor/useHighlight.ts +6 -6
- package/src/components/form/inputs/ColorInput.vue +5 -4
- package/src/components/form/inputs/DateInput.vue +8 -9
- package/src/components/form/inputs/DatePicker.vue +24 -24
- package/src/components/form/inputs/EmailInput.vue +24 -24
- package/src/components/form/inputs/NumberInput.vue +26 -26
- package/src/components/form/inputs/OTP.vue +7 -7
- package/src/components/form/inputs/PasswordInput.vue +3 -2
- package/src/components/form/inputs/RadioGroup.vue +28 -25
- package/src/components/form/inputs/RadioPillsInput.vue +12 -12
- package/src/components/form/inputs/RangeInput.vue +21 -21
- package/src/components/form/inputs/RichText/components/EditorToolbar.vue +107 -92
- package/src/components/form/inputs/RichText/components/TableGridSelector.vue +64 -64
- package/src/components/form/inputs/RichText/components/gridBox.vue +10 -8
- package/src/components/form/inputs/RichText/composables/useCommands.ts +1 -1
- package/src/components/form/inputs/RichText/composables/useEditor.ts +12 -12
- package/src/components/form/inputs/RichText/composables/useEditorKeyboard.ts +1 -1
- package/src/components/form/inputs/RichText/index.vue +138 -138
- package/src/components/form/inputs/RichText/utils/commands.ts +84 -85
- package/src/components/form/inputs/RichText/utils/debug.ts +1 -1
- package/src/components/form/inputs/RichText/utils/formatting.ts +39 -39
- package/src/components/form/inputs/RichText/utils/media.ts +7 -7
- package/src/components/form/inputs/RichText/utils/selection.ts +28 -28
- package/src/components/form/inputs/RichText/utils/table.ts +19 -19
- package/src/components/form/inputs/SelectBtn.vue +1 -1
- package/src/components/form/inputs/SelectInput.vue +54 -54
- package/src/components/form/inputs/SignaturePad.vue +40 -40
- package/src/components/form/inputs/TableField.vue +1 -1
- package/src/components/form/inputs/TelInput.vue +54 -53
- package/src/components/form/inputs/TextInput.vue +19 -19
- package/src/components/form/inputs/ToggleInput.vue +2 -2
- package/src/components/form/inputs/Upload/useFileUpload.ts +6 -6
- package/src/components/form/useBagelFormState.ts +5 -5
- package/src/components/layout/AppContent.vue +6 -3
- package/src/components/layout/AppLayout.vue +2 -2
- package/src/components/layout/AppSidebar.vue +83 -16
- package/src/components/layout/Layout.vue +12 -10
- package/src/components/layout/SidebarMenu.vue +4 -4
- package/src/components/layout/TabbedLayout.vue +17 -17
- package/src/components/layout/Tabs.vue +4 -5
- package/src/components/layout/TabsNav.vue +14 -14
- package/src/components/layout/index.ts +3 -5
- package/src/components/lightbox/Lightbox.vue +276 -126
- package/src/components/lightbox/index.ts +8 -8
- package/src/composables/index.ts +8 -8
- package/src/composables/useAddToCalendar.ts +13 -13
- package/src/composables/useDevice.ts +2 -2
- package/src/composables/useFormField.ts +4 -4
- package/src/composables/usePolling.ts +8 -8
- package/src/composables/useSchemaField.ts +38 -38
- package/src/composables/useTheme.ts +9 -9
- package/src/composables/useValidateFieldValue.ts +2 -2
- package/src/directives/pattern.ts +25 -25
- package/src/directives/ripple.ts +4 -4
- package/src/directives/vResize.ts +6 -6
- package/src/index.ts +1 -0
- package/src/plugins/bagel.ts +4 -4
- package/src/plugins/modalTypes.ts +1 -8
- package/src/plugins/useModal.ts +43 -18
- package/src/styles/layout.css +1 -1
- package/src/types/index.ts +1 -1
- package/src/utils/BagelFormUtils.ts +7 -7
- package/src/utils/calendar/Helpers.ts +8 -8
- package/src/utils/calendar/dateUtils.ts +22 -22
- package/src/utils/calendar/time.ts +25 -25
- package/src/utils/calendar/week.ts +25 -25
- package/src/utils/elementUtils.ts +27 -27
- package/src/utils/sizeParsing.ts +2 -2
- package/src/utils/strings.ts +5 -5
- package/src/utils/tapDetector.ts +11 -11
- package/src/utils/useSearch.ts +29 -29
- package/vite.config.ts +0 -2
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from '@bagelink/vue'
|
|
9
9
|
import { until } from '@vueuse/core'
|
|
10
10
|
|
|
11
|
-
import { onMounted, watch, nextTick } from 'vue'
|
|
11
|
+
import { computed, onMounted, ref, watch, nextTick } from 'vue'
|
|
12
12
|
|
|
13
13
|
export interface TextInputProps extends ValidateInputBaseT {
|
|
14
14
|
id?: string
|
|
@@ -44,40 +44,40 @@ const props = withDefaults(defineProps<TextInputProps>(), {
|
|
|
44
44
|
modelValue: '',
|
|
45
45
|
})
|
|
46
46
|
const emit = defineEmits(['update:modelValue', 'debounce'])
|
|
47
|
-
|
|
47
|
+
const inputVal = ref<string | number>()
|
|
48
48
|
|
|
49
|
-
const input =
|
|
49
|
+
const input = ref<HTMLTextAreaElement | HTMLInputElement>()
|
|
50
50
|
|
|
51
51
|
useValidateFieldValue(
|
|
52
|
-
() => inputVal,
|
|
53
|
-
() => input,
|
|
52
|
+
() => inputVal.value,
|
|
53
|
+
() => input.value,
|
|
54
54
|
props.validate,
|
|
55
55
|
props.getFormData
|
|
56
56
|
)
|
|
57
57
|
|
|
58
|
-
const inputRows =
|
|
58
|
+
const inputRows = computed(() => {
|
|
59
59
|
let rows = Number(props.rows) || 1
|
|
60
60
|
if (props.autoheight)
|
|
61
|
-
|
|
62
|
-
if (props.multiline || props.code) {rows = Math.max(rows, 4)}
|
|
61
|
+
{ rows = Math.max(rows, String(inputVal.value).split('\n').length) }
|
|
62
|
+
if (props.multiline || props.code) { rows = Math.max(rows, 4) }
|
|
63
63
|
return rows
|
|
64
64
|
})
|
|
65
65
|
|
|
66
66
|
const debouncedEmit = useDebounceFn(() => {
|
|
67
|
-
emit('debounce', inputVal as string)
|
|
67
|
+
emit('debounce', inputVal.value as string)
|
|
68
68
|
}, 700)
|
|
69
69
|
|
|
70
70
|
function autoResizeTextarea() {
|
|
71
|
-
if (!props.autoheight || !input || 'TEXTAREA'
|
|
71
|
+
if (!props.autoheight || !input.value || input.value.tagName !== 'TEXTAREA') { return }
|
|
72
72
|
|
|
73
|
-
const textarea = input as HTMLTextAreaElement
|
|
73
|
+
const textarea = input.value as HTMLTextAreaElement
|
|
74
74
|
textarea.style.height = 'auto'
|
|
75
75
|
textarea.style.height = `${textarea.scrollHeight + 1}px`
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
function updateInputVal() {
|
|
79
|
-
if (props.disabled) {return}
|
|
80
|
-
emit('update:modelValue', inputVal as string)
|
|
79
|
+
if (props.disabled) { return }
|
|
80
|
+
emit('update:modelValue', inputVal.value as string)
|
|
81
81
|
debouncedEmit()
|
|
82
82
|
|
|
83
83
|
if (props.autoheight) {
|
|
@@ -88,8 +88,8 @@ function updateInputVal() {
|
|
|
88
88
|
watch(
|
|
89
89
|
() => props.modelValue,
|
|
90
90
|
(newVal) => {
|
|
91
|
-
if (newVal !== inputVal) {
|
|
92
|
-
inputVal = newVal
|
|
91
|
+
if (newVal !== inputVal.value) {
|
|
92
|
+
inputVal.value = newVal
|
|
93
93
|
if (props.autoheight) {
|
|
94
94
|
nextTick(() => { autoResizeTextarea() })
|
|
95
95
|
}
|
|
@@ -98,15 +98,15 @@ watch(
|
|
|
98
98
|
{ immediate: true }
|
|
99
99
|
)
|
|
100
100
|
|
|
101
|
-
const hasFocus = () => document.activeElement === input
|
|
102
|
-
const focus = () => input?.focus()
|
|
101
|
+
const hasFocus = () => document.activeElement === input.value
|
|
102
|
+
const focus = () => input.value?.focus()
|
|
103
103
|
defineExpose({ focus, hasFocus })
|
|
104
104
|
|
|
105
105
|
onMounted(async () => {
|
|
106
106
|
if (props.autofocus) {
|
|
107
|
-
await until(() => input).toBeTruthy()
|
|
107
|
+
await until(() => input.value).toBeTruthy()
|
|
108
108
|
await sleep(400)
|
|
109
|
-
input?.focus()
|
|
109
|
+
input.value?.focus()
|
|
110
110
|
}
|
|
111
111
|
// Don't auto-restore defaultValue - let user control their own content
|
|
112
112
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { onMounted } from 'vue'
|
|
2
|
+
import { computed, onMounted } from 'vue'
|
|
3
3
|
|
|
4
4
|
const props = withDefaults(defineProps<{
|
|
5
5
|
label?: string
|
|
@@ -10,7 +10,7 @@ const props = withDefaults(defineProps<{
|
|
|
10
10
|
defaultValue?: boolean
|
|
11
11
|
}>(), { defaultValue: false })
|
|
12
12
|
|
|
13
|
-
const inputId =
|
|
13
|
+
const inputId = computed(() => props.id || Math.random().toString(36).slice(7))
|
|
14
14
|
|
|
15
15
|
const checked = defineModel<boolean | undefined>('modelValue', { default: undefined })
|
|
16
16
|
|
|
@@ -32,7 +32,7 @@ export function useFileUpload(props: UseFileUploadProps = {}) {
|
|
|
32
32
|
const fileToUrl = (file: File) => URL.createObjectURL(file)
|
|
33
33
|
|
|
34
34
|
function addFile(file?: File | File[] | FileList | null) {
|
|
35
|
-
if (!file) {return}
|
|
35
|
+
if (!file) { return }
|
|
36
36
|
|
|
37
37
|
let filesToAdd: File[] = []
|
|
38
38
|
|
|
@@ -58,18 +58,18 @@ export function useFileUpload(props: UseFileUploadProps = {}) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
// Call the onFilesQueued callback if provided
|
|
61
|
-
if (props.onFileQueued &&
|
|
61
|
+
if (props.onFileQueued && newQueueFiles.length > 0) {
|
|
62
62
|
props.onFileQueued(newQueueFiles)
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
async function removeFile(pathKeyOrFile: string | File | QueueFile) {
|
|
67
|
-
if ('string'
|
|
67
|
+
if (typeof pathKeyOrFile === 'string') {
|
|
68
68
|
// Remove from both lists
|
|
69
69
|
storageFiles.value = storageFiles.value.filter(file => file.path_key !== pathKeyOrFile)
|
|
70
70
|
|
|
71
71
|
const pathKeyIndex = pk.value.indexOf(pathKeyOrFile)
|
|
72
|
-
if (
|
|
72
|
+
if (pathKeyIndex !== -1) {
|
|
73
73
|
pk.value.splice(pathKeyIndex, 1)
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -88,7 +88,7 @@ export function useFileUpload(props: UseFileUploadProps = {}) {
|
|
|
88
88
|
&& queueFile.namespace === namespace
|
|
89
89
|
)
|
|
90
90
|
|
|
91
|
-
if (
|
|
91
|
+
if (index !== -1) {
|
|
92
92
|
fileQueue.value.splice(index, 1)
|
|
93
93
|
}
|
|
94
94
|
}
|
|
@@ -124,7 +124,7 @@ export function useFileUpload(props: UseFileUploadProps = {}) {
|
|
|
124
124
|
|
|
125
125
|
// UI interaction
|
|
126
126
|
function browse(autoFlush = false) {
|
|
127
|
-
if (props.disabled) {return}
|
|
127
|
+
if (props.disabled) { return }
|
|
128
128
|
|
|
129
129
|
const input = document.createElement('input')
|
|
130
130
|
input.type = 'file'
|
|
@@ -13,11 +13,11 @@ export interface BagelFormState<T> {
|
|
|
13
13
|
|
|
14
14
|
// Helper function to safely clone objects without circular references
|
|
15
15
|
function safeClone(obj: any): any {
|
|
16
|
-
if (
|
|
16
|
+
if (obj === null || typeof obj !== 'object') { return obj }
|
|
17
17
|
|
|
18
18
|
const seen = new WeakSet()
|
|
19
19
|
return JSON.parse(JSON.stringify(obj, (key, value) => {
|
|
20
|
-
if ('object'
|
|
20
|
+
if (typeof value === 'object' && value !== null) {
|
|
21
21
|
if (seen.has(value)) {
|
|
22
22
|
return undefined // Remove circular reference
|
|
23
23
|
}
|
|
@@ -37,7 +37,7 @@ export function provideBagelFormState<T>(initialData: T) {
|
|
|
37
37
|
const keys = path.split(/[.[]/)
|
|
38
38
|
|
|
39
39
|
// Initialize the root if it's not an object
|
|
40
|
-
if (
|
|
40
|
+
if (typeof data.value !== 'object' || data.value === null) {
|
|
41
41
|
data.value = {} as T
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -46,7 +46,7 @@ export function provideBagelFormState<T>(initialData: T) {
|
|
|
46
46
|
// Build the path, ensuring each level is an object
|
|
47
47
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
48
48
|
const key = keys[i]
|
|
49
|
-
if (!(key in current) ||
|
|
49
|
+
if (!(key in current) || typeof current[key] !== 'object' || current[key] === null) {
|
|
50
50
|
current[key] = {}
|
|
51
51
|
}
|
|
52
52
|
current = current[key]
|
|
@@ -71,6 +71,6 @@ export function provideBagelFormState<T>(initialData: T) {
|
|
|
71
71
|
|
|
72
72
|
export function useBagelFormState<T>(injectionKey: typeof FORM_STATE_KEY = FORM_STATE_KEY): BagelFormState<T> {
|
|
73
73
|
const state = inject<BagelFormState<T>>(injectionKey)
|
|
74
|
-
if (!state) {throw new Error('BagelFormState must be provided')}
|
|
74
|
+
if (!state) { throw new Error('BagelFormState must be provided') }
|
|
75
75
|
return state
|
|
76
76
|
}
|
|
@@ -18,7 +18,11 @@ withDefaults(defineProps<Props>(), {
|
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
// Inject menu state from parent
|
|
21
|
-
const menuState = inject('menuState'
|
|
21
|
+
const menuState = inject('menuState', {
|
|
22
|
+
isOpen: { value: true },
|
|
23
|
+
isMobile: { value: false },
|
|
24
|
+
toggleMenu: () => { },
|
|
25
|
+
}) as {
|
|
22
26
|
isOpen: { value: boolean }
|
|
23
27
|
isMobile: { value: boolean }
|
|
24
28
|
toggleMenu: () => void
|
|
@@ -83,8 +87,7 @@ const hasSidebarCard = computed(() => {
|
|
|
83
87
|
|
|
84
88
|
<!-- Page Content -->
|
|
85
89
|
<main
|
|
86
|
-
class="pageContent flex-grow overflow pt-1 pb-05 w-100p m_p-05 m_scrollbar-gutter-auto m_vw100"
|
|
87
|
-
:class="{
|
|
90
|
+
class="pageContent flex-grow overflow pt-1 pb-05 w-100p m_p-05 m_scrollbar-gutter-auto m_vw100" :class="{
|
|
88
91
|
'px-1': !hasSidebarCard,
|
|
89
92
|
}"
|
|
90
93
|
>
|
|
@@ -17,7 +17,7 @@ const isMobile = ref(false)
|
|
|
17
17
|
|
|
18
18
|
// Check if mobile
|
|
19
19
|
function checkMobile() {
|
|
20
|
-
isMobile.value =
|
|
20
|
+
isMobile.value = window.innerWidth < 910
|
|
21
21
|
if (isMobile.value) {
|
|
22
22
|
isOpen.value = false
|
|
23
23
|
}
|
|
@@ -128,7 +128,7 @@ onUnmounted(() => {
|
|
|
128
128
|
transition: margin-inline-start 400ms ease;
|
|
129
129
|
min-height: 100vh
|
|
130
130
|
}
|
|
131
|
-
|
|
131
|
+
|
|
132
132
|
.page-content {
|
|
133
133
|
overflow: auto;
|
|
134
134
|
}
|
|
@@ -7,6 +7,7 @@ import { useRoute } from 'vue-router'
|
|
|
7
7
|
// Extended interface for links that can have actions
|
|
8
8
|
interface LinkWithAction extends NavLink {
|
|
9
9
|
action?: () => void
|
|
10
|
+
activeRoutes?: string[] // נתיבים שצריכים להיות מסומנים כאקטיביים עבור הלינק הזה
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
interface Props {
|
|
@@ -38,7 +39,13 @@ const route = useRoute()
|
|
|
38
39
|
const isTransitioning = ref(false)
|
|
39
40
|
|
|
40
41
|
// Inject menu state from parent
|
|
41
|
-
const menuState = inject('menuState'
|
|
42
|
+
const menuState = inject('menuState', {
|
|
43
|
+
isOpen: { value: true },
|
|
44
|
+
isMobile: { value: false },
|
|
45
|
+
closeOnMobile: () => void 0,
|
|
46
|
+
sidebarWidth: '260px',
|
|
47
|
+
sidebarCollapsedWidth: '66px',
|
|
48
|
+
}) as {
|
|
42
49
|
isOpen: { value: boolean }
|
|
43
50
|
isMobile: { value: boolean }
|
|
44
51
|
closeOnMobile: () => void
|
|
@@ -60,6 +67,58 @@ watch(
|
|
|
60
67
|
}
|
|
61
68
|
)
|
|
62
69
|
|
|
70
|
+
// Check if a route is active (exact match, parent route, or custom activeRoutes)
|
|
71
|
+
function isActiveRoute(link: LinkWithAction): boolean {
|
|
72
|
+
const linkPath = link.to
|
|
73
|
+
|
|
74
|
+
if (!linkPath) {
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// אם יש activeRoutes מותאמים אישית, השתמש בהם בלבד
|
|
79
|
+
if (link.activeRoutes && link.activeRoutes.length > 0) {
|
|
80
|
+
return link.activeRoutes.some((activePath) => {
|
|
81
|
+
if (activePath === '/') {
|
|
82
|
+
return route.path === activePath
|
|
83
|
+
}
|
|
84
|
+
return route.path === activePath || route.path.startsWith(`${activePath}/`)
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// אחרת, השתמש בלוגיקה האוטומטית הרגילה
|
|
89
|
+
if (linkPath === '/') {
|
|
90
|
+
return route.path === linkPath
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Exact match
|
|
94
|
+
if (route.path === linkPath) {
|
|
95
|
+
return true
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Check if current path starts with link path followed by a slash
|
|
99
|
+
// This handles nested routes like /organizations/id/settings
|
|
100
|
+
if (route.path.startsWith(`${linkPath}/`)) {
|
|
101
|
+
return true
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Handle dynamic routes - if linkPath has multiple segments and current path matches the pattern
|
|
105
|
+
const linkSegments = linkPath.split('/').filter(Boolean)
|
|
106
|
+
const routeSegments = route.path.split('/').filter(Boolean)
|
|
107
|
+
|
|
108
|
+
// If link has fewer segments than current route, check if the beginning matches
|
|
109
|
+
if (linkSegments.length <= routeSegments.length) {
|
|
110
|
+
return linkSegments.every((segment, index) => {
|
|
111
|
+
// Allow UUID-like patterns to match
|
|
112
|
+
if (segment.match(/^[a-f0-9-]{36}$/i) && routeSegments[index]?.match(/^[a-f0-9-]{36}$/i)) {
|
|
113
|
+
return true
|
|
114
|
+
}
|
|
115
|
+
return segment === routeSegments[index]
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return false
|
|
120
|
+
}
|
|
121
|
+
|
|
63
122
|
// Computed styles
|
|
64
123
|
const sidebarStyles = computed(() => {
|
|
65
124
|
let width = '280px'
|
|
@@ -117,15 +176,19 @@ const sidebarStyles = computed(() => {
|
|
|
117
176
|
<!-- Navigation Links -->
|
|
118
177
|
<nav class="sidebar-nav flex column flex-stretch gap-025 align-items-start scrollbar-gutter-stable">
|
|
119
178
|
<Btn
|
|
120
|
-
v-for="link in props.navLinks"
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
179
|
+
v-for="link in props.navLinks"
|
|
180
|
+
:key="link.to"
|
|
181
|
+
:title="!menuState.isOpen.value && !menuState.isMobile.value ? link.label : ''"
|
|
182
|
+
fullWidth
|
|
183
|
+
alignTxt="start"
|
|
184
|
+
class="flex-shrink-0 px-1"
|
|
185
|
+
:class="{ 'nav-btn-active': isActiveRoute(link) }"
|
|
124
186
|
:style="{
|
|
125
|
-
backgroundColor:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
187
|
+
backgroundColor: isActiveRoute(link) ? props.activeColor : props.bgColor,
|
|
188
|
+
color: isActiveRoute(link) ? 'white' : props.textColor,
|
|
189
|
+
}"
|
|
190
|
+
:to="link.to || '/'"
|
|
191
|
+
@click="link.action"
|
|
129
192
|
>
|
|
130
193
|
<Icon :name="link.icon" size="1.2" />
|
|
131
194
|
<span class="nav-text">
|
|
@@ -134,15 +197,19 @@ const sidebarStyles = computed(() => {
|
|
|
134
197
|
</Btn>
|
|
135
198
|
</nav>
|
|
136
199
|
<!-- Footer -->
|
|
137
|
-
<div
|
|
138
|
-
class="sidebar-footer flex column flex-stretch gap-025 align-items-start mt-auto scrollbar-gutter-stable"
|
|
139
|
-
>
|
|
200
|
+
<div class="sidebar-footer flex column flex-stretch gap-025 align-items-start mt-auto">
|
|
140
201
|
<!-- Footer Links -->
|
|
141
202
|
<Btn
|
|
142
|
-
v-for="link in props.footerLinks"
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
203
|
+
v-for="link in props.footerLinks"
|
|
204
|
+
:key="link.to || link.label"
|
|
205
|
+
:title="!menuState.isOpen.value && !menuState.isMobile.value ? link.label : ''"
|
|
206
|
+
alignTxt="start"
|
|
207
|
+
fullWidth
|
|
208
|
+
flat
|
|
209
|
+
:icon="link.icon"
|
|
210
|
+
class="flex-shrink-0 px-1"
|
|
211
|
+
:to="link.to"
|
|
212
|
+
@click="link.action"
|
|
146
213
|
>
|
|
147
214
|
<span class="nav-text">
|
|
148
215
|
{{ link.label }}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
2
4
|
interface LayoutProrps {
|
|
3
5
|
gap?: number
|
|
4
6
|
h100?: boolean
|
|
@@ -20,20 +22,20 @@ const props = withDefaults(defineProps<LayoutProrps>(), {
|
|
|
20
22
|
|
|
21
23
|
})
|
|
22
24
|
|
|
23
|
-
const gridTemplateRows =
|
|
24
|
-
const gapSize =
|
|
25
|
-
const mGapSize =
|
|
25
|
+
const gridTemplateRows = computed(() => (props.rows.length > 0 ? props.rows.join(' ') : 'auto'))
|
|
26
|
+
const gapSize = computed(() => `${props.gap}rem`)
|
|
27
|
+
const mGapSize = computed(() => props.mGap !== undefined ? `${props.mGap}rem` : gapSize.value)
|
|
26
28
|
|
|
27
|
-
const mGridTemplateRows =
|
|
28
|
-
if (props.mRows?.length) {return props.mRows.join(' ')}
|
|
29
|
-
return gridTemplateRows
|
|
29
|
+
const mGridTemplateRows = computed(() => {
|
|
30
|
+
if (props.mRows?.length) { return props.mRows.join(' ') }
|
|
31
|
+
return gridTemplateRows.value
|
|
30
32
|
})
|
|
31
33
|
|
|
32
|
-
const gridTemplateColumns =
|
|
34
|
+
const gridTemplateColumns = computed(() => (props.columns.length > 0 ? props.columns.join(' ') : 'auto'))
|
|
33
35
|
|
|
34
|
-
const mGridTemplateColumns =
|
|
35
|
-
if (props.mColumns?.length) {return props.mColumns.join(' ')}
|
|
36
|
-
return gridTemplateColumns
|
|
36
|
+
const mGridTemplateColumns = computed(() => {
|
|
37
|
+
if (props.mColumns?.length) { return props.mColumns.join(' ') }
|
|
38
|
+
return gridTemplateColumns.value
|
|
37
39
|
})
|
|
38
40
|
</script>
|
|
39
41
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { NavLink } from '@bagelink/vue'
|
|
3
3
|
import type { SetupContext } from 'vue'
|
|
4
4
|
import { Btn, Card, Icon, useDebounceFn } from '@bagelink/vue'
|
|
5
|
-
import { useSlots } from 'vue'
|
|
5
|
+
import { ref, useSlots } from 'vue'
|
|
6
6
|
|
|
7
7
|
const props = defineProps<{
|
|
8
8
|
navLinks?: NavLink[]
|
|
@@ -14,10 +14,10 @@ const emit = defineEmits(['update:open'])
|
|
|
14
14
|
|
|
15
15
|
const slots: SetupContext['slots'] = useSlots()
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
const isOpen = ref(props.open)
|
|
18
18
|
const toggleMenu = useDebounceFn(() => {
|
|
19
|
-
isOpen = !isOpen
|
|
20
|
-
emit('update:open', isOpen)
|
|
19
|
+
isOpen.value = !isOpen.value
|
|
20
|
+
emit('update:open', isOpen.value)
|
|
21
21
|
})
|
|
22
22
|
</script>
|
|
23
23
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { Raw } from 'vue'
|
|
3
3
|
import type { Router } from 'vue-router'
|
|
4
|
-
import { onMounted } from 'vue'
|
|
4
|
+
import { onMounted, ref } from 'vue'
|
|
5
5
|
|
|
6
6
|
const props = defineProps<{
|
|
7
7
|
title?: string
|
|
@@ -13,11 +13,11 @@ const props = defineProps<{
|
|
|
13
13
|
|
|
14
14
|
const emit = defineEmits(['update:modelValue'])
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
const activeTab = ref<string>()
|
|
17
17
|
function changeTab(tab: string) {
|
|
18
|
-
activeTab = tab
|
|
19
|
-
emit('update:modelValue', activeTab)
|
|
20
|
-
if (!props.router) {return}
|
|
18
|
+
activeTab.value = tab
|
|
19
|
+
emit('update:modelValue', activeTab.value)
|
|
20
|
+
if (!props.router) { return }
|
|
21
21
|
void props.router.push({
|
|
22
22
|
path: props.router.currentRoute.value.path,
|
|
23
23
|
query: { ...props.router.currentRoute.value.query, t: tab },
|
|
@@ -27,10 +27,10 @@ function changeTab(tab: string) {
|
|
|
27
27
|
|
|
28
28
|
onMounted(() => {
|
|
29
29
|
const firstTab
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
activeTab = firstTab as string
|
|
30
|
+
= props.modelValue
|
|
31
|
+
|| props.router?.currentRoute.value.query.t
|
|
32
|
+
|| props.tabs[0]
|
|
33
|
+
activeTab.value = firstTab as string
|
|
34
34
|
})
|
|
35
35
|
</script>
|
|
36
36
|
|
|
@@ -58,25 +58,25 @@ onMounted(() => {
|
|
|
58
58
|
|
|
59
59
|
<style scoped>
|
|
60
60
|
.tab {
|
|
61
|
-
|
|
61
|
+
text-transform: capitalize;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
.side-tabs {
|
|
65
|
-
|
|
65
|
+
display: flex;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
.side-tabs .tabs-top {
|
|
69
|
-
|
|
69
|
+
margin-inline-end: 1rem;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
.side-tabs .tabs {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
display: block;
|
|
74
|
+
padding: 0;
|
|
75
|
+
margin: 0;
|
|
76
|
+
border: none;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
.side-tabs .tab {
|
|
80
|
-
|
|
80
|
+
border: none;
|
|
81
81
|
}
|
|
82
82
|
</style>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { Tab } from '@bagelink/vue'
|
|
3
3
|
import type { SetupContext, VNode } from 'vue'
|
|
4
4
|
import { TabsNav } from '@bagelink/vue'
|
|
5
|
-
import { defineComponent, h, useSlots } from 'vue'
|
|
5
|
+
import { computed, defineComponent, h, useSlots } from 'vue'
|
|
6
6
|
|
|
7
7
|
import { useTabs } from './tabsManager'
|
|
8
8
|
|
|
@@ -17,9 +17,9 @@ const slots: SetupContext['slots'] = useSlots()
|
|
|
17
17
|
const group = Math.random().toString(36).slice(7)
|
|
18
18
|
const { currentTab } = useTabs(group)
|
|
19
19
|
|
|
20
|
-
const tabValue = (tab: Tab) => ('string'
|
|
20
|
+
const tabValue = (tab: Tab) => (typeof tab === 'string' ? tab : tab.id)
|
|
21
21
|
|
|
22
|
-
const slctTab =
|
|
22
|
+
const slctTab = computed({
|
|
23
23
|
get: () => props.modelValue || tabValue(props.tabs[0]),
|
|
24
24
|
set: (value) => {
|
|
25
25
|
emit('update:modelValue', value)
|
|
@@ -31,7 +31,7 @@ const tabComponent = defineComponent({
|
|
|
31
31
|
const currentTabIndex = props.tabs.findIndex(
|
|
32
32
|
tab => tabValue(tab) === currentTab.value
|
|
33
33
|
)
|
|
34
|
-
if (
|
|
34
|
+
if (currentTabIndex === -1) { return }
|
|
35
35
|
const slotChildren = slots.default?.()[1]?.children
|
|
36
36
|
|
|
37
37
|
return h('div', (slotChildren as VNode[])[currentTabIndex])
|
|
@@ -39,7 +39,6 @@ const tabComponent = defineComponent({
|
|
|
39
39
|
})
|
|
40
40
|
</script>
|
|
41
41
|
|
|
42
|
-
z
|
|
43
42
|
<template>
|
|
44
43
|
<TabsNav v-model="slctTab" :flat="flat" :tabs="tabs" :group class="mb-05" />
|
|
45
44
|
<div v-if="currentTab">
|
|
@@ -23,7 +23,7 @@ const props = defineProps<{
|
|
|
23
23
|
const emit = defineEmits(['update:modelValue'])
|
|
24
24
|
const { currentTab } = useTabs(props.group)
|
|
25
25
|
currentTab.value
|
|
26
|
-
= props.modelValue ||
|
|
26
|
+
= props.modelValue || typeof props.tabs[0] === 'string'
|
|
27
27
|
? props.tabs[0]
|
|
28
28
|
: props.tabs[0].id
|
|
29
29
|
|
|
@@ -32,13 +32,13 @@ const tabs = ref<HTMLElement[]>([])
|
|
|
32
32
|
|
|
33
33
|
function updateIndicator() {
|
|
34
34
|
nextTick(() => {
|
|
35
|
-
if (!tabsWrap.value) {return}
|
|
35
|
+
if (!tabsWrap.value) { return }
|
|
36
36
|
const activeTab = tabs.value.find(tab => tab.classList.contains('active'))
|
|
37
37
|
if (activeTab) {
|
|
38
38
|
// Wait a bit more to ensure CSS is fully loaded and elements are properly sized
|
|
39
39
|
setTimeout(() => {
|
|
40
40
|
const { offsetLeft, offsetWidth } = activeTab
|
|
41
|
-
if (
|
|
41
|
+
if (offsetLeft >= 0 && offsetWidth > 0) { // Ensure valid measurements
|
|
42
42
|
tabsWrap.value?.style.setProperty('--indicator-left', `${offsetLeft}px`)
|
|
43
43
|
tabsWrap.value?.style.setProperty('--indicator-width', `${offsetWidth}px`)
|
|
44
44
|
tabsWrap.value?.style.setProperty('--indicator-opacity', '1')
|
|
@@ -49,25 +49,25 @@ function updateIndicator() {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
function selectTab(tab: TabType | string) {
|
|
52
|
-
currentTab.value = 'string'
|
|
52
|
+
currentTab.value = typeof tab === 'string' ? tab : tab.id
|
|
53
53
|
emit('update:modelValue', currentTab.value)
|
|
54
54
|
nextTick(() => { updateIndicator() })
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
function isActive(tab: TabType | string) {
|
|
58
|
-
if ('string'
|
|
58
|
+
if (typeof tab === 'string') { return currentTab.value === tab }
|
|
59
59
|
return currentTab.value === tab.id
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function tabLabel(tab: TabType | string) {
|
|
63
|
-
if ('string'
|
|
63
|
+
if (typeof tab === 'string') { return tab }
|
|
64
64
|
return tab.label
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
watch(
|
|
68
68
|
() => props.modelValue,
|
|
69
69
|
(value) => {
|
|
70
|
-
if (value && !isActive(value)) {currentTab.value = value}
|
|
70
|
+
if (value && !isActive(value)) { currentTab.value = value }
|
|
71
71
|
nextTick(() => { updateIndicator() })
|
|
72
72
|
},
|
|
73
73
|
{ immediate: true }
|
|
@@ -97,14 +97,14 @@ onBeforeUnmount(() => {
|
|
|
97
97
|
</script>
|
|
98
98
|
|
|
99
99
|
<template>
|
|
100
|
-
<div ref="tabsWrap" class="grid auto-flow-columns relative fit-content bgl_tabs_wrap overflow-hidden" :class="{ 'bgl_flat-tabs': flat, 'bgl_vertical-tabs': vertical }">
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
100
|
+
<div ref="tabsWrap" class="grid auto-flow-columns relative fit-content bgl_tabs_wrap overflow-hidden" :class="{ 'bgl_flat-tabs': flat, 'bgl_vertical-tabs': vertical }">
|
|
101
|
+
<slot name="tabs" v-bind="{ selectTab, isActive, tabLabel, tabs }">
|
|
102
|
+
<button v-for="(tab, i) in props.tabs" :key="i" type="button" :class="{ active: isActive(tab) }" class="bgl_tab relative z-1" @click="selectTab(tab)">
|
|
103
|
+
<Icon v-if="typeof tab !== 'string' && tab.icon" :icon="tab.icon" />
|
|
104
|
+
{{ tabLabel(tab) }}
|
|
105
|
+
</button>
|
|
106
106
|
</slot>
|
|
107
|
-
|
|
107
|
+
</div>
|
|
108
108
|
</template>
|
|
109
109
|
|
|
110
110
|
<style scoped>
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export { default as AppContent } from './AppContent.vue'
|
|
2
|
+
export { default as AppLayout } from './AppLayout.vue'
|
|
3
|
+
export { default as AppSidebar } from './AppSidebar.vue'
|
|
1
4
|
export { default as BottomMenu } from './BottomMenu.vue'
|
|
2
5
|
export { default as Layout } from './Layout.vue'
|
|
3
6
|
export { default as SidebarMenu } from './SidebarMenu.vue'
|
|
@@ -6,8 +9,3 @@ export { default as TabbedLayout } from './TabbedLayout.vue'
|
|
|
6
9
|
export { default as Tabs } from './Tabs.vue'
|
|
7
10
|
export { default as TabsBody } from './TabsBody.vue'
|
|
8
11
|
export { default as TabsNav } from './TabsNav.vue'
|
|
9
|
-
export { default as AppContent } from './AppContent.vue'
|
|
10
|
-
export { default as AppLayout } from './AppLayout.vue'
|
|
11
|
-
export { default as AppSidebar } from './AppSidebar.vue'
|
|
12
|
-
|
|
13
|
-
|