@ -8,3 +8,5 @@ NEXT_PUBLIC_SENTRY_DSN=
|
|||||||
NEXT_PUBLIC_VIEWER_INTERNAL_URL=
|
NEXT_PUBLIC_VIEWER_INTERNAL_URL=
|
||||||
NEXT_PUBLIC_E2E_TEST=
|
NEXT_PUBLIC_E2E_TEST=
|
||||||
NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME=
|
NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME=
|
||||||
|
NEXT_PUBLIC_UNSPLASH_APP_NAME=
|
||||||
|
NEXT_PUBLIC_UNSPLASH_ACCESS_KEY=
|
||||||
|
@ -37,7 +37,9 @@
|
|||||||
"@trpc/next": "10.16.0",
|
"@trpc/next": "10.16.0",
|
||||||
"@trpc/react-query": "10.16.0",
|
"@trpc/react-query": "10.16.0",
|
||||||
"@trpc/server": "10.16.0",
|
"@trpc/server": "10.16.0",
|
||||||
|
"@typebot.io/emails": "workspace:*",
|
||||||
"@typebot.io/js": "workspace:*",
|
"@typebot.io/js": "workspace:*",
|
||||||
|
"@typebot.io/next-international": "0.3.8",
|
||||||
"@typebot.io/react": "workspace:*",
|
"@typebot.io/react": "workspace:*",
|
||||||
"@udecode/plate-basic-marks": "20.4.0",
|
"@udecode/plate-basic-marks": "20.4.0",
|
||||||
"@udecode/plate-common": "^20.4.0",
|
"@udecode/plate-common": "^20.4.0",
|
||||||
@ -57,7 +59,6 @@
|
|||||||
"codemirror": "6.0.1",
|
"codemirror": "6.0.1",
|
||||||
"deep-object-diff": "1.1.9",
|
"deep-object-diff": "1.1.9",
|
||||||
"dequal": "2.0.3",
|
"dequal": "2.0.3",
|
||||||
"@typebot.io/emails": "workspace:*",
|
|
||||||
"emojilib": "3.0.8",
|
"emojilib": "3.0.8",
|
||||||
"focus-visible": "5.2.0",
|
"focus-visible": "5.2.0",
|
||||||
"framer-motion": "10.3.0",
|
"framer-motion": "10.3.0",
|
||||||
@ -73,7 +74,6 @@
|
|||||||
"minio": "7.0.32",
|
"minio": "7.0.32",
|
||||||
"next": "13.2.4",
|
"next": "13.2.4",
|
||||||
"next-auth": "4.19.2",
|
"next-auth": "4.19.2",
|
||||||
"@typebot.io/next-international": "0.3.8",
|
|
||||||
"nextjs-cors": "^2.1.2",
|
"nextjs-cors": "^2.1.2",
|
||||||
"nodemailer": "6.9.1",
|
"nodemailer": "6.9.1",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
@ -93,12 +93,17 @@
|
|||||||
"swr": "2.1.0",
|
"swr": "2.1.0",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"trpc-openapi": "1.1.2",
|
"trpc-openapi": "1.1.2",
|
||||||
|
"unsplash-js": "^7.0.15",
|
||||||
"use-debounce": "9.0.3"
|
"use-debounce": "9.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.21.0",
|
"@babel/core": "7.21.0",
|
||||||
"@chakra-ui/styled-system": "2.6.1",
|
"@chakra-ui/styled-system": "2.6.1",
|
||||||
"@playwright/test": "1.31.2",
|
"@playwright/test": "1.31.2",
|
||||||
|
"@typebot.io/lib": "workspace:*",
|
||||||
|
"@typebot.io/prisma": "workspace:*",
|
||||||
|
"@typebot.io/schemas": "workspace:*",
|
||||||
|
"@typebot.io/tsconfig": "workspace:*",
|
||||||
"@types/canvas-confetti": "1.6.0",
|
"@types/canvas-confetti": "1.6.0",
|
||||||
"@types/google-spreadsheet": "3.3.1",
|
"@types/google-spreadsheet": "3.3.1",
|
||||||
"@types/jsonwebtoken": "9.0.1",
|
"@types/jsonwebtoken": "9.0.1",
|
||||||
@ -112,15 +117,11 @@
|
|||||||
"@types/qs": "6.9.7",
|
"@types/qs": "6.9.7",
|
||||||
"@types/react": "18.0.28",
|
"@types/react": "18.0.28",
|
||||||
"@types/tinycolor2": "1.4.3",
|
"@types/tinycolor2": "1.4.3",
|
||||||
"@typebot.io/prisma": "workspace:*",
|
|
||||||
"dotenv": "16.0.3",
|
"dotenv": "16.0.3",
|
||||||
"eslint": "8.36.0",
|
"eslint": "8.36.0",
|
||||||
"eslint-config-custom": "workspace:*",
|
"eslint-config-custom": "workspace:*",
|
||||||
"@typebot.io/schemas": "workspace:*",
|
|
||||||
"superjson": "^1.12.2",
|
"superjson": "^1.12.2",
|
||||||
"@typebot.io/tsconfig": "workspace:*",
|
|
||||||
"typescript": "4.9.5",
|
"typescript": "4.9.5",
|
||||||
"@typebot.io/lib": "workspace:*",
|
|
||||||
"zod": "3.21.4"
|
"zod": "3.21.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,7 @@ export const EditableEmojiOrImageIcon = ({
|
|||||||
defaultUrl={icon ?? ''}
|
defaultUrl={icon ?? ''}
|
||||||
onSubmit={onChangeIcon}
|
onSubmit={onChangeIcon}
|
||||||
isGiphyEnabled={false}
|
isGiphyEnabled={false}
|
||||||
|
isUnsplashEnabled={false}
|
||||||
isEmojiEnabled={true}
|
isEmojiEnabled={true}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Flex, Stack, Text } from '@chakra-ui/react'
|
import { Flex, Stack, Text } from '@chakra-ui/react'
|
||||||
import { GiphyFetch } from '@giphy/js-fetch-api'
|
import { GiphyFetch } from '@giphy/js-fetch-api'
|
||||||
import { Grid } from '@giphy/react-components'
|
import { Grid } from '@giphy/react-components'
|
||||||
import { GiphyLogo } from './GiphyLogo'
|
import { GiphyLogo } from '../logos/GiphyLogo'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { env, isEmpty } from '@typebot.io/lib'
|
import { env, isEmpty } from '@typebot.io/lib'
|
||||||
import { TextInput } from '../inputs'
|
import { TextInput } from '../inputs'
|
||||||
@ -12,7 +12,7 @@ type GiphySearchFormProps = {
|
|||||||
|
|
||||||
const giphyFetch = new GiphyFetch(env('GIPHY_API_KEY') as string)
|
const giphyFetch = new GiphyFetch(env('GIPHY_API_KEY') as string)
|
||||||
|
|
||||||
export const GiphySearchForm = ({ onSubmit }: GiphySearchFormProps) => {
|
export const GiphyPicker = ({ onSubmit }: GiphySearchFormProps) => {
|
||||||
const [inputValue, setInputValue] = useState('')
|
const [inputValue, setInputValue] = useState('')
|
||||||
|
|
||||||
const fetchGifs = (offset: number) =>
|
const fetchGifs = (offset: number) =>
|
||||||
@ -24,7 +24,7 @@ export const GiphySearchForm = ({ onSubmit }: GiphySearchFormProps) => {
|
|||||||
return isEmpty(env('GIPHY_API_KEY')) ? (
|
return isEmpty(env('GIPHY_API_KEY')) ? (
|
||||||
<Text>NEXT_PUBLIC_GIPHY_API_KEY is missing in environment</Text>
|
<Text>NEXT_PUBLIC_GIPHY_API_KEY is missing in environment</Text>
|
||||||
) : (
|
) : (
|
||||||
<Stack>
|
<Stack spacing={4} pt="2">
|
||||||
<Flex align="center">
|
<Flex align="center">
|
||||||
<TextInput
|
<TextInput
|
||||||
autoFocus
|
autoFocus
|
@ -1,9 +1,12 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { Button, Flex, HStack, Stack } from '@chakra-ui/react'
|
import { Button, Flex, HStack, Stack } from '@chakra-ui/react'
|
||||||
import { UploadButton } from './UploadButton'
|
import { UploadButton } from './UploadButton'
|
||||||
import { GiphySearchForm } from './GiphySearchForm'
|
import { GiphyPicker } from './GiphyPicker'
|
||||||
import { TextInput } from '../inputs/TextInput'
|
import { TextInput } from '../inputs/TextInput'
|
||||||
import { EmojiSearchableList } from './emoji/EmojiSearchableList'
|
import { EmojiSearchableList } from './emoji/EmojiSearchableList'
|
||||||
|
import { UnsplashPicker } from './UnsplashPicker'
|
||||||
|
|
||||||
|
type Tabs = 'link' | 'upload' | 'giphy' | 'emoji' | 'unsplash'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
filePath: string
|
filePath: string
|
||||||
@ -11,6 +14,8 @@ type Props = {
|
|||||||
defaultUrl?: string
|
defaultUrl?: string
|
||||||
isEmojiEnabled?: boolean
|
isEmojiEnabled?: boolean
|
||||||
isGiphyEnabled?: boolean
|
isGiphyEnabled?: boolean
|
||||||
|
isUnsplashEnabled?: boolean
|
||||||
|
imageSize?: 'small' | 'regular' | 'thumb'
|
||||||
onSubmit: (url: string) => void
|
onSubmit: (url: string) => void
|
||||||
onClose?: () => void
|
onClose?: () => void
|
||||||
}
|
}
|
||||||
@ -22,11 +27,13 @@ export const ImageUploadContent = ({
|
|||||||
onSubmit,
|
onSubmit,
|
||||||
isEmojiEnabled = false,
|
isEmojiEnabled = false,
|
||||||
isGiphyEnabled = true,
|
isGiphyEnabled = true,
|
||||||
|
isUnsplashEnabled = true,
|
||||||
|
imageSize = 'regular',
|
||||||
onClose,
|
onClose,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [currentTab, setCurrentTab] = useState<
|
const [currentTab, setCurrentTab] = useState<Tabs>(
|
||||||
'link' | 'upload' | 'giphy' | 'emoji'
|
isEmojiEnabled ? 'emoji' : 'upload'
|
||||||
>(isEmojiEnabled ? 'emoji' : 'link')
|
)
|
||||||
|
|
||||||
const handleSubmit = (url: string) => {
|
const handleSubmit = (url: string) => {
|
||||||
onSubmit(url)
|
onSubmit(url)
|
||||||
@ -68,12 +75,22 @@ export const ImageUploadContent = ({
|
|||||||
Giphy
|
Giphy
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
{isUnsplashEnabled && (
|
||||||
|
<Button
|
||||||
|
variant={currentTab === 'unsplash' ? 'solid' : 'ghost'}
|
||||||
|
onClick={() => setCurrentTab('unsplash')}
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
Unsplash
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|
||||||
<BodyContent
|
<BodyContent
|
||||||
filePath={filePath}
|
filePath={filePath}
|
||||||
includeFileName={includeFileName}
|
includeFileName={includeFileName}
|
||||||
tab={currentTab}
|
tab={currentTab}
|
||||||
|
imageSize={imageSize}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
defaultUrl={defaultUrl}
|
defaultUrl={defaultUrl}
|
||||||
/>
|
/>
|
||||||
@ -86,12 +103,14 @@ const BodyContent = ({
|
|||||||
filePath,
|
filePath,
|
||||||
tab,
|
tab,
|
||||||
defaultUrl,
|
defaultUrl,
|
||||||
|
imageSize,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
}: {
|
}: {
|
||||||
includeFileName?: boolean
|
includeFileName?: boolean
|
||||||
filePath: string
|
filePath: string
|
||||||
tab: 'upload' | 'link' | 'giphy' | 'emoji'
|
tab: Tabs
|
||||||
defaultUrl?: string
|
defaultUrl?: string
|
||||||
|
imageSize: 'small' | 'regular' | 'thumb'
|
||||||
onSubmit: (url: string) => void
|
onSubmit: (url: string) => void
|
||||||
}) => {
|
}) => {
|
||||||
switch (tab) {
|
switch (tab) {
|
||||||
@ -109,6 +128,8 @@ const BodyContent = ({
|
|||||||
return <GiphyContent onNewUrl={onSubmit} />
|
return <GiphyContent onNewUrl={onSubmit} />
|
||||||
case 'emoji':
|
case 'emoji':
|
||||||
return <EmojiSearchableList onEmojiSelected={onSubmit} />
|
return <EmojiSearchableList onEmojiSelected={onSubmit} />
|
||||||
|
case 'unsplash':
|
||||||
|
return <UnsplashPicker imageSize={imageSize} onImageSelect={onSubmit} />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,5 +167,5 @@ const EmbedLinkContent = ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
const GiphyContent = ({ onNewUrl }: ContentProps) => (
|
const GiphyContent = ({ onNewUrl }: ContentProps) => (
|
||||||
<GiphySearchForm onSubmit={onNewUrl} />
|
<GiphyPicker onSubmit={onNewUrl} />
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,219 @@
|
|||||||
|
/* eslint-disable @next/next/no-img-element */
|
||||||
|
import {
|
||||||
|
Alert,
|
||||||
|
AlertIcon,
|
||||||
|
Flex,
|
||||||
|
Grid,
|
||||||
|
GridItem,
|
||||||
|
HStack,
|
||||||
|
Image,
|
||||||
|
Link,
|
||||||
|
Spinner,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
} from '@chakra-ui/react'
|
||||||
|
import { env, isDefined, isEmpty } from '@typebot.io/lib'
|
||||||
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
|
import { createApi } from 'unsplash-js'
|
||||||
|
import { Basic as UnsplashImage } from 'unsplash-js/dist/methods/photos/types'
|
||||||
|
import { TextInput } from '../inputs'
|
||||||
|
import { UnsplashLogo } from '../logos/UnsplashLogo'
|
||||||
|
import { TextLink } from '../TextLink'
|
||||||
|
|
||||||
|
const api = createApi({
|
||||||
|
accessKey: env('UNSPLASH_ACCESS_KEY') ?? '',
|
||||||
|
})
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
imageSize: 'regular' | 'small' | 'thumb'
|
||||||
|
onImageSelect: (imageUrl: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UnsplashPicker = ({ imageSize, onImageSelect }: Props) => {
|
||||||
|
const [isFetching, setIsFetching] = useState(false)
|
||||||
|
const [images, setImages] = useState<UnsplashImage[]>([])
|
||||||
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
|
const scrollContainer = useRef<HTMLDivElement>(null)
|
||||||
|
const bottomAnchor = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
const [nextPage, setNextPage] = useState(0)
|
||||||
|
|
||||||
|
const fetchNewImages = useCallback(async (query: string, page: number) => {
|
||||||
|
console.log('Fetch images', query, page)
|
||||||
|
if (query === '') return searchRandomImages()
|
||||||
|
if (query.length <= 2) return
|
||||||
|
setError(null)
|
||||||
|
setIsFetching(true)
|
||||||
|
try {
|
||||||
|
const result = await api.search.getPhotos({
|
||||||
|
query,
|
||||||
|
perPage: 30,
|
||||||
|
orientation: 'landscape',
|
||||||
|
page,
|
||||||
|
})
|
||||||
|
if (result.errors) setError(result.errors[0])
|
||||||
|
if (isDefined(result.response)) {
|
||||||
|
if (page === 0) setImages(result.response.results)
|
||||||
|
else
|
||||||
|
setImages((images) => [
|
||||||
|
...images,
|
||||||
|
...(result.response?.results ?? []),
|
||||||
|
])
|
||||||
|
setNextPage((page) => page + 1)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (err && typeof err === 'object' && 'message' in err)
|
||||||
|
setError(err.message as string)
|
||||||
|
setError('Something went wrong')
|
||||||
|
}
|
||||||
|
setIsFetching(false)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!bottomAnchor.current) return
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
(entities: IntersectionObserverEntry[]) => {
|
||||||
|
console.log('Intersection observer', entities)
|
||||||
|
const target = entities[0]
|
||||||
|
if (target.isIntersecting) fetchNewImages(searchQuery, nextPage + 1)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
root: scrollContainer.current,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (bottomAnchor.current && nextPage > 0)
|
||||||
|
observer.observe(bottomAnchor.current)
|
||||||
|
return () => {
|
||||||
|
observer.disconnect()
|
||||||
|
}
|
||||||
|
}, [fetchNewImages, nextPage, searchQuery])
|
||||||
|
|
||||||
|
const searchRandomImages = async () => {
|
||||||
|
setError(null)
|
||||||
|
setIsFetching(true)
|
||||||
|
try {
|
||||||
|
const result = await api.photos.getRandom({
|
||||||
|
count: 30,
|
||||||
|
orientation: 'landscape',
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.errors) setError(result.errors[0])
|
||||||
|
if (isDefined(result.response))
|
||||||
|
setImages(
|
||||||
|
Array.isArray(result.response) ? result.response : [result.response]
|
||||||
|
)
|
||||||
|
} catch (err) {
|
||||||
|
if (err && typeof err === 'object' && 'message' in err)
|
||||||
|
setError(err.message as string)
|
||||||
|
setError('Something went wrong')
|
||||||
|
}
|
||||||
|
setIsFetching(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectImage = (image: UnsplashImage) => {
|
||||||
|
const url = image.urls[imageSize]
|
||||||
|
api.photos.trackDownload({
|
||||||
|
downloadLocation: image.links.download_location,
|
||||||
|
})
|
||||||
|
if (isDefined(url)) onImageSelect(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
searchRandomImages()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (isEmpty(env('UNSPLASH_ACCESS_KEY')))
|
||||||
|
return (
|
||||||
|
<Text>NEXT_PUBLIC_UNSPLASH_ACCESS_KEY is missing in environment</Text>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack spacing={4} pt="2">
|
||||||
|
<HStack align="center">
|
||||||
|
<TextInput
|
||||||
|
autoFocus
|
||||||
|
placeholder="Search..."
|
||||||
|
onChange={(query) => {
|
||||||
|
setSearchQuery(query)
|
||||||
|
fetchNewImages(query, 0)
|
||||||
|
}}
|
||||||
|
withVariableButton={false}
|
||||||
|
/>
|
||||||
|
<Link
|
||||||
|
isExternal
|
||||||
|
href={`https://unsplash.com/?utm_source=${env(
|
||||||
|
'UNSPLASH_APP_NAME'
|
||||||
|
)}&utm_medium=referral`}
|
||||||
|
>
|
||||||
|
<UnsplashLogo width="80px" />
|
||||||
|
</Link>
|
||||||
|
</HStack>
|
||||||
|
{isDefined(error) && (
|
||||||
|
<Alert status="error">
|
||||||
|
<AlertIcon />
|
||||||
|
{error}
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
<Stack overflowY="scroll" maxH="400px" ref={scrollContainer}>
|
||||||
|
{images.length > 0 && (
|
||||||
|
<Grid templateColumns="repeat(4, 1fr)" columnGap={2} rowGap={3}>
|
||||||
|
{images.map((image, index) => (
|
||||||
|
<GridItem
|
||||||
|
as={Stack}
|
||||||
|
key={image.id}
|
||||||
|
boxSize="100%"
|
||||||
|
spacing="0"
|
||||||
|
ref={index === images.length - 1 ? bottomAnchor : undefined}
|
||||||
|
>
|
||||||
|
<UnsplashImage
|
||||||
|
image={image}
|
||||||
|
onClick={() => selectImage(image)}
|
||||||
|
/>
|
||||||
|
</GridItem>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
{isFetching && (
|
||||||
|
<Flex justifyContent="center" py="4">
|
||||||
|
<Spinner />
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnsplashImageProps = {
|
||||||
|
image: UnsplashImage
|
||||||
|
onClick: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const UnsplashImage = ({ image, onClick }: UnsplashImageProps) => {
|
||||||
|
const { user, urls, alt_description } = image
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Image
|
||||||
|
objectFit="cover"
|
||||||
|
src={urls.thumb}
|
||||||
|
alt={alt_description ?? 'Unsplash image'}
|
||||||
|
onClick={onClick}
|
||||||
|
rounded="md"
|
||||||
|
h="100%"
|
||||||
|
cursor="pointer"
|
||||||
|
/>
|
||||||
|
<TextLink
|
||||||
|
fontSize="xs"
|
||||||
|
isExternal
|
||||||
|
href={`https://unsplash.com/@${user.username}?utm_source=${env(
|
||||||
|
'UNSPLASH_APP_NAME'
|
||||||
|
)}&utm_medium=referral`}
|
||||||
|
noOfLines={1}
|
||||||
|
color="gray.500"
|
||||||
|
>
|
||||||
|
{user.name}
|
||||||
|
</TextLink>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -13,6 +13,7 @@ export const TextLink = ({
|
|||||||
scroll,
|
scroll,
|
||||||
prefetch,
|
prefetch,
|
||||||
isExternal,
|
isExternal,
|
||||||
|
noOfLines,
|
||||||
...textProps
|
...textProps
|
||||||
}: TextLinkProps) => (
|
}: TextLinkProps) => (
|
||||||
<Link
|
<Link
|
||||||
@ -26,7 +27,9 @@ export const TextLink = ({
|
|||||||
<chakra.span textDecor="underline" display="inline-block" {...textProps}>
|
<chakra.span textDecor="underline" display="inline-block" {...textProps}>
|
||||||
{isExternal ? (
|
{isExternal ? (
|
||||||
<HStack spacing={1}>
|
<HStack spacing={1}>
|
||||||
<chakra.span>{children}</chakra.span>
|
<chakra.span noOfLines={noOfLines} maxW="100%">
|
||||||
|
{children}
|
||||||
|
</chakra.span>
|
||||||
<ExternalLinkIcon />
|
<ExternalLinkIcon />
|
||||||
</HStack>
|
</HStack>
|
||||||
) : (
|
) : (
|
||||||
|
7
apps/builder/src/components/logos/UnsplashLogo.tsx
Normal file
7
apps/builder/src/components/logos/UnsplashLogo.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { IconProps, Icon } from '@chakra-ui/react'
|
||||||
|
|
||||||
|
export const UnsplashLogo = (props: IconProps) => (
|
||||||
|
<Icon viewBox="0 0 457.19 104.19" {...props}>
|
||||||
|
<path d="M59.88 38.66h27.53v48.75H0V38.66h27.53v24.37h32.35zm93 25c0 8.25-5.45 13.13-12.9 13.13-7.28 0-12.81-4.88-12.81-13.13V24.41h-12.22v39.13c0 15.45 11 25.21 25.06 25.21s25.15-9.76 25.15-25.21V24.41h-12.25zm43.7-21.13c-4.7 0-9.94 2-12.6 6.57v-5.41h-11.45v43.64h11.81v-25.1c0-5 3-9 8.16-9 5.68 0 8.08 3.82 8.08 8.7v25.4h11.8V59.82c.03-9.59-4.94-17.31-15.77-17.31zm43.31 18.37l-6.48-1.33c-2.47-.5-4-1.77-4-3.9 0-2.49 2.23-4.35 5.33-4.35 4.36 0 6.09 2.25 6.51 4.88h10.18c-.08-6-4.83-13.84-16.51-13.84-9.41 0-16.33 6.47-16.33 14.28 0 6.13 3.81 11.19 12.24 13l6.05 1.33c3.37.71 4.7 2.31 4.7 4.26 0 2.31-2.14 4.35-6 4.35-4.71 0-7.27-2.68-7.87-5.79h-10.5c.59 6.53 5.32 14.84 18.46 14.84 11.45 0 17.22-7.28 17.22-14.38-.01-6.36-4.36-11.59-12.97-13.37zm63.19 4.53c0 13.22-8.26 23-20.59 23-6 0-10.48-2.4-12.61-5.33v21.13h-11.8V43.67h11.45v5.41c2-3.37 6.83-6.39 13.4-6.39 12.81 0 20.18 9.76 20.18 22.72zm-11.63.09c0-7.72-4.79-12.25-10.83-12.25s-10.91 4.53-10.91 12.25 4.88 12.33 10.91 12.33 10.91-4.54 10.91-12.35zm68-21.83h11.45v43.64h-11.8v-5.31c-2 3.5-6.57 6.38-12.61 6.38-12.33 0-20.59-9.77-20.59-23 0-13 7.37-22.72 20.15-22.72 6.57 0 11.32 3.05 13.4 6.39zm-.18 21.83c0-7.72-4.88-12.25-10.91-12.25s-10.83 4.51-10.83 12.23 4.79 12.33 10.83 12.33 10.92-4.6 10.92-12.33zm-50.66 21.81h11.8V24.41h-11.8zm132.35-44.81c-4.17 0-9 1.41-11.81 4.78V24.41h-11.8v62.91h11.8V61.68c.27-4.8 3.2-8.52 8.17-8.52 5.68 0 8.08 3.83 8.07 8.71v25.47h11.81V59.82c-.01-9.59-5.15-17.3-16.24-17.3zm-42 18.36l-6.43-1.33c-2.47-.5-4-1.77-4-3.9 0-2.49 2.22-4.35 5.33-4.35 4.35 0 6.08 2.25 6.5 4.88h10.17c-.08-6-4.83-13.84-16.51-13.84-9.41 0-16.33 6.47-16.33 14.28 0 6.13 3.82 11.19 12.25 13l6 1.33c3.37.71 4.7 2.31 4.7 4.26 0 2.31-2.14 4.35-6 4.35-4.71 0-7.27-2.68-7.87-5.79h-10.49c.58 6.53 5.31 14.84 18.45 14.84 11.45 0 17.22-7.28 17.22-14.38 0-6.34-4.35-11.57-12.95-13.35zM59.88 0H27.53v24.37h32.35z" />
|
||||||
|
</Icon>
|
||||||
|
)
|
@ -65,6 +65,7 @@ export const MetadataForm = ({
|
|||||||
defaultUrl={metadata.favIconUrl ?? ''}
|
defaultUrl={metadata.favIconUrl ?? ''}
|
||||||
onSubmit={handleFavIconSubmit}
|
onSubmit={handleFavIconSubmit}
|
||||||
isGiphyEnabled={false}
|
isGiphyEnabled={false}
|
||||||
|
imageSize="thumb"
|
||||||
/>
|
/>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
@ -91,6 +91,7 @@ export const AvatarForm = ({
|
|||||||
<ImageUploadContent
|
<ImageUploadContent
|
||||||
filePath={uploadFilePath}
|
filePath={uploadFilePath}
|
||||||
defaultUrl={avatarProps?.url}
|
defaultUrl={avatarProps?.url}
|
||||||
|
imageSize="thumb"
|
||||||
onSubmit={handleImageUrl}
|
onSubmit={handleImageUrl}
|
||||||
/>
|
/>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
|
@ -180,6 +180,15 @@ Used to search for GIF. You can create a Giphy app [here](https://developers.gip
|
|||||||
| ------------------------- | ------- | ------------- |
|
| ------------------------- | ------- | ------------- |
|
||||||
| NEXT_PUBLIC_GIPHY_API_KEY | | Giphy API key |
|
| NEXT_PUBLIC_GIPHY_API_KEY | | Giphy API key |
|
||||||
|
|
||||||
|
## Unsplash (image picker)
|
||||||
|
|
||||||
|
Used to search for images. You can create a Giphy app [here](https://unsplash.com/developers)
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| ------------------------------- | ------- | ----------------- |
|
||||||
|
| NEXT_PUBLIC_UNSPLASH_APP_NAME | | Unsplash App name |
|
||||||
|
| NEXT_PUBLIC_UNSPLASH_ACCESS_KEY | | Unsplash API key |
|
||||||
|
|
||||||
## Others
|
## Others
|
||||||
|
|
||||||
<details><summary><h3>Show</h3></summary>
|
<details><summary><h3>Show</h3></summary>
|
||||||
|
14
pnpm-lock.yaml
generated
14
pnpm-lock.yaml
generated
@ -121,6 +121,7 @@ importers:
|
|||||||
tinycolor2: 1.6.0
|
tinycolor2: 1.6.0
|
||||||
trpc-openapi: 1.1.2
|
trpc-openapi: 1.1.2
|
||||||
typescript: 4.9.5
|
typescript: 4.9.5
|
||||||
|
unsplash-js: ^7.0.15
|
||||||
use-debounce: 9.0.3
|
use-debounce: 9.0.3
|
||||||
zod: 3.21.4
|
zod: 3.21.4
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -204,6 +205,7 @@ importers:
|
|||||||
swr: 2.1.0_react@18.2.0
|
swr: 2.1.0_react@18.2.0
|
||||||
tinycolor2: 1.6.0
|
tinycolor2: 1.6.0
|
||||||
trpc-openapi: 1.1.2_k6tpxpkoeqrvdlcqns422rglmm
|
trpc-openapi: 1.1.2_k6tpxpkoeqrvdlcqns422rglmm
|
||||||
|
unsplash-js: 7.0.15
|
||||||
use-debounce: 9.0.3_react@18.2.0
|
use-debounce: 9.0.3_react@18.2.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@babel/core': 7.21.0
|
'@babel/core': 7.21.0
|
||||||
@ -6829,6 +6831,10 @@ packages:
|
|||||||
'@types/node': 18.15.3
|
'@types/node': 18.15.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/content-type/1.1.5:
|
||||||
|
resolution: {integrity: sha512-dgMN+syt1xb7Hk8LU6AODOfPlvz5z1CbXpPuJE5ZrX9STfBOIXF09pEB8N7a97WT9dbngt3ksDCm6GW6yMrxfQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/cors/2.8.13:
|
/@types/cors/2.8.13:
|
||||||
resolution: {integrity: sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==}
|
resolution: {integrity: sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -19688,6 +19694,14 @@ packages:
|
|||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/unsplash-js/7.0.15:
|
||||||
|
resolution: {integrity: sha512-WGqKp9wl2m2tAUPyw2eMZs/KICR+A52tCaRapzVXWxkA4pjHqsaGwiJXTEW7hBy4Pu0QmP6KxTt2jST3tluawA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
'@types/content-type': 1.1.5
|
||||||
|
content-type: 1.0.5
|
||||||
|
dev: false
|
||||||
|
|
||||||
/untildify/4.0.0:
|
/untildify/4.0.0:
|
||||||
resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
|
resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
Reference in New Issue
Block a user