@astryx-vue/lab 0.0.0-stage → 0.1.1

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/lab.css ADDED
@@ -0,0 +1,10 @@
1
+ /* 实验包的全部样式。按需引入时也可以只引 styles/ 下的单份文件。 */
2
+ @import './styles/stat.css';
3
+ @import './styles/circular-progress.css';
4
+ @import './styles/info-tip.css';
5
+ @import './styles/svg-icon.css';
6
+ @import './styles/log-stream.css';
7
+ @import './styles/mobile-tokenizer.css';
8
+ @import './styles/transfer-list.css';
9
+ @import './styles/transfer-list-selector.css';
10
+ @import './styles/list-input.css';
package/package.json CHANGED
@@ -1,6 +1,74 @@
1
1
  {
2
2
  "name": "@astryx-vue/lab",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "type": "module",
4
+ "version": "0.1.1",
5
+ "description": "Astryx lab components for Vue 3 — experimental components that may change. Ships only under the canary dist-tag.",
6
+ "author": "ShenQingchuan",
7
+ "license": "MIT",
8
+ "homepage": "https://astryx-vue.pages.dev/lab/",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/astryx-vue/astryx-vue.git",
12
+ "directory": "packages/lab"
13
+ },
14
+ "bugs": "https://github.com/astryx-vue/astryx-vue/issues",
15
+ "keywords": [
16
+ "vue",
17
+ "vue3",
18
+ "design-system",
19
+ "astryx",
20
+ "lab",
21
+ "experimental"
22
+ ],
23
+ "publishConfig": {
24
+ "access": "public",
25
+ "tag": "canary"
26
+ },
27
+ "astryxVue": {
28
+ "canaryOnly": true
29
+ },
30
+ "sideEffects": [
31
+ "**/*.css"
32
+ ],
33
+ "exports": {
34
+ ".": {
35
+ "types": "./dist/index.d.ts",
36
+ "import": "./dist/index.js"
37
+ },
38
+ "./lab.css": {
39
+ "types": "./css.d.ts",
40
+ "default": "./lab.css"
41
+ },
42
+ "./styles/*.css": "./styles/*.css",
43
+ "./package.json": "./package.json"
44
+ },
45
+ "main": "./dist/index.js",
46
+ "types": "./dist/index.d.ts",
47
+ "files": [
48
+ "README.md",
49
+ "css.d.ts",
50
+ "dist",
51
+ "lab.css",
52
+ "styles"
53
+ ],
54
+ "peerDependencies": {
55
+ "vue": "^3.5.0",
56
+ "@astryx-vue/core": "^0.5.2"
57
+ },
58
+ "devDependencies": {
59
+ "@types/node": "26.6.4",
60
+ "@vue/compiler-sfc": "3.5.43",
61
+ "@vue/test-utils": "2.5.1",
62
+ "tsdown": "0.23.0",
63
+ "typescript": "npm:@typescript/typescript6@6.0.2",
64
+ "unplugin-vue": "8.0.0",
65
+ "vue": "3.5.43",
66
+ "vue-tsc": "3.3.12",
67
+ "@astryx-vue/core": "^0.5.2"
68
+ },
69
+ "scripts": {
70
+ "build": "tsdown",
71
+ "dev": "tsdown --watch",
72
+ "typecheck": "vue-tsc --noEmit -p tsconfig.json"
73
+ }
6
74
  }
