- Explore: replace per-kind colored badges/tints in search results with the app's neutral accent, add an always-visible Usuarios chip, and keep the top-result type label as plain text - Move SearchBox into TopBar/MobileHeader as an inline debounced search form, dropping the standalone SearchBox component - Sync design handoff artboards with the current search UI
184 lines
12 KiB
HTML
184 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<script src="./support.js"></script>
|
|
</head>
|
|
<body>
|
|
<x-dc>
|
|
<helmet>
|
|
<link href="https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700&family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<style>
|
|
*{box-sizing:border-box}
|
|
html,body{margin:0;padding:0;background:#050507}
|
|
a{color:#b4a4ea;text-decoration:none}
|
|
a:hover{color:#cdc0f5}
|
|
img{display:block}
|
|
input,textarea{font-family:inherit}
|
|
input::placeholder,textarea::placeholder{color:#5d5d63}
|
|
@keyframes modalIn{from{opacity:0;transform:translateY(14px) scale(.985)}to{opacity:1;transform:none}}
|
|
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
|
|
</style>
|
|
</helmet>
|
|
|
|
<div style="position:relative;min-height:100vh;display:grid;place-items:center;padding:40px 20px;background:radial-gradient(120% 90% at 50% 0%,#15131c,#08080b 58%,#050507);font-family:'Plus Jakarta Sans',system-ui,sans-serif;color:#c9c8cc;-webkit-font-smoothing:antialiased">
|
|
|
|
<div style="position:absolute;inset:0;background:rgba(4,4,6,.55);backdrop-filter:blur(3px)"></div>
|
|
|
|
<div data-screen-label="Crear playlist" style="position:relative;width:100%;max-width:560px;border-radius:24px;overflow:hidden;border:1px solid rgba(255,255,255,.075);background:linear-gradient(180deg,#141419,#0d0d11 62%,#0b0b0e);box-shadow:0 32px 90px rgba(0,0,0,.66),0 2px 0 rgba(255,255,255,.035) inset;animation:modalIn .28s cubic-bezier(.2,.8,.3,1)">
|
|
|
|
<div style="position:absolute;inset:0;pointer-events:none;background:{{ auraTop }};transition:background .5s ease"></div>
|
|
|
|
<!-- HEADER -->
|
|
<div style="position:relative;display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:24px 26px 0">
|
|
<div>
|
|
<div style="font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;color:#8f8f93;font-weight:600;margin-bottom:9px">Tu biblioteca</div>
|
|
<div style="font-family:'Sora',sans-serif;font-size:21px;font-weight:600;letter-spacing:-.03em;color:#d8d7db">Crear playlist</div>
|
|
</div>
|
|
<div onClick="{{ close }}" title="Cerrar" style="flex:0 0 auto;width:32px;height:32px;border-radius:10px;display:grid;place-items:center;cursor:pointer;color:#87878c;border:1px solid rgba(255,255,255,.06);background:rgba(255,255,255,.025);transition:background .18s ease,color .18s ease" style-hover="background:rgba(255,255,255,.07);color:#d0cfd4">
|
|
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"></path></svg>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- BODY -->
|
|
<div style="position:relative;padding:22px 26px 0;display:flex;gap:20px;align-items:flex-start">
|
|
|
|
<!-- COVER -->
|
|
<div style="flex:0 0 auto;width:124px">
|
|
<div style="position:relative;width:124px;height:124px;border-radius:16px;overflow:hidden;border:1px solid rgba(255,255,255,.07);background:{{ coverGrad }};box-shadow:{{ coverGlow }};transition:background .45s ease,box-shadow .45s ease;display:grid;place-items:center">
|
|
<svg viewBox="0 0 24 24" width="26" height="26" fill="none" stroke="rgba(255,255,255,.62)" stroke-width="1.5" stroke-linecap="round"><path d="M9 18V6l10-2v12"></path><circle cx="6.5" cy="18" r="2.5"></circle><circle cx="16.5" cy="16" r="2.5"></circle></svg>
|
|
</div>
|
|
<div style="display:flex;gap:6px;margin-top:11px">
|
|
<sc-for list="{{ swatches }}" as="s" hint-placeholder-count="5">
|
|
<div onClick="{{ s.pick }}" title="{{ s.name }}" style="flex:1;height:20px;border-radius:7px;cursor:pointer;background:{{ s.dot }};box-shadow:{{ s.ring }};transition:box-shadow .18s ease"></div>
|
|
</sc-for>
|
|
</div>
|
|
<div onClick="{{ upload }}" style="margin-top:9px;text-align:center;font-size:11px;font-weight:500;color:#8a8a8f;padding:7px 0;border-radius:9px;border:1px dashed rgba(255,255,255,.1);cursor:pointer;transition:color .18s ease,border-color .18s ease" style-hover="color:#cfccd9;border-color:rgba(255,255,255,.2)">Subir imagen</div>
|
|
</div>
|
|
|
|
<!-- FIELDS -->
|
|
<div style="flex:1;min-width:0">
|
|
<label style="display:block;font-size:11px;font-weight:600;letter-spacing:.03em;color:#87878c;margin-bottom:8px">Nombre</label>
|
|
<input value="{{ name }}" onChange="{{ onName }}" maxLength="60" placeholder="Playlist sin título" style="width:100%;padding:12px 14px;border-radius:12px;background:rgba(255,255,255,.035);border:1px solid rgba(255,255,255,.075);outline:none;color:#dcdbe0;font-family:'Sora',sans-serif;font-size:16px;font-weight:600;letter-spacing:-.02em" style-focus="border-color:rgba(255,255,255,.2);background:rgba(255,255,255,.055)">
|
|
<div style="display:flex;justify-content:flex-end;font-size:10.5px;color:#65656a;margin-top:6px">{{ counter }}</div>
|
|
|
|
<label style="display:block;font-size:11px;font-weight:600;letter-spacing:.03em;color:#87878c;margin:10px 0 8px">Descripción <span style="font-weight:400;color:#5f5f65">· opcional</span></label>
|
|
<textarea value="{{ desc }}" onChange="{{ onDesc }}" rows="3" placeholder="Para qué sirve esta playlist, cuándo la escuchas…" style="width:100%;resize:none;padding:11px 14px;border-radius:12px;background:rgba(255,255,255,.035);border:1px solid rgba(255,255,255,.075);outline:none;color:#c4c3c9;font-size:12.5px;line-height:1.6" style-focus="border-color:rgba(255,255,255,.2);background:rgba(255,255,255,.055)"></textarea>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- OPTIONS -->
|
|
<div style="position:relative;padding:20px 26px 0">
|
|
<div style="display:flex;align-items:center;justify-content:space-between;gap:14px;padding:13px 15px;border-radius:13px;background:rgba(255,255,255,.026);border:1px solid rgba(255,255,255,.055)">
|
|
<div style="min-width:0">
|
|
<div style="font-size:12.5px;font-weight:600;color:#cfccd9">Visibilidad</div>
|
|
<div style="font-size:11px;color:#7b7b81;margin-top:3px">{{ visHint }}</div>
|
|
</div>
|
|
<div style="flex:0 0 auto;display:flex;gap:3px;padding:3px;border-radius:10px;background:rgba(0,0,0,.34);border:1px solid rgba(255,255,255,.05)">
|
|
<sc-for list="{{ visOptions }}" as="v" hint-placeholder-count="2">
|
|
<div onClick="{{ v.pick }}" style="padding:6px 14px;border-radius:8px;font-size:11.5px;font-weight:600;cursor:pointer;background:{{ v.bg }};color:{{ v.fg }};transition:background .18s ease,color .18s ease">{{ v.label }}</div>
|
|
</sc-for>
|
|
</div>
|
|
</div>
|
|
|
|
<div onClick="{{ toggleCollab }}" style="display:flex;align-items:center;justify-content:space-between;gap:14px;margin-top:9px;padding:13px 15px;border-radius:13px;cursor:pointer;background:rgba(255,255,255,.026);border:1px solid rgba(255,255,255,.055);transition:background .18s ease" style-hover="background:rgba(255,255,255,.045)">
|
|
<div style="min-width:0">
|
|
<div style="font-size:12.5px;font-weight:600;color:#cfccd9">Colaborativa</div>
|
|
<div style="font-size:11px;color:#7b7b81;margin-top:3px">Quien tenga el enlace puede añadir canciones</div>
|
|
</div>
|
|
<div style="flex:0 0 auto;width:40px;height:23px;border-radius:20px;padding:3px;background:{{ collabTrack }};transition:background .2s ease">
|
|
<div style="width:17px;height:17px;border-radius:50%;background:#0c0c10;transform:{{ collabKnob }};transition:transform .2s cubic-bezier(.3,.8,.3,1)"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- FOOTER -->
|
|
<div style="position:relative;display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;margin-top:22px;padding:17px 26px 20px;border-top:1px solid rgba(255,255,255,.055);background:rgba(0,0,0,.2)">
|
|
<div style="font-size:11px;color:#6e6e74;max-width:230px;line-height:1.5">Podrás añadir canciones desde cualquier pantalla.</div>
|
|
<div style="display:flex;gap:9px;margin-left:auto">
|
|
<div onClick="{{ close }}" style="padding:11px 19px;border-radius:11px;border:1px solid rgba(255,255,255,.1);font-size:12.5px;font-weight:500;cursor:pointer;color:#a9a7b2;transition:background .18s ease,color .18s ease" style-hover="background:rgba(255,255,255,.05);color:#d3d2d8">Cancelar</div>
|
|
<div onClick="{{ create }}" style="display:flex;align-items:center;gap:8px;padding:11px 21px;border-radius:11px;font-size:12.5px;font-weight:600;background:{{ ctaBg }};color:{{ ctaFg }};cursor:{{ ctaCursor }};opacity:{{ ctaOpacity }};transition:background .18s ease,opacity .18s ease">
|
|
<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M12 5v14M5 12h14"></path></svg>
|
|
Crear playlist
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<sc-if value="{{ created }}" hint-placeholder-val="{{ false }}">
|
|
<div style="position:fixed;left:50%;bottom:34px;transform:translateX(-50%);display:flex;align-items:center;gap:11px;padding:12px 18px;border-radius:13px;background:#16161c;border:1px solid rgba(255,255,255,.09);box-shadow:0 18px 46px rgba(0,0,0,.6);animation:fadeIn .22s ease">
|
|
<div style="width:26px;height:26px;border-radius:8px;background:{{ coverGrad }};flex:0 0 auto"></div>
|
|
<div style="font-size:12.5px;color:#cfccd9;font-weight:500">Playlist «{{ createdName }}» creada</div>
|
|
</div>
|
|
</sc-if>
|
|
</div>
|
|
|
|
</x-dc>
|
|
<script type="text/x-dc" data-dc-script data-props="{"$preview":{"width":900,"height":760}}">
|
|
const HUES = [
|
|
{ name: 'Ámbar', h: 68 },
|
|
{ name: 'Violeta', h: 288 },
|
|
{ name: 'Azul', h: 244 },
|
|
{ name: 'Verde', h: 158 },
|
|
{ name: 'Rojo', h: 22 },
|
|
];
|
|
|
|
class Component extends DCLogic {
|
|
state = { name: '', desc: '', hue: 1, vis: 'Privada', collab: false, created: false, createdName: '' };
|
|
|
|
renderVals() {
|
|
const h = HUES[this.state.hue].h;
|
|
const name = this.state.name;
|
|
const ready = name.trim().length > 0;
|
|
return {
|
|
name, desc: this.state.desc,
|
|
counter: name.length + '/60',
|
|
onName: e => this.setState({ name: e.target.value }),
|
|
onDesc: e => this.setState({ desc: e.target.value }),
|
|
coverGrad: `linear-gradient(150deg,oklch(.66 .16 ${h}),oklch(.34 .11 ${h}) 58%,#0e0e13)`,
|
|
coverGlow: `0 14px 34px oklch(.5 .13 ${h} / .22)`,
|
|
auraTop: `radial-gradient(110% 60% at 18% -10%,oklch(.55 .13 ${h} / .16),transparent 62%)`,
|
|
swatches: HUES.map((s, i) => ({
|
|
name: s.name,
|
|
dot: `linear-gradient(145deg,oklch(.66 .16 ${s.h}),oklch(.38 .11 ${s.h}))`,
|
|
ring: i === this.state.hue
|
|
? '0 0 0 1.5px rgba(255,255,255,.85), 0 0 0 4px rgba(0,0,0,.5)'
|
|
: '0 0 0 1px rgba(255,255,255,.07)',
|
|
pick: () => this.setState({ hue: i }),
|
|
})),
|
|
visOptions: ['Privada', 'Pública'].map(label => ({
|
|
label,
|
|
bg: this.state.vis === label ? '#cfccd9' : 'transparent',
|
|
fg: this.state.vis === label ? '#0a0a0d' : '#8e8e94',
|
|
pick: () => this.setState({ vis: label }),
|
|
})),
|
|
visHint: this.state.vis === 'Privada'
|
|
? 'Solo tú la ves en tu biblioteca'
|
|
: 'Aparece en tu perfil y se puede compartir',
|
|
collabTrack: this.state.collab ? `oklch(.68 .15 ${h})` : 'rgba(255,255,255,.12)',
|
|
collabKnob: this.state.collab ? 'translateX(17px)' : 'translateX(0)',
|
|
toggleCollab: () => this.setState(s => ({ collab: !s.collab })),
|
|
ctaBg: ready ? '#c6c4cb' : 'rgba(255,255,255,.08)',
|
|
ctaFg: ready ? '#0a0a0d' : '#6c6c72',
|
|
ctaCursor: ready ? 'pointer' : 'not-allowed',
|
|
ctaOpacity: ready ? 1 : 0.85,
|
|
create: () => {
|
|
if (!ready) return;
|
|
this.setState({ created: true, createdName: name.trim() });
|
|
clearTimeout(this._t);
|
|
this._t = setTimeout(() => this.setState({ created: false }), 2600);
|
|
},
|
|
close: () => this.setState({ name: '', desc: '', collab: false, created: false }),
|
|
upload: () => {},
|
|
created: this.state.created,
|
|
createdName: this.state.createdName,
|
|
};
|
|
}
|
|
componentWillUnmount() { clearTimeout(this._t); }
|
|
}
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|