Giter Site home page Giter Site logo

untemps / svelte-palette Goto Github PK

View Code? Open in Web Editor NEW
29.0 3.0 3.0 1.83 MB

Svelte component to display a customisable color picker

Home Page: https://svelte-palette.vercel.app

JavaScript 35.38% CSS 0.09% HTML 0.54% Svelte 63.76% Shell 0.23%
palette color color-picker svelte svelte-components component javascript eye-dropper

svelte-palette's Introduction

svelte-palette

Svelte component to display a customisable color picker


npm GitHub Workflow Status Codecov

Demo

🔴 LIVE DEMO :red_circle:

Installation

yarn add @untemps/svelte-palette

Usage

Basic Usage

<script>
    import { Palette } from '@untemps/svelte-palette'

    const colors = [
		'#865C54',
		'#8F5447',
		'#A65846',
		'#A9715E',
		'#AD8C72',
    ]

	let bgColor = colors[0]
</script>

<main style="--bgColor:{bgColor}">
	<Palette {colors} on:select={({ detail: { color } }) => (bgColor = color)} />
</main>

<style>
	main {
		display: flex;
		align-items: center;
		justify-content: center;
		height: 100%;
		background-color: var(--bgColor);
	}

API

Props Type Default Description
colors string[] or Promise<string[]> or object[] or Promise<object[]> [] Array of colors to be displayed in the palette. See more about colors in the Colors Setting section
selectedColor string null Default selected color. The color must be included in the colors prop.
isCompact boolean false Flag to display the palette in compact mode.
compactColorIndices number[] [] Array of indices to pick from the colors array to be displayed in the compacted palette (see Compact Mode).
allowDuplicates boolean false Flag to allow color duplication.
deletionMode string "none" Mode of slot deletion, between "none" and "tooltip" and "drop" (see Deletion Modes).
tooltipClassName string null Class name to pass down to the deletion tooltip (see Styles).
tooltipContentSelector string null Selector of the deletion tooltip content (see Customize the Content of the Deletion Tooltip).
showTransparentSlot boolean false Flag to display a transparent slot at the start of the slot list.
maxColors number 30 Maximum number of slots to be displayed in the palette. Set this value to -1 to allow infinite number of slots.
showInput boolean false Flag to display the input within the footer slot.
inputType string "text" Type of the input within the footer slot. Only "text" and "color" are allowed. All other value will be replaced by "text".
numColumns number 5 Number of columns of the palette grid. This value can't exceed the number of maximum colors defined in maxColors and can't be lower than 1. Set this value to 0 to display the slots on a single row.
transition object null Animation when a slot is rendered (see Transition).

Events

Event Arguments Type Description
select Dispatched whenever a color is clicked.
color string Selected color string.

Slots

Slot Description Available Properties Props
header Allow to add a header to the palette. By default, it is empty. selectedColor
footer Allow to add a footer to the palette. By default, it contains an input to add colors. selectedColor
slot Allow to replace the default color slots. index, color, colorName, selectedColor, transition, isCompact
transparent_slot Allow to replace the default transparent slot. -
before_slot Allow to add an element before the color slots. selectedColor, transition, isCompact
after_slot Allow to add an element after the color slots. selectedColor, transition, isCompact
input Allow to replace the input in the footer if the default footer slot is kept as it is. selectedColor, inputType
settings Allow to replace the settings panel. See the demo to grab a usage example. onClose
tools Allow to replace the tools panel. isCompact, compactColorIndices, onSelect
loader Allow to replace the loader displayed during the colors async retrieving. -

Example

<script>
	import { Palette } from '@untemps/svelte-palette'

	const colors = ['#865C54', '#8F5447', '#A65846', '#A9715E', '#AD8C72']
</script>

<Palette {colors}>
	<div slot="header" class="palette__header">
		<h1>Pick a color</h1>
	</div>
	<button let:color slot="slot" class="palette__slot" style="--color:{color}" />
	<div slot="footer" class="palette__footer">
		<a href="https://www.untemps.net">@untemps</a>
	</div>
</Palette>

<style>
	.palette__header {
		display: flex;
		justify-content: center;
	}

	.palette__slot {
		cursor: pointer;
		width: 2rem;
		height: 2rem;
		margin: 0;
		background-color: var(--color);
		border-radius: 20%;
		border: 1px solid rgba(0, 0, 0, 0.2);
		box-shadow: 0.1rem 0.1rem 0.3rem rgba(0, 0, 0, 0.2);
	}

	.palette__footer {
		display: flex;
		justify-content: center;
		padding: 0.5rem;
	}
</style>

Colors Setting

