Skip to content

Commit

Permalink
feat: add link component
Browse files Browse the repository at this point in the history
  • Loading branch information
ErickPetru committed Mar 18, 2024
1 parent dbde753 commit f05b4db
Show file tree
Hide file tree
Showing 6 changed files with 436 additions and 0 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ O versionamento deste projeto é aderente aos princípios de [Semantic Versionin

## Unreleased

### Added

- Criação do componente `<cps-link>`.

### Changed

- Pequenos ajustes na documentação.

## 0.10.0 - 2024-03-11

### Added
Expand Down
1 change: 1 addition & 0 deletions docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- [Icon Button](/componentes/icon-button)
- [Input](/componentes/input)
- [Label](/componentes/label)
- [Link](/componentes/link)
- [Menu](/componentes/menu)
- [Menu Item](/componentes/menu-item)
- [Menu Label](/componentes/menu-label)
Expand Down
180 changes: 180 additions & 0 deletions docs/componentes/link.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# Link

[component-header:cps-link]

```html preview
<cps-link href="#">Navegar para página principal</cps-link>
```

?> É viável renderizar âncoras de navegação apenas com elementos HTML puros (como um simples `<a>`) e aplicar a estilização desejada, inclusive se aproveitando de [variáveis de estilo de tipografia](/variáveis-de-estilo/tipografia.md) oferecidas pelos [temas](/fundamentos/temas.md) padrão. Considere o componente `<cps-link>` um facilitador, basicamente um _wrapper_ que agiliza a aplicação da estilização adequada, conforme os estilos de tipografia disponíveis, e em aderência ao CPS Design System.

## Exemplos

### Endereço de destino

Use o atributo `href` para definir o endereço de destino do _link_. O valor poder ser uma URL absoluta válida, ou um caminho relativo para navegação interna, ou um identificador de âncora.

```html preview
<cps-link href="https://www.cps.sp.gov.br/">Navegar para portal CPS</cps-link>
```

```jsx react
import { CpsLink } from '@cps-elements/web/react/link';

const App = () => (
<CpsLink href="https://www.cps.sp.gov.br/">Navegar para portal CPS</CpsLink>
);
```

### Alvo de navegação

Use o atributo `target` para definir o alvo de navegação do _link_. O valor padrão é `_self`, que indica que o _link_ será aberto na mesma janela ou aba. Outros valores possíveis são `_blank`, `_parent`, `_top`, ou um nome de janela ou _frame_ específico.

```html preview
<cps-link href="https://www.cps.sp.gov.br/" target="_blank">Abrir portal CPS em nova aba</cps-link>
```

```jsx react
import { CpsLink } from '@cps-elements/web/react/link';

const App = () => (
<CpsLink href="https://www.cps.sp.gov.br/" target="_blank">Abrir portal CPS em nova aba</CpsLink>
);
```

### Estado desabilitado

Use o atributo `disabled` para desabilitar o _link_. Cliques serão suprimidos, e nenhum tipo de interação ou _feedback_ visual sobre interação serão permitidos enquanto o estado desabilitado não for removido.

```html preview
<cps-link href="#" disabled>Navegação desabilitada</cps-link>
```

```jsx react
import { CpsLink } from '@cps-elements/web/react/link';

const App = () => (
<>
<CpsLink href="#" disabled>Navegação desabilitada</CpsLink>
</>
);
```

### Ícones como prefixo ou sufixo

Use os _slots_ `prefix` e `suffix` para adicionar ícones.

```html preview
<cps-link href="#/fundamentos/instalação">
<cps-icon slot="prefix" name="box"></cps-icon>
Instalação
</cps-link>

<br /><br />

<cps-link href="#">
<cps-icon slot="suffix" name="arrow-counterclockwise"></cps-icon>
Recomeçar
</cps-link>

<br /><br />

<cps-link href="https://www.cps.sp.gov.br/" target="_blank">
<cps-icon slot="prefix" name="cloud-link-fill"></cps-icon>
<cps-icon slot="suffix" name="arrow-up-right"></cps-icon>
Endereço externo
</cps-link>
```

```jsx react
import { CpsLink } from '@cps-elements/web/react/link';
import { CpsIcon } from '@cps-elements/web/react/icon';

const App = () => (
<>
<CpsLink href="#/fundamentos/instalação">
<CpsIcon slot="prefix" name="box"></CpsIcon>
Instalação
</CpsLink>

<br /><br />

<CpsLink href="#">
<CpsIcon slot="suffix" name="arrow-counterclockwise"></CpsIcon>
Recomeçar
</CpsLink>

<br /><br />

<CpsLink href="https://www.cps.sp.gov.br/" target="_blank">
<CpsIcon slot="prefix" name="cloud-link-fill"></CpsIcon>
<CpsIcon slot="suffix" name="arrow-up-right"></CpsIcon>
Endereço externo
</CpsLink>
</>
);
```

### Tamanhos

Use o atributo `size` para definir o tamanho e o estilo do rótulo em conformidade à pilha tipografia.

```html preview
<div class="link-sizes-example">
<cps-link size="stamp">Timbre</cps-link>
<cps-link size="caption">Rubrica</cps-link>
<cps-link size="link">Rótulo</cps-link>
<cps-link size="body">Corpo</cps-link>
<cps-link size="body-emphasized">Corpo enfatizado</cps-link>
<cps-link size="body-strong">Corpo destacado</cps-link>
<cps-link size="body-large">Corpo grande</cps-link>
<cps-link size="subtitle">Subtítulo</cps-link>
<cps-link size="title">Título</cps-link>
<cps-link size="heading">Cabeçalho</cps-link>
<cps-link size="display">Exibição</cps-link>
</div>

<style>
.link-sizes-example {
display: flex;
flex-direction: column;
gap: 1rem;
}
</style>
```

```jsx react
import { CpsLink } from '@cps-elements/web/react/link';

const css = `
.link-sizes-example {
display: flex;
flex-direction: column;
gap: 1rem;
}
`;

const App = () => (
<>
<div class="link-sizes-example">
<CpsLink size="stamp">Timbre</CpsLink>
<CpsLink size="caption">Rubrica</CpsLink>
<CpsLink size="link">Rótulo</CpsLink>
<CpsLink size="body">Corpo</CpsLink>
<CpsLink size="body-emphasized">Corpo enfatizado</CpsLink>
<CpsLink size="body-strong">Corpo destacado</CpsLink>
<CpsLink size="body-large">Corpo grande</CpsLink>
<CpsLink size="subtitle">Subtítulo</CpsLink>
<CpsLink size="title">Título</CpsLink>
<CpsLink size="heading">Cabeçalho</CpsLink>
<CpsLink size="display">Exibição</CpsLink>
</div>

<style>{css}</style>
</>
);
```

?> Não confunda os nomes de tamanhos como `body-emphasized` e `body-strong` com definições de semântica. Esses nomes são apenas para fins de tamanho e estilo, sendo que a semântica pode ser controlada através da definição de _tags_ HTML embrulhando o conteúdo do _link_, por exemplo.

[component-metadata:cps-link]
1 change: 1 addition & 0 deletions src/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export { CpsIconButton } from './components/icon-button.js';
export { CpsInclude } from './components/include.js';
export { CpsInput } from './components/input.js';
export { CpsLabel } from './components/label.js';
export { CpsLink } from './components/link.js';
export { CpsMenu } from './components/menu.js';
export { CpsMenuItem } from './components/menu-item.js';
export { CpsMenuLabel } from './components/menu-label.js';
Expand Down
138 changes: 138 additions & 0 deletions src/components/link.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { classMap } from 'lit/directives/class-map.js';
import { customElement, property, query } from 'lit/decorators.js';
import { HasSlotController } from '../internal/slot.js';
import { html } from 'lit/static-html.js';
import { LocalizeController } from '../utilities/localize.js';
import BaseElement from '../internal/base-element.js';
import styles from './link/link.styles.js';
import type { CSSResultGroup } from 'lit';

/**
* @summary _Links_ são usados para navegação entre páginas ou para abrir URLs externas.
* @documentation https://cpsrepositorio.github.io/cps-elements/#/componentes/link
* @status stable
* @since 0.11
*
* @event cps-blur - Emitido quando o _link_ perde o foco.
* @event cps-focus - Emitido quando o _link_ obtém o foco.
*
* @csspart base - O elemento principal propriamente dito (um `<a>`).
* @csspart prefix - O elemento que embrulha a renderização do _slot_ `prefix`.
* @csspart content - O elemento que embrulha o conteúdo principal, normalmente o texto do _link_.
* @csspart suffix - O elemento que embrulha a renderização do _slot_ `suffix`.
*/
@customElement('cps-link')
export default class CpsLink extends BaseElement {
static styles: CSSResultGroup = styles;

private readonly hasSlotController = new HasSlotController(this, '[default]', 'prefix', 'suffix');
private readonly localize = new LocalizeController(this);

@query('.link') link: HTMLAnchorElement;

/** Desabilita o _link_. */
@property({ type: Boolean, reflect: true }) disabled = false;

/** O destino do _link_, podendo ser tanto um caminho relativo ao documento atual,
* para navegação interna, quanto um caminho absoluto para navegação externa. */
@property() href = '';

/** O tamanho do _link_. */
@property({ reflect: true }) size:
| 'stamp'
| 'caption'
| 'label'
| 'body'
| 'body-emphasized'
| 'body-strong'
| 'body-large'
| 'subtitle'
| 'title'
| 'heading'
| 'display' = 'body';

/** O alvo do _link_, podendo ser `_self`, `_blank`, `_parent`, `_top`, ou um nome de janela ou _frame_ específico. */
@property() target = '_self';

private handleClick(event: MouseEvent) {
// Prevent the click event from being emitted when the anchor is disabled
if (this.disabled) {
event.preventDefault();
event.stopImmediatePropagation();
}
}

private handleBlur() {
this.emit('cps-blur');
}

private handleFocus() {
this.emit('cps-focus');
}


/** Simula um _click_ no elemento. */
click() {
this.link.click();
}

/** Coloca o foco no _link_. */
focus(options?: FocusOptions) {
this.link.focus(options);
}

/** Remove o foco do _link_. */
blur() {
this.link.blur();
}

render() {
return html`
<a
part="base"
href=${this.href}
target=${this.target}
@click=${this.handleClick}
@blur=${this.handleBlur}
@focus=${this.handleFocus}
class=${classMap({
link: true,
// Statuses
'link--disabled': this.disabled,
'link--rtl': this.localize.dir() === 'rtl',
// Slots
'link--has-label': this.hasSlotController.test('[default]'),
'link--has-prefix': this.hasSlotController.test('prefix'),
'link--has-suffix': this.hasSlotController.test('suffix'),
// Sizes
'link--stamp': this.size === 'stamp',
'link--caption': this.size === 'caption',
'link--label': this.size === 'label',
'link--body': this.size === 'body',
'link--body-em': this.size === 'body-emphasized',
'link--body-strong': this.size === 'body-strong',
'link--body-large': this.size === 'body-large',
'link--subtitle': this.size === 'subtitle',
'link--title': this.size === 'title',
'link--heading': this.size === 'heading',
'link--display': this.size === 'display'
})}
>
<slot name="prefix" part="prefix" class="link__prefix"></slot>
<slot part="content" class="link__content"></slot>
<slot name="suffix" part="suffix" class="link__suffix"></slot>
</a>
`;
}
}

export { CpsLink };

declare global {
interface HTMLElementTagNameMap {
'cps-link': CpsLink;
}
}
Loading

0 comments on commit f05b4db

Please sign in to comment.