Skip to content

Commit

Permalink
feat: add component buttons (#110)
Browse files Browse the repository at this point in the history
  • Loading branch information
Snazzah committed Aug 18, 2021
1 parent c4ff3fb commit 50f78c5
Show file tree
Hide file tree
Showing 11 changed files with 300 additions and 0 deletions.
66 changes: 66 additions & 0 deletions packages/core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { DiscordTimestamp } from "./util";
export namespace Components {
interface DiscordActionRow {
}
interface DiscordAttachment {
/**
* The alt text to show in case the image was unable to load
Expand All @@ -29,6 +31,28 @@ export namespace Components {
}
interface DiscordAttachments {
}
interface DiscordButton {
/**
* Whether to show the button as disabled.
*/
"disabled": boolean;
/**
* The emoji URL to use in the button.
*/
"emoji": string;
/**
* The name of the emoji used in the button.
*/
"emojiName": string;
/**
* The type of button this is, this will change the color of the button. Valid values: `primary`, `secondary`, `success`, `destructive`.
*/
"type": 'primary' | 'secondary' | 'success' | 'destructive';
/**
* The URL for the button. Setting this will force the button type to be `secondary`.
*/
"url": string;
}
interface DiscordCommand {
/**
* The message author's username.
Expand Down Expand Up @@ -326,6 +350,12 @@ export namespace Components {
}
}
declare global {
interface HTMLDiscordActionRowElement extends Components.DiscordActionRow, HTMLStencilElement {
}
var HTMLDiscordActionRowElement: {
prototype: HTMLDiscordActionRowElement;
new (): HTMLDiscordActionRowElement;
};
interface HTMLDiscordAttachmentElement extends Components.DiscordAttachment, HTMLStencilElement {
}
var HTMLDiscordAttachmentElement: {
Expand All @@ -338,6 +368,12 @@ declare global {
prototype: HTMLDiscordAttachmentsElement;
new (): HTMLDiscordAttachmentsElement;
};
interface HTMLDiscordButtonElement extends Components.DiscordButton, HTMLStencilElement {
}
var HTMLDiscordButtonElement: {
prototype: HTMLDiscordButtonElement;
new (): HTMLDiscordButtonElement;
};
interface HTMLDiscordCommandElement extends Components.DiscordCommand, HTMLStencilElement {
}
var HTMLDiscordCommandElement: {
Expand Down Expand Up @@ -411,8 +447,10 @@ declare global {
new (): HTMLDiscordSystemMessageElement;
};
interface HTMLElementTagNameMap {
"discord-action-row": HTMLDiscordActionRowElement;
"discord-attachment": HTMLDiscordAttachmentElement;
"discord-attachments": HTMLDiscordAttachmentsElement;
"discord-button": HTMLDiscordButtonElement;
"discord-command": HTMLDiscordCommandElement;
"discord-embed": HTMLDiscordEmbedElement;
"discord-embed-field": HTMLDiscordEmbedFieldElement;
Expand All @@ -428,6 +466,8 @@ declare global {
}
}
declare namespace LocalJSX {
interface DiscordActionRow {
}
interface DiscordAttachment {
/**
* The alt text to show in case the image was unable to load
Expand All @@ -450,6 +490,28 @@ declare namespace LocalJSX {
}
interface DiscordAttachments {
}
interface DiscordButton {
/**
* Whether to show the button as disabled.
*/
"disabled"?: boolean;
/**
* The emoji URL to use in the button.
*/
"emoji"?: string;
/**
* The name of the emoji used in the button.
*/
"emojiName"?: string;
/**
* The type of button this is, this will change the color of the button. Valid values: `primary`, `secondary`, `success`, `destructive`.
*/
"type"?: 'primary' | 'secondary' | 'success' | 'destructive';
/**
* The URL for the button. Setting this will force the button type to be `secondary`.
*/
"url"?: string;
}
interface DiscordCommand {
/**
* The message author's username.
Expand Down Expand Up @@ -746,8 +808,10 @@ declare namespace LocalJSX {
"type"?: 'join' | 'leave' | 'call' | 'missed-call' | 'boost' | 'edit' | 'thread' | 'alert' | 'error';
}
interface IntrinsicElements {
"discord-action-row": DiscordActionRow;
"discord-attachment": DiscordAttachment;
"discord-attachments": DiscordAttachments;
"discord-button": DiscordButton;
"discord-command": DiscordCommand;
"discord-embed": DiscordEmbed;
"discord-embed-field": DiscordEmbedField;
Expand All @@ -766,8 +830,10 @@ export { LocalJSX as JSX };
declare module "@stencil/core" {
export namespace JSX {
interface IntrinsicElements {
"discord-action-row": LocalJSX.DiscordActionRow & JSXBase.HTMLAttributes<HTMLDiscordActionRowElement>;
"discord-attachment": LocalJSX.DiscordAttachment & JSXBase.HTMLAttributes<HTMLDiscordAttachmentElement>;
"discord-attachments": LocalJSX.DiscordAttachments & JSXBase.HTMLAttributes<HTMLDiscordAttachmentsElement>;
"discord-button": LocalJSX.DiscordButton & JSXBase.HTMLAttributes<HTMLDiscordButtonElement>;
"discord-command": LocalJSX.DiscordCommand & JSXBase.HTMLAttributes<HTMLDiscordCommandElement>;
"discord-embed": LocalJSX.DiscordEmbed & JSXBase.HTMLAttributes<HTMLDiscordEmbedElement>;
"discord-embed-field": LocalJSX.DiscordEmbedField & JSXBase.HTMLAttributes<HTMLDiscordEmbedFieldElement>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.discord-action-row {
display: flex;
flex-wrap: nowrap;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Component, ComponentInterface, h, Host } from '@stencil/core';

@Component({
tag: 'discord-action-row',
styleUrl: 'discord-action-row.css'
})
export class DiscordActionRow implements ComponentInterface {
public render() {
return (
<Host class="discord-action-row">
<slot></slot>
</Host>
);
}
}
7 changes: 7 additions & 0 deletions packages/core/src/components/discord-action-row/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# discord-action-row

<!-- Auto Generated Below -->

---

_Built with [StencilJS](https://stenciljs.com/)_
72 changes: 72 additions & 0 deletions packages/core/src/components/discord-button/discord-button.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
.discord-button {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
margin: 4px 8px 4px 0;
padding: 2px 16px;
width: auto;
height: 32px;
min-width: 60px;
min-height: 32px;
-webkit-transition: background-color 0.17s ease, color 0.17s ease;
transition: background-color 0.17s ease, color 0.17s ease;
border-radius: 3px;
font-size: 14px;
font-weight: 500;
line-height: 16px;
text-decoration: none !important;
}

.discord-button.discord-button-success {
color: #fff;
background-color: #3ba55d;
}

.discord-button.discord-button-success.discord-button-hoverable:hover {
background-color: #2d7d46;
}

.discord-button.discord-button-destructive {
color: #fff;
background-color: #ed4245;
}

.discord-button.discord-button-destructive.discord-button-hoverable:hover {
background-color: #c03537;
}

.discord-button.discord-button-primary {
color: #fff;
background-color: #5865f2;
}

.discord-button.discord-button-primary.discord-button-hoverable:hover {
background-color: #4752c4;
}

.discord-button.discord-button-secondary {
color: #fff;
background-color: #4f545c;
}

.discord-button.discord-button-secondary.discord-button-hoverable:hover {
background-color: #5d6269;
}

.discord-button.discord-button-disabled {
cursor: not-allowed;
opacity: 0.5;
}

.discord-button .discord-button-launch {
margin-left: 8px;
}

.discord-button .discord-button-emoji {
margin-right: 4px;
object-fit: contain;
width: 1.375em;
height: 1.375em;
vertical-align: bottom;
}
81 changes: 81 additions & 0 deletions packages/core/src/components/discord-button/discord-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Component, ComponentInterface, Element, h, Host, Prop, Watch } from '@stencil/core';
import Fragment from '../../Fragment';
import LaunchIcon from '../svgs/launch-icon';

@Component({
tag: 'discord-button',
styleUrl: 'discord-button.css'
})
export class DiscordButton implements ComponentInterface {
/**
* The DiscordButton element.
*/
@Element()
public el: HTMLElement;

/**
* The emoji URL to use in the button.
*/
@Prop()
public emoji: string;

/**
* The name of the emoji used in the button.
*/
@Prop()
public emojiName = 'emoji';

/**
* The URL for the button. Setting this will force the button type to be `secondary`.
*/
@Prop()
public url: string;

/**
* Whether to show the button as disabled.
*/
@Prop()
public disabled = false;

/**
* The type of button this is, this will change the color of the button.
* Valid values: `primary`, `secondary`, `success`, `destructive`.
*/
@Prop()
public type: 'primary' | 'secondary' | 'success' | 'destructive' = 'secondary';

@Watch('type')
public handleType(value: string) {
if (typeof value !== 'string') {
throw new TypeError('DiscordButton `type` prop must be a string.');
} else if (!['primary', 'secondary', 'success', 'destructive'].includes(value)) {
throw new RangeError("DiscordButton `type` prop must be one of: 'primary', 'secondary', 'success', 'destructive'");
}
}

public render() {
const parent: HTMLDiscordActionRowElement = this.el.parentElement as HTMLDiscordActionRowElement;

if (parent.tagName.toLowerCase() !== 'discord-action-row') {
throw new Error('All <discord-button> components must be direct children of <discord-action-row>.');
}

const content = (
<Fragment>
{this.emoji && <img src={this.emoji} alt={this.emojiName} draggable={false} class="discord-button-emoji" />}
<span>
<slot />
</span>
{this.url && <LaunchIcon class="discord-button-launch" />}
</Fragment>
);

return this.url && !this.disabled ? (
<a class="discord-button discord-button-secondary" href={this.url} target="_blank" rel="noopener noreferrer">
{content}
</a>
) : (
<Host class={`discord-button discord-button-${this.type} discord-button-${this.disabled ? 'disabled' : 'hoverable'}`}>{content}</Host>
);
}
}
17 changes: 17 additions & 0 deletions packages/core/src/components/discord-button/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# discord-button

<!-- Auto Generated Below -->

## Properties

| Property | Attribute | Description | Type | Default |
| ----------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ------------- |
| `disabled` | `disabled` | Whether to show the button as disabled. | `boolean` | `false` |
| `emoji` | `emoji` | The emoji URL to use in the button. | `string` | `undefined` |
| `emojiName` | `emoji-name` | The name of the emoji used in the button. | `string` | `'emoji'` |
| `type` | `type` | The type of button this is, this will change the color of the button. Valid values: `primary`, `secondary`, `success`, `destructive`. | `"destructive" \| "primary" \| "secondary" \| "success"` | `'secondary'` |
| `url` | `url` | The URL for the button. Setting this will force the button type to be `secondary`. | `string` | `undefined` |

---

_Built with [StencilJS](https://stenciljs.com/)_
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export class DiscordMessage implements ComponentInterface {
<div class="discord-message-compact-indent">
<slot name="embeds"></slot>
<slot name="attachments"></slot>
<slot name="components"></slot>
<slot name="reactions"></slot>
</div>
</div>
Expand Down
13 changes: 13 additions & 0 deletions packages/core/src/components/svgs/launch-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { h } from '@stencil/core';

export default function LaunchIcon<T>(props: T) {
return (
<svg {...props} aria-hidden="false" width="16" height="16" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M10 5V3H5.375C4.06519 3 3 4.06519 3 5.375V18.625C3 19.936 4.06519 21 5.375 21H18.625C19.936 21 21 19.936 21 18.625V14H19V19H5V5H10Z"
/>
<path fill="currentColor" d="M21 2.99902H14V4.99902H17.586L9.29297 13.292L10.707 14.706L19 6.41302V9.99902H21V2.99902Z" />
</svg>
);
}
22 changes: 22 additions & 0 deletions packages/core/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,28 @@ <h3 class="title">Commands in Compact Mode</h3>
Took 100ms.
</discord-message>
</discord-messages>
<h3 class="title">Components</h3>
<discord-messages>
<discord-message profile="skyra">
Here are some components!
<discord-attachments slot="components">
<discord-action-row>
<discord-button type="primary">Primary Button</discord-button>
<discord-button type="secondary">Secondary Button</discord-button>
<discord-button type="success">Green Button</discord-button>
<discord-button type="destructive">Red Button</discord-button>
<discord-button url="https://join.skyra.pw" emoji="/static/eyes.svg" emoji-name="👀">Link</discord-button>
</discord-action-row>
<discord-action-row>
<discord-button type="primary" disabled>Primary Button</discord-button>
<discord-button type="secondary" disabled>Secondary Button</discord-button>
<discord-button type="success" disabled>Green Button</discord-button>
<discord-button type="destructive" disabled>Red Button</discord-button>
<discord-button url="https://join.skyra.pw" disabled emoji="/static/eyes.svg" emoji-name="👀">Link</discord-button>
</discord-action-row>
</discord-attachments>
</discord-message>
</discord-messages>
<h3 class="title">Verified Discord bots</h3>
<discord-messages>
<discord-message profile="skyra"> Wow I just got verified! </discord-message>
Expand Down
2 changes: 2 additions & 0 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import type { JSX } from '@skyra/discord-components-core';
import { defineCustomElements } from '@skyra/discord-components-core/loader';

defineCustomElements();
export const DiscordActionRow = /*@__PURE__*/ createReactComponent<JSX.DiscordActionRow, HTMLDiscordActionRowElement>('discord-action-row');
export const DiscordAttachment = /*@__PURE__*/ createReactComponent<JSX.DiscordAttachment, HTMLDiscordAttachmentElement>('discord-attachment');
export const DiscordAttachments = /*@__PURE__*/ createReactComponent<JSX.DiscordAttachments, HTMLDiscordAttachmentsElement>('discord-attachments');
export const DiscordButton = /*@__PURE__*/ createReactComponent<JSX.DiscordButton, HTMLDiscordButtonElement>('discord-button');
export const DiscordCommand = /*@__PURE__*/ createReactComponent<JSX.DiscordCommand, HTMLDiscordCommandElement>('discord-command');
export const DiscordEmbed = /*@__PURE__*/ createReactComponent<JSX.DiscordEmbed, HTMLDiscordEmbedElement>('discord-embed');
export const DiscordEmbedField = /*@__PURE__*/ createReactComponent<JSX.DiscordEmbedField, HTMLDiscordEmbedFieldElement>('discord-embed-field');
Expand Down

0 comments on commit 50f78c5

Please sign in to comment.