Color can be set in several formats:

Array of Color Strings

colors = ['#865C54', '#8F5447', '#A65846']

Array of Color Objects

colors = [
	{ name: 'Color #1', value: '#865C54' },
	{ name: 'Color #2', value: '#8F5447' },
	{ value: '#A65846' }
]

Promise

A promise to be resolved with an array of color strings or objects can be passed as well (see Use an API to fill the palette)

Deletion Modes

The deletionMode prop allows to define the way users can delete (or not) the color slots:

Value Description
none (Default) Color slots cannot be deleted
tooltip A tooltip is displayed when hovering a color slot, a click within deletes the slot
(You can control tooltip display though the tooltipClassName and tooltipContentSelector props)
drop Colors slots are draggable, a drop outside the palette deletes the slot

As an helper, deletion mode enums are exported in PaletteDeletionMode.

Compact Mode

The compact mode is a way to display a minimal version of the palette with a restricted selection of the original colors and downsized spaces.

The compactColorIndices prop allows to define the list of the colors to be picked from the colors array by their indices.
If set a control is added to toggle the compact mode.

You may also specified whether the palette has to use the compact mode by default by setting isCompact=true.

<script>
	import { Palette } from '@untemps/svelte-palette'

	const colors = ['#865C54', '#8F5447', '#A65846', '#A9715E', '#AD8C72']
	const compactColorIndices = [1, 3, 4]
</script>

<Palette {colors} {compactColorIndices} />

Styles

Root Tag Class

You can style the component by passing a class down to the root tag (div).

  • Flag the class as global to make it available in the Palette component
  • Prefix your class with .palette[role="main"] to give precedence over the default one or mark each style with !important (not recommanded)

Example

<script>
	import { Palette } from '@untemps/svelte-palette'

	const colors = ['#865C54', '#8F5447', '#A65846', '#A9715E', '#AD8C72']
</script>

<Palette {colors} class="palette__custom" />

<style>
	:global(.palette[role='main'].palette__custom) {
		background: yellow;
	}
</style>

Deletion Tooltip Class

If you set deletionMode to "tooltip", you can pass a class name that is set to the tooltip shown when hovering a slot.

To do so, set a global class name to the tooltipClassName prop.

As the tooltip is interactive, make sure you define a sufficient hover area that allow to access the content of the tooltip before the leave event is triggered.

If you ignore that prop, a default class is used.

Please note that the default class name is __tooltip__default.
Provide a different class name otherwise the default class would have the precedence over the custom one.

Example

<script>
	import { Palette } from '@untemps/svelte-palette'

	const colors = ['#865C54', '#8F5447', '#A65846', '#A9715E', '#AD8C72']
</script>

<Palette {colors} deletionMode="tooltip" tooltipClassName="tooltip" />

<style>
	:global(.tooltip) {
		position: absolute;
		z-index: 9999;
		max-width: 120px;
		background-color: black;
		color: #fff;
		text-align: center;
		border-radius: 6px;
		padding: 0.5rem;
	}
</style>

EyeDropper API Support

If supported by the browser, the default component within the input slot displays a button to trigger the Web EyeDropper API.
The tool allows to pick a color from the screen.

eyedropper

Once selected, the color is inserted in the input waiting for the user to submit and adding it to the palette.

If the API is not available, nothing will be rendered.

The PaletteEyeDropper component can be used on its own anywhere within a slot or in an external component as it is exported from this lib.

Transition

svelte-palette-transition

You can customize the way slots appear into the palette by using the transition prop.

This prop works the same way as the in/out directive and accepts an object with two properties :

Value Description
fn Transition function (See Svelte Transitions)
args Parameters to pass to the transition function

fn may be one of the Svelte exported functions or a custom one as described in the docs.

Example

<script>
	import { Palette } from '@untemps/svelte-palette'
	import { elasticOut } from 'svelte/easing'

	const colors = ['#865C54', '#8F5447', '#A65846', '#A9715E', '#AD8C72']

	const whoosh = (node, params) => {
		const existingTransform = getComputedStyle(node).transform.replace('none', '')

		return {
			delay: params.delay || 0,
			duration: params.duration || 400,
			easing: params.easing || elasticOut,
			css: (t, u) => `transform: ${existingTransform} scale(${t})`,
		}
	}
</script>

<Palette {colors} transition={{ fn: whoosh, args: { duration: 3000 } }} />

Recipes

Use an API to Fill the Palette

In case you want to call an API to fetch the palette colors, you may pass a promise to the colors prop.

The component displays a customizable loader waiting to the promise to be resolved. Be aware that the result of the promise must be an array of color strings as well.

