zooming out of SVG using CSS - image

I have this SVG of a paperplane, which I want to have inside of a circle.
However, when I do the following the SVG is cropped at the edges. The nature of SVG is being scaleable, I have tried fiddling with the properties of the SVG however the problem remains. What should I do to keep this SVG
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 18.7 14.3" style="enable-background:new 0 0 18.7 14.3;" xml:space="preserve">
<style type="text/css">
.st0{fill:#000;}
</style>
<path class="st0" d="M18.7,0L0.3,4.1l3,3.6L0,9.4l0.5,4.9l5.6-4.1l2.3,1.5L18.7,0z M1.3,4.4L16,1.2L3.8,7.4L1.3,4.4z M10.3,4.7
l-9.3,5.5l-0.2,1.7L0.6,9.7L10.3,4.7z M1.2,13.2l0.4-2.7l2.6-1.6l1.5,1L1.2,13.2z M4.7,8.6l12-7.1l-8.3,9.5L4.7,8.6z"/>
</svg>
This is my HTML and CSS
.choice {
background-color: #E0D6AF;
border-radius: 50%;
height: 200px;
width: 200px;
margin: 10px auto;
transition: transform 0.2s;
transform: scale(1);
z-index: 10;
}
<img id="paper" class="choice" value="paper" src="http://www.jerrylow.com/demo/rps/paper.svg" alt="a piece of paper"> </img>
Full CSS file
.container {
display: inline-grid;
padding-top:30px;
grid-gap: 2px;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
}
#submit {
border-radius: 50%;
height: 150px;
width: 150px;
margin: auto;
grid-column: 2;
grid-row: 2;
background-color:#E0D6AF;
}
#submit:hover {
transform: scale(1.3);
}
.choice {
background-color: #E0D6AF;
border-radius: 50%;
height: 200px;
width: 200px;
margin: 10px auto;
transition: transform 0.2s;
transform: scale(1);
z-index: 10;
}
.choice > img:hover {
transform: scale(1.1);
}
#rock{
grid-column: 1;
grid-row:1;
}
#paper{
grid-column: 1;
grid-row:2;
}
#scissor{
grid-column: 1;
grid-row:3;
}
#humanScore > p {
padding-top: 80px;
text-align: center !important;
}
#humanScore {
grid-column: 2;
grid-row: 3;
}
#computerScore > p {
padding-top: 80px;
text-align: center !important;
}
#computerScore {
grid-column: 2;
grid-row: 1;
}
#result {
margin: auto;
width: 150px;
height: 150px;
border-radius: 50%;
grid-column: 3;
grid-row: 2;
}
#keyframes pulse{
0%{
transform: scale(0);
}
100%{
transform: scale(1);
}
}
.resultWin{
background-color:lightgreen;
transition: transform 0.2s;
transform: scale(1.3);
animation-name: pulse;
animation-duration: 1s;
animation-iteration-count: 1;
animation-fill-mode: both;
}
.resultLoss{
background-color: crimson;
transition: transform 0.2s;
transform: scale(1.3);
animation-name: pulse;
animation-duration: 1s;
animation-iteration-count: 1;
animation-fill-mode: both;
}
.resultTie{
background-color:goldenrod;
transition: transform 0.2s;
transform: scale(1.3);
animation-name: pulse;
animation-duration: 1s;
animation-iteration-count: 1;
animation-fill-mode: both;
}
.chosen { filter: brightness(70%) }
Full react file - needs a ton of refactoring so difficult to read.
import React from 'react';
import './Game.css'
class Game extends React.Component {
constructor(props) {
super(props);
this.state = {
picked: '',
score: {
player: 0,
computer: 0
},
previousPicks: {
rock: 0,
scissor: 0,
paper: 0
},
result: { result: "", player: "", computer: "" }
};
}
unmarkImage = () => {
if (this.state.picked !== "") {
document.querySelector(`#${this.state.picked}`).classList.remove("chosen")
}
}
onClickHandler = (event) => {
this.unmarkImage();
document.querySelector(`#${event.target.attributes.value.value}`).classList.add("chosen");
this.setState({ picked: event.target.attributes.value.value });
}
onSubmit = () => {
if (this.state.picked === "") {
alert("pick your weapon, before fighting")
return
}
const self = this;
fetch(`/api/play/${this.state.picked}`)
.then(function (res) {
res.text().then((function (text) {
self.setState({ result: JSON.parse('{' + text.replace(/(\w+):(\w+)/g, `"$1":"$2"`) + '}') })
self.updateScore(self.state.result.result);
self.updateResult();
self.unmarkImage();
self.updateComputerChoice();
self.updatePreviousPicks(self.state.picked)
self.setState({ picked: "" })
}))
})
}
updateScore = (result) => {
let scoreClone = { ...this.state.score }
if (result === "win") {
scoreClone.player++
this.setState({
score: scoreClone
})
} else if (result === "loss") {
scoreClone.computer++
this.setState({
score: scoreClone
})
}
}
updatePreviousPicks = (pick) => {
let previousPicksClone = { ...this.state.previousPicks }
if (pick === "rock") {
previousPicksClone.rock++;
this.setState({
previousPicks: previousPicksClone
})
} else if (pick === "scissor") {
previousPicksClone.scissor++;
this.setState({
previousPicks: previousPicksClone
})
} else {
previousPicksClone.paper++;
this.setState({
previousPicks: previousPicksClone
})
}
}
onReset = () => { //looks clumsy, but good performance
let computerDiv = document.querySelector("#computer")
while (computerDiv.firstChild) {
computerDiv.removeChild(computerDiv.firstChild);
}
}
updateResult = () => {
let result = document.querySelector("#result")
if (this.state.result.result === "win") {
result.classList.add("resultWin");
}
else if (this.state.result.result === "loss") {
result.classList.add("resultLoss");
} else {
result.classList.add("resultTie");
}
}
updateComputerChoice = () => {
let computerChoice = this.state.result.computer;
let result = document.querySelector("#result");
while (result.firstChild) {
result.removeChild(result.firstChild);
}
let answer = document.createElement("img");
let img;
if (computerChoice === "rock") {
img = document.getElementById("rock").src;
} else if (computerChoice === "scissor") {
img = document.getElementById("scissor").src;
} else {
img = document.getElementById("paper").src;
}
answer.src = img;
result.appendChild(answer);
}
render() {
return (
<div className="container">
<img id="rock" className="choice" value="rock" src="resources/rock.svg" alt="a rock" onClick={this.onClickHandler}></img>
<img id="paper" className="choice" value="paper" src="resources/paper.svg" alt="a piece of paper" onClick={this.onClickHandler}></img>
<img id="scissor" className="choice" value="scissor" src="resources/scissor.svg" alt="a scissor" onClick={this.onClickHandler}></img>
<button id="submit" className="waves-effect waves-light btn" onClick={this.onSubmit}>Fight the AI!</button>
<div id="humanScore">
<p>You: {this.state.score.player}</p>
</div>
<div id="computerScore">
<p>Computer: {this.state.score.computer}</p>
</div>
<div id="result" className="">
</div>
</div>
)
}
}
export default Game

