Add Switch Component
This commit is contained in:
1 parent
1faac851d0
commit
c9461bd910
9 files changed
+162
-173
No files matched your search
+4
-3
@@ -29,9 +29,10 @@
|
|||||||
"input": "public"
|
"input": "public"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": ["src/styles.scss"],
|
||||||
"src/styles.scss"
|
"stylePreprocessorOptions": {
|
||||||
]
|
"includePaths": ["src","src/styles"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
|
|||||||
+7
-169
@@ -1,173 +1,11 @@
|
|||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
<div class="container">
|
||||||
<!-- * * * * * * * * * * * The content below * * * * * * * * * * * -->
|
<h1>AEF Component Test</h1>
|
||||||
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * Delete the template below * * * * * * * * * -->
|
|
||||||
<!-- * * * * * * * to get started with your project! * * * * * * * -->
|
|
||||||
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
|
|
||||||
|
|
||||||
<style>
|
<section>
|
||||||
:host {
|
<h2>Switch Component</h2>
|
||||||
--bright-blue: oklch(51.01% 0.274 263.83);
|
<switch [(isActive)]="isToggled" (toggle)="handleToggle($event)"></switch>
|
||||||
--electric-violet: oklch(53.18% 0.28 296.97);
|
<p>Switch is: {{ isToggled() ? 'ON' : 'OFF' }}</p>
|
||||||
--french-violet: oklch(47.66% 0.246 305.88);
|
</section>
|
||||||
--vivid-pink: oklch(69.02% 0.277 332.77);
|
|
||||||
--hot-red: oklch(61.42% 0.238 15.34);
|
|
||||||
--orange-red: oklch(63.32% 0.24 31.68);
|
|
||||||
|
|
||||||
--gray-900: oklch(19.37% 0.006 300.98);
|
|
||||||
--gray-700: oklch(36.98% 0.014 302.71);
|
|
||||||
--gray-400: oklch(70.9% 0.015 304.04);
|
|
||||||
|
|
||||||
--red-to-pink-to-purple-vertical-gradient: linear-gradient(
|
|
||||||
180deg,
|
|
||||||
var(--orange-red) 0%,
|
|
||||||
var(--vivid-pink) 50%,
|
|
||||||
var(--electric-violet) 100%
|
|
||||||
);
|
|
||||||
|
|
||||||
--red-to-pink-to-purple-horizontal-gradient: linear-gradient(
|
|
||||||
90deg,
|
|
||||||
var(--orange-red) 0%,
|
|
||||||
var(--vivid-pink) 50%,
|
|
||||||
var(--electric-violet) 100%
|
|
||||||
);
|
|
||||||
|
|
||||||
--pill-accent: var(--bright-blue);
|
|
||||||
|
|
||||||
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
||||||
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
|
|
||||||
"Segoe UI Symbol";
|
|
||||||
box-sizing: border-box;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
display: block;
|
|
||||||
height: 100dvh;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 3.125rem;
|
|
||||||
color: var(--gray-900);
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 100%;
|
|
||||||
letter-spacing: -0.125rem;
|
|
||||||
margin: 0;
|
|
||||||
font-family: "Inter Tight", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
||||||
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
|
|
||||||
"Segoe UI Symbol";
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
color: var(--gray-700);
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
width: 100%;
|
|
||||||
min-height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 1rem;
|
|
||||||
box-sizing: inherit;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.angular-logo {
|
|
||||||
max-width: 9.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-around;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 700px;
|
|
||||||
margin-bottom: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content h1 {
|
|
||||||
margin-top: 1.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content p {
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
width: 1px;
|
|
||||||
background: var(--red-to-pink-to-purple-vertical-gradient);
|
|
||||||
margin-inline: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill-group {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: start;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
--pill-accent: var(--bright-blue);
|
|
||||||
background: color-mix(in srgb, var(--pill-accent) 5%, transparent);
|
|
||||||
color: var(--pill-accent);
|
|
||||||
padding-inline: 0.75rem;
|
|
||||||
padding-block: 0.375rem;
|
|
||||||
border-radius: 2.75rem;
|
|
||||||
border: 0;
|
|
||||||
transition: background 0.3s ease;
|
|
||||||
font-family: var(--inter-font);
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1.4rem;
|
|
||||||
letter-spacing: -0.00875rem;
|
|
||||||
text-decoration: none;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill:hover {
|
|
||||||
background: color-mix(in srgb, var(--pill-accent) 15%, transparent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill-group .pill:nth-child(6n + 1) {
|
|
||||||
--pill-accent: var(--bright-blue);
|
|
||||||
}
|
|
||||||
.pill-group .pill:nth-child(6n + 2) {
|
|
||||||
--pill-accent: var(--electric-violet);
|
|
||||||
}
|
|
||||||
.pill-group .pill:nth-child(6n + 3) {
|
|
||||||
--pill-accent: var(--french-violet);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill-group .pill:nth-child(6n + 4),
|
|
||||||
.pill-group .pill:nth-child(6n + 5),
|
|
||||||
.pill-group .pill:nth-child(6n + 6) {
|
|
||||||
--pill-accent: var(--hot-red);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill-group svg {
|
|
||||||
margin-inline-start: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-links {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.73rem;
|
|
||||||
margin-top: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-links path {
|
|
||||||
transition: fill 0.3s ease;
|
|
||||||
fill: var(--gray-400);
|
|
||||||
}
|
|
||||||
|
|
||||||
.social-links a:hover svg path {
|
|
||||||
fill: var(--gray-900);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 650px) {
|
@media screen and (max-width: 650px) {
|
||||||
.content {
|
.content {
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
.container {
|
||||||
|
padding: 40px;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
padding: 20px;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
+7
-1
@@ -1,12 +1,18 @@
|
|||||||
import { Component, signal } from '@angular/core';
|
import { Component, signal } from '@angular/core';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
|
import { Switch } from './components/switch/switch';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
imports: [RouterOutlet],
|
imports: [RouterOutlet, Switch, Dropdown],
|
||||||
templateUrl: './app.html',
|
templateUrl: './app.html',
|
||||||
styleUrl: './app.scss'
|
styleUrl: './app.scss'
|
||||||
})
|
})
|
||||||
export class App {
|
export class App {
|
||||||
protected readonly title = signal('aef-component-test');
|
protected readonly title = signal('aef-component-test');
|
||||||
|
isToggled = signal(false);
|
||||||
|
|
||||||
|
handleToggle(state: boolean) {
|
||||||
|
console.log('Toggle state:', state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<div class="switch-knob"></div>
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
@use "sass:math";
|
||||||
|
@use "sass:list";
|
||||||
|
@use "styles/library" as library;
|
||||||
|
|
||||||
|
$slider_on_bg: #FF6E37;
|
||||||
|
$slider_off_bg: #999999;
|
||||||
|
|
||||||
|
$switch_dimension: (57px, 30px);
|
||||||
|
$switch_slider_dimension: (24px, 24px);
|
||||||
|
$switch_padding: (3px, 3px);
|
||||||
|
$switch_border_radius: 15px;
|
||||||
|
|
||||||
|
$switch_animation_timing: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
$switch_animation_duration: 0.3s;
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: inline-block;
|
||||||
|
width: list.nth($switch_dimension, 1); height: list.nth($switch_dimension, 2);
|
||||||
|
border-radius: $switch_border_radius;
|
||||||
|
background-color: $slider_off_bg;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2) inset;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
--switch-duration: #{$switch_animation_duration};
|
||||||
|
transition: background-color var(--switch-duration) $switch_animation_timing;
|
||||||
|
|
||||||
|
&.reduce-motion {
|
||||||
|
--switch-duration: 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover, &:focus-within {
|
||||||
|
.switch-knob {
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2) inset, 0 0 0 2px white, 0 0 0 4px $slider_on_bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: $slider_on_bg;
|
||||||
|
|
||||||
|
.switch-knob {
|
||||||
|
|
||||||
|
transform: translate(
|
||||||
|
list.nth($switch_dimension, 1) - list.nth($switch_slider_dimension, 1) - list.nth($switch_padding, 1) * 2,
|
||||||
|
list.nth($switch_dimension, 2) - list.nth($switch_slider_dimension, 2) - list.nth($switch_padding, 2) * 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-knob {
|
||||||
|
position: absolute;
|
||||||
|
width: list.nth($switch_slider_dimension, 1); height: list.nth($switch_slider_dimension, 2);
|
||||||
|
left: list.nth($switch_padding, 1); top: list.nth($switch_padding, 2);
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
user-select: none;
|
||||||
|
transition: transform var(--switch-duration) $switch_animation_timing;
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { Component, input, model, output } from '@angular/core'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'switch',
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './switch.html',
|
||||||
|
styleUrl: './switch.scss',
|
||||||
|
host: {
|
||||||
|
'role': 'switch',
|
||||||
|
'tabindex': '0',
|
||||||
|
'[attr.aria-checked]': 'isActive()',
|
||||||
|
'[class.active]': 'isActive()',
|
||||||
|
'[class.reduce-motion]': 'reduceMotion()',
|
||||||
|
'(click)': 'onClick()',
|
||||||
|
'(keydown.enter)': 'onClick()',
|
||||||
|
'(keydown.space)': 'onSpaceDown($event)'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
export class Switch {
|
||||||
|
isActive = model<boolean>(false);
|
||||||
|
reduceMotion = input<boolean>(
|
||||||
|
typeof window !== 'undefined'
|
||||||
|
? window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||||
|
: false
|
||||||
|
);
|
||||||
|
toggle = output<boolean>()
|
||||||
|
|
||||||
|
onClick(): void {
|
||||||
|
this.isActive.update(value => !value)
|
||||||
|
this.onToggle()
|
||||||
|
}
|
||||||
|
|
||||||
|
onSpaceDown(event: Event): void {
|
||||||
|
event.preventDefault(); // 스페이스바 스크롤 방지
|
||||||
|
this.onClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
onToggle(): void {
|
||||||
|
this.toggle.emit(this.isActive())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
@function append-transition($props, $animation) {
|
||||||
|
$transitions: ();
|
||||||
|
@each $prop in $props {
|
||||||
|
$transitions: append($transitions, #{$prop} $animation, comma);
|
||||||
|
}
|
||||||
|
@return $transitions;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
@forward 'functions';
|
||||||
Reference in new issue
Block a user