Example

<script>
	import { Palette } from '@untemps/svelte-palette'

	const colors = fetch('https://www.colr.org/json/colors/random/30')
		.then((result) => result.json())
		.then((result) => result.colors.filter((c) => c.hex?.length).map((c) => `#${c.hex}`))
</script>

<Palette {colors}>
	<p slot="loader">Loading...</p>
</Palette>

Customize the Content of the Deletion Tooltip

By default, if deletionMode is set to "tooltip", the tooltip displays a trash icon:

trash

You may want to display a different content for various purposes.
That is possible by defining a DOM element selector to the tooltipContentSelector prop.

Note the piece of DOM used ad content is deeply cloned using cloneNode() before appending to the tooltip container.
That means the original element stays as it is but depending on element some props or behaviours may be removed from the clone.

Example

<script>
	import { Palette } from '@untemps/svelte-palette'

	const colors = ['#865C54', '#8F5447', '#A65846', '#A9715E', '#AD8C72']
</script>

<Palette {colors} deletionMode="tooltip" tooltipContentSelector=".palette__tooltip__button" />

<!-- The element used as tooltip content -->
<button class="palette__tooltip__button">Delete</button>

Use a Color Input

By default, the input that allows to add a new slot in the palette is typed as "text".

Although you may use the ìnput slot to display a custom component, it is possible to turn the input into color mode by setting the inputType prop to "color".
That unlocks the color picker provided by the browser. Therefore the color spot and the eyedropper are hidden.

input color

Example

<script>
	import { Palette } from '@untemps/svelte-palette'

	const colors = ['#865C54', '#8F5447', '#A65846', '#A9715E', '#AD8C72']
</script>

<Palette {colors} inputType="color" />

Customize the Tools Panel

The tools panel is a container for two actions:

  • Display the settings panel ("settings")
  • Toggle the compact mode ("compact")

For some use cases, you may want to provide your own controls by using the tools slot.

To access each tool behaviours, the Palette component exports a onSelect function that has to be called with the name of the tool (use the enums from the exported PaletteTool).

Example

<script>
	import { Palette, PaletteTool } from '@untemps/svelte-palette'

	const colors = ['#865C54', '#8F5447', '#A65846', '#A9715E', '#AD8C72']
</script>

<Palette {colors}>
	<div slot="tools" let:onSelect let:isCompact>
		<button on:click="{onSelect(PaletteTool.SETTINGS)}">Settings</button>
		<button on:click="{onSelect(PaletteTool.COMPACT)}">{isCompact ? 'Expand' : 'Compact'}</button>
	</div>
</Palette>

Development

The component can be served for development purpose on http://localhost:5173/ running:

yarn dev

Contributing

Contributions are warmly welcomed:

  • Fork the repository
  • Create a feature branch
  • Develop the feature AND write the tests (or write the tests AND develop the feature)
  • Commit your changes using Angular Git Commit Guidelines
  • Submit a Pull Request

svelte-palette's People

Contributors

dependabot[bot] avatar semantic-release-bot avatar untemps avatar vincent-zelty avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

svelte-palette's Issues

Feature Request: Add a way to limit the number of columns

For some use cases, it would be nice to set a prop limiting the number of columns to be displayed.
This would be applicable when numColumns is lower or equal to 0 only, because, in this case, adding a slot of color always creates a new column. If maxColumns is set, slots would wrap on an additional row when the limit is reached.

Expose an inputType prop to set the type of input "text" or "color"

We may propose a new prop to the Palette and PaletteInput components : inputType with two values text or color.
The value of the prop would be directly passed down to the HTML input component.

We may have to amend the style of the input depending of the value.
We may also hide the slot on the left side of the input if inputType === 'color' since in most browsers the selected color is displayed within the input.
The eyedropper tool may be hidden as well.

Delete slots by index instead of color value

Slots are currently deleted checking the value of the selected slot. That means if several slots have the same value, each of them is removed.
Instead it would be better to reference slots by color and delete the slot a the selected index only.

Support Svelte 4

Hello, can you update the dependencies to use Svelte 4 please.
Thanks

SyntaxError: Named export 'extractByIndices' not found. The requested module '@untemps/utils/array/extractByIndices' is a CommonJS module, which may not support all module.exports as named exports.

Hi Vincent,

Thanks for the great lib, it saved me a few days of work.

I have this error when automatically deploying on Vercel (through git push). I don't have issues when compiling locally, I'm not sure about the cause of it as I can't reproduce it, and I can't access the generated files by Vercel. So I thought, you might have some ideas:

