Skip to content

Instantly share code, notes, and snippets.

@semanticpixel
Last active July 26, 2022 22:22
Show Gist options
  • Save semanticpixel/580aa502e0d3dabb8a148156daaf324f to your computer and use it in GitHub Desktop.
Save semanticpixel/580aa502e0d3dabb8a148156daaf324f to your computer and use it in GitHub Desktop.
Badge-in-element
import Component from '@glimmer/component';
import STEPS from '../steps';
export default class extends Component {
get destinationElement() {
return document.getElementById('badge-outlet');
}
get styles() {
const newDestinationElement = document.getElementById(`${STEPS[this.args.step]}-2`);
const rect = newDestinationElement.getBoundingClientRect();
// console.log('Nav Jobs Rectangle: ', rect);
// 8 is sizeOfBadge / 2
// return `top: ${rect.top - 8}px; left: ${rect.right - 8}px`;
// 24 is sizeOfBadge / 2 + margin of page (8 + 16)
// return `transform: translate(${rect.right - 8}px,${rect.top - 8}px)`;
return `transform: translate(${(rect.x + rect.width) - 8}px,${rect.y - 8}px)`;
}
}
import Component from '@glimmer/component';
import { action } from '@ember/object';
import STEPS from '../steps';
export default class extends Component {
get destinationElement() {
return document.getElementById(STEPS[this.args.step]);
}
get styles() {
const rect = this.destinationElement.getBoundingClientRect();
// console.log(rect, rect.top, rect.right, rect.bottom, rect.left);
// 8 is sizeOfBadge / 2
// return `top: ${-8}px; right: ${-8}px`;
return `transform: translate(${rect.width - 8}px,${-8}px)`;
}
@action
animationEnd(e) {
console.log('Animation Ended');
const element = e.target;
element.classList.remove('badge--animated-focus');
element.classList.remove('badge--animated');
setInterval(() => {
element.classList.add('badge--animated');
}, 0);
}
}
import Controller from '@ember/controller';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import STEPS from '../steps';
export default class ApplicationController extends Controller {
appName = 'Ember Twiddle';
@tracked stepV1 = 0;
@tracked stepV2 = 0;
@action onClickV1() {
this.stepV1 = (this.stepV1 + 1) % STEPS.length;
}
@action onClickV2() {
this.stepV2 = (this.stepV2 + 1) % STEPS.length;
}
}
const STEPS = [
'global-nav-home',
'global-nav-messaging',
'global-nav-jobs',
'global-nav-profile',
'global-nav-notifications',
];
export default STEPS;
body {
margin: 12px 16px;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 65%;
}
.global-nav {
margin-left: auto;
min-height: 52px;
}
.global-nav__items {
display: flex;
flex-wrap: nowrap;
list-style-type: none;
}
.global-nav__item {
align-items: center;
cursor: pointer;
display: flex;
flex-direction: column;
font-size: 1.2rem;
font-weight: 400;
justify-content: center;
line-height: 1.5;
min-height: 52px;
min-width: 80px;
position: relative;
text-decoration: none;
background-color: #fafafa;
border: 1px solid rgba(0,0,0,0.4);
}
.global-nav__icon {
width: 24px;
height: 24px;
background-color: #ddd;
}
.global-nav__label {
font-size: 1.2rem;
color: #666;
}
.badge {
position: absolute;
width: 16px;
height: 16px;
background-color: purple;
border-radius: 50%;
z-index: 1;
}
.badge {
top: 0;
left: 0;
}
.badge--animated-focus {
animation: focus 1s cubic-bezier(0,0,0.32,1);
}
.badge--animated {
animation: pulse 3s linear 2s;
}
.badge--animated-v2 {
animation: pulse 3s linear 3s infinite;
}
.badge--outlet {
background-color: pink;
}
.badge__container {
/* position: fixed; */
position: absolute;
width: 16px;
height: 16px;
top: 0;
left: 0;
z-index: 1;
/* transition: transform 1000ms cubic-bezier(0,0,0.32,1); */
}
@keyframes focus {
0% {
transform: scale(0);
opacity: 0;
border: 0;
background-color: transparent;
}
50% {
transform: scale(3);
opacity: 1;
border: 2px solid;
background-color: transparent;
}
100% {
transform: scale(1);
opacity: 1;
}
}
@keyframes pulse {
0% {
transform: scale(1);
}
/* 40% {
transform: scale(1);
} */
10% {
transform: scale(1.5);
}
90% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
@keyframes pulse-v2 {
0%, 50%, {
transform: scale(1);
}
51% {
transform: scale(1.5);
}
70%, 100% {
transform: scale(1);
}
}
@keyframes pulse-v3 {
0% {
transform: scale(1);
}
20%, {
transform: scale(1.5);
}
100% {
transform: scale(1.5);
}
}
<div id="badge-outlet"></div>
<h1>Welcome to {{this.appName}}</h1>
<br>
<br>
{{outlet}}
<h2>Badge rendering directly on destination element</h2>
<GlobalNav/>
<!-- This badge will attach to the element directly -->
<Badge @step={{this.stepV1}}/>
<button {{on 'click' this.onClickV1}}>Step</button>
<h2>Badge rendering in outlet and the translate correct element</h2>
<GlobalNav @type={{2}}/>
<!-- This badge will render in outlet and the translate to the correct location -->
<BadgeOutlet @step={{this.stepV2}} />
<button {{on 'click' this.onClickV2}}>Step</button>
<br>
<br>
{{#-in-element this.destinationElement}}
<div class="badge__container" style="{{this.styles}}">
<div class="badge badge--outlet badge--animated-v2"></div>
</div>
{{/-in-element}}
{{#-in-element this.destinationElement}}
<div class="badge__container" style="{{this.styles}}">
<div class="badge badge--animated-focus" {{on "animationend" this.animationEnd}}></div>
</div>
{{/-in-element}}
<header>
<nav class="global-nav">
<ul class="global-nav__items">
<li id="global-nav-home{{if @type '-2'}}" class="global-nav__item">
<span class="global-nav__icon"></span>
<span class="global-nav__label">Home</span>
</li>
<li id="global-nav-messaging{{if @type '-2'}}" class="global-nav__item">
<span class="global-nav__icon"></span>
<span class="global-nav__label">Messaging</span>
</li>
<li id="global-nav-jobs{{if @type '-2'}}" class="global-nav__item">
<span class="global-nav__icon"></span>
<span class="global-nav__label">Jobs</span>
</li>
<li id="global-nav-profile{{if @type '-2'}}" class="global-nav__item">
<span class="global-nav__icon"></span>
<span class="global-nav__label">Profile</span>
</li>
<li id="global-nav-notifications{{if @type '-2'}}" class="global-nav__item">
<span class="global-nav__icon"></span>
<span class="global-nav__label">Notifications</span>
</li>
</ul>
</nav>
</header>
{
"version": "0.17.1",
"EmberENV": {
"FEATURES": {},
"_TEMPLATE_ONLY_GLIMMER_COMPONENTS": false,
"_APPLICATION_TEMPLATE_WRAPPER": true,
"_JQUERY_INTEGRATION": true
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.js",
"ember": "3.18.1",
"ember-template-compiler": "3.18.1",
"ember-testing": "3.18.1"
},
"addons": {
"@glimmer/component": "1.0.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment