@brftech/filex-core 0.1.56 → 0.1.61

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 (82) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +108 -108
  3. package/dist/{ArchiveViewer-CJdaw5x3.js → ArchiveViewer-CbUUEREI.js} +20 -20
  4. package/dist/ArchiveViewer-CbUUEREI.js.map +1 -0
  5. package/dist/{CsvViewer-QYx_tePQ.js → CsvViewer-72rDsQJX.js} +3 -3
  6. package/dist/CsvViewer-72rDsQJX.js.map +1 -0
  7. package/dist/{DrawioViewer-GWt0TyB7.js → DrawioViewer-DSyMwGvg.js} +7 -7
  8. package/dist/DrawioViewer-DSyMwGvg.js.map +1 -0
  9. package/dist/{EpubViewer-Je0ywklQ.js → EpubViewer-DvDa17d7.js} +3 -3
  10. package/dist/EpubViewer-DvDa17d7.js.map +1 -0
  11. package/dist/{IpynbViewer-TsB4gqP-.js → IpynbViewer-DibIkdsz.js} +3 -3
  12. package/dist/IpynbViewer-DibIkdsz.js.map +1 -0
  13. package/dist/{MermaidViewer-CXfeDS-7.js → MermaidViewer-BoFLtGvl.js} +3 -3
  14. package/dist/MermaidViewer-BoFLtGvl.js.map +1 -0
  15. package/dist/{PsdViewer-VtPpxKVe.js → PsdViewer-K2iZkwax.js} +3 -3
  16. package/dist/PsdViewer-K2iZkwax.js.map +1 -0
  17. package/dist/{TiffViewer-OINn9bbb.js → TiffViewer-JzDzbqpO.js} +3 -3
  18. package/dist/TiffViewer-JzDzbqpO.js.map +1 -0
  19. package/dist/{Viewer3D-xvvyiWHd.js → Viewer3D-WEMYCP8l.js} +7 -7
  20. package/dist/Viewer3D-WEMYCP8l.js.map +1 -0
  21. package/dist/filex-core.js +1 -1
  22. package/dist/filex-core.umd.cjs +4 -4
  23. package/dist/filex-core.umd.cjs.map +1 -1
  24. package/dist/{index-CmSYzt6L.js → index-B5YT-m5Q.js} +209 -209
  25. package/dist/index-B5YT-m5Q.js.map +1 -0
  26. package/dist/style.css +1 -1
  27. package/dist/useViewerFetch-czqbd2Lj.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/components/Breadcrumb.vue +282 -282
  30. package/src/components/ContextMenu.vue +152 -152
  31. package/src/components/GridView.vue +166 -166
  32. package/src/components/ListView.vue +212 -212
  33. package/src/components/PendingOpsTray.vue +91 -91
  34. package/src/components/RecentlyOpened.vue +155 -155
  35. package/src/components/StarButton.vue +101 -101
  36. package/src/components/TagPicker.vue +184 -184
  37. package/src/components/Toolbar.vue +220 -220
  38. package/src/components/UploadProgress.vue +78 -78
  39. package/src/composables/useFileApi.ts +692 -692
  40. package/src/composables/useKeyboardShortcuts.ts +144 -144
  41. package/src/composables/useLocale.ts +60 -60
  42. package/src/composables/useMonacoLoader.ts +98 -98
  43. package/src/composables/usePendingOps.ts +200 -200
  44. package/src/composables/useSelection.ts +81 -81
  45. package/src/composables/useUploadChunked.ts +250 -250
  46. package/src/composables/useViewerFetch.ts +58 -58
  47. package/src/index.ts +80 -80
  48. package/src/locales/en.ts +122 -122
  49. package/src/locales/index.ts +12 -12
  50. package/src/locales/tr.ts +122 -122
  51. package/src/modals/DeleteConfirmModal.vue +32 -32
  52. package/src/modals/Modal.vue +104 -104
  53. package/src/modals/NewFolderModal.vue +64 -64
  54. package/src/modals/PermissionsModal.vue +815 -815
  55. package/src/modals/PreviewModal.vue +1070 -1070
  56. package/src/modals/RenameModal.vue +58 -58
  57. package/src/modals/ShareModal.vue +135 -135
  58. package/src/styles/base.css +1224 -1224
  59. package/src/styles/variables.css +90 -90
  60. package/src/types/FileNode.ts +136 -136
  61. package/src/types/index.ts +27 -27
  62. package/src/types/peers.d.ts +45 -45
  63. package/src/viewers/ArchiveViewer.vue +186 -186
  64. package/src/viewers/CsvViewer.vue +319 -319
  65. package/src/viewers/DrawioViewer.vue +243 -243
  66. package/src/viewers/EpubViewer.vue +307 -307
  67. package/src/viewers/IpynbViewer.vue +365 -365
  68. package/src/viewers/MermaidViewer.vue +259 -259
  69. package/src/viewers/PdfViewer.vue +562 -562
  70. package/src/viewers/PsdViewer.vue +264 -264
  71. package/src/viewers/TiffViewer.vue +273 -273
  72. package/src/viewers/Viewer3D.vue +120 -120
  73. package/dist/ArchiveViewer-CJdaw5x3.js.map +0 -1
  74. package/dist/CsvViewer-QYx_tePQ.js.map +0 -1
  75. package/dist/DrawioViewer-GWt0TyB7.js.map +0 -1
  76. package/dist/EpubViewer-Je0ywklQ.js.map +0 -1
  77. package/dist/IpynbViewer-TsB4gqP-.js.map +0 -1
  78. package/dist/MermaidViewer-CXfeDS-7.js.map +0 -1
  79. package/dist/PsdViewer-VtPpxKVe.js.map +0 -1
  80. package/dist/TiffViewer-OINn9bbb.js.map +0 -1
  81. package/dist/Viewer3D-xvvyiWHd.js.map +0 -1
  82. package/dist/index-CmSYzt6L.js.map +0 -1
