Skip to content

Commit 361efad

Browse files
committed
[add] Code Block & Range Input components
[remove] Score Range component
1 parent d8079a3 commit 361efad

File tree

10 files changed

+725
-281
lines changed

10 files changed

+725
-281
lines changed

package.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "boot-cell",
3-
"version": "2.0.0-rc.13",
3+
"version": "2.0.0-rc.14",
44
"license": "LGPL-3.0",
55
"author": "shiy2008@gmail.com",
66
"description": "Web Components UI library based on WebCell v3, BootStrap v5, BootStrap Icon v1 & FontAwesome v6",
@@ -29,8 +29,10 @@
2929
"classnames": "^2.5.1",
3030
"dom-renderer": "^2.6.2",
3131
"iterable-observer": "^1.1.0",
32+
"mime": "^4.0.6",
3233
"mobx": "^6.13.6",
3334
"regenerator-runtime": "^0.14.1",
35+
"shiki": "^3.1.0",
3436
"web-cell": "^3.0.3",
3537
"web-utility": "^4.4.3"
3638
},
@@ -59,12 +61,12 @@
5961
"lint-staged": "^15.4.3",
6062
"open-cli": "^8.0.0",
6163
"parcel": "~2.13.3",
62-
"prettier": "^3.5.2",
64+
"prettier": "^3.5.3",
6365
"ts-jest": "^29.2.6",
6466
"ts-node": "^10.9.2",
6567
"typedoc": "^0.27.9",
66-
"typedoc-plugin-mdn-links": "^4.0.14",
67-
"typescript": "~5.7.3"
68+
"typedoc-plugin-mdn-links": "^5.0.1",
69+
"typescript": "~5.8.2"
6870
},
6971
"scripts": {
7072
"prepare": "husky",

pnpm-lock.yaml

Lines changed: 523 additions & 211 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

source/Content/CodeBlock.module.less

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.code pre {
2+
margin: 0;
3+
padding: 1rem;
4+
}

source/Content/CodeBlock.tsx

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import MIME from 'mime';
2+
import { computed, observable } from 'mobx';
3+
import { codeToHtml } from 'shiki';
4+
import {
5+
attribute,
6+
component,
7+
observer,
8+
reaction,
9+
WebCell,
10+
WebCellProps
11+
} from 'web-cell';
12+
13+
import { IconButton } from '../Form';
14+
import * as styles from './CodeBlock.module.less';
15+
16+
export interface CodeBlockProps extends WebCellProps<HTMLPreElement> {
17+
name?: string;
18+
value: string;
19+
language: string;
20+
theme?: string;
21+
}
22+
23+
export interface CodeBlock extends WebCell<CodeBlockProps> {}
24+
25+
@component({ tagName: 'code-block' })
26+
@observer
27+
export class CodeBlock extends HTMLElement implements WebCell<CodeBlockProps> {
28+
@attribute
29+
@observable
30+
accessor name = '';
31+
32+
@observable
33+
accessor value = '';
34+
35+
@attribute
36+
@observable
37+
accessor language = '';
38+
39+
@attribute
40+
@observable
41+
accessor theme = '';
42+
43+
@observable
44+
accessor markup = '';
45+
46+
@computed
47+
get dataURI() {
48+
return `data:${MIME.getType(this.name)};base64,${btoa(this.value)}`;
49+
}
50+
51+
@reaction(({ value, language, theme }) => value + language + theme)
52+
async connectedCallback() {
53+
if (this.value)
54+
this.markup = await codeToHtml(this.value, {
55+
lang: this.language,
56+
theme: this.theme
57+
});
58+
}
59+
60+
render() {
61+
const { name, dataURI, markup } = this;
62+
63+
return (
64+
<>
65+
{name && (
66+
<div className="text-end mb-2">
67+
<IconButton
68+
name="download"
69+
variant="warning"
70+
download={name}
71+
href={dataURI}
72+
/>
73+
</div>
74+
)}
75+
<div
76+
className={`rounded overflow-hidden ${styles.code}`}
77+
innerHTML={markup}
78+
/>
79+
</>
80+
);
81+
}
82+
}

source/Content/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export * from './Card';
66
export * from './MediaObject';
77
export * from './Accordion';
88
export * from './Tabs';
9+
export * from './CodeBlock';

source/Form/RangeInput.tsx

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { JsxChildren } from 'dom-renderer';
2+
import { observable } from 'mobx';
3+
import {
4+
attribute,
5+
component,
6+
formField,
7+
observer,
8+
WebCellProps,
9+
WebField
10+
} from 'web-cell';
11+
12+
export interface RangeInputProps extends WebCellProps<HTMLInputElement> {
13+
icon?: JsxChildren | ((itemValue: number) => JsxChildren);
14+
}
15+
16+
export interface RangeInput extends WebField<RangeInputProps> {}
17+
18+
@component({ tagName: 'range-input', mode: 'open' })
19+
@formField
20+
@observer
21+
export class RangeInput
22+
extends HTMLElement
23+
implements WebField<RangeInputProps>
24+
{
25+
@attribute
26+
@observable
27+
accessor type = 'range';
28+
29+
@attribute
30+
@observable
31+
accessor min: number | undefined;
32+
33+
@attribute
34+
@observable
35+
accessor max: number | undefined;
36+
37+
@attribute
38+
@observable
39+
accessor step = 1;
40+
41+
@observable
42+
accessor icon: RangeInputProps['icon'] | undefined;
43+
44+
connectedCallback() {
45+
this.classList.add('d-inline-block', 'position-relative');
46+
}
47+
48+
handleChange = ({ currentTarget }: Event) => {
49+
this.value = (currentTarget as HTMLInputElement).value;
50+
51+
this.emit('change');
52+
};
53+
54+
renderItem(index: number) {
55+
const { icon, step, value } = this;
56+
const fullValue = step * index;
57+
const itemValue = Math.max(Math.min(+value - fullValue, step), 0);
58+
59+
return (
60+
<li key={index} className="text-center">
61+
{typeof icon === 'function' ? icon(itemValue) : icon}
62+
</li>
63+
);
64+
}
65+
66+
render() {
67+
const { icon, min, max = icon ? 5 : 100, value = min || 0 } = this;
68+
69+
return (
70+
<>
71+
<link
72+
rel="stylesheet"
73+
href="https://unpkg.com/bootstrap@5.3.3/dist/css/bootstrap.min.css"
74+
/>
75+
<input
76+
className={icon ? 'opacity-0' : ''}
77+
style={{ margin: '0 -0.5rem', cursor: 'pointer' }}
78+
type="range"
79+
min={min?.toString()}
80+
max={max?.toString()}
81+
value={value?.toString()}
82+
onChange={this.handleChange}
83+
/>
84+
<ol className="list-unstyled user-select-none position-absolute start-0 top-0 w-100 h-100 pe-none d-flex justify-content-around">
85+
{Array.from({ length: max }, (_, index) =>
86+
this.renderItem(index)
87+
)}
88+
</ol>
89+
</>
90+
);
91+
}
92+
}

source/Form/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './Form';
22
export * from './Button';
33
export * from './ButtonGroup';
4+
export * from './RangeInput';
45
export * from './FilePicker';
56
export * from './FileUploader';

source/global.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
declare module '*.css' {
2+
const map: Record<string, string>;
3+
export = map;
4+
}
5+
declare module '*.less' {
6+
const map: Record<string, string>;
7+
export = map;
8+
}

test/index.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import {
77
FileModel,
88
FilePicker,
99
FileUploader,
10-
FormControl
10+
FormControl,
11+
RangeInput
1112
} from '../source/Form';
1213

1314
configure({ enforceActions: 'never' });
@@ -32,8 +33,12 @@ const Content = () => (
3233
<h3>Regular</h3>
3334
<FormControl type="range" name="count" />
3435

35-
{/* <h3>Star</h3>
36-
<ScoreRange className="text-warning" name="score" /> */}
36+
<h3>Star</h3>
37+
<RangeInput
38+
className="text-warning"
39+
name="score"
40+
icon={value => (value ? '★' : '☆')}
41+
/>
3742
</section>
3843

3944
<section>

v1/Form/ScoreRange.tsx

Lines changed: 0 additions & 63 deletions
This file was deleted.

0 commit comments

Comments
 (0)