Initial commit

This commit is contained in:
Igor Martyniuk
2026-06-25 15:53:49 +03:00
commit 1bb0b7e68a
1517 changed files with 218469 additions and 0 deletions
@@ -0,0 +1,5 @@
*.md
node_modules
dist
public
build
@@ -0,0 +1,13 @@
{
"printWidth": 100,
"tabWidth": 2,
"endOfLine": "lf",
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"semi": true,
"bracketSameLine": false,
"jsxSingleQuote": false,
"arrowParens": "always",
"importOrderCaseSensitive": false
}
+118
View File
@@ -0,0 +1,118 @@
# [←](../../../docs/README.md) @crux-kit/keenicon
Документація для Keenicon реакт компонентів/іконок.
Компонент KeenIcon — це універсальний інструмент для відображення іконок системи KeenIcon. Він підтримує різні стилі (filled, outline, solid, duotone)
Приклади іконок знаходяться у:
`/assets/duotone/demo.html`
`/assets/filled/demo.html`
`/assets/outline/demo.html`
`/assets/solid/demo.html`
### 1. Підключення стилів
Перед використанням компонента потрібно імпортувати основні стилі іконок у головний файл (наприклад, app.tsx або main.tsx)
```jsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import '@crux-kit/keenicon/assets/styles.css'
import "./css/styles.css";
import { App } from "./app";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>
);
```
### 2. Використання компонента
Щоб використати іконку `ki-abstract-1` потрібно передали в проп `icon` значкення `abstract-1`.
Щоб змінити колір іконки потрібно передати у `className` класс з проп `color: red`.
```css
/*
Tailwind
className="text-destructive"
*/
.text-destructive {
color: red;
}
```
```jsx
import { KeenIcon } from './components/KeenIcon';
const MyComponent = () => {
return (
<div>
<KeenIcon icon="abstract-1" />
<KeenIcon icon="abstract-1" variant="outline" size="2x" className="text-primary" />
<KeenIcon icon="abstract-1" variant="duotone" className="text-destructive" size="5x" />
</div>
);
};
```
### 3. Налаштування
| Prop | Тип | За замовчуванням | Опис |
| --------- | --------------- | ---------------- | --------------------------------------------------------- |
| icon | string | Обов'язково | Назва іконки (наприклад, magnifier, abstract-1) |
| variant | KeeniconVariant | 'filled' | Стиль іконки: duotone, filled, solid, outline |
| size | KeeniconSize | 'md' | Розмір іконки від 12px до 64px |
| className | string | '' | Додаткові CSS-класи (наприклад, для кольору чи відступів) |
### 4. Розмірна сітка
```css
.ki-xs {
font-size: 12px;
}
.ki-sm {
font-size: 14px;
}
.ki-md {
font-size: 16px;
}
.ki-lg {
font-size: 18px;
}
.ki-xl {
font-size: 20px;
}
.ki-2x {
font-size: 24px;
}
.ki-3x {
font-size: 32px;
}
.ki-4x {
font-size: 48px;
}
.ki-5x {
font-size: 64px;
}
.ki-6x {
font-size: 80px;
}
```
### 5. Використання у HTML
```html
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/assets/keenicons/styles.css" />
</head>
<body>
<i class="ki-solid ki-abstract-2 ki-xs" />
</body>
</html>
```
+17
View File
@@ -0,0 +1,17 @@
{
"name": "@crux-kit/keenicon",
"version": "1.0.0",
"type": "module",
"types": "src/index.ts",
"private": true,
"exports": {
".": "./src/index.ts",
"./assets/styles.css": "./assets/styles.css"
},
"style": "assets/styles.css",
"scripts": {
"format": "prettier --write ."
},
"dependencies": {},
"devDependencies": {}
}
@@ -0,0 +1,7 @@
Open *demo.html* to see a list of all the glyphs in your font along with their codes/ligatures.
To use the generated font in desktop programs, you can install the TTF font. In order to copy the character associated with each icon, refer to the text box at the bottom right corner of each glyph in demo.html. The character inside this text box may be invisible; but it can still be copied. See this guide for more info: https://icomoon.io/docs/#local-fonts
You won't need any of the files located under the *demo-files* directory when including the generated font in your own projects.
You can import *selection.json* back to the IcoMoon app using the *Import Icons* button (or via Main Menu → Manage Projects) to retrieve your icon selection.
@@ -0,0 +1,160 @@
body {
padding: 0;
margin: 0;
font-family: sans-serif;
font-size: 1em;
line-height: 1.5;
color: #555;
background: #fff;
}
h1 {
font-size: 1.5em;
font-weight: normal;
}
small {
font-size: 0.66666667em;
}
a {
color: #e74c3c;
text-decoration: none;
}
a:hover,
a:focus {
box-shadow: 0 1px #e74c3c;
}
.bshadow0,
input {
box-shadow: inset 0 -2px #e7e7e7;
}
input:hover {
box-shadow: inset 0 -2px #ccc;
}
input,
fieldset {
font-family: sans-serif;
font-size: 1em;
margin: 0;
padding: 0;
border: 0;
}
input {
color: inherit;
line-height: 1.5;
height: 1.5em;
padding: 0.25em 0;
}
input:focus {
outline: none;
box-shadow: inset 0 -2px #449fdb;
}
.glyph {
font-size: 16px;
width: 15em;
padding-bottom: 1em;
margin-right: 4em;
margin-bottom: 1em;
float: left;
overflow: hidden;
}
.liga {
width: 80%;
width: calc(100% - 2.5em);
}
.talign-right {
text-align: right;
}
.talign-center {
text-align: center;
}
.bgc1 {
background: #f1f1f1;
}
.fgc1 {
color: #999;
}
.fgc0 {
color: #000;
}
p {
margin-top: 1em;
margin-bottom: 1em;
}
.mvm {
margin-top: 0.75em;
margin-bottom: 0.75em;
}
.mtn {
margin-top: 0;
}
.mtl,
.mal {
margin-top: 1.5em;
}
.mbl,
.mal {
margin-bottom: 1.5em;
}
.mal,
.mhl {
margin-left: 1.5em;
margin-right: 1.5em;
}
.mhmm {
margin-left: 1em;
margin-right: 1em;
}
.mls {
margin-left: 0.25em;
}
.ptl {
padding-top: 1.5em;
}
.pbs,
.pvs {
padding-bottom: 0.25em;
}
.pvs,
.pts {
padding-top: 0.25em;
}
.unit {
float: left;
}
.unitRight {
float: right;
}
.size1of2 {
width: 50%;
}
.size1of1 {
width: 100%;
}
.clearfix:before,
.clearfix:after {
content: ' ';
display: table;
}
.clearfix:after {
clear: both;
}
.hidden-true {
display: none;
}
.textbox0 {
width: 3em;
background: #f1f1f1;
padding: 0.25em 0.5em;
line-height: 1.5;
height: 1.5em;
}
#testDrive {
display: block;
padding-top: 24px;
line-height: 1.5;
}
.fs0 {
font-size: 16px;
}
.fs1 {
font-size: 24px;
}
@@ -0,0 +1,29 @@
if (!('boxShadow' in document.body.style)) {
document.body.setAttribute('class', 'noBoxShadow');
}
document.body.addEventListener('click', function (e) {
var target = e.target;
if (target.tagName === 'INPUT' && target.getAttribute('class').indexOf('liga') === -1) {
target.select();
}
});
(function () {
var fontSize = document.getElementById('fontSize'),
testDrive = document.getElementById('testDrive'),
testText = document.getElementById('testText');
function updateTest() {
testDrive.innerHTML = testText.value || String.fromCharCode(160);
if (window.icomoonLiga) {
window.icomoonLiga(testDrive);
}
}
function updateSize() {
testDrive.style.fontSize = fontSize.value + 'px';
}
fontSize.addEventListener('change', updateSize, false);
testText.addEventListener('input', updateTest, false);
testText.addEventListener('change', updateTest, false);
updateSize();
})();
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 803 KiB

