@jjlmoya/utils-shared 1.0.0

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/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # @jjlmoya/utils-shared
2
+
3
+ Library of common components and utilities for jjlmoya projects, built with Astro and TypeScript.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @jjlmoya/utils-shared
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ Ensure you have `astro-icon` configured in your `astro.config.mjs`:
14
+
15
+ ```javascript
16
+ import { defineConfig } from "astro/config";
17
+ import icon from "astro-icon";
18
+
19
+ export default defineConfig({
20
+ integrations: [icon()]
21
+ });
22
+ ```
23
+
24
+ ## Main Components
25
+
26
+ ### SEO Components
27
+
28
+ - **SEOCard**: A premium card with icon support for highlighting information.
29
+ - **SEOList**: A stylistic list with custom icons and hover effects.
30
+ - **SEOStats**: Displays metrics and status with trend indicators.
31
+ - **SEOCode**: A custom code block with syntax highlighting and aria-labels.
32
+ - **SEOComparative**: Used for comparing features or options.
33
+ - **SEODiagnostic**: Shows status, success, or warning messages in a diagnostic format.
34
+ - **SEOTip**: Short informative boxes for advice.
35
+ - **SEOSummary**: A checklist-style list for wrapping up key points.
36
+
37
+ ### UI Components
38
+
39
+ - **UtilityHeader**: A modern header for utility pages with gradient backgrounds.
40
+ - **Bibliography**: A list of links for further reading or citations.
41
+
42
+ ## Usage Example
43
+
44
+ ```astro
45
+ ---
46
+ import { SEOCard, UtilityHeader } from "@jjlmoya/utils-shared";
47
+ ---
48
+
49
+ <UtilityHeader
50
+ titleHighlight="My"
51
+ titleBase="Utility"
52
+ description="This is a description."
53
+ />
54
+
55
+ <SEOCard title="Ready to go" icon="mdi:rocket-launch">
56
+ <p>This is a custom card content.</p>
57
+ </SEOCard>
58
+ ```
59
+
60
+ ## Development
61
+
62
+ To preview components in development:
63
+
64
+ ```bash
65
+ npm run dev
66
+ ```
67
+
68
+ Then visit `http://localhost:51000/` to see the showroom.
69
+
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@jjlmoya/utils-shared",
3
+ "version": "1.0.0",
4
+ "description": "Shared SEO primitives and UI components for jjlmoya ecosystem",
5
+ "author": "jjlmoya",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Game-Bob/jjlmoya-utils-shared.git"
10
+ },
11
+ "main": "./src/index.ts",
12
+ "types": "./src/index.ts",
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "type": "module",
17
+ "exports": {
18
+ ".": "./src/index.ts",
19
+ "./theme.css": "./src/styles/theme.css",
20
+ "./seo/*": "./src/seo/*",
21
+ "./types": "./src/types/index.ts",
22
+ "./ui/*": "./src/ui/*",
23
+ "./styles/*": "./src/styles/*"
24
+ },
25
+ "files": [
26
+ "src"
27
+ ],
28
+ "scripts": {
29
+ "dev": "astro dev",
30
+ "start": "astro dev",
31
+ "check": "astro check",
32
+ "lint": "eslint src --ext .ts,.astro",
33
+ "lint:css": "stylelint \"src/**/*.{astro,css}\"",
34
+ "lint:all": "npm run check && npm run lint && npm run lint:css",
35
+ "prepublishOnly": "npm run lint:all"
36
+ },
37
+ "peerDependencies": {
38
+ "@iconify-json/mdi": "^1.0.0",
39
+ "astro": "^5.0.0",
40
+ "astro-icon": "^1.0.0"
41
+ },
42
+ "devDependencies": {
43
+ "@astrojs/check": "^0.9.2",
44
+ "@iconify-json/mdi": "^1.2.3",
45
+ "@typescript-eslint/eslint-plugin": "^8.58.0",
46
+ "@typescript-eslint/parser": "^8.58.0",
47
+ "astro": "^5.15.9",
48
+ "astro-eslint-parser": "^1.4.0",
49
+ "astro-icon": "^1.1.5",
50
+ "eslint": "^9.39.4",
51
+ "eslint-plugin-astro": "^1.6.0",
52
+ "eslint-plugin-no-comments": "^1.1.10",
53
+ "postcss-html": "^1.8.1",
54
+ "stylelint": "^17.6.0",
55
+ "stylelint-config-standard": "^40.0.0",
56
+ "stylelint-declaration-strict-value": "^1.11.1",
57
+ "typescript": "^5.0.0",
58
+ "typescript-eslint": "^8.58.0"
59
+ }
60
+ }
package/src/env.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ interface ImportMetaEnv {
2
+ readonly SITE: string;
3
+ }
4
+
5
+ interface ImportMeta {
6
+ readonly env: ImportMetaEnv;
7
+ }
package/src/index.ts ADDED
@@ -0,0 +1,24 @@
1
+ export { default as CategorySchema } from './seo/CategorySchema.astro';
2
+ export { default as SEOArticle } from './seo/SEOArticle.astro';
3
+ export { default as SEOCard } from './seo/SEOCard.astro';
4
+ export { default as SEOCode } from './seo/SEOCode.astro';
5
+ export { default as SEOComparative } from './seo/SEOComparative.astro';
6
+ export { default as SEODiagnostic } from './seo/SEODiagnostic.astro';
7
+ export { default as SEOGlossary } from './seo/SEOGlossary.astro';
8
+ export { default as SEOGrid } from './seo/SEOGrid.astro';
9
+ export { default as SEOList } from './seo/SEOList.astro';
10
+ export { default as SEOMessageTemplate } from './seo/SEOMessageTemplate.astro';
11
+ export { default as SEOProsCons } from './seo/SEOProsCons.astro';
12
+ export { default as SEORenderer } from './seo/SEORenderer.astro';
13
+ export { default as SEOStats } from './seo/SEOStats.astro';
14
+ export { default as SEOSummary } from './seo/SEOSummary.astro';
15
+ export { default as SEOTable } from './seo/SEOTable.astro';
16
+ export { default as SEOTip } from './seo/SEOTip.astro';
17
+ export { default as SEOTitle } from './seo/SEOTitle.astro';
18
+
19
+ export { default as Bibliography } from './ui/Bibliography.astro';
20
+ export { default as FAQSection } from './ui/FAQSection.astro';
21
+ export { default as UtilityHeader } from './ui/UtilityHeader.astro';
22
+ export { default as UtilityStructuredData } from './ui/UtilityStructuredData.astro';
23
+
24
+ export * from './types/index';
@@ -0,0 +1,13 @@
1
+ ---
2
+ interface Props {
3
+ id: string;
4
+ label: string;
5
+ }
6
+
7
+ const { id, label } = Astro.props;
8
+ ---
9
+
10
+ <div id={id} class="component-section">
11
+ <span class="section-label">{label}</span>
12
+ <slot />
13
+ </div>
@@ -0,0 +1,52 @@
1
+ ---
2
+ import { Icon } from "astro-icon/components";
3
+
4
+ interface Section {
5
+ id: string;
6
+ name: string;
7
+ }
8
+
9
+ interface Props {
10
+ sections: Section[];
11
+ }
12
+
13
+ const { sections } = Astro.props;
14
+ ---
15
+
16
+ <nav class="sidebar">
17
+ <div class="sidebar-header">
18
+ <h2>Componentes</h2>
19
+ <button id="themeToggle" class="theme-btn" aria-label="Cambiar tema">
20
+ <Icon name="mdi:theme-light-dark" class="theme-icon" />
21
+ </button>
22
+ </div>
23
+ <ul>
24
+ {sections.map((s: Section) => (
25
+ <li><a href={`#${s.id}`}>{s.name}</a></li>
26
+ ))}
27
+ </ul>
28
+ </nav>
29
+
30
+ <script is:inline>
31
+ const setupTheme = () => {
32
+ const btn = document.getElementById('themeToggle');
33
+ const body = document.body;
34
+ const savedTheme = localStorage.getItem('preview-theme') || 'theme-light';
35
+
36
+ body.classList.add(savedTheme);
37
+ if (savedTheme === 'theme-light') body.classList.remove('theme-dark');
38
+ else body.classList.remove('theme-light');
39
+
40
+ btn?.addEventListener('click', () => {
41
+ if (body.classList.contains('theme-light')) {
42
+ body.classList.replace('theme-light', 'theme-dark');
43
+ localStorage.setItem('preview-theme', 'theme-dark');
44
+ } else {
45
+ body.classList.replace('theme-dark', 'theme-light');
46
+ localStorage.setItem('preview-theme', 'theme-light');
47
+ }
48
+ });
49
+ }
50
+
51
+ document.addEventListener('DOMContentLoaded', setupTheme);
52
+ </script>
@@ -0,0 +1,55 @@
1
+ ---
2
+ import SEOCode from "../../../seo/SEOCode.astro";
3
+ import SEOComparative from "../../../seo/SEOComparative.astro";
4
+ import SEODiagnostic from "../../../seo/SEODiagnostic.astro";
5
+ import SEOProsCons from "../../../seo/SEOProsCons.astro";
6
+ import SEOGrid from "../../../seo/SEOGrid.astro";
7
+ import SEOCard from "../../../seo/SEOCard.astro";
8
+ import PreviewSection from "../PreviewSection.astro";
9
+ ---
10
+
11
+ <PreviewSection id="code" label="SEO Code">
12
+ <SEOCode
13
+ code={`import { SEOTip } from "@game-bob/utils-shared";`}
14
+ ariaLabel="Ejemplo de Importación"
15
+ />
16
+ </PreviewSection>
17
+
18
+ <PreviewSection id="comparative" label="SEO Comparative">
19
+ <SEOComparative
20
+ items={[
21
+ { title: "Versión 1", description: "Básica y rudimentaria", points: ["Sin tipos", "Sin estilos"], highlight: false },
22
+ { title: "Versión 2", description: "Premium y robusta", points: ["TypeScript", "Design System"], highlight: true }
23
+ ]}
24
+ />
25
+ </PreviewSection>
26
+
27
+ <PreviewSection id="diagnostic" label="SEO Diagnostic">
28
+ <SEODiagnostic
29
+ title="Estado del Repositorio"
30
+ type="success"
31
+ icon="mdi:check-circle"
32
+ >
33
+ <p>Todos los linters han pasado correctamente. El código es seguro y sigue las guías de estilo.</p>
34
+ </SEODiagnostic>
35
+ </PreviewSection>
36
+
37
+ <PreviewSection id="proscons" label="SEO Pros & Cons">
38
+ <SEOProsCons
39
+ items={[
40
+ { pro: "Fácil de usar", con: "Requiere Astro" },
41
+ { pro: "Sólidos principios", con: "Aprender el sistema" }
42
+ ]}
43
+ />
44
+ </PreviewSection>
45
+
46
+ <PreviewSection id="grid" label="SEO Grid">
47
+ <SEOGrid columns={2}>
48
+ <SEOCard title="Caja 1">
49
+ <p>Contenido de la primera caja del grid.</p>
50
+ </SEOCard>
51
+ <SEOCard title="Caja 2">
52
+ <p>Contenido de la segunda caja del grid.</p>
53
+ </SEOCard>
54
+ </SEOGrid>
55
+ </PreviewSection>
@@ -0,0 +1,49 @@
1
+ ---
2
+ import UtilityHeader from "../../../ui/UtilityHeader.astro";
3
+ import SEOList from "../../../seo/SEOList.astro";
4
+ import SEOTip from "../../../seo/SEOTip.astro";
5
+ import SEOCard from "../../../seo/SEOCard.astro";
6
+ import SEOStats from "../../../seo/SEOStats.astro";
7
+ import PreviewSection from "../PreviewSection.astro";
8
+ ---
9
+
10
+ <PreviewSection id="header" label="Utility Header">
11
+ <UtilityHeader
12
+ titleHighlight="Shared"
13
+ titleBase="Library Components"
14
+ description="Vista previa de todos los componentes disponibles en la librería."
15
+ />
16
+ </PreviewSection>
17
+
18
+ <PreviewSection id="list" label="SEO List">
19
+ <SEOList
20
+ items={[
21
+ "Totalmente tipados con TypeScript",
22
+ "Accesibles y semánticos",
23
+ "Estilos modernos y consistentes",
24
+ "Fáciles de integrar en Astro"
25
+ ]}
26
+ icon="mdi:check"
27
+ />
28
+ </PreviewSection>
29
+
30
+ <PreviewSection id="tip" label="SEO Tip">
31
+ <SEOTip title="Consejo de Implementación">
32
+ <p>Usa estos componentes para mejorar el SEO y la legibilidad de tus utilitarios de forma rápida.</p>
33
+ </SEOTip>
34
+ </PreviewSection>
35
+
36
+ <PreviewSection id="card" label="SEO Card">
37
+ <SEOCard title="Componentes Optimizados" icon="mdi:rocket-launch">
38
+ <p>Nuestra librería está diseñada para ser ligera y eficiente en cualquier proyecto Astro.</p>
39
+ </SEOCard>
40
+ </PreviewSection>
41
+
42
+ <PreviewSection id="stats" label="SEO Stats">
43
+ <SEOStats
44
+ stats={[
45
+ { label: "Velocidad", value: "99%", icon: "mdi:speedometer" },
46
+ { label: "Accesibilidad", value: "100/100", icon: "mdi:accessibility" }
47
+ ]}
48
+ />
49
+ </PreviewSection>
@@ -0,0 +1,57 @@
1
+ ---
2
+ import SEOMessageTemplate from "../../../seo/SEOMessageTemplate.astro";
3
+ import SEOSummary from "../../../seo/SEOSummary.astro";
4
+ import SEOTable from "../../../seo/SEOTable.astro";
5
+ import SEOGlossary from "../../../seo/SEOGlossary.astro";
6
+ import Bibliography from "../../../ui/Bibliography.astro";
7
+ import PreviewSection from "../PreviewSection.astro";
8
+ ---
9
+
10
+ <PreviewSection id="message" label="SEO Message Template">
11
+ <SEOMessageTemplate title="Mensaje Informativo">
12
+ Este es un ejemplo de mensaje que utiliza el template genérico.
13
+ </SEOMessageTemplate>
14
+ </PreviewSection>
15
+
16
+ <PreviewSection id="summary" label="SEO Summary">
17
+ <SEOSummary
18
+ title="Puntos Clave"
19
+ items={[
20
+ "<strong>Consistencia:</strong> Visualmente unificado.",
21
+ "<strong>Mantenimiento:</strong> Componentes desacoplados."
22
+ ]}
23
+ />
24
+ </PreviewSection>
25
+
26
+ <PreviewSection id="table" label="SEO Table">
27
+ <SEOTable headers={["Componente", "Tipo", "Estado"]}>
28
+ <tr>
29
+ <td>SEOTable</td>
30
+ <td>Maquetación</td>
31
+ <td>Listo</td>
32
+ </tr>
33
+ <tr>
34
+ <td>SEOStats</td>
35
+ <td>Visualización</td>
36
+ <td>Listo</td>
37
+ </tr>
38
+ </SEOTable>
39
+ </PreviewSection>
40
+
41
+ <PreviewSection id="glossary" label="SEO Glossary">
42
+ <SEOGlossary
43
+ items={[
44
+ { term: "Astro", definition: "Framework web moderno para construir sitios rápidos cargando menos JavaScript." },
45
+ { term: "SOLID", definition: "Cinco principios de diseño orientado a objetos destinados a hacer que los diseños de software sean más comprensibles, flexibles y fáciles de mantener." }
46
+ ]}
47
+ />
48
+ </PreviewSection>
49
+
50
+ <PreviewSection id="bibliography" label="Bibliography">
51
+ <Bibliography
52
+ links={[
53
+ { name: "Astro Documentation", url: "https://docs.astro.build" },
54
+ { name: "GitHub Organization", url: "https://github.com/Game-Bob" }
55
+ ]}
56
+ />
57
+ </PreviewSection>
@@ -0,0 +1,104 @@
1
+ body {
2
+ font-family: Inter, system-ui, sans-serif;
3
+ background: var(--bg-page);
4
+ color: var(--text-base);
5
+ margin: 0;
6
+ display: flex;
7
+ transition: background 0.3s ease, color 0.3s ease;
8
+ }
9
+
10
+ .sidebar {
11
+ width: 260px;
12
+ height: 100vh;
13
+ background: var(--bg-surface);
14
+ border-right: 1px solid var(--border-base);
15
+ padding: 2rem 1.5rem;
16
+ position: fixed;
17
+ overflow-y: auto;
18
+ }
19
+
20
+ .sidebar-header {
21
+ display: flex;
22
+ align-items: center;
23
+ justify-content: space-between;
24
+ margin-bottom: 2rem;
25
+ }
26
+
27
+ .sidebar h2 {
28
+ font-size: 0.75rem;
29
+ text-transform: uppercase;
30
+ letter-spacing: 0.1em;
31
+ color: var(--text-dimmed);
32
+ margin: 0;
33
+ }
34
+
35
+ .theme-btn {
36
+ background: var(--bg-muted);
37
+ border: 1px solid var(--border-base);
38
+ color: var(--text-base);
39
+ width: 32px;
40
+ height: 32px;
41
+ border-radius: 8px;
42
+ display: flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ cursor: pointer;
46
+ transition: all 0.2s;
47
+ }
48
+
49
+ .theme-btn:hover {
50
+ background: var(--border-base);
51
+ transform: scale(1.05);
52
+ }
53
+
54
+ .theme-icon {
55
+ width: 18px;
56
+ height: 18px;
57
+ }
58
+
59
+ .sidebar ul {
60
+ list-style: none;
61
+ padding: 0;
62
+ margin: 0;
63
+ }
64
+
65
+ .sidebar li {
66
+ margin-bottom: 0.5rem;
67
+ }
68
+
69
+ .sidebar a {
70
+ text-decoration: none;
71
+ color: var(--text-muted);
72
+ font-size: 0.95rem;
73
+ font-weight: 500;
74
+ display: block;
75
+ padding: 0.5rem 0.75rem;
76
+ border-radius: 8px;
77
+ transition: all 0.2s;
78
+ }
79
+
80
+ .sidebar a:hover {
81
+ background: var(--bg-muted);
82
+ color: var(--text-base);
83
+ }
84
+
85
+ .main {
86
+ flex: 1;
87
+ margin-left: 260px;
88
+ padding: 4rem;
89
+ }
90
+
91
+ .component-section {
92
+ margin-bottom: 8rem;
93
+ scroll-margin-top: 4rem;
94
+ }
95
+
96
+ .section-label {
97
+ font-size: 0.7rem;
98
+ font-weight: 800;
99
+ color: var(--primary);
100
+ text-transform: uppercase;
101
+ letter-spacing: 0.1em;
102
+ margin-bottom: 1rem;
103
+ display: block;
104
+ }
@@ -0,0 +1,42 @@
1
+ ---
2
+ import PreviewSidebar from "./_components/PreviewSidebar.astro";
3
+ import GroupBasic from "./_components/preview/GroupBasic.astro";
4
+ import GroupAdvanced from "./_components/preview/GroupAdvanced.astro";
5
+ import GroupContent from "./_components/preview/GroupContent.astro";
6
+ import "./_components/preview/layout.css";
7
+ import "../styles/theme.css";
8
+
9
+ const sections = [
10
+ { id: "header", name: "Utility Header" },
11
+ { id: "list", name: "SEO List" },
12
+ { id: "tip", name: "SEO Tip" },
13
+ { id: "card", name: "SEO Card" },
14
+ { id: "stats", name: "SEO Stats" },
15
+ { id: "code", name: "SEO Code" },
16
+ { id: "comparative", name: "SEO Comparative" },
17
+ { id: "diagnostic", name: "SEO Diagnostic" },
18
+ { id: "proscons", name: "SEO Pros & Cons" },
19
+ { id: "grid", name: "SEO Grid" },
20
+ { id: "message", name: "SEO Message Template" },
21
+ { id: "summary", name: "SEO Summary" },
22
+ { id: "table", name: "SEO Table" },
23
+ { id: "glossary", name: "SEO Glossary" },
24
+ { id: "bibliography", name: "Bibliography" },
25
+ ];
26
+ ---
27
+
28
+ <html lang="es">
29
+ <head>
30
+ <meta charset="utf-8" />
31
+ <meta name="viewport" content="width=device-width" />
32
+ <title>Component Library Preview</title>
33
+ </head>
34
+ <body>
35
+ <PreviewSidebar sections={sections} />
36
+ <main class="main">
37
+ <GroupBasic />
38
+ <GroupAdvanced />
39
+ <GroupContent />
40
+ </main>
41
+ </body>
42
+ </html>
@@ -0,0 +1,37 @@
1
+ ---
2
+ import type { SectionData } from "../types/index.ts";
3
+
4
+ interface Props {
5
+ section: SectionData;
6
+ baseUrl?: string | undefined;
7
+ categoryPath?: string | undefined;
8
+ }
9
+
10
+ const {
11
+ section,
12
+ baseUrl = "https://www.jjlmoya.es",
13
+ categoryPath = "/utilidades/categorias/",
14
+ } = Astro.props;
15
+
16
+ const categorySchema = {
17
+ "@context": "https://schema.org",
18
+ "@type": "CollectionPage",
19
+ name: section.title,
20
+ url: `${baseUrl}${categoryPath}${section.slug}/`,
21
+ hasPart: section.utilities.map((u) => ({
22
+ "@context": "https://schema.org",
23
+ "@type": "WebApplication",
24
+ name: u.title,
25
+ description: u.description,
26
+ url: `${baseUrl}${u.href}`,
27
+ applicationCategory: "UtilityApplication",
28
+ operatingSystem: "Any",
29
+ })),
30
+ };
31
+ ---
32
+
33
+ <script
34
+ is:inline
35
+ type="application/ld+json"
36
+ set:html={JSON.stringify(categorySchema)}
37
+ />
@@ -0,0 +1,45 @@
1
+ ---
2
+ interface Props {
3
+ class?: string;
4
+ }
5
+
6
+ const { class: className } = Astro.props;
7
+ ---
8
+
9
+ <article class:list={["seo-article", className]}>
10
+ <slot />
11
+ </article>
12
+
13
+ <style>
14
+ .seo-article {
15
+ line-height: 1.8;
16
+ color: var(--text-muted);
17
+ font-size: 1.125rem;
18
+ max-width: 48rem;
19
+ margin: 0 auto;
20
+ }
21
+
22
+ .seo-article :global(p) {
23
+ margin-bottom: 1.5rem;
24
+ }
25
+
26
+ .seo-article :global(strong) {
27
+ color: var(--text-base);
28
+ font-weight: 700;
29
+ }
30
+
31
+ .seo-article :global(code:not(pre code)) {
32
+ background: var(--bg-muted);
33
+ color: var(--primary);
34
+ padding: 0.2rem 0.4rem;
35
+ border-radius: 0.375rem;
36
+ font-size: 0.875em;
37
+ font-weight: 600;
38
+ }
39
+
40
+ @media (max-width: 768px) {
41
+ .seo-article {
42
+ font-size: 1rem;
43
+ }
44
+ }
45
+ </style>