import { extractByIndices } from "@untemps/utils/array/extractByIndices";
^^^^^^^^^^^^^^^^
SyntaxError: Named export 'extractByIndices' not found. The requested module '@untemps/utils/array/extractByIndices' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from '@untemps/utils/array/extractByIndices';
const { extractByIndices } = pkg;

at ModuleJob._instantiate (node:internal/modules/esm/module_job:124:21)
at async ModuleJob.run (node:internal/modules/esm/module_job:190:5)

Implement EyeDropper API

The JS EyeDropper API is still experimental yet available on some browsers (Chrome, Edge and Opera).
We should propose a new button to open an EyeDropper and allow the user to select a color. This color would be inserted in the input. The user then would have to click the Add button to create a new slot in the palette.
If the API is not available, the button would not be displayed.

As the input is involved in the process, we should add the EyeDropper button inside the PaletteInput component. Embedding the button in a PaletteEyeDropper component and exporting it would allow to use it without the input.

API PaletteEyeDropper component proposal:

  • add: (color) => {} Event dispatched when a color is selected
  • buttonAriaLabel: string Aria label of the button

Feature Request: Allow to customize features

Add a settings section to customize the kind of features we went for the palette.
We may embed some of the configuration parameters we've got in the demo app :

  • Color Preselection
  • Input Type
  • Number of Columns
  • Maximum Number of Colors
  • Duplicates Permission
  • Deletion Mode
  • Transparent Slot Display
  • Compact Mode Display

The access to the settings section may be set behind a prop flag showSettings

Slots don't get any values

Current component values are not fully passed to slots. That means we cannot use them in custom renderings.

Slot List:

  • header : selectedColor
  • header-divider : none
  • footer : selectedColor
  • footer-divider : none
  • slot : color, selectedColor, transition, isCompact
  • transparent-slot : none
  • input : selectedColor, inputType

We may take advantage of this issue to do few more things:

  • Add a slot for the compact toggle button
  • Prevent from displaying the header in compact mode

Implement drag and drop out to delete a color slot

It might be convenient to implement an alternative way to delete a color slot.
The palette displays a tooltip on slot hover when allowDeletion is set to true so far. However, we may propose a deletionMode prop, with two values: tooltip and drop, in replacement of allowDeletion, which, when set, allows to delete a slot the way we defined it.

API proposal:

`deletionMode`: "tooltip" | "drop" | "none" (default)

Upgrade Node version for GitHub Actions

GitHub Actions runs deprecated packages as they're based on Node 16 which is deprecated itself:

  • actions/checkout
  • actions/setup-node
  • codecov/codecov-action

We should upgrade those packages to the last version (v4 for all).

Compacted view of the palette

Add a way to toggle from the original palette to a compacted version with a restricted selection of colors and minimal spaces.
Th API should allow to choose the colors that are part of the compacted palette.
No clue where to embed the toggling control. May be at the very first position of the slot list or into the footer.

Feature Request: Name color slots

Colors are currently defined with a hexa-string through the colors prop.
For some specific use cases, it might be useful to assign a name to each color slot.

For the technical part, this means that the colors props should change to accept an array of colors as an object:

[
   { name: 'Foo', value: '#ff0000' },
   { name: 'Bar', value: '#ff0000' },
   { name: 'Gag', value: '#ff0000' }
]

For some backward compatibility purposes, the legacy color format should still be supported.

In addition, when using the color input, a new field should allow to name the new color.

The color name must be exposed by the color slot slot API to be used for customization.

Issues to address:

  • Duplication: Can two colors be named the same?

Feature Request: Add a way to manage groups of colors

Sometimes, it would be useful to split the slots of colors into groups, to create collections for instance.
The colors prop could be set with an array of specific object, like so:

const colors = [
  { name: 'Yellows', colors: ['#ff0', '#0f0', '#0ff'] }
]

Expected issues:

  • The compactColorIndices is not relevant for groups of colors: what indices of are they?
  • It might be difficult to mix single colors and group of colors: a choice in the API has to be made, to restrict to one type of entry for example

Replace util function by @untemps/utils

The component uses a internal util function to resolve classnames based on condition checks.
The @untemps/utils package contains such a function. We can replace the internal one by the one included in the external package.

Feature Request: Typescript Support

Need declaration file for types

Could not find a declaration file for module '@untemps/svelte-palette'. 'C:/drewbitt/GitHub/project/node_modules/.pnpm/@[email protected]/node_modules/@untemps/svelte-palette/dist/index.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/untemps__svelte-palette` if it exists or add a new declaration (.d.ts) file containing `declare module '@untemps/svelte-palette';`ts(7016)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.