@bagelink/vue 1.4.139 → 1.4.145

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.
Files changed (73) hide show
  1. package/dist/components/Btn.vue.d.ts.map +1 -1
  2. package/dist/components/Carousel.vue.d.ts +1 -1
  3. package/dist/components/Modal.vue.d.ts +3 -0
  4. package/dist/components/Modal.vue.d.ts.map +1 -1
  5. package/dist/components/Slider.vue.d.ts +1 -1
  6. package/dist/components/Slider.vue.d.ts.map +1 -1
  7. package/dist/components/analytics/BarChart.vue.d.ts +11 -3
  8. package/dist/components/analytics/BarChart.vue.d.ts.map +1 -1
  9. package/dist/components/analytics/LineChart.vue.d.ts +9 -0
  10. package/dist/components/analytics/LineChart.vue.d.ts.map +1 -1
  11. package/dist/components/analytics/PieChart.vue.d.ts +30 -2
  12. package/dist/components/analytics/PieChart.vue.d.ts.map +1 -1
  13. package/dist/components/form/inputs/RichText/components/EditorToolbar.vue.d.ts +8 -0
  14. package/dist/components/form/inputs/RichText/components/EditorToolbar.vue.d.ts.map +1 -1
  15. package/dist/components/form/inputs/RichText/components/TableGridSelector.vue.d.ts +9 -0
  16. package/dist/components/form/inputs/RichText/components/TableGridSelector.vue.d.ts.map +1 -0
  17. package/dist/components/form/inputs/RichText/composables/useCommands.d.ts.map +1 -1
  18. package/dist/components/form/inputs/RichText/composables/useEditor.d.ts +0 -14
  19. package/dist/components/form/inputs/RichText/composables/useEditor.d.ts.map +1 -1
  20. package/dist/components/form/inputs/RichText/composables/useEditorKeyboard.d.ts.map +1 -1
  21. package/dist/components/form/inputs/RichText/config.d.ts.map +1 -1
  22. package/dist/components/form/inputs/RichText/index.vue.d.ts +15 -15
  23. package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
  24. package/dist/components/form/inputs/RichText/richTextTypes.d.ts +1 -3
  25. package/dist/components/form/inputs/RichText/richTextTypes.d.ts.map +1 -1
  26. package/dist/components/form/inputs/RichText/utils/commands.d.ts.map +1 -1
  27. package/dist/components/form/inputs/RichText/utils/formatting.d.ts.map +1 -1
  28. package/dist/components/form/inputs/RichText/utils/media-clean.d.ts +2 -0
  29. package/dist/components/form/inputs/RichText/utils/media-clean.d.ts.map +1 -0
  30. package/dist/components/form/inputs/RichText/utils/media.d.ts +4 -4
  31. package/dist/components/form/inputs/RichText/utils/media.d.ts.map +1 -1
  32. package/dist/components/form/inputs/RichText/utils/selection.d.ts.map +1 -1
  33. package/dist/components/form/inputs/RichText/utils/table.d.ts +1 -1
  34. package/dist/components/form/inputs/RichText/utils/table.d.ts.map +1 -1
  35. package/dist/components/index.d.ts +1 -0
  36. package/dist/components/index.d.ts.map +1 -1
  37. package/dist/components/layout/AppContent.vue.d.ts.map +1 -1
  38. package/dist/components/layout/AppLayout.vue.d.ts.map +1 -1
  39. package/dist/components/layout/AppSidebar.vue.d.ts.map +1 -1
  40. package/dist/index.cjs +123 -22
  41. package/dist/index.mjs +123 -22
  42. package/dist/style.css +1 -1
  43. package/package.json +1 -1
  44. package/src/components/Btn.vue +50 -42
  45. package/src/components/Modal.vue +49 -50
  46. package/src/components/analytics/BarChart.vue +118 -7
  47. package/src/components/analytics/KpiCard.vue +2 -2
  48. package/src/components/analytics/LineChart.vue +189 -105
  49. package/src/components/analytics/PieChart.vue +392 -49
  50. package/src/components/dataTable/DataTable.vue +1 -1
  51. package/src/components/form/inputs/RichText/CheckList.md +23 -0
  52. package/src/components/form/inputs/RichText/components/EditorToolbar.vue +243 -27
  53. package/src/components/form/inputs/RichText/components/TableGridSelector.vue +94 -0
  54. package/src/components/form/inputs/RichText/composables/useCommands.ts +45 -0
  55. package/src/components/form/inputs/RichText/composables/useEditor.ts +13 -10
  56. package/src/components/form/inputs/RichText/composables/useEditorKeyboard.ts +3 -128
  57. package/src/components/form/inputs/RichText/config.ts +33 -10
  58. package/src/components/form/inputs/RichText/editor.css +300 -33
  59. package/src/components/form/inputs/RichText/index.vue +3271 -130
  60. package/src/components/form/inputs/RichText/richTextTypes.ts +7 -3
  61. package/src/components/form/inputs/RichText/utils/commands.ts +851 -90
  62. package/src/components/form/inputs/RichText/utils/formatting.ts +17 -15
  63. package/src/components/form/inputs/RichText/utils/media-clean.ts +0 -0
  64. package/src/components/form/inputs/RichText/utils/media.ts +133 -67
  65. package/src/components/form/inputs/RichText/utils/selection.ts +40 -11
  66. package/src/components/form/inputs/RichText/utils/table.ts +1 -1
  67. package/src/components/index.ts +1 -0
  68. package/src/components/layout/AppContent.vue +26 -26
  69. package/src/components/layout/AppLayout.vue +21 -3
  70. package/src/components/layout/AppSidebar.vue +5 -2
  71. package/src/styles/layout.css +267 -0
  72. package/src/styles/mobilLayout.css +266 -0
  73. package/src/styles/modal.css +3 -17