To demonstrate #exaneta 's original suggestion, which I can't imagine would have any effect on a grid system, run the snippet below. If that's not what you are trying to accomplish, I'll delete this answer.
.choice {
background-color: #E0D6AF;
border-radius: 50%;
height: 200px;
width: 200px;
margin: 10px auto;
transition: transform 0.2s;
transform: scale(1);
z-index: 10;
display: flex;
justify-content: center;
align-items: center;
}
.choice > img#paper{
width: 60%;
}
<div class="choice">
<img
id="paper"
value="paper"
src="http://www.jerrylow.com/demo/rps/paper.svg"
alt="a piece of paper"
/>
</div>

Related

How to fade one react component out, then fade another in?

I've got 2 components that are conditionally shown or hidden based on a string value stored in useState as showModal
{showModal === 'SIGNIN' && <SignIn />}
{showModal === 'JOIN' && <Join />}
I want to fade in one component, then when the state changes, fade it out and fade in the other component.
Can this be done with react transition group?
I tried
<TransitionGroup>
<CSSTransition in={showModal === 'SIGNIN' ? true : false} classNames='fade' timeout={220} key={showModal}>
<div>
<SignIn />
</div>
</CSSTransition>
<CSSTransition in={showModal === 'JOIN' ? true : false} classNames='fade' timeout={220} key={showModal}>
<div>
<Join />
</div>
</CSSTransition>
</TransitionGroup>
I don't get any error, one component is shown, changing showModal from 'SIGNIN' to 'JOIN' does nothing. Inspecting the divs with the timeout set to 22000 shows that no new classes have been added.
SwitchTransition from react transition group might help.
Example
const { useState, useEffect } = React;
const { SwitchTransition, CSSTransition } = ReactTransitionGroup;
const SignIn = () => <div className="block sign-in">Sign In</div>;
const Join = () => <div className="block join">Join</div>;
const App = () => {
const [showModal, setModal] = useState("SIGNIN");
useEffect(() => {
let handle;
const loop = () => {
setModal(state => state === "JOIN" ? "SIGNIN" : "JOIN");
handle = setTimeout(loop, 2500);
};
handle = setTimeout(loop, 1000);
return () => {
clearTimeout(handle);
}
}, []);
const addEndListener = (node, done) => {
node.addEventListener("transitionend", done, false);
}
return <div>
<SwitchTransition mode="out-in">
<CSSTransition
key={showModal === "SIGNIN"}
addEndListener={addEndListener}
classNames="fade">
{showModal === "SIGNIN" ? <SignIn/> : <Join/>}
</CSSTransition>
</SwitchTransition>
</div>;
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
body {
margin: 0;
overflow: hidden;
font-family: Georgia, serif;
}
.block {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 10px;
color: white;
width: 200px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
font-size: 30px;
}
.sign-in {
background: #0984e3;
}
.join {
background: #6c5ce7;
}
.fade-enter {
opacity: 0;
transform: translate(-100%, -50%);
}
.fade-exit {
opacity: 1;
transform: translate(-50%, -50%);
}
.fade-enter-active {
opacity: 1;
transform: translate(-50%, -50%);
}
.fade-exit-active {
opacity: 0;
transform: translate(100%, -50%);
}
.fade-enter-active,
.fade-exit-active {
transition: opacity 500ms, transform 500ms;
}
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/react-transition-group#4.4.2
/dist/react-transition-group.min.js"></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<div id="root"></div>

How to view custom log files using Log Viewer in Laravel

I am using Laravel Log Reader
for viewing log files. It works fine.
But I also have other log files, how can I view them usig this viewer?
This package matching specific file pattern logs/laravel-*.log .So your mentioned log file doesn't match. This package doesn't have configuration to change it at present. But still if you want to view your own log files then you can override method and create your own view file.
I can provide you some basic idea and make sure i haven't written code in super clean.This is to get some idea
Custom class which is extended
<?php
namespace App\Helper;
class LaravelLogReader extends \Haruncpi\LaravelLogReader\LaravelLogReader
{
public function getLogFileDates()
{
$dates = [];
$files = glob(storage_path('logs/*.log'));
$files = array_reverse($files);
foreach ($files as $path) {
$fileName = basename($path);
array_push($dates, $fileName);
}
return $dates;
}
public function get()
{
$availableDates = $this->getLogFileDates();
if (count($availableDates) == 0) {
return response()->json([
'success' => false,
'message' => 'No log available'
]);
}
$configDate = $this->config['date'];
if ($configDate == null) {
$configDate = $availableDates[0];
}
if (!in_array($configDate, $availableDates)) {
return response()->json([
'success' => false,
'message' => 'No log file found with selected date ' . $configDate
]);
}
$pattern = "/^\[(?<date>.*)\]\s(?<env>\w+)\.(?<type>\w+):(?<message>.*)/m";
$fileName = $configDate;
$content = file_get_contents(storage_path('logs/' . $fileName));
preg_match_all($pattern, $content, $matches, PREG_SET_ORDER, 0);
$logs = [];
foreach ($matches as $match) {
$logs[] = [
'timestamp' => $match['date'],
'env' => $match['env'],
'type' => $match['type'],
'message' => trim($match['message'])
];
}
$date = $fileName;
$data = [
'available_log_dates' => $availableDates,
'date' => $date,
'filename' => $fileName,
'logs' => $logs
];
return response()->json(['success' => true, 'data' => $data]);
}
}
and view file which is copied from library view .i have named it as log.blade.php
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Log Reader</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script>
var angularUrl = '{{asset('laravel-log-reader/angular.min.js')}}';
window.angular || document.write('<script src="' + angularUrl + '">\x3C/script>')
</script>
<style>
body {
margin: 0;
padding: 0;
background: #f4f4f4;
font-family: sans-serif;
}
.btn {
text-decoration: none;
background: antiquewhite;
padding: 5px 12px;
border-radius: 25px;
}
header {
min-height: 30px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px;
background: #3F51B5;
position: fixed;
left: 0;
right: 0;
top: 0;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
}
header .btn_clear_all {
background: #de4f4f;
color: #fff;
}
header .name {
font-size: 25px;
font-weight: 500;
color: white;
}
.content {
margin-top: 65px;
padding: 15px;
background: #fff;
min-height: 100px;
}
.content .date_selector {
min-height: 26px;
min-width: 130px;
border: 1px solid #ddd;
border-radius: 4px;
}
.top_content {
display: flex;
justify-content: space-between;
align-items: center;
}
.top_content .top_content_left {
display: flex;
}
.top_content .top_content_left .log_filter {
display: flex;
align-items: center;
margin-left: 15px;
}
.top_content .top_content_left .log_filter .log_type_item {
margin-right: 4px;
background: #eae9e9;
max-height: 20px;
font-size: 11px;
box-sizing: border-box;
padding: 4px 6px;
cursor: pointer;
}
.top_content .top_content_left .log_filter .log_type_item.active {
background: #2f2e2f;
color: white;
}
.top_content .top_content_left .log_filter .log_type_item.clear {
background: #607D8B;
color: white;
}
table {
border: 1px solid #ccc;
border-collapse: collapse;
margin: 0;
padding: 0;
width: 100%;
}
table tr {
border: 1px solid #e8e8e8;
padding: 5px;
}
table tr:hover {
background: #f4f4f4;
}
thead tr td {
background: #717171;
color: #fff;
}
table th,
table td {
padding: 5px;
font-size: 14px;
color: #666;
}
table th {
font-size: 14px;
letter-spacing: 1px;
text-transform: uppercase;
}
#media screen and (max-width: 700px) {
.top_content {
flex-direction: column;
}
.top_content .top_content_left {
flex-direction: column;
}
.top_content .log_filter {
flex-wrap: wrap;
}
.top_content .log_filter .log_type_item {
margin-bottom: 3px;
}
}
#media screen and (max-width: 600px) {
header {
flex-direction: column;
}
header .name {
margin-bottom: 20px;
}
.content {
margin-top: 90px;
}
.btn {
font-size: 13px;
}
.dt_box,
.selected_date {
text-align: center;
}
.responsive_table {
max-width: 100%;
overflow-x: auto;
}
table {
border: 0;
}
table thead {
display: none;
}
table tr {
border-bottom: 2px solid #ddd;
display: block;
margin-bottom: 10px;
}
table td {
border-bottom: 1px dotted #ccc;
display: block;
font-size: 15px;
}
table td:last-child {
border-bottom: 0;
}
table td:before {
content: attr(data-label);
float: left;
font-weight: bold;
text-transform: uppercase;
}
}
.badge {
padding: 2px 8px;
-webkit-border-radius: 25px;
-moz-border-radius: 25px;
border-radius: 25px;
font-size: 11px;
}
.badge.info {
background: #6bb5b5;
color: #fff;
}
.badge.warning {
background: #f7be57;
}
.badge.critical {
background: #de4f4f;
color: #fff;
}
.badge.emergency {
background: #ff6060;
color: white;
}
.badge.notice {
background: bisque;
}
.badge.debug {
background: #8e8c8c;
color: white;
}
.badge.alert {
background: #4ba4ea;
color: white;
}
.badge.error {
background: #c36a6a;
color: white;
}
</style>
</head>
<body ng-controller="LogCtrl">
<header>
<div class="name">#{{ title }}</div>
<div class="actions">
<a class="btn btn_clear_all" href="#" ng-click="clearAll()">Clear All</a>
<a class="btn" href="{{url(config('laravel-log-reader.admin_panel_path'))}}">Goto Admin Panel</a>
<a class="btn" href="https://laravelarticle.com/laravel-log-reader" title="Laravel Log Reader">Doc</a>
</div>
</header>
<section class="content">
<div class="top_content">
<div class="top_content_left">
<div>
<p class="selected_date" style="font-size: 14px;"><strong>
<span ng-show="response.success">Showing Logs: #{{data.date}}</span>
<span ng-hide="response.success">#{{response.message}}</span>
</strong></p>
</div>
<div class="log_filter">
<div class="log_type_item" ng-class="selectedType==tp?'active':''"
ng-repeat="tp in logTypes track by $index"
ng-click="filterByType(tp)">#{{ tp }}
</div>
<div class="log_type_item clear" ng-show="selectedType" ng-click="selectedType=undefined">CLEAR FILTER
</div>
</div>
</div>
<div class="top_content_right">
<p class="dt_box">Select Date: <select class="date_selector" ng-model="selectedDate"
ng-change="init(selectedDate)">
<option ng-repeat="dt in data.available_log_dates"
value="#{{ dt }}">#{{ dt }}
</option>
</select>
</p>
</div>
</div>
<div>
<div class="responsive_table">
<table>
<thead>
<tr>
<td width="140">Timestamp</td>
<td width="120">Env</td>
<td width="120">Type</td>
<td>Message</td>
</tr>
</thead>
<tr ng-repeat="log in data.logs |filter: selectedType track by $index">
<td>#{{ log.timestamp }}</td>
<td>#{{log.env}}</td>
<td><span class="badge #{{ log.type.toLowerCase() }}">#{{ log.type }}</span></td>
<td>#{{ log.message }}</td>
</tr>
</table>
</div>
</div>
<script>
var myApp = angular.module("myApp", []);
myApp.controller("LogCtrl", function ($scope, $http) {
$scope.title = "Log Reader";
$scope.selectedType = undefined;
$scope.logTypes = ['INFO', 'EMERGENCY', 'CRITICAL', 'ALERT', 'ERROR', 'WARNING', 'NOTICE', 'DEBUG'];
var originalData = null;
$scope.init = function (date) {
var url = '';
if (date !== '' && date !== undefined) {
url = '{{url(config('laravel-log-reader.api_route_path'))}}?date=' + date
} else {
url = '{{url("custom-logger")}}'
}
alert(url);
$http.get(url)
.success(function (data) {
$scope.response = data;
$scope.data = data.data;
originalData = data.data;
})
};
$scope.init();
$scope.filterByType = function (tp) {
$scope.selectedType = tp
};
$scope.clearAll = function () {
if (confirm("Are you sure?")) {
var url = '{{url(config('laravel-log-reader.view_route_path'))}}'
$http.post(url, {'clear': true})
.success(function (data) {
if (data.success) {
alert(data.message);
$scope.init();
}
})
}
}
})
</script>
</section>
</body>
</html>
And add two routes
Route::get('custom-logger', function () {
$laravelLogReader=new \App\Helper\LaravelLogReader();
return $laravelLogReader->get();
});
Route::get('/log-viewer', function () {
return view('log');
});
Note: this not fully optimized code but you can write it in better way.This is only to show you can override the package
Another package you can use
Ref:https://github.com/rap2hpoutre/laravel-log-viewer
This package will read all log files and i have tested it it works fine

