feat(pages): add /application/[guildId]/[channelId] (#4)

This commit is contained in:
Divlo
2022-01-01 20:42:25 +01:00
committed by GitHub
parent 91e246b759
commit fdc2a2d1de
118 changed files with 6040 additions and 2094 deletions

View File

@ -0,0 +1,15 @@
import { Meta, Story } from '@storybook/react'
import { Emoji as Component, EmojiProps } from './Emoji'
const Stories: Meta = {
title: 'Emoji',
component: Component
}
export default Stories
export const Emoji: Story<EmojiProps> = (arguments_) => {
return <Component {...arguments_} />
}
Emoji.args = { value: ':wave:', size: 20 }

View File

@ -0,0 +1,24 @@
import { Emoji as EmojiMart } from 'emoji-mart'
import { EMOJI_SET } from './emojiPlugin'
export interface EmojiProps {
value: string
size: number
}
export const Emoji: React.FC<EmojiProps> = (props) => {
const { value, size } = props
return (
<EmojiMart
set={EMOJI_SET}
emoji={value}
size={size}
tooltip
fallback={() => {
return <>{value}</>
}}
/>
)
}

View File

@ -0,0 +1,15 @@
import { Meta, Story } from '@storybook/react'
import { EmojiPicker as Component, EmojiPickerProps } from './EmojiPicker'
const Stories: Meta = {
title: 'EmojiPicker',
component: Component
}
export default Stories
export const EmojiPicker: Story<EmojiPickerProps> = (arguments_) => {
return <Component {...arguments_} />
}
EmojiPicker.args = { onClick: (emoji, event) => console.log(emoji, event) }

View File

@ -0,0 +1,28 @@
import 'emoji-mart/css/emoji-mart.css'
import { EmojiData, Picker } from 'emoji-mart'
import { useTheme } from 'next-themes'
import { EMOJI_SET } from '../emojiPlugin'
export type EmojiPickerOnClick = (
emoji: EmojiData,
event: React.MouseEvent<HTMLElement, MouseEvent>
) => void
export interface EmojiPickerProps {
onClick?: EmojiPickerOnClick
}
export const EmojiPicker: React.FC<EmojiPickerProps> = (props) => {
const { theme } = useTheme()
return (
<Picker
set={EMOJI_SET}
theme={theme as 'light' | 'dark' | 'auto'}
onClick={props.onClick}
showPreview={false}
showSkinTones={false}
/>
)
}

View File

@ -0,0 +1 @@
export * from './EmojiPicker'

View File

@ -0,0 +1,71 @@
import { visit } from 'unist-util-visit'
import { Plugin, Transformer } from 'unified'
import { Literal, Parent } from 'unist'
import type { ElementContent } from 'hast'
import type { EmojiSet } from 'emoji-mart'
import { emojiRegex } from './isStringWithOnlyOneEmoji'
export const EMOJI_SET: EmojiSet = 'twitter'
const extractText = (
string: string,
start: number,
end: number
): ElementContent => {
return {
type: 'text',
value: string.slice(start, end)
}
}
export const emojiPlugin: Plugin<[], Literal<string>> = () => {
const transformer: Transformer<Literal<string>> = (tree) => {
visit<Literal<string>, string>(
tree,
'text',
(node, position, parent: Parent<ElementContent> | null) => {
if (typeof node.value !== 'string') {
return
}
position = position ?? 0
const definition: ElementContent[] = []
let lastIndex = 0
const match = emojiRegex.exec(node.value)
if (match != null) {
const value = match[0]
if (match.index !== lastIndex) {
definition.push(extractText(node.value, lastIndex, match.index))
}
definition.push({
type: 'element',
tagName: 'emoji',
properties: { value },
children: []
})
lastIndex = match.index + value.length
if (lastIndex !== node.value.length) {
definition.push(
extractText(node.value, lastIndex, node.value.length)
)
}
if (parent != null) {
const last = parent.children.slice(position + 1)
parent.children = parent.children.slice(0, position)
parent.children = parent.children.concat(definition)
parent.children = parent.children.concat(last)
}
}
}
)
}
return transformer
}
declare global {
namespace JSX {
interface IntrinsicElements {
emoji: { value: string }
}
}
}

View File

@ -0,0 +1,4 @@
export * from './Emoji'
export * from './EmojiPicker'
export * from './emojiPlugin'
export * from './isStringWithOnlyOneEmoji'

View File

@ -0,0 +1,18 @@
import { isStringWithOnlyOneEmoji } from './isStringWithOnlyOneEmoji'
describe('components/Emoji/isStringWithOnlyOneEmoji', () => {
it('returns true with a string with only one emoji', () => {
expect(isStringWithOnlyOneEmoji(':wave:')).toBeTruthy()
expect(isStringWithOnlyOneEmoji(':smile:')).toBeTruthy()
})
it('returns false with a string with multiple emoji or with text', () => {
expect(isStringWithOnlyOneEmoji(':wave: :smile:')).toBeFalsy()
expect(isStringWithOnlyOneEmoji(':wave: some text')).toBeFalsy()
expect(isStringWithOnlyOneEmoji('some text :wave:')).toBeFalsy()
})
it('returns false with a string without emoji', () => {
expect(isStringWithOnlyOneEmoji('some text')).toBeFalsy()
})
})

View File

@ -0,0 +1,6 @@
export const emojiRegex = /:\+1:|:-1:|:[\w-]+:/
export const isStringWithOnlyOneEmoji = (value: string): boolean => {
const result = emojiRegex.exec(value)
return result != null && result.input === result[0]
}