0x-lang 0.1.8 → 0.1.10

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/ast.d.ts CHANGED
@@ -700,6 +700,15 @@ export interface BreadcrumbNode extends BaseNode {
700
700
  type: 'Breadcrumb';
701
701
  props: Record<string, Expression>;
702
702
  }
703
+ export interface DividerNode extends BaseNode {
704
+ type: 'Divider';
705
+ props: Record<string, Expression>;
706
+ }
707
+ export interface ProgressNode extends BaseNode {
708
+ type: 'Progress';
709
+ value: Expression;
710
+ props: Record<string, Expression>;
711
+ }
703
712
  export interface StatsGridNode extends BaseNode {
704
713
  type: 'StatsGrid';
705
714
  cols: number;
@@ -893,7 +902,7 @@ export interface RtlNode extends BaseNode {
893
902
  enabled: boolean;
894
903
  props: Record<string, Expression>;
895
904
  }
896
- export type UINode = LayoutNode | TextNode | ButtonNode | InputNode | ImageNode | LinkNode | ToggleNode | SelectNode | IfBlock | ForBlock | ShowBlock | HideBlock | ComponentCall | CommentNode | TableNode | ChartNode | StatNode | NavNode | UploadNode | ModalNode | ToastNode | CrudNode | ListNode | LayoutShellNode | SlideOverNode | DrawerNode | CommandNode | ConfirmNode | PayNode | CartNode | MediaNode | NotificationNode | SearchNode | FilterNode | SocialNode | ProfileNode | HeroNode | FeaturesNode | PricingNode | FaqNode | TestimonialNode | FooterNode | AdminNode | SeoNode | A11yNode | AnimateNode | GestureNode | AiNode | AutomationNode | DevNode | EmitNode | ResponsiveNode | BreadcrumbNode | StatsGridNode | ErrorNode | LoadingNode | OfflineNode | RetryNode | LogNode;
905
+ export type UINode = LayoutNode | TextNode | ButtonNode | InputNode | ImageNode | LinkNode | ToggleNode | SelectNode | IfBlock | ForBlock | ShowBlock | HideBlock | ComponentCall | CommentNode | TableNode | ChartNode | StatNode | NavNode | UploadNode | ModalNode | ToastNode | CrudNode | ListNode | LayoutShellNode | SlideOverNode | DrawerNode | CommandNode | ConfirmNode | PayNode | CartNode | MediaNode | NotificationNode | SearchNode | FilterNode | SocialNode | ProfileNode | HeroNode | FeaturesNode | PricingNode | FaqNode | TestimonialNode | FooterNode | AdminNode | SeoNode | A11yNode | AnimateNode | GestureNode | AiNode | AutomationNode | DevNode | EmitNode | ResponsiveNode | BreadcrumbNode | StatsGridNode | DividerNode | ProgressNode | ErrorNode | LoadingNode | OfflineNode | RetryNode | LogNode;
897
906
  export type ASTNode = AppNode | PageNode | ComponentNode | StateDecl | DerivedDecl | PropDecl | TypeDecl | StoreDecl | ApiDecl | FnDecl | OnMount | OnDestroy | WatchBlock | CheckDecl | StyleDecl | JsImport | UseImport | JsBlock | ModelNode | DataDecl | FormDecl | AuthDecl | RealtimeDecl | RouteDecl | RoleDecl | AutomationNode | DevNode | DeployNode | EnvNode | DockerNode | CiNode | DomainNode | CdnNode | MonitorNode | BackupNode | EndpointNode | MiddlewareNode | QueueNode | CronNode | CacheNode | MigrateNode | SeedNode | WebhookNode | StorageNode | TestNode | E2eNode | MockNode | FixtureNode | I18nNode | LocaleNode | RtlNode | UINode;
898
907
  export interface CodeGenerator {
899
908
  target: string;
@@ -431,6 +431,13 @@ function genUINode(node, c) {
431
431
  case 'Offline': return genOfflineUI(node, c);
432
432
  case 'Retry': return `{/* retry: max=${genExpr(node.maxRetries, c)} */}`;
433
433
  case 'Log': return `{/* log: ${genExpr(node.message, c)} */}`;
434
+ case 'Divider': return `<hr style={{ border: 'none', borderTop: '1px solid #e2e8f0', margin: '16px 0' }} />`;
435
+ case 'Progress': {
436
+ const pn = node;
437
+ const val = genExpr(pn.value, c);
438
+ const max = pn.props['max'] ? genExpr(pn.props['max'], c) : '100';
439
+ return `<div style={{ width: '100%', backgroundColor: '#e2e8f0', borderRadius: '9999px', height: '8px', overflow: 'hidden' }}>\n<div style={{ width: \`\${(${val} / ${max}) * 100}%\`, backgroundColor: '#3b82f6', height: '100%', borderRadius: '9999px', transition: 'width 0.3s' }} />\n</div>`;
440
+ }
434
441
  default: return `{/* unsupported: ${node.type} */}`;
435
442
  }
436
443
  }
@@ -576,14 +583,27 @@ function genText(node, c) {
576
583
  case 'strike': {
577
584
  // strike={condition} → conditional textDecoration
578
585
  const cond = genExpr(val, c);
579
- style['textDecoration'] = `\${${cond} ? 'line-through' : 'none'}`;
586
+ style['textDecoration'] = `${cond} ? 'line-through' : 'none'`;
587
+ dynamicKeys.add('textDecoration');
580
588
  break;
581
589
  }
582
590
  }
583
591
  }