@@ -0,0 +1,174 @@
1
+ @layer astryx.reset, astryx.tokens, astryx.base, astryx.components, astryx.overrides;
2
+
3
+ @layer astryx.components {
4
+ /* 根:环形和标签叠成一列。标签显示在环上方时才有间距。 */
5
+ .astryx-circular-progress {
6
+ display: inline-flex;
7
+ align-items: center;
8
+ justify-content: center;
9
+ position: relative;
10
+ flex-shrink: 0;
11
+ }
12
+
13
+ .astryx-circular-progress--labelled {
14
+ flex-direction: column;
15
+ gap: var(--astryx-spacing-1, 4px);
16
+ }
17
+
18
+ .astryx-circular-progress__label {
19
+ font-size: var(--astryx-text-supporting-size, 0.75rem);
20
+ line-height: var(--astryx-text-supporting-leading, 1.6667);
21
+ font-weight: var(--astryx-font-weight-medium, 500);
22
+ color: var(--astryx-color-text-secondary, light-dark(#474747, #9e9e9e));
23
+ }
24
+
25
+ .astryx-circular-progress__label--disabled {
26
+ color: var(--astryx-color-text-disabled, light-dark(#919191, #525252));
27
+ }
28
+
29
+ /* 只给读屏软件念的标签。 */
30
+ .astryx-circular-progress__visually-hidden {
31
+ position: absolute;
32
+ width: 1px;
33
+ height: 1px;
34
+ padding: 0;
35
+ margin: -1px;
36
+ overflow: hidden;
37
+ clip: rect(0, 0, 0, 0);
38
+ white-space: nowrap;
39
+ border-width: 0;
40
+ }
41
+
42
+ .astryx-circular-progress__ring {
43
+ position: relative;
44
+ display: inline-flex;
45
+ }
46
+
47
+ /* 起点转到 12 点方向,圆弧从左上方开始长。 */
48
+ .astryx-circular-progress__svg {
49
+ display: block;
50
+ transform: rotate(-90deg);
51
+ }
52
+
53
+ .astryx-circular-progress__svg--indeterminate {
54
+ animation: astryx-circular-progress-rotate 2s linear infinite;
55
+ }
56
+
57
+ .astryx-circular-progress__track {
58
+ fill: none;
59
+ stroke: var(--astryx-color-track, light-dark(#ccd3db, #5a5e66));
60
+ }
61
+
62
+ .astryx-circular-progress__track[data-variant='accent'] {
63
+ stroke: var(--astryx-color-accent-muted, light-dark(#f1f1f1, #262626));
64
+ }
65
+
66
+ .astryx-circular-progress__track[data-variant='success'] {
67
+ stroke: var(--astryx-color-success-muted, light-dark(#bce0bb, #90ca903d));
68
+ }
69
+
70
+ .astryx-circular-progress__track[data-variant='warning'] {
71
+ stroke: var(--astryx-color-warning-muted, light-dark(#fae19e, #e2b6233d));
72
+ }
73
+
74
+ .astryx-circular-progress__track[data-variant='error'] {
75
+ stroke: var(--astryx-color-error-muted, light-dark(#ffc4be, #ff98903d));
76
+ }
77
+
78
+ .astryx-circular-progress__track[data-variant='neutral'] {
79
+ stroke: var(--astryx-color-track, light-dark(#ccd3db, #5a5e66));
80
+ }
81
+
82
+ .astryx-circular-progress__fill {
83
+ fill: none;
84
+ stroke-linecap: round;
85
+ transition-property: stroke-dashoffset;
86
+ transition-duration: var(--astryx-duration-medium, 300ms);
87
+ transition-timing-function: var(--astryx-ease-standard, cubic-bezier(0.24, 1, 0.4, 1));
88
+ }
89
+
90
+ .astryx-circular-progress__fill[data-variant='accent'] {
91
+ stroke: var(--astryx-color-accent, light-dark(#1b1b1b, #f1f1f1));
92
+ }
93
+
94
+ .astryx-circular-progress__fill[data-variant='success'] {
95
+ stroke: var(--astryx-color-success, light-dark(#00490b, #a4d6a3));
96
+ }
97
+
98
+ .astryx-circular-progress__fill[data-variant='warning'] {
99
+ stroke: var(--astryx-color-warning, light-dark(#4b3900, #f8d36a));
100
+ }
101
+
102
+ .astryx-circular-progress__fill[data-variant='error'] {
103
+ stroke: var(--astryx-color-error, light-dark(#76000c, #ffc4be));
104
+ }
105
+
106
+ .astryx-circular-progress__fill[data-variant='neutral'],
107
+ .astryx-circular-progress__fill[data-variant='disabled'] {
108
+ stroke: var(--astryx-color-text-disabled, light-dark(#919191, #525252));
109
+ }
110
+
111
+ /* 不确定状态:一段固定的短弧一边转一边伸缩。 */
112
+ .astryx-circular-progress__fill--indeterminate {
113
+ animation: astryx-circular-progress-dash 1.5s ease-in-out infinite;
114
+ }
115
+
116
+ /* 环中心的内容压在圆弧上,不吃指针事件。 */
117
+ .astryx-circular-progress__center {
118
+ position: absolute;
119
+ inset: 0;
120
+ display: flex;
121
+ align-items: center;
122
+ justify-content: center;
123
+ pointer-events: none;
124
+ }
125
+
126
+ .astryx-circular-progress__value {
127
+ font-size: var(--astryx-text-supporting-size, 0.75rem);
128
+ line-height: var(--astryx-text-supporting-leading, 1.6667);
129
+ font-weight: var(--astryx-font-weight-normal, 400);
130
+ color: var(--astryx-color-text-secondary, light-dark(#474747, #9e9e9e));
131
+ }
132
+
133
+ .astryx-circular-progress__value--disabled {
134
+ color: var(--astryx-color-text-disabled, light-dark(#919191, #525252));
135
+ }
136
+
137
+ @media (prefers-reduced-motion: reduce) {
138
+ .astryx-circular-progress__svg--indeterminate {
139
+ animation-duration: 4s;
140
+ }
141
+
142
+ .astryx-circular-progress__fill--indeterminate {
143
+ animation-duration: 3s;
144
+ }
145
+ }
146
+
147
+ @keyframes astryx-circular-progress-rotate {
148
+ from {
149
+ transform: rotate(0deg);
150
+ }
151
+
152
+ to {
153
+ transform: rotate(360deg);
154
+ }
155
+ }
156
+
157
+ /* 缺口 300% 超过任何环的周长;把「2% + 300%」整体挪 302% 正好接上首尾。 */
158
+ @keyframes astryx-circular-progress-dash {
159
+ 0% {
160
+ stroke-dasharray: 2%, 300%;
161
+ stroke-dashoffset: 0%;
162
+ }
163
+
164
+ 50% {
165
+ stroke-dasharray: 175%, 300%;
166
+ stroke-dashoffset: -68%;
167
+ }
168
+
169
+ 100% {
170
+ stroke-dasharray: 2%, 300%;
171
+ stroke-dashoffset: -302%;
172
+ }
173
+ }
174
+ }
@@ -0,0 +1,39 @@
1
+ @layer astryx.reset, astryx.tokens, astryx.base, astryx.components, astryx.overrides;
2
+
3
+ @layer astryx.components {
4
+ /* 圆形的「i」按钮,对齐周围的文字基线。 */
5
+ .astryx-info-tip {
6
+ display: inline-flex;
7
+ align-items: center;
8
+ justify-content: center;
9
+ vertical-align: middle;
10
+ padding: var(--astryx-spacing-0-5, 2px);
11
+ margin: 0;
12
+ border-style: none;
13
+ background-color: transparent;
14
+ border-radius: var(--astryx-radius-full, 9999px);
15
+ cursor: pointer;
16
+ color: var(--astryx-color-icon-secondary, light-dark(#6a6a6a, #9e9e9e));
17
+ transition-property: color;
18
+ transition-duration: var(--astryx-duration-fast, 125ms);
19
+ transition-timing-function: var(--astryx-ease-standard, cubic-bezier(0.24, 1, 0.4, 1));
20
+ }
21
+
22
+ /* 指针设备上悬停时换成更清楚的主图标色。 */
23
+ @media (hover: hover) {
24
+ .astryx-info-tip:hover:where(:not(:disabled, [aria-disabled='true'])) {
25
+ color: var(--astryx-color-icon-primary, light-dark(#000000, #ffffff));
26
+ }
27
+ }
28
+
29
+ /* 禁用时指针样式和普通文字一致,不做「不可点」的暗示。 */
30
+ .astryx-info-tip:is(:disabled, [aria-disabled='true']) {
31
+ cursor: default;
32
+ }
33
+
34
+ /* 键盘聚焦时的焦点环。 */
35
+ .astryx-info-tip:focus-visible {
36
+ outline: var(--astryx-focus-outline-width, 2px) var(--astryx-focus-outline-style, solid) var(--astryx-focus-outline-color, currentcolor);
37
+ outline-offset: var(--astryx-focus-outline-offset, 3px);
38
+ }
39
+ }
@@ -0,0 +1,206 @@
1
+ @layer astryx.reset, astryx.tokens, astryx.base, astryx.components, astryx.overrides;
2
+
3
+ @layer astryx.components {
4
+ /*
5
+ * 记录列表。容器查询决定是「一行排所有列」(宽)还是「每列一行堆叠」(窄),
6
+ * 和原版一样在 640px 处切换。
7
+ */
8
+ .astryx-list-input {
9
+ display: flex;
10
+ flex-direction: column;
11
+ min-width: 0;
12
+ width: 100%;
13
+ overflow-anchor: none;
14
+ container-type: inline-size;
15
+ container-name: list-input;
16
+ }
17
+
18
+ .astryx-list-input__list {
19
+ display: flex;
20
+ flex-direction: column;
21
+ gap: var(--astryx-spacing-2, 8px);
22
+ list-style: none;
23
+ margin: 0;
24
+ padding: 0;
25
+ }
26
+
27
+ .astryx-list-input__empty-item {
28
+ width: 100%;
29
+ }
30
+
31
+ /* 空状态在空白区里居中,用四周边距撑出竖向空间。 */
32
+ .astryx-list-input__empty-item .astryx-empty-state {
33
+ align-items: center;
34
+ justify-content: center;
35
+ padding-block: var(--astryx-spacing-6, 24px);
36
+ text-align: center;
37
+ }
38
+
39
+ .astryx-list-input__item {
40
+ display: flex;
41
+ flex-direction: column;
42
+ min-width: 0;
43
+ position: relative;
44
+ }
45
+
46
+ /*
47
+ * 一行:先是所有字段,然后是删除按钮,最后是拖动手柄。
48
+ * 控件列宽固定成一格控件的大小,字段列吃掉剩下的空间。
49
+ */
50
+ .astryx-list-input__row {
51
+ display: grid;
52
+ align-items: end;
53
+ grid-template-columns: minmax(0, 1fr) auto auto;
54
+ column-gap: var(--astryx-spacing-1, 4px);
55
+ row-gap: 0;
56
+ min-width: 0;
57
+ }
58
+
59
+ .astryx-list-input__fields {
60
+ display: grid;
61
+ align-items: end;
62
+ gap: var(--astryx-spacing-1, 4px);
63
+ min-width: 0;
64
+ }
65
+
66
+ .astryx-list-input__cell {
67
+ min-width: 0;
68
+ }
69
+
70
+ /* 列名只在第一条上显示;窄屏时每条都显示,按下标由标记切换。 */
71
+ .astryx-list-input__column-label {
72
+ display: block;
73
+ margin-block-end: var(--astryx-spacing-0-5, 2px);
74
+ color: var(--astryx-color-text-primary, light-dark(#0a0a0a, #fafafa));
75
+ font-size: var(--astryx-text-label-size, 0.8125rem);
76
+ line-height: var(--astryx-text-label-leading, 1.3);
77
+ font-weight: var(--astryx-font-weight-medium, 500);
78
+ }
79
+
80
+ .astryx-list-input__column-label[data-list-input-repeated] {
81
+ display: none;
82
+ }
83
+
84
+ .astryx-list-input__cell-content {
85
+ min-width: 0;
86
+ }
87
+
88
+ .astryx-list-input__control-cell {
89
+ display: flex;
90
+ align-items: center;
91
+ align-self: end;
92
+ justify-content: center;
93
+ min-height: var(--astryx-size-element-md, 32px);
94
+ }
95
+
96
+ /* 记录本身的状态消息占满整行,跟在字段和控件下面。 */
97
+ .astryx-list-input__item-status {
98
+ grid-column: 1 / -1;
99
+ grid-row: 2;
100
+ margin-block-start: var(--astryx-spacing-1, 4px);
101
+ min-width: 0;
102
+ }
103
+
104
+ /* 添加按钮和字段列对齐,宽度撑满。 */
105
+ .astryx-list-input__action-row {
106
+ display: grid;
107
+ align-items: center;
108
+ grid-template-columns: minmax(0, 1fr) auto auto;
109
+ column-gap: var(--astryx-spacing-1, 4px);
110
+ margin-block-start: var(--astryx-spacing-2, 8px);
111
+ }
112
+
113
+ .astryx-list-input__action-content {
114
+ min-width: 0;
115
+ }
116
+
117
+ /* 拖动中的源行半透明,说明它是被拿起来那一行。 */
118
+ .astryx-list-input__row--dragging {
119
+ opacity: 0.5;
120
+ user-select: none;
121
+ }
122
+
123
+ /* 落点指示线画在整行的上边或下边。 */
124
+ .astryx-list-input__item[data-list-input-drop-target] {
125
+ position: relative;
126
+ }
127
+
128
+ .astryx-list-input__item[data-list-input-drop-target]::before,
129
+ .astryx-list-input__item[data-list-input-drop-target]::after {
130
+ content: '';
131
+ position: absolute;
132
+ inset-inline: 0;
133
+ height: var(--astryx-spacing-0-5, 2px);
134
+ border-radius: var(--astryx-radius-full, 9999px);
135
+ background-color: var(--astryx-color-accent, light-dark(#0a0a0a, #fafafa));
136
+ pointer-events: none;
137
+ z-index: 2;
138
+ }
139
+
140
+ .astryx-list-input__item[data-list-input-drop-target='before']::before {
141
+ inset-block-start: calc(-1 * var(--astryx-spacing-0-5, 2px));
142
+ }
143
+
144
+ .astryx-list-input__item[data-list-input-drop-target='after']::after {
145
+ inset-block-end: calc(-1 * var(--astryx-spacing-0-5, 2px));
146
+ }
147
+
148
+ /* 手柄给出抓取手势,并且吃掉触摸滚动,否则手机上竖着拖会变成滚动页面。 */
149
+ .astryx-list-input__reorder-handle {
150
+ cursor: grab;
151
+ touch-action: none;
152
+ }
153
+
154
+ .astryx-list-input__reorder-handle[aria-pressed='true'] {
155
+ background-color: var(--astryx-color-accent-muted, light-dark(#f1f1f1, #262626));
156
+ cursor: grabbing;
157
+ }
158
+
159
+ .astryx-list-input__reorder-handle:is(:disabled, [aria-disabled='true']) {
160
+ cursor: default;
161
+ }
162
+
163
+ /* 跟着指针走的克隆体。top / left / width 由组件内联给出。 */
164
+ .astryx-list-input__drag-preview {
165
+ position: fixed;
166
+ z-index: 2147483000;
167
+ opacity: 0.5;
168
+ pointer-events: none;
169
+ user-select: none;
170
+ will-change: transform;
171
+ margin: 0;
172
+ container-type: inline-size;
173
+ container-name: list-input;
174
+ width: 100%;
175
+ }
176
+
177
+ /* 窄的时候每列占一行,行与行之间松一点。 */
178
+ @container list-input (max-width: 640px) {
179
+ .astryx-list-input__list {
180
+ gap: var(--astryx-spacing-8, 32px);
181
+ }
182
+
183
+ .astryx-list-input__row {
184
+ grid-template-columns: minmax(0, 1fr) auto auto;
185
+ row-gap: var(--astryx-spacing-2, 8px);
186
+ }
187
+
188
+ .astryx-list-input__fields {
189
+ grid-template-columns: minmax(0, 1fr);
190
+ gap: var(--astryx-spacing-2, 8px);
191
+ }
192
+
193
+ .astryx-list-input__column-label {
194
+ margin-block-end: var(--astryx-spacing-1, 4px);
195
+ }
196
+
197
+ .astryx-list-input__column-label[data-list-input-repeated] {
198
+ display: block;
199
+ }
200
+
201
+ .astryx-list-input__item-status {
202
+ grid-row: auto;
203
+ margin-block-start: 0;
204
+ }
205
+ }
206
+ }