@bagelink/vue 1.4.101 → 1.4.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/vue",
3
3
  "type": "module",
4
- "version": "1.4.101",
4
+ "version": "1.4.105",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Neveh Allon",
@@ -65,14 +65,12 @@ function toggle() {
65
65
  <template>
66
66
  <div class="accordion-item txt-start" :class="[{ flat }]">
67
67
  <button
68
- :aria-expanded="isOpen ? 'true' : 'false'"
69
- class="accordion-head"
70
- :aria-controls="`accordion-body-${id}`"
71
- @click="toggle()"
68
+ :aria-expanded="isOpen ? 'true' : 'false'" class="accordion-head"
69
+ :aria-controls="`accordion-body-${id}`" @click="toggle()"
72
70
  >
73
71
  <span
74
- v-if="iconPosition === 'start'"
75
- class="accordion-icon" :class="[iconClass, { open: isOpen && iconType === 'expand_more' }]"
72
+ v-if="iconPosition === 'start'" class="accordion-icon"
73
+ :class="[iconClass, { open: isOpen && iconType === 'expand_more' }]"
76
74
  >
77
75
  <Icon :icon="computedIcon" />
78
76
  </span>
@@ -84,17 +82,15 @@ function toggle() {
84
82
  </slot>
85
83
 
86
84
  <span
87
- v-if="iconPosition === 'end'"
88
- class="accordion-icon" :class="[iconClass, { open: isOpen && iconType === 'expand_more' }]"
85
+ v-if="iconPosition === 'end'" class="accordion-icon"
86
+ :class="[iconClass, { open: isOpen && iconType === 'expand_more' }]"
89
87
  >
90
88
  <Icon :icon="computedIcon" />
91
89
  </span>
92
90
  </button>
93
91
  <Transition name="expand">
94
92
  <div
95
- v-if="isOpen"
96
- :id="`accordion-body-${id}`"
97
- class="accordion-body"
93
+ v-if="isOpen" :id="`accordion-body-${id}`" class="accordion-body"
98
94
  :aria-hidden="isOpen ? 'false' : 'true'"
99
95
  >
100
96
  <slot />
@@ -107,9 +103,9 @@ function toggle() {
107
103
  .accordion-item {
108
104
  border-bottom: 1px solid var(--border-color);
109
105
  transition: all 0.2s;
110
- cursor: pointer;
111
106
  overflow: hidden;
112
107
  }
108
+
113
109
  .accordion-item button {
114
110
  cursor: pointer;
115
111
  }
@@ -130,14 +126,14 @@ function toggle() {
130
126
  display: flex;
131
127
  align-items: center;
132
128
  flex-shrink: 0;
133
- width: calc( var(--btn-height) / 1.5);
134
- height: calc( var(--btn-height) / 1.5);
129
+ width: calc(var(--btn-height) / 1.5);
130
+ height: calc(var(--btn-height) / 1.5);
135
131
  padding-inline: calc(var(--btn-padding) / 3);
136
132
  justify-content: center;
137
133
  }
138
134
 
139
- [aria-expanded="true"] .accordion-icon {
140
- transform: rotate(180deg);
135
+ [aria-expanded="true"] .accordion-icon {
136
+ transform: rotate(180deg);
141
137
  }
142
138
 
143
139
  .accordion-label {
@@ -1,7 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  import type { IconType } from '@bagelink/vue'
3
+ import { useDevice } from '@bagelink/vue'
4
+ import { ref, onMounted, watchEffect } from 'vue'
3
5
  import { FONT_AWESOME_ICONS, MATERIAL_ICONS, FONT_AWESOME_BRANDS_ICONS } from './constants'
4
- import { ref, onMounted, onUnmounted } from 'vue'
5
6
 
6
7
  const props = withDefaults(defineProps<{
7
8
  icon?: IconType
@@ -17,22 +18,9 @@ const props = withDefaults(defineProps<{
17
18
  size: 1
18
19
  })
19
20
 
20
- const iconRender = $computed(() => props.icon || props.name) as IconType
21
+ const iconRender = $computed(() => (props.icon ?? props.name) as IconType)
21
22
 
22
- const isMobile = ref(false)
23
-
24
- const checkMobile = () => {
25
- isMobile.value = window.innerWidth <= 910
26
- }
27
-
28
- onMounted(() => {
29
- checkMobile()
30
- window.addEventListener('resize', checkMobile)
31
- })
32
-
33
- onUnmounted(() => {
34
- window.removeEventListener('resize', checkMobile)
35
- })
23
+ const { isMobile } = useDevice()
36
24
 
37
25
  const computedSize = $computed(() => {
38
26
  if (isMobile.value && props.mobileSize !== undefined) {
@@ -49,36 +37,108 @@ const iconRenderType = $computed(() => {
49
37
  })
50
38
 
51
39
  const isFaBrand = $computed(() => FONT_AWESOME_BRANDS_ICONS.includes(iconRender))
40
+
41
+ // Hide until required font is ready to avoid ligature text/fallback flash
42
+ const isMaterialReady = ref(true)
43
+ const isFAFreeReady = ref(true)
44
+ const isFABrandsReady = ref(true)
45
+
46
+ function getFontFaceSet(): FontFaceSet | null {
47
+ if (typeof document === 'undefined') return null
48
+ const maybeFonts = (document as unknown as { fonts?: FontFaceSet }).fonts
49
+ return maybeFonts ?? null
50
+ }
51
+
52
+ function supportsFontLoading(): boolean {
53
+ const fonts = getFontFaceSet()
54
+ return fonts !== null && typeof fonts.load === 'function'
55
+ }
56
+
57
+ async function ensureFontLoaded(family: string): Promise<boolean> {
58
+ if (!supportsFontLoading()) return true
59
+ try {
60
+ const fonts = getFontFaceSet()
61
+ if (!fonts) return true
62
+ if (fonts.check(`1em "${family}"`)) return true
63
+ await fonts.load(`1em "${family}"`)
64
+ return fonts.check(`1em "${family}"`)
65
+ } catch {
66
+ return false
67
+ }
68
+ }
69
+
70
+ async function loadMaterialIfNeeded() {
71
+ const ok = await ensureFontLoaded('Material Symbols Outlined')
72
+ isMaterialReady.value = ok
73
+ }
74
+
75
+ async function loadFAFreeIfNeeded() {
76
+ const ok = await ensureFontLoaded('Font Awesome 6 Free')
77
+ isFAFreeReady.value = ok
78
+ }
79
+
80
+ async function loadFABrandsIfNeeded() {
81
+ const ok = await ensureFontLoaded('Font Awesome 6 Brands')
82
+ isFABrandsReady.value = ok
83
+ }
84
+
85
+ onMounted(() => {
86
+ if (!supportsFontLoading()) return
87
+ if (iconRenderType === 'material') {
88
+ const fonts = getFontFaceSet()
89
+ isMaterialReady.value = fonts ? fonts.check('1em "Material Symbols Outlined"') : true
90
+ void loadMaterialIfNeeded()
91
+ } else if (isFaBrand) {
92
+ const fonts = getFontFaceSet()
93
+ isFABrandsReady.value = fonts ? fonts.check('1em "Font Awesome 6 Brands"') : true
94
+ void loadFABrandsIfNeeded()
95
+ } else {
96
+ const fonts = getFontFaceSet()
97
+ isFAFreeReady.value = fonts ? fonts.check('1em "Font Awesome 6 Free"') : true
98
+ void loadFAFreeIfNeeded()
99
+ }
100
+ })
101
+
102
+ watchEffect(() => {
103
+ if (!supportsFontLoading()) return
104
+ if (iconRenderType === 'material') {
105
+ void loadMaterialIfNeeded()
106
+ } else if (isFaBrand) {
107
+ void loadFABrandsIfNeeded()
108
+ } else {
109
+ void loadFAFreeIfNeeded()
110
+ }
111
+ })
112
+
113
+ const isCurrentFontReady = $computed(() => {
114
+ if (iconRenderType === 'material') return isMaterialReady.value
115
+ return isFaBrand ? isFABrandsReady.value : isFAFreeReady.value
116
+ })
52
117
  </script>
53
118
 
54
119
  <template>
55
120
  <span
56
- v-if="iconRenderType === 'material'"
57
- class="bgl_icon-font notranslate"
58
- :class="{ 'round flex aspect-ratio-1 justify-content-center': round }"
59
- :style="{
121
+ v-if="iconRenderType === 'material'" class="bgl_icon-font notranslate"
122
+ :class="{ 'round flex aspect-ratio-1 justify-content-center': round }" :style="{
60
123
  'fontSize': `${computedSize}rem`,
61
124
  color,
62
125
  'font-variation-settings': `'FILL' ${fill ? 1 : 0}, 'wght' ${weight || 400}`,
63
- 'width': round ? `calc(${computedSize}rem * 2)` : 'auto',
64
- 'height': round ? `calc(${computedSize}rem * 2)` : 'auto',
65
- }"
66
- translate="no"
126
+ 'width': round ? `calc(${computedSize}rem * 2)` : 'auto',
127
+ 'height': round ? `calc(${computedSize}rem * 2)` : 'auto',
128
+ 'visibility': isCurrentFontReady ? 'visible' : 'hidden',
129
+ }" translate="no"
67
130
  >
68
131
  {{ iconRender }}
69
132
  </span>
70
133
  <span
71
- v-else-if="iconRenderType === 'font-awesome'"
72
- class="fa bgl_icon-font notranslate"
73
- :class="[
134
+ v-else-if="iconRenderType === 'font-awesome'" class="fa bgl_icon-font notranslate" :class="[
74
135
  `fa-${iconRender}`,
75
136
  {
76
137
  'fa-brands': isFaBrand,
77
138
  'fa-solid': fill,
78
139
  'far': !fill && !isFaBrand,
79
140
  },
80
- ]"
81
- :style="{ 'fontSize': `${computedSize}rem`, color, 'font-variation-settings': `'wght' ${weight || 400}` }"
141
+ ]" :style="{ 'fontSize': `${computedSize}rem`, color, 'font-variation-settings': `'wght' ${weight || 400}`, 'visibility': isCurrentFontReady ? 'visible' : 'hidden' }"
82
142
  translate="no"
83
143
  />
84
144
  </template>
@@ -86,6 +146,7 @@ const isFaBrand = $computed(() => FONT_AWESOME_BRANDS_ICONS.includes(iconRender)
86
146
  <style>
87
147
  @import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200');
88
148
  @import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css');
149
+
89
150
  [dir='rtl'] .data-row .bgl_icon-font,
90
151
  [dir='rtl'] .embedded-field .bgl_icon-font,
91
152
  [dir='rtl'] .rich-text-editor .toolbar .bgl_icon-font {