@@ -19,30 +19,32 @@ export function formatting(state: EditorState) {
19
19
  // Don't apply inline styles directly to block elements
20
20
  if (parentBlock?.tagName.match(/^H[1-6]|P|BLOCKQUOTE|LI$/)) {
21
21
  if (!range.collapsed && range.toString().trim()) {
22
- const span = doc.createElement('span') as HTMLSpanElement
23
- if (command === 'underline') span.style.textDecoration = 'underline'
24
- else if (command === 'bold') span.style.fontWeight = 'bold'
25
- else if (command === 'italic') span.style.fontStyle = 'italic'
26
-
27
- if (isRTL) span.dir = 'rtl'
28
- range.surroundContents(span)
22
+ let element: HTMLElement
23
+ if (command === 'underline') element = doc.createElement('u')
24
+ else if (command === 'bold') element = doc.createElement('b')
25
+ else if (command === 'italic') element = doc.createElement('i')
26
+ else return
27
+
28
+ if (isRTL) element.dir = 'rtl'
29
+ range.surroundContents(element)
29
30
  }
30
31
  } else {
31
32
  if (range.collapsed) return // No selection, nothing to format
32
33
 
33
- const span = doc.createElement('span')
34
- if (command === 'bold') span.style.fontWeight = 'bold'
35
- else if (command === 'italic') span.style.fontStyle = 'italic'
36
- else if (command === 'underline') span.style.textDecoration = 'underline'
34
+ let element: HTMLElement
35
+ if (command === 'bold') element = doc.createElement('b')
36
+ else if (command === 'italic') element = doc.createElement('i')
37
+ else if (command === 'underline') element = doc.createElement('u')
38
+ else return
37
39
 
38
40
  try {
39
- range.surroundContents(span)
40
- } catch (e) {
41
+ range.surroundContents(element)
42
+ } catch {
41
43
  // If surroundContents fails (e.g., for selections across multiple nodes)
42
44
  // Extract the fragment, wrap it, and insert it back
43
45
  const fragment = range.extractContents()
44
- span.appendChild(fragment)
45
- range.insertNode(span)
46
+ element.appendChild(fragment)
47
+ range.insertNode(element)
46
48
  }
47
49
  }
48
50
  }
@@ -1,5 +1,4 @@
1
1
  import type { ModalApi } from '../../../../../plugins/useModal'
2
-
3
2
  import type { EditorState } from '../richTextTypes'
4
3
  import { bagelFormUtils as frm } from '../../../../../utils'
5
4
 
@@ -53,87 +52,154 @@ export function insertImage(modal: ModalApi, state: EditorState) {
53
52
  }
54
53
 
