@devoplus/one-sdk 0.2.0-next.112 → 0.2.0-next.155
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/CHANGELOG.md +37 -0
- package/README.md +150 -51
- package/dist/client.d.ts +14 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +181 -9
- package/dist/client.js.map +1 -1
- package/dist/components.d.ts +161 -38
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +1145 -11
- package/dist/components.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/components.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// <script src="/one-bridge.js"></script> `window.one` (önce yüklenir: belirteç, dil, indirme, bildirim)
|
|
4
4
|
// <script src="/one-components.js"></script> <one-table> <one-form> <one-filter> <one-person-picker>
|
|
5
5
|
// <one-file-upload> <one-chart>
|
|
6
|
+
// D-96: <one-select> <one-tag-input> <one-dialog> <one-tooltip>
|
|
7
|
+
// <one-menu> <one-avatar> <one-tabs> <one-accordion>
|
|
6
8
|
// Sunucu tarafı üçünü de `clientAssetResponse(pathname)` ile döndürür (client.ts).
|
|
7
9
|
//
|
|
8
10
|
// Tasarım kararları (ÖNERİ — D-83 uygulama biçimi):
|
|
@@ -22,7 +24,23 @@
|
|
|
22
24
|
/** Bileşen betiğinin uygulama içindeki yolu. */
|
|
23
25
|
export const COMPONENTS_SCRIPT_PATH = '/one-components.js';
|
|
24
26
|
/** Bileşenlerin etiket adları. */
|
|
25
|
-
export const ONE_COMPONENT_TAGS = [
|
|
27
|
+
export const ONE_COMPONENT_TAGS = [
|
|
28
|
+
'one-table',
|
|
29
|
+
'one-form',
|
|
30
|
+
'one-filter',
|
|
31
|
+
'one-person-picker',
|
|
32
|
+
'one-file-upload',
|
|
33
|
+
'one-chart',
|
|
34
|
+
// D-96 ilk dalga
|
|
35
|
+
'one-select',
|
|
36
|
+
'one-tag-input',
|
|
37
|
+
'one-dialog',
|
|
38
|
+
'one-tooltip',
|
|
39
|
+
'one-menu',
|
|
40
|
+
'one-avatar',
|
|
41
|
+
'one-tabs',
|
|
42
|
+
'one-accordion',
|
|
43
|
+
];
|
|
26
44
|
/** Kişi aramanın varsayılan adresi (`peopleSearchHandler` bu yolda sunulur). */
|
|
27
45
|
export const PEOPLE_SEARCH_PATH = '/api/one/people';
|
|
28
46
|
/** `<one-file-upload>`'ın varsayılan yükleme adresi (create-one-app iskeletinin dosya ucu). */
|
|
@@ -32,6 +50,23 @@ export const FILE_UPLOAD_DEFAULT_MAX_BYTES = 5 * 1024 * 1024;
|
|
|
32
50
|
/** `<one-table>` sayfa boyutu: varsayılan ve tavan. */
|
|
33
51
|
export const TABLE_DEFAULT_PAGE_SIZE = 20;
|
|
34
52
|
export const TABLE_MAX_PAGE_SIZE = 100;
|
|
53
|
+
/** Bileşen başına olay adları (çalışma anında da okunur; betiğin yaydıklarıyla birebir aynıdır — testle kilitli). */
|
|
54
|
+
export const ONE_COMPONENT_EVENTS = {
|
|
55
|
+
'one-table': ['rowclick', 'selectionchange', 'load'],
|
|
56
|
+
'one-form': ['save', 'saved', 'savefailed', 'cancel', 'dirtychange'],
|
|
57
|
+
'one-filter': ['change'],
|
|
58
|
+
'one-person-picker': ['change'],
|
|
59
|
+
'one-file-upload': ['uploaded', 'change'],
|
|
60
|
+
'one-chart': [],
|
|
61
|
+
'one-select': ['change'],
|
|
62
|
+
'one-tag-input': ['change'],
|
|
63
|
+
'one-dialog': ['open', 'cancel', 'close'],
|
|
64
|
+
'one-tooltip': [],
|
|
65
|
+
'one-menu': ['select'],
|
|
66
|
+
'one-avatar': [],
|
|
67
|
+
'one-tabs': ['change'],
|
|
68
|
+
'one-accordion': ['toggle'],
|
|
69
|
+
};
|
|
35
70
|
/**
|
|
36
71
|
* `/one-components.js` — tarayıcıda çalışır (derleme yok). `String.raw` ters bölüleri olduğu gibi korur; betikte ters
|
|
37
72
|
* tırnak KULLANILMAZ ve tek şablon yer tutucusu bu dosyadaki sabitlerdir (yol ve sınır değerleri tek kaynaktan gelsin). Davranış components.test.ts'te gerçek bir DOM uygulamasında (jsdom) denenir.
|
|
@@ -71,7 +106,15 @@ export const ONE_COMPONENTS_SCRIPT = String.raw `(function (w) {
|
|
|
71
106
|
fileLabel: 'Dosya', tooLarge: '{name}: dosya çok büyük (en çok {max}).', badType: '{name}: bu dosya türü kabul edilmiyor.',
|
|
72
107
|
uploading: '{name} yükleniyor…', uploaded: '{name} yüklendi.', uploadError: '{name} yüklenemedi.',
|
|
73
108
|
allowed: 'Kabul edilen türler: {types}. En çok {max}.', maxOnly: 'En çok {max}.',
|
|
74
|
-
chart: 'Grafik', chartEmpty: 'Gösterilecek veri yok.', chartData: 'Grafik verisi', category: 'Kategori', total: 'Toplam'
|
|
109
|
+
chart: 'Grafik', chartEmpty: 'Gösterilecek veri yok.', chartData: 'Grafik verisi', category: 'Kategori', total: 'Toplam',
|
|
110
|
+
selectLabel: 'Seçim', selectPlaceholder: 'Seçin ya da yazarak arayın', selectNone: 'Eşleşen seçenek yok.', selectCount: '{count} seçenek.',
|
|
111
|
+
selectMore: 'İlk {count} seçenek gösteriliyor; aramayı daraltın.', selectClear: 'Seçimi temizle', selectToggle: 'Seçenekleri göster',
|
|
112
|
+
chosen: '{count} seçili',
|
|
113
|
+
tagLabel: 'Etiketler', tagPlaceholder: 'Etiket yazın', tagHelp: 'Enter ya da virgül ile ekleyin; boş alanda Geri tuşu son etiketi siler.',
|
|
114
|
+
tagAdded: '{name} eklendi.', tagRemoved: '{name} kaldırıldı.', tagDuplicate: '{name} zaten ekli.',
|
|
115
|
+
tagTooLong: 'Etiket en çok {n} karakter olabilir.', tagLimit: 'En çok {n} etiket ekleyebilirsiniz.',
|
|
116
|
+
dialogClose: 'Kapat', dialogConfirm: 'Devam edilsin mi?', toasts: 'Bildirimler', toastClose: 'Bildirimi kapat',
|
|
117
|
+
menuLabel: 'İşlemler', avatarUnknown: 'Kullanıcı'
|
|
75
118
|
},
|
|
76
119
|
en: {
|
|
77
120
|
loading: 'Loading…', empty: 'No records.', noMatch: 'No records match your search.', loadError: 'The list could not be loaded.',
|
|
@@ -101,7 +144,15 @@ export const ONE_COMPONENTS_SCRIPT = String.raw `(function (w) {
|
|
|
101
144
|
fileLabel: 'File', tooLarge: '{name}: the file is too large (at most {max}).', badType: '{name}: this file type is not accepted.',
|
|
102
145
|
uploading: 'Uploading {name}…', uploaded: '{name} uploaded.', uploadError: '{name} could not be uploaded.',
|
|
103
146
|
allowed: 'Accepted types: {types}. At most {max}.', maxOnly: 'At most {max}.',
|
|
104
|
-
chart: 'Chart', chartEmpty: 'There is no data to show.', chartData: 'Chart data', category: 'Category', total: 'Total'
|
|
147
|
+
chart: 'Chart', chartEmpty: 'There is no data to show.', chartData: 'Chart data', category: 'Category', total: 'Total',
|
|
148
|
+
selectLabel: 'Selection', selectPlaceholder: 'Choose or type to search', selectNone: 'No matching options.', selectCount: '{count} options.',
|
|
149
|
+
selectMore: 'Showing the first {count} options; narrow your search.', selectClear: 'Clear the selection', selectToggle: 'Show options',
|
|
150
|
+
chosen: '{count} selected',
|
|
151
|
+
tagLabel: 'Tags', tagPlaceholder: 'Type a tag', tagHelp: 'Add with Enter or a comma; Backspace in an empty field removes the last tag.',
|
|
152
|
+
tagAdded: '{name} added.', tagRemoved: '{name} removed.', tagDuplicate: '{name} is already added.',
|
|
153
|
+
tagTooLong: 'A tag can be at most {n} characters.', tagLimit: 'You can add at most {n} tags.',
|
|
154
|
+
dialogClose: 'Close', dialogConfirm: 'Do you want to continue?', toasts: 'Notifications', toastClose: 'Close the notification',
|
|
155
|
+
menuLabel: 'Actions', avatarUnknown: 'User'
|
|
105
156
|
}
|
|
106
157
|
};
|
|
107
158
|
var XLSX_MIME = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
|
@@ -307,6 +358,16 @@ export const ONE_COMPONENTS_SCRIPT = String.raw `(function (w) {
|
|
|
307
358
|
var one = w.one;
|
|
308
359
|
if (one && typeof one.setDirty === 'function') { try { one.setDirty(dirty); } catch (e) { report(e); } }
|
|
309
360
|
}
|
|
361
|
+
// Portal'a bildirilen kirli durum, sayfadaki BAĞLI kirli formların birleşimidir: bir formun temizlenmesi ya da ayrılması
|
|
362
|
+
// öteki kirli formun korumasını kaldırmaz; ayrılıp yeniden eklenen kirli form korumayı geri getirir.
|
|
363
|
+
var dirtyForms = [], portalDirty = false;
|
|
364
|
+
function syncDirty(form, dirty) {
|
|
365
|
+
var i = dirtyForms.indexOf(form);
|
|
366
|
+
if (dirty && i < 0) dirtyForms.push(form);
|
|
367
|
+
else if (!dirty && i >= 0) dirtyForms.splice(i, 1);
|
|
368
|
+
var any = dirtyForms.length > 0;
|
|
369
|
+
if (any !== portalDirty) { portalDirty = any; setDirty(any); }
|
|
370
|
+
}
|
|
310
371
|
function actingReadOnly(el) { return !!(w.one && w.one.actor) && !el.hasAttribute('acting-write'); }
|
|
311
372
|
|
|
312
373
|
// ── Dil değişimi: bağlı bileşenler 'one:context' gelince yeniden çizilir ─────────────────────────────────
|
|
@@ -332,13 +393,36 @@ export const ONE_COMPONENTS_SCRIPT = String.raw `(function (w) {
|
|
|
332
393
|
if (live.indexOf(this) < 0) live.push(this);
|
|
333
394
|
hookContext();
|
|
334
395
|
this.render();
|
|
335
|
-
if (!this._started) { this._started = true; this.start(); }
|
|
396
|
+
if (!this._started) { this._started = true; this.start(); return; }
|
|
397
|
+
// Yeniden bağlanma: ayrıyken değişen öznitelikler şimdi uygulanır (öznitelik ile durum yeniden eşitlenir). Ayrıyken
|
|
398
|
+
// değişip eski değerine dönen ad atlanır; değişen adlar bileşene tek seferde verilir (tablo bir kez yükler).
|
|
399
|
+
var first = this._detached, names = [];
|
|
400
|
+
this._detached = null;
|
|
401
|
+
for (var n in first) if (own(first, n) && this.getAttribute(n) !== first[n]) names.push(n);
|
|
402
|
+
if (names.length) this.attributesChanged(names);
|
|
403
|
+
this.reconnected();
|
|
336
404
|
}
|
|
337
405
|
disconnectedCallback() { var i = live.indexOf(this); if (i >= 0) live.splice(i, 1); }
|
|
338
406
|
start() {}
|
|
407
|
+
reconnected() {}
|
|
339
408
|
// Yükseltme sırasında ilk öznitelikler connectedCallback'ten ÖNCE bildirilir; o anda iş yapılmaz (çift istek olmasın).
|
|
340
|
-
|
|
409
|
+
// Ayrıyken gelen değişiklik kaybolmaz: adın ayrılmadan önceki değeri not edilir, yeniden bağlanınca uygulanır (değer
|
|
410
|
+
// o anki öznitelikten okunur).
|
|
411
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
412
|
+
if (oldValue === newValue || !this._started) return;
|
|
413
|
+
if (this.isConnected) { this.attributeChanged(name); return; }
|
|
414
|
+
var first = this._detached || (this._detached = Object.create(null));
|
|
415
|
+
// Bileşenin kendi durumunu yazması (ör. ayrılırken kapanan pencere) karşılaştırmanın yeni tabanıdır.
|
|
416
|
+
if (this._reflecting) first[name] = newValue;
|
|
417
|
+
else if (!(name in first)) first[name] = oldValue;
|
|
418
|
+
}
|
|
419
|
+
// Bileşen kendi durumunu gözlenen bir özniteliğe yazar (null = kaldır).
|
|
420
|
+
reflect(name, value) {
|
|
421
|
+
this._reflecting = true;
|
|
422
|
+
try { if (value == null) this.removeAttribute(name); else this.setAttribute(name, value); } finally { this._reflecting = false; }
|
|
423
|
+
}
|
|
341
424
|
attributeChanged() { this.render(); }
|
|
425
|
+
attributesChanged(names) { for (var i = 0; i < names.length; i++) this.attributeChanged(names[i]); }
|
|
342
426
|
lang_() { return langOf(this); }
|
|
343
427
|
// Yeniden çizimde odak aynı denetimde kalır (dil değişimi, sıralama, sayfa).
|
|
344
428
|
render() {
|
|
@@ -415,8 +499,10 @@ export const ONE_COMPONENTS_SCRIPT = String.raw `(function (w) {
|
|
|
415
499
|
}
|
|
416
500
|
reload() { return this._rows ? (this.render(), Promise.resolve()) : this._load(); }
|
|
417
501
|
start() { if (!this._rows && this.getAttribute('src')) this._load(); }
|
|
418
|
-
attributeChanged(name) {
|
|
419
|
-
|
|
502
|
+
attributeChanged(name) { this.attributesChanged([name]); }
|
|
503
|
+
attributesChanged(names) {
|
|
504
|
+
var restart = names.some(function (n) { return n === 'src' || n === 'paging' || n === 'page-size'; });
|
|
505
|
+
if (restart) { this._page = 0; this._cursors = [null]; if (!this._rows) { this._load(); return; } }
|
|
420
506
|
this.render();
|
|
421
507
|
}
|
|
422
508
|
_remote() { return !this._rows && !!this.getAttribute('src'); }
|
|
@@ -829,12 +915,13 @@ export const ONE_COMPONENTS_SCRIPT = String.raw `(function (w) {
|
|
|
829
915
|
get submitLabel() { return this._submitLabel || null; }
|
|
830
916
|
set submitLabel(v) { this._submitLabel = v == null ? null : v; this.render(); }
|
|
831
917
|
get dirty() { return this._dirty; }
|
|
832
|
-
disconnectedCallback() { super.disconnectedCallback();
|
|
918
|
+
disconnectedCallback() { super.disconnectedCallback(); syncDirty(this, false); }
|
|
919
|
+
reconnected() { syncDirty(this, this._dirty); }
|
|
833
920
|
_checkDirty() {
|
|
834
921
|
var d = JSON.stringify(this.value) !== this._baseline;
|
|
835
922
|
if (d === this._dirty) return;
|
|
836
923
|
this._dirty = d;
|
|
837
|
-
|
|
924
|
+
syncDirty(this, d && this.isConnected);
|
|
838
925
|
emit(this, 'dirtychange', { dirty: d });
|
|
839
926
|
}
|
|
840
927
|
reset() {
|
|
@@ -1147,6 +1234,8 @@ export const ONE_COMPONENTS_SCRIPT = String.raw `(function (w) {
|
|
|
1147
1234
|
var self = this;
|
|
1148
1235
|
listen(this, 'focusout', function (ev) { if (!ev.relatedTarget || !self.contains(ev.relatedTarget)) { self._open = false; self._update(); } });
|
|
1149
1236
|
}
|
|
1237
|
+
// Ayrılan öğe odak kaybı olayı almaz: sonuç listesi burada kapanır; sonuçlar kalır (Aşağı ok yeniden açar).
|
|
1238
|
+
disconnectedCallback() { super.disconnectedCallback(); this._open = false; this._active = -1; }
|
|
1150
1239
|
get value() { return clone(this._value); }
|
|
1151
1240
|
set value(v) {
|
|
1152
1241
|
if (this.hasAttribute('multiple')) this._value = (Array.isArray(v) ? v : v ? [v] : []).map(cleanPerson).filter(Boolean);
|
|
@@ -1173,7 +1262,7 @@ export const ONE_COMPONENTS_SCRIPT = String.raw `(function (w) {
|
|
|
1173
1262
|
if (my !== self._seq) return;
|
|
1174
1263
|
if (!res.ok || !body || !Array.isArray(body.items)) { self._status = 'error'; self._open = false; self._update(); return; }
|
|
1175
1264
|
self._items = body.items.map(cleanPerson).filter(Boolean).slice(0, 20);
|
|
1176
|
-
self._status = 'ready'; self._open =
|
|
1265
|
+
self._status = 'ready'; self._open = self.isConnected; self._active = self._open && self._items.length ? 0 : -1;
|
|
1177
1266
|
self._update();
|
|
1178
1267
|
});
|
|
1179
1268
|
}, function (e) { if (my !== self._seq) return; report(e); self._status = 'error'; self._open = false; self._update(); });
|
|
@@ -1484,7 +1573,1052 @@ export const ONE_COMPONENTS_SCRIPT = String.raw `(function (w) {
|
|
|
1484
1573
|
}
|
|
1485
1574
|
}
|
|
1486
1575
|
|
|
1487
|
-
|
|
1576
|
+
// ══ <one-select> (D-96) ═════════════════════════════════════════════════════════════════════════════════
|
|
1577
|
+
// Aranabilir açılır liste: APG "combobox + listbox" kalıbı. Liste giriş alanının altında AKIŞ İÇİNDEDİR (person picker
|
|
1578
|
+
// ile aynı gerekçe: Portal iframe'i içerik boyunda çizer, akış dışındaki liste sayfanın altında kesilirdi).
|
|
1579
|
+
var SELECT_MAX_SHOWN = 200;
|
|
1580
|
+
// İç anahtar ilkel türü de taşır: 1 ile '1', false ile 'false' ayrı seçeneklerdir ve seçilen değerin türü korunur.
|
|
1581
|
+
function optionKey(v) { return typeof v + ':' + String(v); }
|
|
1582
|
+
function cleanOptions(list) {
|
|
1583
|
+
if (!Array.isArray(list)) throw new TypeError('one-select: options bir dizi olmalı');
|
|
1584
|
+
var out = [], seen = Object.create(null);
|
|
1585
|
+
for (var i = 0; i < list.length; i++) {
|
|
1586
|
+
var o = list[i];
|
|
1587
|
+
if (!o || typeof o !== 'object') continue;
|
|
1588
|
+
var v = o.value;
|
|
1589
|
+
if (typeof v !== 'string' && typeof v !== 'number' && typeof v !== 'boolean') continue;
|
|
1590
|
+
var k = optionKey(v);
|
|
1591
|
+
if (k in seen) continue;
|
|
1592
|
+
seen[k] = true;
|
|
1593
|
+
out.push({ value: v, key: k, label: o.label == null ? String(v) : o.label, description: o.description == null ? null : o.description, disabled: o.disabled === true });
|
|
1594
|
+
}
|
|
1595
|
+
return out;
|
|
1596
|
+
}
|
|
1597
|
+
function scrollIntoList(list, el) {
|
|
1598
|
+
if (!list || !el || typeof el.getBoundingClientRect !== 'function') return;
|
|
1599
|
+
var lr = list.getBoundingClientRect(), er = el.getBoundingClientRect();
|
|
1600
|
+
if (er.top < lr.top) list.scrollTop -= lr.top - er.top;
|
|
1601
|
+
else if (er.bottom > lr.bottom) list.scrollTop += er.bottom - lr.bottom;
|
|
1602
|
+
}
|
|
1603
|
+
function requiredMarks(el, lang) {
|
|
1604
|
+
if (!el.hasAttribute('required')) return [];
|
|
1605
|
+
return [h('span', { 'class': 'one-required', 'aria-hidden': 'true', text: ' *' }), h('span', { 'class': 'one-sr-only', text: ' ' + t(lang, 'requiredMark') })];
|
|
1606
|
+
}
|
|
1607
|
+
class OneSelect extends Base {
|
|
1608
|
+
static get observedAttributes() { return ['lang', 'label', 'multiple', 'required', 'disabled', 'placeholder']; }
|
|
1609
|
+
static get props() { return ['options', 'value', 'label']; }
|
|
1610
|
+
constructor() {
|
|
1611
|
+
super();
|
|
1612
|
+
this._options = []; this._sel = []; this._raw = Object.create(null); this._label = null;
|
|
1613
|
+
this._q = ''; this._typing = false; this._open = false; this._active = -1; this._all = []; this._shown = []; this._drawing = false;
|
|
1614
|
+
this._ids = { input: uid('select'), list: uid('options') };
|
|
1615
|
+
var self = this;
|
|
1616
|
+
listen(this, 'focusout', function (ev) {
|
|
1617
|
+
if (self._drawing || (ev.relatedTarget && self.contains(ev.relatedTarget))) return;
|
|
1618
|
+
self._close(true);
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1621
|
+
get options() { return this._options.map(function (o) { return { value: o.value, label: o.label, description: o.description, disabled: o.disabled }; }); }
|
|
1622
|
+
set options(v) {
|
|
1623
|
+
this._options = cleanOptions(v == null ? [] : v);
|
|
1624
|
+
// Seçenekler değişince artık seçenek olmayan '' seçimden düşer (adsız seçim kalmaz).
|
|
1625
|
+
var empty = optionKey(''), i = this._sel.indexOf(empty);
|
|
1626
|
+
if (i >= 0 && !this._opt(empty)) { this._sel.splice(i, 1); delete this._raw[empty]; }
|
|
1627
|
+
this.render();
|
|
1628
|
+
}
|
|
1629
|
+
// Ayrılan öğe odak kaybı olayı almaz: açık liste burada kapanır, yeniden eklenince odaksız açık liste kalmaz.
|
|
1630
|
+
disconnectedCallback() { super.disconnectedCallback(); this._open = false; this._active = -1; this._typing = false; this._q = ''; }
|
|
1631
|
+
get value() {
|
|
1632
|
+
var self = this, vals = this._sel.map(function (k) { var o = self._opt(k); return o ? o.value : self._raw[k]; });
|
|
1633
|
+
return this._multi() ? vals : (vals.length ? vals[0] : null);
|
|
1634
|
+
}
|
|
1635
|
+
// Seçimi yalnız null / undefined temizler: '' meşru bir seçenek değeri olabilir.
|
|
1636
|
+
set value(v) {
|
|
1637
|
+
var list = Array.isArray(v) ? v : (v == null ? [] : [v]), sel = [], raw = Object.create(null);
|
|
1638
|
+
for (var i = 0; i < list.length; i++) {
|
|
1639
|
+
var x = list[i];
|
|
1640
|
+
if (typeof x !== 'string' && typeof x !== 'number' && typeof x !== 'boolean') continue;
|
|
1641
|
+
var k = optionKey(x);
|
|
1642
|
+
if (k in raw || (x === '' && !this._opt(k))) continue; // '' yalnız böyle bir seçenek varsa seçilir (adsız etiket olmaz)
|
|
1643
|
+
raw[k] = x; sel.push(k);
|
|
1644
|
+
if (!this._multi()) break;
|
|
1645
|
+
}
|
|
1646
|
+
this._sel = sel; this._raw = raw; this._q = ''; this._typing = false;
|
|
1647
|
+
this.render();
|
|
1648
|
+
}
|
|
1649
|
+
get label() { return this._label; }
|
|
1650
|
+
set label(v) { this._label = v == null ? null : v; this.render(); }
|
|
1651
|
+
_multi() { return this.hasAttribute('multiple'); }
|
|
1652
|
+
_opt(k) { for (var i = 0; i < this._options.length; i++) if (this._options[i].key === k) return this._options[i]; return null; }
|
|
1653
|
+
_name(k, lang) { var o = this._opt(k); return o ? textOf(o.label, lang) : String(this._raw[k]); }
|
|
1654
|
+
_isSel(k) { return this._sel.indexOf(k) >= 0; }
|
|
1655
|
+
_changed() { emit(this, 'change', { value: this.value }); }
|
|
1656
|
+
_refresh() {
|
|
1657
|
+
var lang = this.lang_(), q = fold(this._q.trim());
|
|
1658
|
+
this._all = !q ? this._options : this._options.filter(function (o) {
|
|
1659
|
+
return fold(textOf(o.label, lang)).indexOf(q) >= 0 || (o.description != null && fold(textOf(o.description, lang)).indexOf(q) >= 0);
|
|
1660
|
+
});
|
|
1661
|
+
this._shown = this._all.slice(0, SELECT_MAX_SHOWN);
|
|
1662
|
+
}
|
|
1663
|
+
// Bir sonraki SEÇİLEBİLİR seçenek (devre dışı olanlar atlanır, sona gelince başa döner).
|
|
1664
|
+
_step(from, dir) {
|
|
1665
|
+
var n = this._shown.length;
|
|
1666
|
+
for (var i = 1; i <= n; i++) { var j = ((from + dir * i) % n + n) % n; if (!this._shown[j].disabled) return j; }
|
|
1667
|
+
return -1;
|
|
1668
|
+
}
|
|
1669
|
+
_selIndex() {
|
|
1670
|
+
for (var i = 0; i < this._shown.length; i++) if (this._isSel(this._shown[i].key) && !this._shown[i].disabled) return i;
|
|
1671
|
+
return this._step(-1, 1);
|
|
1672
|
+
}
|
|
1673
|
+
_input() { return doc.getElementById(this._ids.input); }
|
|
1674
|
+
_openAt(where) {
|
|
1675
|
+
if (this.hasAttribute('disabled')) return;
|
|
1676
|
+
this._open = true; this._refresh();
|
|
1677
|
+
this._active = where === 'last' ? this._step(this._shown.length, -1) : where === 'first' ? this._step(-1, 1) : this._selIndex();
|
|
1678
|
+
this._update();
|
|
1679
|
+
}
|
|
1680
|
+
_close(restore) {
|
|
1681
|
+
this._open = false; this._active = -1;
|
|
1682
|
+
if (restore && (this._typing || this._q)) {
|
|
1683
|
+
this._typing = false; this._q = '';
|
|
1684
|
+
var input = this._input();
|
|
1685
|
+
if (input) input.value = this._multi() || !this._sel.length ? '' : this._name(this._sel[0], this.lang_());
|
|
1686
|
+
}
|
|
1687
|
+
this._update();
|
|
1688
|
+
}
|
|
1689
|
+
_choose(o) {
|
|
1690
|
+
if (!o || o.disabled) return;
|
|
1691
|
+
if (this._multi()) {
|
|
1692
|
+
var i = this._sel.indexOf(o.key);
|
|
1693
|
+
if (i >= 0) this._sel.splice(i, 1); else { this._sel.push(o.key); this._raw[o.key] = o.value; }
|
|
1694
|
+
this._drawChips(); this._update(); this._changed();
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1697
|
+
var same = this._sel.length === 1 && this._sel[0] === o.key;
|
|
1698
|
+
this._sel = [o.key]; this._raw = Object.create(null); this._raw[o.key] = o.value;
|
|
1699
|
+
this._q = ''; this._typing = false; this._open = false; this._active = -1;
|
|
1700
|
+
this.render();
|
|
1701
|
+
var input = this._input();
|
|
1702
|
+
if (input) input.focus();
|
|
1703
|
+
if (!same) this._changed();
|
|
1704
|
+
}
|
|
1705
|
+
_remove(k) {
|
|
1706
|
+
this._sel = this._sel.filter(function (x) { return x !== k; });
|
|
1707
|
+
if (this._multi()) { this._drawChips(); this._update(); } else this.render();
|
|
1708
|
+
var input = this._input();
|
|
1709
|
+
if (input) input.focus();
|
|
1710
|
+
this._changed();
|
|
1711
|
+
}
|
|
1712
|
+
_drawChips() {
|
|
1713
|
+
var self = this, lang = this.lang_(), ul = this.querySelector('[data-one-chips]'), disabled = this.hasAttribute('disabled');
|
|
1714
|
+
if (!ul) return;
|
|
1715
|
+
clear(ul);
|
|
1716
|
+
ul.hidden = this._sel.length === 0;
|
|
1717
|
+
ul.setAttribute('aria-label', t(lang, 'chosen', { count: this._sel.length }));
|
|
1718
|
+
this._sel.forEach(function (k) {
|
|
1719
|
+
var name = self._name(k, lang);
|
|
1720
|
+
var rm = h('button', { type: 'button', 'class': 'one-chip-remove', 'aria-label': t(lang, 'remove', { name: name }), 'data-one-focus': 'rm:' + k, disabled: disabled ? true : null, text: '×' });
|
|
1721
|
+
listen(rm, 'click', function () { self._remove(k); });
|
|
1722
|
+
add(ul, h('li', { 'class': 'one-chip' }, [h('span', { text: name }), rm]));
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
// Liste, durum satırı ve ARIA durumu giriş alanına dokunmadan güncellenir (yazarken odak ve imleç kaybolmaz).
|
|
1726
|
+
_update() {
|
|
1727
|
+
var self = this, lang = this.lang_(), input = this._input(), list = doc.getElementById(this._ids.list), status = this.querySelector('[data-one-status]');
|
|
1728
|
+
var toggle = this.querySelector('[data-one-toggle]');
|
|
1729
|
+
if (!input || !list || !status) return;
|
|
1730
|
+
this._refresh();
|
|
1731
|
+
var n = this._shown.length;
|
|
1732
|
+
if (this._open && (this._active < 0 || this._active >= n || this._shown[this._active].disabled)) this._active = this._step(-1, 1);
|
|
1733
|
+
clear(list);
|
|
1734
|
+
if (this._open) this._shown.forEach(function (o, i) {
|
|
1735
|
+
var sel = self._isSel(o.key);
|
|
1736
|
+
var li = h('li', { id: self._ids.list + '-' + i, role: 'option', 'class': 'one-option' + (i === self._active ? ' one-active' : ''), 'aria-selected': sel ? 'true' : 'false', 'aria-disabled': o.disabled ? 'true' : null }, [
|
|
1737
|
+
h('span', { 'class': 'one-option-check', 'aria-hidden': 'true', text: sel ? '✓' : '' }),
|
|
1738
|
+
h('span', { 'class': 'one-option-text' }, [h('span', { 'class': 'one-option-main', text: textOf(o.label, lang) }),
|
|
1739
|
+
o.description != null ? h('span', { 'class': 'one-option-sub one-muted', text: textOf(o.description, lang) }) : null])]);
|
|
1740
|
+
listen(li, 'mousedown', function (ev) { ev.preventDefault(); });
|
|
1741
|
+
listen(li, 'click', function () { if (o.disabled) return; self._active = i; self._choose(o); });
|
|
1742
|
+
add(list, li);
|
|
1743
|
+
});
|
|
1744
|
+
var open = this._open && n > 0;
|
|
1745
|
+
list.hidden = !open;
|
|
1746
|
+
input.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
1747
|
+
if (toggle) toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
1748
|
+
if (open && this._active >= 0) { input.setAttribute('aria-activedescendant', this._ids.list + '-' + this._active); scrollIntoList(list, doc.getElementById(this._ids.list + '-' + this._active)); }
|
|
1749
|
+
else input.removeAttribute('aria-activedescendant');
|
|
1750
|
+
var msg = '';
|
|
1751
|
+
if (this._open) msg = this._all.length === 0 ? t(lang, 'selectNone') : this._all.length > n ? t(lang, 'selectMore', { count: n }) : t(lang, 'selectCount', { count: n });
|
|
1752
|
+
if (status.textContent !== msg) status.textContent = msg;
|
|
1753
|
+
}
|
|
1754
|
+
draw() {
|
|
1755
|
+
this._drawing = true;
|
|
1756
|
+
try { this._draw(); } finally { this._drawing = false; }
|
|
1757
|
+
}
|
|
1758
|
+
_draw() {
|
|
1759
|
+
var self = this, lang = this.lang_(), multi = this._multi(), disabled = this.hasAttribute('disabled');
|
|
1760
|
+
// 'multiple' çalışma anında kaldırılabilir: tekli seçimde en çok bir değer kalır (görünen ve okunan aynı olsun).
|
|
1761
|
+
if (!multi && this._sel.length > 1) this._sel = this._sel.slice(0, 1);
|
|
1762
|
+
clear(this);
|
|
1763
|
+
var label = this._label != null ? textOf(this._label, lang) : (this.getAttribute('label') || t(lang, 'selectLabel'));
|
|
1764
|
+
var wrap = h('div', { 'class': 'one-field one-combobox' });
|
|
1765
|
+
add(wrap, h('label', { 'for': this._ids.input, id: this._ids.input + '-label', 'class': 'one-label' }, [label].concat(requiredMarks(this, lang))));
|
|
1766
|
+
if (multi) add(wrap, h('ul', { 'class': 'one-chips', 'data-one-chips': '', hidden: true }));
|
|
1767
|
+
var described = [this.getAttribute('aria-describedby'), this._ids.input + '-status'].filter(Boolean).join(' ');
|
|
1768
|
+
var input = h('input', { id: this._ids.input, type: 'text', 'class': 'one-input', role: 'combobox', autocomplete: 'off', spellcheck: 'false',
|
|
1769
|
+
'aria-autocomplete': 'list', 'aria-expanded': 'false', 'aria-controls': this._ids.list, 'aria-describedby': described,
|
|
1770
|
+
placeholder: this.getAttribute('placeholder') || t(lang, 'selectPlaceholder'), 'aria-required': this.hasAttribute('required') ? 'true' : null,
|
|
1771
|
+
'aria-invalid': this.getAttribute('aria-invalid'), disabled: disabled ? true : null, 'data-one-focus': 'input' });
|
|
1772
|
+
input.value = this._typing || multi ? this._q : (this._sel.length ? this._name(this._sel[0], lang) : '');
|
|
1773
|
+
listen(input, 'input', function () { self._q = input.value; self._typing = true; self._open = true; self._active = -1; self._update(); });
|
|
1774
|
+
listen(input, 'click', function () { if (!self._open) self._openAt('selected'); });
|
|
1775
|
+
listen(input, 'keydown', function (ev) {
|
|
1776
|
+
var k = ev.key;
|
|
1777
|
+
if (k === 'ArrowDown' || k === 'ArrowUp') {
|
|
1778
|
+
ev.preventDefault();
|
|
1779
|
+
if (!self._open) { if (!(ev.altKey && k === 'ArrowUp')) self._openAt(ev.altKey ? 'selected' : k === 'ArrowUp' ? 'last' : 'selected'); return; }
|
|
1780
|
+
if (ev.altKey && k === 'ArrowUp') { self._close(false); return; }
|
|
1781
|
+
var dir = k === 'ArrowDown' ? 1 : -1;
|
|
1782
|
+
self._active = self._step(self._active < 0 ? (dir > 0 ? -1 : self._shown.length) : self._active, dir);
|
|
1783
|
+
self._update();
|
|
1784
|
+
} else if (k === 'Enter') {
|
|
1785
|
+
if (self._open && self._active >= 0) { ev.preventDefault(); self._choose(self._shown[self._active]); }
|
|
1786
|
+
} else if (k === 'Escape') {
|
|
1787
|
+
// Açık liste önce kapanır; olay pencereye ('<one-dialog>') ulaşmaz, pencere yalnız ikinci Escape'te kapanır.
|
|
1788
|
+
if (self._open || self._typing) { ev.preventDefault(); ev.stopPropagation(); self._close(true); }
|
|
1789
|
+
} else if (k === 'Backspace' && multi && input.value === '' && self._sel.length && !disabled) {
|
|
1790
|
+
self._remove(self._sel[self._sel.length - 1]);
|
|
1791
|
+
}
|
|
1792
|
+
});
|
|
1793
|
+
var clearBtn = null;
|
|
1794
|
+
if (!multi && this._sel.length && !this.hasAttribute('required')) {
|
|
1795
|
+
clearBtn = h('button', { type: 'button', 'class': 'one-icon-btn', 'aria-label': t(lang, 'selectClear'), disabled: disabled ? true : null, 'data-one-focus': 'clear', text: '×' });
|
|
1796
|
+
listen(clearBtn, 'click', function () {
|
|
1797
|
+
self._sel = []; self._raw = Object.create(null); self._q = ''; self._typing = false; self._open = false;
|
|
1798
|
+
self.render(); var i2 = self._input(); if (i2) i2.focus(); self._changed();
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
var toggle = h('button', { type: 'button', 'class': 'one-icon-btn', tabindex: '-1', 'aria-label': t(lang, 'selectToggle'), 'aria-controls': this._ids.list,
|
|
1802
|
+
'aria-expanded': 'false', 'data-one-toggle': '', disabled: disabled ? true : null, text: '▾' });
|
|
1803
|
+
listen(toggle, 'mousedown', function (ev) { ev.preventDefault(); });
|
|
1804
|
+
listen(toggle, 'click', function () { if (self._open) self._close(false); else self._openAt('selected'); var i3 = self._input(); if (i3) i3.focus(); });
|
|
1805
|
+
add(wrap, h('div', { 'class': 'one-select-control' }, [input, clearBtn, toggle]));
|
|
1806
|
+
add(wrap, h('ul', { id: this._ids.list, role: 'listbox', 'class': 'one-listbox one-listbox-select', 'aria-labelledby': this._ids.input + '-label',
|
|
1807
|
+
'aria-multiselectable': multi ? 'true' : null, hidden: true }));
|
|
1808
|
+
add(wrap, h('p', { id: this._ids.input + '-status', 'class': 'one-help', role: 'status', 'aria-live': 'polite', 'data-one-status': '' }));
|
|
1809
|
+
add(this, wrap);
|
|
1810
|
+
if (multi) this._drawChips();
|
|
1811
|
+
this._update();
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
// ══ <one-tag-input> (D-96) ══════════════════════════════════════════════════════════════════════════════
|
|
1816
|
+
var TAG_MAX_LENGTH = 50, TAG_MAX_COUNT = 50, TAG_SUGGEST_SHOWN = 50;
|
|
1817
|
+
function cleanTag(v) { return String(v == null ? '' : v).replace(/[\u0000-\u001f\u007f]+/g, ' ').replace(/\s+/g, ' ').trim(); }
|
|
1818
|
+
class OneTagInput extends Base {
|
|
1819
|
+
static get observedAttributes() { return ['lang', 'label', 'required', 'disabled', 'placeholder', 'max', 'max-length']; }
|
|
1820
|
+
static get props() { return ['value', 'suggestions', 'label']; }
|
|
1821
|
+
constructor() {
|
|
1822
|
+
super();
|
|
1823
|
+
this._tags = []; this._suggest = []; this._label = null; this._q = ''; this._open = false; this._active = -1; this._shown = []; this._msg = null; this._drawing = false;
|
|
1824
|
+
this._ids = { input: uid('tags'), list: uid('tag-options') };
|
|
1825
|
+
var self = this;
|
|
1826
|
+
listen(this, 'focusout', function (ev) {
|
|
1827
|
+
if (self._drawing || (ev.relatedTarget && self.contains(ev.relatedTarget))) return;
|
|
1828
|
+
// Ayrılırken yazılı kalan metin etikete dönüşür (yazılan kaybolmaz); geçersizse alanda kalır ve neden yazılır.
|
|
1829
|
+
if (cleanTag(self._q)) self._commit(self._q);
|
|
1830
|
+
self._open = false; self._active = -1; self._update();
|
|
1831
|
+
});
|
|
1832
|
+
}
|
|
1833
|
+
// Ayrılan öğe odak kaybı olayı almaz: öneri listesi burada kapanır (yazılan metin alanda kalır).
|
|
1834
|
+
disconnectedCallback() { super.disconnectedCallback(); this._open = false; this._active = -1; }
|
|
1835
|
+
get value() { return this._tags.slice(); }
|
|
1836
|
+
set value(v) {
|
|
1837
|
+
var list = Array.isArray(v) ? v : (v == null || v === '' ? [] : [v]), out = [], seen = Object.create(null), maxLen = this._maxLen(), max = this._max();
|
|
1838
|
+
for (var i = 0; i < list.length && out.length < max; i++) {
|
|
1839
|
+
var tag = cleanTag(list[i]);
|
|
1840
|
+
if (!tag || tag.length > maxLen) continue;
|
|
1841
|
+
var f = fold(tag);
|
|
1842
|
+
if (f in seen) continue;
|
|
1843
|
+
seen[f] = true; out.push(tag);
|
|
1844
|
+
}
|
|
1845
|
+
this._tags = out; this._q = ''; this._msg = null;
|
|
1846
|
+
this.render();
|
|
1847
|
+
}
|
|
1848
|
+
get suggestions() { return this._suggest.slice(); }
|
|
1849
|
+
set suggestions(v) {
|
|
1850
|
+
if (v != null && !Array.isArray(v)) throw new TypeError('one-tag-input: suggestions bir dizi olmalı');
|
|
1851
|
+
var out = [], seen = Object.create(null);
|
|
1852
|
+
(v || []).forEach(function (x) {
|
|
1853
|
+
var tag = cleanTag(typeof x === 'string' || typeof x === 'number' ? x : '');
|
|
1854
|
+
var f = fold(tag);
|
|
1855
|
+
if (!tag || f in seen) return;
|
|
1856
|
+
seen[f] = true; out.push(tag);
|
|
1857
|
+
});
|
|
1858
|
+
this._suggest = out;
|
|
1859
|
+
this.render();
|
|
1860
|
+
}
|
|
1861
|
+
get label() { return this._label; }
|
|
1862
|
+
set label(v) { this._label = v == null ? null : v; this.render(); }
|
|
1863
|
+
_max() { var n = parseInt(this.getAttribute('max') || '', 10); return n > 0 ? Math.min(n, 500) : TAG_MAX_COUNT; }
|
|
1864
|
+
_maxLen() { var n = parseInt(this.getAttribute('max-length') || '', 10); return n > 0 ? Math.min(n, 500) : TAG_MAX_LENGTH; }
|
|
1865
|
+
_input() { return doc.getElementById(this._ids.input); }
|
|
1866
|
+
_has(tag) { var f = fold(tag); for (var i = 0; i < this._tags.length; i++) if (fold(this._tags[i]) === f) return this._tags[i]; return null; }
|
|
1867
|
+
_setQuery(q) { this._q = q; var input = this._input(); if (input && input.value !== q) input.value = q; }
|
|
1868
|
+
_commit(raw) {
|
|
1869
|
+
var tag = cleanTag(raw), dup;
|
|
1870
|
+
if (!tag) { this._setQuery(''); return false; }
|
|
1871
|
+
if (tag.length > this._maxLen()) { this._msg = { key: 'tagTooLong', vars: { n: this._maxLen() }, error: true }; this._update(); return false; }
|
|
1872
|
+
if ((dup = this._has(tag))) { this._msg = { key: 'tagDuplicate', vars: { name: dup }, error: true }; this._setQuery(''); this._update(); return false; }
|
|
1873
|
+
if (this._tags.length >= this._max()) { this._msg = { key: 'tagLimit', vars: { n: this._max() }, error: true }; this._update(); return false; }
|
|
1874
|
+
this._tags.push(tag); this._msg = { key: 'tagAdded', vars: { name: tag } };
|
|
1875
|
+
this._setQuery(''); this._open = false; this._active = -1;
|
|
1876
|
+
this._drawChips(); this._update();
|
|
1877
|
+
emit(this, 'change', { value: this.value });
|
|
1878
|
+
return true;
|
|
1879
|
+
}
|
|
1880
|
+
_remove(i) {
|
|
1881
|
+
var tag = this._tags[i];
|
|
1882
|
+
if (tag == null) return;
|
|
1883
|
+
this._tags.splice(i, 1); this._msg = { key: 'tagRemoved', vars: { name: tag } };
|
|
1884
|
+
this._drawChips(); this._update();
|
|
1885
|
+
var input = this._input();
|
|
1886
|
+
if (input) input.focus();
|
|
1887
|
+
emit(this, 'change', { value: this.value });
|
|
1888
|
+
}
|
|
1889
|
+
_refresh() {
|
|
1890
|
+
var self = this, q = fold(this._q.trim());
|
|
1891
|
+
this._shown = !q || this._suggest.length === 0 ? [] : this._suggest.filter(function (s) { return !self._has(s) && fold(s).indexOf(q) >= 0; }).slice(0, TAG_SUGGEST_SHOWN);
|
|
1892
|
+
}
|
|
1893
|
+
_drawChips() {
|
|
1894
|
+
var self = this, lang = this.lang_(), ul = this.querySelector('[data-one-chips]'), disabled = this.hasAttribute('disabled');
|
|
1895
|
+
if (!ul) return;
|
|
1896
|
+
clear(ul);
|
|
1897
|
+
ul.hidden = this._tags.length === 0;
|
|
1898
|
+
this._tags.forEach(function (tag, i) {
|
|
1899
|
+
var rm = h('button', { type: 'button', 'class': 'one-chip-remove', 'aria-label': t(lang, 'remove', { name: tag }), 'data-one-focus': 'rm:' + i, disabled: disabled ? true : null, text: '×' });
|
|
1900
|
+
listen(rm, 'click', function () { self._remove(i); });
|
|
1901
|
+
add(ul, h('li', { 'class': 'one-chip' }, [h('span', { text: tag }), rm]));
|
|
1902
|
+
});
|
|
1903
|
+
}
|
|
1904
|
+
_update() {
|
|
1905
|
+
var self = this, lang = this.lang_(), input = this._input(), list = doc.getElementById(this._ids.list), status = this.querySelector('[data-one-status]');
|
|
1906
|
+
if (!input || !status) return;
|
|
1907
|
+
this._refresh();
|
|
1908
|
+
var n = this._shown.length, open = this._open && n > 0;
|
|
1909
|
+
if (open && (this._active < 0 || this._active >= n)) this._active = 0;
|
|
1910
|
+
if (list) {
|
|
1911
|
+
clear(list);
|
|
1912
|
+
if (open) this._shown.forEach(function (s, i) {
|
|
1913
|
+
var li = h('li', { id: self._ids.list + '-' + i, role: 'option', 'class': 'one-option' + (i === self._active ? ' one-active' : ''), 'aria-selected': i === self._active ? 'true' : 'false', text: s });
|
|
1914
|
+
listen(li, 'mousedown', function (ev) { ev.preventDefault(); });
|
|
1915
|
+
listen(li, 'click', function () { self._commit(s); });
|
|
1916
|
+
add(list, li);
|
|
1917
|
+
});
|
|
1918
|
+
list.hidden = !open;
|
|
1919
|
+
input.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
1920
|
+
if (open && this._active >= 0) { input.setAttribute('aria-activedescendant', this._ids.list + '-' + this._active); scrollIntoList(list, doc.getElementById(this._ids.list + '-' + this._active)); }
|
|
1921
|
+
else input.removeAttribute('aria-activedescendant');
|
|
1922
|
+
}
|
|
1923
|
+
var m = this._msg, text = m ? t(lang, m.key, m.vars) : '';
|
|
1924
|
+
if (status.textContent !== text) status.textContent = text;
|
|
1925
|
+
status.className = m && m.error ? 'one-field-error' : 'one-help';
|
|
1926
|
+
if (m && m.error) input.setAttribute('aria-invalid', 'true'); else input.removeAttribute('aria-invalid');
|
|
1927
|
+
}
|
|
1928
|
+
draw() {
|
|
1929
|
+
this._drawing = true;
|
|
1930
|
+
try { this._draw(); } finally { this._drawing = false; }
|
|
1931
|
+
}
|
|
1932
|
+
_draw() {
|
|
1933
|
+
var self = this, lang = this.lang_(), disabled = this.hasAttribute('disabled'), suggest = this._suggest.length > 0;
|
|
1934
|
+
clear(this);
|
|
1935
|
+
var label = this._label != null ? textOf(this._label, lang) : (this.getAttribute('label') || t(lang, 'tagLabel'));
|
|
1936
|
+
var wrap = h('div', { 'class': 'one-field one-combobox' });
|
|
1937
|
+
add(wrap, h('label', { 'for': this._ids.input, 'class': 'one-label' }, [label].concat(requiredMarks(this, lang))));
|
|
1938
|
+
var described = [this.getAttribute('aria-describedby'), this._ids.input + '-help', this._ids.input + '-status'].filter(Boolean).join(' ');
|
|
1939
|
+
var input = h('input', { id: this._ids.input, type: 'text', 'class': 'one-tags-input', autocomplete: 'off', 'aria-describedby': described,
|
|
1940
|
+
placeholder: this.getAttribute('placeholder') || t(lang, 'tagPlaceholder'), 'aria-required': this.hasAttribute('required') ? 'true' : null,
|
|
1941
|
+
disabled: disabled ? true : null, 'data-one-focus': 'input', maxlength: String(this._maxLen() * 4) });
|
|
1942
|
+
if (suggest) setAttrs(input, { role: 'combobox', 'aria-autocomplete': 'list', 'aria-expanded': 'false', 'aria-controls': this._ids.list });
|
|
1943
|
+
input.value = this._q;
|
|
1944
|
+
listen(input, 'input', function () { self._q = input.value; self._msg = null; self._open = true; self._active = -1; self._update(); });
|
|
1945
|
+
// Yapıştırılan çok satırlı ya da virgüllü liste ayrı etiketlere bölünür (metin kutusu satır sonlarını kendiliğinden siler).
|
|
1946
|
+
listen(input, 'paste', function (ev) {
|
|
1947
|
+
var data = ev.clipboardData && typeof ev.clipboardData.getData === 'function' ? ev.clipboardData.getData('text') : '';
|
|
1948
|
+
if (!/[,\n\r]/.test(data)) return;
|
|
1949
|
+
ev.preventDefault();
|
|
1950
|
+
var parts = (self._q + data).split(/[,\n\r]+/), last = parts.pop();
|
|
1951
|
+
for (var i = 0; i < parts.length; i++) self._commit(parts[i]);
|
|
1952
|
+
self._setQuery(last || ''); self._update();
|
|
1953
|
+
});
|
|
1954
|
+
listen(input, 'keydown', function (ev) {
|
|
1955
|
+
var k = ev.key, n = self._shown.length;
|
|
1956
|
+
if (k === 'Enter' || k === ',') {
|
|
1957
|
+
ev.preventDefault();
|
|
1958
|
+
if (k === 'Enter' && self._open && n && self._active >= 0) self._commit(self._shown[self._active]); else self._commit(self._q);
|
|
1959
|
+
} else if (k === 'Backspace' && input.value === '' && self._tags.length && !disabled) {
|
|
1960
|
+
ev.preventDefault(); self._remove(self._tags.length - 1);
|
|
1961
|
+
} else if ((k === 'ArrowDown' || k === 'ArrowUp') && suggest) {
|
|
1962
|
+
ev.preventDefault();
|
|
1963
|
+
if (!self._open) { self._open = true; self._active = -1; self._update(); return; }
|
|
1964
|
+
if (n) { self._active = k === 'ArrowDown' ? (self._active + 1) % n : (self._active <= 0 ? n - 1 : self._active - 1); self._update(); }
|
|
1965
|
+
} else if (k === 'Escape' && self._open && n) {
|
|
1966
|
+
ev.preventDefault(); ev.stopPropagation(); self._open = false; self._update();
|
|
1967
|
+
}
|
|
1968
|
+
});
|
|
1969
|
+
var control = h('div', { 'class': 'one-tags-control', 'aria-disabled': disabled ? 'true' : null }, [h('ul', { 'class': 'one-chips', 'data-one-chips': '', 'aria-label': label, hidden: true }), input]);
|
|
1970
|
+
// Kutunun boş yerine tıklamak yazma alanına götürür (etiketler arasında kalan alan da kutunun parçası görünür).
|
|
1971
|
+
listen(control, 'mousedown', function (ev) { if (ev.target === control) { ev.preventDefault(); input.focus(); } });
|
|
1972
|
+
add(wrap, control);
|
|
1973
|
+
if (suggest) add(wrap, h('ul', { id: this._ids.list, role: 'listbox', 'class': 'one-listbox', 'aria-label': label, hidden: true }));
|
|
1974
|
+
add(wrap, h('p', { id: this._ids.input + '-help', 'class': 'one-help', text: t(lang, 'tagHelp') }));
|
|
1975
|
+
add(wrap, h('p', { id: this._ids.input + '-status', 'class': 'one-help', role: 'status', 'aria-live': 'polite', 'data-one-status': '' }));
|
|
1976
|
+
add(this, wrap);
|
|
1977
|
+
this._drawChips();
|
|
1978
|
+
this._update();
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
// ══ <one-dialog> (D-96) ═════════════════════════════════════════════════════════════════════════════════
|
|
1983
|
+
// Tarayıcının kendi kipli penceresi ('<dialog>.showModal()'): odak pencerede tutulur, sayfanın geri kalanı etkisizdir,
|
|
1984
|
+
// Escape "vazgeç" isteği doğurur. Pencere iframe'in GÖRÜNEN dilimine yerleşir (köprünün yazdığı '--one-viewport-*'
|
|
1985
|
+
// değişkenleri; betik konum yazmaz). Açıkken köprü Portal'a 'one:modal { open: true }' bildirir ve Portal kendi ekranında
|
|
1986
|
+
// da aynı karartmayı çizer. Portal karartmasına tıklanırsa (ya da Portal'da Escape) Portal karartmayı KENDİSİ kaldırır ve
|
|
1987
|
+
// 'one:modal-dismiss' gönderir: açık pencereler "vazgeçildi" diye kapanır — uygulama Portal'ı kilitleyemez.
|
|
1988
|
+
var openDialogs = [], dismissing = false;
|
|
1989
|
+
function syncModal() {
|
|
1990
|
+
// Portal'ın toplu vazgeçmesi sürerken eşitlenmez: kapanan her pencerede kalanlar için "açık" yeniden gönderilir ve
|
|
1991
|
+
// kullanıcı vazgeçtikten hemen sonra Portal yeniden kararırdı.
|
|
1992
|
+
if (dismissing) return;
|
|
1993
|
+
var one = w.one;
|
|
1994
|
+
if (one && typeof one.setModal === 'function') { try { one.setModal(openDialogs.length > 0); } catch (e) { report(e); } }
|
|
1995
|
+
}
|
|
1996
|
+
var dismissHooked = false;
|
|
1997
|
+
function hookDismiss() {
|
|
1998
|
+
if (dismissHooked || !w.one || typeof w.one.onModalDismiss !== 'function') return;
|
|
1999
|
+
dismissHooked = true;
|
|
2000
|
+
w.one.onModalDismiss(function () {
|
|
2001
|
+
var list = openDialogs.slice().reverse();
|
|
2002
|
+
dismissing = true;
|
|
2003
|
+
try {
|
|
2004
|
+
for (var i = 0; i < list.length; i++) { try { list[i]._dismiss('portal'); } catch (e) { report(e); } }
|
|
2005
|
+
} finally { dismissing = false; }
|
|
2006
|
+
// Kapanmayı reddeden pencere açık kalsa da Portal YENİDEN karartılmaz (kullanıcı vazgeçti; köprünün durumu zaten
|
|
2007
|
+
// kapalı). Hepsi kapandıysa kapalı durum köprüde zaten kapalıdır; bir sonraki gerçek değişim durumu yeniden bildirir.
|
|
2008
|
+
if (openDialogs.length === 0) syncModal();
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
function focusables(root) {
|
|
2012
|
+
var list = root.querySelectorAll('button, [href], input, select, textarea, [tabindex]');
|
|
2013
|
+
return Array.prototype.filter.call(list, function (n) { return !n.disabled && n.getAttribute('tabindex') !== '-1' && !n.closest('[hidden]'); });
|
|
2014
|
+
}
|
|
2015
|
+
class OneDialog extends Base {
|
|
2016
|
+
static get observedAttributes() { return ['lang', 'label', 'size', 'accept-label', 'reject-label', 'severity', 'alert', 'open']; }
|
|
2017
|
+
static get props() { return ['label']; }
|
|
2018
|
+
// 'open' özniteliği açık durumla aynı kaynaktır: sayfada <one-dialog open> açılır, öznitelik eklemek açar, kaldırmak
|
|
2019
|
+
// "vazgeçildi" ('') ile kapatır; bileşen de kendi durumunu bu özniteliğe yazar.
|
|
2020
|
+
start() { if (this.hasAttribute('open') && !this._open) this.show(); }
|
|
2021
|
+
attributeChanged(name) {
|
|
2022
|
+
if (name !== 'open') { this.render(); return; }
|
|
2023
|
+
var want = this.hasAttribute('open');
|
|
2024
|
+
if (want && !this._open) this.show();
|
|
2025
|
+
else if (!want && this._open) {
|
|
2026
|
+
// Portal'ın onay penceresi uygulamadan kapatılamaz: öznitelik gerçek durumu göstermeye devam eder.
|
|
2027
|
+
if (this._viaPortal) this.reflect('open', ''); else this.close('');
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
constructor() {
|
|
2031
|
+
super();
|
|
2032
|
+
this._label = null; this._open = false; this._viaPortal = false; this._promise = null; this._resolve = null; this._returnFocus = null; this._downOnSelf = false;
|
|
2033
|
+
this._ids = { title: uid('dialog-title'), body: uid('dialog-body') };
|
|
2034
|
+
}
|
|
2035
|
+
get label() { return this._label; }
|
|
2036
|
+
set label(v) { this._label = v == null ? null : v; this.render(); }
|
|
2037
|
+
get open() { return this._open; }
|
|
2038
|
+
/** Uygulamanın kendi içeriğini koyacağı gövde. */
|
|
2039
|
+
get body() { this._build(); return this._body; }
|
|
2040
|
+
_build() {
|
|
2041
|
+
if (this._dialog) return;
|
|
2042
|
+
var self = this;
|
|
2043
|
+
this._dialog = h('dialog', { 'class': 'one-dialog', 'aria-labelledby': this._ids.title });
|
|
2044
|
+
this._title = h('h2', { id: this._ids.title, 'class': 'one-dialog-title' });
|
|
2045
|
+
this._closeBtn = h('button', { type: 'button', 'class': 'one-icon-btn one-dialog-close', 'data-one-focus': 'close', text: '×' });
|
|
2046
|
+
this._body = h('div', { id: this._ids.body, 'class': 'one-dialog-body' });
|
|
2047
|
+
this._footer = h('div', { 'class': 'one-dialog-footer', hidden: true });
|
|
2048
|
+
add(this._dialog, h('div', { 'class': 'one-dialog-panel' }, [h('div', { 'class': 'one-dialog-header' }, [this._title, this._closeBtn]), this._body, this._footer]));
|
|
2049
|
+
this._adopt(Array.prototype.slice.call(this.childNodes));
|
|
2050
|
+
add(this, this._dialog);
|
|
2051
|
+
listen(this._closeBtn, 'click', function () { self._dismiss('close'); });
|
|
2052
|
+
// Escape: tarayıcının kapatma isteği önce bileşene sorulur (iptal edilebilir 'cancel' olayı).
|
|
2053
|
+
listen(this._dialog, 'cancel', function (ev) { ev.preventDefault(); self._dismiss('escape'); });
|
|
2054
|
+
listen(this._dialog, 'close', function () { self._finish(self._dialog.returnValue || ''); });
|
|
2055
|
+
// Karartılmış alana tıklama "vazgeç"tir; metin seçerken pencere dışına taşan sürükleme sayılmaz.
|
|
2056
|
+
listen(this._dialog, 'mousedown', function (ev) { self._downOnSelf = ev.target === self._dialog; });
|
|
2057
|
+
listen(this._dialog, 'click', function (ev) {
|
|
2058
|
+
var target = ev.target && ev.target.closest ? ev.target.closest('[data-one-close]') : null;
|
|
2059
|
+
if (target && self._dialog.contains(target)) { self.close(target.getAttribute('data-one-close') || ''); return; }
|
|
2060
|
+
if (ev.target === self._dialog && self._downOnSelf) self._dismiss('backdrop');
|
|
2061
|
+
self._downOnSelf = false;
|
|
2062
|
+
});
|
|
2063
|
+
// Bileşen kurulduktan SONRA eklenen alt öğeler de gövdeye taşınır.
|
|
2064
|
+
if (typeof w.MutationObserver === 'function') {
|
|
2065
|
+
new w.MutationObserver(function (records) {
|
|
2066
|
+
var nodes = [];
|
|
2067
|
+
records.forEach(function (r) { Array.prototype.forEach.call(r.addedNodes, function (n) { if (n !== self._dialog && n.parentNode === self) nodes.push(n); }); });
|
|
2068
|
+
if (nodes.length) self._adopt(nodes);
|
|
2069
|
+
}).observe(this, { childList: true });
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
_adopt(nodes) {
|
|
2073
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
2074
|
+
var n = nodes[i];
|
|
2075
|
+
if (n === this._dialog) continue;
|
|
2076
|
+
if (n.nodeType === 1 && n.hasAttribute('data-one-dialog-footer')) { this._footer.appendChild(n); this._footer.hidden = false; } else this._body.appendChild(n);
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
draw() {
|
|
2080
|
+
this._build();
|
|
2081
|
+
var lang = this.lang_(), size = this.getAttribute('size'), severity = this.getAttribute('severity');
|
|
2082
|
+
this._dialog.className = 'one-dialog' + (size === 'sm' || size === 'lg' ? ' one-dialog-' + size : '');
|
|
2083
|
+
if (this.hasAttribute('alert')) setAttrs(this._dialog, { role: 'alertdialog', 'aria-describedby': this._ids.body });
|
|
2084
|
+
else { this._dialog.removeAttribute('role'); this._dialog.removeAttribute('aria-describedby'); }
|
|
2085
|
+
this._title.textContent = this._label != null ? textOf(this._label, lang) : (this.getAttribute('label') || '');
|
|
2086
|
+
this._closeBtn.setAttribute('aria-label', t(lang, 'dialogClose'));
|
|
2087
|
+
// Onay penceresi: 'accept-label' / 'reject-label' verilirse alt bilgide iki düğme çizilir; sonuç 'accept' / 'reject'.
|
|
2088
|
+
var acc = this.getAttribute('accept-label'), rej = this.getAttribute('reject-label'), own_ = this._footer.querySelector('[data-one-confirm]');
|
|
2089
|
+
if (own_) own_.parentNode.removeChild(own_);
|
|
2090
|
+
if (acc || rej) {
|
|
2091
|
+
add(this._footer, h('div', { 'class': 'one-row', 'data-one-confirm': '' }, [
|
|
2092
|
+
rej ? h('button', { type: 'button', 'class': 'one-btn', 'data-one-close': 'reject', 'data-one-focus': 'reject', text: rej }) : null,
|
|
2093
|
+
acc ? h('button', { type: 'button', 'class': 'one-btn ' + (severity === 'danger' ? 'one-btn-danger' : 'one-btn-primary'), 'data-one-close': 'accept', 'data-one-focus': 'accept', text: acc }) : null]));
|
|
2094
|
+
}
|
|
2095
|
+
this._footer.hidden = this._footer.childNodes.length === 0;
|
|
2096
|
+
}
|
|
2097
|
+
_isConfirm() { return this.hasAttribute('accept-label') || this.hasAttribute('reject-label'); }
|
|
2098
|
+
// Onay penceresi Portal içindeyken Portal'ın kendi onay penceresiyle sorulur (D-76 U4 - D-96 sözleşmesi): iframe içinde
|
|
2099
|
+
// ikinci bir onay görünümü çizilmez. İçerik DÜZ METNE indirgenir (Portal HTML yorumlamaz).
|
|
2100
|
+
_confirmViaPortal() {
|
|
2101
|
+
var one = w.one;
|
|
2102
|
+
return !!(this._isConfirm() && one && one.framed && typeof one.confirm === 'function');
|
|
2103
|
+
}
|
|
2104
|
+
_askPortal() {
|
|
2105
|
+
var self = this, one = w.one, lang = this.lang_();
|
|
2106
|
+
var title = this._label != null ? textOf(this._label, lang) : (this.getAttribute('label') || '');
|
|
2107
|
+
var message = cleanTag(this._body.textContent) || title || t(lang, 'dialogConfirm');
|
|
2108
|
+
var sev = this.getAttribute('severity'), opts = { message: message.slice(0, 1000) };
|
|
2109
|
+
if (title && title !== message) opts.title = title.slice(0, 120);
|
|
2110
|
+
if (sev === 'danger' || sev === 'warn' || sev === 'info') opts.severity = sev;
|
|
2111
|
+
if (this.getAttribute('accept-label')) opts.acceptLabel = this.getAttribute('accept-label');
|
|
2112
|
+
if (this.getAttribute('reject-label')) opts.rejectLabel = this.getAttribute('reject-label');
|
|
2113
|
+
this._open = true;
|
|
2114
|
+
this._viaPortal = true;
|
|
2115
|
+
this.reflect('open', '');
|
|
2116
|
+
this._promise = new Promise(function (resolve) { self._resolve = resolve; });
|
|
2117
|
+
emit(this, 'open', {});
|
|
2118
|
+
var settle = function (accepted) {
|
|
2119
|
+
self._viaPortal = false;
|
|
2120
|
+
self._finish(accepted ? 'accept' : 'reject');
|
|
2121
|
+
};
|
|
2122
|
+
try { one.confirm(opts).then(settle, function (e) { report(e); settle(false); }); } catch (e) { report(e); settle(false); }
|
|
2123
|
+
return this._promise;
|
|
2124
|
+
}
|
|
2125
|
+
/**
|
|
2126
|
+
* Pencereyi açar; kapanınca sonuç değeriyle çözülür ('' = vazgeçildi). Onay penceresi ('accept-label' / 'reject-label')
|
|
2127
|
+
* Portal içindeyken Portal'ın onay penceresine yönlenir ve 'accept' / 'reject' ile çözülür.
|
|
2128
|
+
*/
|
|
2129
|
+
show() {
|
|
2130
|
+
if (!this.isConnected) return Promise.reject(new Error('one-dialog: sayfaya eklenmemiş pencere açılamaz'));
|
|
2131
|
+
if (this._open) return this._promise;
|
|
2132
|
+
this._build();
|
|
2133
|
+
if (this._confirmViaPortal()) return this._askPortal();
|
|
2134
|
+
var self = this, d = this._dialog;
|
|
2135
|
+
this._returnFocus = doc.activeElement;
|
|
2136
|
+
if (d.hasAttribute('open')) d.removeAttribute('open'); // kalıntı 'open' varken showModal() InvalidStateError atar
|
|
2137
|
+
d.returnValue = '';
|
|
2138
|
+
// Kipsiz yedek (showModal yok ya da hata verdi) işaretlenir: tarayıcı Escape'i orada işlemez, köprü Portal'a iletir.
|
|
2139
|
+
d.removeAttribute('data-one-modeless');
|
|
2140
|
+
var modeless = false;
|
|
2141
|
+
if (typeof d.showModal === 'function') { try { d.showModal(); } catch (e) { report(e); modeless = true; } } else modeless = true;
|
|
2142
|
+
if (modeless) { d.setAttribute('data-one-modeless', ''); d.setAttribute('open', ''); }
|
|
2143
|
+
this._open = true;
|
|
2144
|
+
this.reflect('open', '');
|
|
2145
|
+
this._promise = new Promise(function (resolve) { self._resolve = resolve; });
|
|
2146
|
+
openDialogs.push(this);
|
|
2147
|
+
hookDismiss();
|
|
2148
|
+
syncModal();
|
|
2149
|
+
this._focusFirst();
|
|
2150
|
+
emit(this, 'open', {});
|
|
2151
|
+
return this._promise;
|
|
2152
|
+
}
|
|
2153
|
+
// İlk odak: 'autofocus' taşıyan öğe → onay penceresinde "vazgeç" (yıkıcı olmayan seçenek) → gövdedeki ilk odaklanabilir
|
|
2154
|
+
// öğe → kapat düğmesi.
|
|
2155
|
+
_focusFirst() {
|
|
2156
|
+
var auto = this._dialog.querySelector('[autofocus]'), reject = this._footer.querySelector('[data-one-close="reject"]');
|
|
2157
|
+
var target = auto || reject || focusables(this._body)[0] || focusables(this._footer)[0] || this._closeBtn;
|
|
2158
|
+
if (target && typeof target.focus === 'function') target.focus();
|
|
2159
|
+
}
|
|
2160
|
+
/** Pencereyi kapatır; 'value' sonuç değeridir. */
|
|
2161
|
+
close(value) {
|
|
2162
|
+
// Portal'ın onay penceresi uygulamadan kapatılamaz; yanıt gelince çözülür.
|
|
2163
|
+
if (!this._open || this._viaPortal) return;
|
|
2164
|
+
var rv = value == null ? '' : String(value), d = this._dialog;
|
|
2165
|
+
if (typeof d.close === 'function' && d.open) { d.close(rv); if (this._open) this._finish(rv); }
|
|
2166
|
+
else { d.removeAttribute('open'); this._finish(rv); }
|
|
2167
|
+
}
|
|
2168
|
+
// Vazgeçme (Escape - kapat düğmesi - karartmaya tıklama - Portal): iptal edilebilir 'cancel' olayı; iptal edilmezse kapanır.
|
|
2169
|
+
_dismiss(reason) {
|
|
2170
|
+
if (!this._open || this._viaPortal) return;
|
|
2171
|
+
if (emit(this, 'cancel', { reason: reason }, true)) this.close('');
|
|
2172
|
+
}
|
|
2173
|
+
_finish(rv) {
|
|
2174
|
+
if (!this._open) return;
|
|
2175
|
+
this._open = false;
|
|
2176
|
+
// İçteki <dialog> da kapanır: sayfadan ayrılan <dialog> üst katmandan çıkar ama 'open' özniteliğini korur; kalırsa
|
|
2177
|
+
// yeniden eklenince kipsiz ve kapatılamaz bir panel görünür.
|
|
2178
|
+
var d = this._dialog;
|
|
2179
|
+
if (d && d.hasAttribute('open')) {
|
|
2180
|
+
if (d.isConnected && typeof d.close === 'function') { try { d.close(rv); } catch (e) { report(e); } }
|
|
2181
|
+
if (d.hasAttribute('open')) d.removeAttribute('open');
|
|
2182
|
+
}
|
|
2183
|
+
this.reflect('open', null);
|
|
2184
|
+
var i = openDialogs.indexOf(this);
|
|
2185
|
+
if (i >= 0) openDialogs.splice(i, 1);
|
|
2186
|
+
syncModal();
|
|
2187
|
+
var back = this._returnFocus;
|
|
2188
|
+
this._returnFocus = null;
|
|
2189
|
+
if (this.isConnected && back && back.isConnected && typeof back.focus === 'function') { try { back.focus(); } catch (e) { report(e); } }
|
|
2190
|
+
emit(this, 'close', { returnValue: rv });
|
|
2191
|
+
var resolve = this._resolve;
|
|
2192
|
+
this._resolve = null;
|
|
2193
|
+
if (resolve) resolve(rv);
|
|
2194
|
+
}
|
|
2195
|
+
disconnectedCallback() { super.disconnectedCallback(); if (this._open && !this._viaPortal) this._finish(''); }
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
// ══ Sayfa içi bildirim (D-96) — yalnız Portal DIŞINDA ═════════════════════════════════════════════════════
|
|
2199
|
+
// Portal içindeyken 'one.toast' bildirimi Portal'a verir ve Portal çizer (tek görünüm). Çerçevesiz açılışta (yerel
|
|
2200
|
+
// geliştirme) köprü 'one:toast' olayını yayar; burada sayfanın üst katmanında (popover) gösterilir.
|
|
2201
|
+
var TOAST_SEVERITIES = ['success', 'info', 'warn', 'error'];
|
|
2202
|
+
var toastRegion = null;
|
|
2203
|
+
function toastLang() { return langOf(doc.documentElement); }
|
|
2204
|
+
function dropToast(item) {
|
|
2205
|
+
if (item.parentNode) item.parentNode.removeChild(item);
|
|
2206
|
+
if (toastRegion && !toastRegion.firstChild) {
|
|
2207
|
+
if (typeof toastRegion.hidePopover === 'function') { try { toastRegion.hidePopover(); } catch (e) { /* zaten kapalı */ } }
|
|
2208
|
+
if (toastRegion.parentNode) toastRegion.parentNode.removeChild(toastRegion);
|
|
2209
|
+
toastRegion = null;
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
function showLocalToast(d) {
|
|
2213
|
+
var lang = toastLang();
|
|
2214
|
+
if (!toastRegion) {
|
|
2215
|
+
toastRegion = h('div', { 'class': 'one-toast-region', role: 'region', 'aria-label': t(lang, 'toasts') });
|
|
2216
|
+
if (typeof toastRegion.showPopover === 'function') toastRegion.setAttribute('popover', 'manual');
|
|
2217
|
+
add(doc.body, toastRegion);
|
|
2218
|
+
if (typeof toastRegion.showPopover === 'function') { try { toastRegion.showPopover(); } catch (e) { report(e); } }
|
|
2219
|
+
}
|
|
2220
|
+
var close = h('button', { type: 'button', 'class': 'one-icon-btn', 'aria-label': t(lang, 'toastClose'), text: '×' });
|
|
2221
|
+
var item = h('div', { 'class': 'one-message one-message-' + d.severity + ' one-toast', role: d.severity === 'error' ? 'alert' : 'status' }, [
|
|
2222
|
+
h('div', { 'class': 'one-toast-text' }, [h('strong', { 'class': 'one-toast-title', text: d.summary }), d.detail ? h('span', { text: d.detail }) : null]), close]);
|
|
2223
|
+
listen(close, 'click', function () { dropToast(item); });
|
|
2224
|
+
add(toastRegion, item);
|
|
2225
|
+
w.setTimeout(function () { dropToast(item); }, d.severity === 'error' ? 8000 : 5000);
|
|
2226
|
+
}
|
|
2227
|
+
w.addEventListener('one:toast', function (ev) {
|
|
2228
|
+
var d = ev.detail;
|
|
2229
|
+
if (!d || TOAST_SEVERITIES.indexOf(d.severity) < 0 || typeof d.summary !== 'string' || !doc.body) return;
|
|
2230
|
+
ev.preventDefault();
|
|
2231
|
+
showLocalToast({ severity: d.severity, summary: d.summary, detail: typeof d.detail === 'string' ? d.detail : '' });
|
|
2232
|
+
});
|
|
2233
|
+
|
|
2234
|
+
// ══ Açılır öğe yerleşimi (ipucu, eylem menüsü) ═══════════════════════════════════════════════════════════
|
|
2235
|
+
// Mutlak konum kapsayıcıya göredir; betik yalnız ÖLÇER ve 'data-placement' / 'data-align' yazar (konum CSS'tedir).
|
|
2236
|
+
// Görünen alan: Portal'ın bildirdiği dilim (one.viewport) — yoksa pencere.
|
|
2237
|
+
function visibleArea() {
|
|
2238
|
+
var v = w.one && w.one.viewport && w.one.viewport.current;
|
|
2239
|
+
if (v && v.height > 0) return { top: v.top, bottom: v.top + v.height };
|
|
2240
|
+
return { top: 0, bottom: w.innerHeight || doc.documentElement.clientHeight || 0 };
|
|
2241
|
+
}
|
|
2242
|
+
function place(pop, anchor, preferTop) {
|
|
2243
|
+
pop.removeAttribute('data-placement'); pop.removeAttribute('data-align');
|
|
2244
|
+
if (typeof pop.getBoundingClientRect !== 'function') return;
|
|
2245
|
+
var area = visibleArea(), a = anchor.getBoundingClientRect(), r = pop.getBoundingClientRect(), width = doc.documentElement.clientWidth || w.innerWidth || 0;
|
|
2246
|
+
if (r.height === 0 && r.width === 0) return;
|
|
2247
|
+
var roomAbove = a.top - area.top, roomBelow = area.bottom - a.bottom;
|
|
2248
|
+
var top = preferTop ? roomAbove >= r.height + 8 || roomAbove >= roomBelow : !(roomBelow >= r.height + 8) && roomAbove > roomBelow && roomAbove >= r.height + 8;
|
|
2249
|
+
pop.setAttribute('data-placement', top ? 'top' : 'bottom');
|
|
2250
|
+
r = pop.getBoundingClientRect();
|
|
2251
|
+
if (width > 0 && r.right > width - 4) pop.setAttribute('data-align', 'end');
|
|
2252
|
+
else if (r.left < 4) pop.setAttribute('data-align', 'start');
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
// ══ <one-tooltip> (D-96) ════════════════════════════════════════════════════════════════════════════════
|
|
2256
|
+
// İlk alt öğe tetikleyicidir (odaklanabilir olmalı: düğme, bağlantı). İpucu fareyle üzerine gelince ve klavye odağıyla
|
|
2257
|
+
// görünür, Escape ile kapanır, fare ipucunun üzerine geçebilir (WCAG 1.4.13). Metin düz metindir.
|
|
2258
|
+
class OneTooltip extends Base {
|
|
2259
|
+
static get observedAttributes() { return ['lang', 'text', 'text-en', 'placement']; }
|
|
2260
|
+
static get props() { return ['text']; }
|
|
2261
|
+
constructor() {
|
|
2262
|
+
super();
|
|
2263
|
+
this._text = null; this._visible = false; this._hideTimer = 0; this._showTimer = 0; this._id = uid('tooltip');
|
|
2264
|
+
var self = this;
|
|
2265
|
+
this._onKey = function (ev) { if (ev.key === 'Escape' && self._visible) { ev.preventDefault(); ev.stopPropagation(); self._hide(); } };
|
|
2266
|
+
listen(this, 'mouseenter', function () { w.clearTimeout(self._hideTimer); w.clearTimeout(self._showTimer); self._showTimer = w.setTimeout(function () { self._show(); }, 250); });
|
|
2267
|
+
listen(this, 'mouseleave', function () { w.clearTimeout(self._showTimer); self._hideTimer = w.setTimeout(function () { if (!self.contains(doc.activeElement)) self._hide(); }, 150); });
|
|
2268
|
+
listen(this, 'focusin', function () { w.clearTimeout(self._hideTimer); self._show(); });
|
|
2269
|
+
listen(this, 'focusout', function (ev) { if (!ev.relatedTarget || !self.contains(ev.relatedTarget)) self._hide(); });
|
|
2270
|
+
}
|
|
2271
|
+
get text() { return this._text; }
|
|
2272
|
+
set text(v) { this._text = v == null ? null : v; this.render(); }
|
|
2273
|
+
_trigger() {
|
|
2274
|
+
for (var n = this.firstElementChild; n; n = n.nextElementSibling) if (n !== this._bubble) return n;
|
|
2275
|
+
return null;
|
|
2276
|
+
}
|
|
2277
|
+
_show() {
|
|
2278
|
+
if (!this._bubble || !this._bubble.textContent) return;
|
|
2279
|
+
if (!this._visible) { this._visible = true; doc.addEventListener('keydown', this._onKey, true); }
|
|
2280
|
+
this._bubble.hidden = false;
|
|
2281
|
+
var trigger = this._trigger();
|
|
2282
|
+
if (trigger) place(this._bubble, trigger, this.getAttribute('placement') !== 'bottom');
|
|
2283
|
+
}
|
|
2284
|
+
_hide() {
|
|
2285
|
+
if (!this._bubble) return;
|
|
2286
|
+
this._visible = false;
|
|
2287
|
+
doc.removeEventListener('keydown', this._onKey, true);
|
|
2288
|
+
this._bubble.hidden = true;
|
|
2289
|
+
}
|
|
2290
|
+
draw() {
|
|
2291
|
+
var self = this, lang = this.lang_();
|
|
2292
|
+
if (!this._bubble) {
|
|
2293
|
+
this._bubble = h('span', { id: this._id, role: 'tooltip', 'class': 'one-tooltip-bubble', 'data-one-popup': '', hidden: true });
|
|
2294
|
+
add(this, this._bubble);
|
|
2295
|
+
// Tetikleyici bileşen bağlandıktan sonra gelebilir (betik sayfanın başında): gelince ipucuna bağlanır.
|
|
2296
|
+
if (typeof w.MutationObserver === 'function') new w.MutationObserver(function () { self.render(); }).observe(this, { childList: true });
|
|
2297
|
+
}
|
|
2298
|
+
var text = this._text != null ? textOf(this._text, lang) : (lang === 'en' && this.getAttribute('text-en') ? this.getAttribute('text-en') : (this.getAttribute('text') || ''));
|
|
2299
|
+
this._bubble.textContent = text;
|
|
2300
|
+
var trigger = this._trigger();
|
|
2301
|
+
if (trigger) {
|
|
2302
|
+
var ids = (trigger.getAttribute('aria-describedby') || '').split(/\s+/).filter(Boolean);
|
|
2303
|
+
if (ids.indexOf(this._id) < 0) { ids.push(this._id); trigger.setAttribute('aria-describedby', ids.join(' ')); }
|
|
2304
|
+
}
|
|
2305
|
+
if (!text) this._hide();
|
|
2306
|
+
}
|
|
2307
|
+
disconnectedCallback() { super.disconnectedCallback(); this._hide(); w.clearTimeout(this._hideTimer); w.clearTimeout(this._showTimer); }
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
// ══ <one-menu> (D-96) ═══════════════════════════════════════════════════════════════════════════════════
|
|
2311
|
+
// Eylem menüsü: APG "menu button" kalıbı. Seçilen öğe 'select { id }' olayıyla bildirilir.
|
|
2312
|
+
function cleanMenuItems(list) {
|
|
2313
|
+
if (!Array.isArray(list)) throw new TypeError('one-menu: items bir dizi olmalı');
|
|
2314
|
+
var out = [], seen = Object.create(null);
|
|
2315
|
+
for (var i = 0; i < list.length; i++) {
|
|
2316
|
+
var it = list[i];
|
|
2317
|
+
if (!it || typeof it !== 'object') continue;
|
|
2318
|
+
if (it.separator === true) { if (out.length && !out[out.length - 1].separator) out.push({ separator: true }); continue; }
|
|
2319
|
+
if (typeof it.id !== 'string' || !KEY_RE.test(it.id) || it.id in seen) continue;
|
|
2320
|
+
seen[it.id] = true;
|
|
2321
|
+
out.push({ id: it.id, label: it.label == null ? it.id : it.label, danger: it.danger === true, disabled: it.disabled === true });
|
|
2322
|
+
}
|
|
2323
|
+
while (out.length && out[out.length - 1].separator) out.pop();
|
|
2324
|
+
return out;
|
|
2325
|
+
}
|
|
2326
|
+
class OneMenu extends Base {
|
|
2327
|
+
static get observedAttributes() { return ['lang', 'label', 'disabled', 'compact']; }
|
|
2328
|
+
static get props() { return ['items', 'label']; }
|
|
2329
|
+
constructor() {
|
|
2330
|
+
super();
|
|
2331
|
+
this._items = []; this._label = null; this._open = false; this._typed = ''; this._typedAt = 0; this._drawing = false;
|
|
2332
|
+
this._ids = { button: uid('menu-button'), list: uid('menu') };
|
|
2333
|
+
var self = this;
|
|
2334
|
+
this._onDown = function (ev) { if (self._open && !self.contains(ev.target)) self._close(false); };
|
|
2335
|
+
listen(this, 'focusout', function (ev) { if (!self._drawing && self._open && (!ev.relatedTarget || !self.contains(ev.relatedTarget))) self._close(false); });
|
|
2336
|
+
}
|
|
2337
|
+
get items() { return this._items.map(function (it) { return it.separator ? { separator: true } : { id: it.id, label: it.label, danger: it.danger, disabled: it.disabled }; }); }
|
|
2338
|
+
set items(v) { this._items = cleanMenuItems(v == null ? [] : v); this.render(); }
|
|
2339
|
+
get label() { return this._label; }
|
|
2340
|
+
set label(v) { this._label = v == null ? null : v; this.render(); }
|
|
2341
|
+
_entries() { return Array.prototype.slice.call(this.querySelectorAll('[role=menuitem]')); }
|
|
2342
|
+
_focusAt(i) { var list = this._entries(); if (!list.length) return; var n = list[((i % list.length) + list.length) % list.length]; n.focus(); }
|
|
2343
|
+
_openMenu(at) {
|
|
2344
|
+
if (this.hasAttribute('disabled') || !this._items.some(function (it) { return !it.separator; })) return;
|
|
2345
|
+
var list = doc.getElementById(this._ids.list), btn = doc.getElementById(this._ids.button);
|
|
2346
|
+
if (!list || !btn) return;
|
|
2347
|
+
this._open = true;
|
|
2348
|
+
list.hidden = false;
|
|
2349
|
+
btn.setAttribute('aria-expanded', 'true');
|
|
2350
|
+
doc.addEventListener('mousedown', this._onDown, true);
|
|
2351
|
+
place(list, btn, false);
|
|
2352
|
+
this._focusAt(at === 'last' ? -1 : 0);
|
|
2353
|
+
}
|
|
2354
|
+
_close(focusButton) {
|
|
2355
|
+
var list = doc.getElementById(this._ids.list), btn = doc.getElementById(this._ids.button);
|
|
2356
|
+
this._open = false;
|
|
2357
|
+
doc.removeEventListener('mousedown', this._onDown, true);
|
|
2358
|
+
if (list) list.hidden = true;
|
|
2359
|
+
if (btn) { btn.setAttribute('aria-expanded', 'false'); if (focusButton) btn.focus(); }
|
|
2360
|
+
}
|
|
2361
|
+
_activate(it) {
|
|
2362
|
+
if (!it || it.disabled) return;
|
|
2363
|
+
this._close(true);
|
|
2364
|
+
emit(this, 'select', { id: it.id });
|
|
2365
|
+
}
|
|
2366
|
+
draw() {
|
|
2367
|
+
this._drawing = true;
|
|
2368
|
+
try { this._draw(); } finally { this._drawing = false; }
|
|
2369
|
+
}
|
|
2370
|
+
_draw() {
|
|
2371
|
+
var self = this, lang = this.lang_(), compact = this.hasAttribute('compact');
|
|
2372
|
+
var wasOpen = this._open;
|
|
2373
|
+
clear(this);
|
|
2374
|
+
var label = this._label != null ? textOf(this._label, lang) : (this.getAttribute('label') || t(lang, 'menuLabel'));
|
|
2375
|
+
var btn = h('button', { type: 'button', id: this._ids.button, 'class': compact ? 'one-btn one-btn-text' : 'one-btn', 'aria-haspopup': 'menu', 'aria-expanded': 'false',
|
|
2376
|
+
'aria-controls': this._ids.list, 'aria-label': compact ? label : null, disabled: this.hasAttribute('disabled') ? true : null, 'data-one-focus': 'button' },
|
|
2377
|
+
compact ? [h('span', { 'aria-hidden': 'true', text: '⋯' })] : [label, h('span', { 'aria-hidden': 'true', 'class': 'one-sort-icon', text: '▾' })]);
|
|
2378
|
+
listen(btn, 'click', function () { if (self._open) self._close(false); else self._openMenu('first'); });
|
|
2379
|
+
listen(btn, 'keydown', function (ev) {
|
|
2380
|
+
if (ev.key === 'ArrowDown' || ev.key === 'ArrowUp') { ev.preventDefault(); self._openMenu(ev.key === 'ArrowUp' ? 'last' : 'first'); }
|
|
2381
|
+
});
|
|
2382
|
+
var list = h('ul', { id: this._ids.list, role: 'menu', 'class': 'one-menu-list', 'aria-labelledby': this._ids.button, 'data-one-popup': '', hidden: true });
|
|
2383
|
+
this._items.forEach(function (it) {
|
|
2384
|
+
if (it.separator) { add(list, h('li', { role: 'separator', 'class': 'one-menu-sep' })); return; }
|
|
2385
|
+
var li = h('li', { role: 'menuitem', tabindex: '-1', 'class': 'one-menu-item' + (it.danger ? ' one-menu-item-danger' : ''), 'aria-disabled': it.disabled ? 'true' : null,
|
|
2386
|
+
'data-one-focus': 'item:' + it.id, text: textOf(it.label, lang) });
|
|
2387
|
+
listen(li, 'click', function () { self._activate(it); });
|
|
2388
|
+
add(list, li);
|
|
2389
|
+
});
|
|
2390
|
+
listen(list, 'keydown', function (ev) {
|
|
2391
|
+
var entries = self._entries(), i = entries.indexOf(doc.activeElement), k = ev.key;
|
|
2392
|
+
if (k === 'ArrowDown' || k === 'ArrowUp') { ev.preventDefault(); self._focusAt(i + (k === 'ArrowDown' ? 1 : -1)); }
|
|
2393
|
+
else if (k === 'Home' || k === 'End') { ev.preventDefault(); self._focusAt(k === 'Home' ? 0 : -1); }
|
|
2394
|
+
else if (k === 'Enter' || k === ' ') {
|
|
2395
|
+
ev.preventDefault();
|
|
2396
|
+
var id = i >= 0 ? (entries[i].getAttribute('data-one-focus') || '').slice(5) : null;
|
|
2397
|
+
for (var j = 0; j < self._items.length; j++) if (self._items[j].id === id) { self._activate(self._items[j]); break; }
|
|
2398
|
+
}
|
|
2399
|
+
else if (k === 'Escape') { ev.preventDefault(); ev.stopPropagation(); self._close(true); }
|
|
2400
|
+
else if (k === 'Tab') self._close(false);
|
|
2401
|
+
else if (k.length === 1 && /\S/.test(k) && !ev.ctrlKey && !ev.metaKey && !ev.altKey) {
|
|
2402
|
+
// Yazarak atlama: yazılan harflerle başlayan sıradaki öğeye gidilir (1 sn içinde yazılanlar birleşir).
|
|
2403
|
+
var now = Date.now();
|
|
2404
|
+
self._typed = (now - self._typedAt < 1000 ? self._typed : '') + fold(k); self._typedAt = now;
|
|
2405
|
+
for (var s = 1; s <= entries.length; s++) {
|
|
2406
|
+
var e = entries[(i + s) % entries.length];
|
|
2407
|
+
if (fold(e.textContent).indexOf(self._typed) === 0) { e.focus(); break; }
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
});
|
|
2411
|
+
add(this, [btn, list]);
|
|
2412
|
+
this._open = false;
|
|
2413
|
+
if (wasOpen) this._openMenu('first');
|
|
2414
|
+
}
|
|
2415
|
+
disconnectedCallback() { super.disconnectedCallback(); doc.removeEventListener('mousedown', this._onDown, true); this._open = false; }
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
// ══ <one-avatar> (D-96) ═════════════════════════════════════════════════════════════════════════════════
|
|
2419
|
+
function initials(name) {
|
|
2420
|
+
var words = cleanTag(name).split(' ').filter(Boolean);
|
|
2421
|
+
if (!words.length) return '?';
|
|
2422
|
+
var s = words[0].charAt(0) + (words.length > 1 ? words[words.length - 1].charAt(0) : '');
|
|
2423
|
+
return s.toLocaleUpperCase('tr-TR');
|
|
2424
|
+
}
|
|
2425
|
+
function avatarTone(name) {
|
|
2426
|
+
var f = fold(cleanTag(name)), sum = 0;
|
|
2427
|
+
for (var i = 0; i < f.length; i++) sum = (sum * 31 + f.charCodeAt(i)) % 9973;
|
|
2428
|
+
return (sum % 6) + 1;
|
|
2429
|
+
}
|
|
2430
|
+
// Görsel adresi: uygulamanın kendi adresi, https, ya da data:image / blob: (sayfanın görsel kuralı izin verdiyse yüklenir).
|
|
2431
|
+
function imageSrc(v) {
|
|
2432
|
+
if (typeof v !== 'string' || !v.trim()) return null;
|
|
2433
|
+
var u;
|
|
2434
|
+
try { u = new URL(v.trim(), w.location.href); } catch (e) { return null; }
|
|
2435
|
+
if (u.protocol === 'https:' || u.protocol === 'blob:') return u.href;
|
|
2436
|
+
if (u.protocol === 'data:' && /^data:image\//i.test(u.href)) return u.href;
|
|
2437
|
+
if (u.protocol === 'http:' && u.origin === w.location.origin) return u.href;
|
|
2438
|
+
return null;
|
|
2439
|
+
}
|
|
2440
|
+
class OneAvatar extends Base {
|
|
2441
|
+
static get observedAttributes() { return ['lang', 'name', 'src', 'size']; }
|
|
2442
|
+
constructor() { super(); this._failed = null; }
|
|
2443
|
+
draw() {
|
|
2444
|
+
var self = this, lang = this.lang_(), name = cleanTag(this.getAttribute('name')), src = imageSrc(this.getAttribute('src')), size = this.getAttribute('size');
|
|
2445
|
+
clear(this);
|
|
2446
|
+
var box = h('span', { 'class': 'one-avatar one-avatar-c' + avatarTone(name) + (size === 'sm' || size === 'lg' ? ' one-avatar-' + size : ''), role: 'img', 'aria-label': name || t(lang, 'avatarUnknown') });
|
|
2447
|
+
if (src && src !== this._failed) {
|
|
2448
|
+
var img = h('img', { src: src, alt: '', loading: 'lazy', decoding: 'async' });
|
|
2449
|
+
listen(img, 'error', function () { self._failed = src; self.render(); });
|
|
2450
|
+
add(box, img);
|
|
2451
|
+
} else add(box, h('span', { 'aria-hidden': 'true', text: initials(name) }));
|
|
2452
|
+
add(this, box);
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
// ══ <one-tabs> (D-96) ═══════════════════════════════════════════════════════════════════════════════════
|
|
2457
|
+
// Alt öğeler sekme panelleridir: 'data-label' (ve isteğe bağlı 'data-label-en', 'data-tab', 'data-disabled'). APG "tabs"
|
|
2458
|
+
// kalıbı: ok tuşları sekmeler arasında gezer ve seçer (otomatik etkinleştirme), Home/End uçlara gider.
|
|
2459
|
+
function panelLabel(p, lang) { return (lang === 'en' && p.getAttribute('data-label-en')) || p.getAttribute('data-label') || ''; }
|
|
2460
|
+
class OneTabs extends Base {
|
|
2461
|
+
static get observedAttributes() { return ['lang', 'label', 'selected']; }
|
|
2462
|
+
static get props() { return ['selected']; }
|
|
2463
|
+
// Tek doğruluk kaynağı 'selected' özniteliğidir: özellik ona yazar, kullanıcının seçimi ona yansır, uygulamanın
|
|
2464
|
+
// sonradan yazdığı değer de oradan okunur. Özellik her zaman GÖRÜNEN sekmeyi döner (yoksa ilk etkin panel).
|
|
2465
|
+
constructor() { super(); this._list = null; this._uid = uid('tabs'); }
|
|
2466
|
+
get selected() { return this._current(this._panels()); }
|
|
2467
|
+
set selected(v) {
|
|
2468
|
+
if (v == null) this.removeAttribute('selected'); else this.setAttribute('selected', String(v));
|
|
2469
|
+
this.render();
|
|
2470
|
+
}
|
|
2471
|
+
_panels() { var self = this; return Array.prototype.filter.call(this.children, function (n) { return n !== self._list; }); }
|
|
2472
|
+
_key(p, i) { var k = p.getAttribute('data-tab'); return k && KEY_RE.test(k) ? k : String(i); }
|
|
2473
|
+
_current(panels) {
|
|
2474
|
+
var want = this.getAttribute('selected'), first = null;
|
|
2475
|
+
for (var i = 0; i < panels.length; i++) {
|
|
2476
|
+
if (panels[i].hasAttribute('data-disabled')) continue;
|
|
2477
|
+
if (first === null) first = this._key(panels[i], i);
|
|
2478
|
+
if (want != null && this._key(panels[i], i) === want) return want;
|
|
2479
|
+
}
|
|
2480
|
+
return first;
|
|
2481
|
+
}
|
|
2482
|
+
_select(key, focus) {
|
|
2483
|
+
if (key === this._current(this._panels())) { if (focus) this._focusTab(key); return; }
|
|
2484
|
+
this.setAttribute('selected', key);
|
|
2485
|
+
this.render();
|
|
2486
|
+
if (focus) this._focusTab(key);
|
|
2487
|
+
emit(this, 'change', { tab: key });
|
|
2488
|
+
}
|
|
2489
|
+
_focusTab(key) { var b = doc.getElementById(this._uid + '-tab-' + key); if (b) b.focus(); }
|
|
2490
|
+
draw() {
|
|
2491
|
+
var self = this, lang = this.lang_(), panels = this._panels(), current = this._current(panels);
|
|
2492
|
+
if (!this._list) {
|
|
2493
|
+
this._list = h('div', { role: 'tablist', 'class': 'one-tabs' });
|
|
2494
|
+
this.insertBefore(this._list, this.firstChild);
|
|
2495
|
+
// Sonradan eklenen panel ve panelin etiketi/durumu (data-label …) sekme şeridine yansır.
|
|
2496
|
+
if (typeof w.MutationObserver === 'function') {
|
|
2497
|
+
new w.MutationObserver(function (records) {
|
|
2498
|
+
// Yalnız bileşenin KENDİ çocukları: alt ağaçtaki değişim (sekme şeridinin yeniden çizimi dahil) sayılmaz.
|
|
2499
|
+
if (records.some(function (r) { return r.type === 'childList' ? r.target === self : r.target.parentNode === self; })) self.render();
|
|
2500
|
+
}).observe(this, { childList: true, subtree: true, attributes: true, attributeFilter: ['data-label', 'data-label-en', 'data-disabled', 'data-tab'] });
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
var label = this.getAttribute('label');
|
|
2504
|
+
if (label) this._list.setAttribute('aria-label', label); else this._list.removeAttribute('aria-label');
|
|
2505
|
+
clear(this._list);
|
|
2506
|
+
var keys = [];
|
|
2507
|
+
panels.forEach(function (p, i) {
|
|
2508
|
+
var key = self._key(p, i), selected = key === current, disabled = p.hasAttribute('data-disabled');
|
|
2509
|
+
keys.push(key);
|
|
2510
|
+
var tabId = self._uid + '-tab-' + key, panelId = p.id || (self._uid + '-panel-' + key);
|
|
2511
|
+
if (!p.id) p.id = panelId;
|
|
2512
|
+
setAttrs(p, { role: 'tabpanel', 'aria-labelledby': tabId, tabindex: '0' });
|
|
2513
|
+
p.classList.add('one-tab-panel');
|
|
2514
|
+
p.hidden = !selected;
|
|
2515
|
+
var b = h('button', { type: 'button', role: 'tab', id: tabId, 'class': 'one-tab', 'aria-selected': selected ? 'true' : 'false', 'aria-controls': panelId,
|
|
2516
|
+
tabindex: selected ? '0' : '-1', 'aria-disabled': disabled ? 'true' : null, 'data-one-focus': 'tab:' + key, text: panelLabel(p, lang) || key });
|
|
2517
|
+
listen(b, 'click', function () { if (!disabled) self._select(key, true); });
|
|
2518
|
+
add(self._list, b);
|
|
2519
|
+
});
|
|
2520
|
+
if (this._listKeys) return;
|
|
2521
|
+
this._listKeys = true;
|
|
2522
|
+
listen(this._list, 'keydown', function (ev) {
|
|
2523
|
+
var k = ev.key;
|
|
2524
|
+
if (k !== 'ArrowRight' && k !== 'ArrowLeft' && k !== 'Home' && k !== 'End') return;
|
|
2525
|
+
ev.preventDefault();
|
|
2526
|
+
var ps = self._panels(), ks = ps.map(function (p, i) { return self._key(p, i); }), cur = ks.indexOf(self._current(ps)), n = ks.length;
|
|
2527
|
+
var order = k === 'Home' ? range(0, 1, n) : k === 'End' ? range(n - 1, -1, n) : range(cur + (k === 'ArrowRight' ? 1 : -1), k === 'ArrowRight' ? 1 : -1, n);
|
|
2528
|
+
for (var j = 0; j < order.length; j++) { if (!ps[order[j]].hasAttribute('data-disabled')) { self._select(ks[order[j]], true); break; } }
|
|
2529
|
+
});
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
// 'start'tan başlayıp 'dir' yönünde, sona gelince başa dönerek n indeks.
|
|
2533
|
+
function range(start, dir, n) { var out = []; for (var i = 0; i < n; i++) out.push((((start + dir * i) % n) + n) % n); return out; }
|
|
2534
|
+
|
|
2535
|
+
// ══ <one-accordion> (D-96) ══════════════════════════════════════════════════════════════════════════════
|
|
2536
|
+
// Alt öğeler bölümlerdir: 'data-label' (+ 'data-label-en', 'data-open'). APG "accordion": her başlık bir düğmedir
|
|
2537
|
+
// ('aria-expanded'), ok tuşları başlıklar arasında gezer. 'multiple' yoksa aynı anda tek bölüm açıktır.
|
|
2538
|
+
class OneAccordion extends Base {
|
|
2539
|
+
static get observedAttributes() { return ['lang', 'multiple', 'heading-level']; }
|
|
2540
|
+
constructor() { super(); this._open = []; this._seen = []; this._uid = uid('accordion'); this._observed = false; }
|
|
2541
|
+
_isHead(n) { return n.nodeType === 1 && n.hasAttribute('data-one-accordion-head'); }
|
|
2542
|
+
_sections() { var self = this; return Array.prototype.filter.call(this.children, function (n) { return !self._isHead(n); }); }
|
|
2543
|
+
_toggle(s) {
|
|
2544
|
+
var i = this._sections().indexOf(s), was = this._open.indexOf(s) >= 0;
|
|
2545
|
+
if (was) this._open.splice(this._open.indexOf(s), 1);
|
|
2546
|
+
else { if (!this.hasAttribute('multiple')) this._open = []; this._open.push(s); }
|
|
2547
|
+
this.render();
|
|
2548
|
+
emit(this, 'toggle', { index: i, open: !was, key: s.getAttribute('data-key') || String(i) });
|
|
2549
|
+
}
|
|
2550
|
+
draw() {
|
|
2551
|
+
var self = this, lang = this.lang_(), sections = this._sections(), multiple = this.hasAttribute('multiple');
|
|
2552
|
+
this.classList.add('one-accordion');
|
|
2553
|
+
if (!this._observed && typeof w.MutationObserver === 'function') {
|
|
2554
|
+
this._observed = true;
|
|
2555
|
+
// Betik sayfanın başında yüklenince ayrıştırıcı bölümleri bileşen bağlandıktan SONRA ekler; eklenen ya da çıkan her
|
|
2556
|
+
// bölüm (bileşenin kendi başlıkları hariç) yeniden çizilir.
|
|
2557
|
+
var foreign = function (list) { return Array.prototype.some.call(list, function (n) { return n.nodeType === 1 && !self._isHead(n); }); };
|
|
2558
|
+
new w.MutationObserver(function (records) {
|
|
2559
|
+
var redraw = false, openChanged = false;
|
|
2560
|
+
records.forEach(function (r) {
|
|
2561
|
+
if (r.type === 'childList') { if (r.target === self && (foreign(r.addedNodes) || foreign(r.removedNodes))) redraw = true; return; }
|
|
2562
|
+
if (r.target.parentNode !== self || self._isHead(r.target)) return;
|
|
2563
|
+
if (r.attributeName === 'data-open') openChanged = true; else redraw = true;
|
|
2564
|
+
});
|
|
2565
|
+
// 'data-open' açık durumla iki yönlü eşittir: uygulamanın yazdığı değer, kayıtların SONUNDAKİ DOM durumundan
|
|
2566
|
+
// okunur (kayıt başına okumak, aynı turdaki kendi yansıtmamızla karışırdı).
|
|
2567
|
+
if (openChanged) {
|
|
2568
|
+
var sections = self._sections(), added = sections.filter(function (s) { return s.hasAttribute('data-open') && self._open.indexOf(s) < 0; });
|
|
2569
|
+
var kept = self._open.filter(function (s) { return s.hasAttribute('data-open'); });
|
|
2570
|
+
var next = self.hasAttribute('multiple') ? kept.concat(added) : (added.length ? added.slice(-1) : kept.slice(-1));
|
|
2571
|
+
if (next.length !== self._open.length || next.some(function (s, i) { return s !== self._open[i]; })) { self._open = next; redraw = true; }
|
|
2572
|
+
}
|
|
2573
|
+
if (redraw) self.render();
|
|
2574
|
+
}).observe(this, { childList: true, subtree: true, attributes: true, attributeFilter: ['data-open', 'data-label', 'data-label-en'] });
|
|
2575
|
+
}
|
|
2576
|
+
sections.forEach(function (s) {
|
|
2577
|
+
if (self._seen.indexOf(s) >= 0) return;
|
|
2578
|
+
self._seen.push(s);
|
|
2579
|
+
if (s.hasAttribute('data-open') && (multiple || self._open.length === 0)) self._open.push(s);
|
|
2580
|
+
});
|
|
2581
|
+
this._open = this._open.filter(function (s) { return sections.indexOf(s) >= 0; });
|
|
2582
|
+
// 'multiple' çalışma anında kaldırılabilir: tekli kipte en çok bir bölüm açıktır (en son açılan kalır).
|
|
2583
|
+
if (!multiple && this._open.length > 1) this._open = this._open.slice(-1);
|
|
2584
|
+
var level = parseInt(this.getAttribute('heading-level') || '3', 10);
|
|
2585
|
+
if (!(level >= 2 && level <= 6)) level = 3;
|
|
2586
|
+
// Bölümü kalmamış başlık kaldırılır.
|
|
2587
|
+
Array.prototype.slice.call(this.children).forEach(function (n) {
|
|
2588
|
+
if (self._isHead(n) && (!n.nextElementSibling || self._isHead(n.nextElementSibling))) self.removeChild(n);
|
|
2589
|
+
});
|
|
2590
|
+
var heads = [], region = sections.length <= 6;
|
|
2591
|
+
sections.forEach(function (s, i) {
|
|
2592
|
+
var k = self._seen.indexOf(s), open = self._open.indexOf(s) >= 0, btnId = self._uid + '-h-' + k;
|
|
2593
|
+
if (!s.id) s.id = self._uid + '-p-' + k;
|
|
2594
|
+
s.classList.add('one-accordion-panel');
|
|
2595
|
+
if (region) setAttrs(s, { role: 'region', 'aria-labelledby': btnId }); else { s.removeAttribute('role'); s.removeAttribute('aria-labelledby'); }
|
|
2596
|
+
s.hidden = !open;
|
|
2597
|
+
if (open !== s.hasAttribute('data-open')) { if (open) s.setAttribute('data-open', ''); else s.removeAttribute('data-open'); }
|
|
2598
|
+
var head = s.previousElementSibling && self._isHead(s.previousElementSibling) ? s.previousElementSibling : null;
|
|
2599
|
+
if (head && head.tagName.toLowerCase() !== 'h' + level) { self.removeChild(head); head = null; }
|
|
2600
|
+
if (!head) { head = h('h' + level, { 'class': 'one-accordion-heading', 'data-one-accordion-head': '' }); self.insertBefore(head, s); }
|
|
2601
|
+
clear(head);
|
|
2602
|
+
var btn = h('button', { type: 'button', id: btnId, 'class': 'one-accordion-trigger', 'aria-expanded': open ? 'true' : 'false', 'aria-controls': s.id, 'data-one-focus': 'h:' + k },
|
|
2603
|
+
[h('span', { text: panelLabel(s, lang) || String(i + 1) }), h('span', { 'class': 'one-accordion-icon', 'aria-hidden': 'true', text: '▾' })]);
|
|
2604
|
+
listen(btn, 'click', function () { self._toggle(s); });
|
|
2605
|
+
listen(btn, 'keydown', function (ev) {
|
|
2606
|
+
var key_ = ev.key, n = heads.length;
|
|
2607
|
+
if (key_ !== 'ArrowDown' && key_ !== 'ArrowUp' && key_ !== 'Home' && key_ !== 'End') return;
|
|
2608
|
+
ev.preventDefault();
|
|
2609
|
+
var to = key_ === 'Home' ? 0 : key_ === 'End' ? n - 1 : (i + (key_ === 'ArrowDown' ? 1 : -1) + n) % n;
|
|
2610
|
+
var b = heads[to] && heads[to].firstChild;
|
|
2611
|
+
if (b) b.focus();
|
|
2612
|
+
});
|
|
2613
|
+
add(head, btn);
|
|
2614
|
+
heads.push(head);
|
|
2615
|
+
});
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2619
|
+
var DEFS = [['one-table', OneTable], ['one-form', OneForm], ['one-filter', OneFilter], ['one-person-picker', OnePersonPicker], ['one-file-upload', OneFileUpload], ['one-chart', OneChart],
|
|
2620
|
+
['one-select', OneSelect], ['one-tag-input', OneTagInput], ['one-dialog', OneDialog], ['one-tooltip', OneTooltip], ['one-menu', OneMenu], ['one-avatar', OneAvatar],
|
|
2621
|
+
['one-tabs', OneTabs], ['one-accordion', OneAccordion]];
|
|
1488
2622
|
for (var i = 0; i < DEFS.length; i++) if (!w.customElements.get(DEFS[i][0])) w.customElements.define(DEFS[i][0], DEFS[i][1]);
|
|
1489
2623
|
})(window);
|
|
1490
2624
|
`;
|