添加slider及tip组件

This commit is contained in:
KeiferJu 2020-06-02 21:56:14 +08:00
parent c308a83a1b
commit 89816f7249
18 changed files with 687 additions and 10 deletions

View File

@ -12,7 +12,7 @@
# 文档 # 文档
[文档及Demo]](http://www.myllcn.com/svelma-pro/) [文档及Demo](http://www.myllcn.com/svelma-pro/)
# 快速开始 # 快速开始

View File

@ -12,7 +12,7 @@
# 文档 # 文档
[文档及Demo]](http://www.myllcn.com/svelma-pro/) [文档及Demo](http://www.myllcn.com/svelma-pro/)
# 快速开始 # 快速开始

View File

@ -2370,5 +2370,69 @@
"name": "change", "name": "change",
"values": "" "values": ""
} }
],
"Slider": [
{
"description": "绑定值,单选以Array[0]为准",
"type": [
"Array [number, number]"
],
"defaultvalue":"[min, max]",
"name": "value",
"values": "[min, max]"
},{
"description": "最小值",
"type": [
"Number"
],
"defaultvalue":"0",
"name": "min",
"values": ""
},{
"description": "最大值",
"type": [
"Number"
],
"defaultvalue":"100",
"name": "max",
"values": ""
},{
"description": "步长",
"type": [
"Number"
],
"defaultvalue":"1",
"name": "step",
"values": ""
},{
"description": "设置为true使用范围输入",
"type": [
"Boolean"
],
"defaultvalue":"false",
"name": "range",
"values": ""
},{
"description": "如果你想要value[0]的值总是大于value[1]就将它设置为true",
"type": [
"Boolean"
],
"defaultvalue":"false",
"name": "order",
"values": ""
},{
"description": "自定义滑块",
"type": [
"Slots"
],
"defaultvalue":"default",
"name": "slot",
"values": "default,left,right"
},{
"isEvent": true,
"description": "选择改变事件",
"name": "change",
"values": ""
}
] ]
} }

View File

@ -42,4 +42,4 @@
</div> </div>
</Example> </Example>
<JSDoc {jsdoc} showEvent="true"/> <JSDoc {jsdoc} showEvent="true"/>

View File

@ -0,0 +1,55 @@
<script context="module">
export async function preload() {
const res = await this.fetch(`components/slider.json`);
const jsdoc = await res.json();
return { jsdoc };
}
</script>
<script>
import { Slider } from 'svelma-pro'
import DocHeader from '../../components/DocHeader.svelte'
import Example from '../../components/Example.svelte'
import JSDoc from '../../components/JSDoc.svelte'
export let jsdoc
let value = [0, 1];
let range2 = [10, 110];
</script>
<DocHeader title="Slider" subtitle="滑动条" />
<Example code={`<script>
import { Slider } from 'svelma-pro'
let value = [0, 1];
</script>
{value[0]}
<Slider bind:value min="0" max="1" step="0.01"/>
`}>
<div slot="preview">
{value[0]}
<Slider bind:value min="0" max="1" step="0.01"/>
</div>
</Example>
<hr class="is-medium" />
<p class="title is-4">范围选择</p>
<Example code={`<script>
import { Slider } from 'svelma-pro'
let range2 = [10, 110];
</script>
{value[0]}-{value[1]}
<Slider max="200" step="10" bind:value={range2} range order tip="true"/>
`}>
<div slot="preview">
{range2[0]}-{range2[1]}
<Slider max="200" step="10" bind:value={range2} range order tip="true"/>
</div>
</Example>
<JSDoc {jsdoc} showEvent="true"></JSDoc>

View File

@ -200,7 +200,7 @@
<h3 class="subtitle">Tabs</h3> <h3 class="subtitle">Tabs</h3>
<JSDoc jsdoc={jsdocTabs} showHeader={false}></JSDoc> <JSDoc jsdoc={jsdocTabs} showHeader={false} showEvent="true"></JSDoc>
<br> <br>
<br> <br>