package/src/locales/tr.ts CHANGED
@@ -1,122 +1,122 @@
1
- /** Turkish UI strings. */
2
- export const tr: Record<string, string> = {
3
- 'toolbar.new_folder': 'Yeni Klasör',
4
- 'toolbar.upload': 'Yükle',
5
- 'toolbar.search': 'Ara',
6
- 'toolbar.search.placeholder': 'Dosya adı…',
7
- 'toolbar.view.list': 'Liste',
8
- 'toolbar.view.grid': 'Izgara',
9
- 'toolbar.refresh': 'Yenile',
10
- 'toolbar.go_up': 'Üst Klasör',
11
-
12
- 'ctx.open': 'Aç',
13
- 'ctx.preview': 'Önizle',
14
- 'ctx.download': 'İndir',
15
- 'ctx.convert': 'Dönüştür',
16
- 'ctx.rename': 'Yeniden Adlandır',
17
- 'ctx.delete': 'Sil',
18
- 'ctx.delete_perm': 'Kalıcı Olarak Sil',
19
- 'ctx.restore': 'Geri Getir',
20
- 'ctx.share': 'Paylaş',
21
- 'ctx.move': 'Taşı',
22
- 'ctx.copy': 'Kopyala',
23
- 'ctx.cut': 'Kes',
24
- 'ctx.paste': 'Yapıştır',
25
- 'ctx.info': 'Bilgi',
26
- 'ctx.duplicate': 'Kopyasını Oluştur',
27
- 'ctx.new_file': 'Yeni Dosya',
28
-
29
- 'modal.delete.title': 'Silinsin mi?',
30
- 'modal.delete.message': '{count} öğe çöpe atılacak.',
31
- 'modal.delete.confirm': 'Çöpe At',
32
- 'modal.delete.cancel': 'Vazgeç',
33
-
34
- 'modal.rename.title': 'Yeniden Adlandır',
35
- 'modal.rename.save': 'Kaydet',
36
- 'modal.rename.cancel': 'Vazgeç',
37
-
38
- 'modal.newfolder.title': 'Yeni Klasör',
39
- 'modal.newfolder.placeholder': 'Klasör adı',
40
- 'modal.newfolder.create': 'Oluştur',
41
- 'modal.newfolder.cancel': 'Vazgeç',
42
-
43
- 'modal.share.title': 'Paylaşım Linki',
44
- 'modal.share.pin': 'PIN koruması',
45
- 'modal.share.expires': 'Son kullanma',
46
- 'modal.share.max_downloads': 'İndirme limiti',
47
- 'modal.share.create': 'Paylaş',
48
- 'modal.share.cancel': 'Vazgeç',
49
- 'modal.share.copy': 'Kopyala',
50
- 'modal.share.url_copied': 'Link kopyalandı',
51
- 'modal.share.pin_copied': 'PIN kopyalandı',
52
- 'modal.share.cli_copied': 'CLI komutu kopyalandı',
53
-
54
- 'upload.progress': 'Yükleniyor',
55
- 'upload.done': 'Tamamlandı',
56
- 'upload.error': 'Hata',
57
- 'upload.aborted': 'İptal edildi',
58
- 'upload.cancel': 'İptal',
59
-
60
- 'empty.folder': 'Bu klasör boş',
61
- 'empty.search': 'Sonuç bulunamadı',
62
- 'loading': 'Yükleniyor…',
63
-
64
- 'col.name': 'Ad',
65
- 'col.size': 'Boyut',
66
- 'col.modified': 'Tarih',
67
- 'col.type': 'Tür',
68
-
69
- 'unit.bytes': 'B',
70
- 'unit.kb': 'KB',
71
- 'unit.mb': 'MB',
72
- 'unit.gb': 'GB',
73
- 'unit.tb': 'TB',
74
-
75
- 'breadcrumb.root': 'Kök',
76
- 'breadcrumb.copy_path': 'Yolu Kopyala',
77
- 'breadcrumb.go_to_path': 'Yola Git',
78
- 'breadcrumb.path_placeholder': 'fileman/foo/bar',
79
-
80
- 'ops.copy': 'Kopyalanıyor',
81
- 'ops.move': 'Taşınıyor',
82
- 'ops.delete': 'Siliniyor',
83
- 'ops.queued': 'sırada',
84
- 'ops.done': 'tamamlandı',
85
- 'ops.error': 'hata',
86
-
87
- 'node.trash': 'Çöp Kutusu',
88
-
89
- // ——— Zengin önizleyici chrome ———
90
- 'viewer.open_in_new_tab': 'Yeni Sekmede Aç',
91
- 'viewer.loading': 'Yükleniyor…',
92
- 'viewer.failed_to_load': 'Dosya yüklenemedi',
93
- 'viewer.peer_not_installed': 'Bu dosya tipi için opsiyonel kütüphane kurulu değil. Peer dependency yükle veya İndir düğmesini kullan.',
94
- 'viewer.format_unsupported_3d': 'Bu 3B format tarayıcı önizlemesinde desteklenmiyor (model-viewer yalnızca .glb / .gltf / .usdz okur). Lütfen indirip yerel bir araçta açın.',
95
- 'viewer.zoom_in': 'Yakınlaştır',
96
- 'viewer.zoom_out': 'Uzaklaştır',
97
- 'viewer.fit_width': 'Sayfa genişliğine sığdır',
98
- 'viewer.next_page': 'Sonraki sayfa',
99
- 'viewer.prev_page': 'Önceki sayfa',
100
- 'viewer.page_n_of_m': 'Sayfa {n} / {m}',
101
- 'viewer.tags': 'Etiketler',
102
- 'viewer.layers': 'Katmanlar',
103
- 'viewer.toc': 'İçindekiler',
104
- 'viewer.font_smaller': 'Yazıyı küçült',
105
- 'viewer.font_larger': 'Yazıyı büyüt',
106
- 'viewer.search': 'Ara',
107
- 'viewer.download': 'İndir',
108
- 'viewer.save_annotations': 'Açıklamaları kaydet',
109
- 'viewer.annotations_on': 'Açıklamalar: açık',
110
- 'viewer.annotations_off': 'Açıklamalar: kapalı',
111
- 'viewer.read_only': 'Salt okunur',
112
- 'viewer.csv_first_row_header': 'İlk satır başlık olsun',
113
- 'viewer.csv_filter': 'Satır filtrele…',
114
- 'viewer.epub_prev': 'Önceki',
115
- 'viewer.epub_next': 'Sonraki',
116
- 'viewer.drawio.disabled': 'diagrams.net editörü bu filex kurulumunda yapılandırılmamış — yönetici FILEX_DRAWIO_URL\'yi ayarlamalı.',
117
- 'viewer.archive.entries': '{n} dosya',
118
- 'viewer.archive.empty': 'Arşiv boş.',
119
- 'viewer.archive.error': 'Arşiv okunamadı.',
120
- 'viewer.name': 'Ad',
121
- 'viewer.size': 'Boyut',
122
- };
1
+ /** Turkish UI strings. */
2
+ export const tr: Record<string, string> = {
3
+ 'toolbar.new_folder': 'Yeni Klasör',
4
+ 'toolbar.upload': 'Yükle',
5
+ 'toolbar.search': 'Ara',
6
+ 'toolbar.search.placeholder': 'Dosya adı…',
7
+ 'toolbar.view.list': 'Liste',
8
+ 'toolbar.view.grid': 'Izgara',
9
+ 'toolbar.refresh': 'Yenile',
10
+ 'toolbar.go_up': 'Üst Klasör',
11
+
12
+ 'ctx.open': 'Aç',
13
+ 'ctx.preview': 'Önizle',
14
+ 'ctx.download': 'İndir',
15
+ 'ctx.convert': 'Dönüştür',
16
+ 'ctx.rename': 'Yeniden Adlandır',
17
+ 'ctx.delete': 'Sil',
18
+ 'ctx.delete_perm': 'Kalıcı Olarak Sil',
19
+ 'ctx.restore': 'Geri Getir',
20
+ 'ctx.share': 'Paylaş',
21
+ 'ctx.move': 'Taşı',
22
+ 'ctx.copy': 'Kopyala',
23
+ 'ctx.cut': 'Kes',
24
+ 'ctx.paste': 'Yapıştır',
25
+ 'ctx.info': 'Bilgi',
26
+ 'ctx.duplicate': 'Kopyasını Oluştur',
27
+ 'ctx.new_file': 'Yeni Dosya',
28
+
29
+ 'modal.delete.title': 'Silinsin mi?',
30
+ 'modal.delete.message': '{count} öğe çöpe atılacak.',
31
+ 'modal.delete.confirm': 'Çöpe At',
32
+ 'modal.delete.cancel': 'Vazgeç',
33
+
34
+ 'modal.rename.title': 'Yeniden Adlandır',
35
+ 'modal.rename.save': 'Kaydet',
36
+ 'modal.rename.cancel': 'Vazgeç',
37
+
38
+ 'modal.newfolder.title': 'Yeni Klasör',
39
+ 'modal.newfolder.placeholder': 'Klasör adı',
40
+ 'modal.newfolder.create': 'Oluştur',
41
+ 'modal.newfolder.cancel': 'Vazgeç',
42
+
43
+ 'modal.share.title': 'Paylaşım Linki',
44
+ 'modal.share.pin': 'PIN koruması',
45
+ 'modal.share.expires': 'Son kullanma',
46
+ 'modal.share.max_downloads': 'İndirme limiti',
47
+ 'modal.share.create': 'Paylaş',
48
+ 'modal.share.cancel': 'Vazgeç',
49
+ 'modal.share.copy': 'Kopyala',
50
+ 'modal.share.url_copied': 'Link kopyalandı',
51
+ 'modal.share.pin_copied': 'PIN kopyalandı',
52
+ 'modal.share.cli_copied': 'CLI komutu kopyalandı',
53
+
54
+ 'upload.progress': 'Yükleniyor',
55
+ 'upload.done': 'Tamamlandı',
56
+ 'upload.error': 'Hata',
57
+ 'upload.aborted': 'İptal edildi',
58
+ 'upload.cancel': 'İptal',
59
+
60
+ 'empty.folder': 'Bu klasör boş',
61
+ 'empty.search': 'Sonuç bulunamadı',
62
+ 'loading': 'Yükleniyor…',
63
+
64
+ 'col.name': 'Ad',
65
+ 'col.size': 'Boyut',
66
+ 'col.modified': 'Tarih',
67
+ 'col.type': 'Tür',
68
+
69
+ 'unit.bytes': 'B',
70
+ 'unit.kb': 'KB',
71
+ 'unit.mb': 'MB',
72
+ 'unit.gb': 'GB',
73
+ 'unit.tb': 'TB',
74
+
75
+ 'breadcrumb.root': 'Kök',
76
+ 'breadcrumb.copy_path': 'Yolu Kopyala',
77
+ 'breadcrumb.go_to_path': 'Yola Git',
78
+ 'breadcrumb.path_placeholder': 'fileman/foo/bar',
79
+
80
+ 'ops.copy': 'Kopyalanıyor',
81
+ 'ops.move': 'Taşınıyor',
82
+ 'ops.delete': 'Siliniyor',
83
+ 'ops.queued': 'sırada',
84
+ 'ops.done': 'tamamlandı',
85
+ 'ops.error': 'hata',
86
+
87
+ 'node.trash': 'Çöp Kutusu',
88
+
89
+ // ——— Zengin önizleyici chrome ———
90
+ 'viewer.open_in_new_tab': 'Yeni Sekmede Aç',
91
+ 'viewer.loading': 'Yükleniyor…',
92
+ 'viewer.failed_to_load': 'Dosya yüklenemedi',
93
+ 'viewer.peer_not_installed': 'Bu dosya tipi için opsiyonel kütüphane kurulu değil. Peer dependency yükle veya İndir düğmesini kullan.',
94
+ 'viewer.format_unsupported_3d': 'Bu 3B format tarayıcı önizlemesinde desteklenmiyor (model-viewer yalnızca .glb / .gltf / .usdz okur). Lütfen indirip yerel bir araçta açın.',
95
+ 'viewer.zoom_in': 'Yakınlaştır',
96
+ 'viewer.zoom_out': 'Uzaklaştır',
97
+ 'viewer.fit_width': 'Sayfa genişliğine sığdır',
98
+ 'viewer.next_page': 'Sonraki sayfa',
99
+ 'viewer.prev_page': 'Önceki sayfa',
100
+ 'viewer.page_n_of_m': 'Sayfa {n} / {m}',
101
+ 'viewer.tags': 'Etiketler',
102
+ 'viewer.layers': 'Katmanlar',
103
+ 'viewer.toc': 'İçindekiler',
104
+ 'viewer.font_smaller': 'Yazıyı küçült',
105
+ 'viewer.font_larger': 'Yazıyı büyüt',
106
+ 'viewer.search': 'Ara',
107
+ 'viewer.download': 'İndir',
108
+ 'viewer.save_annotations': 'Açıklamaları kaydet',
109
+ 'viewer.annotations_on': 'Açıklamalar: açık',
110
+ 'viewer.annotations_off': 'Açıklamalar: kapalı',
111
+ 'viewer.read_only': 'Salt okunur',
112
+ 'viewer.csv_first_row_header': 'İlk satır başlık olsun',
113
+ 'viewer.csv_filter': 'Satır filtrele…',
114
+ 'viewer.epub_prev': 'Önceki',
115
+ 'viewer.epub_next': 'Sonraki',
116
+ 'viewer.drawio.disabled': 'diagrams.net editörü bu filex kurulumunda yapılandırılmamış — yönetici FILEX_DRAWIO_URL\'yi ayarlamalı.',
117
+ 'viewer.archive.entries': '{n} dosya',
118
+ 'viewer.archive.empty': 'Arşiv boş.',
119
+ 'viewer.archive.error': 'Arşiv okunamadı.',
120
+ 'viewer.name': 'Ad',
121
+ 'viewer.size': 'Boyut',
122
+ };
@@ -1,32 +1,32 @@
1
- <script setup lang="ts">
2
- import type { LocaleCode } from '../types/ExplorerConfig';
3
- import { useLocale } from '../composables/useLocale';
4
- import Modal from './Modal.vue';
5
-
6
- const props = defineProps<{
7
- open: boolean;
8
- locale: LocaleCode;
9
- count: number;
10
- }>();
11
-
12
- const emit = defineEmits<{
13
- (e: 'close'): void;
14
- (e: 'confirm'): void;
15
- }>();
16
-
17
- const { t } = useLocale(() => props.locale);
18
- </script>
19
-
20
- <template>
21
- <Modal :open="open" :title="t('modal.delete.title')" size="sm" @close="emit('close')">
22
- <p>{{ t('modal.delete.message', { count }) }}</p>
23
- <template #actions>
24
- <button type="button" class="fe-btn" @click="emit('close')">
25
- {{ t('modal.delete.cancel') }}
26
- </button>
27
- <button type="button" class="fe-btn fe-btn--danger" @click="emit('confirm')">
28
- {{ t('modal.delete.confirm') }}
29
- </button>
30
- </template>
31
- </Modal>
32
- </template>
1
+ <script setup lang="ts">
2
+ import type { LocaleCode } from '../types/ExplorerConfig';
3
+ import { useLocale } from '../composables/useLocale';
4
+ import Modal from './Modal.vue';
5
+
6
+ const props = defineProps<{
7
+ open: boolean;
8
+ locale: LocaleCode;
9
+ count: number;
10
+ }>();
11
+
12
+ const emit = defineEmits<{
13
+ (e: 'close'): void;
14
+ (e: 'confirm'): void;
15
+ }>();
16
+
17
+ const { t } = useLocale(() => props.locale);
18
+ </script>
19
+
20
+ <template>
21
+ <Modal :open="open" :title="t('modal.delete.title')" size="sm" @close="emit('close')">
22
+ <p>{{ t('modal.delete.message', { count }) }}</p>
23
+ <template #actions>
24
+ <button type="button" class="fe-btn" @click="emit('close')">
25
+ {{ t('modal.delete.cancel') }}
26
+ </button>
27
+ <button type="button" class="fe-btn fe-btn--danger" @click="emit('confirm')">
28
+ {{ t('modal.delete.confirm') }}
29
+ </button>
30
+ </template>
31
+ </Modal>
32
+ </template>
@@ -1,104 +1,104 @@
1
- <script setup lang="ts">
2
- /**
3
- * Modal — tiny headless wrapper. Backdrop + centered card + ESC + autofocus.
4
- */
5
- import { watch, onBeforeUnmount, ref } from 'vue';
6
-
7
- const props = defineProps<{
8
- open: boolean;
9
- title?: string;
10
- size?: 'sm' | 'md' | 'lg' | 'xl';
11
- closeOnBackdrop?: boolean;
12
- /** When true, drop the dialog chrome (backdrop tint, header, footer,
13
- * centered card with border-radius) and render the slot full-bleed.
14
- * Used by the standalone /files/edit route where the browser tab IS
15
- * the container — a modal frame on top of it just steals real estate
16
- * from the editor. ESC + emit('close') still wire through so the
17
- * parent route can window.close() the tab. */
18
- chromeless?: boolean;
19
- /** Explicit theme. When set the modal stamps the appropriate
20
- * `.fe--theme-{light,dark}` class on its `.fe` backdrop so the CSS
21
- * variable cascade matches the host shell regardless of OS
22
- * preference (Modal is a portal-shaped descendant of `<body>`
23
- * rather than a child of the parent component, so plain DOM
24
- * inheritance isn't always enough). */
25
- theme?: 'light' | 'dark' | 'auto';
26
- }>();
27
-
28
- const emit = defineEmits<{
29
- (e: 'close'): void;
30
- }>();
31
-
32
- const cardEl = ref<HTMLElement | null>(null);
33
-
34
- watch(
35
- () => props.open,
36
- (v) => {
37
- if (v) {
38
- document.addEventListener('keydown', onKey);
39
- setTimeout(() => {
40
- const focusable = cardEl.value?.querySelector<HTMLElement>(
41
- 'input,select,textarea,button,[tabindex]:not([tabindex="-1"])',
42
- );
43
- focusable?.focus();
44
- }, 30);
45
- } else {
46
- document.removeEventListener('keydown', onKey);
47
- }
48
- },
49
- );
50
-
51
- onBeforeUnmount(() => document.removeEventListener('keydown', onKey));
52
-
53
- function onKey(e: KeyboardEvent) {
54
- if (e.key === 'Escape') emit('close');
55
- }
56
-
57
- function onBackdrop() {
58
- if (props.closeOnBackdrop !== false) emit('close');
59
- }
60
- </script>
61
-
62
- <template>
63
- <transition name="fe-modal">
64
- <div
65
- v-if="open"
66
- class="fe fe-modal__backdrop"
67
- :class="[
68
- { 'fe-modal__backdrop--chromeless': chromeless },
69
- theme === 'light' ? 'fe--theme-light' : '',
70
- theme === 'dark' ? 'fe--theme-dark' : '',
71
- ]"
72
- role="presentation"
73
- @click="onBackdrop"
74
- >
75
- <div
76
- ref="cardEl"
77
- class="fe-modal__card"
78
- :class="[
79
- `fe-modal__card--${size || 'md'}`,
80
- chromeless && 'fe-modal__card--chromeless',
81
- ]"
82
- role="dialog"
83
- aria-modal="true"
84
- @click.stop
85
- >
86
- <header v-if="title && !chromeless" class="fe-modal__head">
87
- <h2 class="fe-modal__title">{{ title }}</h2>
88
- <button
89
- type="button"
90
- class="fe-modal__close"
91
- aria-label="Close"
92
- @click="emit('close')"
93
- >×</button>
94
- </header>
95
- <div class="fe-modal__body">
96
- <slot />
97
- </div>
98
- <footer v-if="$slots.actions && !chromeless" class="fe-modal__actions">
99
- <slot name="actions" />
100
- </footer>
101
- </div>
102
- </div>
103
- </transition>
104
- </template>
1
+ <script setup lang="ts">
2
+ /**
3
+ * Modal — tiny headless wrapper. Backdrop + centered card + ESC + autofocus.
4
+ */
5
+ import { watch, onBeforeUnmount, ref } from 'vue';
6
+
7
+ const props = defineProps<{
8
+ open: boolean;
9
+ title?: string;
10
+ size?: 'sm' | 'md' | 'lg' | 'xl';
11
+ closeOnBackdrop?: boolean;
12
+ /** When true, drop the dialog chrome (backdrop tint, header, footer,
13
+ * centered card with border-radius) and render the slot full-bleed.
14
+ * Used by the standalone /files/edit route where the browser tab IS
15
+ * the container — a modal frame on top of it just steals real estate
16
+ * from the editor. ESC + emit('close') still wire through so the
17
+ * parent route can window.close() the tab. */
18
+ chromeless?: boolean;
19
+ /** Explicit theme. When set the modal stamps the appropriate
20
+ * `.fe--theme-{light,dark}` class on its `.fe` backdrop so the CSS
21
+ * variable cascade matches the host shell regardless of OS
22
+ * preference (Modal is a portal-shaped descendant of `<body>`
23
+ * rather than a child of the parent component, so plain DOM
24
+ * inheritance isn't always enough). */
25
+ theme?: 'light' | 'dark' | 'auto';
26
+ }>();
27
+
28
+ const emit = defineEmits<{
29
+ (e: 'close'): void;
30
+ }>();
31
+
32
+ const cardEl = ref<HTMLElement | null>(null);
33
+
34
+ watch(
35
+ () => props.open,
36
+ (v) => {
37
+ if (v) {
38
+ document.addEventListener('keydown', onKey);
39
+ setTimeout(() => {
40
+ const focusable = cardEl.value?.querySelector<HTMLElement>(
41
+ 'input,select,textarea,button,[tabindex]:not([tabindex="-1"])',
42
+ );
43
+ focusable?.focus();
44
+ }, 30);
45
+ } else {
46
+ document.removeEventListener('keydown', onKey);
47
+ }
48
+ },
49
+ );
50
+
51
+ onBeforeUnmount(() => document.removeEventListener('keydown', onKey));
52
+
53
+ function onKey(e: KeyboardEvent) {
54
+ if (e.key === 'Escape') emit('close');
55
+ }
56
+
57
+ function onBackdrop() {
58
+ if (props.closeOnBackdrop !== false) emit('close');
59
+ }
60
+ </script>
61
+
62
+ <template>
63
+ <transition name="fe-modal">
64
+ <div
65
+ v-if="open"
66
+ class="fe fe-modal__backdrop"
67
+ :class="[
68
+ { 'fe-modal__backdrop--chromeless': chromeless },
69
+ theme === 'light' ? 'fe--theme-light' : '',
70
+ theme === 'dark' ? 'fe--theme-dark' : '',
71
+ ]"
72
+ role="presentation"
73
+ @click="onBackdrop"
74
+ >
75
+ <div
76
+ ref="cardEl"
77
+ class="fe-modal__card"
78
+ :class="[
79
+ `fe-modal__card--${size || 'md'}`,
80
+ chromeless && 'fe-modal__card--chromeless',
81
+ ]"
82
+ role="dialog"
83
+ aria-modal="true"
84
+ @click.stop
85
+ >
86
+ <header v-if="title && !chromeless" class="fe-modal__head">
87
+ <h2 class="fe-modal__title">{{ title }}</h2>
88
+ <button
89
+ type="button"
90
+ class="fe-modal__close"
91
+ aria-label="Close"
92
+ @click="emit('close')"
93
+ >×</button>
94
+ </header>
95
+ <div class="fe-modal__body">
96
+ <slot />
97
+ </div>
98
+ <footer v-if="$slots.actions && !chromeless" class="fe-modal__actions">
99
+ <slot name="actions" />
100
+ </footer>
101
+ </div>
102
+ </div>
103
+ </transition>
104
+ </template>
@@ -1,64 +1,64 @@
1
- <script setup lang="ts">
2
- import { ref, watch } from 'vue';
3
- import type { LocaleCode } from '../types/ExplorerConfig';
4
- import { useLocale } from '../composables/useLocale';
5
- import Modal from './Modal.vue';
6
-
7
- const props = defineProps<{
8
- open: boolean;
9
- locale: LocaleCode;
10
- }>();
11
-
12
- const emit = defineEmits<{
13
- (e: 'close'): void;
14
- (e: 'submit', name: string): void;
15
- }>();
16
-
17
- const { t } = useLocale(() => props.locale);
18
- const name = ref('');
19
- const err = ref<string | null>(null);
20
-
21
- watch(() => props.open, (v) => {
22
- if (v) {
23
- name.value = '';
24
- err.value = null;
25
- }
26
- });
27
-
28
- function submit() {
29
- const clean = name.value.trim();
30
- if (!clean) {
31
- err.value = t('modal.newfolder.placeholder');
32
- return;
33
- }
34
- if (/[\\/]/.test(clean) || clean === '.' || clean === '..') {
35
- err.value = 'Geçersiz karakter';
36
- return;
37
- }
38
- emit('submit', clean);
39
- }
40
- </script>
41
-
42
- <template>
43
- <Modal :open="open" :title="t('modal.newfolder.title')" size="sm" @close="emit('close')">
44
- <form @submit.prevent="submit">
45
- <input
46
- v-model="name"
47
- type="text"
48
- class="fe-input"
49
- :placeholder="t('modal.newfolder.placeholder')"
50
- autocomplete="off"
51
- @keydown.enter.prevent="submit"
52
- />
53
- <p v-if="err" class="fe-form__error">{{ err }}</p>
54
- </form>
55
- <template #actions>
56
- <button type="button" class="fe-btn" @click="emit('close')">
57
- {{ t('modal.newfolder.cancel') }}
58
- </button>
59
- <button type="button" class="fe-btn fe-btn--primary" @click="submit">
60
- {{ t('modal.newfolder.create') }}
61
- </button>
62
- </template>
63
- </Modal>
64
- </template>
1
+ <script setup lang="ts">
2
+ import { ref, watch } from 'vue';
3
+ import type { LocaleCode } from '../types/ExplorerConfig';
4
+ import { useLocale } from '../composables/useLocale';
5
+ import Modal from './Modal.vue';
6
+
7
+ const props = defineProps<{
8
+ open: boolean;
9
+ locale: LocaleCode;
10
+ }>();
11
+
12
+ const emit = defineEmits<{
13
+ (e: 'close'): void;
14
+ (e: 'submit', name: string): void;
15
+ }>();
16
+
17
+ const { t } = useLocale(() => props.locale);
18
+ const name = ref('');
19
+ const err = ref<string | null>(null);
20
+
21
+ watch(() => props.open, (v) => {
22
+ if (v) {
23
+ name.value = '';
24
+ err.value = null;
25
+ }
26
+ });
27
+
28
+ function submit() {
29
+ const clean = name.value.trim();
30
+ if (!clean) {
31
+ err.value = t('modal.newfolder.placeholder');
32
+ return;
33
+ }
34
+ if (/[\\/]/.test(clean) || clean === '.' || clean === '..') {
35
+ err.value = 'Geçersiz karakter';
36
+ return;
37
+ }
38
+ emit('submit', clean);
39
+ }
40
+ </script>
41
+
42
+ <template>
43
+ <Modal :open="open" :title="t('modal.newfolder.title')" size="sm" @close="emit('close')">
44
+ <form @submit.prevent="submit">
45
+ <input
46
+ v-model="name"
47
+ type="text"
48
+ class="fe-input"
49
+ :placeholder="t('modal.newfolder.placeholder')"
50
+ autocomplete="off"
51
+ @keydown.enter.prevent="submit"
52
+ />
53
+ <p v-if="err" class="fe-form__error">{{ err }}</p>
54
+ </form>
55
+ <template #actions>
56
+ <button type="button" class="fe-btn" @click="emit('close')">
57
+ {{ t('modal.newfolder.cancel') }}
58
+ </button>
59
+ <button type="button" class="fe-btn fe-btn--primary" @click="submit">
60
+ {{ t('modal.newfolder.create') }}
61
+ </button>
62
+ </template>
63
+ </Modal>
64
+ </template>