How to style dropdown with styled-components

I am using React JS + Typescript for my app. For styling I am using styled-components. I am really new in styled components. I have created one dropdown. The logic works fine but the UI looks horrible. I uploaded my code in Code sand box. I want design my Dropdown like Tailwind. But since I am new styled-components, I just don't know how to do that.
This is my dropdown component
import React, { useState } from "react";
import styled from "styled-components";
import Arrow from './Arrow.svg'
const Wrapper = styled.div<
{
active: boolean;
}
>`
text-align: left;
width: 100%;
color: #bfc5cd;
font-size: 16px;
font-weight: 300;
position: relative;
margin: 2em 0;
#media (min-width: 400px) {
max-width: 300px;
}
svg {
fill: #798697;
transition: all 0.2s ease;
}
${props =>
props.active
? `
svg {
transform: rotate(180deg);
}
`
: ``}
`;
const MenuLabel = styled.span`
display:inline-block;
color: grey;
border: 1px solid green;
background: white;
box-shadow: 0 0 5px -1px rgba(0,0,0,0.2);
cursor:pointer;
vertical-align:middle;
max-width: 100px;
padding: 40px 40px;
font-size: 12px;
text-align: center;
border: 1px solid ${({ theme }) => theme.inputBorderColor};
&:focus {
outline: none;
box-shadow: 0px 0px 0px 1px ${({ theme }) => theme.inputBorderColorActive};
border: 1px solid ${({ theme }) => theme.inputBorderColorActive};
}
`;
const ItemList = styled.div`
color: #798697;
background: white;
line-height: 30px;
padding: .25em 2em .25em 2em;
cursor: defaul;
user-select: none;
transition: all .25s ease;
&:hover,
&.selected {
background: #F7F7F7;
color: #4A4A4A;
}
`;
export interface IOptions {
label: string;
value: number;
}
export interface IDropdown {
labelDefault: string;
options: IOptions[];
}
const Dropdown = ({ labelDefault, options }: IDropdown) => {
const [isOpened, setIsOpened] = useState(false);
const [selectedOption, setSelectedOption] = useState("");
const [label, setLabel] = useState("");
const handleSelectedItem = (obj: any) => {
setSelectedOption(obj.value);
setLabel(obj.label);
setIsOpened(!isOpened);
};
return (
<Wrapper active={isOpened}>
<MenuLabel onClick={() => setIsOpened(!isOpened)}>
{selectedOption ? label : labelDefault}
</MenuLabel>
<ul
style={
isOpened
? {
display: "block",
listStyleType: "none"
}
: { display: "none" }
}
>
{options.map(el => (
<ItemList
key={el.value.toString()}
onClick={() => handleSelectedItem(el)}
>
{el.label}
</ItemList>
))}
</ul>
</Wrapper>
);
}
export default Dropdown;
This is the parent component
import * as React from "react";
import Dropdown from "./dropdown";
const MockData = [
{ label: "one", value: 1 },
{ label: "two", value: 2 },
{ label: "three", value: 3 }
];
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<Dropdown labelDefault="Select a label" options={MockData} />
</div>
);
}