File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
Open *demo.html* to see a list of all the glyphs in your font along with their codes/ligatures.
To use the generated font in desktop programs, you can install the TTF font. In order to copy the character associated with each icon, refer to the text box at the bottom right corner of each glyph in demo.html. The character inside this text box may be invisible; but it can still be copied. See this guide for more info: https://icomoon.io/docs/#local-fonts
You won't need any of the files located under the *demo-files* directory when including the generated font in your own projects.
You can import *selection.json* back to the IcoMoon app using the *Import Icons* button (or via Main Menu → Manage Projects) to retrieve your icon selection.
@@ -0,0 +1,160 @@
body {
padding: 0;
margin: 0;
font-family: sans-serif;
font-size: 1em;
line-height: 1.5;
color: #555;
background: #fff;
}
h1 {
font-size: 1.5em;
font-weight: normal;
}
small {
font-size: 0.66666667em;
}
a {
color: #e74c3c;
text-decoration: none;
}
a:hover,
a:focus {
box-shadow: 0 1px #e74c3c;
}
.bshadow0,
input {
box-shadow: inset 0 -2px #e7e7e7;
}
input:hover {
box-shadow: inset 0 -2px #ccc;
}
input,
fieldset {
font-family: sans-serif;
font-size: 1em;
margin: 0;
padding: 0;
border: 0;
}
input {
color: inherit;
line-height: 1.5;
height: 1.5em;
padding: 0.25em 0;
}
input:focus {
outline: none;
box-shadow: inset 0 -2px #449fdb;
}
.glyph {
font-size: 16px;
width: 15em;
padding-bottom: 1em;
margin-right: 4em;
margin-bottom: 1em;
float: left;
overflow: hidden;
}
.liga {
width: 80%;
width: calc(100% - 2.5em);
}
.talign-right {
text-align: right;
}
.talign-center {
text-align: center;
}
.bgc1 {
background: #f1f1f1;
}
.fgc1 {
color: #999;
}
.fgc0 {
color: #000;
}
p {
margin-top: 1em;
margin-bottom: 1em;
}
.mvm {
margin-top: 0.75em;
margin-bottom: 0.75em;
}
.mtn {
margin-top: 0;
}
.mtl,
.mal {
margin-top: 1.5em;
}
.mbl,
.mal {
margin-bottom: 1.5em;
}
.mal,
.mhl {
margin-left: 1.5em;
margin-right: 1.5em;
}
.mhmm {
margin-left: 1em;
margin-right: 1em;
}
.mls {
margin-left: 0.25em;
}
.ptl {
padding-top: 1.5em;
}
.pbs,
.pvs {
padding-bottom: 0.25em;
}
.pvs,
.pts {
padding-top: 0.25em;
}
.unit {
float: left;
}
.unitRight {
float: right;
}
.size1of2 {
width: 50%;
}
.size1of1 {
width: 100%;
}
.clearfix:before,
.clearfix:after {
content: ' ';
display: table;
}
.clearfix:after {
clear: both;
}
.hidden-true {
display: none;
}
.textbox0 {
width: 3em;
background: #f1f1f1;
padding: 0.25em 0.5em;
line-height: 1.5;
height: 1.5em;
}
#testDrive {
display: block;
padding-top: 24px;
line-height: 1.5;
}
.fs0 {
font-size: 16px;
}
.fs1 {
font-size: 24px;
}
@@ -0,0 +1,29 @@
if (!('boxShadow' in document.body.style)) {
document.body.setAttribute('class', 'noBoxShadow');
}
document.body.addEventListener('click', function (e) {
var target = e.target;
if (target.tagName === 'INPUT' && target.getAttribute('class').indexOf('liga') === -1) {
target.select();
}
});
(function () {
var fontSize = document.getElementById('fontSize'),
testDrive = document.getElementById('testDrive'),
testText = document.getElementById('testText');
function updateTest() {
testDrive.innerHTML = testText.value || String.fromCharCode(160);
if (window.icomoonLiga) {
window.icomoonLiga(testDrive);
}
}
function updateSize() {
testDrive.style.fontSize = fontSize.value + 'px';
}
fontSize.addEventListener('change', updateSize, false);
testText.addEventListener('input', updateTest, false);
testText.addEventListener('change', updateTest, false);
updateSize();
})();
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.6 MiB