View File

@ -0,0 +1,73 @@
<script>
import { Tip,Button } from 'svelma-pro'
import DocHeader from '../../components/DocHeader.svelte'
import Example from '../../components/Example.svelte'
let config = {
content: '<div class="tooltip">Styled tooltip text</div>',
allowHTML: true,
trigger: 'click',
onShow: function(instance) {
console.log(instance);
}
}
</script>
<style>
:global(.tooltip) {
color: orange;
font-weight: bold;
text-transform: uppercase;
}
</style>
<DocHeader title="Tip" subtitle="提示" />
<Example code={`<script>
import { Tip } from 'svelma-pro'
</script>
<Tip content="你好"></Tip>
`}>
<div slot="preview">
<Tip content="你好">
<Button type="is-link">Link</Button>
</Tip>
</div>
</Example>
<hr class="is-medium" />
<p class="title is-4">自定义设置</p>
<p class="content">更多配置请参考<a href="https://atomiks.github.io/tippyjs/">tippy</a>.</p>
<Example code={`<script>
import { Tip } from 'svelma-pro'
let config = {
content: '<div class="tooltip">Styled tooltip text</div>',
allowHTML: true,
trigger: 'click',
onShow: function(instance) {
console.log(instance);
}
</script>
<style>
:global(.tooltip) {
color: orange;
font-weight: bold;
text-transform: uppercase;
}
</style>
<Tip config="{config}">
<Button type="is-link">Link</Button>
</Tip>
`}>
<div slot="preview">
<Tip config={config}>
<Button type="is-link">Link</Button>
</Tip>
</div>
</Example>

13
package-lock.json generated
View File