Autosave Status in CKEditor 5

I have gotten stuck on a rather simple aspect of the autosave feature and that is the current status of the action like found on the overview page: https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/saving-data.html#demo. But it doesn't look like they actually reference it anywhere (example below).
My html is just:
<textarea class="form-control" name="notes" id="notes">{!! $shipmentShortage->notes !!}</textarea>
My create script is below, the autosave feature works just fine, but the status just isn't there:
<script>
ClassicEditor
.create( document.querySelector( '#notes' ), {
toolbar: [ 'heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' ],
image: {
toolbar: [ 'imageStyle:full', 'imageStyle:side', '|', 'imageTextAlternative' ],
},
autosave: {
save( editor ) {
console.log(editor.getData());
// The saveData() function must return a promise
// which should be resolved when the data is successfully saved.
return saveData( editor.getData() );
}
}
} );
// Save the data to a fake HTTP server (emulated here with a setTimeout()).
function saveData( data ) {
return new Promise( resolve => {
setTimeout( () => {
console.log( 'Saved', data );
$.ajax({
url: '/osd/shortages/update',
type: 'POST',
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
data: {
'shortage_id':'{{$shipmentShortage->id}}',
'notes': data,
},
dataType: 'json',
success: function (response) {
console.log('saved');
}
});
resolve();
}, 5000 );
} );
}
// Update the "Status: Saving..." info.
function displayStatus( editor ) {
const pendingActions = editor.plugins.get( 'PendingActions' );
const statusIndicator = document.querySelector( '#editor-status' );
pendingActions.on( 'change:hasAny', ( evt, propertyName, newValue ) => {
if ( newValue ) {
statusIndicator.classList.add( 'busy' );
} else {
statusIndicator.classList.remove( 'busy' );
}
} );
}
</script>
You are absolutely correct. They show us a sexy status updater but don't give us the code for it. Here is what I extracted from the demo page by looking at the page source. This should give you the Status updates as you asked. Let me know if you have any questions.
HTML:
<div id="snippet-autosave">
<textarea name="content" id="CKeditor_Notes">
Sample text
</textarea>
</div>
<!-- This will show the save status -->
<div id="snippet-autosave-header">
<div id="snippet-autosave-status" class="">
<div id="snippet-autosave-status_label">Status:</div>
<div id="snippet-autosave-status_spinner">
<span id="snippet-autosave-status_spinner-label"></span>
<span id="snippet-autosave-status_spinner-loader"></span>
</div>
</div>
</div>
CSS:
<style>
#snippet-autosave-header{
display: flex;
justify-content: space-between;
align-items: center;
background: var(--ck-color-toolbar-background);
border: 1px solid var(--ck-color-toolbar-border);
padding: 10px;
border-radius: var(--ck-border-radius);
/*margin-top: -1.5em;*/
margin-bottom: 1.5em;
border-top: 0;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
#snippet-autosave-status_spinner {
display: flex;
align-items: center;
position: relative;
}
#snippet-autosave-status_spinner-label {
position: relative;
}
#snippet-autosave-status_spinner-label::after {
content: 'Saved!';
color: green;
display: inline-block;
margin-right: var(--ck-spacing-medium);
}
/* During "Saving" display spinner and change content of label. */
#snippet-autosave-status.busy #snippet-autosave-status_spinner-label::after {
content: 'Saving...';
color: red;
}
#snippet-autosave-status.busy #snippet-autosave-status_spinner-loader {
display: block;
width: 16px;
height: 16px;
border-radius: 50%;
border-top: 3px solid hsl(0, 0%, 70%);
border-right: 2px solid transparent;
animation: autosave-status-spinner 1s linear infinite;
}
#snippet-autosave-status,
#snippet-autosave-server {
display: flex;
align-items: center;
}
#snippet-autosave-server_label,
#snippet-autosave-status_label {
font-weight: bold;
margin-right: var(--ck-spacing-medium);
}
#snippet-autosave + .ck.ck-editor .ck-editor__editable {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
#snippet-autosave-lag {
padding: 4px;
}
#snippet-autosave-console {
max-height: 300px;
overflow: auto;
white-space: normal;
background: #2b2c26;
transition: background-color 500ms;
}
#snippet-autosave-console.updated {
background: green;
}
#keyframes autosave-status-spinner {
to {
transform: rotate( 360deg );
}
}
</style>
The rest is just initializing the Editor just like on the demo page here.
ClassicEditor
.create(document.querySelector('#CKeditor_Notes'), {
autosave: {
save(editor) {
return saveData(editor.getData());
}
}
})
.then(editor => {
window.editor = editor;
displayStatus(editor);
})
.catch(err => {
console.error(err.stack);
});
// Save the data to Server Side DB.
function saveData(data) {
return new Promise(resolve => {
setTimeout(() => {
console.log('Saved', data);
SaveDataToDB(data)
resolve();
});
});
}
// Update the "Status: Saving..." info.
function displayStatus(editor) {
const pendingActions = editor.plugins.get('PendingActions');
const statusIndicator = document.querySelector('#snippet-autosave-status');
pendingActions.on('change:hasAny', (evt, propertyName, newValue) => {
if (newValue) {
statusIndicator.classList.add('busy');
} else {
statusIndicator.classList.remove('busy');
}
});
}