584
592
  const content = genTextContent(node.content, c);
585
593
  const styleStr = Object.keys(style).length > 0 ? ` style={${genStyleObj(style, dynamicKeys)}}` : '';
586
- return `<span${styleStr}>${content}</span>`;
594
+ // Badge prop: render a badge indicator next to content
595
+ const badgeExpr = node.props['badge'];
596
+ const tooltipExpr = node.props['tooltip'];
597
+ let result = `<span${styleStr}>${content}</span>`;
598
+ if (badgeExpr) {
599
+ const badge = genExpr(badgeExpr, c);
600
+ result = `<span style={{ position: 'relative', display: 'inline-flex', alignItems: 'center' }}>\n<span${styleStr}>${content}</span>\n<span style={{ marginLeft: '6px', padding: '2px 6px', fontSize: '12px', fontWeight: 'bold', borderRadius: '9999px', backgroundColor: '#ef4444', color: '#fff', minWidth: '20px', textAlign: 'center' }}>{${badge}}</span>\n</span>`;
601
+ }
602
+ if (tooltipExpr) {
603
+ const tooltip = genExpr(tooltipExpr, c);
604
+ result = `<span title={${tooltip}}>${badgeExpr ? result.replace(/^<span/, '<span') : `<span${styleStr}>${content}</span>`}</span>`;
605
+ }
606
+ return result;
587
607
  }
588
608
  function genButton(node, c) {
589
609
  const label = genTextContent(node.label, c);
@@ -633,6 +653,7 @@ function genInput(node, c) {
633
653
  function genImage(node, c) {
634
654
  const src = genExpr(node.src, c);
635
655
  const props = [`src={${src}}`];
656
+ const style = {};
636
657
  for (const [key, val] of Object.entries(node.props)) {
637
658
  const v = genExpr(val, c);
638
659
  switch (key) {
@@ -645,8 +666,24 @@ function genImage(node, c) {
645
666
  case 'alt':
646
667
  props.push(`alt=${quoteJsx(v)}`);
647
668
  break;
669
+ case 'round':
670
+ style['borderRadius'] = "'50%'";
671
+ break;
672
+ case 'radius':
673
+ style['borderRadius'] = `'${addPx(v)}'`;
674
+ break;
675
+ case 'size': {
676
+ const px = `'${addPx(v)}'`;
677
+ style['width'] = px;
678
+ style['height'] = px;
679
+ break;
680
+ }
648
681
  }
649
682
  }
683
+ if (Object.keys(style).length > 0) {
684
+ const entries = Object.entries(style).map(([k, v]) => `${k}: ${v}`).join(', ');
685
+ props.push(`style={{ ${entries} }}`);
686
+ }
650
687
  return `<img ${props.join(' ')} />`;
651
688
  }
652
689
  function genLink(node, c) {
@@ -824,13 +861,26 @@ function genExpr(expr, c) {
824
861
  const method = expr.callee.property;
825
862
  if (objName && c.states.has(objName)) {
826
863
  const setter = 'set' + capitalize(objName);
864
+ const memberPath = extractMemberPath(expr.callee.object);
827
865
  if (method === 'push') {
866
+ if (memberPath.length > 0) {
867
+ const arrayPath = 'prev.' + memberPath.join('.');
868
+ return `${setter}(prev => ${buildSpreadUpdate('prev', memberPath, `[...${arrayPath}, ${args}]`)})`;
869
+ }
828
870
  return `${setter}(prev => [...prev, ${args}])`;
829
871
  }
830
872
  if (method === 'filter') {
873
+ if (memberPath.length > 0) {
874
+ const arrayPath = 'prev.' + memberPath.join('.');
875
+ return `${setter}(prev => ${buildSpreadUpdate('prev', memberPath, `${arrayPath}.filter(${args})`)})`;
876
+ }
831
877
  return `${setter}(prev => prev.filter(${args}))`;
832
878
  }
833
879
  if (method === 'remove') {
880
+ if (memberPath.length > 0) {
881
+ const arrayPath = 'prev.' + memberPath.join('.');
882
+ return `${setter}(prev => ${buildSpreadUpdate('prev', memberPath, `${arrayPath}.filter(item => item.id !== ${args})`)})`;
883
+ }
834
884
  return `${setter}(prev => prev.filter(item => item.id !== ${args}))`;
835
885
  }
836
886
  }
@@ -927,7 +977,19 @@ function genActionExpr(expr, c) {
927
977
  const stateName = extractStateName(expr.target);
928
978
  if (stateName && c.states.has(stateName)) {
929
979
  const setter = 'set' + capitalize(stateName);
980
+ // Evaluate value in readOnly mode to prevent double setState wrapping
981
+ const prevReadOnly = c.readOnly;
982
+ c.readOnly = true;
930
983
  const value = genExpr(expr.value, c);
984
+ c.readOnly = prevReadOnly;
985
+ const memberPath = extractMemberPath(expr.target);
986
+ if (memberPath.length > 0) {
987
+ if (expr.op === '=')
988
+ return `${setter}(prev => ${buildSpreadUpdate('prev', memberPath, value)})`;
989
+ const opChar = expr.op.charAt(0);
990
+ const prevAccess = 'prev.' + memberPath.join('.');
991
+ return `${setter}(prev => ${buildSpreadUpdate('prev', memberPath, `${prevAccess} ${opChar} ${value}`)})`;
992
+ }
931
993
  if (expr.op === '+=')
932
994
  return `${setter}(prev => prev + ${value})`;
933
995
  if (expr.op === '-=')