55
54
  export function insertLink(modal: ModalApi, state: EditorState) {
56
- const { range, doc } = state
57
- if (!range || !doc) return
55
+ const { doc } = state
56
+ if (!doc) {
57
+ console.log('No doc found')
58
+ return
59
+ }
58
60
 
59
- modal.showModalForm({
60
- title: 'Insert Link',
61
- schema: [
62
- { id: 'url', $el: 'text', label: 'URL' },
63
- { id: 'openInNewTab', $el: 'check', label: 'Open in new tab' },
64
- ],
65
- onSubmit: (data: { url: string, openInNewTab: boolean }) => {
66
- if (data.url) {
67
- const anchor = doc.createElement('a')
68
- anchor.href = data.url
69
- if (data.openInNewTab) anchor.target = '_blank'
70
- range.surroundContents(anchor)
71
- state.content = doc.body.innerHTML
61
+ // Get current selection
62
+ const selection = doc.getSelection()
63
+ if (!selection || !selection.rangeCount) {
64
+ console.log('No selection found')
65
+ return
66
+ }
67
+
68
+ let range = selection.getRangeAt(0)
69
+
70
+ // If no text is selected, try to select word at cursor
71
+ if (range.collapsed) {
72
+ const success = selectWordAtCursor(doc, range)
73
+ if (!success) {
74
+ console.log('Failed to select word at cursor')
75
+ // Show a helpful tooltip to the user
76
+ const showTooltipMessage = (state as any).showTooltipMessage
77
+ if (showTooltipMessage) {
78
+ showTooltipMessage('To add a link, please select text first or place cursor within a word')
79
+ } else {
80
+ // Fallback to alert if tooltip system is not available
81
+ alert('To add a link, please select text first or place the cursor within a word you want to turn into a link.')
72
82
  }
83
+ return
73
84
  }
74
- })
85
+ // Get the updated range after word selection
86
+ range = selection.getRangeAt(0)
87
+ }
88
+
89
+ // Check if we're editing an existing link
90
+ const commonAncestor = range.commonAncestorContainer
91
+ const parentElement = commonAncestor.nodeType === Node.TEXT_NODE
92
+ ? commonAncestor.parentElement
93
+ : commonAncestor as Element
94
+
95
+ const existingLink = parentElement?.closest('a')
96
+
97
+ // Use the new modal function if available
98
+ if ((state as any).openLinkModal) {
99
+ (state as any).openLinkModal(selection, range, existingLink)
100
+ return
101
+ }
102
+
103
+ // Fallback - just show an alert if modal is not available
104
+ console.warn('Link modal not available - openLinkModal function not found')
105
+ alert('Link functionality requires proper modal setup')
75
106
  }
76
107
 
77
- export interface InsertImbedModalData { url: string, width?: number, height?: number, allowFullscreen?: boolean }
108
+ // Helper function to select word at cursor
109
+ function selectWordAtCursor(doc: Document, range: Range): boolean {
110
+ if (!range.collapsed) return true // Already has selection
111
+
112
+ let textNode = range.startContainer
113
+ let offset = range.startOffset
114
+
115
+ // If we're in an element node, try to find a text node
116
+ if (textNode.nodeType !== Node.TEXT_NODE) {
117
+ const element = textNode as Element
118
+ if (element.childNodes.length > 0 && offset < element.childNodes.length) {
119
+ const childNode = element.childNodes[offset]
120
+ if (childNode.nodeType === Node.TEXT_NODE) {
121
+ textNode = childNode
122
+ offset = 0
123
+ } else {
124
+ return false
125
+ }
126
+ } else {
127
+ return false
128
+ }
129
+ }
130
+
131
+ const text = textNode.textContent || ''
132
+ if (!text) return false
133
+
134
+ // Find word boundaries - support Hebrew, English, and numbers
135
+ let start = offset
136
+ let end = offset
137
+
138
+ // Define word characters (Hebrew, English, numbers)
139
+ const wordRegex = /[\u0590-\u05FF\u0600-\u06FF\w]/
140
+
141
+ // Move start backwards to find beginning of word
142
+ while (start > 0 && wordRegex.test(text[start - 1])) {
143
+ start--
144
+ }
145
+
146
+ // Move end forwards to find end of word
147
+ while (end < text.length && wordRegex.test(text[end])) {
148
+ end++
149
+ }
150
+
151
+ // If we found a word
152
+ if (start < end) {
153
+ range.setStart(textNode, start)
154
+ range.setEnd(textNode, end)
155
+
156
+ const selection = doc.getSelection()
157
+ if (selection) {
158
+ selection.removeAllRanges()
159
+ selection.addRange(range)
160
+ }
161
+ return true
162
+ }
163
+
164
+ return false
165
+ }
166
+
167
+ export interface InsertImbedModalData {
168
+ src: string
169
+ width: number
170
+ height: number
171
+ allowfullscreen: boolean
172
+ }
78
173
 
79
174
  export function insertEmbed(modal: ModalApi, state: EditorState) {
80
175
  const { range, doc } = state
81
176
  if (!range || !doc) return
82
177
 
83
- modal.showModalForm<InsertImbedModalData
84
- >({
85
- title: 'Insert Embed',
178
+ modal.showModalForm<InsertImbedModalData>({
179
+ title: 'Embed iframe',
86
180
  schema: [
87
- { id: 'url', $el: 'text', label: 'URL', attrs: { placeholder: 'Enter URL (YouTube, Vimeo, etc.)' } },
181
+ { id: 'src', $el: 'text', label: 'Source URL' },
88
182
  frm.frmRow(
89
- frm.numField('width', 'Width', { min: 200, placeholder: '560' }),
90
- frm.numField('height', 'Height', { min: 200, placeholder: '315' })
183
+ frm.numField('width', 'Width', { min: 1 }),
184
+ frm.numField('height', 'Height', { min: 1 }),
91
185
  ),
92
- { id: 'allowFullscreen', $el: 'check', label: 'Allow Fullscreen', attrs: { value: true } },
186
+ { id: 'allowfullscreen', $el: 'check', label: 'Allow Fullscreen' },
93
187
  ],
94
- onSubmit: (data: { url: string, width?: number, height?: number, allowFullscreen?: boolean }) => {
95
- if (!data.url) return
96
-
97
- // Convert common video URLs to embed URLs
98
- const url = new URL(data.url)
99
- let embedUrl = data.url
100
-
101
- // YouTube
102
- if (url.hostname.includes('youtube.com') || url.hostname === 'youtu.be') {
103
- const videoId = url.hostname === 'youtu.be'
104
- ? url.pathname.slice(1)
105
- : url.searchParams.get('v')
106
- if (videoId) {
107
- embedUrl = `https://www.youtube.com/embed/${videoId}`
108
- }
109
- }
110
- // Vimeo
111
- else if (url.hostname.includes('vimeo.com')) {
112
- const videoId = url.pathname.split('/').pop()
113
- if (videoId) {
114
- embedUrl = `https://player.vimeo.com/video/${videoId}`
115
- }
116
- }
188
+ onSubmit: (data: InsertImbedModalData) => {
189
+ if (data.src) {
190
+ const iframe = doc.createElement('iframe')
191
+ Object.assign(iframe, {
192
+ src: data.src,
193
+ width: data.width || 560,
194
+ height: data.height || 315,
195
+ allowfullscreen: data.allowfullscreen || false,
196
+ frameborder: '0'
197
+ })
117
198
 
118
- const iframe = doc.createElement('iframe')
119
- Object.assign(iframe, {
120
- src: embedUrl,
121
- width: data.width || 560,
122
- height: data.height || 315,
123
- frameBorder: '0',
124
- allow: 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture',
125
- allowFullscreen: data.allowFullscreen
126
- })
127
-
128
- // Create a wrapper div for proper alignment and spacing
129
- const wrapper = doc.createElement('div')
130
- wrapper.style.textAlign = 'center'
131
- wrapper.style.margin = '1em 0'
132
- wrapper.appendChild(iframe)
133
-
134
- range.deleteContents()
135
- range.insertNode(wrapper)
136
- state.content = doc.body.innerHTML
199
+ range.collapse(false)
200
+ range.insertNode(iframe)
201
+ state.content = doc.body.innerHTML
202
+ }
137
203
  }
138
204
  })
139
205
  }
@@ -25,26 +25,23 @@ export function isStyleActive(style: string, doc: Document) {
25
25
 
26
26
  // Define style checkers for different formatting types
27
27
  const styleCheckers: { [key: string]: (el: Element) => boolean } = {
28
- // Text formatting - check for elements or inline styles
28
+ // Text formatting - check for elements only, not CSS styles
29
29
  bold: (el) => {
30
30
  const tagName = el.tagName?.toLowerCase()
31
- if (tagName === 'strong' || tagName === 'b') return true
32
- const styles = window.getComputedStyle(el)
33
- return styles.fontWeight === 'bold' || styles.fontWeight === '700'
31
+ // Only consider <b> and <strong> tags, not CSS bold styling
32
+ return tagName === 'strong' || tagName === 'b'
34
33
  },
35
34
 
36
35
  italic: (el) => {
37
36
  const tagName = el.tagName?.toLowerCase()
38
- if (tagName === 'em' || tagName === 'i') return true
39
- const styles = window.getComputedStyle(el)
40
- return styles.fontStyle === 'italic'
37
+ // Only consider <i> and <em> tags, not CSS italic styling
38
+ return tagName === 'em' || tagName === 'i'
41
39
  },
42
40
 
43
41
  underline: (el) => {
44
42
  const tagName = el.tagName?.toLowerCase()
45
- if (tagName === 'u') return true
46
- const styles = window.getComputedStyle(el)
47
- return styles.textDecoration.includes('underline')
43
+ // Only consider <u> tag, not CSS underline styling
44
+ return tagName === 'u'
48
45
  },
49
46
 
50
47
  // Block elements
@@ -66,6 +63,38 @@ export function isStyleActive(style: string, doc: Document) {
66
63
  unorderedList: (el) => {
67
64
  // Check if we're inside an unordered list
68
65
  return !!el.closest('ul')
66
+ },
67
+
68
+ // Text alignment
69
+ alignLeft: (el) => {
70
+ const paragraph = el.closest('p, h1, h2, h3, h4, h5, h6')
71
+ return (paragraph as HTMLElement)?.style.textAlign === 'start'
72
+ },
73
+ alignCenter: (el) => {
74
+ const paragraph = el.closest('p, h1, h2, h3, h4, h5, h6')
75
+ return (paragraph as HTMLElement)?.style.textAlign === 'center'
76
+ },
77
+ alignRight: (el) => {
78
+ const paragraph = el.closest('p, h1, h2, h3, h4, h5, h6')
79
+ return (paragraph as HTMLElement)?.style.textAlign === 'end'
80
+ },
81
+ alignJustify: (el) => {
82
+ const paragraph = el.closest('p, h1, h2, h3, h4, h5, h6')
83
+ return (paragraph as HTMLElement)?.style.textAlign === 'justify'
84
+ },
85
+
86
+ // Text direction
87
+ textDirection: (el) => {
88
+ const paragraph = el.closest('p, h1, h2, h3, h4, h5, h6')
89
+ return (paragraph as HTMLElement)?.dir === 'rtl'
90
+ },
91
+ ltrDirection: (el) => {
92
+ const paragraph = el.closest('p, h1, h2, h3, h4, h5, h6')
93
+ return (paragraph as HTMLElement)?.dir === 'ltr'
94
+ },
95
+ rtlDirection: (el) => {
96
+ const paragraph = el.closest('p, h1, h2, h3, h4, h5, h6')
97
+ return (paragraph as HTMLElement)?.dir === 'rtl'
69
98
  }
70
99
  }
71
100
 
@@ -141,7 +170,7 @@ export function restoreSelection(
141
170
  try {
142
171
  range.setStart(info.originalStart, info.originalStartOffset)
143
172
  range.setEnd(info.originalEnd, info.originalEndOffset)
144
- } catch (e) {
173
+ } catch {
145
174
  if (fallbackNode) {
146
175
  range.selectNodeContents(fallbackNode)
147
176
  }
@@ -209,7 +209,7 @@ export function deleteColumn(range: Range) {
209
209
  }
210
210
  }
211
211
 
212
- export function alignColumn(range: Range, alignment: 'left' | 'center' | 'right' | 'justify') {
212
+ export function alignColumn(range: Range, alignment: 'start' | 'center' | 'end' | 'justify') {
213
213
  const cell = range.startContainer.parentElement?.closest('td')
214
214
  if (!cell) return
215
215
 
@@ -2,6 +2,7 @@ export { default as Accordion } from './Accordion.vue'
2
2
  export { default as AccordionItem } from './AccordionItem.vue'
3
3
  export { default as AddressSearch } from './AddressSearch.vue'
4
4
  export { default as Alert } from './Alert.vue'
5
+ export * from './analytics'
5
6
  export { default as Avatar } from './Avatar.vue'
6
7
  export { default as Badge } from './Badge.vue'
7
8
  export { default as BglVideo } from './BglVideo.vue'
@@ -1,5 +1,5 @@
1
1
  <script lang="ts" setup>
2
- import { inject } from 'vue'
2
+ import { inject, computed, onMounted, ref } from 'vue'
3
3
  import { Btn, PageTitle } from '@bagelink/vue'
4
4
 
5
5
  interface Props {
@@ -25,22 +25,30 @@ const menuState = inject('menuState') as {
25
25
  }
26
26
 
27
27
  // Inject sidebar card style state
28
- const sidebarCardStyle = inject('sidebarCardStyle', { value: true })
28
+ const sidebarCardStyle = inject('sidebarCardStyle', { value: false })
29
+
30
+ // Computed property to check if sidebar has card style
31
+ const hasSidebarCard = computed(() => {
32
+ // Check if there's an AppSidebar with card class in the DOM
33
+ const sidebar = document.querySelector('.app-sidebar .card')
34
+ return sidebar !== null || sidebarCardStyle?.value
35
+ })
29
36
  </script>
30
37
 
31
38
  <template>
32
39
  <div class="app-content h-100p flex column" :class="{
33
- 'paddingAppContent': sidebarCardStyle?.value,
40
+ 'paddingAppContent': hasSidebarCard,
34
41
  }">
35
42
  <!-- Header -->
36
- <header class="app-header flex align-items-center space-between py-1 min-h60px w-100p m_flex-wrap" :class="{
43
+ <header class="app-header flex align-items-center space-between py-1 m_pt-025 m_pb-05 min-h60px w-100p m_flex-wrap" :class="{
37
44
  'border-bottom': border,
38
- 'px-1': !sidebarCardStyle?.value,
39
- }">
45
+ 'px-1': !hasSidebarCard,
46
+ 'm_px-1': hasSidebarCard
47
+ }">
40
48
  <!-- Left Side -->
41
- <div class="flex align-items-center gap-col-075 m_flex-wrap">
49
+ <div class="flex align-items-center gap-col-075 m_flex-wrap m_pe-075">
42
50
  <!-- Menu Toggle Button -->
43
- <Btn v-if="showMenuButton" flat icon="dock_to_right" @click="menuState.toggleMenu" />
51
+ <Btn v-if="showMenuButton" flat icon="dock_to_right" class="menuToggleButton" @click="menuState.toggleMenu" />
44
52
 
45
53
  <!-- Back Button -->
46
54
  <Btn v-if="showBackButton" flat icon="arrow_back" :to="backTo" class="back-btn" />
@@ -60,21 +68,21 @@ const sidebarCardStyle = inject('sidebarCardStyle', { value: true })
60
68
  </div>
61
69
 
62
70
  <!-- Right Side -->
63
- <div class="flex align-items-center gap-row-05 m_flex-grow-1">
71
+ <div class="flex align-items-center gap-row-05 m_flex-grow-1 endNavTools">
64
72
  <slot name="header-right" />
65
73
  </div>
66
74
  </header>
67
75
 
68
76
  <!-- Page Content -->
69
- <main class="pageContent flex-grow overflow py-1 w-100p m_p-05 m_scrollbar-gutter-stable-both m_vw100" :class="{
70
- 'px-1': !sidebarCardStyle?.value,
77
+ <main class="pageContent flex-grow overflow pt-1 pb-05 w-100p m_p-05 m_scrollbar-gutter-stable-both m_vw100" :class="{
78
+ 'px-1': !hasSidebarCard,
71
79
  }">
72
80
  <slot name="content">
73
81
  <!-- Default slot for content without explicit template -->
74
82
  <slot />
75
83
  </slot>
76
84
  </main>
77
- </div>
85
+ </div>
78
86
  </template>
79
87
 
80
88
  <style>
@@ -98,27 +106,19 @@ const sidebarCardStyle = inject('sidebarCardStyle', { value: true })
98
106
  </style>
99
107
 
100
108
  <style scoped>
101
- .paddingAppContent {
102
- padding-inline-start: 0.5rem;
103
- padding-inline-end: 1rem;
104
- }
105
-
106
- body:has(.sidebar-collapsed) .paddingAppContent {
107
- padding-inline-start: 1.5rem;
108
- }
109
109
  .app-content {
110
- height: 100vh;
110
+ height: 100vh;
111
111
  }
112
-
112
+
113
113
  .app-header {
114
- flex-shrink: 0;
114
+ flex-shrink: 0;
115
115
  }
116
-
116
+
117
117
  main {
118
- min-height: 0; /* חשוב לאפשר overflow נכון */
118
+ min-height: 0;
119
119
  }
120
120
  @media screen and (max-width: 910px) {
121
- .paddingAppContent {
121
+ .app-header {
122
122
  padding-inline: 0.5rem;
123
123
  }
124
124
  }
@@ -38,11 +38,11 @@ function closeOnMobile() {
38
38
  // Computed styles for main content margin
39
39
  const mainContentStyles = computed(() => {
40
40
  if (isMobile.value) {
41
- return { marginLeft: '0' }
41
+ return { marginInlineStart: '0' }
42
42
  }
43
43
  const collapsedWidth = props.sidebarCardStyle ? '82px' : '66px'
44
44
  return {
45
- marginLeft: isOpen.value ? props.sidebarWidth : collapsedWidth
45
+ marginInlineStart: isOpen.value ? props.sidebarWidth : collapsedWidth
46
46
  }
47
47
  })
48
48
 
@@ -56,6 +56,9 @@ provide('menuState', {
56
56
  sidebarCollapsedWidth: props.sidebarCardStyle ? '82px' : '66px'
57
57
  })
58
58
 
59
+ // Provide sidebar card style based on prop
60
+ provide('sidebarCardStyle', { value: props.sidebarCardStyle })
61
+
59
62
  // Initialize
60
63
  onMounted(() => {
61
64
  checkMobile()
@@ -101,6 +104,14 @@ onUnmounted(() => {
101
104
  height: 100vh;
102
105
  overflow: hidden;
103
106
  }
107
+ .app-layout:has(.cardWrapSide) {
108
+ padding-inline-start: 0.5rem;
109
+ padding-inline-end: 1rem;
110
+ }
111
+
112
+ .app-layout:has(.sidebar-collapsed .cardWrapSide) {
113
+ padding-inline-start: 1.5rem;
114
+ }
104
115
 
105
116
  /* Overlay for mobile */
106
117
  .overlay {
@@ -114,11 +125,18 @@ onUnmounted(() => {
114
125
  .main-content {
115
126
  flex: 1;
116
127
  overflow: hidden;
117
- transition: margin-left 400ms ease;
128
+ transition: margin-inline-start 400ms ease;
118
129
  min-height: 100vh
119
130
  }
120
131
 
121
132
  .page-content {
122
133
  overflow: auto;
123
134
  }
135
+ @media screen and (max-width: 910px) {
136
+ .app-layout:has(.cardWrapSide),
137
+ .app-layout:has(.sidebar-collapsed .cardWrapSide) {
138
+ padding-inline-start: 0rem;
139
+ padding-inline-end: 0rem;
140
+ }
141
+ }
124
142
  </style>
@@ -65,7 +65,7 @@ const sidebarStyles = computed(() => {
65
65
  let width = '280px'
66
66
 
67
67
  if (!menuState.isMobile.value) {
68
- const collapsedWidth = props.card ? '82px' : '76px'
68
+ const collapsedWidth = props.card ? '82px' : '68px'
69
69
  width = menuState.isOpen.value ? menuState.sidebarWidth : collapsedWidth
70
70
  }
71
71
 
@@ -100,7 +100,7 @@ function logout() {
100
100
  ...(props.card && { borderRadius: 'var(--card-border-radius)' }),
101
101
  }"
102
102
  :class="{
103
- card: props.card,
103
+ 'card cardWrapSide': props.card,
104
104
  'ps-05': !menuState.isOpen.value,
105
105
  'scrollbar-gutter-both': menuState.isOpen.value,
106
106
  aside_frame: props.frame,
@@ -267,5 +267,8 @@ function logout() {
267
267
  .sidebar-mobile-closed {
268
268
  transform: translateX(-100%);
269
269
  }
270
+ [dir="rtl"] .sidebar-mobile-closed {
271
+ transform: translateX(100%);
272
+ }
270
273
  }
271
274
  </style>