File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
Open *demo.html* to see a list of all the glyphs in your font along with their codes/ligatures.
To use the generated font in desktop programs, you can install the TTF font. In order to copy the character associated with each icon, refer to the text box at the bottom right corner of each glyph in demo.html. The character inside this text box may be invisible; but it can still be copied. See this guide for more info: https://icomoon.io/docs/#local-fonts
You won't need any of the files located under the *demo-files* directory when including the generated font in your own projects.
You can import *selection.json* back to the IcoMoon app using the *Import Icons* button (or via Main Menu → Manage Projects) to retrieve your icon selection.
@@ -0,0 +1,160 @@
body {
padding: 0;
margin: 0;
font-family: sans-serif;
font-size: 1em;
line-height: 1.5;
color: #555;
background: #fff;
}
h1 {
font-size: 1.5em;
font-weight: normal;
}
small {
font-size: 0.66666667em;
}
a {
color: #e74c3c;
text-decoration: none;
}
a:hover,
a:focus {
box-shadow: 0 1px #e74c3c;
}
.bshadow0,
input {
box-shadow: inset 0 -2px #e7e7e7;
}
input:hover {
box-shadow: inset 0 -2px #ccc;
}
input,
fieldset {
font-family: sans-serif;
font-size: 1em;
margin: 0;
padding: 0;
border: 0;
}
input {
color: inherit;
line-height: 1.5;
height: 1.5em;
padding: 0.25em 0;
}
input:focus {
outline: none;
box-shadow: inset 0 -2px #449fdb;
}
.glyph {
font-size: 16px;
width: 15em;
padding-bottom: 1em;
margin-right: 4em;
margin-bottom: 1em;
float: left;
overflow: hidden;
}
.liga {
width: 80%;
width: calc(100% - 2.5em);
}
.talign-right {
text-align: right;
}
.talign-center {
text-align: center;
}
.bgc1 {
background: #f1f1f1;
}
.fgc1 {
color: #999;
}
.fgc0 {
color: #000;
}
p {
margin-top: 1em;
margin-bottom: 1em;
}
.mvm {
margin-top: 0.75em;
margin-bottom: 0.75em;
}
.mtn {
margin-top: 0;
}
.mtl,
.mal {
margin-top: 1.5em;
}
.mbl,
.mal {
margin-bottom: 1.5em;
}
.mal,
.mhl {
margin-left: 1.5em;
margin-right: 1.5em;
}
.mhmm {
margin-left: 1em;
margin-right: 1em;
}
.mls {
margin-left: 0.25em;
}
.ptl {
padding-top: 1.5em;
}
.pbs,
.pvs {
padding-bottom: 0.25em;
}
.pvs,
.pts {
padding-top: 0.25em;
}
.unit {
float: left;
}
.unitRight {
float: right;
}
.size1of2 {
width: 50%;
}
.size1of1 {
width: 100%;
}
.clearfix:before,
.clearfix:after {
content: ' ';
display: table;
}
.clearfix:after {
clear: both;
}
.hidden-true {
display: none;
}
.textbox0 {
width: 3em;
background: #f1f1f1;
padding: 0.25em 0.5em;
line-height: 1.5;
height: 1.5em;
}
#testDrive {
display: block;
padding-top: 24px;
line-height: 1.5;
}
.fs0 {
font-size: 16px;
}
.fs1 {
font-size: 24px;
}
@@ -0,0 +1,29 @@
if (!('boxShadow' in document.body.style)) {
document.body.setAttribute('class', 'noBoxShadow');
}
document.body.addEventListener('click', function (e) {
var target = e.target;
if (target.tagName === 'INPUT' && target.getAttribute('class').indexOf('liga') === -1) {
target.select();
}
});
(function () {
var fontSize = document.getElementById('fontSize'),
testDrive = document.getElementById('testDrive'),
testText = document.getElementById('testText');
function updateTest() {
testDrive.innerHTML = testText.value || String.fromCharCode(160);
if (window.icomoonLiga) {
window.icomoonLiga(testDrive);
}
}
function updateSize() {
testDrive.style.fontSize = fontSize.value + 'px';
}
fontSize.addEventListener('change', updateSize, false);
testText.addEventListener('input', updateTest, false);
testText.addEventListener('change', updateTest, false);
updateSize();
})();
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.2 MiB

