imean-service-engine-htmx-plugin 2.5.0 → 2.7.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/dist/index.js CHANGED
@@ -80,7 +80,6 @@ function renderActionButton(action, index) {
80
80
  close,
81
81
  submit,
82
82
  formId,
83
- onClick,
84
83
  className = "",
85
84
  target
86
85
  } = action;
@@ -106,8 +105,7 @@ function renderActionButton(action, index) {
106
105
  index
107
106
  );
108
107
  }
109
- const finalOnClick = close ? CLOSE_DIALOG_SCRIPT : onClick;
110
- if (finalOnClick) {
108
+ if (close) {
111
109
  const variantStyles = {
112
110
  primary: "bg-blue-600 text-white hover:bg-blue-700",
113
111
  secondary: "bg-gray-200 text-gray-800 hover:bg-gray-300",
@@ -125,7 +123,8 @@ function renderActionButton(action, index) {
125
123
  "button",
126
124
  {
127
125
  type: "button",
128
- _: finalOnClick,
126
+ "x-data": CLOSE_DIALOG_ALPINE_DATA,
127
+ "x-on:click": "closeDialog()",
129
128
  className: `px-4 py-2 rounded transition-colors font-medium ${buttonStyle} ${className}`,
130
129
  "data-testid": testId2,
131
130
  ...confirm && { "data-confirm": confirm },
@@ -169,15 +168,25 @@ function renderActionButton(action, index) {
169
168
  function renderActionButtons(actions) {
170
169
  return actions.map((action, index) => renderActionButton(action, index));
171
170
  }
172
- var CLOSE_DIALOG_SCRIPT;
171
+ var CLOSE_DIALOG_ALPINE_DATA;
173
172
  var init_action_button_renderer = __esm({
174
173
  "src/utils/action-button-renderer.tsx"() {
175
174
  init_button();
176
- CLOSE_DIALOG_SCRIPT = `on click
177
- add .dialog-exit to .dialog-backdrop
178
- add .dialog-content-exit to .dialog-content
179
- wait 200ms
180
- set #dialog-container's innerHTML to '' end`;
175
+ CLOSE_DIALOG_ALPINE_DATA = `{
176
+ closeDialog() {
177
+ const backdrop = this.$el.closest('.dialog-backdrop');
178
+ if (backdrop) {
179
+ backdrop.classList.add('dialog-exit');
180
+ const content = backdrop.querySelector('.dialog-content');
181
+ if (content) {
182
+ content.classList.add('dialog-content-exit');
183
+ }
184
+ setTimeout(() => {
185
+ backdrop.remove();
186
+ }, 200);
187
+ }
188
+ }
189
+ }`;
181
190
  }
182
191
  });
183
192
 
@@ -307,11 +316,6 @@ var init_cdn_cache = __esm({
307
316
  url: "https://unpkg.com/htmx.org@latest",
308
317
  mimeType: "application/javascript"
309
318
  },
310
- {
311
- name: "hyperscript",
312
- url: "https://unpkg.com/hyperscript.org@latest",
313
- mimeType: "application/javascript"
314
- },
315
319
  {
316
320
  name: "tailwindcss",
317
321
  url: "https://cdn.tailwindcss.com",
@@ -321,16 +325,6 @@ var init_cdn_cache = __esm({
321
325
  name: "alpinejs",
322
326
  url: "https://unpkg.com/alpinejs@latest/dist/cdn.min.js",
323
327
  mimeType: "application/javascript"
324
- },
325
- {
326
- name: "idiomorph",
327
- url: "https://unpkg.com/idiomorph@0.7.4/dist/idiomorph-ext.min.js",
328
- mimeType: "application/javascript"
329
- },
330
- {
331
- name: "sortablejs",
332
- url: "https://unpkg.com/sortablejs@latest/Sortable.min.js",
333
- mimeType: "application/javascript"
334
328
  }
335
329
  ];
336
330
  cache = /* @__PURE__ */ new Map();
@@ -555,8 +549,9 @@ function renderFormField(field, initialData, formFieldRenderers) {
555
549
  /* @__PURE__ */ jsxRuntime.jsx("div", { children: customRenderer({
556
550
  field,
557
551
  value: parsedValue,
558
- initialData,
559
- fieldName: field.name
552
+ item: initialData || {},
553
+ name: field.name,
554
+ label: field.label
560
555
  }) }),
561
556
  field.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 mt-1", children: field.description })
562
557
  ]
@@ -1853,7 +1848,12 @@ function renderDefaultValue(value) {
1853
1848
  function renderField(field, value, item) {
1854
1849
  let content;
1855
1850
  if (field.render) {
1856
- const rendered = field.render(value, item);
1851
+ const rendered = field.render({
1852
+ value,
1853
+ item,
1854
+ name: field.key,
1855
+ label: field.label
1856
+ });
1857
1857
  if (rendered === null || rendered === void 0) {
1858
1858
  content = /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: "-" });
1859
1859
  } else if (typeof rendered === "string" || typeof rendered === "number" || typeof rendered === "boolean") {
@@ -1989,11 +1989,14 @@ var DefaultDetailFeature = class extends BaseFeature {
1989
1989
  const groupFields = groupSchemas.map(
1990
1990
  ({ label, schema: schema2, fields: fieldNames }) => {
1991
1991
  const groupFields2 = parseSchemaToFields(schema2);
1992
- const detailFields2 = groupFields2.map((field) => ({
1993
- key: field.name,
1994
- label: field.label,
1995
- render: this.fieldRenderers?.[field.name]
1996
- }));
1992
+ const detailFields2 = groupFields2.map((field) => {
1993
+ const renderer = this.fieldRenderers?.[field.name];
1994
+ return {
1995
+ key: field.name,
1996
+ label: field.label,
1997
+ render: renderer
1998
+ };
1999
+ });
1997
2000
  return {
1998
2001
  label,
1999
2002
  fields: detailFields2,
@@ -2025,12 +2028,16 @@ var DefaultDetailFeature = class extends BaseFeature {
2025
2028
  }
2026
2029
  }
2027
2030
  const detailFieldNames = getFieldNamesFromFields(detailFields);
2028
- const fields = detailFieldNames.map((fieldName) => ({
2029
- key: fieldName,
2030
- label: getFieldLabelFromFields(this.fields || [], fieldName) || fieldName,
2031
- render: this.fieldRenderers?.[fieldName]
2032
- // 如果有自定义渲染函数则使用
2033
- }));
2031
+ const fields = detailFieldNames.map((fieldName) => {
2032
+ const label = getFieldLabelFromFields(this.fields || [], fieldName) || fieldName;
2033
+ const renderer = this.fieldRenderers?.[fieldName];
2034
+ return {
2035
+ key: fieldName,
2036
+ label,
2037
+ // 直接使用统一的渲染器类型
2038
+ render: renderer
2039
+ };
2040
+ });
2034
2041
  return /* @__PURE__ */ jsxRuntime.jsx(DetailPage, { item, fields });
2035
2042
  }
2036
2043
  async getActions(context) {
@@ -2809,286 +2816,6 @@ var DefaultListFeature = class extends BaseFeature {
2809
2816
  }
2810
2817
  };
2811
2818
 
2812
- // src/component-system/store.ts
2813
- var STATE_EXPIRATION_TIME = 864e5;
2814
- var StateStore = class _StateStore {
2815
- static instance;
2816
- state;
2817
- constructor() {
2818
- this.state = /* @__PURE__ */ new Map();
2819
- setInterval(() => {
2820
- this.state.forEach((state) => {
2821
- if (state.lastUpdated < Date.now() - STATE_EXPIRATION_TIME) {
2822
- this.state.delete(state.instanceId);
2823
- }
2824
- });
2825
- }, 3e4);
2826
- }
2827
- static get() {
2828
- if (!_StateStore.instance) {
2829
- _StateStore.instance = new _StateStore();
2830
- }
2831
- return _StateStore.instance;
2832
- }
2833
- /** 获取实例状态 */
2834
- getState(instanceId) {
2835
- const state = this.state.get(instanceId);
2836
- if (state) {
2837
- return state;
2838
- }
2839
- const newState = {
2840
- instanceId,
2841
- data: {
2842
- props: {},
2843
- state: {}
2844
- },
2845
- lastUpdated: Date.now()
2846
- };
2847
- this.state.set(instanceId, newState);
2848
- return newState;
2849
- }
2850
- /** 设置实例状态 */
2851
- setState(instanceId, state) {
2852
- const instanceState = this.getState(instanceId);
2853
- instanceState.data = Object.assign(instanceState.data.state, state);
2854
- instanceState.lastUpdated = Date.now();
2855
- }
2856
- };
2857
-
2858
- // src/component-system/utils.ts
2859
- var globalIdCounter = 0;
2860
- function generateUniqueId() {
2861
- return `htmx-cid-${globalIdCounter++}`;
2862
- }
2863
- var HTMX_COMPONENT_PREFIX = "/_htmx_components";
2864
-
2865
- // src/component-system/context.tsx
2866
- var RenderContext = class {
2867
- constructor(prefix, instanceId, componentName) {
2868
- this.prefix = prefix;
2869
- this.instanceId = instanceId;
2870
- this.componentName = componentName;
2871
- }
2872
- // 生成唯一 ID(使用全局共享计数器,避免冲突)
2873
- $id() {
2874
- return generateUniqueId();
2875
- }
2876
- setState(state) {
2877
- StateStore.get().setState(this.instanceId, state);
2878
- }
2879
- get state() {
2880
- return StateStore.get().getState(this.instanceId).data.state;
2881
- }
2882
- get props() {
2883
- return StateStore.get().getState(this.instanceId).data.props;
2884
- }
2885
- // 生成方法 URL(使用当前 instanceId)
2886
- url(methodName, params) {
2887
- const baseUrl = `${this.prefix}/${HTMX_COMPONENT_PREFIX}/${this.componentName}/${this.instanceId}/${methodName}`;
2888
- if (params && Object.keys(params).length > 0) {
2889
- const queryString = new URLSearchParams(
2890
- Object.entries(params).reduce(
2891
- (acc, [key, value]) => {
2892
- acc[key] = String(value);
2893
- return acc;
2894
- },
2895
- {}
2896
- )
2897
- ).toString();
2898
- return `${baseUrl}?${queryString}`;
2899
- }
2900
- return baseUrl;
2901
- }
2902
- callMethod(methodName, params) {
2903
- const selectors = Object.entries(params).map(([name, expression]) => `${name}:${expression}`).join(",");
2904
- return {
2905
- "hx-post": this.url(methodName),
2906
- "hx-vals": `js:{_params_:{${selectors}}}`,
2907
- "hx-params": "_state_,_params_,_this_value_"
2908
- };
2909
- }
2910
- };
2911
- var ComponentContext = class extends RenderContext {
2912
- constructor(prefix, ctx, componentName) {
2913
- const routeParams = ctx.req.param();
2914
- const instanceId = String(routeParams.instanceId || "");
2915
- super(prefix, instanceId, componentName);
2916
- this.ctx = ctx;
2917
- }
2918
- // 获取所有参数(统一接口:聚合 query string 和 body)
2919
- async params() {
2920
- const params = {};
2921
- const routeParams = this.ctx.req.param();
2922
- Object.assign(params, routeParams);
2923
- const url = new URL(this.ctx.req.url);
2924
- for (const [key, value] of url.searchParams.entries()) {
2925
- params[key] = value;
2926
- }
2927
- const contentType = this.ctx.req.header("Content-Type") || "";
2928
- if (contentType.includes("application/json")) {
2929
- try {
2930
- const body = await this.ctx.req.json();
2931
- Object.assign(params, body);
2932
- } catch (e) {
2933
- console.warn("[ComponentContext] Failed to parse JSON body:", e);
2934
- }
2935
- } else if (this.ctx.req.method === "POST" || this.ctx.req.method === "PUT" || this.ctx.req.method === "PATCH" || this.ctx.req.method === "DELETE") {
2936
- try {
2937
- const formData = await this.ctx.req.formData();
2938
- for (const [key, value] of formData.entries()) {
2939
- params[key] = value instanceof File ? value : value.toString();
2940
- }
2941
- } catch (e) {
2942
- console.warn("[ComponentContext] Failed to parse form data:", e);
2943
- }
2944
- }
2945
- return params;
2946
- }
2947
- // 获取查询参数
2948
- query() {
2949
- const url = new URL(this.ctx.req.url);
2950
- return Object.fromEntries(url.searchParams.entries());
2951
- }
2952
- // 获取请求体
2953
- async body() {
2954
- const contentType = this.ctx.req.header("Content-Type") || "";
2955
- if (contentType.includes("application/json")) {
2956
- try {
2957
- return await this.ctx.req.json();
2958
- } catch (e) {
2959
- console.warn("[ComponentContext] Failed to parse JSON body:", e);
2960
- return {};
2961
- }
2962
- }
2963
- try {
2964
- const formData = await this.ctx.req.formData();
2965
- const body = {};
2966
- for (const [key, value] of formData.entries()) {
2967
- body[key] = value instanceof File ? value : value.toString();
2968
- }
2969
- return body;
2970
- } catch (e) {
2971
- console.warn("[ComponentContext] Failed to parse form data:", e);
2972
- return {};
2973
- }
2974
- }
2975
- };
2976
-
2977
- // src/component-system/component.tsx
2978
- var METHOD_METADATA_KEY = /* @__PURE__ */ Symbol("htmx:method");
2979
- function Method(config) {
2980
- return function(target, propertyKey, _descriptor) {
2981
- if (!target[METHOD_METADATA_KEY]) {
2982
- target[METHOD_METADATA_KEY] = /* @__PURE__ */ new Map();
2983
- }
2984
- target[METHOD_METADATA_KEY].set(propertyKey, {
2985
- method: config?.method || "get",
2986
- path: config?.path
2987
- });
2988
- };
2989
- }
2990
- var HtmxComponent = class {
2991
- constructor(name) {
2992
- this.name = name;
2993
- }
2994
- prefix;
2995
- // 组件函数
2996
- Component = (props) => {
2997
- const instanceId = generateUniqueId();
2998
- const state = StateStore.get().getState(instanceId).data;
2999
- state.props = props;
3000
- state.state = {};
3001
- const renderCtx = new RenderContext(
3002
- this.prefix,
3003
- instanceId,
3004
- this.name
3005
- );
3006
- return this.render(renderCtx, props);
3007
- };
3008
- // 返回 JSX script 元素
3009
- // 获取所有标记为 @Method() 的方法
3010
- // 注意:handler 不再绑定 this,因为方法会接收 ComponentContext 作为第一个参数
3011
- static getMethods(component) {
3012
- const methods = /* @__PURE__ */ new Map();
3013
- const metadata = component[METHOD_METADATA_KEY];
3014
- if (!metadata) return methods;
3015
- for (const [methodName, config] of metadata.entries()) {
3016
- const handler = component[methodName];
3017
- methods.set(methodName, {
3018
- method: config.method,
3019
- path: config.path,
3020
- handler
3021
- });
3022
- }
3023
- return methods;
3024
- }
3025
- };
3026
- var HtmxComponentHandler = class {
3027
- constructor(hono, prefix, components) {
3028
- this.hono = hono;
3029
- this.prefix = prefix;
3030
- this.components = components;
3031
- for (const component of this.components) {
3032
- component.prefix = this.prefix;
3033
- this.registerHandler(component);
3034
- }
3035
- }
3036
- registerHandler(component) {
3037
- const methods = HtmxComponent.getMethods(component);
3038
- for (const [methodName, methodConfig] of methods) {
3039
- const routePath = `${this.prefix}/${HTMX_COMPONENT_PREFIX}/${component.name}/:instanceId/${methodName}`;
3040
- imeanServiceEngine.logger.info(
3041
- `[HtmxComponent] Registering handler ${methodConfig.method} ${routePath}`
3042
- );
3043
- this.hono[methodConfig.method](routePath, async (ctx) => {
3044
- return this.handleComponentMethod(
3045
- ctx,
3046
- component,
3047
- methodConfig.handler.bind(component)
3048
- );
3049
- });
3050
- }
3051
- }
3052
- async handleComponentMethod(ctx, component, handler) {
3053
- const componentContext = new ComponentContext(
3054
- this.prefix,
3055
- ctx,
3056
- component.name
3057
- );
3058
- const result = await handler(componentContext);
3059
- if (result instanceof Object && ("target" in result || "swap" in result || "body" in result || "oobs" in result)) {
3060
- const { target, swap, body, oobs, trigger } = result;
3061
- const headers = {};
3062
- let bodyContent = body;
3063
- if (target) headers["HX-Retarget"] = target;
3064
- if (swap) headers["HX-Reswap"] = swap;
3065
- if (trigger) headers["HX-Trigger"] = trigger;
3066
- if (oobs) {
3067
- oobs.forEach((oob) => {
3068
- oob.props["hx-swap-oob"] = "true";
3069
- });
3070
- if (!body) {
3071
- headers["HX-Reswap"] = "delete";
3072
- }
3073
- bodyContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3074
- body,
3075
- oobs
3076
- ] });
3077
- }
3078
- return ctx.html(bodyContent, 200, headers);
3079
- }
3080
- if (result === null) {
3081
- return ctx.html(/* @__PURE__ */ jsxRuntime.jsx("div", {}), 200, {
3082
- "HX-Reswap": "none"
3083
- });
3084
- }
3085
- if (result instanceof Response) {
3086
- return result;
3087
- }
3088
- return ctx.html(result, 200);
3089
- }
3090
- };
3091
-
3092
2819
  // src/utils/path.ts
3093
2820
  function modelNameToPath(modelName) {
3094
2821
  return `/${modelName.replace(/([A-Z])/g, "-$1").toLowerCase()}`;
@@ -3100,8 +2827,9 @@ function ErrorAlert(props) {
3100
2827
  type = "error",
3101
2828
  showClose = true,
3102
2829
  className = "",
3103
- autoClose = 5e3
2830
+ autoClose = 5e3,
3104
2831
  // 默认 5 秒自动关闭
2832
+ "hx-swap-oob": hxSwapOob
3105
2833
  } = props;
3106
2834
  const typeClasses = {
3107
2835
  error: "bg-red-50 border-red-200 text-red-800",
@@ -3190,12 +2918,20 @@ function ErrorAlert(props) {
3190
2918
  const now = Date.now();
3191
2919
  this.elapsedTime = now - this.startTime;
3192
2920
  this.progress = Math.max(0, 100 - (this.elapsedTime / this.autoCloseDelay) * 100);
2921
+ // \u66F4\u65B0\u8FDB\u5EA6\u6761\u6837\u5F0F
2922
+ const progressBar = this.$el.querySelector('.progress-bar');
2923
+ if (progressBar) {
2924
+ progressBar.style.width = this.progress + '%';
2925
+ }
3193
2926
  if (this.progress > 0 && this.visible) {
3194
2927
  this.progressTimer = requestAnimationFrame(updateProgress);
3195
2928
  }
3196
2929
  };
3197
2930
  this.progressTimer = requestAnimationFrame(updateProgress);
3198
2931
  },
2932
+ getProgressStyle() {
2933
+ return { width: this.progress + '%' };
2934
+ },
3199
2935
  pauseAutoClose() {
3200
2936
  if (this.autoCloseTimer) {
3201
2937
  clearTimeout(this.autoCloseTimer);
@@ -3224,6 +2960,9 @@ function ErrorAlert(props) {
3224
2960
  cancelAnimationFrame(this.progressTimer);
3225
2961
  this.progressTimer = null;
3226
2962
  }
2963
+ // \u6DFB\u52A0\u6DE1\u51FA\u52A8\u753B
2964
+ this.$el.classList.add('error-alert-exit');
2965
+ // \u52A8\u753B\u7ED3\u675F\u540E\u5B8C\u5168\u79FB\u9664\u5143\u7D20\uFF0C\u4E0D\u7559\u7A7A div
3227
2966
  setTimeout(() => {
3228
2967
  if (this.$el && this.$el.parentNode) {
3229
2968
  this.$el.parentNode.removeChild(this.$el);
@@ -3234,6 +2973,9 @@ function ErrorAlert(props) {
3234
2973
  visible: true,
3235
2974
  close() {
3236
2975
  this.visible = false;
2976
+ // \u6DFB\u52A0\u6DE1\u51FA\u52A8\u753B
2977
+ this.$el.classList.add('error-alert-exit');
2978
+ // \u52A8\u753B\u7ED3\u675F\u540E\u5B8C\u5168\u79FB\u9664\u5143\u7D20\uFF0C\u4E0D\u7559\u7A7A div
3237
2979
  setTimeout(() => {
3238
2980
  if (this.$el && this.$el.parentNode) {
3239
2981
  this.$el.parentNode.removeChild(this.$el);
@@ -3255,18 +2997,26 @@ function ErrorAlert(props) {
3255
2997
  alpineProps["@mouseenter"] = "pauseAutoClose()";
3256
2998
  alpineProps["@mouseleave"] = "resumeAutoClose()";
3257
2999
  }
3000
+ const htmxProps = {};
3001
+ if (hxSwapOob) {
3002
+ htmxProps["hx-swap-oob"] = hxSwapOob;
3003
+ }
3258
3004
  return /* @__PURE__ */ jsxRuntime.jsxs(
3259
3005
  "div",
3260
3006
  {
3261
3007
  ...alpineProps,
3262
- className: `border rounded-lg p-4 shadow-lg mb-2 ${typeClasses[type]} ${className} relative overflow-hidden`,
3008
+ ...htmxProps,
3009
+ className: `w-full border rounded-lg p-4 shadow-lg ${typeClasses[type]} ${className} relative overflow-hidden pointer-events-auto`,
3263
3010
  role: "alert",
3011
+ style: {
3012
+ animation: "slideInRight 0.3s ease-out"
3013
+ },
3264
3014
  children: [
3265
3015
  autoClose > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-0 left-0 right-0 h-1 bg-gray-200", children: /* @__PURE__ */ jsxRuntime.jsx(
3266
3016
  "div",
3267
3017
  {
3268
- className: `h-full ${progressBarColors[type]}`,
3269
- ...{ ":style": "`width: ${progress}%`" }
3018
+ className: `h-full ${progressBarColors[type]} progress-bar`,
3019
+ ...{ "x-bind:style": "getProgressStyle()" }
3270
3020
  }
3271
3021
  ) }),
3272
3022
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start", children: [
@@ -3385,6 +3135,9 @@ async function createFeatureContext(ctx, page, feature, user, options) {
3385
3135
  };
3386
3136
  return featureContext;
3387
3137
  }
3138
+ function generateDialogId() {
3139
+ return `dialog-${Date.now()}}`;
3140
+ }
3388
3141
  function Dialog(props) {
3389
3142
  const {
3390
3143
  title,
@@ -3396,6 +3149,7 @@ function Dialog(props) {
3396
3149
  actions = [],
3397
3150
  fixedContentHeight = false
3398
3151
  } = props;
3152
+ const dialogId = generateDialogId();
3399
3153
  const sizeClasses = {
3400
3154
  sm: "max-w-md",
3401
3155
  md: "max-w-lg",
@@ -3403,19 +3157,33 @@ function Dialog(props) {
3403
3157
  xl: "max-w-4xl",
3404
3158
  full: "max-w-7xl"
3405
3159
  };
3406
- const backdropClickHandler = closeOnBackdropClick ? `on click if event.target is me
3407
- add .dialog-exit to me
3408
- add .dialog-content-exit to .dialog-content
3409
- wait 200ms
3410
- set #dialog-container's innerHTML to '' end` : "";
3160
+ const alpineData = `{
3161
+ closeDialog() {
3162
+ const backdrop = this.$el.closest('.dialog-backdrop');
3163
+ if (backdrop) {
3164
+ backdrop.classList.add('dialog-exit');
3165
+ const content = backdrop.querySelector('.dialog-content');
3166
+ if (content) {
3167
+ content.classList.add('dialog-content-exit');
3168
+ }
3169
+ setTimeout(() => {
3170
+ backdrop.remove();
3171
+ }, 200);
3172
+ }
3173
+ }
3174
+ }`;
3411
3175
  return /* @__PURE__ */ jsxRuntime.jsx(
3412
3176
  "div",
3413
3177
  {
3178
+ id: dialogId,
3414
3179
  className: "fixed inset-0 bg-black bg-opacity-50 z-[100] flex items-center justify-center p-4 dialog-backdrop",
3415
3180
  style: {
3416
3181
  animation: "fadeIn 0.2s ease-out"
3417
3182
  },
3418
- _: backdropClickHandler,
3183
+ "x-data": alpineData,
3184
+ ...closeOnBackdropClick && {
3185
+ "x-on:click": "if ($event.target === $el) closeDialog()"
3186
+ },
3419
3187
  children: /* @__PURE__ */ jsxRuntime.jsxs(
3420
3188
  "div",
3421
3189
  {
@@ -3423,7 +3191,7 @@ function Dialog(props) {
3423
3191
  style: {
3424
3192
  animation: "slideIn 0.3s ease-out"
3425
3193
  },
3426
- _: "on click call event.stopPropagation()",
3194
+ ...{ "x-on:click.stop": "" },
3427
3195
  children: [
3428
3196
  (title || showClose) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 border-b border-gray-200 bg-white flex items-center justify-between", children: [
3429
3197
  title && /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-900", children: title }),
@@ -3431,7 +3199,7 @@ function Dialog(props) {
3431
3199
  "button",
3432
3200
  {
3433
3201
  className: "text-gray-400 hover:text-gray-600 transition-colors",
3434
- _: "on click \r\n add .dialog-exit to .dialog-backdrop\r\n add .dialog-content-exit to .dialog-content\r\n wait 200ms\r\n set #dialog-container's innerHTML to '' end",
3202
+ "x-on:click": "closeDialog()",
3435
3203
  children: /* @__PURE__ */ jsxRuntime.jsx(
3436
3204
  "svg",
3437
3205
  {
@@ -3528,11 +3296,26 @@ function PermissionDeniedContent(props) {
3528
3296
  Button,
3529
3297
  {
3530
3298
  variant: "secondary",
3531
- _: "on click set #dialog-container's innerHTML to ''",
3299
+ "x-data": `{
3300
+ closeDialog() {
3301
+ const backdrop = this.$el.closest('.dialog-backdrop');
3302
+ if (backdrop) {
3303
+ backdrop.classList.add('dialog-exit');
3304
+ const content = backdrop.querySelector('.dialog-content');
3305
+ if (content) {
3306
+ content.classList.add('dialog-content-exit');
3307
+ }
3308
+ setTimeout(() => {
3309
+ backdrop.remove();
3310
+ }, 200);
3311
+ }
3312
+ }
3313
+ }`,
3314
+ "x-on:click": "closeDialog()",
3532
3315
  children: "\u5173\u95ED"
3533
3316
  }
3534
3317
  ),
3535
- !isDialog && userInfo && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "secondary", _: "on click window.history.back()", children: "\u8FD4\u56DE" })
3318
+ !isDialog && userInfo && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "secondary", onclick: "window.history.back()", children: "\u8FD4\u56DE" })
3536
3319
  ] })
3537
3320
  ] });
3538
3321
  }
@@ -3553,13 +3336,8 @@ var getResourceUrl = (prefix, name) => {
3553
3336
  function globalScripts(prefix) {
3554
3337
  return html.html`
3555
3338
  <script src=${getResourceUrl(prefix, "htmx")}></script>
3556
- <script src=${getResourceUrl(prefix, "htmx-ext-form-json")}></script>
3557
- <script src=${getResourceUrl(prefix, "hyperscript")}></script>
3558
3339
  <script src=${getResourceUrl(prefix, "tailwindcss")}></script>
3559
3340
  <script src=${getResourceUrl(prefix, "alpinejs")} defer></script>
3560
- <script src=${getResourceUrl(prefix, "sortablejs")}></script>
3561
- <script src=${getResourceUrl(prefix, "idiomorph")}></script>
3562
- <script type="module" src=${getResourceUrl(prefix, "datastar")}></script>
3563
3341
  `;
3564
3342
  }
3565
3343
  function globalStyles() {
@@ -3689,36 +3467,6 @@ function globalStyles() {
3689
3467
  }
3690
3468
  </style>`;
3691
3469
  }
3692
- function sortableScript() {
3693
- html.html`<script>
3694
- if (typeof htmx !== "undefined" && typeof Sortable !== "undefined") {
3695
- htmx.onLoad(function (content) {
3696
- var sortables = content.querySelectorAll(".sortable");
3697
- for (var i = 0; i < sortables.length; i++) {
3698
- var sortable = sortables[i];
3699
- // 检查是否已经初始化
3700
- if (sortable.sortableInstance) {
3701
- continue;
3702
- }
3703
- var sortableInstance = new Sortable(sortable, {
3704
- animation: 150,
3705
- ghostClass: "sortable-ghost",
3706
- filter: ".htmx-indicator",
3707
- onMove: function (evt) {
3708
- return evt.related.className.indexOf("htmx-indicator") === -1;
3709
- },
3710
- });
3711
- sortable.sortableInstance = sortableInstance;
3712
- sortable.addEventListener("htmx:afterSwap", function () {
3713
- if (sortable.sortableInstance) {
3714
- sortable.sortableInstance.option("disabled", false);
3715
- }
3716
- });
3717
- }
3718
- });
3719
- }
3720
- </script>`;
3721
- }
3722
3470
  function Breadcrumb(props) {
3723
3471
  const { items } = props;
3724
3472
  if (items.length === 0) {
@@ -4074,193 +3822,183 @@ function SortableList(props) {
4074
3822
  function StringArrayEditor(props) {
4075
3823
  const {
4076
3824
  value,
4077
- fieldName,
4078
3825
  placeholder = "\u8BF7\u8F93\u5165\u5185\u5BB9",
4079
3826
  allowEmpty = false,
4080
3827
  rows = 1
4081
3828
  } = props;
3829
+ const name = props.name;
4082
3830
  const initialItems = value || [];
4083
3831
  const initialDataJson = JSON.stringify({
4084
3832
  items: initialItems.map((item) => item || ""),
4085
- fieldName,
3833
+ fieldName: name,
4086
3834
  placeholder,
4087
3835
  allowEmpty,
4088
3836
  rows
4089
3837
  });
4090
- return /* @__PURE__ */ jsxRuntime.jsxs(
4091
- "div",
4092
- {
4093
- className: "space-y-3",
4094
- "x-data": initialDataJson,
4095
- children: [
4096
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs(
4097
- "button",
4098
- {
4099
- type: "button",
4100
- ...{
4101
- "x-on:click": `
3838
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", "x-data": initialDataJson, children: [
3839
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs(
3840
+ "button",
3841
+ {
3842
+ type: "button",
3843
+ ...{
3844
+ "x-on:click": `
4102
3845
  items.push('');
4103
3846
  `
4104
- },
4105
- className: "px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors text-sm font-medium flex items-center gap-2",
4106
- "data-testid": `${fieldName}-add-button`,
4107
- children: [
4108
- /* @__PURE__ */ jsxRuntime.jsx(
4109
- "svg",
3847
+ },
3848
+ className: "px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors text-sm font-medium flex items-center gap-2",
3849
+ "data-testid": `${name}-add-button`,
3850
+ children: [
3851
+ /* @__PURE__ */ jsxRuntime.jsx(
3852
+ "svg",
3853
+ {
3854
+ className: "w-4 h-4",
3855
+ fill: "none",
3856
+ stroke: "currentColor",
3857
+ viewBox: "0 0 24 24",
3858
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3859
+ "path",
4110
3860
  {
4111
- className: "w-4 h-4",
4112
- fill: "none",
4113
- stroke: "currentColor",
4114
- viewBox: "0 0 24 24",
4115
- children: /* @__PURE__ */ jsxRuntime.jsx(
4116
- "path",
4117
- {
4118
- strokeLinecap: "round",
4119
- strokeLinejoin: "round",
4120
- strokeWidth: "2",
4121
- d: "M12 4v16m8-8H4"
4122
- }
4123
- )
3861
+ strokeLinecap: "round",
3862
+ strokeLinejoin: "round",
3863
+ strokeWidth: "2",
3864
+ d: "M12 4v16m8-8H4"
4124
3865
  }
4125
- ),
4126
- "\u6DFB\u52A0\u9879"
4127
- ]
4128
- }
4129
- ) }),
4130
- /* @__PURE__ */ jsxRuntime.jsx(
4131
- "div",
4132
- {
4133
- "x-show": "items.length > 0",
4134
- "data-testid": `${fieldName}-list-container`,
4135
- ...{
4136
- "@sortable:change.stop": `
3866
+ )
3867
+ }
3868
+ ),
3869
+ "\u6DFB\u52A0\u9879"
3870
+ ]
3871
+ }
3872
+ ) }),
3873
+ /* @__PURE__ */ jsxRuntime.jsx(
3874
+ "div",
3875
+ {
3876
+ "x-show": "items.length > 0",
3877
+ "data-testid": `${name}-list-container`,
3878
+ ...{
3879
+ "@sortable:change.stop": `
4137
3880
  (function() {
4138
3881
  const { oldIndex, newIndex } = $event.detail;
4139
3882
  [items[oldIndex], items[newIndex]] = [items[newIndex], items[oldIndex]];
4140
3883
  })();
4141
3884
  `
4142
- },
4143
- children: /* @__PURE__ */ jsxRuntime.jsx(SortableList, { className: "space-y-2", handle: "[data-drag-handle]", children: /* @__PURE__ */ jsxRuntime.jsx("template", { "x-for": "(item, index) in items", "x-bind:key": "index", children: /* @__PURE__ */ jsxRuntime.jsx(ArrayItem, { fieldName, rows }) }) })
4144
- }
4145
- ),
4146
- /* @__PURE__ */ jsxRuntime.jsx(
4147
- "div",
4148
- {
4149
- className: "empty-state text-center py-8 text-gray-400 text-sm border border-dashed border-gray-300 rounded-lg",
4150
- "x-show": "items.length === 0",
4151
- "data-testid": `${fieldName}-empty-state`,
4152
- children: '\u6682\u65E0\u9879\uFF0C\u70B9\u51FB"\u6DFB\u52A0\u9879"\u6309\u94AE\u6DFB\u52A0'
4153
- }
4154
- )
4155
- ]
4156
- }
4157
- );
3885
+ },
3886
+ children: /* @__PURE__ */ jsxRuntime.jsx(SortableList, { className: "space-y-2", handle: "[data-drag-handle]", children: /* @__PURE__ */ jsxRuntime.jsx("template", { "x-for": "(item, index) in items", "x-bind:key": "index", children: /* @__PURE__ */ jsxRuntime.jsx(ArrayItem, { fieldName: name, rows }) }) })
3887
+ }
3888
+ ),
3889
+ /* @__PURE__ */ jsxRuntime.jsx(
3890
+ "div",
3891
+ {
3892
+ className: "empty-state text-center py-8 text-gray-400 text-sm border border-dashed border-gray-300 rounded-lg",
3893
+ "x-show": "items.length === 0",
3894
+ "data-testid": `${name}-empty-state`,
3895
+ children: '\u6682\u65E0\u9879\uFF0C\u70B9\u51FB"\u6DFB\u52A0\u9879"\u6309\u94AE\u6DFB\u52A0'
3896
+ }
3897
+ )
3898
+ ] });
4158
3899
  }
4159
- function ArrayItem({ fieldName, rows = 1 }) {
4160
- return /* @__PURE__ */ jsxRuntime.jsxs(
4161
- "div",
4162
- {
4163
- "data-array-item": true,
4164
- className: "flex items-center gap-2 group",
4165
- children: [
4166
- /* @__PURE__ */ jsxRuntime.jsx(
4167
- "div",
3900
+ function ArrayItem({
3901
+ fieldName,
3902
+ rows = 1
3903
+ }) {
3904
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-array-item": true, className: "flex items-center gap-2 group", children: [
3905
+ /* @__PURE__ */ jsxRuntime.jsx(
3906
+ "div",
3907
+ {
3908
+ className: "flex-shrink-0 cursor-move text-gray-400 hover:text-gray-600 transition-colors p-1",
3909
+ "data-drag-handle": true,
3910
+ "data-testid": `${fieldName}-drag-handle`,
3911
+ title: "\u62D6\u62FD\u6392\u5E8F",
3912
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3913
+ "svg",
4168
3914
  {
4169
- className: "flex-shrink-0 cursor-move text-gray-400 hover:text-gray-600 transition-colors p-1",
4170
- "data-drag-handle": true,
4171
- "data-testid": `${fieldName}-drag-handle`,
4172
- title: "\u62D6\u62FD\u6392\u5E8F",
3915
+ className: "w-5 h-5",
3916
+ fill: "none",
3917
+ stroke: "currentColor",
3918
+ viewBox: "0 0 24 24",
4173
3919
  children: /* @__PURE__ */ jsxRuntime.jsx(
4174
- "svg",
3920
+ "path",
4175
3921
  {
4176
- className: "w-5 h-5",
4177
- fill: "none",
4178
- stroke: "currentColor",
4179
- viewBox: "0 0 24 24",
4180
- children: /* @__PURE__ */ jsxRuntime.jsx(
4181
- "path",
4182
- {
4183
- strokeLinecap: "round",
4184
- strokeLinejoin: "round",
4185
- strokeWidth: "2",
4186
- d: "M4 8h16M4 16h16"
4187
- }
4188
- )
3922
+ strokeLinecap: "round",
3923
+ strokeLinejoin: "round",
3924
+ strokeWidth: "2",
3925
+ d: "M4 8h16M4 16h16"
4189
3926
  }
4190
3927
  )
4191
3928
  }
4192
- ),
4193
- rows === 1 ? /* @__PURE__ */ jsxRuntime.jsx(
4194
- "input",
4195
- {
4196
- type: "text",
4197
- "x-model": "items[index]",
4198
- "x-bind:placeholder": "placeholder + ' ' + (index + 1)",
4199
- className: "flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent",
4200
- "data-testid": `${fieldName}-input`,
4201
- "x-bind:required": "!allowEmpty"
4202
- }
4203
- ) : /* @__PURE__ */ jsxRuntime.jsx(
4204
- "textarea",
4205
- {
4206
- "x-model": "items[index]",
4207
- "x-bind:placeholder": "placeholder + ' ' + (index + 1)",
4208
- rows,
4209
- className: "flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-y",
4210
- "data-testid": `${fieldName}-input`,
4211
- "x-bind:required": "!allowEmpty"
4212
- }
4213
- ),
4214
- /* @__PURE__ */ jsxRuntime.jsx(
4215
- "input",
4216
- {
4217
- type: "hidden",
4218
- "x-bind:name": "fieldName + '[' + index + ']'",
4219
- "x-bind:value": "item"
4220
- }
4221
- ),
4222
- /* @__PURE__ */ jsxRuntime.jsx(
4223
- "button",
4224
- {
4225
- type: "button",
4226
- ...{
4227
- "x-on:click": `
3929
+ )
3930
+ }
3931
+ ),
3932
+ rows === 1 ? /* @__PURE__ */ jsxRuntime.jsx(
3933
+ "input",
3934
+ {
3935
+ type: "text",
3936
+ "x-model": "items[index]",
3937
+ "x-bind:placeholder": "placeholder + ' ' + (index + 1)",
3938
+ className: "flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent",
3939
+ "data-testid": `${fieldName}-input`,
3940
+ "x-bind:required": "!allowEmpty"
3941
+ }
3942
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
3943
+ "textarea",
3944
+ {
3945
+ "x-model": "items[index]",
3946
+ "x-bind:placeholder": "placeholder + ' ' + (index + 1)",
3947
+ rows,
3948
+ className: "flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-y",
3949
+ "data-testid": `${fieldName}-input`,
3950
+ "x-bind:required": "!allowEmpty"
3951
+ }
3952
+ ),
3953
+ /* @__PURE__ */ jsxRuntime.jsx(
3954
+ "input",
3955
+ {
3956
+ type: "hidden",
3957
+ "x-bind:name": "fieldName + '[' + index + ']'",
3958
+ "x-bind:value": "item"
3959
+ }
3960
+ ),
3961
+ /* @__PURE__ */ jsxRuntime.jsx(
3962
+ "button",
3963
+ {
3964
+ type: "button",
3965
+ ...{
3966
+ "x-on:click": `
4228
3967
  items.splice(index, 1);
4229
3968
  `
4230
- },
4231
- className: "flex-shrink-0 px-3 py-2 text-sm text-red-600 hover:bg-red-50 rounded-lg transition-colors",
4232
- "data-testid": `${fieldName}-remove-button`,
4233
- title: "\u5220\u9664\u6B64\u9879",
3969
+ },
3970
+ className: "flex-shrink-0 px-3 py-2 text-sm text-red-600 hover:bg-red-50 rounded-lg transition-colors",
3971
+ "data-testid": `${fieldName}-remove-button`,
3972
+ title: "\u5220\u9664\u6B64\u9879",
3973
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3974
+ "svg",
3975
+ {
3976
+ className: "w-5 h-5",
3977
+ fill: "none",
3978
+ stroke: "currentColor",
3979
+ viewBox: "0 0 24 24",
4234
3980
  children: /* @__PURE__ */ jsxRuntime.jsx(
4235
- "svg",
3981
+ "path",
4236
3982
  {
4237
- className: "w-5 h-5",
4238
- fill: "none",
4239
- stroke: "currentColor",
4240
- viewBox: "0 0 24 24",
4241
- children: /* @__PURE__ */ jsxRuntime.jsx(
4242
- "path",
4243
- {
4244
- strokeLinecap: "round",
4245
- strokeLinejoin: "round",
4246
- strokeWidth: "2",
4247
- d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
4248
- }
4249
- )
3983
+ strokeLinecap: "round",
3984
+ strokeLinejoin: "round",
3985
+ strokeWidth: "2",
3986
+ d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
4250
3987
  }
4251
3988
  )
4252
3989
  }
4253
3990
  )
4254
- ]
4255
- }
4256
- );
3991
+ }
3992
+ )
3993
+ ] });
4257
3994
  }
4258
3995
  function TagsEditor(props) {
4259
- const { value, fieldName, placeholder = "\u8F93\u5165\u6807\u7B7E\u540E\u6309\u56DE\u8F66\u6DFB\u52A0" } = props;
3996
+ const { value, placeholder = "\u8F93\u5165\u6807\u7B7E\u540E\u6309\u56DE\u8F66\u6DFB\u52A0" } = props;
3997
+ const name = props.name;
4260
3998
  const initialTags = value || [];
4261
3999
  const initialDataJson = JSON.stringify({
4262
4000
  tags: initialTags.map((tag) => tag || ""),
4263
- fieldName,
4001
+ fieldName: name,
4264
4002
  newTag: "",
4265
4003
  editingIndex: null,
4266
4004
  editingValue: "",
@@ -4287,7 +4025,7 @@ function TagsEditor(props) {
4287
4025
  placeholder,
4288
4026
  autocomplete: "off",
4289
4027
  className: "w-full px-3 py-1.5 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent",
4290
- "data-testid": `${fieldName}-input`
4028
+ "data-testid": `${name}-input`
4291
4029
  }
4292
4030
  ),
4293
4031
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -4296,7 +4034,7 @@ function TagsEditor(props) {
4296
4034
  "x-show": "error && editingIndex === null",
4297
4035
  "x-text": "error",
4298
4036
  className: "text-red-600 text-sm p-2",
4299
- id: `${fieldName}-error`
4037
+ id: `${name}-error`
4300
4038
  }
4301
4039
  )
4302
4040
  ] }),
@@ -4304,7 +4042,7 @@ function TagsEditor(props) {
4304
4042
  "div",
4305
4043
  {
4306
4044
  "x-show": "tags.length > 0",
4307
- "data-testid": `${fieldName}-tags-container`,
4045
+ "data-testid": `${name}-tags-container`,
4308
4046
  ...{
4309
4047
  "@sortable:change.stop": `
4310
4048
  (function() {
@@ -4327,8 +4065,8 @@ function TagsEditor(props) {
4327
4065
  "x-bind:value": "editingIndex === index ? editingValue : tag"
4328
4066
  }
4329
4067
  ),
4330
- /* @__PURE__ */ jsxRuntime.jsx(TagItem, { fieldName }),
4331
- /* @__PURE__ */ jsxRuntime.jsx(TagItemEdit, { fieldName })
4068
+ /* @__PURE__ */ jsxRuntime.jsx(TagItem, { fieldName: name }),
4069
+ /* @__PURE__ */ jsxRuntime.jsx(TagItemEdit, { fieldName: name })
4332
4070
  ] }) })
4333
4071
  }
4334
4072
  )
@@ -4339,7 +4077,7 @@ function TagsEditor(props) {
4339
4077
  {
4340
4078
  className: "empty-state text-center py-4 text-gray-400 text-sm border border-dashed border-gray-300 rounded-md",
4341
4079
  "x-show": "tags.length === 0",
4342
- "data-testid": `${fieldName}-empty-state`,
4080
+ "data-testid": `${name}-empty-state`,
4343
4081
  children: "\u6682\u65E0\u6807\u7B7E\uFF0C\u5728\u4E0A\u65B9\u8F93\u5165\u6846\u4E2D\u8F93\u5165\u6807\u7B7E\u540E\u6309\u56DE\u8F66\u6DFB\u52A0"
4344
4082
  }
4345
4083
  )
@@ -4520,7 +4258,7 @@ function TagItemEdit({ fieldName }) {
4520
4258
  );
4521
4259
  }
4522
4260
  function ObjectEditor(props) {
4523
- const { value, fieldName, objectSchema } = props;
4261
+ const { value, name, objectSchema } = props;
4524
4262
  if (!objectSchema) {
4525
4263
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 border border-yellow-300 rounded-lg bg-yellow-50 text-yellow-800 text-sm", children: "\u8BF7\u63D0\u4F9B objectSchema \u53C2\u6570\u4EE5\u4F7F\u7528\u5BF9\u8C61\u7F16\u8F91\u5668" });
4526
4264
  }
@@ -4540,7 +4278,7 @@ function ObjectEditor(props) {
4540
4278
  const initialValueJson = JSON.stringify(initialObject);
4541
4279
  JSON.stringify(fields.map((f) => f.name));
4542
4280
  const generateField = (field) => {
4543
- const fieldId = `${fieldName}-${field.name}`;
4281
+ const fieldId = `${String(name)}-${field.name}`;
4544
4282
  const fieldValue = initialObject[field.name];
4545
4283
  const fieldValueStr = fieldValue === void 0 || fieldValue === null ? "" : typeof fieldValue === "object" ? JSON.stringify(fieldValue) : String(fieldValue);
4546
4284
  const requiredAttr = field.required ? "required" : "";
@@ -4550,11 +4288,11 @@ function ObjectEditor(props) {
4550
4288
  <input
4551
4289
  type="text"
4552
4290
  id="${fieldId}"
4553
- name="${fieldName}.${field.name}"
4291
+ name="${name}.${field.name}"
4554
4292
  value="${fieldValueStr}"
4555
4293
  class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
4556
- data-testid="${fieldName}-input-${field.name}"
4557
- oninput="updateObjectField('${fieldName}', '${field.name}', this.value, 'text', ${field.required})"
4294
+ data-testid="${name}-input-${field.name}"
4295
+ oninput="updateObjectField('${name}', '${field.name}', this.value, 'text', ${field.required})"
4558
4296
  ${requiredAttr}
4559
4297
  />
4560
4298
  `;
@@ -4562,11 +4300,11 @@ function ObjectEditor(props) {
4562
4300
  inputElement = html.html`
4563
4301
  <textarea
4564
4302
  id="${fieldId}"
4565
- name="${fieldName}.${field.name}"
4303
+ name="${name}.${field.name}"
4566
4304
  class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-y"
4567
4305
  rows="4"
4568
- data-testid="${fieldName}-input-${field.name}"
4569
- oninput="updateObjectField('${fieldName}', '${field.name}', this.value, 'text', ${field.required})"
4306
+ data-testid="${name}-input-${field.name}"
4307
+ oninput="updateObjectField('${name}', '${field.name}', this.value, 'text', ${field.required})"
4570
4308
  ${requiredAttr}
4571
4309
  >${fieldValueStr}</textarea>
4572
4310
  `;
@@ -4576,12 +4314,12 @@ function ObjectEditor(props) {
4576
4314
  <input
4577
4315
  type="number"
4578
4316
  id="${fieldId}"
4579
- name="${fieldName}.${field.name}"
4317
+ name="${name}.${field.name}"
4580
4318
  value="${fieldValueStr}"
4581
4319
  step="${step}"
4582
4320
  class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
4583
- data-testid="${fieldName}-input-${field.name}"
4584
- oninput="updateObjectField('${fieldName}', '${field.name}', this.value, 'number', ${field.required})"
4321
+ data-testid="${name}-input-${field.name}"
4322
+ oninput="updateObjectField('${name}', '${field.name}', this.value, 'number', ${field.required})"
4585
4323
  ${requiredAttr}
4586
4324
  />
4587
4325
  `;
@@ -4590,11 +4328,11 @@ function ObjectEditor(props) {
4590
4328
  <input
4591
4329
  type="date"
4592
4330
  id="${fieldId}"
4593
- name="${fieldName}.${field.name}"
4331
+ name="${name}.${field.name}"
4594
4332
  value="${fieldValueStr}"
4595
4333
  class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
4596
- data-testid="${fieldName}-input-${field.name}"
4597
- oninput="updateObjectField('${fieldName}', '${field.name}', this.value, 'date', ${field.required})"
4334
+ data-testid="${name}-input-${field.name}"
4335
+ oninput="updateObjectField('${name}', '${field.name}', this.value, 'date', ${field.required})"
4598
4336
  ${requiredAttr}
4599
4337
  />
4600
4338
  `;
@@ -4603,11 +4341,11 @@ function ObjectEditor(props) {
4603
4341
  <input
4604
4342
  type="email"
4605
4343
  id="${fieldId}"
4606
- name="${fieldName}.${field.name}"
4344
+ name="${name}.${field.name}"
4607
4345
  value="${fieldValueStr}"
4608
4346
  class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
4609
- data-testid="${fieldName}-input-${field.name}"
4610
- oninput="updateObjectField('${fieldName}', '${field.name}', this.value, 'text', ${field.required})"
4347
+ data-testid="${name}-input-${field.name}"
4348
+ oninput="updateObjectField('${name}', '${field.name}', this.value, 'text', ${field.required})"
4611
4349
  ${requiredAttr}
4612
4350
  />
4613
4351
  `;
@@ -4615,10 +4353,10 @@ function ObjectEditor(props) {
4615
4353
  inputElement = html.html`
4616
4354
  <select
4617
4355
  id="${fieldId}"
4618
- name="${fieldName}.${field.name}"
4356
+ name="${name}.${field.name}"
4619
4357
  class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white"
4620
- data-testid="${fieldName}-select-${field.name}"
4621
- onchange="updateObjectField('${fieldName}', '${field.name}', this.value, 'text', ${field.required})"
4358
+ data-testid="${name}-select-${field.name}"
4359
+ onchange="updateObjectField('${name}', '${field.name}', this.value, 'text', ${field.required})"
4622
4360
  ${requiredAttr}
4623
4361
  >
4624
4362
  ${!field.required ? html.html`<option value="">请选择</option>` : ""}
@@ -4641,11 +4379,11 @@ function ObjectEditor(props) {
4641
4379
  <input
4642
4380
  type="checkbox"
4643
4381
  id="${fieldId}"
4644
- name="${fieldName}.${field.name}"
4382
+ name="${name}.${field.name}"
4645
4383
  ${checked ? "checked" : ""}
4646
4384
  class="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
4647
- data-testid="${fieldName}-checkbox-${field.name}"
4648
- onchange="updateObjectField('${fieldName}', '${field.name}', this.checked, 'checkbox', ${field.required}')"
4385
+ data-testid="${name}-checkbox-${field.name}"
4386
+ onchange="updateObjectField('${name}', '${field.name}', this.checked, 'checkbox', ${field.required}')"
4649
4387
  />
4650
4388
  <label for="${fieldId}" class="ml-2 text-sm text-gray-700">
4651
4389
  ${field.label}
@@ -4654,12 +4392,12 @@ function ObjectEditor(props) {
4654
4392
  `;
4655
4393
  }
4656
4394
  return html.html`
4657
- <div class="space-y-2" data-testid="${fieldName}-field-${field.name}">
4395
+ <div class="space-y-2" data-testid="${name}-field-${field.name}">
4658
4396
  ${field.type !== "checkbox" ? html.html`
4659
4397
  <label
4660
4398
  for="${fieldId}"
4661
4399
  class="block text-sm font-semibold text-gray-700"
4662
- data-testid="${fieldName}-label-${field.name}"
4400
+ data-testid="${name}-label-${field.name}"
4663
4401
  >
4664
4402
  ${field.label}
4665
4403
  ${field.required ? html.html`<span class="text-red-500 ml-1">*</span>` : ""}
@@ -4671,15 +4409,15 @@ function ObjectEditor(props) {
4671
4409
  };
4672
4410
  return html.html`
4673
4411
  <div
4674
- id="object-editor-${fieldName}"
4412
+ id="object-editor-${name}"
4675
4413
  class="space-y-4"
4676
4414
  data-initial-value="${initialValueJson}"
4677
4415
  >
4678
4416
  <input
4679
4417
  type="hidden"
4680
- name="${fieldName}"
4418
+ name="${name}"
4681
4419
  value="${initialValueJson}"
4682
- data-testid="hidden-${fieldName}"
4420
+ data-testid="hidden-${name}"
4683
4421
  />
4684
4422
  <div class="space-y-4">
4685
4423
  ${fields.map((field) => generateField(field))}
@@ -4741,14 +4479,13 @@ function BaseLayout(props) {
4741
4479
  <meta name="description" content="${props.description || ""}" />
4742
4480
  ${globalScripts(props.prefix)} ${globalStyles()}
4743
4481
  </head>
4744
- <body hx-ext="morph" className="bg-gray-50" hx-indicator="#loading-bar">
4482
+ <body className="bg-gray-50" hx-indicator="#loading-bar">
4745
4483
  ${LoadingBar()} ${props.children}
4746
4484
  <div
4747
- id="error-container"
4748
- className="fixed top-4 right-4 z-[200] w-full max-w-2xl px-4"
4485
+ id="notification-container"
4486
+ style="position: fixed; top: 1rem; right: 1rem; z-index: 200; display: flex; flex-direction: column; gap: 0.5rem; pointer-events: none; width: 100%; max-width: 28rem; padding-left: 1rem; padding-right: 1rem;"
4749
4487
  ></div>
4750
4488
  <div id="dialog-container"></div>
4751
- ${sortableScript()}
4752
4489
  </body>
4753
4490
  </html>
4754
4491
  `;
@@ -5000,7 +4737,7 @@ async function handlePermissionDenied(ctx, result, options) {
5000
4737
  if (isHtmxRequest) {
5001
4738
  const headers = {
5002
4739
  "HX-Retarget": "#dialog-container",
5003
- "HX-Reswap": "innerHTML",
4740
+ "HX-Reswap": "beforeend",
5004
4741
  "X-Permission-Denied": "true"
5005
4742
  };
5006
4743
  return ctx.html(
@@ -5045,22 +4782,15 @@ function buildNotificationFragments(notifications) {
5045
4782
  if (notifications.length === 0) {
5046
4783
  return null;
5047
4784
  }
5048
- return notifications.map((notification, index) => /* @__PURE__ */ jsxRuntime.jsx(
5049
- "div",
4785
+ return notifications.map((notification, index) => /* @__PURE__ */ jsxRuntime.jsx("div", { id: "notification-container", "hx-swap-oob": "beforeend", children: /* @__PURE__ */ jsxRuntime.jsx(
4786
+ ErrorAlert,
5050
4787
  {
5051
- id: "error-container",
5052
- "hx-swap-oob": "beforeend",
5053
- children: /* @__PURE__ */ jsxRuntime.jsx(
5054
- ErrorAlert,
5055
- {
5056
- type: notification.type,
5057
- title: notification.title,
5058
- message: notification.message
5059
- }
5060
- )
4788
+ type: notification.type,
4789
+ title: notification.title,
4790
+ message: notification.message
5061
4791
  },
5062
4792
  `notification-${index}`
5063
- ));
4793
+ ) }));
5064
4794
  }
5065
4795
  function isEmptyContent(result) {
5066
4796
  if (result === null || result === void 0) return true;
@@ -5154,7 +4884,6 @@ async function renderResult(ctx, context, result, renderOptions) {
5154
4884
  if (context.isDialog) {
5155
4885
  return ctx.html(
5156
4886
  /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5157
- /* @__PURE__ */ jsxRuntime.jsx("div", { id: "dialog-container", "hx-swap-oob": "innerHTML" }),
5158
4887
  notificationFragments,
5159
4888
  /* @__PURE__ */ jsxRuntime.jsx("title", { children: metadata.title })
5160
4889
  ] }),
@@ -5210,7 +4939,7 @@ async function renderResult(ctx, context, result, renderOptions) {
5210
4939
  );
5211
4940
  }
5212
4941
  const target = context.isDialog ? "#dialog-container" : "#main-content";
5213
- const swap = context.isDialog ? "innerHTML" : "outerHTML";
4942
+ const swap = context.isDialog ? "beforeend" : "outerHTML";
5214
4943
  const headers = {
5215
4944
  "HX-Retarget": target,
5216
4945
  "HX-Reswap": swap
@@ -5273,7 +5002,6 @@ async function renderResult(ctx, context, result, renderOptions) {
5273
5002
  useAdminLayout,
5274
5003
  currentPath,
5275
5004
  userInfo: user,
5276
- componentRegistry: renderOptions.componentRegistry,
5277
5005
  breadcrumbs,
5278
5006
  actions,
5279
5007
  children: result
@@ -5309,7 +5037,6 @@ async function renderResult(ctx, context, result, renderOptions) {
5309
5037
  prefix: options.prefix,
5310
5038
  title: dynamicMetadata.title,
5311
5039
  description: dynamicMetadata.description,
5312
- componentRegistry: renderOptions.componentRegistry,
5313
5040
  children: /* @__PURE__ */ jsxRuntime.jsx(
5314
5041
  AdminLayout,
5315
5042
  {
@@ -5321,7 +5048,6 @@ async function renderResult(ctx, context, result, renderOptions) {
5321
5048
  userInfo: user,
5322
5049
  breadcrumbs,
5323
5050
  actions,
5324
- componentRegistry: renderOptions.componentRegistry,
5325
5051
  children: result
5326
5052
  }
5327
5053
  )
@@ -5336,7 +5062,6 @@ async function renderResult(ctx, context, result, renderOptions) {
5336
5062
  prefix: options.prefix,
5337
5063
  title: dynamicMetadata.title,
5338
5064
  description: dynamicMetadata.description,
5339
- componentRegistry: renderOptions.componentRegistry,
5340
5065
  children: /* @__PURE__ */ jsxRuntime.jsx(NoLayout, { children: result })
5341
5066
  }
5342
5067
  )
@@ -5425,14 +5150,15 @@ async function handleRequest(ctx, page, feature, handlerOptions) {
5425
5150
  if (isHtmxRequest) {
5426
5151
  return ctx.html(
5427
5152
  /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5428
- /* @__PURE__ */ jsxRuntime.jsx("div", { id: "error-container", "hx-swap-oob": "beforeend", children: /* @__PURE__ */ jsxRuntime.jsx(
5153
+ /* @__PURE__ */ jsxRuntime.jsx(
5429
5154
  ErrorAlert,
5430
5155
  {
5156
+ "hx-swap-oob": "beforeend:#notification-container",
5431
5157
  type: "error",
5432
5158
  title: "\u8BF7\u6C42\u5931\u8D25",
5433
5159
  message: "Feature has no handler or render method"
5434
5160
  }
5435
- ) }),
5161
+ ),
5436
5162
  /* @__PURE__ */ jsxRuntime.jsx("title", { children: "\u9519\u8BEF" })
5437
5163
  ] }),
5438
5164
  500,
@@ -5449,10 +5175,15 @@ async function handleRequest(ctx, page, feature, handlerOptions) {
5449
5175
  if (isHtmxRequest) {
5450
5176
  const errorMessage = error instanceof Error ? error.message : "Internal server error";
5451
5177
  return ctx.html(
5452
- /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5453
- /* @__PURE__ */ jsxRuntime.jsx("div", { id: "error-container", "hx-swap-oob": "beforeend", children: /* @__PURE__ */ jsxRuntime.jsx(ErrorAlert, { type: "error", title: "\u8BF7\u6C42\u5931\u8D25", message: errorMessage }) }),
5454
- /* @__PURE__ */ jsxRuntime.jsx("title", { children: "\u9519\u8BEF" })
5455
- ] }),
5178
+ /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx(
5179
+ ErrorAlert,
5180
+ {
5181
+ "hx-swap-oob": "beforeend:#notification-container",
5182
+ type: "error",
5183
+ title: "\u8BF7\u6C42\u5931\u8D25",
5184
+ message: errorMessage
5185
+ }
5186
+ ) }),
5456
5187
  500,
5457
5188
  {
5458
5189
  "HX-Reswap": "none"
@@ -5555,7 +5286,6 @@ var HtmxAdminPlugin = class {
5555
5286
  options;
5556
5287
  serviceName = "";
5557
5288
  pages = /* @__PURE__ */ new Map();
5558
- componentHandler;
5559
5289
  constructor(options) {
5560
5290
  this.options = {
5561
5291
  title: options?.title || "\u7BA1\u7406\u540E\u53F0",
@@ -5564,8 +5294,7 @@ var HtmxAdminPlugin = class {
5564
5294
  homePath: options?.homePath || "",
5565
5295
  navigation: options?.navigation ?? [],
5566
5296
  authProvider: options?.authProvider,
5567
- pages: options?.pages ?? [],
5568
- components: options?.components ?? []
5297
+ pages: options?.pages ?? []
5569
5298
  };
5570
5299
  this.initPages();
5571
5300
  }
@@ -5597,11 +5326,6 @@ var HtmxAdminPlugin = class {
5597
5326
  imeanServiceEngine.logger.info(
5598
5327
  `HtmxAdminPlugin initialized${this.serviceName ? ` (service: ${this.serviceName})` : ""}`
5599
5328
  );
5600
- this.componentHandler = new HtmxComponentHandler(
5601
- this.hono,
5602
- this.options.prefix,
5603
- this.options.components
5604
- );
5605
5329
  initializeCdnCache().catch((error) => {
5606
5330
  imeanServiceEngine.logger.error("[HtmxAdminPlugin] CDN \u7F13\u5B58\u521D\u59CB\u5316\u5931\u8D25", error);
5607
5331
  });
@@ -5624,7 +5348,10 @@ var HtmxAdminPlugin = class {
5624
5348
  };
5625
5349
 
5626
5350
  exports.BaseFeature = BaseFeature;
5627
- exports.ComponentContext = ComponentContext;
5351
+ exports.BaseFormFeature = BaseFormFeature;
5352
+ exports.Breadcrumb = Breadcrumb;
5353
+ exports.Button = Button;
5354
+ exports.Card = Card;
5628
5355
  exports.CustomFeature = CustomFeature;
5629
5356
  exports.DefaultCreateFeature = DefaultCreateFeature;
5630
5357
  exports.DefaultDeleteFeature = DefaultDeleteFeature;
@@ -5632,16 +5359,20 @@ exports.DefaultDetailFeature = DefaultDetailFeature;
5632
5359
  exports.DefaultEditFeature = DefaultEditFeature;
5633
5360
  exports.DefaultListFeature = DefaultListFeature;
5634
5361
  exports.Dialog = Dialog;
5362
+ exports.EmptyState = EmptyState;
5635
5363
  exports.ErrorAlert = ErrorAlert;
5364
+ exports.FilterForm = FilterForm;
5365
+ exports.Header = Header;
5636
5366
  exports.HtmxAdminPlugin = HtmxAdminPlugin;
5637
- exports.HtmxComponent = HtmxComponent;
5638
5367
  exports.LoadingBar = LoadingBar;
5639
- exports.Method = Method;
5640
5368
  exports.ObjectEditor = ObjectEditor;
5641
5369
  exports.PageModel = PageModel;
5642
- exports.RenderContext = RenderContext;
5370
+ exports.Pagination = Pagination;
5371
+ exports.PermissionDeniedContent = PermissionDeniedContent;
5372
+ exports.PermissionDeniedPage = PermissionDeniedPage;
5643
5373
  exports.SortableList = SortableList;
5644
5374
  exports.StringArrayEditor = StringArrayEditor;
5375
+ exports.Table = Table;
5645
5376
  exports.TagsEditor = TagsEditor;
5646
5377
  exports.checkUserPermission = checkUserPermission;
5647
5378
  exports.getUserInfo = getUserInfo;