hortimagic 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "hortimagic",
3
+ "private": false,
4
+ "version": "1.0.0",
5
+ "author": "Narlen",
6
+ "description": "园艺魔法,花园插件",
7
+ "keywords": [
8
+ "iirose",
9
+ "plugins",
10
+ "hortimagic"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/NarlenHua/hortimagic.git"
15
+ },
16
+ "license": "MIT",
17
+ "type": "module",
18
+ "scripts": {
19
+ "dev": "vite",
20
+ "build": "tsc && vite build",
21
+ "preview": "vite preview"
22
+ },
23
+ "dependencies": {
24
+ "lit": "^3.3.1",
25
+ "terser": "^5.44.0",
26
+ "tiny-emitter": "^2.1.0",
27
+ "vite-plugin-dts": "^4.5.4"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^24.9.1",
31
+ "typescript": "~5.9.3",
32
+ "vite": "^7.1.7"
33
+ },
34
+ "main": "dist/Horticraft.life.js",
35
+ "module": "dist/HortiCraft.es.js",
36
+ "typings": "dist/index.d.ts",
37
+ "types": "dist/index.d.ts",
38
+ "files": [
39
+ "dist",
40
+ "src/components",
41
+ "types"
42
+ ]
43
+ }
@@ -0,0 +1,141 @@
1
+ import { LitElement, html, css } from 'lit';
2
+ import { customElement, property } from 'lit/decorators.js';
3
+ /**
4
+ * @example
5
+ * <hm-accordion>
6
+ <span slot="header">我的折叠面板</span>
7
+ <div>内容项 1</div>
8
+ <div>内容项 2</div>
9
+ </hm-accordion>
10
+ */
11
+ @customElement('hm-accordion')
12
+ export class HmAccordion extends LitElement {
13
+ @property({ type: String, attribute: 'max-height' })
14
+ maxHeight = '500px';
15
+
16
+ @property({ type: Array })
17
+ items: any[] = [];
18
+
19
+ /** 是否展开 */
20
+ @property({ type: Boolean })
21
+ expanded = false;
22
+
23
+ static styles = css`
24
+ :host {
25
+ display: block;
26
+ width: 100%;
27
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
28
+ }
29
+
30
+ .accordion-container {
31
+ display: flex;
32
+ flex-direction: column;
33
+ border-radius: 8px;
34
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
35
+ background-color: white;
36
+ overflow: hidden;
37
+ transition: all 0.3s ease;
38
+ }
39
+
40
+ .accordion-header {
41
+ padding: 16px 20px;
42
+ background-color: #f8f9fa;
43
+ border-bottom: 1px solid #e9ecef;
44
+ font-size: 1.25rem;
45
+ font-weight: 600;
46
+ color: #212529;
47
+ display: flex;
48
+ justify-content: space-between;
49
+ align-items: center;
50
+ cursor: pointer;
51
+ transition: background-color 0.2s;
52
+ }
53
+
54
+ .accordion-header:hover {
55
+ background-color: #e9ecef;
56
+ }
57
+
58
+ .accordion-toggle {
59
+ transition: transform 0.3s ease;
60
+ width: 24px;
61
+ height: 24px;
62
+ display: flex;
63
+ align-items: center;
64
+ justify-content: center;
65
+ }
66
+
67
+ .accordion-content {
68
+ flex: 1;
69
+ overflow-y: auto;
70
+ padding: 0;
71
+ background-color: #ffffff;
72
+ transition: max-height 0.3s ease, opacity 0.3s ease;
73
+ }
74
+
75
+ .accordion-footer {
76
+ padding: 12px 20px;
77
+ background-color: #f8f9fa;
78
+ border-top: 1px solid #e9ecef;
79
+ display: flex;
80
+ justify-content: flex-end;
81
+ gap: 10px;
82
+ }
83
+
84
+ .accordion-item {
85
+ padding: 12px 20px;
86
+ border-bottom: 1px solid #e9ecef;
87
+ transition: background-color 0.2s;
88
+ }
89
+
90
+ .accordion-item:last-child {
91
+ border-bottom: none;
92
+ }
93
+
94
+ .accordion-item:hover {
95
+ background-color: #f8f9fa;
96
+ }
97
+ `;
98
+ /** 开关容器 */
99
+ togglePanel() {
100
+ this.expanded = !this.expanded;
101
+ }
102
+
103
+ render() {
104
+ return html`
105
+ <div class="accordion-container" style="max-height: ${this.maxHeight}">
106
+ <div class="accordion-header" @click=${this.togglePanel}>
107
+ <slot name="header">面板标题</slot>
108
+ <div class="accordion-toggle">
109
+ ${!this.expanded
110
+ ? html`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
111
+ <path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"/>
112
+ </svg>`
113
+ : html`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
114
+ <path d="M7.247 4.86l-4.796 5.481c-.566.647-.106 1.659.753 1.659h9.592a1 1 0 0 0 .753-1.659l-4.796-5.48a1 1 0 0 0-1.506 0z"/>
115
+ </svg>`}
116
+ </div>
117
+ </div>
118
+
119
+ <div class="accordion-content" ?hidden=${!this.expanded}>
120
+ ${this.items.length > 0
121
+ ? this.items.map(
122
+ (item) => html`<div class="accordion-item">${item}</div>`
123
+ )
124
+ : html`<slot></slot>`}
125
+ </div>
126
+
127
+ <div class="accordion-footer" ?hidden=${!this.expanded}>
128
+ <slot name="footer">
129
+ <hm-button @hm-button-click="${() => { this.expanded = false }}">关闭</hm-button>
130
+ </slot>
131
+ </div>
132
+ </div>
133
+ `;
134
+ }
135
+ }
136
+
137
+ declare global {
138
+ interface HTMLElementTagNameMap {
139
+ 'hm-accordion': HmAccordion;
140
+ }
141
+ }
@@ -0,0 +1,161 @@
1
+ import { LitElement, css, html } from 'lit';
2
+ import { customElement, property } from 'lit/decorators.js';
3
+
4
+ /**
5
+ * 按钮组件
6
+ *
7
+ * @example
8
+ * ```html
9
+ * <!-- 基础用法 -->
10
+ * <hm-button content="普通按钮"></hm-button>
11
+ *
12
+ * <!-- 带图标按钮 -->
13
+ * <hm-button icon="plus" content="添加"></hm-button>
14
+ *
15
+ * <!-- 自定义颜色 -->
16
+ * <hm-button
17
+ * content="自定义样式"
18
+ * color="#ffffff"
19
+ * background="#4caf50">
20
+ * </hm-button>
21
+ *
22
+ * <!-- 禁用状态 -->
23
+ * <hm-button content="禁用按钮" .enable="${false}"></hm-button>
24
+ *
25
+ * <!-- 加载状态 -->
26
+ * <hm-button content="加载中" .loading="${true}"></hm-button>
27
+ *
28
+ * <!-- 自定义尺寸 -->
29
+ * <hm-button content="大按钮" width="200px" height="50px"></hm-button>
30
+ * <hm-button content="小按钮" width="60px" height="30px" fontSize="8px"></hm-button>
31
+ * ```
32
+ */
33
+ @customElement('hm-button')
34
+ export class HmButton extends LitElement {
35
+ /** 按钮图标 */
36
+ @property({ type: String })
37
+ icon = '';
38
+
39
+ /** 按钮文字内容 */
40
+ @property({ type: String })
41
+ content = '';
42
+ /** 字体大小 */
43
+ @property({ type: String })
44
+ fontSize = '14px'
45
+ /** 字体颜色 */
46
+ @property({ type: String })
47
+ color = '';
48
+
49
+ /** 背景颜色 */
50
+ @property({ type: String })
51
+ background = '';
52
+ @property({ type: String })
53
+ width = '';
54
+ @property({ type: String })
55
+ height = '';
56
+
57
+ /** 是否启用 */
58
+ @property({ type: Boolean })
59
+ enable = true;
60
+
61
+ /** 是否加载中 */
62
+ @property({ type: Boolean })
63
+ loading = false;
64
+
65
+
66
+ static styles = css`
67
+ :host {
68
+ display: inline-block;
69
+ }
70
+
71
+ .button {
72
+ display: inline-flex;
73
+ align-items: center;
74
+ justify-content: center;
75
+ border: none;
76
+ border-radius: 4px;
77
+ padding: 8px 16px;
78
+ cursor: pointer;
79
+ transition: all 0.3s;
80
+ opacity: 1;
81
+ // 添加鼠标悬停动画
82
+ &:hover:not(:disabled) {
83
+ transform: translateY(-2px);
84
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
85
+ }
86
+ }
87
+
88
+ .button:disabled {
89
+ cursor: not-allowed;
90
+ opacity: 0.6;
91
+ }
92
+
93
+ .button.loading {
94
+ cursor: not-allowed;
95
+ }
96
+
97
+ .loading-spinner {
98
+ width: 14px;
99
+ height: 14px;
100
+ border: 2px solid transparent;
101
+ border-top-color: currentColor;
102
+ border-radius: 50%;
103
+ animation: rotate 1s linear infinite;
104
+ margin-right: 8px;
105
+ }
106
+
107
+ @keyframes rotate {
108
+ from {
109
+ transform: rotate(0deg);
110
+ }
111
+ to {
112
+ transform: rotate(360deg);
113
+ }
114
+ }
115
+
116
+ .button-content {
117
+ display: flex;
118
+ align-items: center;
119
+ }
120
+ `;
121
+
122
+ render() {
123
+ const buttonStyle = `
124
+ ${this.color ? `color: ${this.color};` : ''}
125
+ ${this.background ? `background-color: ${this.background};` : ''}
126
+ ${this.width ? `width: ${this.width};` : ''}
127
+ ${this.height ? `height: ${this.height};` : ''}
128
+ ${this.fontSize ? `font-size: ${this.fontSize};` : '14px'}
129
+ `;
130
+
131
+ return html`
132
+ <button
133
+ class="button"
134
+ style="${buttonStyle}"
135
+ ?disabled="${!this.enable || this.loading}"
136
+ @click="${this._handleClick}">
137
+
138
+ ${this.loading ? html`
139
+ <div class="loading-spinner"></div>
140
+ ` : this.icon ? html`
141
+ <slot name="icon">
142
+ <hm-icon icon="${this.icon}" style="margin-right: 8px;"></hm-icon>
143
+ </slot>
144
+ ` : ''}
145
+
146
+ <span class="button-content">
147
+ <slot>${this.content}</slot>
148
+ </span>
149
+ </button>
150
+ `;
151
+ }
152
+
153
+ private _handleClick(e: MouseEvent) {
154
+ if (!this.enable || this.loading) {
155
+ e.stopPropagation();
156
+ return;
157
+ }
158
+
159
+ this.dispatchEvent(new CustomEvent('hm-button-click'));
160
+ }
161
+ }
@@ -0,0 +1,181 @@
1
+ import { LitElement, html, css } from 'lit';
2
+ import { customElement, property } from 'lit/decorators.js';
3
+
4
+ /**
5
+ * 单元格组件
6
+ * 用于展示列表内容或选项,采用左右分栏布局
7
+ *
8
+ * @slot title - 标题内容(左侧)
9
+ * @slot description - 描述信息(左侧)
10
+ * @slot content - 主要内容(右侧)
11
+ *
12
+ * @cssprop --hm-cell-padding - 内边距
13
+ * @cssprop --hm-cell-border - 边框样式
14
+ * @cssprop --hm-cell-background - 背景颜色
15
+ *
16
+ * @example
17
+ * ```html
18
+ * <!-- 基础用法 -->
19
+ * <hm-cell
20
+ * titleName="单元格标题"
21
+ * descripthion="这是描述信息"
22
+ * content="内容区域">
23
+ * </hm-cell>
24
+ *
25
+ * <!-- 使用插槽自定义内容 -->
26
+ * <hm-cell>
27
+ * <div slot="title">自定义标题</div>
28
+ * <div slot="description">自定义描述</div>
29
+ * <div slot="content">自定义内容</div>
30
+ * </hm-cell>
31
+ *
32
+ * <!-- 带点击事件 -->
33
+ * <hm-cell
34
+ * titleName="可点击标题"
35
+ * content="点击查看详情"
36
+ * .titleClickCallback="${() => console.log('标题被点击')}"
37
+ * .contentClickCallback="${() => console.log('内容被点击')}">
38
+ * </hm-cell>
39
+ *
40
+ * <!-- 自定义样式 -->
41
+ * <hm-cell
42
+ * titleName="自定义样式"
43
+ * content="特殊样式"
44
+ * style="--hm-cell-background: #f0f8ff; --hm-cell-title-color: #1890ff;">
45
+ * </hm-cell>
46
+ * ```
47
+ */
48
+ @customElement('hm-cell')
49
+ export class HmCell extends LitElement {
50
+ /** 标题,使用slot后失效 */
51
+ @property()
52
+ titleName = "单元格";
53
+ /** 标题下方描述,使用slot后失效 */
54
+ @property()
55
+ descripthion = "描述信息";
56
+ /** 右侧正文,使用slot后失效 */
57
+ @property()
58
+ content = "内容";
59
+ /** 标题点击回调函数 */
60
+ @property()
61
+ titleClickCallback = () => { }
62
+ /** 正文点击回调函数 */
63
+ @property()
64
+ contentClickCallback = () => { }
65
+ render() {
66
+ return html`
67
+ <div class="cell" part="cell">
68
+ <div
69
+ class="left-section"
70
+ part="left-section"
71
+ @click="${this.titleClickCallback}"
72
+ >
73
+ <div class="title" part="title">
74
+ <slot name="title">${this.titleName}</slot>
75
+ </div>
76
+ <div class="description" part="description">
77
+ <slot name="description">${this.descripthion}</slot>
78
+ </div>
79
+ </div>
80
+ <div
81
+ class="right-section"
82
+ part="right-section"
83
+ @click="${this.contentClickCallback}"
84
+ >
85
+ <div class="content" part="content">
86
+ <slot name="content">${this.content}</slot>
87
+ </div>
88
+ </div>
89
+ </div>
90
+
91
+ `;
92
+ }
93
+
94
+ static styles = css`
95
+ :host {
96
+ display: block;
97
+ width: 100%;
98
+ }
99
+ .cell {
100
+ display: flex;
101
+ align-items: center;
102
+ justify-content: space-between;
103
+ width: 100%;
104
+ min-height: 60px;
105
+ padding: var(--hm-cell-padding, 12px 16px);
106
+ background: var(--hm-cell-background, #ffffff);
107
+ border: var(--hm-cell-border, 1px dashed #e0e0e0);
108
+ box-sizing: border-box;
109
+ font-family: system-ui, -apple-system, sans-serif;
110
+ }
111
+
112
+ .left-section {
113
+ display: flex;
114
+ flex-direction: column;
115
+ justify-content: center;
116
+ flex: 1;
117
+ min-width: 0; /* 允许文本截断 */
118
+ }
119
+
120
+ .title {
121
+ font-size: var(--hm-cell-title-font-size, 18px);
122
+ font-weight: var(--hm-cell-title-font-weight, 600);
123
+ color: var(--hm-cell-title-color, #000000);
124
+ line-height: 1.4;
125
+ margin-bottom: 2px;
126
+ }
127
+
128
+ .description {
129
+ font-size: var(--hm-cell-description-font-size, 14px);
130
+ color: var(--hm-cell-description-color, #666666);
131
+ line-height: 1.4;
132
+ }
133
+
134
+ .right-section {
135
+ display: flex;
136
+ align-items: center;
137
+ justify-content: flex-end;
138
+ margin-left: auto;
139
+ padding-left: 16px;
140
+ }
141
+
142
+ .content {
143
+ font-size: var(--hm-cell-content-font-size, 16px);
144
+ color: var(--hm-cell-content-color, #333333);
145
+ text-align: right;
146
+ padding-right: var(--hm-cell-content-padding-right, 30px);
147
+ }
148
+
149
+ /* 响应式设计 */
150
+ @media (max-width: 480px) {
151
+ .cell {
152
+ padding: var(--hm-cell-mobile-padding, 10px 12px);
153
+ min-height: 50px;
154
+ }
155
+
156
+ .title {
157
+ font-size: var(--hm-cell-mobile-title-font-size, 16px);
158
+ }
159
+
160
+ .description {
161
+ font-size: var(--hm-cell-mobile-description-font-size, 12px);
162
+ }
163
+
164
+ .content {
165
+ font-size: var(--hm-cell-mobile-content-font-size, 14px);
166
+ padding-right: var(--hm-cell-mobile-content-padding-right, 16px);
167
+ }
168
+ }
169
+ /* 当鼠标或触摸悬停在整个单元格上时,改变内部文本的样式 */
170
+ .cell:hover .title,
171
+ .cell:hover .description,
172
+ .cell:hover .content {
173
+ /* 使用CSS变量允许自定义,并提供默认高亮颜色 */
174
+ color: var(--hm-cell-hover-font-color, #1890ff);
175
+ /* 可选:增加字体粗细使其更突出 */
176
+ font-weight: var(--hm-cell-hover-font-weight, 600);
177
+ /* 可选:添加文字阴影增强视觉效果 */
178
+ text-shadow: var(--hm-cell-hover-text-shadow, 0 0 5px rgba(24, 144, 255, 0.2));
179
+ }
180
+ `;
181
+ }
@@ -0,0 +1,125 @@
1
+ import { LitElement, html, css } from 'lit';
2
+ import { customElement, property } from 'lit/decorators.js';
3
+
4
+ /**
5
+ * @example
6
+ * <hm-dialog isopen>
7
+ * <h2>对话框标题</h2>
8
+ * <p>这是对话框内容</p>
9
+ * </hm-dialog>
10
+ *
11
+ * @example
12
+ * <hm-dialog>
13
+ * <h2>带自定义按钮的对话框</h2>
14
+ * <p>这是对话框内容</p>
15
+ * <div slot="footer">
16
+ * <hm-button @click="handleCancel">取消</hm-button>
17
+ * <hm-button @click="handleConfirm">确定</hm-button>
18
+ * </div>
19
+ * </hm-dialog>
20
+ */
21
+ @customElement('hm-dialog')
22
+ export class HmDialog extends LitElement {
23
+ static styles = css`
24
+ :host {
25
+ display: none;
26
+ position: fixed;
27
+ top: 0;
28
+ left: 0;
29
+ width: 100%;
30
+ height: 100%;
31
+ z-index: 1000;
32
+ }
33
+ :host([isopen]) {
34
+ display: block;
35
+ }
36
+ .overlay {
37
+ position: fixed;
38
+ top: 0;
39
+ left: 0;
40
+ width: 100%;
41
+ height: 100%;
42
+ background-color: rgba(0, 0, 0, 0.5);
43
+ }
44
+
45
+ .content {
46
+ position: absolute;
47
+ top: 50%;
48
+ left: 50%;
49
+ transform: translate(-50%, -50%);
50
+ background: white;
51
+ border-radius: 4px;
52
+ min-width: 300px;
53
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
54
+ padding: 20px;
55
+ }
56
+
57
+ .footer {
58
+ display: flex;
59
+ justify-content: flex-end;
60
+ gap: 10px;
61
+ margin-top: 20px;
62
+ }
63
+
64
+ hm-button {
65
+ min-width: 80px;
66
+ }
67
+ `;
68
+
69
+ // 修改属性名以匹配使用的属性
70
+ @property({ type: Boolean, attribute: 'isopen' })
71
+ isOpen = false;
72
+ // isOpen = true;
73
+
74
+ dialog = this;
75
+
76
+ open() {
77
+ this.isOpen = true;
78
+ this.dispatchEvent(new CustomEvent('hm-dialog-open'));
79
+ }
80
+
81
+ close() {
82
+ this.isOpen = false;
83
+ this.dispatchEvent(new CustomEvent('hm-dialog-close'));
84
+ }
85
+
86
+ /** 确认,触发 dialog-close dialog-confirm事件*/
87
+ confirm() {
88
+ // console.debug("确认对话框事件");
89
+ this.close();
90
+ this.dispatchEvent(new CustomEvent('hm-dialog-confirm'));
91
+ }
92
+
93
+ /** 取消,触发 dialog-close dialog-cancel事件*/
94
+ cancel() {
95
+ this.close();
96
+ this.dispatchEvent(new CustomEvent('hm-dialog-cancel'));
97
+ }
98
+
99
+ updated(changedProperties: Map<string, unknown>) {
100
+ if (changedProperties.has('isOpen')) {
101
+ if (this.isOpen) {
102
+ this.style.display = 'block';
103
+ } else {
104
+ this.style.display = 'none';
105
+ }
106
+ }
107
+ }
108
+
109
+ render() {
110
+ return html`
111
+ <div class="overlay"
112
+ @click="${this.close}"
113
+ ></div>
114
+ <div class="content">
115
+ <slot></slot>
116
+ <div class="footer">
117
+ <slot name="footer">
118
+ <hm-button @click="${() => { this.cancel(); console.debug("取消") }}">取消</hm-button>
119
+ <hm-button @click="${() => { this.confirm(); console.debug("确定") }}">确定</hm-button>
120
+ </slot>
121
+ </div>
122
+ </div>
123
+ `;
124
+ }
125
+ }