Created
February 14, 2025 15:25
-
-
Save Salemsky/ec99bf42f3b4912b2b569f55237837d1 to your computer and use it in GitHub Desktop.
sc responsive button
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use client'; | |
import { styled } from '@styled'; | |
import { useRef } from 'react'; | |
import { Button } from './Button.tsx'; | |
const ButtonPrimary = styled(Button)` | |
background: var(--button-primary-background); | |
${Button.selector.label} { | |
color: var(--button-primary-text-color); | |
} | |
@media not all and (pointer: coarse) { | |
&:not([disabled]):hover { | |
background: var(--button-primary-background-hover); | |
} | |
&:not([disabled]):active { | |
background: var(--button-primary-background-active); | |
} | |
} | |
@media (pointer: coarse) { | |
&:not([disabled]):active { | |
background: var(--button-primary-background-active); | |
} | |
} | |
`; | |
const StyledItem = styled.div` | |
display: flex; | |
flex-wrap: wrap; | |
align-items: center; | |
position: relative; | |
margin: 10px 50px 10px 50px; | |
${ButtonPrimary} { | |
margin: 10px 5px 10px 5px; | |
} | |
${ButtonPrimary}:last-child { | |
width: 80px; | |
} | |
${ButtonPrimary} { | |
${ButtonPrimary.selector.icon} { | |
color: purple; | |
} | |
${ButtonPrimary.selector.label} { | |
color: inherit; | |
} | |
} | |
`; | |
const ButtonsShelf = (): Array<import('react').ReactElement> => { | |
const params: Array<Parameters<typeof ButtonPrimary>[0]> = [ | |
{ | |
ref: useRef<HTMLButtonElement>(null), | |
icon: (p) => ( | |
<svg fill="red" {...p}> | |
<path d="m6 7 6 6 6-6 2 2-8 8-8-8z" /> | |
</svg> | |
), | |
onClick(ev: import('react').MouseEvent<HTMLButtonElement>): void { | |
console.log(ev); | |
}, | |
}, | |
{ | |
ref: useRef<HTMLAnchorElement>(null), | |
icon: 'logo-github', | |
label: 'disabled', | |
disabled: true, | |
href: 'http://localhost:8000/', | |
onClick(): void { | |
console.log('c'); | |
}, | |
}, | |
{ | |
icon: 'logo-github', | |
label: 'sm', | |
size: 'sm', | |
}, | |
{ | |
icon: 'logo-github', | |
label: 'md', | |
size: 'md', | |
}, | |
{ | |
icon: 'logo-github', | |
label: 'lg', | |
size: 'lg', | |
}, | |
{ | |
label: 'route', | |
title: 'route', | |
href: '/about', | |
}, | |
{ | |
label: 'external', | |
title: 'external', | |
href: 'http://localhost:4000/', | |
}, | |
{ | |
icon: 'logo-github', | |
label: 'ddddddddddddddddddddddd', | |
ellipsis: true, | |
}, | |
]; | |
return params.map((p, i) => <ButtonPrimary key={i} {...p} />); | |
}; | |
export default function Home(): import('react').ReactElement { | |
return ( | |
<StyledItem> | |
<ButtonsShelf /> | |
</StyledItem> | |
); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { mqw, styled } from '@styled'; | |
import { icons } from './Icons'; | |
const PX_SIZE = { | |
sm: 20, | |
md: 24, | |
lg: 28, | |
}; | |
type SVGComponent = (typeof icons)[keyof typeof icons]; | |
type Props = Readonly<{ | |
aspectRatio?: number; | |
element: SVGComponent | keyof typeof icons | undefined; | |
size?: keyof typeof PX_SIZE; | |
}>; | |
type StyledIconProps = Required<Omit<Props, 'aspectRatio' | 'element'>>; | |
const StyledIcon = styled.div.withConfig({ | |
shouldForwardProp: (p) => !['size'].includes(p), | |
})<StyledIconProps>` | |
display: inline-flex; | |
position: relative; | |
align-items: center; | |
overflow: hidden; | |
user-select: none; | |
pointer-events: none; | |
${({ size }): import('styled-components').RuleSet => { | |
const v = PX_SIZE[size]; | |
return [ | |
mqw({ min: 'mobile' })` | |
width: calc(var(--icon-size-${size}, ${v}px) - var(--icon-mobile-size-reduced, 4px)); | |
height: calc(var(--icon-size-${size}, ${v}px) - var(--icon-mobile-size-reduced, 4px)); | |
`, | |
mqw({ min: 'tablet-portrait' })` | |
width: var(--icon-size-${size}, ${v}px); | |
height: var(--icon-size-${size}, ${v}px); | |
`, | |
]; | |
}} | |
`; | |
const StyledIconAspectRatio = styled.div.withConfig({ | |
shouldForwardProp: (p) => !['aspectRatio'].includes(p), | |
})<Required<Pick<Props, 'aspectRatio'>>>` | |
position: relative; | |
width: 100%; | |
overflow: hidden; | |
aspect-ratio: ${({ aspectRatio }): number => aspectRatio}; | |
& > * { | |
position: absolute; | |
left: 0; | |
top: 0; | |
width: 100%; | |
height: 100%; | |
} | |
`; | |
export type IconComponent<P extends object = Props> = { | |
(p: P): import('react').ReactElement<P> | null; | |
selector: string; | |
}; | |
export const Icon: IconComponent = ({ | |
aspectRatio = 1, | |
element, | |
size = 'md', | |
}) => { | |
if (!element) return null; | |
const Svg = typeof element === 'string' ? icons[element] : element; | |
const rendered = | |
typeof Svg === 'function' | |
? Svg({ | |
fill: 'currentColor', | |
width: '100%', | |
height: '100%', | |
viewBox: '0 0 24 24', | |
xmlns: 'http://www.w3.org/2000/svg', | |
...Svg().props, | |
}) | |
: Svg; | |
return ( | |
<StyledIcon size={size}> | |
<StyledIconAspectRatio aspectRatio={aspectRatio}> | |
{rendered} | |
</StyledIconAspectRatio> | |
</StyledIcon> | |
); | |
}; | |
Icon.selector = StyledIcon.toString(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type SVGComponent = ( | |
p?: import('react').SVGProps<SVGSVGElement>, | |
) => Omit<import('react').ReactSVGElement, 'ref'>; | |
type Direction = 'left' | 'right' | 'up' | 'down'; | |
const dir: Record<Direction, string> = { | |
left: 'rotate(90deg)', | |
right: 'rotate(-90deg)', | |
up: 'rotate(180deg)', | |
down: 'rotate(0)', | |
}; | |
const Arrow: Record<Direction, SVGComponent> = Object.create(null); | |
for (const k in dir) { | |
Arrow[k as Direction] = (p): ReturnType<SVGComponent> => ( | |
<svg style={{ transform: dir[k as Direction] }} viewBox="0 0 24 24" {...p}> | |
<path d="m6 7 6 6 6-6 2 2-8 8-8-8z" /> | |
</svg> | |
); | |
} | |
const LogoGitHub: SVGComponent = (p) => ( | |
<svg viewBox="0 0 24 24" {...p}> | |
<path d="M12.5.75C6.146.75 1 5.896 1 12.25c0 5.089 3.292 9.387 7.863 10.91.575.101.79-.244.79-.546 0-.273-.014-1.178-.014-2.142-2.889.532-3.636-.704-3.866-1.35-.13-.331-.69-1.352-1.18-1.625-.402-.216-.977-.748-.014-.762.906-.014 1.553.834 1.769 1.179 1.035 1.74 2.688 1.25 3.349.948.1-.747.402-1.25.733-1.538-2.559-.287-5.232-1.279-5.232-5.678 0-1.25.445-2.285 1.178-3.09-.115-.288-.517-1.467.115-3.048 0 0 .963-.302 3.163 1.179.92-.259 1.897-.388 2.875-.388.977 0 1.955.13 2.875.388 2.2-1.495 3.162-1.179 3.162-1.179.633 1.581.23 2.76.115 3.048.733.805 1.179 1.825 1.179 3.09 0 4.413-2.688 5.39-5.247 5.678.417.36.776 1.05.776 2.128 0 1.538-.014 2.774-.014 3.162 0 .302.216.662.79.547C20.709 21.637 24 17.324 24 12.25 24 5.896 18.854.75 12.5.75Z" /> | |
</svg> | |
); | |
export const icons = { | |
'arrow-left': Arrow.left, | |
'arrow-right': Arrow.right, | |
'arrow-up': Arrow.up, | |
'arrow-down': Arrow.down, | |
'logo-github': LogoGitHub, | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { mqw, rem, styled } from '@styled'; | |
const PX_SIZE = { | |
sm: 10, | |
md: 12, | |
lg: 16, | |
}; | |
type Props = Readonly<{ | |
ellipsis?: boolean; | |
size?: keyof typeof PX_SIZE; | |
text: string; | |
title?: string; | |
}>; | |
type StyledLabelProps = Props & Required<Pick<Props, 'size'>>; | |
const StyledLabel = styled.span.withConfig({ | |
shouldForwardProp: (p) => !['ellipsis', 'size', 'text'].includes(p), | |
})<StyledLabelProps>` | |
font-size: inherit; | |
color: inherit; | |
${({ ellipsis }): string | false | undefined => | |
ellipsis && | |
'text-overflow: ellipsis; white-space: nowrap; overflow: hidden;'} | |
${({ size }): import('styled-components').RuleSet => { | |
const v = PX_SIZE[size]; | |
return [ | |
mqw({ min: 'mobile' })` | |
font-size: calc(var(--label-size-${size}, ${rem(v)}) - var(--label-mobile-size-reduced, ${rem(0.5)})); | |
`, | |
mqw({ min: 'tablet-portrait' })` | |
font-size: var(--label-size-${size}, ${rem(v)}); | |
`, | |
]; | |
}} | |
`; | |
export type LabelComponent<P extends object = Props> = { | |
(p: P): import('react').ReactElement<P>; | |
selector: string; | |
}; | |
export const Label: LabelComponent = ({ | |
ellipsis, | |
size = 'md', | |
text, | |
title, | |
}) => { | |
return ( | |
<StyledLabel | |
ellipsis={ellipsis} | |
size={size} | |
text={text} | |
title={ellipsis ? text : title} | |
> | |
{text} | |
</StyledLabel> | |
); | |
}; | |
Label.selector = StyledLabel.toString(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment