Rafael Lopez

template obejtos agregado

This diff is collapsed. Click to expand it.
1 +<script>
2 + import { scaleLinear, scaleBand } from 'd3-scale';
3 +
4 + let {
5 + data = [],
6 + hoveredYear = $bindable(null),
7 + height = 220,
8 + fill = false,
9 + marginTop = 20,
10 + marginRight = 10,
11 + marginBottom = 30,
12 + marginLeft = 50
13 + } = $props();
14 +
15 + // Medir altura real del contenedor cuando fill=true
16 + let wrapperEl = $state(null);
17 + let measuredHeight = $state(height);
18 +
19 + $effect(() => {
20 + if (fill && wrapperEl) {
21 + const updateHeight = () => {
22 + const h = wrapperEl.clientHeight;
23 + if (h > 0) measuredHeight = h;
24 + };
25 + updateHeight();
26 + const observer = new ResizeObserver(updateHeight);
27 + observer.observe(wrapperEl);
28 + return () => observer.disconnect();
29 + } else {
30 + measuredHeight = height;
31 + }
32 + });
33 +
34 + // Dimensiones del área del gráfico
35 + let effectiveHeight = $derived(fill ? measuredHeight : height);
36 + let chartHeight = $derived(effectiveHeight - marginTop - marginBottom);
37 +
38 + // Escalas D3
39 + let xScale = $derived(
40 + scaleBand()
41 + .domain(data.map(d => d.año))
42 + .range([0, 100]) // porcentaje
43 + .padding(0.2)
44 + );
45 +
46 + // Para CSS bottom positioning: 0 → 0%, max → 100%
47 + let yScale = $derived(
48 + scaleLinear()
49 + .domain([0, Math.max(...data.map(d => d.perCapita))])
50 + .range([0, chartHeight])
51 + .nice()
52 + );
53 +
54 + // Ticks para el eje Y
55 + let yTicks = $derived(yScale.ticks(3));
56 +
57 + // Índices para mostrar en eje X
58 + let midIndex = $derived(Math.floor(data.length / 2));
59 +
60 + // Formato para valores del eje Y
61 + function formatValue(v) {
62 + if (v >= 1e6) return (v / 1e6).toLocaleString('es-BO', { maximumFractionDigits: 1 }) + ' M';
63 + if (v >= 1e3) return Math.round(v / 1e3).toLocaleString('es-BO') + ' K';
64 + return v.toLocaleString('es-BO');
65 + }
66 +</script>
67 +
68 +<div class="chart-wrapper" bind:this={wrapperEl} style="height: {fill ? '100%' : height + 'px'}">
69 + <!-- Contenedor principal con márgenes -->
70 + <div class="chart-inner" style="top: {marginTop}px; bottom: {marginBottom}px; left: {marginLeft}px; right: {marginRight}px">
71 +
72 + <!-- Y-axis labels (fuera del área de barras, a la izquierda) -->
73 + {#each yTicks as tick}
74 + {@const pct = (yScale(tick) / chartHeight) * 100}
75 + <span class="y-label" style="bottom: {pct}%; left: -{marginLeft}px">
76 + Bs {formatValue(tick)}
77 + </span>
78 + {/each}
79 +
80 + <!-- Grid lines -->
81 + {#each yTicks as tick}
82 + {@const pct = (yScale(tick) / chartHeight) * 100}
83 + <div class="grid-line" style="bottom: {pct}%"></div>
84 + {/each}
85 +
86 + <!-- Barras -->
87 + <div class="bars" onmouseleave={() => hoveredYear = null} role="group">
88 + {#each data as d}
89 + {@const barPct = (yScale(d.perCapita) / chartHeight) * 100}
90 + <div
91 + class="bar-container"
92 + onmouseenter={() => hoveredYear = d.año}
93 + role="button"
94 + tabindex="0"
95 + >
96 + <div
97 + class="bar"
98 + class:hovered={hoveredYear === d.año}
99 + style="height: {barPct}%"
100 + ></div>
101 + </div>
102 + {/each}
103 + </div>
104 + </div>
105 +
106 + <!-- Eje X -->
107 + <div class="x-axis" style="height: {marginBottom}px; left: {marginLeft}px; right: {marginRight}px">
108 + {#each data as d, i}
109 + {@const isFirst = i === 0}
110 + {@const isLast = i === data.length - 1}
111 + {@const isMid = i === midIndex && data.length > 5}
112 + {@const showLabel = isFirst || isLast || isMid || hoveredYear === d.año}
113 + <span
114 + class="x-label"
115 + class:visible={showLabel}
116 + >
117 + {d.año}
118 + </span>
119 + {/each}
120 + </div>
121 +</div>
122 +
123 +<style>
124 + .chart-wrapper {
125 + position: relative;
126 + width: 100%;
127 + transition: height 0.3s ease;
128 + }
129 +
130 + .chart-inner {
131 + position: absolute;
132 + }
133 +
134 + .y-label {
135 + position: absolute;
136 + width: 45px;
137 + text-align: right;
138 + transform: translateY(50%);
139 + font-family: 'Qanelas', var(--font-sans);
140 + font-size: 0.6875rem;
141 + color: var(--theme-texto);
142 + white-space: nowrap;
143 + }
144 +
145 + .grid-line {
146 + position: absolute;
147 + left: 0;
148 + right: 0;
149 + border-top: 0.5px dotted var(--theme-texto);
150 + opacity: 0.15;
151 + pointer-events: none;
152 + }
153 +
154 + .bars {
155 + position: absolute;
156 + top: 0;
157 + left: 0;
158 + right: 0;
159 + bottom: 0;
160 + display: flex;
161 + align-items: flex-end;
162 + gap: 2px;
163 + }
164 +
165 + .bar-container {
166 + flex: 1;
167 + height: 100%;
168 + display: flex;
169 + align-items: flex-end;
170 + cursor: default;
171 + }
172 +
173 + .bar {
174 + width: 100%;
175 + background: #c4897d;
176 + border-radius: 4px 4px 0 0;
177 + transition: opacity 0.1s ease;
178 + min-height: 2px;
179 + }
180 +
181 + .bars:hover .bar {
182 + opacity: 0.35;
183 + }
184 +
185 + .bars:hover .bar.hovered {
186 + opacity: 1;
187 + }
188 +
189 + .x-axis {
190 + position: absolute;
191 + bottom: 0;
192 + display: flex;
193 + align-items: center;
194 + }
195 +
196 + .x-label {
197 + flex: 1;
198 + text-align: center;
199 + font-family: 'Qanelas', var(--font-sans);
200 + font-size: 0.6875rem;
201 + color: var(--theme-texto);
202 + opacity: 0;
203 + transition: opacity 0.2s ease;
204 + }
205 +
206 + .x-label.visible {
207 + opacity: 1;
208 + }
209 +</style>
1 +<script>
2 + let {
3 + entidades = [],
4 + selectedEntity = $bindable(null),
5 + nEntidades = 0
6 + } = $props();
7 +
8 + let dropdownOpen = $state(false);
9 + let searchQuery = $state('');
10 + let entityLimit = $state(30);
11 +
12 + let filteredEntities = $derived.by(() => {
13 + if (!searchQuery) return entidades.slice(0, entityLimit);
14 + const q = searchQuery.toLowerCase();
15 + return entidades
16 + .filter(e =>
17 + e.entidad_desc?.toLowerCase().includes(q) ||
18 + e.entidad?.toString().includes(q)
19 + )
20 + .slice(0, entityLimit);
21 + });
22 +
23 + function selectEntity(entity) {
24 + selectedEntity = entity;
25 + dropdownOpen = false;
26 + searchQuery = '';
27 + }
28 +
29 + function clearEntity() {
30 + selectedEntity = null;
31 + dropdownOpen = false;
32 + searchQuery = '';
33 + }
34 +
35 + function handleScroll(e) {
36 + const { scrollTop, scrollHeight, clientHeight } = e.target;
37 + if (scrollTop + clientHeight >= scrollHeight - 50) {
38 + entityLimit += 20;
39 + }
40 + }
41 +</script>
42 +
43 +<div class="entity-selector">
44 + <button class="entity-btn" onclick={() => dropdownOpen = !dropdownOpen}>
45 + <span class="entity-label">
46 + {#if selectedEntity}
47 + {selectedEntity.entidad_desc}
48 + {:else}
49 + Todo el sector público
50 + {/if}
51 + </span>
52 + <svg class="chevron" class:open={dropdownOpen} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
53 + <path d="M6 9l6 6 6-6"/>
54 + </svg>
55 + </button>
56 +
57 + {#if dropdownOpen}
58 + <div class="entity-dropdown">
59 + <div class="dropdown-search">
60 + <input
61 + type="text"
62 + bind:value={searchQuery}
63 + placeholder="Buscar entidad..."
64 + />
65 + </div>
66 + <button
67 + class="dropdown-item"
68 + class:active={!selectedEntity}
69 + onclick={clearEntity}
70 + >
71 + Todo el sector público
72 + <span class="item-count">({nEntidades})</span>
73 + </button>
74 + <div class="dropdown-list" onscroll={handleScroll}>
75 + {#each filteredEntities as entity}
76 + <button
77 + class="dropdown-item"
78 + class:active={selectedEntity?.entidad === entity.entidad}
79 + onclick={() => selectEntity(entity)}
80 + >
81 + <span class="item-code">{entity.entidad}</span>
82 + <span class="item-name">{entity.entidad_desc}</span>
83 + </button>
84 + {/each}
85 + </div>
86 + </div>
87 + {/if}
88 +</div>
89 +
90 +<style>
91 + .entity-selector {
92 + position: relative;
93 + flex: 1;
94 + min-width: 0;
95 + }
96 +
97 + .entity-btn {
98 + display: inline-flex;
99 + align-items: center;
100 + gap: 0.375rem;
101 + padding: 0.125rem 0;
102 + background: transparent;
103 + border: none;
104 + border-bottom: 1px dotted #333333;
105 + color: var(--theme-titulo);
106 + font-size: 1rem;
107 + font-weight: 500;
108 + cursor: pointer;
109 + transition: border-color 0.2s;
110 + }
111 +
112 + .entity-btn:hover {
113 + border-bottom: 1px dotted #666666;
114 + }
115 +
116 + .entity-label {
117 + text-align: left;
118 + }
119 +
120 + .chevron {
121 + color: var(--theme-texto);
122 + opacity: 0.5;
123 + transition: transform 0.2s;
124 + flex-shrink: 0;
125 + }
126 +
127 + .chevron.open {
128 + transform: rotate(180deg);
129 + }
130 +
131 + .entity-dropdown {
132 + position: absolute;
133 + top: calc(100% + 4px);
134 + left: 0;
135 + min-width: 280px;
136 + background: var(--theme-surface);
137 + border: 1px solid var(--theme-borde);
138 + border-radius: 10px;
139 + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
140 + z-index: 100;
141 + overflow: hidden;
142 + }
143 +
144 + .dropdown-search {
145 + padding: 0.5rem;
146 + border-bottom: 1px solid var(--theme-borde);
147 + }
148 +
149 + .dropdown-search input {
150 + width: 100%;
151 + padding: 0.5rem 0.75rem;
152 + border: 1px solid var(--theme-borde);
153 + border-radius: 6px;
154 + background: var(--theme-body);
155 + color: var(--theme-titulo);
156 + font-size: 0.8125rem;
157 + outline: none;
158 + }
159 +
160 + .dropdown-search input:focus {
161 + border-color: #6B9FD4;
162 + }
163 +
164 + .dropdown-item {
165 + display: flex;
166 + align-items: center;
167 + gap: 0.5rem;
168 + width: 100%;
169 + padding: 0.625rem 0.75rem;
170 + border: none;
171 + background: transparent;
172 + color: var(--theme-titulo);
173 + font-size: 0.8125rem;
174 + text-align: left;
175 + cursor: pointer;
176 + transition: background 0.15s;
177 + }
178 +
179 + .dropdown-item:hover {
180 + background: var(--theme-surface-hover);
181 + }
182 +
183 + .dropdown-item.active {
184 + background: rgba(107, 159, 212, 0.1);
185 + color: #6B9FD4;
186 + }
187 +
188 + .item-count {
189 + margin-left: auto;
190 + font-size: 0.75rem;
191 + color: var(--theme-texto);
192 + opacity: 0.6;
193 + }
194 +
195 + .item-code {
196 + font-family: 'DM Mono', monospace;
197 + font-size: 0.6875rem;
198 + color: var(--theme-texto);
199 + min-width: 2rem;
200 + }
201 +
202 + .item-name {
203 + flex: 1;
204 + overflow: hidden;
205 + text-overflow: ellipsis;
206 + white-space: nowrap;
207 + }
208 +
209 + .dropdown-list {
210 + max-height: 240px;
211 + overflow-y: auto;
212 + }
213 +
214 + :global(html.dark) .dropdown-item.active {
215 + background: rgba(201, 167, 81, 0.1);
216 + color: var(--theme-accent);
217 + }
218 +</style>
1 +<script>
2 + import { goto } from '$app/navigation';
3 + import {
4 + query,
5 + results,
6 + isLoading as searchLoading,
7 + indexLoaded,
8 + selectedIndex,
9 + performSearch,
10 + clearSearch,
11 + navigateResults
12 + } from '$lib/stores/searchStore';
13 +
14 + let searchInputRef = $state(null);
15 + let searchVal = $state('');
16 + let searchFocused = $state(false);
17 + let debounceTimer;
18 +
19 + // Filtrar solo objeto_gasto
20 + let filteredSearchResults = $derived($results.filter(r => r.tipo === 'objeto_gasto'));
21 +
22 + function handleSearchInput(e) {
23 + const val = e.target.value;
24 + clearTimeout(debounceTimer);
25 + debounceTimer = setTimeout(() => {
26 + if (val.length >= 2) {
27 + performSearch(val);
28 + } else {
29 + clearSearch();
30 + }
31 + }, 200);
32 + }
33 +
34 + function handleSearchKeydown(e) {
35 + if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
36 + e.preventDefault();
37 + navigateResults(e.key === 'ArrowDown' ? 1 : -1, filteredSearchResults.length);
38 + } else if (e.key === 'Enter' && filteredSearchResults.length > 0) {
39 + e.preventDefault();
40 + const selected = filteredSearchResults[$selectedIndex];
41 + if (selected) handleSearchSelect(selected);
42 + } else if (e.key === 'Escape') {
43 + searchVal = '';
44 + clearSearch();
45 + searchInputRef?.blur();
46 + }
47 + }
48 +
49 + function handleSearchSelect(result) {
50 + goto(`/objeto/${result.codigo}`);
51 + searchVal = '';
52 + clearSearch();
53 + searchFocused = false;
54 + }
55 +
56 + function handleSearchBlur() {
57 + setTimeout(() => {
58 + searchFocused = false;
59 + }, 150);
60 + }
61 +
62 + function handleClear() {
63 + clearSearch();
64 + searchVal = '';
65 + }
66 +</script>
67 +
68 +<div class="objeto-search" class:focused={searchFocused}>
69 + <svg class="search-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
70 + <circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/>
71 + </svg>
72 + <input
73 + type="text"
74 + bind:this={searchInputRef}
75 + bind:value={searchVal}
76 + oninput={handleSearchInput}
77 + onkeydown={handleSearchKeydown}
78 + onfocus={() => searchFocused = true}
79 + onblur={handleSearchBlur}
80 + placeholder="Buscar objeto..."
81 + disabled={!$indexLoaded}
82 + />
83 + {#if searchVal}
84 + <button class="search-clear" onclick={handleClear}>
85 + <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
86 + <line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
87 + </svg>
88 + </button>
89 + {/if}
90 + {#if searchFocused && searchVal.length >= 2}
91 + <div class="search-dropdown">
92 + {#if $searchLoading}
93 + <div class="search-msg">Buscando...</div>
94 + {:else if filteredSearchResults.length === 0}
95 + <div class="search-msg">Sin resultados</div>
96 + {:else}
97 + {#each filteredSearchResults as result, i}
98 + <button
99 + class="search-item"
100 + class:selected={$selectedIndex === i}
101 + onclick={() => handleSearchSelect(result)}
102 + onmouseenter={() => selectedIndex.set(i)}
103 + >
104 + <span class="item-code">{result.codigo}</span>
105 + <span class="item-name">{result.nombre}</span>
106 + </button>
107 + {/each}
108 + {/if}
109 + </div>
110 + {/if}
111 +</div>
112 +
113 +<style>
114 + .objeto-search {
115 + position: relative;
116 + display: flex;
117 + align-items: center;
118 + gap: 0.5rem;
119 + background: #f5f5f5;
120 + border: none;
121 + border-radius: 8px;
122 + padding: 0.625rem 1rem;
123 + transition: box-shadow 0.2s;
124 + width: 100%;
125 + }
126 +
127 + .objeto-search.focused {
128 + border-color: #6B9FD4;
129 + box-shadow: 0 0 0 3px rgba(107, 159, 212, 0.15);
130 + }
131 +
132 + .search-icon {
133 + color: var(--theme-texto);
134 + opacity: 0.5;
135 + flex-shrink: 0;
136 + }
137 +
138 + input {
139 + flex: 1;
140 + border: none;
141 + background: transparent;
142 + color: var(--theme-titulo);
143 + font-size: 0.8125rem;
144 + outline: none;
145 + min-width: 120px;
146 + }
147 +
148 + input::placeholder {
149 + color: var(--theme-texto);
150 + opacity: 0.5;
151 + }
152 +
153 + .search-clear {
154 + display: flex;
155 + align-items: center;
156 + justify-content: center;
157 + padding: 0.25rem;
158 + border-radius: 4px;
159 + border: none;
160 + background: transparent;
161 + color: var(--theme-texto);
162 + cursor: pointer;
163 + transition: background 0.2s;
164 + }
165 +
166 + .search-clear:hover {
167 + background: var(--theme-borde);
168 + }
169 +
170 + .search-dropdown {
171 + position: absolute;
172 + top: calc(100% + 4px);
173 + left: 0;
174 + right: 0;
175 + background: var(--theme-surface);
176 + border: 1px solid var(--theme-borde);
177 + border-radius: 8px;
178 + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
179 + max-height: 280px;
180 + overflow-y: auto;
181 + z-index: 100;
182 + }
183 +
184 + .search-msg {
185 + padding: 0.75rem 1rem;
186 + font-size: 0.8125rem;
187 + color: var(--theme-texto);
188 + opacity: 0.7;
189 + }
190 +
191 + .search-item {
192 + display: flex;
193 + align-items: center;
194 + gap: 0.75rem;
195 + width: 100%;
196 + padding: 0.625rem 1rem;
197 + border: none;
198 + background: transparent;
199 + text-align: left;
200 + cursor: pointer;
201 + transition: background 0.15s;
202 + }
203 +
204 + .search-item:hover,
205 + .search-item.selected {
206 + background: var(--theme-surface-hover);
207 + }
208 +
209 + .item-code {
210 + font-family: 'DM Mono', monospace;
211 + font-size: 0.75rem;
212 + color: var(--theme-texto);
213 + min-width: 3.5rem;
214 + }
215 +
216 + .item-name {
217 + font-size: 0.8125rem;
218 + color: var(--theme-titulo);
219 + }
220 +</style>
1 +<script>
2 + let { vista = $bindable('agregado') } = $props();
3 +
4 + function toggle() {
5 + vista = vista === 'agregado' ? 'comparar' : 'agregado';
6 + }
7 +</script>
8 +
9 +<button class="vista-btn" onclick={toggle}>
10 + {#if vista === 'agregado'}
11 + Comparar entre entidades
12 + <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
13 + <path d="M5 12h14M12 5l7 7-7 7"/>
14 + </svg>
15 + {:else}
16 + <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
17 + <path d="M19 12H5M12 19l-7-7 7-7"/>
18 + </svg>
19 + Ver todo
20 + {/if}
21 +</button>
22 +
23 +<style>
24 + .vista-btn {
25 + display: inline-flex;
26 + align-items: center;
27 + gap: 0.375rem;
28 + font-family: 'Qanelas', var(--font-sans);
29 + font-size: 0.75rem;
30 + font-weight: 500;
31 + padding: 0;
32 + border: none;
33 + background: transparent;
34 + color: #666666;
35 + cursor: pointer;
36 + transition: color 0.15s ease;
37 + white-space: nowrap;
38 + text-decoration: underline dotted;
39 + text-underline-offset: 3px;
40 + text-decoration-color: #666666;
41 + }
42 +
43 + .vista-btn svg {
44 + flex-shrink: 0;
45 + opacity: 0.7;
46 + }
47 +
48 + .vista-btn:hover {
49 + color: #999999;
50 + text-decoration-color: #666666;
51 + }
52 +
53 + .vista-btn:hover svg {
54 + opacity: 1;
55 + }
56 +
57 + .vista-btn:active {
58 + color: #555555;
59 + }
60 +</style>
...@@ -60,13 +60,25 @@ ...@@ -60,13 +60,25 @@
60 </div> 60 </div>
61 {/if} 61 {/if}
62 62
63 -<div class="min-h-screen" style="background-color: var(--theme-body);"> 63 +<div class="layout-wrapper">
64 - <main> 64 + <main class="layout-main">
65 {@render children()} 65 {@render children()}
66 </main> 66 </main>
67 </div> 67 </div>
68 68
69 <style> 69 <style>
70 + .layout-wrapper {
71 + min-height: 100vh;
72 + width: 100%;
73 + background-color: var(--theme-body);
74 + }
75 +
76 + .layout-main {
77 + width: 100%;
78 + max-width: 100%;
79 + overflow-x: hidden;
80 + }
81 +
70 .nav-loader { 82 .nav-loader {
71 position: fixed; 83 position: fixed;
72 top: 0; 84 top: 0;
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
37 let gearMenuOpen = false; 37 let gearMenuOpen = false;
38 let searchInput; 38 let searchInput;
39 let debounceTimer; 39 let debounceTimer;
40 + let autoFocused = false;
40 41
41 // Search filters - which types to include 42 // Search filters - which types to include
42 let searchFilters = { 43 let searchFilters = {
...@@ -68,6 +69,14 @@ ...@@ -68,6 +69,14 @@
68 warmupSupabase(); 69 warmupSupabase();
69 }); 70 });
70 71
72 + // Auto-focus en el buscador cuando el índice esté listo (solo una vez)
73 + $: if ($indexLoaded && searchInput && !autoFocused) {
74 + autoFocused = true;
75 + setTimeout(() => {
76 + searchInput?.focus();
77 + }, 50);
78 + }
79 +
71 function toggleMobileSettings() { 80 function toggleMobileSettings() {
72 mobileSettingsOpen = !mobileSettingsOpen; 81 mobileSettingsOpen = !mobileSettingsOpen;
73 } 82 }
...@@ -124,8 +133,12 @@ ...@@ -124,8 +133,12 @@
124 break; 133 break;
125 case 'Enter': 134 case 'Enter':
126 e.preventDefault(); 135 e.preventDefault();
136 + // Si hay un resultado seleccionado, navegar a él
127 if ($selectedIndex >= 0 && filteredResults[$selectedIndex]) { 137 if ($selectedIndex >= 0 && filteredResults[$selectedIndex]) {
128 handleSelect(filteredResults[$selectedIndex]); 138 handleSelect(filteredResults[$selectedIndex]);
139 + } else if (filteredResults.length > 0) {
140 + // Si no hay selección pero hay resultados, ir al primero
141 + handleSelect(filteredResults[0]);
129 } 142 }
130 break; 143 break;
131 case 'Escape': 144 case 'Escape':
...@@ -323,11 +336,11 @@ ...@@ -323,11 +336,11 @@
323 on:blur={handleBlur} 336 on:blur={handleBlur}
324 disabled={!$indexLoaded} 337 disabled={!$indexLoaded}
325 /> 338 />
326 - {#if !searchVal && !searchFocused} 339 + {#if !searchVal}
327 <div class="search-placeholder"> 340 <div class="search-placeholder">
328 <span class="blink-cursor" style="background:{accent}"></span> 341 <span class="blink-cursor" style="background:{accent}"></span>
329 - <span class="placeholder-text placeholder-desktop">{$indexLoaded ? placeholder : 'Cargando...'}</span> 342 + <span class="placeholder-text placeholder-desktop">{placeholder}</span>
330 - <span class="placeholder-text placeholder-mobile">{$indexLoaded ? (isD ? 'MEFP, Min. Salud, GAM...' : 'BOA, Publicidad, IDH...') : 'Cargando...'}</span> 343 + <span class="placeholder-text placeholder-mobile">{isD ? 'MEFP, Min. Salud, GAM...' : 'BOA, Publicidad, IDH...'}</span>
331 </div> 344 </div>
332 {/if} 345 {/if}
333 {#if searchVal && !$isLoading} 346 {#if searchVal && !$isLoading}
......
...@@ -16,19 +16,6 @@ ...@@ -16,19 +16,6 @@
16 let highlightedItem = $state(null); 16 let highlightedItem = $state(null);
17 let sidebarOpen = $state(false); 17 let sidebarOpen = $state(false);
18 18
19 - // Modo embed (cuando está dentro de un iframe en /objeto/[codigo])
20 - let isEmbed = $derived($page.url.searchParams.get('embed') === 'true');
21 - let initialObjeto = $derived($page.url.searchParams.get('objeto'));
22 -
23 - // Función para navegar a un objeto (en modo embed envía mensaje al padre)
24 - function navigateToObjeto(codigo) {
25 - if (isEmbed && window.parent !== window) {
26 - window.parent.postMessage({ type: 'navigate-objeto', codigo }, '*');
27 - } else {
28 - goto(`/objeto/${codigo}`);
29 - }
30 - }
31 -
32 // Modo de visualización desde URL 19 // Modo de visualización desde URL
33 let viewMode = $derived($page.url.searchParams.get('modo') || 'lista'); 20 let viewMode = $derived($page.url.searchParams.get('modo') || 'lista');
34 21
...@@ -1459,7 +1446,6 @@ ...@@ -1459,7 +1446,6 @@
1459 class="transition-colors hover:text-[var(--theme-accent)]" 1446 class="transition-colors hover:text-[var(--theme-accent)]"
1460 style="color: var(--theme-texto);" 1447 style="color: var(--theme-texto);"
1461 title="Ver detalle" 1448 title="Ver detalle"
1462 - onclick={(e) => { if (isEmbed) { e.preventDefault(); navigateToObjeto(selectedGrupo.objeto); }}}
1463 > 1449 >
1464 <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> 1450 <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1465 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> 1451 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
...@@ -1510,7 +1496,6 @@ ...@@ -1510,7 +1496,6 @@
1510 class="transition-colors hover:text-[var(--theme-accent)]" 1496 class="transition-colors hover:text-[var(--theme-accent)]"
1511 style="color: var(--theme-texto);" 1497 style="color: var(--theme-texto);"
1512 title="Ver página de detalle" 1498 title="Ver página de detalle"
1513 - onclick={(e) => { if (isEmbed) { e.preventDefault(); navigateToObjeto(subgrupo.objeto); }}}
1514 > 1499 >
1515 <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> 1500 <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1516 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> 1501 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
...@@ -1557,7 +1542,6 @@ ...@@ -1557,7 +1542,6 @@
1557 class="transition-colors hover:text-[var(--theme-accent)]" 1542 class="transition-colors hover:text-[var(--theme-accent)]"
1558 style="color: var(--theme-texto);" 1543 style="color: var(--theme-texto);"
1559 title="Ver página de detalle" 1544 title="Ver página de detalle"
1560 - onclick={(e) => { if (isEmbed) { e.preventDefault(); navigateToObjeto(partida.objeto); }}}
1561 > 1545 >
1562 <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> 1546 <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1563 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> 1547 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
...@@ -1600,7 +1584,6 @@ ...@@ -1600,7 +1584,6 @@
1600 class="transition-colors hover:text-[var(--theme-accent)]" 1584 class="transition-colors hover:text-[var(--theme-accent)]"
1601 style="color: var(--theme-texto);" 1585 style="color: var(--theme-texto);"
1602 title="Ver página de detalle" 1586 title="Ver página de detalle"
1603 - onclick={(e) => { if (isEmbed) { e.preventDefault(); navigateToObjeto(subpartida.objeto); }}}
1604 > 1587 >
1605 <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> 1588 <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1606 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> 1589 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
......
This diff is collapsed. Click to expand it.