- Installed p7h/nas-file-manager package via private VCS repo - Published config/nas-file-manager.php with super_admin middleware restriction - Added NAS env vars to .env.example - Created admin/nas-storage page with connection info panel and file browser widget - Added NAS Storage link to admin sidebar (super_admin only) - Added SuperAdminController@nasStorage method and admin.nas-storage route - Includes all accumulated branch changes: profile wall, 2FA, audit logs, settings panel, country/phone/timezone components, posts, slideshow, playlist shares, video downloads/shares, comment likes, notifications, social links, and more Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
173 lines
7.2 KiB
PHP
173 lines
7.2 KiB
PHP
@props([
|
|
'name' => 'gender',
|
|
'id' => null,
|
|
'value' => null,
|
|
'label' => null,
|
|
'required' => false,
|
|
'class' => '',
|
|
'style' => '',
|
|
])
|
|
|
|
@php
|
|
$uid = 'gsd_' . ($id ?? $name) . '_' . substr(md5(uniqid()), 0, 8);
|
|
$inputId = $id ?? $name;
|
|
|
|
$options = [
|
|
['value' => 'male', 'symbol' => '♂', 'label' => 'Male', 'color' => '#4a9eff'],
|
|
['value' => 'female', 'symbol' => '♀', 'label' => 'Female', 'color' => '#ff6eb0'],
|
|
];
|
|
|
|
$selected = null;
|
|
$placeholder = 'Select gender';
|
|
foreach ($options as $opt) {
|
|
if ($opt['value'] === $value) { $selected = $opt; break; }
|
|
}
|
|
$isPlaceholder = !$selected;
|
|
@endphp
|
|
|
|
@once('gsd-styles')
|
|
<style>
|
|
.gsd-wrap { position: relative; }
|
|
.gsd-lbl { display: block; margin-bottom: 6px; font-weight: 500; font-size: 14px; color: var(--text-primary, #f1f1f1); }
|
|
.gsd-lbl .req { color: var(--brand-red, #e61e1e); margin-left: 2px; }
|
|
.gsd-btn {
|
|
display: flex; align-items: center; gap: 10px; width: 100%;
|
|
background: var(--bg-dark, #0f0f0f); border: 1px solid var(--border-color, #303030);
|
|
border-radius: 8px; padding: 10px 14px; color: var(--text-primary, #f1f1f1);
|
|
font-size: 14px; font-family: inherit; cursor: pointer; text-align: left;
|
|
transition: border-color .2s; outline: none; min-height: 46px; box-sizing: border-box;
|
|
}
|
|
.gsd-btn:hover, .gsd-btn:focus-visible { border-color: var(--brand-red, #e61e1e); }
|
|
.gsd-btn[aria-expanded="true"] { border-color: var(--brand-red, #e61e1e); }
|
|
.gsd-sym { font-size: 20px; line-height: 1; flex-shrink: 0; user-select: none; }
|
|
.gsd-val { flex: 1; }
|
|
.gsd-val.ph { color: var(--text-secondary, #aaa); }
|
|
.gsd-arr { flex-shrink: 0; font-size: 11px; color: var(--text-secondary, #aaa); margin-left: auto; transition: transform .2s; }
|
|
.gsd-btn[aria-expanded="true"] .gsd-arr { transform: rotate(180deg); }
|
|
.gsd-panel {
|
|
position: fixed; z-index: 1060; min-width: 180px;
|
|
background: var(--bg-secondary, #1e1e1e); border: 1px solid var(--border-color, #303030);
|
|
border-radius: 10px; box-shadow: 0 12px 32px rgba(0,0,0,.55); overflow: hidden;
|
|
padding: 4px 0;
|
|
}
|
|
.gsd-opt {
|
|
display: flex; align-items: center; gap: 12px; padding: 10px 14px;
|
|
cursor: pointer; font-size: 14px; color: var(--text-primary, #f1f1f1);
|
|
transition: background .1s; outline: none; border: none; background: none;
|
|
width: 100%; text-align: left; font-family: inherit;
|
|
}
|
|
.gsd-opt:hover, .gsd-opt:focus { background: rgba(255,255,255,.07); }
|
|
.gsd-opt[aria-selected="true"] { background: rgba(255,255,255,.05); }
|
|
.gsd-opt-sym { font-size: 22px; line-height: 1; user-select: none; }
|
|
.gsd-opt-lbl { font-weight: 500; }
|
|
</style>
|
|
@endonce
|
|
|
|
@if($label)
|
|
<div class="gsd-wrap {{ $class }}" id="{{ $uid }}" @if($style) style="{{ $style }}" @endif>
|
|
<label class="gsd-lbl" for="{{ $inputId }}">
|
|
{{ $label }}@if($required)<span class="req">*</span>@endif
|
|
</label>
|
|
@else
|
|
<div class="gsd-wrap {{ $class }}" id="{{ $uid }}" @if($style) style="{{ $style }}" @endif>
|
|
@endif
|
|
|
|
<button type="button"
|
|
class="gsd-btn"
|
|
aria-haspopup="listbox"
|
|
aria-expanded="false"
|
|
aria-label="{{ $label ?? 'Select gender' }}">
|
|
@if($selected)
|
|
<span class="gsd-sym" style="color:{{ $selected['color'] }}">{{ $selected['symbol'] }}</span>
|
|
<span class="gsd-val">{{ $selected['label'] }}</span>
|
|
@else
|
|
<span class="gsd-sym" style="color:var(--text-secondary,#aaa)">⚧</span>
|
|
<span class="gsd-val ph">{{ $placeholder }}</span>
|
|
@endif
|
|
<i class="bi bi-chevron-down gsd-arr"></i>
|
|
</button>
|
|
|
|
<div class="gsd-panel" hidden role="listbox">
|
|
@foreach($options as $opt)
|
|
<button type="button"
|
|
class="gsd-opt"
|
|
role="option"
|
|
data-v="{{ $opt['value'] }}"
|
|
aria-selected="{{ $value === $opt['value'] ? 'true' : 'false' }}">
|
|
<span class="gsd-opt-sym" style="color:{{ $opt['color'] }}">{{ $opt['symbol'] }}</span>
|
|
<span class="gsd-opt-lbl">{{ $opt['label'] }}</span>
|
|
</button>
|
|
@endforeach
|
|
</div>
|
|
|
|
<input type="hidden"
|
|
name="{{ $name }}"
|
|
id="{{ $inputId }}"
|
|
value="{{ $value }}"
|
|
@if($required) required @endif>
|
|
</div>
|
|
|
|
<script>
|
|
(function () {
|
|
var uid = '{{ $uid }}';
|
|
function boot() {
|
|
var root = document.getElementById(uid);
|
|
var btn = root.querySelector('.gsd-btn');
|
|
var panel = root.querySelector('.gsd-panel');
|
|
var hidden = root.querySelector('input[type=hidden]');
|
|
var symEl = btn.querySelector('.gsd-sym');
|
|
var valEl = btn.querySelector('.gsd-val');
|
|
|
|
function open() {
|
|
document.querySelectorAll('.gsd-panel:not([hidden])').forEach(function(p) { p.hidden = true; });
|
|
document.querySelectorAll('.gsd-btn[aria-expanded=true]').forEach(function(b) { b.setAttribute('aria-expanded','false'); });
|
|
panel.hidden = false;
|
|
btn.setAttribute('aria-expanded', 'true');
|
|
var r = btn.getBoundingClientRect();
|
|
var goUp = window.innerHeight - r.bottom < 120 && r.top > 120;
|
|
panel.style.left = r.left + 'px';
|
|
panel.style.width = Math.max(r.width, 180) + 'px';
|
|
if (goUp) {
|
|
panel.style.top = '';
|
|
panel.style.bottom = (window.innerHeight - r.top + 4) + 'px';
|
|
} else {
|
|
panel.style.top = (r.bottom + 4) + 'px';
|
|
panel.style.bottom = '';
|
|
}
|
|
}
|
|
function close() { panel.hidden = true; btn.setAttribute('aria-expanded', 'false'); }
|
|
function pick(opt) {
|
|
hidden.value = opt.dataset.v;
|
|
var sym = opt.querySelector('.gsd-opt-sym');
|
|
var lbl = opt.querySelector('.gsd-opt-lbl');
|
|
symEl.textContent = sym.textContent;
|
|
symEl.style.color = sym.style.color;
|
|
valEl.textContent = lbl.textContent;
|
|
valEl.classList.remove('ph');
|
|
panel.querySelectorAll('.gsd-opt').forEach(function(o) {
|
|
o.setAttribute('aria-selected', o === opt ? 'true' : 'false');
|
|
});
|
|
close(); btn.focus();
|
|
hidden.dispatchEvent(new Event('change', { bubbles: true }));
|
|
}
|
|
|
|
btn.addEventListener('click', function(e) { e.stopPropagation(); panel.hidden ? open() : close(); });
|
|
btn.addEventListener('keydown', function(e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); panel.hidden ? open() : close(); } });
|
|
panel.addEventListener('mousedown', function(e) { e.stopPropagation(); });
|
|
panel.querySelectorAll('.gsd-opt').forEach(function(opt) {
|
|
opt.addEventListener('click', function() { pick(opt); });
|
|
opt.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); pick(opt); }
|
|
if (e.key === 'Escape') { close(); btn.focus(); }
|
|
});
|
|
});
|
|
document.addEventListener('mousedown', function(e) { if (!root.contains(e.target)) close(); });
|
|
}
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', boot);
|
|
} else {
|
|
boot();
|
|
}
|
|
}());
|
|
</script>
|