Zoom In and Out Transition in Codenameone

Inspired by native Android zoomOut for form transitions and iOS app launching zoom effect, which are both really cool...
How do I go about implementing zoom In or Out transition for Forms, Dialogs and Containers in Codenameone?
I want the transition to have zooming animation like below:
#charset "UTF-8";
*,
:before,
:after {
margin: 0;
padding: 0;
position: relative;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
input,
select,
button,
textarea {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
font: inherit;
color: inherit;
}
.butt,
.input {
padding: .75rem;
margin: .375rem;
background-color: transparent;
border-radius: 4px;
}
.butt:focus,
.input:focus {
outline: none;
}
.butt {
border: 2px solid #f35626;
line-height: 1.375;
padding-left: 1.5rem;
padding-right: 1.5rem;
font-weight: 700;
color: #f35626;
cursor: pointer;
-webkit-animation: hue 60s infinite linear;
}
.butt--primary {
background-color: #f35626;
color: #fff;
}
.input {
border: 1px solid #c0c8c9;
border-radius: 4px;
}
.input--dropdown {
background-image: url("images/ddown.png");
background-image: url("images/ddown.svg?3"), none;
background-repeat: no-repeat;
background-size: 1.5rem 1rem;
background-position: right center;
}
h1,
.alpha {
margin-bottom: 1.5rem;
font-size: 3rem;
font-weight: 100;
line-height: 1;
letter-spacing: -.05em;
}
h2,
.beta {
margin-bottom: .75rem;
font-weight: 400;
font-size: 1.5rem;
line-height: 1;
}
#media (min-width: 650px) {
.mega {
font-size: 6rem;
line-height: 1;
}
}
.subhead,
.meta {
color: #7b8993;
}
.promo {
text-align: center;
}
p,
hr,
form {
margin-bottom: 1.5rem;
}
hr {
border: none;
margin-top: -1px;
height: 1px;
background-color: #c0c8c9;
background-image: -webkit-linear-gradient(0deg, #fff, #c0c8c9, #fff);
}
a {
color: inherit;
text-decoration: underline;
-webkit-animation: hue 60s infinite linear;
}
a:hover {
color: #f35626;
}
.wrap {
max-width: 38rem;
margin: 0 auto;
}
.island {
padding: 1.5rem;
}
.isle {
padding: .75rem;
}
.spit {
padding: .375rem;
}
html {
font: 100%/1.5"Roboto", Verdana, sans-serif;
color: #3d464d;
background-color: #fff;
-webkit-font-smoothing: antialiased;
width: 100%;
overflow: hidden-x;
text-align: center;
}
#media (min-width: 650px) {
html {
height: 100%;
}
html:before {
content: '';
display: inline-block;
height: 100%;
vertical-align: middle;
margin-right: -0.25em;
}
body {
display: inline-block;
vertical-align: middle;
max-width: 38rem;
}
}
.site__header {
-webkit-animation: bounceInUp 1s;
}
.site__title {
color: #f35626;
background-image: -webkit-linear-gradient(92deg, #f35626, #feab3a);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
-webkit-animation: hue 60s infinite linear;
}
.site__content {
-webkit-animation: bounceInUp 1s;
-webkit-animation-delay: .1s;
}
.site__content form {
-webkit-animation: bounceInUp 1s;
-webkit-animation-delay: .1s;
}
.animated {
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.animated.infinite {
-webkit-animation-iteration-count: infinite;
animation-iteration-count: infinite;
}
.animated.hinge {
-webkit-animation-duration: 2s;
animation-duration: 2s;
}
.animated.bounceIn,
.animated.bounceOut {
-webkit-animation-duration: .75s;
animation-duration: .75s;
}
.animated.flipOutX,
.animated.flipOutY {
-webkit-animation-duration: .75s;
animation-duration: .75s;
}
#-webkit-keyframes zoomIn {
from {
opacity: 0;
-webkit-transform: scale3d(.3, .3, .3);
transform: scale3d(.3, .3, .3);
}
50% {
opacity: 1;
}
}
#keyframes zoomIn {
from {
opacity: 0;
-webkit-transform: scale3d(.3, .3, .3);
transform: scale3d(.3, .3, .3);
}
50% {
opacity: 1;
}
}
.zoomIn {
-webkit-animation-name: zoomIn;
animation-name: zoomIn;
}
#-webkit-keyframes zoomOut {
from {
opacity: 1;
}
50% {
opacity: 0;
-webkit-transform: scale3d(.3, .3, .3);
transform: scale3d(.3, .3, .3);
}
to {
opacity: 0;
}
}
#keyframes zoomOut {
from {
opacity: 1;
}
50% {
opacity: 0;
-webkit-transform: scale3d(.3, .3, .3);
transform: scale3d(.3, .3, .3);
}
to {
opacity: 0;
}
}
.zoomOut {
-webkit-animation-name: zoomOut;
animation-name: zoomOut;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, minimal-ui" />
<link rel="dns-prefetch" href="//fonts.googleapis.com" />
<link rel="dns-prefetch" href="//code.jquery.com" />
<link href='//fonts.googleapis.com/css?family=Roboto:400,100,400italic,700italic,700' rel='stylesheet' type='text/css'>
</head>
<body>
<header class="site__header island">
<div class="wrap">
<span id="animationSandbox" style="display: block;"><h1 class="site__title mega">Zoom me in and out</h1></span>
</div>
</header>
<!-- /.site__header -->
<main class="site__content island" role="content">
<div class="wrap">
<form>
<select class="input input--dropdown js--animations">
<optgroup label="Zoom Entrances">
<option value="zoomIn">zoomIn</option>
</optgroup>
<optgroup label="Zoom Exits">
<option value="zoomOut">zoomOut</option>
</optgroup>
</select>
<button class="butt js--triggerAnimation">Animate it</button>
</form>
<hr />
</div>
</main>
<!-- /.site__content -->
<script src="//code.jquery.com/jquery-1.10.2.min.js"></script>
<script>
function testAnim(x) {
$('#animationSandbox').removeClass().addClass(x + ' animated').one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() {
$(this).removeClass();
});
};
$(document).ready(function() {
$('.js--triggerAnimation').click(function(e) {
e.preventDefault();
var anim = $('.js--animations').val();
testAnim(anim);
});
$('.js--animations').change(function() {
var anim = $(this).val();
testAnim(anim);
});
});
</script>
</body>
</html>
Probably the closest thing to some of the Android effects is the Morph transition between forms that allows a component to grow into the next form http://www.codenameone.com/blog/mighty-morphing-components.html
If you want something closer to the above code you can just position the component in the right location and make it smaller then use animateLayout() to make it grow into place.
In the old days we had a FlyIn transition which mapped to this exactly but it used old 3D code. I think it should be trivial to implement though with the affine transform and scale similarly to the FlipTransition.

Resources