@ -145,6 +145,11 @@
"integrity": "sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==", "integrity": "sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==",
"dev": true "dev": true
}, },
"@popperjs/core": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.4.0.tgz",
"integrity": "sha512-NMrDy6EWh9TPdSRiHmHH2ye1v5U0gBD7pRYwSwJvomx7Bm4GG04vu63dYiVzebLOx2obPpJugew06xVP0Nk7hA=="
},
"@semantic-release/commit-analyzer": { "@semantic-release/commit-analyzer": {
"version": "6.3.0", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-6.3.0.tgz", "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-6.3.0.tgz",
@ -10882,6 +10887,14 @@
"integrity": "sha512-Imqa6iv3Ig5FmC3ESwmqczusIn1h8D5RqNbpatGc1eLHeoytuhodbsAPpSJ8iKiLhxBtLuRsrywWHlJM1bA3Rg==", "integrity": "sha512-Imqa6iv3Ig5FmC3ESwmqczusIn1h8D5RqNbpatGc1eLHeoytuhodbsAPpSJ8iKiLhxBtLuRsrywWHlJM1bA3Rg==",
"dev": true "dev": true
}, },
"tippy.js": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.2.3.tgz",
"integrity": "sha512-MzqHMrr2C0IC8ZUnG5kLQPxonWJ7V+Usqiy2W5b+dCvAfousio0mA85h+Ea5wRq94AQGd8mbFGeciRgkP+F+7w==",
"requires": {
"@popperjs/core": "^2.3.2"
}
},
"to-object-path": { "to-object-path": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",

View File

@ -27,8 +27,11 @@
"src" "src"
], ],
"peerDependencies": { "peerDependencies": {
"bulma": "^0.8.0", "bulma": "^0.8.0"
"tinycolor2": "^1.4.1" },
"dependencies": {
"tinycolor2": "^1.4.1",
"tippy.js": "^6.2.3"
}, },
"devDependencies": { "devDependencies": {
"autoprefixer": "^9.6.0", "autoprefixer": "^9.6.0",

View File

@ -0,0 +1,138 @@
<script>
import {createEventDispatcher} from "svelte";
import Thumb from "./Thumb.svelte";
const dispatch = createEventDispatcher();
export let name = [];
export let range = false;
export let min = 0;
export let max = 100;
export let step = 1;
export let value = [min, max];
export let pos;
export let active = false;
export let order = false;
export let tip = false;
$: if (active) setValue(pos);
$: if (!active) setPos(value);
$: if (range && order && active) pos = checkPos(pos);
$: min, max, clamp();
$: progress = `
left: ${range ? Math.min(pos[0], pos[1]) * 100 : 0}%;
right: ${100 - Math.max(pos[0], (range ? pos[1] : pos[0])) * 100}%;
`;
function setValue(pos) {
const offset = min % step;
const width = max - min
value = pos
.map(v => {
const t = min + v * width;
return t;
})
.map(v => {
let r;
if(step < 1){
const digit = getDigit(step);
const num1 = Math.round((v - offset) / step);
const num2 = step;
r = formatNum(num1 * num2, digit)
}else{
r = Math.round((v - offset) / step) * step + offset;
}
return r;
});
dispatch("change", value);
}
function setPos(value) {
pos = value
.map(v => Math.min(Math.max(v, min), max))
.map(v => (v - min) / (max - min));
}
function checkPos(pos) {
return [Math.min(...pos), Math.max(...pos)];
}
function clamp() {
setPos(value);
setValue(pos);
}
function getDigit(num) {
const strs = num.toString().split(".");
if (strs[1]) {
return strs[1].length;
} else {
return 0;
}
}
function formatNum(f, digit) {
var m = Math.pow(10, digit);
return parseInt(f * m, 10) / m;
}
</script>
<style>
input {
display: none;
}
.track {
margin: 16px 8px;
position: relative;
height: 4px;
width: calc(100% - 16px);
border-radius: 100vh;
background: var(--track-background, #ebebeb);
}
.progress-sli {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
border-radius: 100vh;
background: var(--progress-background, #8abdff);
}
.thumb {
width: 16px;
height: 16px;
border-radius: 100vh;
background: var(--thumb-background, #5784fd);
}
</style>
<input type="number" value={value[0]} name={name[0]}/>
{#if range}
<input type="number" value={value[1]} name={name[1]}/>
{/if}
<div class="track">
<div
class="progress-sli"
style={progress}/>
<Thumb bind:pos={pos[0]} on:active={({ detail: v }) => active = v} value={value[0]} {tip}>
<slot name="left">
<slot>
<div class="thumb"/>
</slot>
</slot>
</Thumb>
{#if range}
<Thumb bind:pos={pos[1]} on:active={({ detail: v }) => active = v} value={value[1]} {tip}>
<slot name="right">
<slot>
<div class="thumb"/>
</slot>
</slot>
</Thumb>
{/if}
</div>

View File

@ -0,0 +1,88 @@
<script>
import {createEventDispatcher, onMount} from "svelte";
import handle from "./index.js";
const dispatch = createEventDispatcher();
let active;
export let pos;
export let value;
export let tip;
function dragstart() {
active = true;
dispatch('active', true)
}
function dragend() {
active = false;
dispatch('active', false);
}
</script>
<style>
.sli-tip {
position: absolute;
bottom: 0px;
background: #000;
padding: 5px;
border-radius: 5px;
color: #fff;
transform: translate(-50%, -50%);
}
.sli-tip::after {
content: "";
width: 0;
bottom: -10px;
left: calc(50% - 5px);
position: absolute;
height: 0;
border: 5px solid transparent;
border-top-color: #000;
}
.thumb {
position: absolute;
top: 50%;
width: 0;
height: 0;
}
.thumb-content {
position: relative;
width: fit-content;
height: fit-content;
transform: translate(-50%, -50%);
}
.thumb-content::before {
content: "";
position: absolute;
width: 200%;
height: 200%;
transform: translate(-25%, -25%) scale(0);
border-radius: 100vh;
background: var(--thumb-background, #5784fd);
opacity: 30%;
transition: transform 100ms ease-in-out;
}
.thumb-content.active::before {
transform: translate(-25%, -25%) scale(1);
}
</style>
<div class="thumb"
style={`left: ${pos * 100}%;`}
use:handle
on:dragstart={dragstart}
on:dragend={dragend}
on:drag={({ detail: v }) => (pos = v)}>
{#if tip}
<div class="sli-tip">{value}</div>
{/if}
<div class="thumb-content" class:active>
<slot/>
</div>
</div>

View File

@ -0,0 +1,47 @@
export default function handle(node) {
const onDown = getOnDown(node);
node.addEventListener("touchstart", onDown);
node.addEventListener("mousedown", onDown);
return {
destroy() {
node.removeEventListener("touchstart", onDown);
node.removeEventListener("mousedown", onDown);
}
};
}
function getOnDown(node) {
const onMove = getOnMove(node);
return function (e) {
e.preventDefault();
node.dispatchEvent(new CustomEvent("dragstart"));
const moveevent = "touches" in e ? "touchmove" : "mousemove";
const upevent = "touches" in e ? "touchend" : "mouseup";
document.addEventListener(moveevent, onMove);
document.addEventListener(upevent, onUp);
function onUp(e) {
e.stopPropagation();
document.removeEventListener(moveevent, onMove);
document.removeEventListener(upevent, onUp);
node.dispatchEvent(new CustomEvent("dragend"));
};
};
}
function getOnMove(node) {
const track = node.parentNode;
return function (e) {
const { left, width } = track.getBoundingClientRect();
const clickOffset = "touches" in e ? e.touches[0].clientX : e.clientX;
const clickPos = Math.min(Math.max((clickOffset - left) / width, 0), 1) || 0;
node.dispatchEvent(new CustomEvent("drag", { detail: clickPos }));
};
}

View File

@ -0,0 +1,24 @@
<script>
import tippy from './index';
export let content = 'Tip';
export let config = {
content: content
};
</script>
<style>
span {
padding: 16px;
border-radius: 5px;
background-color: blue;
color: white;
cursor: pointer;
}
</style>
<div use:tippy={config} style="display: inline-block">
<slot>
<span class="button is-primary">no elements</span>
</slot>
</div>

View File

@ -0,0 +1,5 @@
import tippy from 'tippy.js';
export default function(node, props) {
tippy(node, props);
}

View File

@ -26,13 +26,15 @@ import {
import Pagination from './components/Pagination/Pagination.svelte' import Pagination from './components/Pagination/Pagination.svelte'
import Datepicker from './components/DatePicker/Datepicker.svelte' import Datepicker from './components/DatePicker/Datepicker.svelte'
import Timepicker from './components/Timepicker/TimePicker.svelte' import Timepicker from './components/Timepicker/TimePicker.svelte'
import Carousel from './components/carousel/Carousel.svelte' import Carousel from './components/Carousel/Carousel.svelte'
import { import {
Nav, Nav,
NavItem, NavItem,
NavLayout NavLayout
} from './components/Nav' } from './components/Nav'
import ColorPicker from './components/ColorPicker/ColorPicker.svelte'; import ColorPicker from './components/ColorPicker/ColorPicker.svelte';
import Tip from './components/Tip/Tip.svelte'
import Slider from './components/Slider/Slider.svelte';
export { export {
Button, Button,
@ -62,7 +64,9 @@ export {
Nav, Nav,
NavItem, NavItem,
NavLayout, NavLayout,
ColorPicker ColorPicker,
Tip,
Slider
} }
export const Svelma = { export const Svelma = {
@ -93,5 +97,7 @@ export const Svelma = {
Nav, Nav,
NavItem, NavItem,
NavLayout, NavLayout,
ColorPicker ColorPicker,
Tip,
Slider
} }

87
src/style/tippy.scss Normal file
View File

@ -0,0 +1,87 @@
.tippy-box[data-animation=fade][data-state=hidden] {
opacity: 0
}
[data-tippy-root] {
max-width: calc(100vw - 10px)
}
.tippy-box {
position: relative;
background-color: #333;
color: #fff;
border-radius: 4px;
font-size: 14px;
line-height: 1.4;
outline: 0;
transition-property: transform, visibility, opacity
}
.tippy-box[data-placement^=top]>.tippy-arrow {
bottom: 0
}
.tippy-box[data-placement^=top]>.tippy-arrow:before {
bottom: -7px;
left: 0;
border-width: 8px 8px 0;
border-top-color: initial;
transform-origin: center top
}
.tippy-box[data-placement^=bottom]>.tippy-arrow {
top: 0
}
.tippy-box[data-placement^=bottom]>.tippy-arrow:before {
top: -7px;
left: 0;
border-width: 0 8px 8px;
border-bottom-color: initial;
transform-origin: center bottom
}
.tippy-box[data-placement^=left]>.tippy-arrow {
right: 0
}
.tippy-box[data-placement^=left]>.tippy-arrow:before {
border-width: 8px 0 8px 8px;
border-left-color: initial;
right: -7px;
transform-origin: center left
}
.tippy-box[data-placement^=right]>.tippy-arrow {
left: 0
}
.tippy-box[data-placement^=right]>.tippy-arrow:before {
left: -7px;
border-width: 8px 8px 8px 0;
border-right-color: initial;
transform-origin: center right
}
.tippy-box[data-inertia][data-state=visible] {
transition-timing-function: cubic-bezier(.54, 1.5, .38, 1.11)
}
.tippy-arrow {
width: 16px;
height: 16px;
color: #333
}
.tippy-arrow:before {
content: "";
position: absolute;
border-color: transparent;
border-style: solid
}
.tippy-content {
position: relative;
padding: 5px 9px;
z-index: 1
}

View File

@ -526,3 +526,76 @@ button.swiper-pagination-bullet {
z-index: 0; z-index: 0;
-webkit-backface-visibility: hidden; -webkit-backface-visibility: hidden;
backface-visibility: hidden; } backface-visibility: hidden; }
.tippy-box[data-animation=fade][data-state=hidden] {
opacity: 0; }
[data-tippy-root] {
max-width: calc(100vw - 10px); }
.tippy-box {
position: relative;
background-color: #333;
color: #fff;
border-radius: 4px;
font-size: 14px;
line-height: 1.4;
outline: 0;
transition-property: transform, visibility, opacity; }
.tippy-box[data-placement^=top] > .tippy-arrow {
bottom: 0; }
.tippy-box[data-placement^=top] > .tippy-arrow:before {
bottom: -7px;
left: 0;
border-width: 8px 8px 0;
border-top-color: initial;
transform-origin: center top; }
.tippy-box[data-placement^=bottom] > .tippy-arrow {
top: 0; }
.tippy-box[data-placement^=bottom] > .tippy-arrow:before {
top: -7px;
left: 0;
border-width: 0 8px 8px;
border-bottom-color: initial;
transform-origin: center bottom; }
.tippy-box[data-placement^=left] > .tippy-arrow {
right: 0; }
.tippy-box[data-placement^=left] > .tippy-arrow:before {
border-width: 8px 0 8px 8px;
border-left-color: initial;
right: -7px;
transform-origin: center left; }
.tippy-box[data-placement^=right] > .tippy-arrow {
left: 0; }
.tippy-box[data-placement^=right] > .tippy-arrow:before {
left: -7px;
border-width: 8px 8px 8px 0;
border-right-color: initial;
transform-origin: center right; }
.tippy-box[data-inertia][data-state=visible] {
transition-timing-function: cubic-bezier(0.54, 1.5, 0.38, 1.11); }
.tippy-arrow {
width: 16px;
height: 16px;
color: #333; }
.tippy-arrow:before {
content: "";
position: absolute;
border-color: transparent;
border-style: solid; }
.tippy-content {
position: relative;
padding: 5px 9px;
z-index: 1; }

View File

@ -1 +1,2 @@
@import 'style/carousel.scss'; @import 'style/carousel.scss';
@import 'style/tippy.scss';