@dile/ui 2.18.3 → 2.20.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/components/copy-markdown-url/README.md +87 -0
- package/components/copy-markdown-url/copy-markdown-url.js +3 -0
- package/components/copy-markdown-url/index.js +5 -0
- package/components/copy-markdown-url/src/DileCopyMarkdownUrl.js +99 -0
- package/components/copy-text/src/DileCopyText.js +21 -16
- package/components/floating-feedback/src/DileFloatingFeedback.js +10 -3
- package/package.json +2 -2
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# dile-copy-markdown-url
|
|
2
|
+
|
|
3
|
+
Un componente Web que permite copiar el contenido de una URL al portapapeles. Reutiliza el componente `dile-copy-text` y usa fetch para obtener el contenido de la URL de forma simple y eficiente.
|
|
4
|
+
|
|
5
|
+
## Instalación
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @dile/ui
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Importación
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
import '@dile/ui/components/copy-markdown-url/copy-markdown-url.js';
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Uso
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<dile-copy-markdown-url
|
|
21
|
+
url="https://ejemplo.com/archivo.md"
|
|
22
|
+
>
|
|
23
|
+
Copiar markdown
|
|
24
|
+
</dile-copy-markdown-url>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Propiedades
|
|
28
|
+
|
|
29
|
+
- **url** (String): La URL del contenido a copiar
|
|
30
|
+
- **copiedDuration** (Number): Duración en milisegundos del feedback de copia (por defecto: 1000)
|
|
31
|
+
- **feedbackText** (String): Texto del feedback visual (por defecto: 'Copied!')
|
|
32
|
+
- **hideIcon** (Boolean): Ocultar el icono en el feedback (por defecto: false)
|
|
33
|
+
|
|
34
|
+
## Eventos
|
|
35
|
+
|
|
36
|
+
Hereda todos los eventos de `dile-copy-text`:
|
|
37
|
+
|
|
38
|
+
- **dile-text-copied**: Se dispara cuando el contenido se ha copiado correctamente
|
|
39
|
+
- `detail.text`: El contenido que se copió
|
|
40
|
+
|
|
41
|
+
Además, dispara:
|
|
42
|
+
|
|
43
|
+
- **dile-copy-markdown-url-error**: Se dispara cuando hay un error al obtener el contenido de la URL
|
|
44
|
+
- `detail.message`: Descripción del error
|
|
45
|
+
|
|
46
|
+
## Estilos CSS personalizables
|
|
47
|
+
|
|
48
|
+
El componente hereda los estilos de `dile-copy-text`, así que puedes usar las mismas variables CSS:
|
|
49
|
+
|
|
50
|
+
- `--dile-copy-text-text-decoration`: Decoración del texto del enlace (por defecto: none)
|
|
51
|
+
- `--dile-copy-text-color`: Color del texto del enlace (por defecto: inherit)
|
|
52
|
+
- `--dile-copy-text-transition-duration`: Duración de la transición (por defecto: 0.3s)
|
|
53
|
+
- `--dile-copy-text-hover-color`: Color del texto al hacer hover (por defecto: #0066cc)
|
|
54
|
+
- `--dile-copy-text-hover-text-decoration`: Decoración al hacer hover (por defecto: underline)
|
|
55
|
+
- `--dile-copy-text-focus-outline`: Contorno al tener enfoque (por defecto: 2px solid #0066cc)
|
|
56
|
+
|
|
57
|
+
## Ejemplo completo
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<dile-copy-markdown-url
|
|
61
|
+
url="https://raw.githubusercontent.com/user/repo/main/README.md"
|
|
62
|
+
feedbackText="¡Markdown copiado!"
|
|
63
|
+
copiedDuration="2000"
|
|
64
|
+
>
|
|
65
|
+
📋 Copiar README
|
|
66
|
+
</dile-copy-markdown-url>
|
|
67
|
+
|
|
68
|
+
<script type="module">
|
|
69
|
+
const copyBtn = document.querySelector('dile-copy-markdown-url');
|
|
70
|
+
|
|
71
|
+
copyBtn.addEventListener('dile-text-copied', (e) => {
|
|
72
|
+
console.log('Contenido copiado:', e.detail.text);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
copyBtn.addEventListener('dile-copy-markdown-url-error', (e) => {
|
|
76
|
+
console.error('Error:', e.detail.message);
|
|
77
|
+
});
|
|
78
|
+
</script>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Comportamiento
|
|
82
|
+
|
|
83
|
+
1. El usuario hace click en el componente
|
|
84
|
+
2. Si es la primera vez, el componente hace un fetch a la URL especificada
|
|
85
|
+
3. Una vez obtiene el contenido, automáticamente lo copia al portapapeles
|
|
86
|
+
4. Se muestra un feedback visual indicando que el contenido se copió
|
|
87
|
+
5. En los siguientes clicks, el contenido ya está cacheado y se copia directamente
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { LitElement, html } from 'lit';
|
|
2
|
+
import '../../copy-text/copy-text.js';
|
|
3
|
+
|
|
4
|
+
export class DileCopyMarkdownUrl extends LitElement {
|
|
5
|
+
static get properties() {
|
|
6
|
+
return {
|
|
7
|
+
url: { type: String },
|
|
8
|
+
copiedDuration: { type: Number },
|
|
9
|
+
feedbackText: { type: String },
|
|
10
|
+
hideIcon: { type: Boolean },
|
|
11
|
+
loading: { type: Boolean, state: true },
|
|
12
|
+
error: { type: String, state: true },
|
|
13
|
+
content: { type: String, state: true }
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
constructor() {
|
|
18
|
+
super();
|
|
19
|
+
this.url = '';
|
|
20
|
+
this.copiedDuration = 1000;
|
|
21
|
+
this.feedbackText = 'Copied!';
|
|
22
|
+
this.hideIcon = false;
|
|
23
|
+
this.loading = false;
|
|
24
|
+
this.error = null;
|
|
25
|
+
this.content = '';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
render() {
|
|
29
|
+
return html`
|
|
30
|
+
<dile-copy-text
|
|
31
|
+
id="copy-component"
|
|
32
|
+
.content="${this.content}"
|
|
33
|
+
.feedbackText="${this.feedbackText}"
|
|
34
|
+
.copiedDuration="${this.copiedDuration}"
|
|
35
|
+
?hideIcon="${this.hideIcon}"
|
|
36
|
+
@click="${this.handleClick}"
|
|
37
|
+
>
|
|
38
|
+
<slot></slot>
|
|
39
|
+
</dile-copy-text>
|
|
40
|
+
`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async handleClick(e) {
|
|
44
|
+
if (this.loading) {
|
|
45
|
+
e.preventDefault();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!this.content && this.url) {
|
|
50
|
+
e.preventDefault();
|
|
51
|
+
await this.fetchContent();
|
|
52
|
+
|
|
53
|
+
// After fetching, trigger copy on the component using its public API
|
|
54
|
+
const copyComponent = this.shadowRoot.querySelector('#copy-component');
|
|
55
|
+
if (copyComponent && this.content) {
|
|
56
|
+
await copyComponent.copy();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async fetchContent() {
|
|
62
|
+
if (!this.url) {
|
|
63
|
+
this.error = 'URL is not set';
|
|
64
|
+
this.dispatchEvent(
|
|
65
|
+
new CustomEvent('dile-copy-markdown-url-error', {
|
|
66
|
+
detail: { message: this.error },
|
|
67
|
+
bubbles: true,
|
|
68
|
+
composed: true
|
|
69
|
+
})
|
|
70
|
+
);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
this.loading = true;
|
|
75
|
+
this.error = null;
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const response = await fetch(this.url);
|
|
79
|
+
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this.content = await response.text();
|
|
85
|
+
} catch (err) {
|
|
86
|
+
this.error = err.message || 'Error fetching content';
|
|
87
|
+
|
|
88
|
+
this.dispatchEvent(
|
|
89
|
+
new CustomEvent('dile-copy-markdown-url-error', {
|
|
90
|
+
detail: { message: this.error },
|
|
91
|
+
bubbles: true,
|
|
92
|
+
composed: true
|
|
93
|
+
})
|
|
94
|
+
);
|
|
95
|
+
} finally {
|
|
96
|
+
this.loading = false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -30,7 +30,7 @@ export class DileCopyText extends LitElement {
|
|
|
30
30
|
|
|
31
31
|
static get properties() {
|
|
32
32
|
return {
|
|
33
|
-
|
|
33
|
+
content: { type: String },
|
|
34
34
|
copiedDuration: { type: Number },
|
|
35
35
|
feedbackText: { type: String },
|
|
36
36
|
hideIcon: { type: Boolean }
|
|
@@ -39,7 +39,7 @@ export class DileCopyText extends LitElement {
|
|
|
39
39
|
|
|
40
40
|
constructor() {
|
|
41
41
|
super();
|
|
42
|
-
this.
|
|
42
|
+
this.content = '';
|
|
43
43
|
this.copiedDuration = 1000; // 2 seconds by default
|
|
44
44
|
this.feedbackText = 'Copied!';
|
|
45
45
|
this.hideIcon = false;
|
|
@@ -47,22 +47,23 @@ export class DileCopyText extends LitElement {
|
|
|
47
47
|
|
|
48
48
|
render() {
|
|
49
49
|
return html`
|
|
50
|
-
<a
|
|
51
|
-
href="#"
|
|
52
|
-
role="button"
|
|
53
|
-
tabindex="0"
|
|
54
|
-
@click="${this.handleCopy}"
|
|
55
|
-
@keydown="${this.handleKeydown}"
|
|
56
|
-
>
|
|
57
|
-
<slot></slot>
|
|
58
|
-
</a>
|
|
59
50
|
<dile-floating-feedback
|
|
60
51
|
id="copy-feedback"
|
|
61
52
|
feedback="${this.feedbackText}"
|
|
62
53
|
duration="${this.copiedDuration}"
|
|
63
54
|
?hideIcon=${this.hideIcon}
|
|
64
55
|
.icon=${contentCopyIcon}
|
|
65
|
-
|
|
56
|
+
>
|
|
57
|
+
<a
|
|
58
|
+
href="#"
|
|
59
|
+
role="button"
|
|
60
|
+
tabindex="0"
|
|
61
|
+
@click="${this.handleCopy}"
|
|
62
|
+
@keydown="${this.handleKeydown}"
|
|
63
|
+
>
|
|
64
|
+
<slot></slot>
|
|
65
|
+
</a>
|
|
66
|
+
</dile-floating-feedback>
|
|
66
67
|
`;
|
|
67
68
|
}
|
|
68
69
|
|
|
@@ -82,7 +83,7 @@ export class DileCopyText extends LitElement {
|
|
|
82
83
|
try {
|
|
83
84
|
// Try to use Clipboard API first (modern browsers)
|
|
84
85
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
85
|
-
await navigator.clipboard.writeText(this.
|
|
86
|
+
await navigator.clipboard.writeText(this.content);
|
|
86
87
|
} else {
|
|
87
88
|
// Fallback for older browsers
|
|
88
89
|
this.copyWithExecCommand();
|
|
@@ -91,7 +92,7 @@ export class DileCopyText extends LitElement {
|
|
|
91
92
|
// Dispatch custom event
|
|
92
93
|
this.dispatchEvent(
|
|
93
94
|
new CustomEvent('dile-text-copied', {
|
|
94
|
-
detail: { text: this.
|
|
95
|
+
detail: { text: this.content },
|
|
95
96
|
bubbles: true,
|
|
96
97
|
composed: true
|
|
97
98
|
})
|
|
@@ -106,7 +107,7 @@ export class DileCopyText extends LitElement {
|
|
|
106
107
|
this.copyWithExecCommand();
|
|
107
108
|
this.dispatchEvent(
|
|
108
109
|
new CustomEvent('dile-text-copied', {
|
|
109
|
-
detail: { text: this.
|
|
110
|
+
detail: { text: this.content },
|
|
110
111
|
bubbles: true,
|
|
111
112
|
composed: true
|
|
112
113
|
})
|
|
@@ -120,7 +121,7 @@ export class DileCopyText extends LitElement {
|
|
|
120
121
|
|
|
121
122
|
copyWithExecCommand() {
|
|
122
123
|
const textArea = document.createElement('textarea');
|
|
123
|
-
textArea.value = this.
|
|
124
|
+
textArea.value = this.content;
|
|
124
125
|
textArea.style.position = 'fixed';
|
|
125
126
|
textArea.style.left = '-999999px';
|
|
126
127
|
textArea.style.top = '-999999px';
|
|
@@ -140,4 +141,8 @@ export class DileCopyText extends LitElement {
|
|
|
140
141
|
feedback.show();
|
|
141
142
|
}
|
|
142
143
|
}
|
|
144
|
+
|
|
145
|
+
async copy() {
|
|
146
|
+
await this.copyToClipboard();
|
|
147
|
+
}
|
|
143
148
|
}
|
|
@@ -35,7 +35,7 @@ export class DileFloatingFeedback extends LitElement {
|
|
|
35
35
|
|
|
36
36
|
render() {
|
|
37
37
|
return html`
|
|
38
|
-
<
|
|
38
|
+
<slot class="feedback-anchor"></slot>
|
|
39
39
|
`;
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -116,8 +116,15 @@ export class DileFloatingFeedback extends LitElement {
|
|
|
116
116
|
// Add content container to floating element
|
|
117
117
|
floatingElement.appendChild(contentContainer);
|
|
118
118
|
|
|
119
|
-
// Calculate position relative to
|
|
120
|
-
const
|
|
119
|
+
// Calculate position relative to the slot's content
|
|
120
|
+
const anchorElement = this.children[0];
|
|
121
|
+
if (!anchorElement) {
|
|
122
|
+
console.warn('dile-floating-feedback: No element in slot to anchor to');
|
|
123
|
+
floatingElement.remove();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const rect = anchorElement.getBoundingClientRect();
|
|
121
128
|
const x = rect.left + rect.width / 2;
|
|
122
129
|
const y = rect.top;
|
|
123
130
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dile/ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.20.0",
|
|
4
4
|
"description": "UI Core components from dile-components.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -26,5 +26,5 @@
|
|
|
26
26
|
"publishConfig": {
|
|
27
27
|
"access": "public"
|
|
28
28
|
},
|
|
29
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "cd0b6acfaed41004ccd714f0ad586bc66b303479"
|
|
30
30
|
}
|