File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,43 @@
/* KeenIcon Size Utilities */
.ki-xs {
font-size: 12px !important;
line-height: 1;
}
.ki-sm {
font-size: 14px !important;
line-height: 1;
}
.ki-md {
font-size: 16px !important;
line-height: 1;
} /* Base size */
.ki-lg {
font-size: 18px !important;
line-height: 1;
}
.ki-xl {
font-size: 20px !important;
line-height: 1;
}
/* Larger Display Sizes */
.ki-2x {
font-size: 24px !important;
line-height: 1;
}
.ki-3x {
font-size: 32px !important;
line-height: 1;
}
.ki-4x {
font-size: 40px !important;
line-height: 1;
}
.ki-5x {
font-size: 48px !important;
line-height: 1;
}
.ki-6x {
font-size: 64px !important;
line-height: 1;
}
@@ -0,0 +1,7 @@
Open *demo.html* to see a list of all the glyphs in your font along with their codes/ligatures.
To use the generated font in desktop programs, you can install the TTF font. In order to copy the character associated with each icon, refer to the text box at the bottom right corner of each glyph in demo.html. The character inside this text box may be invisible; but it can still be copied. See this guide for more info: https://icomoon.io/docs/#local-fonts
You won't need any of the files located under the *demo-files* directory when including the generated font in your own projects.
You can import *selection.json* back to the IcoMoon app using the *Import Icons* button (or via Main Menu → Manage Projects) to retrieve your icon selection.
@@ -0,0 +1,160 @@
body {
padding: 0;
margin: 0;
font-family: sans-serif;
font-size: 1em;
line-height: 1.5;
color: #555;
background: #fff;
}
h1 {
font-size: 1.5em;
font-weight: normal;
}
small {
font-size: 0.66666667em;
}
a {
color: #e74c3c;
text-decoration: none;
}
a:hover,
a:focus {
box-shadow: 0 1px #e74c3c;
}
.bshadow0,
input {
box-shadow: inset 0 -2px #e7e7e7;
}
input:hover {
box-shadow: inset 0 -2px #ccc;
}
input,
fieldset {
font-family: sans-serif;
font-size: 1em;
margin: 0;
padding: 0;
border: 0;
}
input {
color: inherit;
line-height: 1.5;
height: 1.5em;
padding: 0.25em 0;
}
input:focus {
outline: none;
box-shadow: inset 0 -2px #449fdb;
}
.glyph {
font-size: 16px;
width: 15em;
padding-bottom: 1em;
margin-right: 4em;
margin-bottom: 1em;
float: left;
overflow: hidden;
}
.liga {
width: 80%;
width: calc(100% - 2.5em);
}
.talign-right {
text-align: right;
}
.talign-center {
text-align: center;
}
.bgc1 {
background: #f1f1f1;
}
.fgc1 {
color: #999;
}
.fgc0 {
color: #000;
}
p {
margin-top: 1em;
margin-bottom: 1em;
}
.mvm {
margin-top: 0.75em;
margin-bottom: 0.75em;
}
.mtn {
margin-top: 0;
}
.mtl,
.mal {
margin-top: 1.5em;
}
.mbl,
.mal {
margin-bottom: 1.5em;
}
.mal,
.mhl {
margin-left: 1.5em;
margin-right: 1.5em;
}
.mhmm {
margin-left: 1em;
margin-right: 1em;
}
.mls {
margin-left: 0.25em;
}
.ptl {
padding-top: 1.5em;
}
.pbs,
.pvs {
padding-bottom: 0.25em;
}
.pvs,
.pts {
padding-top: 0.25em;
}
.unit {
float: left;
}
.unitRight {
float: right;
}
.size1of2 {
width: 50%;
}
.size1of1 {
width: 100%;
}
.clearfix:before,
.clearfix:after {
content: ' ';
display: table;
}
.clearfix:after {
clear: both;
}
.hidden-true {
display: none;
}
.textbox0 {
width: 3em;
background: #f1f1f1;
padding: 0.25em 0.5em;
line-height: 1.5;
height: 1.5em;
}
#testDrive {
display: block;
padding-top: 24px;
line-height: 1.5;
}
.fs0 {
font-size: 16px;
}
.fs1 {
font-size: 24px;
}
@@ -0,0 +1,29 @@
if (!('boxShadow' in document.body.style)) {
document.body.setAttribute('class', 'noBoxShadow');
}
document.body.addEventListener('click', function (e) {
var target = e.target;
if (target.tagName === 'INPUT' && target.getAttribute('class').indexOf('liga') === -1) {
target.select();
}
});
(function () {
var fontSize = document.getElementById('fontSize'),
testDrive = document.getElementById('testDrive'),
testText = document.getElementById('testText');
function updateTest() {
testDrive.innerHTML = testText.value || String.fromCharCode(160);
if (window.icomoonLiga) {
window.icomoonLiga(testDrive);
}
}
function updateSize() {
testDrive.style.fontSize = fontSize.value + 'px';
}
fontSize.addEventListener('change', updateSize, false);
testText.addEventListener('input', updateTest, false);
testText.addEventListener('change', updateTest, false);
updateSize();
})();
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 878 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
+6
View File
@@ -0,0 +1,6 @@
@import './duotone/style.css';
@import './outline/style.css';
@import './filled/style.css';
@import './solid/style.css';
@import './sizing.css';
+2
View File
@@ -0,0 +1,2 @@
export * from './types';
export * from './keenicon';
+20
View File
@@ -0,0 +1,20 @@
import { forwardRef } from 'react';
import type { KeeniconsProps } from './types';
function joinClasses(...args: string[]): string {
return args.join(' ');
}
export const KeenIcon = forwardRef<HTMLElement, KeeniconsProps>((props, ref) => {
const { icon, variant = 'filled', className = '', size = 'md', ...rest } = props;
return (
<i
ref={ref}
{...rest}
className={joinClasses(`ki-${variant}`, `ki-${icon}`, `ki-${size}`, className)}
/>
);
});
KeenIcon.displayName = 'KeenIcon';
+9
View File
@@ -0,0 +1,9 @@
export type KeeniconVariant = 'duotone' | 'filled' | 'solid' | 'outline';
export type KeeniconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2x' | '3x' | '4x' | '5x' | '6x';
export interface KeeniconsProps {
icon: string;
variant?: KeeniconVariant;
className?: string;
size?: KeeniconSize;
}
+21
View File
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2023", "DOM"],
"jsx": "react-jsx",
"skipLibCheck": true,
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"strict": true,
"composite": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}