Custom Component düzenlemesi
This commit is contained in:
parent
9cc77fb3a1
commit
25ae424f42
40 changed files with 6852 additions and 4592 deletions
|
|
@ -22,6 +22,19 @@ public class CustomComponentAppService : CrudAppService<
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task<CustomComponentDto> UpdateAsync(
|
||||||
|
Guid id,
|
||||||
|
CreateUpdateCustomComponentDto input)
|
||||||
|
{
|
||||||
|
if (input.Props is null)
|
||||||
|
{
|
||||||
|
var existingComponent = await Repository.GetAsync(id);
|
||||||
|
input.Props = existingComponent.Props;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await base.UpdateAsync(id, input);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<List<CustomComponentDto>> GetActiveComponentsAsync()
|
public async Task<List<CustomComponentDto>> GetActiveComponentsAsync()
|
||||||
{
|
{
|
||||||
var components = await Repository.GetListAsync(x => x.IsActive);
|
var components = await Repository.GetListAsync(x => x.IsActive);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ public static class PlatformConsts
|
||||||
{
|
{
|
||||||
public static string Disabled = "{ \"disabled\" : true }";
|
public static string Disabled = "{ \"disabled\" : true }";
|
||||||
public static string ShowClearButton = "{ \"showClearButton\" : true }";
|
public static string ShowClearButton = "{ \"showClearButton\" : true }";
|
||||||
public static string HtmlEditorOptions = """{"mediaResizing":{"enabled":true},"imageUpload":{"tabs":["file","url"],"fileUploadMode":"base64"},"toolbar":{"multiline":true,"items":[{"name":"undo"},{"name":"redo"},{"name":"separator"},{"name":"size","acceptedValues":["8pt","10pt","12pt","14pt","18pt","24pt","36pt"],"options":{"inputAttr":{"aria-label":"Font size"}}},{"name":"font","acceptedValues":["Arial","Courier New","Georgia","Impact","Lucida Console","Tahoma","Times New Roman","Verdana"],"options":{"inputAttr":{"aria-label":"Font family"}}},{"name":"separator"},{"name":"bold"},{"name":"italic"},{"name":"strike"},{"name":"underline"},{"name":"separator"},{"name":"alignLeft"},{"name":"alignCenter"},{"name":"alignRight"},{"name":"alignJustify"},{"name":"separator"},{"name":"orderedList"},{"name":"bulletList"},{"name":"separator"},{"name":"header","acceptedValues":[false,1,2,3,4,5]},{"name":"separator"},{"name":"color"},{"name":"background"},{"name":"separator"},{"name":"link"},{"name":"image"},{"name":"separator"},{"name":"clear"},{"name":"codeBlock"},{"name":"blockquote"},{"name":"separator"},{"name":"insertTable"},{"name":"deleteTable"},{"name":"insertRowAbove"},{"name":"insertRowBelow"},{"name":"deleteRow"},{"name":"insertColumnLeft"},{"name":"insertColumnRight"},{"name":"deleteColumn"},{"name":"cellProperties"},{"name":"tableProperties"}]},"encodeHtml":true}""";
|
public static string HtmlEditorOptions = """{"mediaResizing":{"enabled":true},"imageUpload":{"tabs":["file","url"],"fileUploadMode":"base64"},"toolbar":{"multiline":true,"items":[{"name":"undo"},{"name":"redo"},{"name":"separator"},{"name":"size","acceptedValues":["8pt","10pt","12pt","14pt","18pt","24pt","36pt"],"options":{"inputAttr":{"aria-label":"Font size"}}},{"name":"font","acceptedValues":["Arial","Courier New","Georgia","Impact","Lucida Console","Tahoma","Times New Roman","Verdana"],"options":{"inputAttr":{"aria-label":"Font family"}}},{"name":"separator"},{"name":"bold"},{"name":"italic"},{"name":"strike"},{"name":"underline"},{"name":"separator"},{"name":"alignLeft"},{"name":"alignCenter"},{"name":"alignRight"},{"name":"alignJustify"},{"name":"separator"},{"name":"orderedList"},{"name":"bulletList"},{"name":"separator"},{"name":"header","acceptedValues":[false,1,2,3,4,5]},{"name":"separator"},{"name":"color"},{"name":"background"},{"name":"separator"},{"name":"link"},{"name":"image"},{"name":"separator"},{"name":"clear"},{"name":"codeBlock"},{"name":"blockquote"},{"name":"separator"},{"name":"insertTable"},{"name":"deleteTable"},{"name":"insertRowAbove"},{"name":"insertRowBelow"},{"name":"deleteRow"},{"name":"insertColumnLeft"},{"name":"insertColumnRight"},{"name":"deleteColumn"},{"name":"cellProperties"},{"name":"tableProperties"}]},"encodeHtml":true,"height":200}""";
|
||||||
public static string PhoneEditorOptions = "{\"format\": \"phoneGlobal\", \"mask\":\"(000) 000-0000\", \"maskInvalidMessage\":\"Lütfen geçerli bir telefon numarası girin\", \"useMaskedValue\":false, \"maskRules\": { \"X\": \"[0-9]\" }, \"placeholder\": \"(555) 123-4567\" }";
|
public static string PhoneEditorOptions = "{\"format\": \"phoneGlobal\", \"mask\":\"(000) 000-0000\", \"maskInvalidMessage\":\"Lütfen geçerli bir telefon numarası girin\", \"useMaskedValue\":false, \"maskRules\": { \"X\": \"[0-9]\" }, \"placeholder\": \"(555) 123-4567\" }";
|
||||||
public static string TimeSpanOptions = "{\"type\":\"time\",\"pickerType\":\"list\",\"displayFormat\":\"HH:mm\",\"dateSerializationFormat\":\"yyyy-MM-ddTHH:mm:ss\",\"interval\":5,\"width\":\"100%\"}";
|
public static string TimeSpanOptions = "{\"type\":\"time\",\"pickerType\":\"list\",\"displayFormat\":\"HH:mm\",\"dateSerializationFormat\":\"yyyy-MM-ddTHH:mm:ss\",\"interval\":5,\"width\":\"100%\"}";
|
||||||
public static string NumberStandartFormat(int precision = 2) => "{ \"format\": { \"type\": \"fixedPoint\", \"precision\": " + precision + " }, \"useMaskBehavior\": true, \"showSpinButtons\": true }";
|
public static string NumberStandartFormat(int precision = 2) => "{ \"format\": { \"type\": \"fixedPoint\", \"precision\": " + precision + " }, \"useMaskBehavior\": true, \"showSpinButtons\": true }";
|
||||||
|
|
|
||||||
|
|
@ -196,8 +196,8 @@ public class PlatformDbContext :
|
||||||
|
|
||||||
b.Property(a => a.Name).IsRequired().HasMaxLength(128);
|
b.Property(a => a.Name).IsRequired().HasMaxLength(128);
|
||||||
b.Property(a => a.ApiUrl).HasMaxLength(256);
|
b.Property(a => a.ApiUrl).HasMaxLength(256);
|
||||||
b.Property(a => a.Description).HasColumnType("text");
|
b.Property(a => a.Description).HasColumnType("ntext");
|
||||||
b.Property(a => a.Tenants).HasColumnType("text");
|
b.Property(a => a.Tenants).HasColumnType("ntext");
|
||||||
|
|
||||||
b.HasIndex(x => new { x.Name }).IsUnique().HasFilter(null);
|
b.HasIndex(x => new { x.Name }).IsUnique().HasFilter(null);
|
||||||
});
|
});
|
||||||
|
|
@ -349,60 +349,60 @@ public class PlatformDbContext :
|
||||||
b.Property(a => a.ListFormType).HasMaxLength(20);
|
b.Property(a => a.ListFormType).HasMaxLength(20);
|
||||||
|
|
||||||
// Uzun JSON alanları için nvarchar(max)
|
// Uzun JSON alanları için nvarchar(max)
|
||||||
b.Property(a => a.SelectCommand).HasColumnType("text");
|
b.Property(a => a.SelectCommand).HasColumnType("ntext");
|
||||||
b.Property(a => a.SelectFieldsDefaultValueJson).HasColumnType("text");
|
b.Property(a => a.SelectFieldsDefaultValueJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.DefaultFilter).IsUnicode(true);
|
b.Property(a => a.DefaultFilter).IsUnicode(true);
|
||||||
b.Property(a => a.ColumnOptionJson).HasColumnType("text");
|
b.Property(a => a.ColumnOptionJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.PivotOptionJson).HasColumnType("text");
|
b.Property(a => a.PivotOptionJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.FilterRowJson).HasColumnType("text");
|
b.Property(a => a.FilterRowJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.HeaderFilterJson).HasColumnType("text");
|
b.Property(a => a.HeaderFilterJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.FilterPanelJson).HasColumnType("text");
|
b.Property(a => a.FilterPanelJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.SearchPanelJson).HasColumnType("text");
|
b.Property(a => a.SearchPanelJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.GroupPanelJson).HasColumnType("text");
|
b.Property(a => a.GroupPanelJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.SelectionJson).HasColumnType("text");
|
b.Property(a => a.SelectionJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.PagerOptionJson).HasColumnType("text");
|
b.Property(a => a.PagerOptionJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.EditingOptionJson).HasColumnType("text");
|
b.Property(a => a.EditingOptionJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.EditingFormJson).HasColumnType("text");
|
b.Property(a => a.EditingFormJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.PermissionJson).HasColumnType("text");
|
b.Property(a => a.PermissionJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.InsertFieldsDefaultValueJson).HasColumnType("text");
|
b.Property(a => a.InsertFieldsDefaultValueJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.UpdateFieldsDefaultValueJson).HasColumnType("text");
|
b.Property(a => a.UpdateFieldsDefaultValueJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.DeleteFieldsDefaultValueJson).HasColumnType("text");
|
b.Property(a => a.DeleteFieldsDefaultValueJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.CommandColumnJson).HasColumnType("text");
|
b.Property(a => a.CommandColumnJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.StateStoringJson).HasColumnType("text");
|
b.Property(a => a.StateStoringJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.DeleteCommand).HasColumnType("text");
|
b.Property(a => a.DeleteCommand).HasColumnType("ntext");
|
||||||
b.Property(a => a.UpdateCommand).HasColumnType("text");
|
b.Property(a => a.UpdateCommand).HasColumnType("ntext");
|
||||||
b.Property(a => a.InsertCommand).HasColumnType("text");
|
b.Property(a => a.InsertCommand).HasColumnType("ntext");
|
||||||
b.Property(a => a.CustomJsSourcesJson).HasColumnType("text");
|
b.Property(a => a.CustomJsSourcesJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.CustomStyleSourcesJson).HasColumnType("text");
|
b.Property(a => a.CustomStyleSourcesJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.FormFieldsDefaultValueJson).HasColumnType("text");
|
b.Property(a => a.FormFieldsDefaultValueJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.SubFormsJson).HasColumnType("text");
|
b.Property(a => a.SubFormsJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.WidgetsJson).HasColumnType("text");
|
b.Property(a => a.WidgetsJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.WorkflowJson).HasColumnType("text");
|
b.Property(a => a.WorkflowJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.ExtraFilterJson).HasColumnType("text");
|
b.Property(a => a.ExtraFilterJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.LayoutJson).HasColumnType("text");
|
b.Property(a => a.LayoutJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.TodoOptionJson).HasColumnType("text");
|
b.Property(a => a.TodoOptionJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.CommonJson).HasColumnType("text");
|
b.Property(a => a.CommonJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.DataSourceJson).HasColumnType("text");
|
b.Property(a => a.DataSourceJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.AdaptiveLayoutJson).HasColumnType("text");
|
b.Property(a => a.AdaptiveLayoutJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.AnimationJson).HasColumnType("text");
|
b.Property(a => a.AnimationJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.AnnotationsJson).HasColumnType("text");
|
b.Property(a => a.AnnotationsJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.ArgumentAxisJson).HasColumnType("text");
|
b.Property(a => a.ArgumentAxisJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.CommonAnnotationsSettingsJson).HasColumnType("text");
|
b.Property(a => a.CommonAnnotationsSettingsJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.CommonAxisSettingsJson).HasColumnType("text");
|
b.Property(a => a.CommonAxisSettingsJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.CommonPaneSettingsJson).HasColumnType("text");
|
b.Property(a => a.CommonPaneSettingsJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.CommonSeriesSettingsJson).HasColumnType("text");
|
b.Property(a => a.CommonSeriesSettingsJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.CrosshairJson).HasColumnType("text");
|
b.Property(a => a.CrosshairJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.ExportJson).HasColumnType("text");
|
b.Property(a => a.ExportJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.LegendJson).HasColumnType("text");
|
b.Property(a => a.LegendJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.MarginJson).HasColumnType("text");
|
b.Property(a => a.MarginJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.PanesJson).HasColumnType("text");
|
b.Property(a => a.PanesJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.ScrollBarJson).HasColumnType("text");
|
b.Property(a => a.ScrollBarJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.SeriesJson).HasColumnType("text");
|
b.Property(a => a.SeriesJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.SizeJson).HasColumnType("text");
|
b.Property(a => a.SizeJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.TitleJson).HasColumnType("text");
|
b.Property(a => a.TitleJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.TooltipJson).HasColumnType("text");
|
b.Property(a => a.TooltipJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.ValueAxisJson).HasColumnType("text");
|
b.Property(a => a.ValueAxisJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.ZoomAndPanJson).HasColumnType("text");
|
b.Property(a => a.ZoomAndPanJson).HasColumnType("ntext");
|
||||||
|
|
||||||
b.HasIndex(x => new { x.ListFormCode }).IsUnique().HasFilter(null);
|
b.HasIndex(x => new { x.ListFormCode }).IsUnique().HasFilter(null);
|
||||||
|
|
||||||
|
|
@ -438,27 +438,27 @@ public class PlatformDbContext :
|
||||||
b.Property(a => a.BandName).HasMaxLength(128);
|
b.Property(a => a.BandName).HasMaxLength(128);
|
||||||
|
|
||||||
// JSON alanlar
|
// JSON alanlar
|
||||||
b.Property(a => a.ColumnFilterJson).HasColumnType("text");
|
b.Property(a => a.ColumnFilterJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.ColumnHeaderJson).HasColumnType("text");
|
b.Property(a => a.ColumnHeaderJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.GroupingJson).HasColumnType("text");
|
b.Property(a => a.GroupingJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.ColumnCustomizationJson).HasColumnType("text");
|
b.Property(a => a.ColumnCustomizationJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.TotalSummaryJson).HasColumnType("text");
|
b.Property(a => a.TotalSummaryJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.GroupSummaryJson).HasColumnType("text");
|
b.Property(a => a.GroupSummaryJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.JoinTableJson).HasColumnType("text");
|
b.Property(a => a.JoinTableJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.LookupJson).HasColumnType("text");
|
b.Property(a => a.LookupJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.ValidationRuleJson).HasColumnType("text");
|
b.Property(a => a.ValidationRuleJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.ColumnStylingJson).HasColumnType("text");
|
b.Property(a => a.ColumnStylingJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.PermissionJson).HasColumnType("text");
|
b.Property(a => a.PermissionJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.PivotSettingsJson).HasColumnType("text");
|
b.Property(a => a.PivotSettingsJson).HasColumnType("ntext");
|
||||||
|
|
||||||
// Stil ve format
|
// Stil ve format
|
||||||
b.Property(a => a.ColumnCssClass).HasMaxLength(128);
|
b.Property(a => a.ColumnCssClass).HasMaxLength(128);
|
||||||
b.Property(a => a.ColumnCssValue).HasMaxLength(256);
|
b.Property(a => a.ColumnCssValue).HasMaxLength(256);
|
||||||
b.Property(a => a.Alignment).HasMaxLength(20).HasDefaultValue("left");
|
b.Property(a => a.Alignment).HasMaxLength(20).HasDefaultValue("left");
|
||||||
b.Property(a => a.Format).HasMaxLength(128);
|
b.Property(a => a.Format).HasMaxLength(128);
|
||||||
b.Property(a => a.EditorOptions).HasColumnType("text");
|
b.Property(a => a.EditorOptions).HasColumnType("ntext");
|
||||||
b.Property(a => a.EditorType2).HasMaxLength(64);
|
b.Property(a => a.EditorType2).HasMaxLength(64);
|
||||||
b.Property(a => a.EditorScript).HasColumnType("text");
|
b.Property(a => a.EditorScript).HasColumnType("ntext");
|
||||||
|
|
||||||
b.HasIndex(x => new { x.ListFormCode, x.UserId, x.RoleId, x.CultureName, x.FieldName }).IsUnique().HasFilter(null);
|
b.HasIndex(x => new { x.ListFormCode, x.UserId, x.RoleId, x.CultureName, x.FieldName }).IsUnique().HasFilter(null);
|
||||||
});
|
});
|
||||||
|
|
@ -472,7 +472,7 @@ public class PlatformDbContext :
|
||||||
b.Property(a => a.UserId).HasMaxLength(256);
|
b.Property(a => a.UserId).HasMaxLength(256);
|
||||||
b.Property(a => a.RoleId).HasMaxLength(256);
|
b.Property(a => a.RoleId).HasMaxLength(256);
|
||||||
b.Property(a => a.FilterName).HasMaxLength(128);
|
b.Property(a => a.FilterName).HasMaxLength(128);
|
||||||
b.Property(a => a.CustomizationData).HasColumnType("text");
|
b.Property(a => a.CustomizationData).HasColumnType("ntext");
|
||||||
b.Property(a => a.CustomizationType).IsRequired();
|
b.Property(a => a.CustomizationType).IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -496,7 +496,7 @@ public class PlatformDbContext :
|
||||||
b.Property(x => x.ImportId).IsRequired();
|
b.Property(x => x.ImportId).IsRequired();
|
||||||
b.Property(x => x.BlobName).IsRequired().HasMaxLength(256);
|
b.Property(x => x.BlobName).IsRequired().HasMaxLength(256);
|
||||||
b.Property(x => x.Status).IsRequired().HasMaxLength(64);
|
b.Property(x => x.Status).IsRequired().HasMaxLength(64);
|
||||||
b.Property(x => x.ErrorsJson).HasColumnType("text");
|
b.Property(x => x.ErrorsJson).HasColumnType("ntext");
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Entity<ListFormWorkflow>(b =>
|
builder.Entity<ListFormWorkflow>(b =>
|
||||||
|
|
@ -519,7 +519,7 @@ public class PlatformDbContext :
|
||||||
b.Property(x => x.NextOnReject).IsRequired().HasMaxLength(128);
|
b.Property(x => x.NextOnReject).IsRequired().HasMaxLength(128);
|
||||||
b.Property(x => x.PositionX).IsRequired();
|
b.Property(x => x.PositionX).IsRequired();
|
||||||
b.Property(x => x.PositionY).IsRequired();
|
b.Property(x => x.PositionY).IsRequired();
|
||||||
b.Property(x => x.CompareOutcomesJson).HasColumnType("text");
|
b.Property(x => x.CompareOutcomesJson).HasColumnType("ntext");
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Entity<Note>(b =>
|
builder.Entity<Note>(b =>
|
||||||
|
|
@ -546,7 +546,7 @@ public class PlatformDbContext :
|
||||||
b.Property(a => a.DataSourceCode).HasMaxLength(64);
|
b.Property(a => a.DataSourceCode).HasMaxLength(64);
|
||||||
b.Property(a => a.BeforeSp).HasMaxLength(128);
|
b.Property(a => a.BeforeSp).HasMaxLength(128);
|
||||||
b.Property(a => a.AfterSp).HasMaxLength(128);
|
b.Property(a => a.AfterSp).HasMaxLength(128);
|
||||||
b.Property(a => a.Options).HasColumnType("text");
|
b.Property(a => a.Options).HasColumnType("ntext");
|
||||||
|
|
||||||
b.HasIndex(x => new { x.Name }).IsUnique().HasFilter("[IsDeleted] = 0");
|
b.HasIndex(x => new { x.Name }).IsUnique().HasFilter("[IsDeleted] = 0");
|
||||||
});
|
});
|
||||||
|
|
@ -638,8 +638,8 @@ public class PlatformDbContext :
|
||||||
b.Property(a => a.Method).IsRequired().HasMaxLength(10);
|
b.Property(a => a.Method).IsRequired().HasMaxLength(10);
|
||||||
b.Property(a => a.DataSourceCode).IsRequired().HasMaxLength(128);
|
b.Property(a => a.DataSourceCode).IsRequired().HasMaxLength(128);
|
||||||
b.Property(a => a.Sql).IsRequired();
|
b.Property(a => a.Sql).IsRequired();
|
||||||
b.Property(a => a.ParametersJson).HasColumnType("text");
|
b.Property(a => a.ParametersJson).HasColumnType("ntext");
|
||||||
b.Property(a => a.PermissionsJson).HasColumnType("text");
|
b.Property(a => a.PermissionsJson).HasColumnType("ntext");
|
||||||
|
|
||||||
b.HasIndex(x => new { x.TenantId, x.Name }).IsUnique().HasFilter("[IsDeleted] = 0");
|
b.HasIndex(x => new { x.TenantId, x.Name }).IsUnique().HasFilter("[IsDeleted] = 0");
|
||||||
});
|
});
|
||||||
|
|
@ -652,7 +652,9 @@ public class PlatformDbContext :
|
||||||
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
|
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
|
||||||
b.Property(x => x.RoutePath).IsRequired().HasMaxLength(512);
|
b.Property(x => x.RoutePath).IsRequired().HasMaxLength(512);
|
||||||
b.Property(x => x.Code).IsRequired();
|
b.Property(x => x.Code).IsRequired();
|
||||||
b.Property(x => x.Props).HasMaxLength(1024);
|
// The visual designer document contains user-facing text. Keep it Unicode so
|
||||||
|
// Turkish and other non-ASCII property values survive a database round-trip.
|
||||||
|
b.Property(x => x.Props).HasColumnType("nvarchar(max)");
|
||||||
b.Property(x => x.Description).HasMaxLength(512);
|
b.Property(x => x.Description).HasMaxLength(512);
|
||||||
b.Property(x => x.Dependencies).HasMaxLength(2048);
|
b.Property(x => x.Dependencies).HasMaxLength(2048);
|
||||||
|
|
||||||
|
|
@ -931,9 +933,9 @@ public class PlatformDbContext :
|
||||||
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.About)), Prefix.DbSchema);
|
b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.About)), Prefix.DbSchema);
|
||||||
b.ConfigureByConvention();
|
b.ConfigureByConvention();
|
||||||
|
|
||||||
b.Property(x => x.StatsJson).HasColumnType("text");
|
b.Property(x => x.StatsJson).HasColumnType("ntext");
|
||||||
b.Property(x => x.DescriptionsJson).HasColumnType("text");
|
b.Property(x => x.DescriptionsJson).HasColumnType("ntext");
|
||||||
b.Property(x => x.SectionsJson).HasColumnType("text");
|
b.Property(x => x.SectionsJson).HasColumnType("ntext");
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Entity<Home>(b =>
|
builder.Entity<Home>(b =>
|
||||||
|
|
@ -951,9 +953,9 @@ public class PlatformDbContext :
|
||||||
b.Property(x => x.CtaTitleKey).HasMaxLength(256);
|
b.Property(x => x.CtaTitleKey).HasMaxLength(256);
|
||||||
b.Property(x => x.CtaSubtitleKey).HasMaxLength(256);
|
b.Property(x => x.CtaSubtitleKey).HasMaxLength(256);
|
||||||
b.Property(x => x.CtaButtonLabelKey).HasMaxLength(256);
|
b.Property(x => x.CtaButtonLabelKey).HasMaxLength(256);
|
||||||
b.Property(x => x.SlidesJson).HasColumnType("text");
|
b.Property(x => x.SlidesJson).HasColumnType("ntext");
|
||||||
b.Property(x => x.FeaturesJson).HasColumnType("text");
|
b.Property(x => x.FeaturesJson).HasColumnType("ntext");
|
||||||
b.Property(x => x.SolutionsJson).HasColumnType("text");
|
b.Property(x => x.SolutionsJson).HasColumnType("ntext");
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Entity<Service>(b =>
|
builder.Entity<Service>(b =>
|
||||||
|
|
@ -1143,9 +1145,9 @@ public class PlatformDbContext :
|
||||||
b.Property(x => x.Email).HasMaxLength(128);
|
b.Property(x => x.Email).HasMaxLength(128);
|
||||||
b.Property(x => x.Location).HasMaxLength(256);
|
b.Property(x => x.Location).HasMaxLength(256);
|
||||||
|
|
||||||
b.Property(x => x.BankJson).HasColumnType("text");
|
b.Property(x => x.BankJson).HasColumnType("ntext");
|
||||||
b.Property(x => x.WorkHoursJson).HasColumnType("text");
|
b.Property(x => x.WorkHoursJson).HasColumnType("ntext");
|
||||||
b.Property(x => x.MapJson).HasColumnType("text");
|
b.Property(x => x.MapJson).HasColumnType("ntext");
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Entity<LogEntry>(b =>
|
builder.Entity<LogEntry>(b =>
|
||||||
|
|
@ -1162,10 +1164,10 @@ public class PlatformDbContext :
|
||||||
b.Property(x => x.Name).IsRequired().HasMaxLength(256);
|
b.Property(x => x.Name).IsRequired().HasMaxLength(256);
|
||||||
b.Property(x => x.DisplayName).HasMaxLength(512);
|
b.Property(x => x.DisplayName).HasMaxLength(512);
|
||||||
b.Property(x => x.Description).HasMaxLength(2048);
|
b.Property(x => x.Description).HasMaxLength(2048);
|
||||||
b.Property(x => x.Code).IsRequired().HasColumnType("text"); // Uzun C# kod metni için
|
b.Property(x => x.Code).IsRequired().HasColumnType("ntext"); // Uzun C# kod metni için
|
||||||
b.Property(x => x.IsActive).IsRequired().HasDefaultValue(true);
|
b.Property(x => x.IsActive).IsRequired().HasDefaultValue(true);
|
||||||
b.Property(x => x.CompilationStatus).IsRequired().HasConversion<string>().HasMaxLength(20);
|
b.Property(x => x.CompilationStatus).IsRequired().HasConversion<string>().HasMaxLength(20);
|
||||||
b.Property(x => x.LastCompilationError).HasColumnType("text"); // Uzun hata mesajları için
|
b.Property(x => x.LastCompilationError).HasColumnType("ntext"); // Uzun hata mesajları için
|
||||||
b.Property(x => x.LastSuccessfulCompilation).IsRequired(false);
|
b.Property(x => x.LastSuccessfulCompilation).IsRequired(false);
|
||||||
b.Property(x => x.Version).IsRequired().HasDefaultValue(1);
|
b.Property(x => x.Version).IsRequired().HasDefaultValue(1);
|
||||||
b.Property(x => x.CodeHash).HasMaxLength(64);
|
b.Property(x => x.CodeHash).HasMaxLength(64);
|
||||||
|
|
@ -1187,7 +1189,7 @@ public class PlatformDbContext :
|
||||||
b.Property(x => x.PublishDate).IsRequired();
|
b.Property(x => x.PublishDate).IsRequired();
|
||||||
b.Property(x => x.Attachments).HasMaxLength(2048);
|
b.Property(x => x.Attachments).HasMaxLength(2048);
|
||||||
b.Property(x => x.ViewCount).HasDefaultValue(0);
|
b.Property(x => x.ViewCount).HasDefaultValue(0);
|
||||||
b.Property(x => x.ImageUrl).HasColumnType("text");
|
b.Property(x => x.ImageUrl).HasColumnType("ntext");
|
||||||
|
|
||||||
b.HasIndex(x => new { x.TenantId, x.Title }).IsUnique().HasFilter("[IsDeleted] = 0");
|
b.HasIndex(x => new { x.TenantId, x.Title }).IsUnique().HasFilter("[IsDeleted] = 0");
|
||||||
});
|
});
|
||||||
|
|
@ -1403,7 +1405,7 @@ public class PlatformDbContext :
|
||||||
b.Property(x => x.Description).HasMaxLength(1024);
|
b.Property(x => x.Description).HasMaxLength(1024);
|
||||||
b.Property(x => x.Status).HasMaxLength(20);
|
b.Property(x => x.Status).HasMaxLength(20);
|
||||||
b.Property(x => x.isPublished).HasDefaultValue(false);
|
b.Property(x => x.isPublished).HasDefaultValue(false);
|
||||||
b.Property(x => x.Photos).HasColumnType("text");
|
b.Property(x => x.Photos).HasColumnType("ntext");
|
||||||
|
|
||||||
b.HasIndex(x => new { x.TenantId, x.CategoryId, x.TypeId, x.Name }).IsUnique().HasFilter("[IsDeleted] = 0");
|
b.HasIndex(x => new { x.TenantId, x.CategoryId, x.TypeId, x.Name }).IsUnique().HasFilter("[IsDeleted] = 0");
|
||||||
|
|
||||||
|
|
@ -1452,7 +1454,7 @@ public class PlatformDbContext :
|
||||||
|
|
||||||
b.Property(x => x.Title).HasMaxLength(256);
|
b.Property(x => x.Title).HasMaxLength(256);
|
||||||
b.Property(x => x.ParticipantKey).IsRequired().HasMaxLength(512);
|
b.Property(x => x.ParticipantKey).IsRequired().HasMaxLength(512);
|
||||||
b.Property(x => x.ParticipantIdsJson).IsRequired().HasColumnType("text");
|
b.Property(x => x.ParticipantIdsJson).IsRequired().HasColumnType("ntext");
|
||||||
b.Property(x => x.LastMessagePreview).HasMaxLength(512);
|
b.Property(x => x.LastMessagePreview).HasMaxLength(512);
|
||||||
b.Property(x => x.MessageCount).HasDefaultValue(0);
|
b.Property(x => x.MessageCount).HasDefaultValue(0);
|
||||||
|
|
||||||
|
|
@ -1469,9 +1471,9 @@ public class PlatformDbContext :
|
||||||
|
|
||||||
b.Property(x => x.SenderUserName).IsRequired().HasMaxLength(256);
|
b.Property(x => x.SenderUserName).IsRequired().HasMaxLength(256);
|
||||||
b.Property(x => x.SenderName).IsRequired().HasMaxLength(256);
|
b.Property(x => x.SenderName).IsRequired().HasMaxLength(256);
|
||||||
b.Property(x => x.RecipientIdsJson).IsRequired().HasColumnType("text");
|
b.Property(x => x.RecipientIdsJson).IsRequired().HasColumnType("ntext");
|
||||||
b.Property(x => x.Text).HasMaxLength(4096);
|
b.Property(x => x.Text).HasMaxLength(4096);
|
||||||
b.Property(x => x.AttachmentsJson).IsRequired().HasColumnType("text");
|
b.Property(x => x.AttachmentsJson).IsRequired().HasColumnType("ntext");
|
||||||
b.Property(x => x.SentAt).IsRequired();
|
b.Property(x => x.SentAt).IsRequired();
|
||||||
|
|
||||||
b.HasIndex(x => new { x.TenantId, x.ConversationId, x.SentAt });
|
b.HasIndex(x => new { x.TenantId, x.ConversationId, x.SentAt });
|
||||||
|
|
@ -1575,7 +1577,7 @@ public class PlatformDbContext :
|
||||||
b.Property(x => x.ResourceName).HasMaxLength(64);
|
b.Property(x => x.ResourceName).HasMaxLength(64);
|
||||||
b.Property(x => x.MaxForms).HasDefaultValue(500);
|
b.Property(x => x.MaxForms).HasDefaultValue(500);
|
||||||
b.Property(x => x.CacheKey).HasMaxLength(512).IsRequired();
|
b.Property(x => x.CacheKey).HasMaxLength(512).IsRequired();
|
||||||
b.Property(x => x.JsonData).IsRequired().HasColumnType("text");
|
b.Property(x => x.JsonData).IsRequired().HasColumnType("ntext");
|
||||||
b.Property(x => x.FormCount).HasDefaultValue(0);
|
b.Property(x => x.FormCount).HasDefaultValue(0);
|
||||||
b.Property(x => x.FieldCount).HasDefaultValue(0);
|
b.Property(x => x.FieldCount).HasDefaultValue(0);
|
||||||
b.Property(x => x.JsonLength).HasDefaultValue(0);
|
b.Property(x => x.JsonLength).HasDefaultValue(0);
|
||||||
|
|
@ -1594,7 +1596,7 @@ public class PlatformDbContext :
|
||||||
|
|
||||||
b.Property(e => e.Title).HasMaxLength(512).IsRequired();
|
b.Property(e => e.Title).HasMaxLength(512).IsRequired();
|
||||||
b.Property(e => e.Status).HasMaxLength(64).IsRequired();
|
b.Property(e => e.Status).HasMaxLength(64).IsRequired();
|
||||||
b.Property(e => e.Description).HasColumnType("text");
|
b.Property(e => e.Description).HasColumnType("ntext");
|
||||||
b.Property(e => e.Tags).HasMaxLength(256);
|
b.Property(e => e.Tags).HasMaxLength(256);
|
||||||
b.Property(e => e.Assignees).HasMaxLength(512);
|
b.Property(e => e.Assignees).HasMaxLength(512);
|
||||||
b.Property(e => e.Priority).HasMaxLength(64);
|
b.Property(e => e.Priority).HasMaxLength(64);
|
||||||
|
|
@ -1610,10 +1612,10 @@ public class PlatformDbContext :
|
||||||
b.Property(e => e.UserName).HasMaxLength(256).IsRequired();
|
b.Property(e => e.UserName).HasMaxLength(256).IsRequired();
|
||||||
|
|
||||||
b.Property(e => e.Text).HasMaxLength(512).IsRequired();
|
b.Property(e => e.Text).HasMaxLength(512).IsRequired();
|
||||||
b.Property(e => e.Description).HasColumnType("text");
|
b.Property(e => e.Description).HasColumnType("ntext");
|
||||||
b.Property(e => e.AllDay).HasDefaultValue(false);
|
b.Property(e => e.AllDay).HasDefaultValue(false);
|
||||||
b.Property(e => e.RecurrenceRule).HasMaxLength(512);
|
b.Property(e => e.RecurrenceRule).HasMaxLength(512);
|
||||||
b.Property(e => e.RecurrenceException).HasColumnType("text");
|
b.Property(e => e.RecurrenceException).HasColumnType("ntext");
|
||||||
});
|
});
|
||||||
|
|
||||||
ConfigureProviderSpecificModel(builder);
|
ConfigureProviderSpecificModel(builder);
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
||||||
namespace Sozsoft.Platform.Migrations
|
namespace Sozsoft.Platform.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PlatformDbContext))]
|
[DbContext(typeof(PlatformDbContext))]
|
||||||
[Migration("20260731143824_Initial")]
|
[Migration("20260805202101_Initial")]
|
||||||
partial class Initial
|
partial class Initial
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -652,7 +652,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("DeletionTime");
|
.HasColumnName("DeletionTime");
|
||||||
|
|
||||||
b.Property<string>("DescriptionsJson")
|
b.Property<string>("DescriptionsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<bool>("IsDeleted")
|
b.Property<bool>("IsDeleted")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
|
|
@ -669,10 +669,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("LastModifierId");
|
.HasColumnName("LastModifierId");
|
||||||
|
|
||||||
b.Property<string>("SectionsJson")
|
b.Property<string>("SectionsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("StatsJson")
|
b.Property<string>("StatsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
|
@ -706,7 +706,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("DeletionTime");
|
.HasColumnName("DeletionTime");
|
||||||
|
|
||||||
b.Property<string>("Description")
|
b.Property<string>("Description")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<DateTime>("EndDate")
|
b.Property<DateTime>("EndDate")
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
@ -726,7 +726,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("LastModifierId");
|
.HasColumnName("LastModifierId");
|
||||||
|
|
||||||
b.Property<string>("RecurrenceException")
|
b.Property<string>("RecurrenceException")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("RecurrenceRule")
|
b.Property<string>("RecurrenceRule")
|
||||||
.HasMaxLength(512)
|
.HasMaxLength(512)
|
||||||
|
|
@ -764,7 +764,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
b.Property<string>("Description")
|
b.Property<string>("Description")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<bool>("IsActive")
|
b.Property<bool>("IsActive")
|
||||||
.HasColumnType("bit");
|
.HasColumnType("bit");
|
||||||
|
|
@ -775,7 +775,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(128)");
|
.HasColumnType("nvarchar(128)");
|
||||||
|
|
||||||
b.Property<string>("Tenants")
|
b.Property<string>("Tenants")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
|
@ -829,7 +829,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
b.Property<string>("ImageUrl")
|
b.Property<string>("ImageUrl")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<bool>("IsDeleted")
|
b.Property<bool>("IsDeleted")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
|
|
@ -1059,7 +1059,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(128)");
|
.HasColumnType("nvarchar(128)");
|
||||||
|
|
||||||
b.Property<string>("Options")
|
b.Property<string>("Options")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<int>("WorkerType")
|
b.Property<int>("WorkerType")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
@ -1453,7 +1453,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(512)");
|
.HasColumnType("nvarchar(512)");
|
||||||
|
|
||||||
b.Property<string>("BankJson")
|
b.Property<string>("BankJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<DateTime>("CreationTime")
|
b.Property<DateTime>("CreationTime")
|
||||||
.HasColumnType("datetime2")
|
.HasColumnType("datetime2")
|
||||||
|
|
@ -1494,7 +1494,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
b.Property<string>("MapJson")
|
b.Property<string>("MapJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("PhoneNumber")
|
b.Property<string>("PhoneNumber")
|
||||||
.HasMaxLength(20)
|
.HasMaxLength(20)
|
||||||
|
|
@ -1504,7 +1504,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<string>("WorkHoursJson")
|
b.Property<string>("WorkHoursJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
|
@ -1898,8 +1898,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(128)");
|
.HasColumnType("nvarchar(128)");
|
||||||
|
|
||||||
b.Property<string>("Props")
|
b.Property<string>("Props")
|
||||||
.HasMaxLength(1024)
|
.HasColumnType("nvarchar(max)");
|
||||||
.HasColumnType("nvarchar(1024)");
|
|
||||||
|
|
||||||
b.Property<string>("RoutePath")
|
b.Property<string>("RoutePath")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
|
@ -1978,10 +1977,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(128)");
|
.HasColumnType("nvarchar(128)");
|
||||||
|
|
||||||
b.Property<string>("ParametersJson")
|
b.Property<string>("ParametersJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("PermissionsJson")
|
b.Property<string>("PermissionsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("Sql")
|
b.Property<string>("Sql")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
|
@ -2103,7 +2102,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.Property<string>("JsonData")
|
b.Property<string>("JsonData")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<int>("JsonLength")
|
b.Property<int>("JsonLength")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
|
|
@ -2323,7 +2322,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("DeletionTime");
|
.HasColumnName("DeletionTime");
|
||||||
|
|
||||||
b.Property<string>("Description")
|
b.Property<string>("Description")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<DateTime?>("DueDate")
|
b.Property<DateTime?>("DueDate")
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
@ -2458,7 +2457,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.Property<string>("Code")
|
b.Property<string>("Code")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CodeHash")
|
b.Property<string>("CodeHash")
|
||||||
.HasMaxLength(64)
|
.HasMaxLength(64)
|
||||||
|
|
@ -2509,7 +2508,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("IsDeleted");
|
.HasColumnName("IsDeleted");
|
||||||
|
|
||||||
b.Property<string>("LastCompilationError")
|
b.Property<string>("LastCompilationError")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<DateTime?>("LastModificationTime")
|
b.Property<DateTime?>("LastModificationTime")
|
||||||
.HasColumnType("datetime2")
|
.HasColumnType("datetime2")
|
||||||
|
|
@ -2606,7 +2605,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("Photos")
|
b.Property<string>("Photos")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("Place")
|
b.Property<string>("Place")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
|
|
@ -2937,7 +2936,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("DeletionTime");
|
.HasColumnName("DeletionTime");
|
||||||
|
|
||||||
b.Property<string>("FeaturesJson")
|
b.Property<string>("FeaturesJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("FeaturesSubtitleKey")
|
b.Property<string>("FeaturesSubtitleKey")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
|
|
@ -2974,10 +2973,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("LastModifierId");
|
.HasColumnName("LastModifierId");
|
||||||
|
|
||||||
b.Property<string>("SlidesJson")
|
b.Property<string>("SlidesJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SolutionsJson")
|
b.Property<string>("SolutionsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SolutionsSubtitleKey")
|
b.Property<string>("SolutionsSubtitleKey")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
|
|
@ -3180,40 +3179,40 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
b.Property<string>("AdaptiveLayoutJson")
|
b.Property<string>("AdaptiveLayoutJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("AnimationJson")
|
b.Property<string>("AnimationJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("AnnotationsJson")
|
b.Property<string>("AnnotationsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ArgumentAxisJson")
|
b.Property<string>("ArgumentAxisJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ColumnOptionJson")
|
b.Property<string>("ColumnOptionJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CommandColumnJson")
|
b.Property<string>("CommandColumnJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CommonAnnotationsSettingsJson")
|
b.Property<string>("CommonAnnotationsSettingsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CommonAxisSettingsJson")
|
b.Property<string>("CommonAxisSettingsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CommonJson")
|
b.Property<string>("CommonJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CommonPaneSettingsJson")
|
b.Property<string>("CommonPaneSettingsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CommonSeriesSettingsJson")
|
b.Property<string>("CommonSeriesSettingsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CrosshairJson")
|
b.Property<string>("CrosshairJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CultureName")
|
b.Property<string>("CultureName")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
|
@ -3221,17 +3220,17 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(10)");
|
.HasColumnType("nvarchar(10)");
|
||||||
|
|
||||||
b.Property<string>("CustomJsSourcesJson")
|
b.Property<string>("CustomJsSourcesJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CustomStyleSourcesJson")
|
b.Property<string>("CustomStyleSourcesJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("DataSourceCode")
|
b.Property<string>("DataSourceCode")
|
||||||
.HasMaxLength(128)
|
.HasMaxLength(128)
|
||||||
.HasColumnType("nvarchar(128)");
|
.HasColumnType("nvarchar(128)");
|
||||||
|
|
||||||
b.Property<string>("DataSourceJson")
|
b.Property<string>("DataSourceJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("DefaultFilter")
|
b.Property<string>("DefaultFilter")
|
||||||
.IsUnicode(true)
|
.IsUnicode(true)
|
||||||
|
|
@ -3244,10 +3243,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("DeleteCommand")
|
b.Property<string>("DeleteCommand")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("DeleteFieldsDefaultValueJson")
|
b.Property<string>("DeleteFieldsDefaultValueJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("DeleteServiceAddress")
|
b.Property<string>("DeleteServiceAddress")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
|
|
@ -3258,25 +3257,25 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(512)");
|
.HasColumnType("nvarchar(512)");
|
||||||
|
|
||||||
b.Property<string>("EditingFormJson")
|
b.Property<string>("EditingFormJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("EditingOptionJson")
|
b.Property<string>("EditingOptionJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ExportJson")
|
b.Property<string>("ExportJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ExtraFilterJson")
|
b.Property<string>("ExtraFilterJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("FilterPanelJson")
|
b.Property<string>("FilterPanelJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("FilterRowJson")
|
b.Property<string>("FilterRowJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("FormFieldsDefaultValueJson")
|
b.Property<string>("FormFieldsDefaultValueJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<bool>("FullHeight")
|
b.Property<bool>("FullHeight")
|
||||||
.HasColumnType("bit");
|
.HasColumnType("bit");
|
||||||
|
|
@ -3285,10 +3284,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("GroupPanelJson")
|
b.Property<string>("GroupPanelJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("HeaderFilterJson")
|
b.Property<string>("HeaderFilterJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<int?>("Height")
|
b.Property<int?>("Height")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
@ -3300,10 +3299,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("InsertCommand")
|
b.Property<string>("InsertCommand")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("InsertFieldsDefaultValueJson")
|
b.Property<string>("InsertFieldsDefaultValueJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("InsertServiceAddress")
|
b.Property<string>("InsertServiceAddress")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
|
|
@ -3329,10 +3328,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(64)");
|
.HasColumnType("nvarchar(64)");
|
||||||
|
|
||||||
b.Property<string>("LayoutJson")
|
b.Property<string>("LayoutJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("LegendJson")
|
b.Property<string>("LegendJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ListFormCode")
|
b.Property<string>("ListFormCode")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
|
@ -3344,7 +3343,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(20)");
|
.HasColumnType("nvarchar(20)");
|
||||||
|
|
||||||
b.Property<string>("MarginJson")
|
b.Property<string>("MarginJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.HasMaxLength(128)
|
.HasMaxLength(128)
|
||||||
|
|
@ -3356,16 +3355,16 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasDefaultValue(50);
|
.HasDefaultValue(50);
|
||||||
|
|
||||||
b.Property<string>("PagerOptionJson")
|
b.Property<string>("PagerOptionJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("PanesJson")
|
b.Property<string>("PanesJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("PermissionJson")
|
b.Property<string>("PermissionJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("PivotOptionJson")
|
b.Property<string>("PivotOptionJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("RoleId")
|
b.Property<string>("RoleId")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
|
|
@ -3378,41 +3377,41 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("ScrollBarJson")
|
b.Property<string>("ScrollBarJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SearchPanelJson")
|
b.Property<string>("SearchPanelJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SelectCommand")
|
b.Property<string>("SelectCommand")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<int>("SelectCommandType")
|
b.Property<int>("SelectCommandType")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("SelectFieldsDefaultValueJson")
|
b.Property<string>("SelectFieldsDefaultValueJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SelectionJson")
|
b.Property<string>("SelectionJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SeriesJson")
|
b.Property<string>("SeriesJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<bool>("ShowNote")
|
b.Property<bool>("ShowNote")
|
||||||
.HasColumnType("bit");
|
.HasColumnType("bit");
|
||||||
|
|
||||||
b.Property<string>("SizeJson")
|
b.Property<string>("SizeJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SortMode")
|
b.Property<string>("SortMode")
|
||||||
.HasMaxLength(20)
|
.HasMaxLength(20)
|
||||||
.HasColumnType("nvarchar(20)");
|
.HasColumnType("nvarchar(20)");
|
||||||
|
|
||||||
b.Property<string>("StateStoringJson")
|
b.Property<string>("StateStoringJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SubFormsJson")
|
b.Property<string>("SubFormsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SubFormsListFormType")
|
b.Property<string>("SubFormsListFormType")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
@ -3426,13 +3425,13 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
b.Property<string>("TitleJson")
|
b.Property<string>("TitleJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("TodoOptionJson")
|
b.Property<string>("TodoOptionJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("TooltipJson")
|
b.Property<string>("TooltipJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("TreeOptionJson")
|
b.Property<string>("TreeOptionJson")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
@ -3444,10 +3443,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("UpdateCommand")
|
b.Property<string>("UpdateCommand")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("UpdateFieldsDefaultValueJson")
|
b.Property<string>("UpdateFieldsDefaultValueJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("UpdateServiceAddress")
|
b.Property<string>("UpdateServiceAddress")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
|
|
@ -3458,19 +3457,19 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
b.Property<string>("ValueAxisJson")
|
b.Property<string>("ValueAxisJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("WidgetsJson")
|
b.Property<string>("WidgetsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<int?>("Width")
|
b.Property<int?>("Width")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("WorkflowJson")
|
b.Property<string>("WorkflowJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ZoomAndPanJson")
|
b.Property<string>("ZoomAndPanJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
|
@ -3494,7 +3493,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("CreatorId");
|
.HasColumnName("CreatorId");
|
||||||
|
|
||||||
b.Property<string>("CustomizationData")
|
b.Property<string>("CustomizationData")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<int>("CustomizationType")
|
b.Property<int>("CustomizationType")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
@ -3591,16 +3590,16 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
b.Property<string>("ColumnCustomizationJson")
|
b.Property<string>("ColumnCustomizationJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ColumnFilterJson")
|
b.Property<string>("ColumnFilterJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ColumnHeaderJson")
|
b.Property<string>("ColumnHeaderJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ColumnStylingJson")
|
b.Property<string>("ColumnStylingJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CultureName")
|
b.Property<string>("CultureName")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
|
@ -3614,10 +3613,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("EditorOptions")
|
b.Property<string>("EditorOptions")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("EditorScript")
|
b.Property<string>("EditorScript")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("EditorType2")
|
b.Property<string>("EditorType2")
|
||||||
.HasMaxLength(64)
|
.HasMaxLength(64)
|
||||||
|
|
@ -3633,10 +3632,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(128)");
|
.HasColumnType("nvarchar(128)");
|
||||||
|
|
||||||
b.Property<string>("GroupSummaryJson")
|
b.Property<string>("GroupSummaryJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("GroupingJson")
|
b.Property<string>("GroupingJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<bool?>("IsActive")
|
b.Property<bool?>("IsActive")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
|
|
@ -3644,7 +3643,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasDefaultValue(true);
|
.HasDefaultValue(true);
|
||||||
|
|
||||||
b.Property<string>("JoinTableJson")
|
b.Property<string>("JoinTableJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ListFormCode")
|
b.Property<string>("ListFormCode")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
|
@ -3657,13 +3656,13 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasDefaultValue(30);
|
.HasDefaultValue(30);
|
||||||
|
|
||||||
b.Property<string>("LookupJson")
|
b.Property<string>("LookupJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("PermissionJson")
|
b.Property<string>("PermissionJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("PivotSettingsJson")
|
b.Property<string>("PivotSettingsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("PlaceHolder")
|
b.Property<string>("PlaceHolder")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
|
|
@ -3684,14 +3683,14 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("TotalSummaryJson")
|
b.Property<string>("TotalSummaryJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("UserId")
|
b.Property<string>("UserId")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
b.Property<string>("ValidationRuleJson")
|
b.Property<string>("ValidationRuleJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<bool?>("Visible")
|
b.Property<bool?>("Visible")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
|
|
@ -3801,7 +3800,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("ErrorsJson")
|
b.Property<string>("ErrorsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<int>("ExecRows")
|
b.Property<int>("ExecRows")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
@ -3863,7 +3862,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(20)");
|
.HasColumnType("nvarchar(20)");
|
||||||
|
|
||||||
b.Property<string>("CompareOutcomesJson")
|
b.Property<string>("CompareOutcomesJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<decimal>("CompareValue")
|
b.Property<decimal>("CompareValue")
|
||||||
.HasPrecision(18, 2)
|
.HasPrecision(18, 2)
|
||||||
|
|
@ -4095,7 +4094,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.Property<string>("ParticipantIdsJson")
|
b.Property<string>("ParticipantIdsJson")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ParticipantKey")
|
b.Property<string>("ParticipantKey")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
|
@ -4122,7 +4121,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.Property<string>("AttachmentsJson")
|
b.Property<string>("AttachmentsJson")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<Guid>("ConversationId")
|
b.Property<Guid>("ConversationId")
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
@ -4159,7 +4158,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.Property<string>("RecipientIdsJson")
|
b.Property<string>("RecipientIdsJson")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<Guid>("SenderId")
|
b.Property<Guid>("SenderId")
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
@ -489,12 +489,12 @@ namespace Sozsoft.Platform.Migrations
|
||||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
UserName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
UserName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||||
Text = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
Text = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
||||||
Description = table.Column<string>(type: "text", nullable: true),
|
Description = table.Column<string>(type: "ntext", nullable: true),
|
||||||
StartDate = table.Column<DateTime>(type: "datetime2", nullable: false),
|
StartDate = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
EndDate = table.Column<DateTime>(type: "datetime2", nullable: false),
|
EndDate = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
AllDay = table.Column<bool>(type: "bit", nullable: true, defaultValue: false),
|
AllDay = table.Column<bool>(type: "bit", nullable: true, defaultValue: false),
|
||||||
RecurrenceRule = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
RecurrenceRule = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
||||||
RecurrenceException = table.Column<string>(type: "text", nullable: true),
|
RecurrenceException = table.Column<string>(type: "ntext", nullable: true),
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
|
@ -517,7 +517,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
Title = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
Title = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||||
Excerpt = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
Excerpt = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
||||||
Content = table.Column<string>(type: "nvarchar(max)", maxLength: 4096, nullable: false),
|
Content = table.Column<string>(type: "nvarchar(max)", maxLength: 4096, nullable: false),
|
||||||
ImageUrl = table.Column<string>(type: "text", nullable: true),
|
ImageUrl = table.Column<string>(type: "ntext", nullable: true),
|
||||||
Category = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
Category = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||||
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
UserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
PublishDate = table.Column<DateTime>(type: "datetime2", nullable: false),
|
PublishDate = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
|
@ -630,7 +630,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
Title = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
Title = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
ParticipantKey = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
ParticipantKey = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
||||||
ParticipantIdsJson = table.Column<string>(type: "text", nullable: false),
|
ParticipantIdsJson = table.Column<string>(type: "ntext", nullable: false),
|
||||||
IsGroup = table.Column<bool>(type: "bit", nullable: false),
|
IsGroup = table.Column<bool>(type: "bit", nullable: false),
|
||||||
LastSenderId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
LastSenderId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
LastMessagePreview = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
LastMessagePreview = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
||||||
|
|
@ -926,9 +926,9 @@ namespace Sozsoft.Platform.Migrations
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
StatsJson = table.Column<string>(type: "text", nullable: true),
|
StatsJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
DescriptionsJson = table.Column<string>(type: "text", nullable: true),
|
DescriptionsJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
SectionsJson = table.Column<string>(type: "text", nullable: true),
|
SectionsJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
|
@ -950,8 +950,8 @@ namespace Sozsoft.Platform.Migrations
|
||||||
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
||||||
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
||||||
ApiUrl = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
ApiUrl = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
Description = table.Column<string>(type: "text", nullable: true),
|
Description = table.Column<string>(type: "ntext", nullable: true),
|
||||||
Tenants = table.Column<string>(type: "text", nullable: true)
|
Tenants = table.Column<string>(type: "ntext", nullable: true)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
|
|
@ -970,7 +970,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
DataSourceCode = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
DataSourceCode = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||||
BeforeSp = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
BeforeSp = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||||
AfterSp = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
AfterSp = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||||
Options = table.Column<string>(type: "text", nullable: true),
|
Options = table.Column<string>(type: "ntext", nullable: true),
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
|
@ -1045,9 +1045,9 @@ namespace Sozsoft.Platform.Migrations
|
||||||
Email = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
Email = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||||
Location = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
Location = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
TaxNumber = table.Column<long>(type: "bigint", nullable: true),
|
TaxNumber = table.Column<long>(type: "bigint", nullable: true),
|
||||||
BankJson = table.Column<string>(type: "text", nullable: true),
|
BankJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
WorkHoursJson = table.Column<string>(type: "text", nullable: true),
|
WorkHoursJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
MapJson = table.Column<string>(type: "text", nullable: true),
|
MapJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
|
@ -1158,7 +1158,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
||||||
RoutePath = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
RoutePath = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
||||||
Code = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
Code = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||||
Props = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: true),
|
Props = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
Description = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
Description = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
||||||
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
||||||
Dependencies = table.Column<string>(type: "nvarchar(2048)", maxLength: 2048, nullable: true),
|
Dependencies = table.Column<string>(type: "nvarchar(2048)", maxLength: 2048, nullable: true),
|
||||||
|
|
@ -1187,8 +1187,8 @@ namespace Sozsoft.Platform.Migrations
|
||||||
Method = table.Column<string>(type: "nvarchar(10)", maxLength: 10, nullable: false),
|
Method = table.Column<string>(type: "nvarchar(10)", maxLength: 10, nullable: false),
|
||||||
DataSourceCode = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
DataSourceCode = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
||||||
Sql = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
Sql = table.Column<string>(type: "nvarchar(max)", nullable: false),
|
||||||
ParametersJson = table.Column<string>(type: "text", nullable: true),
|
ParametersJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
PermissionsJson = table.Column<string>(type: "text", nullable: true),
|
PermissionsJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
|
@ -1237,7 +1237,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
ResourceName = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
ResourceName = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||||
MaxForms = table.Column<int>(type: "int", nullable: false, defaultValue: 500),
|
MaxForms = table.Column<int>(type: "int", nullable: false, defaultValue: 500),
|
||||||
CacheKey = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
CacheKey = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
||||||
JsonData = table.Column<string>(type: "text", nullable: false),
|
JsonData = table.Column<string>(type: "ntext", nullable: false),
|
||||||
FormCount = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
|
FormCount = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
|
||||||
FieldCount = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
|
FieldCount = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
|
||||||
JsonLength = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
|
JsonLength = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
|
||||||
|
|
@ -1287,7 +1287,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
RequestDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
RequestDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
Title = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
Title = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: false),
|
||||||
Status = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
Status = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false),
|
||||||
Description = table.Column<string>(type: "text", nullable: true),
|
Description = table.Column<string>(type: "ntext", nullable: true),
|
||||||
DueDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
DueDate = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
Tags = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
Tags = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
Assignees = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
Assignees = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
||||||
|
|
@ -1316,10 +1316,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||||
DisplayName = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
DisplayName = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
||||||
Description = table.Column<string>(type: "nvarchar(2048)", maxLength: 2048, nullable: true),
|
Description = table.Column<string>(type: "nvarchar(2048)", maxLength: 2048, nullable: true),
|
||||||
Code = table.Column<string>(type: "text", nullable: false),
|
Code = table.Column<string>(type: "ntext", nullable: false),
|
||||||
IsActive = table.Column<bool>(type: "bit", nullable: false, defaultValue: true),
|
IsActive = table.Column<bool>(type: "bit", nullable: false, defaultValue: true),
|
||||||
CompilationStatus = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false),
|
CompilationStatus = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false),
|
||||||
LastCompilationError = table.Column<string>(type: "text", nullable: true),
|
LastCompilationError = table.Column<string>(type: "ntext", nullable: true),
|
||||||
LastSuccessfulCompilation = table.Column<DateTime>(type: "datetime2", nullable: true),
|
LastSuccessfulCompilation = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
Version = table.Column<int>(type: "int", nullable: false, defaultValue: 1),
|
Version = table.Column<int>(type: "int", nullable: false, defaultValue: 1),
|
||||||
CodeHash = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
CodeHash = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||||
|
|
@ -1353,9 +1353,9 @@ namespace Sozsoft.Platform.Migrations
|
||||||
CtaTitleKey = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
CtaTitleKey = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
CtaSubtitleKey = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
CtaSubtitleKey = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
CtaButtonLabelKey = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
CtaButtonLabelKey = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
SlidesJson = table.Column<string>(type: "text", nullable: true),
|
SlidesJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
FeaturesJson = table.Column<string>(type: "text", nullable: true),
|
FeaturesJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
SolutionsJson = table.Column<string>(type: "text", nullable: true),
|
SolutionsJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
|
@ -1432,10 +1432,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
DataSourceCode = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
DataSourceCode = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||||
SelectCommandType = table.Column<int>(type: "int", nullable: false),
|
SelectCommandType = table.Column<int>(type: "int", nullable: false),
|
||||||
TableName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
TableName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||||
SelectCommand = table.Column<string>(type: "text", nullable: true),
|
SelectCommand = table.Column<string>(type: "ntext", nullable: true),
|
||||||
KeyFieldName = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
KeyFieldName = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||||
KeyFieldDbSourceType = table.Column<int>(type: "int", nullable: false),
|
KeyFieldDbSourceType = table.Column<int>(type: "int", nullable: false),
|
||||||
SelectFieldsDefaultValueJson = table.Column<string>(type: "text", nullable: true),
|
SelectFieldsDefaultValueJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
Name = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||||
Description = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
Description = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
|
||||||
Title = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
Title = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
|
|
@ -1445,43 +1445,43 @@ namespace Sozsoft.Platform.Migrations
|
||||||
Height = table.Column<int>(type: "int", nullable: true),
|
Height = table.Column<int>(type: "int", nullable: true),
|
||||||
FullHeight = table.Column<bool>(type: "bit", nullable: false),
|
FullHeight = table.Column<bool>(type: "bit", nullable: false),
|
||||||
DefaultFilter = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
DefaultFilter = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
ColumnOptionJson = table.Column<string>(type: "text", nullable: true),
|
ColumnOptionJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
PivotOptionJson = table.Column<string>(type: "text", nullable: true),
|
PivotOptionJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
TreeOptionJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
TreeOptionJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
GanttOptionJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
GanttOptionJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
SchedulerOptionJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
SchedulerOptionJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
TodoOptionJson = table.Column<string>(type: "text", nullable: true),
|
TodoOptionJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
FilterRowJson = table.Column<string>(type: "text", nullable: true),
|
FilterRowJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
RowJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
RowJson = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
HeaderFilterJson = table.Column<string>(type: "text", nullable: true),
|
HeaderFilterJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
FilterPanelJson = table.Column<string>(type: "text", nullable: true),
|
FilterPanelJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
SearchPanelJson = table.Column<string>(type: "text", nullable: true),
|
SearchPanelJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
GroupPanelJson = table.Column<string>(type: "text", nullable: true),
|
GroupPanelJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
SelectionJson = table.Column<string>(type: "text", nullable: true),
|
SelectionJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
PagerOptionJson = table.Column<string>(type: "text", nullable: true),
|
PagerOptionJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
EditingOptionJson = table.Column<string>(type: "text", nullable: true),
|
EditingOptionJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
EditingFormJson = table.Column<string>(type: "text", nullable: true),
|
EditingFormJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
PermissionJson = table.Column<string>(type: "text", nullable: true),
|
PermissionJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
InsertFieldsDefaultValueJson = table.Column<string>(type: "text", nullable: true),
|
InsertFieldsDefaultValueJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
UpdateFieldsDefaultValueJson = table.Column<string>(type: "text", nullable: true),
|
UpdateFieldsDefaultValueJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
DeleteFieldsDefaultValueJson = table.Column<string>(type: "text", nullable: true),
|
DeleteFieldsDefaultValueJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
CommandColumnJson = table.Column<string>(type: "text", nullable: true),
|
CommandColumnJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
StateStoringJson = table.Column<string>(type: "text", nullable: true),
|
StateStoringJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
DeleteCommand = table.Column<string>(type: "text", nullable: true),
|
DeleteCommand = table.Column<string>(type: "ntext", nullable: true),
|
||||||
DeleteBeforeCommand = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
DeleteBeforeCommand = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
DeleteAfterCommand = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
DeleteAfterCommand = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
UpdateCommand = table.Column<string>(type: "text", nullable: true),
|
UpdateCommand = table.Column<string>(type: "ntext", nullable: true),
|
||||||
UpdateBeforeCommand = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
UpdateBeforeCommand = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
UpdateAfterCommand = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
UpdateAfterCommand = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
InsertCommand = table.Column<string>(type: "text", nullable: true),
|
InsertCommand = table.Column<string>(type: "ntext", nullable: true),
|
||||||
InsertBeforeCommand = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
InsertBeforeCommand = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
InsertAfterCommand = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
InsertAfterCommand = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
UpdateServiceAddress = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
UpdateServiceAddress = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
InsertServiceAddress = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
InsertServiceAddress = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
DeleteServiceAddress = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
DeleteServiceAddress = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
CustomJsSourcesJson = table.Column<string>(type: "text", nullable: true),
|
CustomJsSourcesJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
CustomStyleSourcesJson = table.Column<string>(type: "text", nullable: true),
|
CustomStyleSourcesJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
FormFieldsDefaultValueJson = table.Column<string>(type: "text", nullable: true),
|
FormFieldsDefaultValueJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
IsTenant = table.Column<bool>(type: "bit", nullable: false),
|
IsTenant = table.Column<bool>(type: "bit", nullable: false),
|
||||||
IsBranch = table.Column<bool>(type: "bit", nullable: false),
|
IsBranch = table.Column<bool>(type: "bit", nullable: false),
|
||||||
IsOrganizationUnit = table.Column<bool>(type: "bit", nullable: false),
|
IsOrganizationUnit = table.Column<bool>(type: "bit", nullable: false),
|
||||||
|
|
@ -1489,35 +1489,35 @@ namespace Sozsoft.Platform.Migrations
|
||||||
IsSubForm = table.Column<bool>(type: "bit", nullable: false),
|
IsSubForm = table.Column<bool>(type: "bit", nullable: false),
|
||||||
SubFormsListFormType = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
SubFormsListFormType = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
ShowNote = table.Column<bool>(type: "bit", nullable: false),
|
ShowNote = table.Column<bool>(type: "bit", nullable: false),
|
||||||
SubFormsJson = table.Column<string>(type: "text", nullable: true),
|
SubFormsJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
WidgetsJson = table.Column<string>(type: "text", nullable: true),
|
WidgetsJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
WorkflowJson = table.Column<string>(type: "text", nullable: true),
|
WorkflowJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
ExtraFilterJson = table.Column<string>(type: "text", nullable: true),
|
ExtraFilterJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
LayoutJson = table.Column<string>(type: "text", nullable: true),
|
LayoutJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
UserId = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
UserId = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
RoleId = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
RoleId = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
CommonJson = table.Column<string>(type: "text", nullable: true),
|
CommonJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
DataSourceJson = table.Column<string>(type: "text", nullable: true),
|
DataSourceJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
AdaptiveLayoutJson = table.Column<string>(type: "text", nullable: true),
|
AdaptiveLayoutJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
AnimationJson = table.Column<string>(type: "text", nullable: true),
|
AnimationJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
AnnotationsJson = table.Column<string>(type: "text", nullable: true),
|
AnnotationsJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
ArgumentAxisJson = table.Column<string>(type: "text", nullable: true),
|
ArgumentAxisJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
CommonAnnotationsSettingsJson = table.Column<string>(type: "text", nullable: true),
|
CommonAnnotationsSettingsJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
CommonAxisSettingsJson = table.Column<string>(type: "text", nullable: true),
|
CommonAxisSettingsJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
CommonPaneSettingsJson = table.Column<string>(type: "text", nullable: true),
|
CommonPaneSettingsJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
CommonSeriesSettingsJson = table.Column<string>(type: "text", nullable: true),
|
CommonSeriesSettingsJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
CrosshairJson = table.Column<string>(type: "text", nullable: true),
|
CrosshairJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
ExportJson = table.Column<string>(type: "text", nullable: true),
|
ExportJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
LegendJson = table.Column<string>(type: "text", nullable: true),
|
LegendJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
MarginJson = table.Column<string>(type: "text", nullable: true),
|
MarginJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
PanesJson = table.Column<string>(type: "text", nullable: true),
|
PanesJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
ScrollBarJson = table.Column<string>(type: "text", nullable: true),
|
ScrollBarJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
SeriesJson = table.Column<string>(type: "text", nullable: true),
|
SeriesJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
SizeJson = table.Column<string>(type: "text", nullable: true),
|
SizeJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
TitleJson = table.Column<string>(type: "text", nullable: true),
|
TitleJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
TooltipJson = table.Column<string>(type: "text", nullable: true),
|
TooltipJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
ValueAxisJson = table.Column<string>(type: "text", nullable: true),
|
ValueAxisJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
ZoomAndPanJson = table.Column<string>(type: "text", nullable: true)
|
ZoomAndPanJson = table.Column<string>(type: "ntext", nullable: true)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
|
|
@ -2250,7 +2250,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
ParticipantsCount = table.Column<int>(type: "int", nullable: false),
|
ParticipantsCount = table.Column<int>(type: "int", nullable: false),
|
||||||
Likes = table.Column<int>(type: "int", nullable: false),
|
Likes = table.Column<int>(type: "int", nullable: false),
|
||||||
isPublished = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
isPublished = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
|
||||||
Photos = table.Column<string>(type: "text", nullable: true),
|
Photos = table.Column<string>(type: "ntext", nullable: true),
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
|
@ -2286,9 +2286,9 @@ namespace Sozsoft.Platform.Migrations
|
||||||
SenderId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
SenderId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||||
SenderUserName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
SenderUserName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||||
SenderName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
SenderName = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
|
||||||
RecipientIdsJson = table.Column<string>(type: "text", nullable: false),
|
RecipientIdsJson = table.Column<string>(type: "ntext", nullable: false),
|
||||||
Text = table.Column<string>(type: "nvarchar(max)", maxLength: 4096, nullable: true),
|
Text = table.Column<string>(type: "nvarchar(max)", maxLength: 4096, nullable: true),
|
||||||
AttachmentsJson = table.Column<string>(type: "text", nullable: false),
|
AttachmentsJson = table.Column<string>(type: "ntext", nullable: false),
|
||||||
SentAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
SentAt = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
|
@ -2789,7 +2789,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
UserId = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
UserId = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
RoleId = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
RoleId = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
FilterName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
FilterName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||||
CustomizationData = table.Column<string>(type: "text", nullable: true),
|
CustomizationData = table.Column<string>(type: "ntext", nullable: true),
|
||||||
CustomizationType = table.Column<int>(type: "int", nullable: false),
|
CustomizationType = table.Column<int>(type: "int", nullable: false),
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
|
|
@ -2833,28 +2833,28 @@ namespace Sozsoft.Platform.Migrations
|
||||||
AllowEditing = table.Column<bool>(type: "bit", nullable: true, defaultValue: true),
|
AllowEditing = table.Column<bool>(type: "bit", nullable: true, defaultValue: true),
|
||||||
AllowAdding = table.Column<bool>(type: "bit", nullable: true, defaultValue: true),
|
AllowAdding = table.Column<bool>(type: "bit", nullable: true, defaultValue: true),
|
||||||
BandName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
BandName = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||||
ColumnFilterJson = table.Column<string>(type: "text", nullable: true),
|
ColumnFilterJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
ColumnHeaderJson = table.Column<string>(type: "text", nullable: true),
|
ColumnHeaderJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
GroupingJson = table.Column<string>(type: "text", nullable: true),
|
GroupingJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
ColumnCustomizationJson = table.Column<string>(type: "text", nullable: true),
|
ColumnCustomizationJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
TotalSummaryJson = table.Column<string>(type: "text", nullable: true),
|
TotalSummaryJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
GroupSummaryJson = table.Column<string>(type: "text", nullable: true),
|
GroupSummaryJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
ColumnCssClass = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
ColumnCssClass = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||||
ColumnCssValue = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
ColumnCssValue = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
|
||||||
JoinTableJson = table.Column<string>(type: "text", nullable: true),
|
JoinTableJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
LookupJson = table.Column<string>(type: "text", nullable: true),
|
LookupJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
ValidationRuleJson = table.Column<string>(type: "text", nullable: true),
|
ValidationRuleJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
ColumnStylingJson = table.Column<string>(type: "text", nullable: true),
|
ColumnStylingJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
PermissionJson = table.Column<string>(type: "text", nullable: true),
|
PermissionJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
PivotSettingsJson = table.Column<string>(type: "text", nullable: true),
|
PivotSettingsJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
Alignment = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true, defaultValue: "left"),
|
Alignment = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true, defaultValue: "left"),
|
||||||
Format = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
Format = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: true),
|
||||||
EditOrderNo = table.Column<int>(type: "int", nullable: true),
|
EditOrderNo = table.Column<int>(type: "int", nullable: true),
|
||||||
EditGroupOrderNo = table.Column<int>(type: "int", nullable: true),
|
EditGroupOrderNo = table.Column<int>(type: "int", nullable: true),
|
||||||
EditorType2 = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
EditorType2 = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: true),
|
||||||
ColSpan = table.Column<int>(type: "int", nullable: true),
|
ColSpan = table.Column<int>(type: "int", nullable: true),
|
||||||
EditorScript = table.Column<string>(type: "text", nullable: true),
|
EditorScript = table.Column<string>(type: "ntext", nullable: true),
|
||||||
EditorOptions = table.Column<string>(type: "text", nullable: true)
|
EditorOptions = table.Column<string>(type: "ntext", nullable: true)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
|
|
@ -2914,7 +2914,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
NextOnReject = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
NextOnReject = table.Column<string>(type: "nvarchar(128)", maxLength: 128, nullable: false),
|
||||||
PositionX = table.Column<int>(type: "int", nullable: false),
|
PositionX = table.Column<int>(type: "int", nullable: false),
|
||||||
PositionY = table.Column<int>(type: "int", nullable: false),
|
PositionY = table.Column<int>(type: "int", nullable: false),
|
||||||
CompareOutcomesJson = table.Column<string>(type: "text", nullable: true)
|
CompareOutcomesJson = table.Column<string>(type: "ntext", nullable: true)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
|
|
@ -3392,7 +3392,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
ValidRows = table.Column<int>(type: "int", nullable: false),
|
ValidRows = table.Column<int>(type: "int", nullable: false),
|
||||||
ErrorRows = table.Column<int>(type: "int", nullable: false),
|
ErrorRows = table.Column<int>(type: "int", nullable: false),
|
||||||
Progress = table.Column<double>(type: "float", nullable: false),
|
Progress = table.Column<double>(type: "float", nullable: false),
|
||||||
ErrorsJson = table.Column<string>(type: "text", nullable: true),
|
ErrorsJson = table.Column<string>(type: "ntext", nullable: true),
|
||||||
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
CreationTime = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
CreatorId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||||
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
LastModificationTime = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
|
@ -649,7 +649,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("DeletionTime");
|
.HasColumnName("DeletionTime");
|
||||||
|
|
||||||
b.Property<string>("DescriptionsJson")
|
b.Property<string>("DescriptionsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<bool>("IsDeleted")
|
b.Property<bool>("IsDeleted")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
|
|
@ -666,10 +666,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("LastModifierId");
|
.HasColumnName("LastModifierId");
|
||||||
|
|
||||||
b.Property<string>("SectionsJson")
|
b.Property<string>("SectionsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("StatsJson")
|
b.Property<string>("StatsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
|
@ -703,7 +703,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("DeletionTime");
|
.HasColumnName("DeletionTime");
|
||||||
|
|
||||||
b.Property<string>("Description")
|
b.Property<string>("Description")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<DateTime>("EndDate")
|
b.Property<DateTime>("EndDate")
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
@ -723,7 +723,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("LastModifierId");
|
.HasColumnName("LastModifierId");
|
||||||
|
|
||||||
b.Property<string>("RecurrenceException")
|
b.Property<string>("RecurrenceException")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("RecurrenceRule")
|
b.Property<string>("RecurrenceRule")
|
||||||
.HasMaxLength(512)
|
.HasMaxLength(512)
|
||||||
|
|
@ -761,7 +761,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
b.Property<string>("Description")
|
b.Property<string>("Description")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<bool>("IsActive")
|
b.Property<bool>("IsActive")
|
||||||
.HasColumnType("bit");
|
.HasColumnType("bit");
|
||||||
|
|
@ -772,7 +772,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(128)");
|
.HasColumnType("nvarchar(128)");
|
||||||
|
|
||||||
b.Property<string>("Tenants")
|
b.Property<string>("Tenants")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
|
@ -826,7 +826,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
b.Property<string>("ImageUrl")
|
b.Property<string>("ImageUrl")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<bool>("IsDeleted")
|
b.Property<bool>("IsDeleted")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
|
|
@ -1056,7 +1056,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(128)");
|
.HasColumnType("nvarchar(128)");
|
||||||
|
|
||||||
b.Property<string>("Options")
|
b.Property<string>("Options")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<int>("WorkerType")
|
b.Property<int>("WorkerType")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
@ -1450,7 +1450,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(512)");
|
.HasColumnType("nvarchar(512)");
|
||||||
|
|
||||||
b.Property<string>("BankJson")
|
b.Property<string>("BankJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<DateTime>("CreationTime")
|
b.Property<DateTime>("CreationTime")
|
||||||
.HasColumnType("datetime2")
|
.HasColumnType("datetime2")
|
||||||
|
|
@ -1491,7 +1491,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
b.Property<string>("MapJson")
|
b.Property<string>("MapJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("PhoneNumber")
|
b.Property<string>("PhoneNumber")
|
||||||
.HasMaxLength(20)
|
.HasMaxLength(20)
|
||||||
|
|
@ -1501,7 +1501,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<string>("WorkHoursJson")
|
b.Property<string>("WorkHoursJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
|
@ -1895,8 +1895,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(128)");
|
.HasColumnType("nvarchar(128)");
|
||||||
|
|
||||||
b.Property<string>("Props")
|
b.Property<string>("Props")
|
||||||
.HasMaxLength(1024)
|
.HasColumnType("nvarchar(max)");
|
||||||
.HasColumnType("nvarchar(1024)");
|
|
||||||
|
|
||||||
b.Property<string>("RoutePath")
|
b.Property<string>("RoutePath")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
|
@ -1975,10 +1974,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(128)");
|
.HasColumnType("nvarchar(128)");
|
||||||
|
|
||||||
b.Property<string>("ParametersJson")
|
b.Property<string>("ParametersJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("PermissionsJson")
|
b.Property<string>("PermissionsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("Sql")
|
b.Property<string>("Sql")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
|
@ -2100,7 +2099,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.Property<string>("JsonData")
|
b.Property<string>("JsonData")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<int>("JsonLength")
|
b.Property<int>("JsonLength")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
|
|
@ -2320,7 +2319,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("DeletionTime");
|
.HasColumnName("DeletionTime");
|
||||||
|
|
||||||
b.Property<string>("Description")
|
b.Property<string>("Description")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<DateTime?>("DueDate")
|
b.Property<DateTime?>("DueDate")
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
@ -2455,7 +2454,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.Property<string>("Code")
|
b.Property<string>("Code")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CodeHash")
|
b.Property<string>("CodeHash")
|
||||||
.HasMaxLength(64)
|
.HasMaxLength(64)
|
||||||
|
|
@ -2506,7 +2505,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("IsDeleted");
|
.HasColumnName("IsDeleted");
|
||||||
|
|
||||||
b.Property<string>("LastCompilationError")
|
b.Property<string>("LastCompilationError")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<DateTime?>("LastModificationTime")
|
b.Property<DateTime?>("LastModificationTime")
|
||||||
.HasColumnType("datetime2")
|
.HasColumnType("datetime2")
|
||||||
|
|
@ -2603,7 +2602,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("Photos")
|
b.Property<string>("Photos")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("Place")
|
b.Property<string>("Place")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
|
|
@ -2934,7 +2933,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("DeletionTime");
|
.HasColumnName("DeletionTime");
|
||||||
|
|
||||||
b.Property<string>("FeaturesJson")
|
b.Property<string>("FeaturesJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("FeaturesSubtitleKey")
|
b.Property<string>("FeaturesSubtitleKey")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
|
|
@ -2971,10 +2970,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("LastModifierId");
|
.HasColumnName("LastModifierId");
|
||||||
|
|
||||||
b.Property<string>("SlidesJson")
|
b.Property<string>("SlidesJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SolutionsJson")
|
b.Property<string>("SolutionsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SolutionsSubtitleKey")
|
b.Property<string>("SolutionsSubtitleKey")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
|
|
@ -3177,40 +3176,40 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
b.Property<string>("AdaptiveLayoutJson")
|
b.Property<string>("AdaptiveLayoutJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("AnimationJson")
|
b.Property<string>("AnimationJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("AnnotationsJson")
|
b.Property<string>("AnnotationsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ArgumentAxisJson")
|
b.Property<string>("ArgumentAxisJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ColumnOptionJson")
|
b.Property<string>("ColumnOptionJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CommandColumnJson")
|
b.Property<string>("CommandColumnJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CommonAnnotationsSettingsJson")
|
b.Property<string>("CommonAnnotationsSettingsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CommonAxisSettingsJson")
|
b.Property<string>("CommonAxisSettingsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CommonJson")
|
b.Property<string>("CommonJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CommonPaneSettingsJson")
|
b.Property<string>("CommonPaneSettingsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CommonSeriesSettingsJson")
|
b.Property<string>("CommonSeriesSettingsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CrosshairJson")
|
b.Property<string>("CrosshairJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CultureName")
|
b.Property<string>("CultureName")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
|
@ -3218,17 +3217,17 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(10)");
|
.HasColumnType("nvarchar(10)");
|
||||||
|
|
||||||
b.Property<string>("CustomJsSourcesJson")
|
b.Property<string>("CustomJsSourcesJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CustomStyleSourcesJson")
|
b.Property<string>("CustomStyleSourcesJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("DataSourceCode")
|
b.Property<string>("DataSourceCode")
|
||||||
.HasMaxLength(128)
|
.HasMaxLength(128)
|
||||||
.HasColumnType("nvarchar(128)");
|
.HasColumnType("nvarchar(128)");
|
||||||
|
|
||||||
b.Property<string>("DataSourceJson")
|
b.Property<string>("DataSourceJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("DefaultFilter")
|
b.Property<string>("DefaultFilter")
|
||||||
.IsUnicode(true)
|
.IsUnicode(true)
|
||||||
|
|
@ -3241,10 +3240,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("DeleteCommand")
|
b.Property<string>("DeleteCommand")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("DeleteFieldsDefaultValueJson")
|
b.Property<string>("DeleteFieldsDefaultValueJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("DeleteServiceAddress")
|
b.Property<string>("DeleteServiceAddress")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
|
|
@ -3255,25 +3254,25 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(512)");
|
.HasColumnType("nvarchar(512)");
|
||||||
|
|
||||||
b.Property<string>("EditingFormJson")
|
b.Property<string>("EditingFormJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("EditingOptionJson")
|
b.Property<string>("EditingOptionJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ExportJson")
|
b.Property<string>("ExportJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ExtraFilterJson")
|
b.Property<string>("ExtraFilterJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("FilterPanelJson")
|
b.Property<string>("FilterPanelJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("FilterRowJson")
|
b.Property<string>("FilterRowJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("FormFieldsDefaultValueJson")
|
b.Property<string>("FormFieldsDefaultValueJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<bool>("FullHeight")
|
b.Property<bool>("FullHeight")
|
||||||
.HasColumnType("bit");
|
.HasColumnType("bit");
|
||||||
|
|
@ -3282,10 +3281,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("GroupPanelJson")
|
b.Property<string>("GroupPanelJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("HeaderFilterJson")
|
b.Property<string>("HeaderFilterJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<int?>("Height")
|
b.Property<int?>("Height")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
@ -3297,10 +3296,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("InsertCommand")
|
b.Property<string>("InsertCommand")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("InsertFieldsDefaultValueJson")
|
b.Property<string>("InsertFieldsDefaultValueJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("InsertServiceAddress")
|
b.Property<string>("InsertServiceAddress")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
|
|
@ -3326,10 +3325,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(64)");
|
.HasColumnType("nvarchar(64)");
|
||||||
|
|
||||||
b.Property<string>("LayoutJson")
|
b.Property<string>("LayoutJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("LegendJson")
|
b.Property<string>("LegendJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ListFormCode")
|
b.Property<string>("ListFormCode")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
|
@ -3341,7 +3340,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(20)");
|
.HasColumnType("nvarchar(20)");
|
||||||
|
|
||||||
b.Property<string>("MarginJson")
|
b.Property<string>("MarginJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.HasMaxLength(128)
|
.HasMaxLength(128)
|
||||||
|
|
@ -3353,16 +3352,16 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasDefaultValue(50);
|
.HasDefaultValue(50);
|
||||||
|
|
||||||
b.Property<string>("PagerOptionJson")
|
b.Property<string>("PagerOptionJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("PanesJson")
|
b.Property<string>("PanesJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("PermissionJson")
|
b.Property<string>("PermissionJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("PivotOptionJson")
|
b.Property<string>("PivotOptionJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("RoleId")
|
b.Property<string>("RoleId")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
|
|
@ -3375,41 +3374,41 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("ScrollBarJson")
|
b.Property<string>("ScrollBarJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SearchPanelJson")
|
b.Property<string>("SearchPanelJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SelectCommand")
|
b.Property<string>("SelectCommand")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<int>("SelectCommandType")
|
b.Property<int>("SelectCommandType")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("SelectFieldsDefaultValueJson")
|
b.Property<string>("SelectFieldsDefaultValueJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SelectionJson")
|
b.Property<string>("SelectionJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SeriesJson")
|
b.Property<string>("SeriesJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<bool>("ShowNote")
|
b.Property<bool>("ShowNote")
|
||||||
.HasColumnType("bit");
|
.HasColumnType("bit");
|
||||||
|
|
||||||
b.Property<string>("SizeJson")
|
b.Property<string>("SizeJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SortMode")
|
b.Property<string>("SortMode")
|
||||||
.HasMaxLength(20)
|
.HasMaxLength(20)
|
||||||
.HasColumnType("nvarchar(20)");
|
.HasColumnType("nvarchar(20)");
|
||||||
|
|
||||||
b.Property<string>("StateStoringJson")
|
b.Property<string>("StateStoringJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SubFormsJson")
|
b.Property<string>("SubFormsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("SubFormsListFormType")
|
b.Property<string>("SubFormsListFormType")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
@ -3423,13 +3422,13 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
b.Property<string>("TitleJson")
|
b.Property<string>("TitleJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("TodoOptionJson")
|
b.Property<string>("TodoOptionJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("TooltipJson")
|
b.Property<string>("TooltipJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("TreeOptionJson")
|
b.Property<string>("TreeOptionJson")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
@ -3441,10 +3440,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("UpdateCommand")
|
b.Property<string>("UpdateCommand")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("UpdateFieldsDefaultValueJson")
|
b.Property<string>("UpdateFieldsDefaultValueJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("UpdateServiceAddress")
|
b.Property<string>("UpdateServiceAddress")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
|
|
@ -3455,19 +3454,19 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
b.Property<string>("ValueAxisJson")
|
b.Property<string>("ValueAxisJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("WidgetsJson")
|
b.Property<string>("WidgetsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<int?>("Width")
|
b.Property<int?>("Width")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("WorkflowJson")
|
b.Property<string>("WorkflowJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ZoomAndPanJson")
|
b.Property<string>("ZoomAndPanJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
|
@ -3491,7 +3490,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnName("CreatorId");
|
.HasColumnName("CreatorId");
|
||||||
|
|
||||||
b.Property<string>("CustomizationData")
|
b.Property<string>("CustomizationData")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<int>("CustomizationType")
|
b.Property<int>("CustomizationType")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
@ -3588,16 +3587,16 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
b.Property<string>("ColumnCustomizationJson")
|
b.Property<string>("ColumnCustomizationJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ColumnFilterJson")
|
b.Property<string>("ColumnFilterJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ColumnHeaderJson")
|
b.Property<string>("ColumnHeaderJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ColumnStylingJson")
|
b.Property<string>("ColumnStylingJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("CultureName")
|
b.Property<string>("CultureName")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
|
@ -3611,10 +3610,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("EditorOptions")
|
b.Property<string>("EditorOptions")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("EditorScript")
|
b.Property<string>("EditorScript")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("EditorType2")
|
b.Property<string>("EditorType2")
|
||||||
.HasMaxLength(64)
|
.HasMaxLength(64)
|
||||||
|
|
@ -3630,10 +3629,10 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(128)");
|
.HasColumnType("nvarchar(128)");
|
||||||
|
|
||||||
b.Property<string>("GroupSummaryJson")
|
b.Property<string>("GroupSummaryJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("GroupingJson")
|
b.Property<string>("GroupingJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<bool?>("IsActive")
|
b.Property<bool?>("IsActive")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
|
|
@ -3641,7 +3640,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasDefaultValue(true);
|
.HasDefaultValue(true);
|
||||||
|
|
||||||
b.Property<string>("JoinTableJson")
|
b.Property<string>("JoinTableJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ListFormCode")
|
b.Property<string>("ListFormCode")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
|
@ -3654,13 +3653,13 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasDefaultValue(30);
|
.HasDefaultValue(30);
|
||||||
|
|
||||||
b.Property<string>("LookupJson")
|
b.Property<string>("LookupJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("PermissionJson")
|
b.Property<string>("PermissionJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("PivotSettingsJson")
|
b.Property<string>("PivotSettingsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("PlaceHolder")
|
b.Property<string>("PlaceHolder")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
|
|
@ -3681,14 +3680,14 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("TotalSummaryJson")
|
b.Property<string>("TotalSummaryJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("UserId")
|
b.Property<string>("UserId")
|
||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
b.Property<string>("ValidationRuleJson")
|
b.Property<string>("ValidationRuleJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<bool?>("Visible")
|
b.Property<bool?>("Visible")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
|
|
@ -3798,7 +3797,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("ErrorsJson")
|
b.Property<string>("ErrorsJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<int>("ExecRows")
|
b.Property<int>("ExecRows")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
@ -3860,7 +3859,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
.HasColumnType("nvarchar(20)");
|
.HasColumnType("nvarchar(20)");
|
||||||
|
|
||||||
b.Property<string>("CompareOutcomesJson")
|
b.Property<string>("CompareOutcomesJson")
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<decimal>("CompareValue")
|
b.Property<decimal>("CompareValue")
|
||||||
.HasPrecision(18, 2)
|
.HasPrecision(18, 2)
|
||||||
|
|
@ -4092,7 +4091,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.Property<string>("ParticipantIdsJson")
|
b.Property<string>("ParticipantIdsJson")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<string>("ParticipantKey")
|
b.Property<string>("ParticipantKey")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
|
@ -4119,7 +4118,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.Property<string>("AttachmentsJson")
|
b.Property<string>("AttachmentsJson")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<Guid>("ConversationId")
|
b.Property<Guid>("ConversationId")
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
@ -4156,7 +4155,7 @@ namespace Sozsoft.Platform.Migrations
|
||||||
|
|
||||||
b.Property<string>("RecipientIdsJson")
|
b.Property<string>("RecipientIdsJson")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("ntext");
|
||||||
|
|
||||||
b.Property<Guid>("SenderId")
|
b.Property<Guid>("SenderId")
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
|
||||||
1
ui/.gitignore
vendored
1
ui/.gitignore
vendored
|
|
@ -26,3 +26,4 @@ build
|
||||||
*.sw?
|
*.sw?
|
||||||
.vite-cache/
|
.vite-cache/
|
||||||
src/devextreme-license.ts
|
src/devextreme-license.ts
|
||||||
|
src/components/visualDesigner/generated/componentProps.json
|
||||||
|
|
|
||||||
14
ui/package-lock.json
generated
14
ui/package-lock.json
generated
|
|
@ -72,9 +72,7 @@
|
||||||
"@typescript-eslint/parser": "^8.62.1",
|
"@typescript-eslint/parser": "^8.62.1",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"baseline-browser-mapping": "^2.9.19",
|
|
||||||
"browserslist": "^4.25.2",
|
"browserslist": "^4.25.2",
|
||||||
"caniuse-lite": "^1.0.30001734",
|
|
||||||
"cssnano": "^6.0.1",
|
"cssnano": "^6.0.1",
|
||||||
"eslint": "^9.39.0",
|
"eslint": "^9.39.0",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
|
@ -5135,9 +5133,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/baseline-browser-mapping": {
|
"node_modules/baseline-browser-mapping": {
|
||||||
"version": "2.10.0",
|
"version": "2.11.12",
|
||||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.12.tgz",
|
||||||
"integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==",
|
"integrity": "sha512-r7WnVImvVCeFpf2DOXfy41aPWzeNg3H/A2X4dKmy1QL0MSyyk/e7z8ihJ3N6Nn2PsdhkVlqnEfnUE4a05P2aTA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
@ -5315,9 +5313,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001774",
|
"version": "1.0.30001806",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz",
|
||||||
"integrity": "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==",
|
"integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,10 @@
|
||||||
"elstarVersion": "2.1.6",
|
"elstarVersion": "2.1.6",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "vite",
|
"generate:component-metadata": "node scripts/generate-component-metadata.mjs",
|
||||||
"build": "node scripts/generate-version.js && vite build",
|
"start": "node scripts/generate-component-metadata.mjs && vite",
|
||||||
"build:production": "vite build",
|
"build": "node scripts/generate-component-metadata.mjs && node scripts/generate-version.js && vite build",
|
||||||
|
"build:production": "node scripts/generate-component-metadata.mjs && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"lint:fix": "npm run lint -- --fix",
|
"lint:fix": "npm run lint -- --fix",
|
||||||
|
|
@ -80,9 +81,7 @@
|
||||||
"@typescript-eslint/parser": "^8.62.1",
|
"@typescript-eslint/parser": "^8.62.1",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"baseline-browser-mapping": "^2.9.19",
|
|
||||||
"browserslist": "^4.25.2",
|
"browserslist": "^4.25.2",
|
||||||
"caniuse-lite": "^1.0.30001734",
|
|
||||||
"cssnano": "^6.0.1",
|
"cssnano": "^6.0.1",
|
||||||
"eslint": "^9.39.0",
|
"eslint": "^9.39.0",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
|
|
||||||
182
ui/scripts/generate-component-metadata.mjs
Normal file
182
ui/scripts/generate-component-metadata.mjs
Normal file
|
|
@ -0,0 +1,182 @@
|
||||||
|
/* global console, process */
|
||||||
|
import fs from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
import prettier from 'prettier'
|
||||||
|
import ts from 'typescript'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
const scriptDirectory = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
const uiRoot = path.resolve(scriptDirectory, '..')
|
||||||
|
const sourceRoot = path.join(uiRoot, 'src', 'components', 'ui')
|
||||||
|
const outputFile = path.join(sourceRoot, '..', 'visualDesigner', 'generated', 'componentProps.json')
|
||||||
|
|
||||||
|
const componentTypeOverrides = {
|
||||||
|
Calendar: 'CalenderProps',
|
||||||
|
ScrollBar: 'ScrollbarProps',
|
||||||
|
}
|
||||||
|
|
||||||
|
const namespaceOptions = {
|
||||||
|
'TypeAttributes.Size': ['lg', 'md', 'sm', 'xs'],
|
||||||
|
'TypeAttributes.ControlSize': ['lg', 'md', 'sm', 'xs'],
|
||||||
|
'TypeAttributes.Shape': ['round', 'circle', 'none'],
|
||||||
|
'TypeAttributes.Status': ['success', 'warning', 'danger', 'info'],
|
||||||
|
'TypeAttributes.FormLayout': ['horizontal', 'vertical', 'inline'],
|
||||||
|
'TypeAttributes.MenuVariant': ['light', 'dark', 'themed', 'transparent'],
|
||||||
|
'TypeAttributes.Direction': ['ltr', 'rtl'],
|
||||||
|
}
|
||||||
|
|
||||||
|
const walkFiles = (directory) =>
|
||||||
|
fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
||||||
|
const target = path.join(directory, entry.name)
|
||||||
|
if (entry.isDirectory()) return walkFiles(target)
|
||||||
|
return /\.(ts|tsx)$/.test(entry.name) ? [target] : []
|
||||||
|
})
|
||||||
|
|
||||||
|
const sourceFiles = walkFiles(sourceRoot)
|
||||||
|
const metadataInputs = [...sourceFiles, fileURLToPath(import.meta.url)]
|
||||||
|
const outputIsCurrent =
|
||||||
|
!process.argv.includes('--force') &&
|
||||||
|
fs.existsSync(outputFile) &&
|
||||||
|
fs.statSync(outputFile).mtimeMs >=
|
||||||
|
Math.max(...metadataInputs.map((fileName) => fs.statSync(fileName).mtimeMs))
|
||||||
|
|
||||||
|
if (outputIsCurrent) {
|
||||||
|
console.log('Designer component metadata is up to date.')
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const program = ts.createProgram(sourceFiles, {
|
||||||
|
target: ts.ScriptTarget.Latest,
|
||||||
|
jsx: ts.JsxEmit.ReactJSX,
|
||||||
|
})
|
||||||
|
|
||||||
|
const interfaces = new Map()
|
||||||
|
const defaultsByFile = new Map()
|
||||||
|
|
||||||
|
const literalValue = (node, sourceFile) => {
|
||||||
|
if (!node) return undefined
|
||||||
|
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text
|
||||||
|
if (ts.isNumericLiteral(node)) return Number(node.text)
|
||||||
|
if (node.kind === ts.SyntaxKind.TrueKeyword) return true
|
||||||
|
if (node.kind === ts.SyntaxKind.FalseKeyword) return false
|
||||||
|
if (node.kind === ts.SyntaxKind.NullKeyword) return null
|
||||||
|
if (ts.isArrayLiteralExpression(node)) {
|
||||||
|
const values = node.elements.map((element) => literalValue(element, sourceFile))
|
||||||
|
return values.some((value) => value === undefined) ? undefined : values
|
||||||
|
}
|
||||||
|
if (ts.isObjectLiteralExpression(node)) {
|
||||||
|
const result = {}
|
||||||
|
for (const property of node.properties) {
|
||||||
|
if (!ts.isPropertyAssignment(property)) return undefined
|
||||||
|
const name = property.name.getText(sourceFile).replace(/^['"]|['"]$/g, '')
|
||||||
|
const value = literalValue(property.initializer, sourceFile)
|
||||||
|
if (value === undefined) return undefined
|
||||||
|
result[name] = value
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const sourceFile of program.getSourceFiles()) {
|
||||||
|
if (!path.resolve(sourceFile.fileName).startsWith(path.resolve(sourceRoot))) continue
|
||||||
|
const fileDefaults = {}
|
||||||
|
|
||||||
|
const visit = (node) => {
|
||||||
|
if (ts.isInterfaceDeclaration(node) && node.name.text.endsWith('Props')) {
|
||||||
|
interfaces.set(node.name.text, { node, sourceFile })
|
||||||
|
}
|
||||||
|
if (ts.isVariableDeclaration(node) && ts.isObjectBindingPattern(node.name)) {
|
||||||
|
for (const element of node.name.elements) {
|
||||||
|
if (!ts.isIdentifier(element.name) || !element.initializer) continue
|
||||||
|
const value = literalValue(element.initializer, sourceFile)
|
||||||
|
if (value !== undefined) fileDefaults[element.name.text] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ts.forEachChild(node, visit)
|
||||||
|
}
|
||||||
|
visit(sourceFile)
|
||||||
|
defaultsByFile.set(sourceFile.fileName, fileDefaults)
|
||||||
|
}
|
||||||
|
|
||||||
|
const indexSource = program.getSourceFile(path.join(sourceRoot, 'index.ts'))
|
||||||
|
if (!indexSource) throw new Error('UI component index could not be found.')
|
||||||
|
|
||||||
|
const componentNames = []
|
||||||
|
for (const statement of indexSource.statements) {
|
||||||
|
if (!ts.isExportDeclaration(statement) || statement.isTypeOnly || !statement.exportClause)
|
||||||
|
continue
|
||||||
|
if (!ts.isNamedExports(statement.exportClause)) continue
|
||||||
|
for (const element of statement.exportClause.elements) componentNames.push(element.name.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
const unionOptions = (typeNode, sourceFile) => {
|
||||||
|
if (!typeNode) return []
|
||||||
|
const typeText = typeNode.getText(sourceFile)
|
||||||
|
if (namespaceOptions[typeText]) return namespaceOptions[typeText]
|
||||||
|
if (!ts.isUnionTypeNode(typeNode)) return []
|
||||||
|
return typeNode.types
|
||||||
|
.filter((type) => ts.isLiteralTypeNode(type) && ts.isStringLiteral(type.literal))
|
||||||
|
.map((type) => type.literal.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
const propertyType = (typeNode, sourceFile) => {
|
||||||
|
const text = typeNode?.getText(sourceFile) || 'unknown'
|
||||||
|
const options = unionOptions(typeNode, sourceFile)
|
||||||
|
if (options.length) return { type: 'select', options }
|
||||||
|
if (text.includes('=>') || text.startsWith('MouseEventHandler')) return { type: 'function' }
|
||||||
|
if (/\bboolean\b/.test(text)) return { type: 'boolean' }
|
||||||
|
if (/\bnumber\b/.test(text)) return { type: 'number' }
|
||||||
|
if (/\[\]|Array<|ReadonlyArray</.test(text)) return { type: 'array' }
|
||||||
|
if (/CSSProperties|Record<|object/.test(text)) return { type: 'object' }
|
||||||
|
return { type: 'string' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const commonProperties = [
|
||||||
|
{ name: 'className', tsType: 'string', type: 'string', required: false },
|
||||||
|
{ name: 'children', tsType: 'ReactNode', type: 'string', required: false },
|
||||||
|
{ name: 'style', tsType: 'CSSProperties', type: 'object', required: false },
|
||||||
|
]
|
||||||
|
|
||||||
|
const metadata = {}
|
||||||
|
for (const componentName of [...new Set(componentNames)]) {
|
||||||
|
const interfaceName = componentTypeOverrides[componentName] || `${componentName}Props`
|
||||||
|
const entry = interfaces.get(interfaceName)
|
||||||
|
if (!entry) continue
|
||||||
|
const { node, sourceFile } = entry
|
||||||
|
const defaults = defaultsByFile.get(sourceFile.fileName) || {}
|
||||||
|
const extendsCommonProps = node.heritageClauses?.some((clause) =>
|
||||||
|
clause.types.some((type) => type.expression.getText(sourceFile) === 'CommonProps'),
|
||||||
|
)
|
||||||
|
const properties = []
|
||||||
|
|
||||||
|
for (const member of node.members) {
|
||||||
|
if (!ts.isPropertySignature(member) || !member.name) continue
|
||||||
|
const name = member.name.getText(sourceFile).replace(/^['"]|['"]$/g, '')
|
||||||
|
const typeInfo = propertyType(member.type, sourceFile)
|
||||||
|
properties.push({
|
||||||
|
name,
|
||||||
|
tsType: member.type?.getText(sourceFile) || 'unknown',
|
||||||
|
...typeInfo,
|
||||||
|
required: !member.questionToken,
|
||||||
|
...(defaults[name] !== undefined ? { defaultValue: defaults[name] } : {}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extendsCommonProps) {
|
||||||
|
for (const property of commonProperties) {
|
||||||
|
if (!properties.some((item) => item.name === property.name)) properties.push(property)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata[componentName] = {
|
||||||
|
interfaceName,
|
||||||
|
sourceFile: path.relative(uiRoot, sourceFile.fileName).replaceAll('\\', '/'),
|
||||||
|
properties,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.mkdirSync(path.dirname(outputFile), { recursive: true })
|
||||||
|
const formattedMetadata = await prettier.format(JSON.stringify(metadata), { parser: 'json' })
|
||||||
|
fs.writeFileSync(outputFile, formattedMetadata)
|
||||||
|
console.log(`Generated designer metadata for ${Object.keys(metadata).length} UI components.`)
|
||||||
|
|
@ -25,6 +25,41 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.select-md {
|
||||||
|
@apply text-sm;
|
||||||
|
|
||||||
|
.select-dropdown-indicator,
|
||||||
|
.select-clear-indicator {
|
||||||
|
@apply p-2 text-lg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.select-sm {
|
||||||
|
@apply text-sm;
|
||||||
|
|
||||||
|
.select-dropdown-indicator,
|
||||||
|
.select-clear-indicator {
|
||||||
|
@apply px-1.5 py-1 text-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select__multi-value {
|
||||||
|
@apply rounded text-sm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.select-xs {
|
||||||
|
@apply text-xs;
|
||||||
|
|
||||||
|
.select-dropdown-indicator,
|
||||||
|
.select-clear-indicator {
|
||||||
|
@apply px-1 py-0.5 text-xs;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select__multi-value {
|
||||||
|
@apply rounded text-xs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.select__single-value {
|
.select__single-value {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,783 +0,0 @@
|
||||||
import React, { useState, useEffect, useRef } from 'react'
|
|
||||||
import Editor from '@monaco-editor/react'
|
|
||||||
import { ComponentDefinition } from '../../proxy/developerKit/componentInfo'
|
|
||||||
import { generateSingleComponentJSX, generateUniqueId } from '@/utils/codeParser'
|
|
||||||
import { FaCheck, FaCode, FaMousePointer, FaSave, FaCog, FaTimes } from 'react-icons/fa'
|
|
||||||
import { Button, toast } from '../ui'
|
|
||||||
import Notification from '../ui/Notification/Notification'
|
|
||||||
|
|
||||||
interface ComponentCodeEditorProps {
|
|
||||||
code: string
|
|
||||||
onChange: (code: string) => void
|
|
||||||
onApplyCodeChanges: (code: string) => void
|
|
||||||
onResetCodeChanges: () => void
|
|
||||||
language?: string
|
|
||||||
theme?: 'vs-dark' | 'light'
|
|
||||||
onCursorChange?: (componentId: string | null) => void
|
|
||||||
onComponentAdded?: (componentDef: ComponentDefinition) => void
|
|
||||||
onComponentSave: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ComponentCodeEditor: React.FC<ComponentCodeEditorProps> = ({
|
|
||||||
code,
|
|
||||||
onChange,
|
|
||||||
onApplyCodeChanges,
|
|
||||||
onResetCodeChanges,
|
|
||||||
language = 'typescript',
|
|
||||||
theme = 'vs-dark',
|
|
||||||
onCursorChange,
|
|
||||||
onComponentAdded,
|
|
||||||
onComponentSave,
|
|
||||||
}) => {
|
|
||||||
const [localCode, setLocalCode] = useState(code)
|
|
||||||
const [hasChanges, setHasChanges] = useState(false)
|
|
||||||
const [isFormatting, setIsFormatting] = useState(false)
|
|
||||||
const [showSettings, setShowSettings] = useState(false)
|
|
||||||
const [editorTheme, setEditorTheme] = useState(theme)
|
|
||||||
const [fontSize, setFontSize] = useState(14)
|
|
||||||
const [wordWrap, setWordWrap] = useState<'on' | 'off'>('on')
|
|
||||||
const [minimap, setMinimap] = useState(true)
|
|
||||||
const [showSuccessMessage, setShowSuccessMessage] = useState(false)
|
|
||||||
const [isDragOver, setIsDragOver] = useState(false)
|
|
||||||
const [dropIndicator, setDropIndicator] = useState<{
|
|
||||||
show: boolean
|
|
||||||
line: number
|
|
||||||
column: number
|
|
||||||
}>({ show: false, line: 0, column: 0 })
|
|
||||||
|
|
||||||
const editorRef = useRef<any>(null)
|
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
|
||||||
// Refs to keep latest values accessible inside native DOM listeners
|
|
||||||
const localCodeRef = useRef(localCode)
|
|
||||||
const dropCallbacksRef = useRef({ onChange, onComponentAdded, onApplyCodeChanges })
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setLocalCode(code)
|
|
||||||
setHasChanges(false)
|
|
||||||
}, [code])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
localCodeRef.current = localCode
|
|
||||||
}, [localCode])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
dropCallbacksRef.current = { onChange, onComponentAdded, onApplyCodeChanges }
|
|
||||||
}, [onChange, onComponentAdded, onApplyCodeChanges])
|
|
||||||
|
|
||||||
const findComponentIdAtPosition = (
|
|
||||||
code: string,
|
|
||||||
position: { lineNumber: number; column: number },
|
|
||||||
): string | null => {
|
|
||||||
const lines = code.split('\n')
|
|
||||||
const currentLineIndex = position.lineNumber - 1
|
|
||||||
|
|
||||||
// Önce bulunduğu satırı kontrol et
|
|
||||||
const line = lines[currentLineIndex] || ''
|
|
||||||
const idMatch = line.match(/id=["']([\w-]+)["']/)
|
|
||||||
if (idMatch) return idMatch[1]
|
|
||||||
|
|
||||||
// Eğer bulunduğu satırda yoksa, yakındaki satırları kontrol et
|
|
||||||
// Önce yukarı doğru ara, sonra aşağı doğru ara
|
|
||||||
for (let distance = 1; distance <= 3; distance++) {
|
|
||||||
// Yukarı ara
|
|
||||||
if (currentLineIndex - distance >= 0) {
|
|
||||||
const upLine = lines[currentLineIndex - distance] || ''
|
|
||||||
// Component tag başlangıcını kontrol et
|
|
||||||
const componentMatch = upLine.match(/<([A-Z][a-zA-Z]*)/)
|
|
||||||
if (componentMatch) {
|
|
||||||
// Bu component tag'inin ID'sini ara (aynı satırda veya sonraki satırlarda)
|
|
||||||
for (
|
|
||||||
let j = currentLineIndex - distance;
|
|
||||||
j <= Math.min(lines.length - 1, currentLineIndex + 2);
|
|
||||||
j++
|
|
||||||
) {
|
|
||||||
const searchLine = lines[j] || ''
|
|
||||||
const foundId = searchLine.match(/id=["']([\w-]+)["']/)
|
|
||||||
if (foundId) return foundId[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Aşağı ara
|
|
||||||
if (currentLineIndex + distance < lines.length) {
|
|
||||||
const downLine = lines[currentLineIndex + distance] || ''
|
|
||||||
const componentMatch = downLine.match(/<([A-Z][a-zA-Z]*)/)
|
|
||||||
if (componentMatch) {
|
|
||||||
// Bu component tag'inin ID'sini ara
|
|
||||||
for (
|
|
||||||
let j = currentLineIndex + distance;
|
|
||||||
j <= Math.min(lines.length - 1, currentLineIndex + distance + 2);
|
|
||||||
j++
|
|
||||||
) {
|
|
||||||
const searchLine = lines[j] || ''
|
|
||||||
const foundId = searchLine.match(/id=["']([\w-]+)["']/)
|
|
||||||
if (foundId) return foundId[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hiçbir şey bulunamadıysa, cursor'ın bulunduğu satırda herhangi bir JSX tag var mı kontrol et
|
|
||||||
const jsxMatch = line.match(/<\/?([A-Z][a-zA-Z]*)/)
|
|
||||||
if (jsxMatch) {
|
|
||||||
// Bu tag'e ait ID'yi bulabilir miyiz?
|
|
||||||
const componentName = jsxMatch[1]
|
|
||||||
for (
|
|
||||||
let i = Math.max(0, currentLineIndex - 5);
|
|
||||||
i <= Math.min(lines.length - 1, currentLineIndex + 5);
|
|
||||||
i++
|
|
||||||
) {
|
|
||||||
const searchLine = lines[i] || ''
|
|
||||||
if (searchLine.includes(`<${componentName}`) && searchLine.includes('id=')) {
|
|
||||||
const foundId = searchLine.match(/id=["']([\w-]+)["']/)
|
|
||||||
if (foundId) return foundId[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the given 1-based lineNumber is inside a `return (...)` block.
|
|
||||||
* Uses parenthesis depth counting to track entry and exit of the return expression.
|
|
||||||
*/
|
|
||||||
const isPositionInsideJSXReturn = (codeStr: string, lineNumber: number): boolean => {
|
|
||||||
const lines = codeStr.split('\n')
|
|
||||||
let insideReturn = false
|
|
||||||
let parenDepth = 0
|
|
||||||
|
|
||||||
for (let i = 0; i < lineNumber && i < lines.length; i++) {
|
|
||||||
const line = lines[i]
|
|
||||||
if (!insideReturn) {
|
|
||||||
if (/\breturn\s*\(/.test(line)) {
|
|
||||||
insideReturn = true
|
|
||||||
parenDepth = 0
|
|
||||||
const returnIdx = line.search(/\breturn/)
|
|
||||||
const fromReturn = line.slice(returnIdx)
|
|
||||||
for (const ch of fromReturn) {
|
|
||||||
if (ch === '(') parenDepth++
|
|
||||||
else if (ch === ')') {
|
|
||||||
parenDepth--
|
|
||||||
if (parenDepth <= 0) {
|
|
||||||
insideReturn = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const ch of line) {
|
|
||||||
if (ch === '(') parenDepth++
|
|
||||||
else if (ch === ')') {
|
|
||||||
parenDepth--
|
|
||||||
if (parenDepth <= 0) {
|
|
||||||
insideReturn = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return insideReturn
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleEditorCursorChange = () => {
|
|
||||||
if (!editorRef.current) return
|
|
||||||
const position = editorRef.current.getPosition()
|
|
||||||
if (!position) return
|
|
||||||
|
|
||||||
const id = findComponentIdAtPosition(localCode, position)
|
|
||||||
if (onCursorChange) onCursorChange(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleEditorDidMount = (editor: any, monaco: any) => {
|
|
||||||
editorRef.current = editor
|
|
||||||
|
|
||||||
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
|
||||||
target: monaco.languages.typescript.ScriptTarget.Latest,
|
|
||||||
allowNonTsExtensions: true,
|
|
||||||
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
|
|
||||||
module: monaco.languages.typescript.ModuleKind.CommonJS,
|
|
||||||
noEmit: true,
|
|
||||||
esModuleInterop: true,
|
|
||||||
jsx: monaco.languages.typescript.JsxEmit.React,
|
|
||||||
reactNamespace: 'React',
|
|
||||||
allowJs: true,
|
|
||||||
typeRoots: ['node_modules/@types'],
|
|
||||||
})
|
|
||||||
|
|
||||||
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
|
|
||||||
handleApplyChanges()
|
|
||||||
})
|
|
||||||
|
|
||||||
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyF, () => {
|
|
||||||
handleFormatCode()
|
|
||||||
})
|
|
||||||
|
|
||||||
editor.onDidChangeCursorPosition(handleEditorCursorChange)
|
|
||||||
|
|
||||||
// Intercept Monaco's native drop so it never inserts raw JSON text.
|
|
||||||
// We handle the drop ourselves: insert JSX when inside return(), skip otherwise.
|
|
||||||
const domNode = editor.getDomNode()
|
|
||||||
if (domNode) {
|
|
||||||
domNode.addEventListener(
|
|
||||||
'drop',
|
|
||||||
(nativeEvent: DragEvent) => {
|
|
||||||
nativeEvent.preventDefault()
|
|
||||||
nativeEvent.stopPropagation()
|
|
||||||
nativeEvent.stopImmediatePropagation()
|
|
||||||
|
|
||||||
if (!nativeEvent.dataTransfer) return
|
|
||||||
|
|
||||||
// Read component definition from drag data
|
|
||||||
let componentDefData: string | undefined
|
|
||||||
for (const fmt of ['application/json', 'text/plain']) {
|
|
||||||
const d = nativeEvent.dataTransfer.getData(fmt)
|
|
||||||
if (d) {
|
|
||||||
componentDefData = d
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!componentDefData) return
|
|
||||||
|
|
||||||
let componentDef: any
|
|
||||||
try {
|
|
||||||
componentDef = JSON.parse(componentDefData)
|
|
||||||
} catch {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!componentDef?.name) return
|
|
||||||
|
|
||||||
// Resolve the drop position inside Monaco
|
|
||||||
const dropPos = editor.getTargetAtClientPoint(nativeEvent.clientX, nativeEvent.clientY)
|
|
||||||
const position = dropPos?.position ?? editor.getPosition()
|
|
||||||
if (!position) return
|
|
||||||
|
|
||||||
// Only allow drop inside JSX return(...) block
|
|
||||||
if (!isPositionInsideJSXReturn(localCodeRef.current, position.lineNumber)) {
|
|
||||||
toast.push(
|
|
||||||
<Notification type="warning" duration={3000}>
|
|
||||||
Bileşen yalnızca <strong>return(...)</strong> bloğu içine eklenebilir.
|
|
||||||
</Notification>,
|
|
||||||
{ placement: 'bottom-end' },
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const componentId = generateUniqueId()
|
|
||||||
const adaptedProps = {
|
|
||||||
...(componentDef.properties || []).reduce(
|
|
||||||
(acc: any, prop: any) => {
|
|
||||||
acc[prop.name] = {
|
|
||||||
type: prop.type,
|
|
||||||
value: prop.value,
|
|
||||||
...(prop.options ? { options: prop.options } : {}),
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
},
|
|
||||||
{} as Record<string, any>,
|
|
||||||
),
|
|
||||||
id: { type: 'string', value: componentId },
|
|
||||||
}
|
|
||||||
|
|
||||||
const componentJSX = generateSingleComponentJSX(componentDef.name, adaptedProps)
|
|
||||||
const componentDefWithId = {
|
|
||||||
...componentDef,
|
|
||||||
id: componentId,
|
|
||||||
properties: [
|
|
||||||
...(componentDef.properties || []).filter((p: any) => p.name !== 'id'),
|
|
||||||
{ name: 'id', type: 'string', value: componentId, category: 'properties' },
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
const lines = localCodeRef.current.split('\n')
|
|
||||||
const indent = (lines[position.lineNumber - 1] || '').match(/^(\s*)/)?.[1] ?? ' '
|
|
||||||
const formattedJSX = `${indent}${componentJSX}`
|
|
||||||
const newLines = [...lines]
|
|
||||||
newLines.splice(position.lineNumber, 0, formattedJSX)
|
|
||||||
const newCode = newLines.join('\n')
|
|
||||||
|
|
||||||
setLocalCode(newCode)
|
|
||||||
const {
|
|
||||||
onChange: onChg,
|
|
||||||
onComponentAdded: onAdded,
|
|
||||||
onApplyCodeChanges: onApply,
|
|
||||||
} = dropCallbacksRef.current
|
|
||||||
onChg(newCode)
|
|
||||||
if (onAdded) onAdded(componentDefWithId)
|
|
||||||
setTimeout(() => onApply(newCode), 100)
|
|
||||||
|
|
||||||
setIsDragOver(false)
|
|
||||||
setDropIndicator({ show: false, line: 0, column: 0 })
|
|
||||||
},
|
|
||||||
true, // capture phase — fires before Monaco's own listeners
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleCodeChange = (value: string | undefined) => {
|
|
||||||
if (value !== undefined) {
|
|
||||||
setLocalCode(value)
|
|
||||||
setHasChanges(value !== code)
|
|
||||||
onChange(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleApplyChanges = () => {
|
|
||||||
onApplyCodeChanges(localCode)
|
|
||||||
setHasChanges(false)
|
|
||||||
setShowSuccessMessage(true)
|
|
||||||
handleFormatCode()
|
|
||||||
setTimeout(() => setShowSuccessMessage(false), 3000)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleFormatCode = async () => {
|
|
||||||
if (editorRef.current) {
|
|
||||||
setIsFormatting(true)
|
|
||||||
try {
|
|
||||||
await editorRef.current.getAction('editor.action.formatDocument').run()
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Kod formatlama hatası:', error)
|
|
||||||
} finally {
|
|
||||||
setIsFormatting(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleResetChanges = () => {
|
|
||||||
setLocalCode(code)
|
|
||||||
setHasChanges(false)
|
|
||||||
onResetCodeChanges()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drag & Drop handlers for Code Editor
|
|
||||||
const handleDragOver = (e: React.DragEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
|
|
||||||
if (!editorRef.current || !containerRef.current) return
|
|
||||||
|
|
||||||
try {
|
|
||||||
const hasComponentData = e.dataTransfer.types.some(
|
|
||||||
(type) => type === 'application/json' || type === 'text/plain' || type === 'text',
|
|
||||||
)
|
|
||||||
|
|
||||||
if (hasComponentData) {
|
|
||||||
e.dataTransfer.dropEffect = 'copy'
|
|
||||||
setIsDragOver(true)
|
|
||||||
|
|
||||||
// Get cursor position from mouse coordinates
|
|
||||||
const rect = containerRef.current.getBoundingClientRect()
|
|
||||||
const x = e.clientX - rect.left
|
|
||||||
const y = e.clientY - rect.top
|
|
||||||
|
|
||||||
// Convert to editor position
|
|
||||||
const position = editorRef.current.getTargetAtClientPoint(x, y)
|
|
||||||
if (position) {
|
|
||||||
setDropIndicator({
|
|
||||||
show: true,
|
|
||||||
line: position.position.lineNumber,
|
|
||||||
column: position.position.column,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('Error handling drag over:', err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDragLeave = (e: React.DragEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
|
|
||||||
if (!containerRef.current) return
|
|
||||||
|
|
||||||
const rect = containerRef.current.getBoundingClientRect()
|
|
||||||
const x = e.clientX
|
|
||||||
const y = e.clientY
|
|
||||||
|
|
||||||
if (x <= rect.left || x >= rect.right || y <= rect.top || y >= rect.bottom) {
|
|
||||||
setIsDragOver(false)
|
|
||||||
setDropIndicator({ show: false, line: 0, column: 0 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDrop = (e: React.DragEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
|
|
||||||
setIsDragOver(false)
|
|
||||||
setDropIndicator({ show: false, line: 0, column: 0 })
|
|
||||||
|
|
||||||
if (!editorRef.current || !containerRef.current) return
|
|
||||||
|
|
||||||
try {
|
|
||||||
let componentDefData
|
|
||||||
const formats = ['application/json', 'text/plain', 'text']
|
|
||||||
|
|
||||||
// Sürüklenen bileşenin verisini al
|
|
||||||
for (const format of formats) {
|
|
||||||
const data = e.dataTransfer.getData(format)
|
|
||||||
|
|
||||||
if (data) {
|
|
||||||
componentDefData = data
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!componentDefData) return
|
|
||||||
|
|
||||||
let componentDef
|
|
||||||
try {
|
|
||||||
componentDef = JSON.parse(componentDefData)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Component verisi çözümlenemedi:', error)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Component ID'sini oluştur
|
|
||||||
const componentId = generateUniqueId()
|
|
||||||
|
|
||||||
// 2. Props'ları adapt et ve ID'yi dahil et
|
|
||||||
const adaptedProps = {
|
|
||||||
...(componentDef.properties || []).reduce(
|
|
||||||
(acc: any, prop: any) => {
|
|
||||||
acc[prop.name] = {
|
|
||||||
type: prop.type,
|
|
||||||
value: prop.value,
|
|
||||||
...(prop.options ? { options: prop.options } : {}),
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
},
|
|
||||||
{} as Record<string, any>,
|
|
||||||
),
|
|
||||||
id: {
|
|
||||||
type: 'string',
|
|
||||||
value: componentId,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. JSX oluştur
|
|
||||||
const componentJSX = generateSingleComponentJSX(componentDef.name, adaptedProps)
|
|
||||||
|
|
||||||
// 4. Component tanımını ID ile genişlet
|
|
||||||
const componentDefWithId = {
|
|
||||||
...componentDef,
|
|
||||||
id: componentId,
|
|
||||||
properties: [
|
|
||||||
...(componentDef.properties || []).filter((p: any) => p.name !== 'id'),
|
|
||||||
{
|
|
||||||
name: 'id',
|
|
||||||
type: 'string',
|
|
||||||
value: componentId,
|
|
||||||
category: 'properties',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Pozisyonu al
|
|
||||||
let position = editorRef.current.getPosition()
|
|
||||||
if (!position) {
|
|
||||||
console.error('Geçersiz pozisyon tespit edildi. Yedek pozisyon kullanılıyor.')
|
|
||||||
position = { lineNumber: 0, column: 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. JSX kodunu pozisyonda ekle
|
|
||||||
const newCode = insertJSXAtPosition(localCode, componentJSX, position)
|
|
||||||
setLocalCode(newCode)
|
|
||||||
onChange(newCode)
|
|
||||||
|
|
||||||
// 7. Parent'a bildirim gönder
|
|
||||||
if (onComponentAdded) {
|
|
||||||
onComponentAdded(componentDefWithId)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. Uygulamayı tetikle
|
|
||||||
setTimeout(() => {
|
|
||||||
onApplyCodeChanges(newCode)
|
|
||||||
}, 100)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Bileşen bırakılırken hata oluştu:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const insertJSXAtPosition = (
|
|
||||||
code: string,
|
|
||||||
jsx: string,
|
|
||||||
position: { lineNumber: number; column: number },
|
|
||||||
): string => {
|
|
||||||
const updatedCode = code
|
|
||||||
const lines = updatedCode.split('\n')
|
|
||||||
|
|
||||||
// Pozisyonu doğrula (import eklendiyse satır numaraları değişmiş olabilir)
|
|
||||||
if (position.lineNumber < 1 || position.lineNumber > lines.length) {
|
|
||||||
console.error('Geçersiz satır numarası:', position.lineNumber)
|
|
||||||
position.lineNumber = lines.length // Geçersiz satırda son satıra ekle
|
|
||||||
}
|
|
||||||
|
|
||||||
const line = lines[position.lineNumber - 1] // 1 tabanlı indeks, 0 tabanlıya çevrilir
|
|
||||||
|
|
||||||
if (!line) {
|
|
||||||
console.error('Satır bulunamadı:', position.lineNumber)
|
|
||||||
return updatedCode // Eğer satır yoksa, kodu değiştirmeden döndür
|
|
||||||
}
|
|
||||||
|
|
||||||
// Satırın indentasyonunu belirle
|
|
||||||
const indentMatch = line.match(/^(\s*)/)
|
|
||||||
const indent = indentMatch ? indentMatch[1] : ' ' // Default 6 boşluk
|
|
||||||
|
|
||||||
// Component'i yeni satıra ekle (mevcut satırın altına)
|
|
||||||
const formattedJSX = `${indent}${jsx}`
|
|
||||||
lines.splice(position.lineNumber, 0, formattedJSX)
|
|
||||||
|
|
||||||
return lines.join('\n') // Tüm satırları birleştir ve yeni kodu döndür
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDragEnter = (e: React.DragEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={containerRef}
|
|
||||||
className={`w-full h-full flex flex-col min-h-0 max-h-full bg-gray-900 text-white relative ${
|
|
||||||
isDragOver ? 'ring-2 ring-blue-500 ring-offset-2' : ''
|
|
||||||
}`}
|
|
||||||
style={{ flex: 1, minHeight: 0, maxHeight: '100vh' }}
|
|
||||||
onDragOver={handleDragOver}
|
|
||||||
onDragEnter={handleDragEnter}
|
|
||||||
onDragLeave={handleDragLeave}
|
|
||||||
onDrop={handleDrop}
|
|
||||||
>
|
|
||||||
{/* Drop Indicator */}
|
|
||||||
{dropIndicator.show && (
|
|
||||||
<div className="absolute inset-0 bg-blue-500 bg-opacity-10 border-2 border-dashed border-blue-500 rounded-lg flex items-center justify-center z-50 pointer-events-none">
|
|
||||||
<div className="bg-blue-500 text-white px-4 py-2 rounded-lg font-medium shadow-lg">
|
|
||||||
Bileşeni Line {dropIndicator.line}, Column {dropIndicator.column} konumuna bırak
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Header */}
|
|
||||||
<div className="bg-gray-800 border-b border-gray-700 p-4 flex items-end justify-between shrink-0">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Button
|
|
||||||
loading={isFormatting}
|
|
||||||
icon={<FaCode className="w-4 h-4" />}
|
|
||||||
variant="default"
|
|
||||||
size="sm"
|
|
||||||
onClick={handleFormatCode}
|
|
||||||
>
|
|
||||||
Formatla
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
icon={<FaCog className="w-4 h-4" />}
|
|
||||||
variant={showSettings ? 'solid' : 'default'}
|
|
||||||
color="blue-600"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => setShowSettings(!showSettings)}
|
|
||||||
>
|
|
||||||
Ayarlar
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
icon={<FaTimes className="w-4 h-4" />}
|
|
||||||
variant="solid"
|
|
||||||
color="red-600"
|
|
||||||
size="sm"
|
|
||||||
onClick={handleResetChanges}
|
|
||||||
>
|
|
||||||
Sıfırla
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
icon={<FaCheck className="w-4 h-4" />}
|
|
||||||
variant="solid"
|
|
||||||
color="green-600"
|
|
||||||
size="sm"
|
|
||||||
onClick={handleApplyChanges}
|
|
||||||
>
|
|
||||||
Uygula
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="col-span-2 flex items-center justify-end">
|
|
||||||
<Button
|
|
||||||
icon={<FaSave className="w-4 h-4" />}
|
|
||||||
variant="solid"
|
|
||||||
color="yellow-600"
|
|
||||||
size="sm"
|
|
||||||
className="shadow-sm"
|
|
||||||
onClick={onComponentSave}
|
|
||||||
>
|
|
||||||
Kaydet
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Settings Panel */}
|
|
||||||
{showSettings && (
|
|
||||||
<div className="bg-gray-800 border-b border-gray-700 p-4 shrink-0">
|
|
||||||
<div className="grid grid-cols-4 gap-4">
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-300 mb-2">Tema</label>
|
|
||||||
<select
|
|
||||||
value={editorTheme}
|
|
||||||
className="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-md text-white text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
onChange={(e) => setEditorTheme(e.target.value as 'vs-dark' | 'light')}
|
|
||||||
>
|
|
||||||
<option value="vs-dark">Koyu</option>
|
|
||||||
<option value="light">Açık</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-300 mb-2">Font Boyutu</label>
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
min="10"
|
|
||||||
max="24"
|
|
||||||
value={fontSize}
|
|
||||||
className="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-md text-white text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
onChange={(e) => setFontSize(parseInt(e.target.value))}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-300 mb-2">
|
|
||||||
Kelime Kaydırma
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
value={wordWrap}
|
|
||||||
className="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-md text-white text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
onChange={(e) => setWordWrap(e.target.value as 'on' | 'off')}
|
|
||||||
>
|
|
||||||
<option value="on">Açık</option>
|
|
||||||
<option value="off">Kapalı</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-gray-300 mb-2">Mini Harita</label>
|
|
||||||
<Button
|
|
||||||
block
|
|
||||||
variant={minimap ? 'solid' : 'default'}
|
|
||||||
color="blue-600"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => setMinimap(!minimap)}
|
|
||||||
>
|
|
||||||
{minimap ? 'Etkin' : 'Devre Dışı'}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Editor */}
|
|
||||||
<div className="flex-1 h-full">
|
|
||||||
<Editor
|
|
||||||
height="100%"
|
|
||||||
width="100%"
|
|
||||||
language={language}
|
|
||||||
theme={editorTheme}
|
|
||||||
value={localCode}
|
|
||||||
options={{
|
|
||||||
fontSize: fontSize,
|
|
||||||
wordWrap: wordWrap,
|
|
||||||
minimap: { enabled: minimap },
|
|
||||||
automaticLayout: true,
|
|
||||||
scrollBeyondLastLine: false,
|
|
||||||
renderWhitespace: 'selection',
|
|
||||||
bracketPairColorization: { enabled: true },
|
|
||||||
guides: {
|
|
||||||
bracketPairs: true,
|
|
||||||
indentation: true,
|
|
||||||
},
|
|
||||||
suggest: {
|
|
||||||
showKeywords: true,
|
|
||||||
showSnippets: true,
|
|
||||||
showFunctions: true,
|
|
||||||
showConstructors: true,
|
|
||||||
showFields: true,
|
|
||||||
showVariables: true,
|
|
||||||
showClasses: true,
|
|
||||||
showStructs: true,
|
|
||||||
showInterfaces: true,
|
|
||||||
showModules: true,
|
|
||||||
showProperties: true,
|
|
||||||
showEvents: true,
|
|
||||||
showOperators: true,
|
|
||||||
showUnits: true,
|
|
||||||
showValues: true,
|
|
||||||
showConstants: true,
|
|
||||||
showEnums: true,
|
|
||||||
showEnumMembers: true,
|
|
||||||
showColors: true,
|
|
||||||
showFiles: true,
|
|
||||||
showReferences: true,
|
|
||||||
showFolders: true,
|
|
||||||
showTypeParameters: true,
|
|
||||||
showUsers: true,
|
|
||||||
showIssues: true,
|
|
||||||
},
|
|
||||||
quickSuggestions: {
|
|
||||||
other: true,
|
|
||||||
comments: true,
|
|
||||||
strings: true,
|
|
||||||
},
|
|
||||||
parameterHints: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
contextmenu: true,
|
|
||||||
mouseWheelZoom: true,
|
|
||||||
cursorBlinking: 'smooth',
|
|
||||||
cursorSmoothCaretAnimation: 'on',
|
|
||||||
smoothScrolling: true,
|
|
||||||
folding: true,
|
|
||||||
foldingStrategy: 'indentation',
|
|
||||||
showFoldingControls: 'always',
|
|
||||||
unfoldOnClickAfterEndOfLine: false,
|
|
||||||
tabSize: 2,
|
|
||||||
insertSpaces: true,
|
|
||||||
detectIndentation: true,
|
|
||||||
trimAutoWhitespace: true,
|
|
||||||
formatOnPaste: true,
|
|
||||||
formatOnType: true,
|
|
||||||
}}
|
|
||||||
onChange={handleCodeChange}
|
|
||||||
onMount={handleEditorDidMount}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Status Bar */}
|
|
||||||
<div className="bg-gray-800 border-t border-gray-700 px-4 py-2 flex items-center justify-between text-sm text-gray-400 shrink-0">
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<span>TypeScript React</span>
|
|
||||||
<span>UTF-8</span>
|
|
||||||
<span>LF</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<span>Satır 1, Sütun 1</span>
|
|
||||||
<span>Boşluklar: 2</span>
|
|
||||||
{hasChanges && <span className="text-orange-400">● Kaydedilmemiş değişiklikler</span>}
|
|
||||||
{showSuccessMessage && (
|
|
||||||
<span className="text-green-400 flex items-center gap-1">
|
|
||||||
<FaCheck className="w-4 h-4" />
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{isDragOver && (
|
|
||||||
<span className="text-blue-400 flex items-center gap-1">
|
|
||||||
<FaMousePointer className="w-4 h-4" />
|
|
||||||
Bileşeni bırakmaya hazır
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,190 +0,0 @@
|
||||||
import React, { useMemo, useState } from "react";
|
|
||||||
import { FaSearch, FaSquare } from 'react-icons/fa';
|
|
||||||
import { ComponentDefinition, HookInfo, PropertyInfo } from "../../proxy/developerKit/componentInfo";
|
|
||||||
import { getAllComponentDefinitions } from "./data/componentDefinitions";
|
|
||||||
import navigationIcon from "@/proxy/menus/navigation-icon.config";
|
|
||||||
|
|
||||||
interface ComponentLibraryProps {
|
|
||||||
onDragStart: (componentDef: ComponentDefinition, e: React.DragEvent) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ComponentLibrary: React.FC<ComponentLibraryProps> = ({
|
|
||||||
onDragStart,
|
|
||||||
}) => {
|
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
|
||||||
|
|
||||||
const handleDragStart = (
|
|
||||||
componentDef: ComponentDefinition,
|
|
||||||
e: React.DragEvent
|
|
||||||
) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
try {
|
|
||||||
const data = JSON.stringify(componentDef);
|
|
||||||
e.dataTransfer.setData("text/plain", data);
|
|
||||||
e.dataTransfer.setData("application/json", data);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error setting drag data:", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.dataTransfer.effectAllowed = "copy";
|
|
||||||
|
|
||||||
if (e.dataTransfer.setDragImage) {
|
|
||||||
try {
|
|
||||||
const dragPreview = document.createElement("div");
|
|
||||||
dragPreview.className = "component-drag-preview";
|
|
||||||
dragPreview.innerHTML = `<div class="flex items-center">
|
|
||||||
<span class="mr-2">${componentDef.icon || "📦"}</span>
|
|
||||||
<span>${componentDef.name}</span>
|
|
||||||
</div>`;
|
|
||||||
Object.assign(dragPreview.style, {
|
|
||||||
position: "absolute",
|
|
||||||
top: "-1000px",
|
|
||||||
left: "0",
|
|
||||||
padding: "8px 12px",
|
|
||||||
backgroundColor: "white",
|
|
||||||
border: "2px solid #4f46e5",
|
|
||||||
borderRadius: "4px",
|
|
||||||
boxShadow: "0 2px 5px rgba(0,0,0,0.2)",
|
|
||||||
zIndex: "9999",
|
|
||||||
pointerEvents: "none",
|
|
||||||
});
|
|
||||||
document.body.appendChild(dragPreview);
|
|
||||||
e.dataTransfer.setDragImage(dragPreview, 20, 20);
|
|
||||||
setTimeout(() => document.body.removeChild(dragPreview), 0);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error setting drag image:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onDragStart(componentDef, e);
|
|
||||||
};
|
|
||||||
|
|
||||||
const filteredComponents = useMemo(
|
|
||||||
() =>
|
|
||||||
getAllComponentDefinitions().filter(
|
|
||||||
(comp) =>
|
|
||||||
comp.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
||||||
comp.description.toLowerCase().includes(searchTerm.toLowerCase())
|
|
||||||
),
|
|
||||||
[searchTerm]
|
|
||||||
);
|
|
||||||
|
|
||||||
const categories = [
|
|
||||||
"basic",
|
|
||||||
"form",
|
|
||||||
"layout",
|
|
||||||
"feedback",
|
|
||||||
"media",
|
|
||||||
"interactive",
|
|
||||||
"navigation",
|
|
||||||
"data",
|
|
||||||
]
|
|
||||||
.map((cat: any) => ({
|
|
||||||
id: cat,
|
|
||||||
name: cat,
|
|
||||||
components: filteredComponents?.filter((c) => c.category === cat),
|
|
||||||
}))
|
|
||||||
.filter((cat) => cat.components.length > 0);
|
|
||||||
|
|
||||||
const getIcon = (iconName: string): React.ComponentType<any> => {
|
|
||||||
return navigationIcon[iconName] || FaSquare;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-full bg-gray-900 text-white flex flex-col h-full">
|
|
||||||
{/* Arama kutusu */}
|
|
||||||
<div className="p-4 border-b border-gray-700">
|
|
||||||
<div className="relative">
|
|
||||||
<FaSearch className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Search Components..."
|
|
||||||
value={searchTerm}
|
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
|
||||||
className="w-full pl-10 pr-4 py-2 bg-gray-800 border border-gray-600 rounded-lg text-sm text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Bileşen kategorileri */}
|
|
||||||
<div className="flex-1 overflow-y-auto">
|
|
||||||
<div className="p-4 space-y-6">
|
|
||||||
{categories.map((category) => (
|
|
||||||
<div key={category.id}>
|
|
||||||
<h3 className="text-xs font-semibold text-gray-400 uppercase tracking-wider mb-3">
|
|
||||||
{category.name}
|
|
||||||
</h3>
|
|
||||||
<div className="space-y-1">
|
|
||||||
{category.components.map((componentDef) => {
|
|
||||||
const IconComponent = getIcon(componentDef.icon);
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={componentDef.name}
|
|
||||||
className="component-library-item flex items-center p-3 bg-gray-800 rounded-lg cursor-move hover:bg-gray-700 border border-gray-700 hover:border-gray-600"
|
|
||||||
draggable
|
|
||||||
onDragStart={(e) => handleDragStart(componentDef, e)}
|
|
||||||
onDragEnd={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="w-8 h-8 bg-gray-700 rounded-lg flex items-center justify-center mr-3 flex-shrink-0">
|
|
||||||
<IconComponent className="w-4 h-4 text-gray-300" />
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 min-w-0 pointer-events-none">
|
|
||||||
<span className="text-sm font-medium text-gray-200 block truncate">
|
|
||||||
{componentDef.name}
|
|
||||||
</span>
|
|
||||||
<p className="text-xs text-gray-400 truncate">
|
|
||||||
{componentDef.description}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{/* Özellikler */}
|
|
||||||
{componentDef.properties?.length > 0 && (
|
|
||||||
<div className="mt-1 flex flex-wrap gap-1">
|
|
||||||
{componentDef.properties.slice(0, 2).map((prop: PropertyInfo) => (
|
|
||||||
<span
|
|
||||||
key={prop.name}
|
|
||||||
className="bg-gray-700 text-gray-300 text-[10px] px-2 py-0.5 rounded mr-1"
|
|
||||||
>
|
|
||||||
{prop.name}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
{componentDef.properties.length > 2 && (
|
|
||||||
<span className="text-gray-500 text-[10px]">
|
|
||||||
...
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Hooklar */}
|
|
||||||
{componentDef.hooks?.length > 0 && (
|
|
||||||
<div className="mt-1 flex flex-wrap gap-1">
|
|
||||||
{componentDef.hooks.slice(0, 2).map((hook: HookInfo) => (
|
|
||||||
<span
|
|
||||||
key={hook.name}
|
|
||||||
className="bg-gray-900 text-green-300 text-[10px] px-2 py-0.5 rounded mr-1"
|
|
||||||
>
|
|
||||||
{hook.name}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
{componentDef.hooks.length > 2 && (
|
|
||||||
<span className="text-gray-500 text-[10px]">
|
|
||||||
...
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { ComponentInfo } from '../../proxy/developerKit/componentInfo';
|
|
||||||
import { Button } from '../ui';
|
|
||||||
|
|
||||||
interface ComponentSelectorProps {
|
|
||||||
components: ComponentInfo[];
|
|
||||||
selectedComponentId: string | null;
|
|
||||||
onSelectComponent: (componentId: string | null) => void;
|
|
||||||
onRefresh: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ComponentSelector: React.FC<ComponentSelectorProps> = ({
|
|
||||||
components,
|
|
||||||
selectedComponentId,
|
|
||||||
onSelectComponent,
|
|
||||||
onRefresh
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<div className="p-4 bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700">
|
|
||||||
<div className="flex items-center justify-between mb-2">
|
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-200">
|
|
||||||
Select Component
|
|
||||||
</label>
|
|
||||||
<Button
|
|
||||||
variant='solid'
|
|
||||||
size="sm"
|
|
||||||
onClick={onRefresh}
|
|
||||||
className="px-3 py-1 bg-blue-500 text-white text-xs rounded hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700 dark:text-white transition-colors"
|
|
||||||
title="Refresh component list"
|
|
||||||
>
|
|
||||||
Refresh
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<select
|
|
||||||
value={selectedComponentId || ''}
|
|
||||||
onChange={(e) => onSelectComponent(e.target.value || null)}
|
|
||||||
className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white dark:placeholder-gray-400"
|
|
||||||
>
|
|
||||||
<option value="">No component selected</option>
|
|
||||||
{components.map(component => (
|
|
||||||
<option key={component.id} value={component.id}>
|
|
||||||
{component.type} - {component.id}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ComponentSelector;
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import {
|
|
||||||
FaCog,
|
|
||||||
FaTimes,
|
|
||||||
FaBars,
|
|
||||||
FaEye,
|
|
||||||
FaEyeSlash
|
|
||||||
} from 'react-icons/fa';
|
|
||||||
import { Button } from "../ui";
|
|
||||||
import { PanelState } from "./data/componentDefinitions";
|
|
||||||
|
|
||||||
interface PanelManagerProps {
|
|
||||||
isOpen: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
panelState: PanelState;
|
|
||||||
onPanelToggle: (panel: keyof PanelState) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PanelManager: React.FC<PanelManagerProps> = ({
|
|
||||||
isOpen,
|
|
||||||
onClose,
|
|
||||||
panelState,
|
|
||||||
onPanelToggle,
|
|
||||||
}) => {
|
|
||||||
|
|
||||||
if (!isOpen) return null;
|
|
||||||
|
|
||||||
const paneller = [
|
|
||||||
{ key: "toolbox" as keyof PanelState, label: "Toolbox", icon: FaBars },
|
|
||||||
{ key: "properties" as keyof PanelState, label: "Properties", icon: FaCog },
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center">
|
|
||||||
<div className="bg-white dark:bg-gray-900 rounded-lg shadow-xl w-96 max-w-full mx-4">
|
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<FaBars className="w-5 h-5 text-blue-600 dark:text-blue-400" />
|
|
||||||
<h2 className="text-base font-semibold text-gray-900 dark:text-gray-100">Panel Manager</h2>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
onClick={onClose}
|
|
||||||
icon={<FaTimes className="w-5 h-5" />}
|
|
||||||
variant="plain"
|
|
||||||
size="xs"
|
|
||||||
title="Kapat"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="p-4">
|
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-300 mb-4">Customize Workspace</p>
|
|
||||||
<div className="space-y-3">
|
|
||||||
<h3 className="text-sm font-medium text-gray-900 dark:text-gray-100">Panels</h3>
|
|
||||||
{paneller.map(({ key, label, icon: Icon }) => (
|
|
||||||
<div key={key} className="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
|
||||||
<div className="flex items-center space-x-3">
|
|
||||||
<Icon className="w-4 h-4 text-gray-600 dark:text-gray-300" />
|
|
||||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-200">{label}</span>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
onClick={() => onPanelToggle(key)}
|
|
||||||
icon={
|
|
||||||
panelState[key] ? (
|
|
||||||
<FaEye className="w-4 h-4" />
|
|
||||||
) : (
|
|
||||||
<FaEyeSlash className="w-4 h-4" />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
variant={panelState[key] ? "twoTone" : "plain"}
|
|
||||||
size="xs"
|
|
||||||
title={panelState[key] ? "Hide" : "Show"}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,619 +0,0 @@
|
||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import TailwindModal from "./TailwindModal";
|
|
||||||
import { ComponentInfo, HookInfo, PropertyInfo } from "../../proxy/developerKit/componentInfo";
|
|
||||||
import { getComponentDefinition } from "./data/componentDefinitions";
|
|
||||||
import { Button } from "../ui";
|
|
||||||
import { FaCheck, FaCode, FaTimes, FaTrash } from "react-icons/fa";
|
|
||||||
|
|
||||||
interface PropertyPanelProps {
|
|
||||||
selectedComponent: ComponentInfo | null;
|
|
||||||
currentCode: string;
|
|
||||||
onPropertiesChange: (
|
|
||||||
componentId: string,
|
|
||||||
updates: Record<string, any>
|
|
||||||
) => void;
|
|
||||||
onHookToggle: (
|
|
||||||
componentId: string,
|
|
||||||
hookType: string,
|
|
||||||
enabled: boolean
|
|
||||||
) => void;
|
|
||||||
onMultipleHookToggle: (
|
|
||||||
toggles: { componentId: string; hookType: string; enabled: boolean }[]
|
|
||||||
) => void;
|
|
||||||
onDeleteComponent: (componentId: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PropertyPanel: React.FC<PropertyPanelProps> = ({
|
|
||||||
selectedComponent,
|
|
||||||
currentCode,
|
|
||||||
onPropertiesChange,
|
|
||||||
onHookToggle,
|
|
||||||
onMultipleHookToggle,
|
|
||||||
onDeleteComponent,
|
|
||||||
}) => {
|
|
||||||
const [tailwindModalOpen, setTailwindModalOpen] = useState(false);
|
|
||||||
const [currentTailwindProperty, setCurrentTailwindProperty] =
|
|
||||||
useState<string>("");
|
|
||||||
const [activeHooks, setActiveHooks] = useState<Set<string>>(new Set());
|
|
||||||
const [activeTab, setActiveTab] = useState<"props" | "hooks">("props");
|
|
||||||
|
|
||||||
// Local state for pending changes
|
|
||||||
const [pendingProperties, setPendingProperties] = useState<
|
|
||||||
Record<string, any>
|
|
||||||
>({});
|
|
||||||
const [pendingEvents, setPendingEvents] = useState<Record<string, string>>(
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
const [pendingHooks, setPendingHooks] = useState<Record<string, boolean>>({});
|
|
||||||
const [hasChanges, setHasChanges] = useState(false);
|
|
||||||
const [hasHookChanges, setHasHookChanges] = useState(false);
|
|
||||||
|
|
||||||
const componentDefinition = selectedComponent
|
|
||||||
? getComponentDefinition(selectedComponent.name)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
// Reset pending changes when component changes
|
|
||||||
useEffect(() => {
|
|
||||||
setPendingProperties({});
|
|
||||||
setPendingEvents({});
|
|
||||||
setPendingHooks({});
|
|
||||||
setHasChanges(false);
|
|
||||||
setHasHookChanges(false);
|
|
||||||
}, [selectedComponent?.id]);
|
|
||||||
|
|
||||||
// Check which hooks are currently active in the code
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedComponent && currentCode) {
|
|
||||||
const hooks = new Set<string>();
|
|
||||||
|
|
||||||
// Check for useState
|
|
||||||
if (
|
|
||||||
currentCode.includes(
|
|
||||||
`const [state_${selectedComponent.id}, setState_${selectedComponent.id}]`
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
hooks.add("useState");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for useRef
|
|
||||||
if (
|
|
||||||
currentCode.includes(`const ref_${selectedComponent.id}`) &&
|
|
||||||
currentCode.includes(`ref={ref_${selectedComponent.id}`)
|
|
||||||
) {
|
|
||||||
hooks.add("useRef");
|
|
||||||
}
|
|
||||||
|
|
||||||
// // Check for useEffect
|
|
||||||
// if (
|
|
||||||
// currentCode.includes("useEffect") &&
|
|
||||||
// currentCode.includes(selectedComponent.id)
|
|
||||||
// ) {
|
|
||||||
// hooks.add("useEffect");
|
|
||||||
// }
|
|
||||||
|
|
||||||
setActiveHooks(hooks);
|
|
||||||
}
|
|
||||||
}, [selectedComponent, currentCode]);
|
|
||||||
|
|
||||||
// Handle local property changes
|
|
||||||
const handleLocalPropertyChange = (propName: string, value: any) => {
|
|
||||||
setPendingProperties((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[propName]: value,
|
|
||||||
}));
|
|
||||||
setHasChanges(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle local hook changes
|
|
||||||
const handleLocalHookToggle = (hookType: string, enabled: boolean) => {
|
|
||||||
setPendingHooks((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[hookType]: enabled,
|
|
||||||
}));
|
|
||||||
setHasHookChanges(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Apply only property/event changes
|
|
||||||
const handleApplyPropChanges = () => {
|
|
||||||
if (!selectedComponent) return;
|
|
||||||
|
|
||||||
// Combine all changes into a single update object
|
|
||||||
const allUpdates = {
|
|
||||||
...pendingProperties,
|
|
||||||
...pendingEvents,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Apply property and event changes together
|
|
||||||
if (Object.keys(allUpdates).length > 0) {
|
|
||||||
onPropertiesChange(selectedComponent.id, allUpdates);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset pending changes
|
|
||||||
setPendingProperties({});
|
|
||||||
setPendingEvents({});
|
|
||||||
setHasChanges(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Apply only hook changes
|
|
||||||
const handleApplyHookChanges = () => {
|
|
||||||
if (!selectedComponent) return;
|
|
||||||
|
|
||||||
const hookToggles = Object.entries(pendingHooks).map(
|
|
||||||
([hookType, enabled]) => ({
|
|
||||||
componentId: selectedComponent.id,
|
|
||||||
hookType,
|
|
||||||
enabled,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
if (hookToggles.length > 1) {
|
|
||||||
onMultipleHookToggle(hookToggles);
|
|
||||||
} else if (hookToggles.length === 1) {
|
|
||||||
const { componentId, hookType, enabled } = hookToggles[0];
|
|
||||||
onHookToggle(componentId, hookType, enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset pending changes
|
|
||||||
setPendingHooks({});
|
|
||||||
setHasHookChanges(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reset all pending changes
|
|
||||||
const handleResetChanges = () => {
|
|
||||||
setPendingProperties({});
|
|
||||||
setPendingEvents({});
|
|
||||||
setPendingHooks({});
|
|
||||||
setHasChanges(false);
|
|
||||||
setHasHookChanges(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const openTailwindModal = (propertyName: string) => {
|
|
||||||
setCurrentTailwindProperty(propertyName);
|
|
||||||
setTailwindModalOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleTailwindClassSelect = (className: string) => {
|
|
||||||
const currentValue =
|
|
||||||
pendingProperties[currentTailwindProperty] ||
|
|
||||||
selectedComponent?.props[currentTailwindProperty] ||
|
|
||||||
"";
|
|
||||||
const newValue = currentValue ? `${currentValue} ${className}` : className;
|
|
||||||
handleLocalPropertyChange(currentTailwindProperty, newValue);
|
|
||||||
// Don't close modal - let user continue selecting
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderPropertyControl = (property: PropertyInfo) => {
|
|
||||||
// Handle children property specially - get from children, not props
|
|
||||||
const currentValue =
|
|
||||||
property.name === "children"
|
|
||||||
? typeof selectedComponent?.children === "string"
|
|
||||||
? selectedComponent.children
|
|
||||||
: selectedComponent?.props[property.name] || property.value
|
|
||||||
: selectedComponent?.props[property.name] || property.value;
|
|
||||||
|
|
||||||
// Use pending value if available, otherwise use current value
|
|
||||||
const value =
|
|
||||||
pendingProperties[property.name] !== undefined
|
|
||||||
? pendingProperties[property.name]
|
|
||||||
: currentValue;
|
|
||||||
const isTailwindProperty = ["className", "class", "css"].includes(
|
|
||||||
property.name
|
|
||||||
);
|
|
||||||
const isColorProperty = property.name.toLowerCase().includes("color");
|
|
||||||
|
|
||||||
// Don't show children property if component has nested elements
|
|
||||||
if (
|
|
||||||
property.name === "children" &&
|
|
||||||
Array.isArray(selectedComponent?.children)
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let arrayError = "";
|
|
||||||
let arrayInputValue = "";
|
|
||||||
if (property.type === "array") {
|
|
||||||
try {
|
|
||||||
arrayInputValue = JSON.stringify(value, null, 2);
|
|
||||||
} catch {
|
|
||||||
arrayInputValue = "";
|
|
||||||
arrayError = "Array verisi gösterilemiyor.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div key={property.name} className="mb-4">
|
|
||||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-200 mb-2">
|
|
||||||
{property.name}
|
|
||||||
{property.description && (
|
|
||||||
<span className="text-gray-500 dark:text-gray-400 text-xs ml-1">
|
|
||||||
({property.description})
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{pendingProperties[property.name] !== undefined && (
|
|
||||||
<span className="text-orange-500 text-xs ml-1">(değiştirildi)</span>
|
|
||||||
)}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div className="flex gap-2 flex-col">
|
|
||||||
{property.type === "boolean" && (
|
|
||||||
<label className="flex items-center">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={Boolean(value)}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleLocalPropertyChange(property.name, e.target.checked)
|
|
||||||
}
|
|
||||||
className="mr-2"
|
|
||||||
/>
|
|
||||||
<span className="text-sm text-gray-600 dark:text-gray-300">{property.name}</span>
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{property.type === "select" && property.options && (
|
|
||||||
<select
|
|
||||||
value={value || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleLocalPropertyChange(property.name, e.target.value)
|
|
||||||
}
|
|
||||||
className="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white dark:placeholder-gray-400"
|
|
||||||
>
|
|
||||||
<option value="">Select {property.name}</option>
|
|
||||||
{property.options.map((option) => (
|
|
||||||
<option key={option} value={option}>
|
|
||||||
{option}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{property.type === "function" && (
|
|
||||||
<>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={value || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleLocalPropertyChange(property.name, e.target.value)
|
|
||||||
}
|
|
||||||
className="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white dark:placeholder-gray-400"
|
|
||||||
placeholder={`Enter ${property.name}`}
|
|
||||||
/>
|
|
||||||
{isTailwindProperty && (
|
|
||||||
<Button
|
|
||||||
onClick={() => openTailwindModal(property.name)}
|
|
||||||
icon={<FaCode className="w-3 h-3" />}
|
|
||||||
variant="solid"
|
|
||||||
size="xs"
|
|
||||||
title="Select Tailwind Classes"
|
|
||||||
>
|
|
||||||
TW
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{isColorProperty && (
|
|
||||||
<input
|
|
||||||
type="color"
|
|
||||||
value={value || "#000000"}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleLocalPropertyChange(property.name, e.target.value)
|
|
||||||
}
|
|
||||||
className="w-10 h-10 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{property.type === "string" && (
|
|
||||||
<>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={value || ""}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleLocalPropertyChange(property.name, e.target.value)
|
|
||||||
}
|
|
||||||
className="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white dark:placeholder-gray-400"
|
|
||||||
placeholder={`Enter ${property.name}`}
|
|
||||||
/>
|
|
||||||
{isTailwindProperty && (
|
|
||||||
<Button
|
|
||||||
onClick={() => openTailwindModal(property.name)}
|
|
||||||
icon={<FaCode className="w-3 h-3" />}
|
|
||||||
variant="solid"
|
|
||||||
size="xs"
|
|
||||||
title="Select Tailwind Classes"
|
|
||||||
>
|
|
||||||
TW
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{isColorProperty && (
|
|
||||||
<input
|
|
||||||
type="color"
|
|
||||||
value={value || "#000000"}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleLocalPropertyChange(property.name, e.target.value)
|
|
||||||
}
|
|
||||||
className="w-10 h-10 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{property.type === "number" && (
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
value={value || 0}
|
|
||||||
onChange={(e) =>
|
|
||||||
handleLocalPropertyChange(property.name, Number(e.target.value))
|
|
||||||
}
|
|
||||||
className="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white dark:placeholder-gray-400"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{property.type === "array" && (
|
|
||||||
<>
|
|
||||||
<textarea
|
|
||||||
className="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md font-mono text-xs focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white dark:placeholder-gray-400"
|
|
||||||
rows={Math.max(3, arrayInputValue.split('\n').length)}
|
|
||||||
value={arrayInputValue}
|
|
||||||
onChange={(e) => {
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(e.target.value);
|
|
||||||
handleLocalPropertyChange(property.name, parsed);
|
|
||||||
arrayError = "";
|
|
||||||
} catch {
|
|
||||||
arrayError = "Geçersiz JSON formatı";
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
placeholder="[\n { ... }\n]"
|
|
||||||
spellCheck={false}
|
|
||||||
/>
|
|
||||||
{arrayError && (
|
|
||||||
<span className="text-xs text-red-500">{arrayError}</span>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderHookControl = (hook: HookInfo) => {
|
|
||||||
const currentlyActive = activeHooks.has(hook.type);
|
|
||||||
const isActive =
|
|
||||||
pendingHooks[hook.type] !== undefined
|
|
||||||
? pendingHooks[hook.type]
|
|
||||||
: currentlyActive;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={hook.name} className="mb-4">
|
|
||||||
<label className="flex items-center">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={isActive}
|
|
||||||
onChange={(e) => handleLocalHookToggle(hook.type, e.target.checked)}
|
|
||||||
className="mr-2"
|
|
||||||
/>
|
|
||||||
<span className="text-sm font-medium text-gray-700">
|
|
||||||
{hook.name} ({hook.type})
|
|
||||||
</span>
|
|
||||||
{pendingHooks[hook.type] !== undefined && (
|
|
||||||
<span className="text-orange-500 text-xs ml-1">(değiştirildi)</span>
|
|
||||||
)}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!selectedComponent) {
|
|
||||||
return (
|
|
||||||
<div className="h-full bg-gray-50 dark:bg-gray-900 p-4">
|
|
||||||
<div className="text-center text-gray-500 dark:text-gray-400 mt-8">
|
|
||||||
<div className="text-4xl mb-4">🎯</div>
|
|
||||||
<h3 className="text-lg font-medium mb-2 text-gray-700 dark:text-gray-200">No Component Selected</h3>
|
|
||||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
|
||||||
Select a component from the editor to edit its properties
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDynamicProperties(): PropertyInfo[] {
|
|
||||||
if (!selectedComponent) return [];
|
|
||||||
|
|
||||||
const allDefinitionProps = componentDefinition?.properties || [];
|
|
||||||
|
|
||||||
// Sadece tanımdan gelen properties
|
|
||||||
const defProps = allDefinitionProps.filter(
|
|
||||||
(p: any) => p.category === "properties"
|
|
||||||
);
|
|
||||||
|
|
||||||
// allDefinitionNames prop isimlerini belirle
|
|
||||||
const allDefinitionNames = new Set(allDefinitionProps.map((p:any) => p.name));
|
|
||||||
|
|
||||||
// Koddan gelen tüm props (id hariç), styling hariç, events hariç
|
|
||||||
const codeProps = Object.entries(selectedComponent.props || {})
|
|
||||||
.filter(([name]) => name !== "id" && !allDefinitionNames.has(name))
|
|
||||||
.map(([name, value]) => {
|
|
||||||
let type: PropertyInfo["type"] = "string";
|
|
||||||
if (typeof value === "boolean") type = "boolean";
|
|
||||||
else if (typeof value === "number") type = "number";
|
|
||||||
else if (typeof value === "function") type = "function";
|
|
||||||
else if (Array.isArray(value)) type = "array";
|
|
||||||
else if (typeof value === "object" && value !== null) type = "object";
|
|
||||||
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
type,
|
|
||||||
value,
|
|
||||||
category: "properties",
|
|
||||||
} as PropertyInfo;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Merge - öncelik kodda olanlar
|
|
||||||
const merged: PropertyInfo[] = [];
|
|
||||||
const seen = new Set<string>();
|
|
||||||
|
|
||||||
for (const prop of codeProps) {
|
|
||||||
merged.push(prop);
|
|
||||||
seen.add(prop.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const prop of defProps) {
|
|
||||||
if (!seen.has(prop.name)) {
|
|
||||||
merged.push(prop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return merged;
|
|
||||||
}
|
|
||||||
|
|
||||||
const properties = getDynamicProperties();
|
|
||||||
const hooks = componentDefinition?.hooks || [];
|
|
||||||
const styling =
|
|
||||||
componentDefinition?.properties?.filter((p: any) => p.category === "styling") ||
|
|
||||||
[];
|
|
||||||
const events =
|
|
||||||
componentDefinition?.properties?.filter((p: any) => p.category === "events") ||
|
|
||||||
[];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-full text-white flex flex-col h-full">
|
|
||||||
{/* Header */}
|
|
||||||
<div className="border-b bg-gray-50 dark:bg-gray-900 flex items-center justify-between dark:border-gray-700">
|
|
||||||
<div>
|
|
||||||
{(hasChanges || hasHookChanges) && (
|
|
||||||
<p className="text-sm text-orange-600 mt-1">
|
|
||||||
Bekleyen değişiklikler var
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{/* Tabs */}
|
|
||||||
<div className="flex gap-2 p-1">
|
|
||||||
<Button
|
|
||||||
variant={activeTab === "props" ? "twoTone" : "plain"}
|
|
||||||
size="xs"
|
|
||||||
onClick={() => setActiveTab("props")}
|
|
||||||
>
|
|
||||||
Properties
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant={activeTab === "hooks" ? "twoTone" : "plain"}
|
|
||||||
size="xs"
|
|
||||||
onClick={() => setActiveTab("hooks")}
|
|
||||||
>
|
|
||||||
Hooks
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* Sil Butonu */}
|
|
||||||
<Button
|
|
||||||
variant="solid"
|
|
||||||
color="red-600"
|
|
||||||
size="xs"
|
|
||||||
icon={<FaTrash className="w-4 h-4" />}
|
|
||||||
className="mr-2"
|
|
||||||
onClick={() => {
|
|
||||||
if (selectedComponent) {
|
|
||||||
if (
|
|
||||||
window.confirm(
|
|
||||||
"Seçili komponenti silmek istediğinize emin misiniz?"
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
onDeleteComponent(selectedComponent.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
title="Komponenti Sil"
|
|
||||||
>
|
|
||||||
Sil
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Footer Action Buttons - her iki tabda da sabit */}
|
|
||||||
<div className="p-4 border-t">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="solid"
|
|
||||||
color="green-600"
|
|
||||||
icon={<FaCheck className="w-4 h-4" />}
|
|
||||||
onClick={
|
|
||||||
activeTab === "props"
|
|
||||||
? handleApplyPropChanges
|
|
||||||
: handleApplyHookChanges
|
|
||||||
}
|
|
||||||
disabled={activeTab === "props" ? !hasChanges : !hasHookChanges}
|
|
||||||
className="flex-1"
|
|
||||||
>
|
|
||||||
Uygula
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="solid"
|
|
||||||
color="red-600"
|
|
||||||
icon={<FaTimes className="w-4 h-4" />}
|
|
||||||
onClick={
|
|
||||||
activeTab === "props"
|
|
||||||
? handleResetChanges
|
|
||||||
: () => {
|
|
||||||
setPendingHooks({});
|
|
||||||
setHasHookChanges(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
disabled={activeTab === "props" ? !hasChanges : !hasHookChanges}
|
|
||||||
className="flex-1"
|
|
||||||
>
|
|
||||||
İptal
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Content */}
|
|
||||||
{activeTab === "props" && (
|
|
||||||
<div className="flex-1 text-black dark:text-gray-200 overflow-y-auto p-4 max-h-[calc(100vh-200px)] bg-white dark:bg-gray-900">
|
|
||||||
<h3 className="text-md font-medium text-gray-800 dark:text-gray-100 mb-4">Properties</h3>
|
|
||||||
{/* Properties */}
|
|
||||||
{properties.length > 0 && (
|
|
||||||
<div>{properties.map(renderPropertyControl)}</div>
|
|
||||||
)}
|
|
||||||
{/* Events */}
|
|
||||||
{events.length > 0 && (
|
|
||||||
<div>
|
|
||||||
<h3 className="text-md font-medium text-gray-800 dark:text-gray-100 mb-4 mt-6">
|
|
||||||
Events
|
|
||||||
</h3>
|
|
||||||
{events.map(renderPropertyControl)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{/* Styling */}
|
|
||||||
{styling.length > 0 && (
|
|
||||||
<div>
|
|
||||||
<h3 className="text-md font-medium text-gray-800 dark:text-gray-100 mb-4 mt-6">
|
|
||||||
Styling
|
|
||||||
</h3>
|
|
||||||
{styling.map(renderPropertyControl)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{activeTab === "hooks" && (
|
|
||||||
<div className="flex-1 overflow-y-auto p-4">
|
|
||||||
{/* Sadece useState ve useRef göster */}
|
|
||||||
{hooks
|
|
||||||
.filter((h: any) => h.type === "useState" || h.type === "useRef")
|
|
||||||
.map(renderHookControl)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Tailwind Modal */}
|
|
||||||
<TailwindModal
|
|
||||||
isOpen={tailwindModalOpen}
|
|
||||||
onClose={() => setTailwindModalOpen(false)}
|
|
||||||
onSelectClass={handleTailwindClassSelect}
|
|
||||||
currentValue={
|
|
||||||
pendingProperties[currentTailwindProperty] ||
|
|
||||||
(selectedComponent &&
|
|
||||||
selectedComponent.props[currentTailwindProperty]) ||
|
|
||||||
""
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default PropertyPanel;
|
|
||||||
|
|
@ -1,150 +1,79 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useMemo, useState } from 'react'
|
||||||
import { searchTailwindClasses, TAILWIND_CLASSES } from './data/tailwindClasses';
|
import { FaSearch, FaTimes } from 'react-icons/fa'
|
||||||
import { Button } from '../ui';
|
import { Button } from '@/components/ui'
|
||||||
import { FaTimes } from 'react-icons/fa';
|
|
||||||
|
|
||||||
interface TailwindModalProps {
|
interface TailwindModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean
|
||||||
onClose: () => void;
|
currentValue?: string
|
||||||
onSelectClass: (className: string) => void;
|
onClose: () => void
|
||||||
currentValue?: string;
|
onSelectClass: (className: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CLASS_GROUPS = {
|
||||||
|
Layout: ['block', 'inline-block', 'flex', 'inline-flex', 'grid', 'hidden', 'relative', 'absolute', 'fixed', 'sticky', 'overflow-auto', 'overflow-hidden'],
|
||||||
|
Flex: ['flex-row', 'flex-col', 'flex-wrap', 'items-start', 'items-center', 'items-end', 'items-stretch', 'justify-start', 'justify-center', 'justify-between', 'justify-end', 'gap-1', 'gap-2', 'gap-3', 'gap-4', 'gap-6', 'gap-8'],
|
||||||
|
Grid: ['grid-cols-1', 'grid-cols-2', 'grid-cols-3', 'grid-cols-4', 'grid-cols-6', 'grid-cols-12', 'col-span-1', 'col-span-2', 'col-span-3', 'col-span-6', 'col-span-12'],
|
||||||
|
Spacing: ['m-0', 'm-1', 'm-2', 'm-3', 'm-4', 'm-auto', 'mx-auto', 'my-2', 'my-4', 'p-0', 'p-1', 'p-2', 'p-3', 'p-4', 'p-6', 'p-8', 'px-2', 'px-3', 'px-4', 'px-6', 'py-1', 'py-2', 'py-3', 'py-4'],
|
||||||
|
Sizing: ['w-auto', 'w-full', 'w-1/2', 'w-1/3', 'w-2/3', 'h-auto', 'h-full', 'min-w-0', 'min-h-0', 'max-w-sm', 'max-w-md', 'max-w-lg', 'max-w-xl', 'max-w-full'],
|
||||||
|
Typography: ['text-xs', 'text-sm', 'text-base', 'text-lg', 'text-xl', 'text-2xl', 'font-normal', 'font-medium', 'font-semibold', 'font-bold', 'text-left', 'text-center', 'text-right', 'truncate', 'whitespace-nowrap'],
|
||||||
|
Colors: ['text-white', 'text-black', 'text-slate-500', 'text-slate-700', 'text-slate-900', 'bg-white', 'bg-slate-50', 'bg-slate-100', 'bg-slate-800', 'bg-slate-900', 'bg-sky-500', 'bg-blue-600', 'bg-red-500', 'bg-emerald-500'],
|
||||||
|
Border: ['border', 'border-0', 'border-2', 'border-slate-200', 'border-slate-700', 'rounded', 'rounded-md', 'rounded-lg', 'rounded-xl', 'rounded-full', 'shadow-sm', 'shadow', 'shadow-lg'],
|
||||||
|
State: ['cursor-pointer', 'select-none', 'transition', 'hover:opacity-80', 'hover:bg-slate-100', 'dark:bg-slate-900', 'dark:text-white', 'dark:border-slate-700'],
|
||||||
|
} as const
|
||||||
|
|
||||||
const TailwindModal: React.FC<TailwindModalProps> = ({
|
const TailwindModal: React.FC<TailwindModalProps> = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
|
currentValue = '',
|
||||||
onClose,
|
onClose,
|
||||||
onSelectClass,
|
onSelectClass,
|
||||||
currentValue = ''
|
|
||||||
}) => {
|
}) => {
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [search, setSearch] = useState('')
|
||||||
const [selectedCategory, setSelectedCategory] = useState('All');
|
const [category, setCategory] = useState<keyof typeof CLASS_GROUPS | 'Tümü'>('Tümü')
|
||||||
const [filteredClasses, setFilteredClasses] = useState<string[]>([]);
|
const selectedClasses = useMemo(() => new Set(currentValue.split(/\s+/).filter(Boolean)), [currentValue])
|
||||||
|
const classes = useMemo(() => {
|
||||||
|
const source = category === 'Tümü' ? Object.values(CLASS_GROUPS).flat() : CLASS_GROUPS[category]
|
||||||
|
const query = search.trim().toLowerCase()
|
||||||
|
return [...new Set(source)].filter((className) => !query || className.toLowerCase().includes(query))
|
||||||
|
}, [category, search])
|
||||||
|
|
||||||
useEffect(() => {
|
if (!isOpen) return null
|
||||||
if (searchTerm) {
|
|
||||||
const searchResults = searchTailwindClasses(searchTerm);
|
|
||||||
// Remove duplicates using Set
|
|
||||||
setFilteredClasses([...new Set(searchResults)]);
|
|
||||||
} else if (selectedCategory === 'All') {
|
|
||||||
const allClasses = TAILWIND_CLASSES.flatMap(cat => cat.classes);
|
|
||||||
// Remove duplicates using Set
|
|
||||||
setFilteredClasses([...new Set(allClasses)]);
|
|
||||||
} else {
|
|
||||||
const category = TAILWIND_CLASSES.find(cat => cat.category === selectedCategory);
|
|
||||||
setFilteredClasses(category ? [...new Set(category.classes)] : []);
|
|
||||||
}
|
|
||||||
}, [searchTerm, selectedCategory]);
|
|
||||||
|
|
||||||
const handleClassSelect = (className: string) => {
|
|
||||||
onSelectClass(className);
|
|
||||||
// Don't close modal - let user select multiple classes
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!isOpen) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<div className="fixed inset-0 z-[100] flex items-center justify-center bg-black/60 p-4" onMouseDown={onClose}>
|
||||||
<div className="bg-white rounded-lg shadow-2xl w-4/5 h-4/5 max-w-4xl max-h-4xl flex flex-col">
|
<div className="flex max-h-[85vh] w-full max-w-4xl flex-col overflow-hidden rounded-xl bg-white shadow-2xl dark:bg-slate-900" onMouseDown={(event) => event.stopPropagation()}>
|
||||||
{/* Header */}
|
<div className="flex items-center justify-between border-b border-slate-200 p-4 dark:border-slate-700">
|
||||||
<div className="flex items-center justify-between p-4 border-b">
|
<div>
|
||||||
<h2 className="text-xl font-semibold text-gray-800">Tailwind CSS Classes</h2>
|
<h2 className="font-semibold text-slate-900 dark:text-white">Tailwind CSS sınıfları</h2>
|
||||||
<Button
|
<p className="mt-1 text-xs text-slate-500">Birden fazla sınıf seçebilirsiniz.</p>
|
||||||
onClick={onClose}
|
|
||||||
icon={<FaTimes className="w-5 h-5" />}
|
|
||||||
variant="plain"
|
|
||||||
size="xs"
|
|
||||||
title="Close"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<Button icon={<FaTimes />} size="xs" variant="plain" onClick={onClose} />
|
||||||
{/* Search and Filter */}
|
</div>
|
||||||
<div className="p-4 border-b bg-gray-50 text-black">
|
<div className="flex gap-3 border-b border-slate-200 p-4 dark:border-slate-700">
|
||||||
<div className="flex gap-4 mb-4">
|
<label className="relative flex-1">
|
||||||
<input
|
<FaSearch className="absolute left-3 top-3 text-slate-400" />
|
||||||
type="text"
|
<input className="w-full rounded-md border border-slate-300 bg-white py-2 pl-9 pr-3 text-sm dark:border-slate-700 dark:bg-slate-950 dark:text-white" placeholder="Sınıf ara…" value={search} onChange={(event) => setSearch(event.target.value)} />
|
||||||
placeholder="Search classes..."
|
</label>
|
||||||
value={searchTerm}
|
<select className="rounded-md border border-slate-300 bg-white px-3 text-sm dark:border-slate-700 dark:bg-slate-950 dark:text-white" value={category} onChange={(event) => setCategory(event.target.value as typeof category)}>
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
<option value="Tümü">Tüm kategoriler</option>
|
||||||
className="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
{Object.keys(CLASS_GROUPS).map((name) => <option key={name} value={name}>{name}</option>)}
|
||||||
/>
|
|
||||||
<select
|
|
||||||
value={selectedCategory}
|
|
||||||
onChange={(e) => setSelectedCategory(e.target.value)}
|
|
||||||
className="px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
>
|
|
||||||
<option value="All">All Categories</option>
|
|
||||||
{TAILWIND_CLASSES.map(category => (
|
|
||||||
<option key={category.category} value={category.category}>
|
|
||||||
{category.category}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="border-b border-slate-200 bg-slate-50 px-4 py-3 text-xs dark:border-slate-700 dark:bg-slate-950">
|
||||||
{/* Current Value Display */}
|
<span className="font-semibold text-slate-600 dark:text-slate-300">className: </span>
|
||||||
{currentValue && (
|
<code className="break-all text-sky-600 dark:text-sky-400">{currentValue || '—'}</code>
|
||||||
<div className="mb-4">
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
||||||
Current Value:
|
|
||||||
</label>
|
|
||||||
<div className="bg-gray-100 p-2 rounded border text-sm font-mono">
|
|
||||||
{currentValue}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Class List */}
|
|
||||||
<div className="flex-1 overflow-hidden">
|
|
||||||
<div className="h-full overflow-y-auto p-4">
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2">
|
|
||||||
{filteredClasses.map((className, index) => (
|
|
||||||
<div
|
|
||||||
key={`${className}-${index}`}
|
|
||||||
className="group relative"
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
block
|
|
||||||
variant="solid"
|
|
||||||
size="xs"
|
|
||||||
onClick={() => handleClassSelect(className)}
|
|
||||||
className="justify-start"
|
|
||||||
>
|
|
||||||
<span className="font-mono text-xs text-gray-600">
|
|
||||||
{className}
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="grid flex-1 grid-cols-2 gap-2 overflow-y-auto p-4 sm:grid-cols-3 lg:grid-cols-4">
|
||||||
|
{classes.map((className) => (
|
||||||
|
<button key={className} className={`rounded-md border px-3 py-2 text-left font-mono text-xs transition ${selectedClasses.has(className) ? 'border-sky-500 bg-sky-50 text-sky-700 dark:bg-sky-950 dark:text-sky-300' : 'border-slate-200 text-slate-600 hover:border-sky-300 dark:border-slate-700 dark:text-slate-300'}`} type="button" onClick={() => onSelectClass(className)}>{className}</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex items-center justify-between border-t border-slate-200 p-4 dark:border-slate-700">
|
||||||
|
<span className="text-xs text-slate-500">{classes.length} sınıf</span>
|
||||||
|
<div className="flex gap-2"><Button size="sm" variant="default" onClick={() => onSelectClass('')}>Temizle</Button><Button size="sm" variant="solid" onClick={onClose}>Tamam</Button></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
{/* Footer */}
|
export default TailwindModal
|
||||||
<div className="p-4 border-t bg-gray-50 flex justify-between">
|
|
||||||
<div className="text-sm text-gray-600">
|
|
||||||
{filteredClasses.length} classes found
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Button
|
|
||||||
variant="default"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => onSelectClass('')}
|
|
||||||
>
|
|
||||||
Clear
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="solid"
|
|
||||||
size="sm"
|
|
||||||
onClick={onClose}
|
|
||||||
>
|
|
||||||
Close
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TailwindModal;
|
|
||||||
|
|
|
||||||
|
|
@ -1763,7 +1763,6 @@ export const CUSTOM_COMPONENTS: ComponentDefinition[] = [
|
||||||
hooks: [],
|
hooks: [],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const HTML_ELEMENTS: ComponentDefinition[] = [
|
export const HTML_ELEMENTS: ComponentDefinition[] = [
|
||||||
{
|
{
|
||||||
name: "div",
|
name: "div",
|
||||||
|
|
@ -2203,13 +2202,13 @@ export const HTML_ELEMENTS: ComponentDefinition[] = [
|
||||||
name: "className",
|
name: "className",
|
||||||
type: "string",
|
type: "string",
|
||||||
value: "",
|
value: "",
|
||||||
category: "properties",
|
category: "styling",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "onClick",
|
name: "onClick",
|
||||||
type: "function",
|
type: "function",
|
||||||
value: "",
|
value: "",
|
||||||
category: "properties",
|
category: "events",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
hooks: [
|
hooks: [
|
||||||
|
|
@ -2220,6 +2219,62 @@ export const HTML_ELEMENTS: ComponentDefinition[] = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "h4",
|
||||||
|
icon: "Heading4",
|
||||||
|
category: "basic",
|
||||||
|
description: "Başlık metni (h4)",
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
name: "children",
|
||||||
|
type: "string",
|
||||||
|
value: "",
|
||||||
|
description: "Heading text",
|
||||||
|
category: "properties",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "className",
|
||||||
|
type: "string",
|
||||||
|
value: "",
|
||||||
|
category: "styling",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "onClick",
|
||||||
|
type: "function",
|
||||||
|
value: "",
|
||||||
|
category: "events",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
hooks: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "h5",
|
||||||
|
icon: "Heading5",
|
||||||
|
category: "basic",
|
||||||
|
description: "Başlık metni (h5)",
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
name: "children",
|
||||||
|
type: "string",
|
||||||
|
value: "",
|
||||||
|
description: "Heading text",
|
||||||
|
category: "properties",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "className",
|
||||||
|
type: "string",
|
||||||
|
value: "",
|
||||||
|
category: "styling",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "onClick",
|
||||||
|
type: "function",
|
||||||
|
value: "",
|
||||||
|
category: "events",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
hooks: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "img",
|
name: "img",
|
||||||
icon: "Image",
|
icon: "Image",
|
||||||
|
|
@ -2278,19 +2333,3 @@ export const HTML_ELEMENTS: ComponentDefinition[] = [
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const getAllComponentDefinitions = (): ComponentDefinition[] => {
|
|
||||||
//return [...HTML_ELEMENTS, ...CUSTOM_COMPONENTS];
|
|
||||||
return [...CUSTOM_COMPONENTS];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getComponentDefinition = (
|
|
||||||
name: string
|
|
||||||
): ComponentDefinition | undefined => {
|
|
||||||
return getAllComponentDefinitions().find((comp) => comp.name === name);
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface PanelState {
|
|
||||||
toolbox: boolean;
|
|
||||||
properties: boolean;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,297 +0,0 @@
|
||||||
import { TailwindClass } from "../../../proxy/developerKit/componentInfo";
|
|
||||||
|
|
||||||
export const TAILWIND_CLASSES: TailwindClass[] = [
|
|
||||||
{
|
|
||||||
category: 'Layout',
|
|
||||||
classes: [
|
|
||||||
'container', 'box-border', 'box-content',
|
|
||||||
'block', 'inline-block', 'inline', 'flex', 'inline-flex', 'table', 'inline-table',
|
|
||||||
'table-caption', 'table-cell', 'table-column', 'table-column-group',
|
|
||||||
'table-footer-group', 'table-header-group', 'table-row-group', 'table-row',
|
|
||||||
'flow-root', 'grid', 'inline-grid', 'contents', 'list-item', 'hidden'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: 'Flexbox & Grid',
|
|
||||||
classes: [
|
|
||||||
'basis-0', 'basis-1', 'basis-2', 'basis-3', 'basis-4', 'basis-5', 'basis-6',
|
|
||||||
'basis-auto', 'basis-px', 'basis-full',
|
|
||||||
'flex-row', 'flex-row-reverse', 'flex-col', 'flex-col-reverse',
|
|
||||||
'flex-wrap', 'flex-wrap-reverse', 'flex-nowrap',
|
|
||||||
'flex-1', 'flex-auto', 'flex-initial', 'flex-none',
|
|
||||||
'grow', 'grow-0', 'shrink', 'shrink-0',
|
|
||||||
'order-1', 'order-2', 'order-3', 'order-4', 'order-5', 'order-6',
|
|
||||||
'order-7', 'order-8', 'order-9', 'order-10', 'order-11', 'order-12',
|
|
||||||
'order-first', 'order-last', 'order-none',
|
|
||||||
'grid-cols-1', 'grid-cols-2', 'grid-cols-3', 'grid-cols-4', 'grid-cols-5',
|
|
||||||
'grid-cols-6', 'grid-cols-7', 'grid-cols-8', 'grid-cols-9', 'grid-cols-10',
|
|
||||||
'grid-cols-11', 'grid-cols-12', 'grid-cols-none',
|
|
||||||
'col-auto', 'col-span-1', 'col-span-2', 'col-span-3', 'col-span-4',
|
|
||||||
'col-span-5', 'col-span-6', 'col-span-7', 'col-span-8', 'col-span-9',
|
|
||||||
'col-span-10', 'col-span-11', 'col-span-12', 'col-span-full',
|
|
||||||
'grid-rows-1', 'grid-rows-2', 'grid-rows-3', 'grid-rows-4', 'grid-rows-5',
|
|
||||||
'grid-rows-6', 'grid-rows-none',
|
|
||||||
'row-auto', 'row-span-1', 'row-span-2', 'row-span-3', 'row-span-4',
|
|
||||||
'row-span-5', 'row-span-6', 'row-span-full',
|
|
||||||
'gap-0', 'gap-1', 'gap-2', 'gap-3', 'gap-4', 'gap-5', 'gap-6', 'gap-7',
|
|
||||||
'gap-8', 'gap-9', 'gap-10', 'gap-11', 'gap-12', 'gap-14', 'gap-16',
|
|
||||||
'gap-20', 'gap-24', 'gap-28', 'gap-32', 'gap-36', 'gap-40', 'gap-44',
|
|
||||||
'gap-48', 'gap-52', 'gap-56', 'gap-60', 'gap-64', 'gap-72', 'gap-80',
|
|
||||||
'gap-96', 'gap-px'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: 'Spacing',
|
|
||||||
classes: [
|
|
||||||
'm-0', 'm-1', 'm-2', 'm-3', 'm-4', 'm-5', 'm-6', 'm-7', 'm-8', 'm-9',
|
|
||||||
'm-10', 'm-11', 'm-12', 'm-14', 'm-16', 'm-20', 'm-24', 'm-28', 'm-32',
|
|
||||||
'm-36', 'm-40', 'm-44', 'm-48', 'm-52', 'm-56', 'm-60', 'm-64', 'm-72',
|
|
||||||
'm-80', 'm-96', 'm-auto', 'm-px',
|
|
||||||
'mx-0', 'mx-1', 'mx-2', 'mx-3', 'mx-4', 'mx-5', 'mx-6', 'mx-7', 'mx-8',
|
|
||||||
'mx-9', 'mx-10', 'mx-11', 'mx-12', 'mx-14', 'mx-16', 'mx-20', 'mx-24',
|
|
||||||
'mx-28', 'mx-32', 'mx-36', 'mx-40', 'mx-44', 'mx-48', 'mx-52', 'mx-56',
|
|
||||||
'mx-60', 'mx-64', 'mx-72', 'mx-80', 'mx-96', 'mx-auto', 'mx-px',
|
|
||||||
'my-0', 'my-1', 'my-2', 'my-3', 'my-4', 'my-5', 'my-6', 'my-7', 'my-8',
|
|
||||||
'my-9', 'my-10', 'my-11', 'my-12', 'my-14', 'my-16', 'my-20', 'my-24',
|
|
||||||
'my-28', 'my-32', 'my-36', 'my-40', 'my-44', 'my-48', 'my-52', 'my-56',
|
|
||||||
'my-60', 'my-64', 'my-72', 'my-80', 'my-96', 'my-auto', 'my-px',
|
|
||||||
'p-0', 'p-1', 'p-2', 'p-3', 'p-4', 'p-5', 'p-6', 'p-7', 'p-8', 'p-9',
|
|
||||||
'p-10', 'p-11', 'p-12', 'p-14', 'p-16', 'p-20', 'p-24', 'p-28', 'p-32',
|
|
||||||
'p-36', 'p-40', 'p-44', 'p-48', 'p-52', 'p-56', 'p-60', 'p-64', 'p-72',
|
|
||||||
'p-80', 'p-96', 'p-px',
|
|
||||||
'px-0', 'px-1', 'px-2', 'px-3', 'px-4', 'px-5', 'px-6', 'px-7', 'px-8',
|
|
||||||
'px-9', 'px-10', 'px-11', 'px-12', 'px-14', 'px-16', 'px-20', 'px-24',
|
|
||||||
'px-28', 'px-32', 'px-36', 'px-40', 'px-44', 'px-48', 'px-52', 'px-56',
|
|
||||||
'px-60', 'px-64', 'px-72', 'px-80', 'px-96', 'px-px',
|
|
||||||
'py-0', 'py-1', 'py-2', 'py-3', 'py-4', 'py-5', 'py-6', 'py-7', 'py-8',
|
|
||||||
'py-9', 'py-10', 'py-11', 'py-12', 'py-14', 'py-16', 'py-20', 'py-24',
|
|
||||||
'py-28', 'py-32', 'py-36', 'py-40', 'py-44', 'py-48', 'py-52', 'py-56',
|
|
||||||
'py-60', 'py-64', 'py-72', 'py-80', 'py-96', 'py-px'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: 'Sizing',
|
|
||||||
classes: [
|
|
||||||
'w-0', 'w-1', 'w-2', 'w-3', 'w-4', 'w-5', 'w-6', 'w-7', 'w-8', 'w-9',
|
|
||||||
'w-10', 'w-11', 'w-12', 'w-14', 'w-16', 'w-20', 'w-24', 'w-28', 'w-32',
|
|
||||||
'w-36', 'w-40', 'w-44', 'w-48', 'w-52', 'w-56', 'w-60', 'w-64', 'w-72',
|
|
||||||
'w-80', 'w-96', 'w-auto', 'w-px', 'w-0.5', 'w-1.5', 'w-2.5', 'w-3.5',
|
|
||||||
'w-1/2', 'w-1/3', 'w-2/3', 'w-1/4', 'w-2/4', 'w-3/4', 'w-1/5', 'w-2/5',
|
|
||||||
'w-3/5', 'w-4/5', 'w-1/6', 'w-2/6', 'w-3/6', 'w-4/6', 'w-5/6', 'w-1/12',
|
|
||||||
'w-2/12', 'w-3/12', 'w-4/12', 'w-5/12', 'w-6/12', 'w-7/12', 'w-8/12',
|
|
||||||
'w-9/12', 'w-10/12', 'w-11/12', 'w-full', 'w-screen', 'w-min', 'w-max',
|
|
||||||
'w-fit',
|
|
||||||
'h-0', 'h-1', 'h-2', 'h-3', 'h-4', 'h-5', 'h-6', 'h-7', 'h-8', 'h-9',
|
|
||||||
'h-10', 'h-11', 'h-12', 'h-14', 'h-16', 'h-20', 'h-24', 'h-28', 'h-32',
|
|
||||||
'h-36', 'h-40', 'h-44', 'h-48', 'h-52', 'h-56', 'h-60', 'h-64', 'h-72',
|
|
||||||
'h-80', 'h-96', 'h-auto', 'h-px', 'h-0.5', 'h-1.5', 'h-2.5', 'h-3.5',
|
|
||||||
'h-1/2', 'h-1/3', 'h-2/3', 'h-1/4', 'h-2/4', 'h-3/4', 'h-1/5', 'h-2/5',
|
|
||||||
'h-3/5', 'h-4/5', 'h-1/6', 'h-2/6', 'h-3/6', 'h-4/6', 'h-5/6', 'h-full',
|
|
||||||
'h-screen', 'h-min', 'h-max', 'h-fit'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: 'Typography',
|
|
||||||
classes: [
|
|
||||||
'font-sans', 'font-serif', 'font-mono',
|
|
||||||
'text-xs', 'text-sm', 'text-base', 'text-lg', 'text-xl', 'text-2xl',
|
|
||||||
'text-3xl', 'text-4xl', 'text-5xl', 'text-6xl', 'text-7xl', 'text-8xl',
|
|
||||||
'text-9xl',
|
|
||||||
'font-thin', 'font-extralight', 'font-light', 'font-normal', 'font-medium',
|
|
||||||
'font-semibold', 'font-bold', 'font-extrabold', 'font-black',
|
|
||||||
'italic', 'not-italic',
|
|
||||||
'underline', 'overline', 'line-through', 'no-underline',
|
|
||||||
'text-left', 'text-center', 'text-right', 'text-justify',
|
|
||||||
'text-black', 'text-white', 'text-gray-50', 'text-gray-100', 'text-gray-200',
|
|
||||||
'text-gray-300', 'text-gray-400', 'text-gray-500', 'text-gray-600',
|
|
||||||
'text-gray-700', 'text-gray-800', 'text-gray-900', 'text-red-50',
|
|
||||||
'text-red-100', 'text-red-200', 'text-red-300', 'text-red-400',
|
|
||||||
'text-red-500', 'text-red-600', 'text-red-700', 'text-red-800',
|
|
||||||
'text-red-900', 'text-blue-50', 'text-blue-100', 'text-blue-200',
|
|
||||||
'text-blue-300', 'text-blue-400', 'text-blue-500', 'text-blue-600',
|
|
||||||
'text-blue-700', 'text-blue-800', 'text-blue-900'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: 'Background',
|
|
||||||
classes: [
|
|
||||||
'bg-transparent', 'bg-current', 'bg-black', 'bg-white',
|
|
||||||
'bg-gray-50', 'bg-gray-100', 'bg-gray-200', 'bg-gray-300', 'bg-gray-400',
|
|
||||||
'bg-gray-500', 'bg-gray-600', 'bg-gray-700', 'bg-gray-800', 'bg-gray-900',
|
|
||||||
'bg-red-50', 'bg-red-100', 'bg-red-200', 'bg-red-300', 'bg-red-400',
|
|
||||||
'bg-red-500', 'bg-red-600', 'bg-red-700', 'bg-red-800', 'bg-red-900',
|
|
||||||
'bg-yellow-50', 'bg-yellow-100', 'bg-yellow-200', 'bg-yellow-300',
|
|
||||||
'bg-yellow-400', 'bg-yellow-500', 'bg-yellow-600', 'bg-yellow-700',
|
|
||||||
'bg-yellow-800', 'bg-yellow-900',
|
|
||||||
'bg-green-50', 'bg-green-100', 'bg-green-200', 'bg-green-300',
|
|
||||||
'bg-green-400', 'bg-green-500', 'bg-green-600', 'bg-green-700',
|
|
||||||
'bg-green-800', 'bg-green-900',
|
|
||||||
'bg-blue-50', 'bg-blue-100', 'bg-blue-200', 'bg-blue-300', 'bg-blue-400',
|
|
||||||
'bg-blue-500', 'bg-blue-600', 'bg-blue-700', 'bg-blue-800', 'bg-blue-900',
|
|
||||||
'bg-indigo-50', 'bg-indigo-100', 'bg-indigo-200', 'bg-indigo-300',
|
|
||||||
'bg-indigo-400', 'bg-indigo-500', 'bg-indigo-600', 'bg-indigo-700',
|
|
||||||
'bg-indigo-800', 'bg-indigo-900',
|
|
||||||
'bg-purple-50', 'bg-purple-100', 'bg-purple-200', 'bg-purple-300',
|
|
||||||
'bg-purple-400', 'bg-purple-500', 'bg-purple-600', 'bg-purple-700',
|
|
||||||
'bg-purple-800', 'bg-purple-900',
|
|
||||||
'bg-pink-50', 'bg-pink-100', 'bg-pink-200', 'bg-pink-300', 'bg-pink-400',
|
|
||||||
'bg-pink-500', 'bg-pink-600', 'bg-pink-700', 'bg-pink-800', 'bg-pink-900'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: 'Border',
|
|
||||||
classes: [
|
|
||||||
'border-0', 'border-2', 'border-4', 'border-8', 'border',
|
|
||||||
'border-t-0', 'border-t-2', 'border-t-4', 'border-t-8', 'border-t',
|
|
||||||
'border-r-0', 'border-r-2', 'border-r-4', 'border-r-8', 'border-r',
|
|
||||||
'border-b-0', 'border-b-2', 'border-b-4', 'border-b-8', 'border-b',
|
|
||||||
'border-l-0', 'border-l-2', 'border-l-4', 'border-l-8', 'border-l',
|
|
||||||
'border-solid', 'border-dashed', 'border-dotted', 'border-double',
|
|
||||||
'border-none',
|
|
||||||
'border-transparent', 'border-current', 'border-black', 'border-white',
|
|
||||||
'border-gray-50', 'border-gray-100', 'border-gray-200', 'border-gray-300',
|
|
||||||
'border-gray-400', 'border-gray-500', 'border-gray-600', 'border-gray-700',
|
|
||||||
'border-gray-800', 'border-gray-900',
|
|
||||||
'rounded-none', 'rounded-sm', 'rounded', 'rounded-md', 'rounded-lg',
|
|
||||||
'rounded-xl', 'rounded-2xl', 'rounded-3xl', 'rounded-full',
|
|
||||||
'rounded-t-none', 'rounded-t-sm', 'rounded-t', 'rounded-t-md',
|
|
||||||
'rounded-t-lg', 'rounded-t-xl', 'rounded-t-2xl', 'rounded-t-3xl',
|
|
||||||
'rounded-t-full'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: 'Effects',
|
|
||||||
classes: [
|
|
||||||
'shadow-sm', 'shadow', 'shadow-md', 'shadow-lg', 'shadow-xl',
|
|
||||||
'shadow-2xl', 'shadow-inner', 'shadow-none',
|
|
||||||
'opacity-0', 'opacity-5', 'opacity-10', 'opacity-20', 'opacity-25',
|
|
||||||
'opacity-30', 'opacity-40', 'opacity-50', 'opacity-60', 'opacity-70',
|
|
||||||
'opacity-75', 'opacity-80', 'opacity-90', 'opacity-95', 'opacity-100',
|
|
||||||
'blur-none', 'blur-sm', 'blur', 'blur-md', 'blur-lg', 'blur-xl',
|
|
||||||
'blur-2xl', 'blur-3xl',
|
|
||||||
'brightness-0', 'brightness-50', 'brightness-75', 'brightness-90',
|
|
||||||
'brightness-95', 'brightness-100', 'brightness-105', 'brightness-110',
|
|
||||||
'brightness-125', 'brightness-150', 'brightness-200'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: 'Positioning',
|
|
||||||
classes: [
|
|
||||||
'static', 'fixed', 'absolute', 'relative', 'sticky',
|
|
||||||
'inset-0', 'inset-x-0', 'inset-y-0', 'top-0', 'right-0', 'bottom-0',
|
|
||||||
'left-0', 'inset-px', 'inset-x-px', 'inset-y-px', 'top-px', 'right-px',
|
|
||||||
'bottom-px', 'left-px', 'inset-0.5', 'inset-1', 'inset-1.5', 'inset-2',
|
|
||||||
'inset-2.5', 'inset-3', 'inset-3.5', 'inset-4', 'inset-5', 'inset-6',
|
|
||||||
'inset-7', 'inset-8', 'inset-9', 'inset-10', 'inset-11', 'inset-12',
|
|
||||||
'inset-14', 'inset-16', 'inset-20', 'inset-24', 'inset-28', 'inset-32',
|
|
||||||
'inset-36', 'inset-40', 'inset-44', 'inset-48', 'inset-52', 'inset-56',
|
|
||||||
'inset-60', 'inset-64', 'inset-72', 'inset-80', 'inset-96', 'inset-auto',
|
|
||||||
'inset-1/2', 'inset-1/3', 'inset-2/3', 'inset-1/4', 'inset-2/4',
|
|
||||||
'inset-3/4', 'inset-full',
|
|
||||||
'z-0', 'z-10', 'z-20', 'z-30', 'z-40', 'z-50', 'z-auto'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: 'Filters',
|
|
||||||
classes: [
|
|
||||||
'filter', 'filter-none', 'contrast-0', 'contrast-50',
|
|
||||||
'contrast-75', 'contrast-100', 'contrast-125', 'contrast-150',
|
|
||||||
'contrast-200', 'drop-shadow-sm', 'drop-shadow', 'drop-shadow-md',
|
|
||||||
'drop-shadow-lg', 'drop-shadow-xl', 'drop-shadow-2xl', 'drop-shadow-none',
|
|
||||||
'grayscale-0', 'grayscale', 'hue-rotate-0', 'hue-rotate-15',
|
|
||||||
'hue-rotate-30', 'hue-rotate-60', 'hue-rotate-90', 'hue-rotate-180',
|
|
||||||
'invert-0', 'invert', 'saturate-0', 'saturate-50', 'saturate-100',
|
|
||||||
'saturate-150', 'saturate-200', 'sepia-0', 'sepia'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: 'Interactivity',
|
|
||||||
classes: [
|
|
||||||
'cursor-auto', 'cursor-default', 'cursor-pointer', 'cursor-wait',
|
|
||||||
'cursor-text', 'cursor-move', 'cursor-help', 'cursor-not-allowed',
|
|
||||||
'cursor-none', 'cursor-context-menu', 'cursor-progress', 'cursor-cell',
|
|
||||||
'cursor-crosshair', 'cursor-vertical-text', 'cursor-alias', 'cursor-copy',
|
|
||||||
'cursor-no-drop', 'cursor-grab', 'cursor-grabbing', 'cursor-all-scroll',
|
|
||||||
'cursor-col-resize', 'cursor-row-resize', 'cursor-n-resize',
|
|
||||||
'cursor-e-resize', 'cursor-s-resize', 'cursor-w-resize', 'cursor-ne-resize',
|
|
||||||
'cursor-nw-resize', 'cursor-se-resize', 'cursor-sw-resize',
|
|
||||||
'cursor-ew-resize', 'cursor-ns-resize', 'cursor-nesw-resize',
|
|
||||||
'cursor-nwse-resize', 'cursor-zoom-in', 'cursor-zoom-out',
|
|
||||||
'select-none', 'select-text', 'select-all', 'select-auto',
|
|
||||||
'resize-none', 'resize-y', 'resize-x', 'resize',
|
|
||||||
'scroll-auto', 'scroll-smooth',
|
|
||||||
'snap-none', 'snap-x', 'snap-y', 'snap-both', 'snap-mandatory',
|
|
||||||
'snap-proximity',
|
|
||||||
'touch-auto', 'touch-none', 'touch-pan-x', 'touch-pan-left',
|
|
||||||
'touch-pan-right', 'touch-pan-y', 'touch-pan-up', 'touch-pan-down',
|
|
||||||
'touch-pinch-zoom', 'touch-manipulation',
|
|
||||||
'pointer-events-none', 'pointer-events-auto'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: 'Transitions',
|
|
||||||
classes: [
|
|
||||||
'transition-none', 'transition-all', 'transition', 'transition-colors',
|
|
||||||
'transition-opacity', 'transition-shadow', 'transition-transform',
|
|
||||||
'duration-75', 'duration-100', 'duration-150', 'duration-200',
|
|
||||||
'duration-300', 'duration-500', 'duration-700', 'duration-1000',
|
|
||||||
'ease-linear', 'ease-in', 'ease-out', 'ease-in-out',
|
|
||||||
'delay-75', 'delay-100', 'delay-150', 'delay-200', 'delay-300',
|
|
||||||
'delay-500', 'delay-700', 'delay-1000',
|
|
||||||
'animate-none', 'animate-spin', 'animate-ping', 'animate-pulse',
|
|
||||||
'animate-bounce'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: 'Transforms',
|
|
||||||
classes: [
|
|
||||||
'transform', 'transform-gpu', 'transform-none',
|
|
||||||
'scale-0', 'scale-50', 'scale-75', 'scale-90', 'scale-95', 'scale-100',
|
|
||||||
'scale-105', 'scale-110', 'scale-125', 'scale-150',
|
|
||||||
'scale-x-0', 'scale-x-50', 'scale-x-75', 'scale-x-90', 'scale-x-95',
|
|
||||||
'scale-x-100', 'scale-x-105', 'scale-x-110', 'scale-x-125', 'scale-x-150',
|
|
||||||
'scale-y-0', 'scale-y-50', 'scale-y-75', 'scale-y-90', 'scale-y-95',
|
|
||||||
'scale-y-100', 'scale-y-105', 'scale-y-110', 'scale-y-125', 'scale-y-150',
|
|
||||||
'rotate-0', 'rotate-1', 'rotate-2', 'rotate-3', 'rotate-6', 'rotate-12',
|
|
||||||
'rotate-45', 'rotate-90', 'rotate-180', '-rotate-180', '-rotate-90',
|
|
||||||
'-rotate-45', '-rotate-12', '-rotate-6', '-rotate-3', '-rotate-2',
|
|
||||||
'-rotate-1',
|
|
||||||
'translate-x-0', 'translate-x-1', 'translate-x-2', 'translate-x-3',
|
|
||||||
'translate-x-4', 'translate-x-5', 'translate-x-6', 'translate-x-7',
|
|
||||||
'translate-x-8', 'translate-x-9', 'translate-x-10', 'translate-x-11',
|
|
||||||
'translate-x-12', 'translate-x-14', 'translate-x-16', 'translate-x-20',
|
|
||||||
'translate-x-24', 'translate-x-28', 'translate-x-32', 'translate-x-36',
|
|
||||||
'translate-x-40', 'translate-x-44', 'translate-x-48', 'translate-x-52',
|
|
||||||
'translate-x-56', 'translate-x-60', 'translate-x-64', 'translate-x-72',
|
|
||||||
'translate-x-80', 'translate-x-96', 'translate-x-px', 'translate-x-0.5',
|
|
||||||
'translate-x-1.5', 'translate-x-2.5', 'translate-x-3.5',
|
|
||||||
'translate-x-1/2', 'translate-x-1/3', 'translate-x-2/3', 'translate-x-1/4',
|
|
||||||
'translate-x-2/4', 'translate-x-3/4', 'translate-x-full',
|
|
||||||
'translate-y-0', 'translate-y-1', 'translate-y-2', 'translate-y-3',
|
|
||||||
'translate-y-4', 'translate-y-5', 'translate-y-6', 'translate-y-7',
|
|
||||||
'translate-y-8', 'translate-y-9', 'translate-y-10', 'translate-y-11',
|
|
||||||
'translate-y-12', 'translate-y-14', 'translate-y-16', 'translate-y-20',
|
|
||||||
'translate-y-24', 'translate-y-28', 'translate-y-32', 'translate-y-36',
|
|
||||||
'translate-y-40', 'translate-y-44', 'translate-y-48', 'translate-y-52',
|
|
||||||
'translate-y-56', 'translate-y-60', 'translate-y-64', 'translate-y-72',
|
|
||||||
'translate-y-80', 'translate-y-96', 'translate-y-px', 'translate-y-0.5',
|
|
||||||
'translate-y-1.5', 'translate-y-2.5', 'translate-y-3.5',
|
|
||||||
'translate-y-1/2', 'translate-y-1/3', 'translate-y-2/3', 'translate-y-1/4',
|
|
||||||
'translate-y-2/4', 'translate-y-3/4', 'translate-y-full',
|
|
||||||
'skew-x-0', 'skew-x-1', 'skew-x-2', 'skew-x-3', 'skew-x-6', 'skew-x-12',
|
|
||||||
'skew-y-0', 'skew-y-1', 'skew-y-2', 'skew-y-3', 'skew-y-6', 'skew-y-12'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export const searchTailwindClasses = (query: string): string[] => {
|
|
||||||
const allClasses = TAILWIND_CLASSES.flatMap(category => category.classes);
|
|
||||||
return allClasses.filter(className =>
|
|
||||||
className.toLowerCase().includes(query.toLowerCase())
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getClassesByCategory = (category: string): string[] => {
|
|
||||||
const categoryData = TAILWIND_CLASSES.find(c => c.category === category);
|
|
||||||
return categoryData ? categoryData.classes : [];
|
|
||||||
};
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
declare interface Window {
|
|
||||||
ReactDOM?: {
|
|
||||||
render: (element: React.ReactNode, container: Element) => void;
|
|
||||||
createRoot?: (container: Element) => {
|
|
||||||
render: (element: React.ReactNode) => void;
|
|
||||||
unmount: () => void;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add other global declarations your app needs here
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
import { Monaco } from "@monaco-editor/react";
|
|
||||||
|
|
||||||
export function setupMonacoReact(monaco: Monaco) {
|
|
||||||
// TypeScript ayarları
|
|
||||||
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
|
||||||
target: monaco.languages.typescript.ScriptTarget.ESNext,
|
|
||||||
module: monaco.languages.typescript.ModuleKind.ESNext,
|
|
||||||
jsx: monaco.languages.typescript.JsxEmit.React,
|
|
||||||
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
|
|
||||||
allowJs: true,
|
|
||||||
esModuleInterop: true,
|
|
||||||
allowSyntheticDefaultImports: true,
|
|
||||||
noEmit: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// React tiplerini manuel olarak ekle
|
|
||||||
const reactDTS = `
|
|
||||||
declare module "react" {
|
|
||||||
import { ReactNode } from 'react';
|
|
||||||
|
|
||||||
export type FC<P = {}> = (props: P & { children?: ReactNode }) => ReactNode;
|
|
||||||
export type ReactNode = any;
|
|
||||||
export default any;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
|
||||||
reactDTS,
|
|
||||||
"file:///node_modules/@types/react/index.d.ts"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import * as Babel from '@babel/standalone'
|
import * as Babel from '@babel/standalone'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import DOMPurify from 'dompurify'
|
||||||
|
import apiService from '@/services/api.service'
|
||||||
|
import * as UiKit from '@/components/ui'
|
||||||
|
import { generateDesignerCode } from '@/components/visualDesigner/codeGenerator'
|
||||||
|
import type { DesignerDocument } from '@/components/visualDesigner/types'
|
||||||
|
import type { CustomComponent } from '@/proxy/developerKit/models'
|
||||||
import { useComponents } from '../../contexts/ComponentContext'
|
import { useComponents } from '../../contexts/ComponentContext'
|
||||||
import ErrorBoundary from './ErrorBoundary'
|
import ErrorBoundary from './ErrorBoundary'
|
||||||
import { toast } from '../ui'
|
import { toast } from '../ui'
|
||||||
|
|
@ -17,6 +23,8 @@ const compileComponent = (code: string, scope: Record<string, any> = {}) => {
|
||||||
const require = (moduleName: string) => {
|
const require = (moduleName: string) => {
|
||||||
if (moduleName === 'react') return React
|
if (moduleName === 'react') return React
|
||||||
if (moduleName === 'axios') return axios
|
if (moduleName === 'axios') return axios
|
||||||
|
if (moduleName === 'dompurify') return DOMPurify
|
||||||
|
if (moduleName === '@/services/api.service') return apiService
|
||||||
throw new Error(`Modül bulunamadı: ${moduleName}`)
|
throw new Error(`Modül bulunamadı: ${moduleName}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,49 +47,74 @@ interface DynamicRendererProps {
|
||||||
dependencies?: string[]
|
dependencies?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const staticComponents: Record<string, any> = {
|
||||||
|
...UiKit,
|
||||||
|
Alert: React.lazy(() => import('../ui/Alert')),
|
||||||
|
Avatar: React.lazy(() => import('../ui/Avatar')),
|
||||||
|
Badge: React.lazy(() => import('../ui/Badge')),
|
||||||
|
Button: React.lazy(() => import('../ui/Button')),
|
||||||
|
Calendar: React.lazy(() => import('../ui/Calendar')),
|
||||||
|
Card: React.lazy(() => import('../ui/Card')),
|
||||||
|
Checkbox: React.lazy(() => import('../ui/Checkbox')),
|
||||||
|
ConfigProvider: React.lazy(() => import('../ui/ConfigProvider')),
|
||||||
|
DatePicker: React.lazy(() => import('../ui/DatePicker')),
|
||||||
|
Dialog: React.lazy(() => import('../ui/Dialog')),
|
||||||
|
Drawer: React.lazy(() => import('../ui/Drawer')),
|
||||||
|
Dropdown: React.lazy(() => import('../ui/Dropdown')),
|
||||||
|
FormItem: React.lazy(() => import('../ui/Form/FormItem')),
|
||||||
|
FormContainer: React.lazy(() => import('../ui/Form/FormContainer')),
|
||||||
|
Input: React.lazy(() => import('../ui/Input')),
|
||||||
|
InputGroup: React.lazy(() => import('../ui/InputGroup')),
|
||||||
|
Menu: React.lazy(() => import('../ui/Menu')),
|
||||||
|
MenuItem: React.lazy(() => import('../ui/MenuItem')),
|
||||||
|
Notification: React.lazy(() => import('../ui/Notification')),
|
||||||
|
Pagination: React.lazy(() => import('../ui/Pagination')),
|
||||||
|
Progress: React.lazy(() => import('../ui/Progress')),
|
||||||
|
Radio: React.lazy(() => import('../ui/Radio')),
|
||||||
|
RangeCalendar: React.lazy(() => import('../ui/RangeCalendar')),
|
||||||
|
ScrollBar: React.lazy(() => import('../ui/ScrollBar')),
|
||||||
|
Segment: React.lazy(() => import('../ui/Segment')),
|
||||||
|
Select: React.lazy(() => import('../ui/Select')),
|
||||||
|
Skeleton: React.lazy(() => import('../ui/Skeleton')),
|
||||||
|
Spinner: React.lazy(() => import('../ui/Spinner')),
|
||||||
|
Steps: React.lazy(() => import('../ui/Steps')),
|
||||||
|
Switcher: React.lazy(() => import('../ui/Switcher')),
|
||||||
|
Table: React.lazy(() => import('../ui/Table')),
|
||||||
|
Tabs: React.lazy(() => import('../ui/Tabs')),
|
||||||
|
Tag: React.lazy(() => import('../ui/Tag')),
|
||||||
|
TimeInput: React.lazy(() => import('../ui/TimeInput')),
|
||||||
|
Timeline: React.lazy(() => import('../ui/Timeline')),
|
||||||
|
Tooltip: React.lazy(() => import('../ui/Tooltip')),
|
||||||
|
Upload: React.lazy(() => import('../ui/Upload')),
|
||||||
|
PlatformViewHost: React.lazy(() => import('./PlatformViewHost')),
|
||||||
|
toast,
|
||||||
|
apiService,
|
||||||
|
DOMPurify,
|
||||||
|
UiKit,
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRuntimeCode = (component: CustomComponent) => {
|
||||||
|
if (!component) return ''
|
||||||
|
try {
|
||||||
|
const props = component.props ? JSON.parse(component.props) : null
|
||||||
|
const document = props?.visualDesigner as DesignerDocument | undefined
|
||||||
|
if (
|
||||||
|
document?.version === 1 &&
|
||||||
|
document.sourceMode === 'visual' &&
|
||||||
|
Array.isArray(document.nodes)
|
||||||
|
) {
|
||||||
|
return generateDesignerCode(component.name, document)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Visual designer schema could not be read for ${component.name}:`, error)
|
||||||
|
}
|
||||||
|
return component.code
|
||||||
|
}
|
||||||
|
|
||||||
const DynamicRenderer: React.FC<DynamicRendererProps> = ({
|
const DynamicRenderer: React.FC<DynamicRendererProps> = ({
|
||||||
componentName,
|
componentName,
|
||||||
dependencies: externalDeps,
|
dependencies: externalDeps,
|
||||||
}) => {
|
}) => {
|
||||||
// Lazy load for UI components
|
|
||||||
const Alert = React.lazy(() => import('../ui/Alert'))
|
|
||||||
const Avatar = React.lazy(() => import('../ui/Avatar'))
|
|
||||||
const Badge = React.lazy(() => import('../ui/Badge'))
|
|
||||||
const Button = React.lazy(() => import('../ui/Button'))
|
|
||||||
const Calendar = React.lazy(() => import('../ui/Calendar'))
|
|
||||||
const Card = React.lazy(() => import('../ui/Card'))
|
|
||||||
const Checkbox = React.lazy(() => import('../ui/Checkbox'))
|
|
||||||
const ConfigProvider = React.lazy(() => import('../ui/ConfigProvider'))
|
|
||||||
const DatePicker = React.lazy(() => import('../ui/DatePicker'))
|
|
||||||
const Dialog = React.lazy(() => import('../ui/Dialog'))
|
|
||||||
const Drawer = React.lazy(() => import('../ui/Drawer'))
|
|
||||||
const Dropdown = React.lazy(() => import('../ui/Dropdown'))
|
|
||||||
const FormItem = React.lazy(() => import('../ui/Form/FormItem'))
|
|
||||||
const FormContainer = React.lazy(() => import('../ui/Form/FormContainer'))
|
|
||||||
const Input = React.lazy(() => import('../ui/Input'))
|
|
||||||
const InputGroup = React.lazy(() => import('../ui/InputGroup'))
|
|
||||||
const Menu = React.lazy(() => import('../ui/Menu'))
|
|
||||||
const MenuItem = React.lazy(() => import('../ui/MenuItem'))
|
|
||||||
const Notification = React.lazy(() => import('../ui/Notification'))
|
|
||||||
const Pagination = React.lazy(() => import('../ui/Pagination'))
|
|
||||||
const Progress = React.lazy(() => import('../ui/Progress'))
|
|
||||||
const Radio = React.lazy(() => import('../ui/Radio'))
|
|
||||||
const RangeCalendar = React.lazy(() => import('../ui/RangeCalendar'))
|
|
||||||
const ScrollBar = React.lazy(() => import('../ui/ScrollBar'))
|
|
||||||
const Segment = React.lazy(() => import('../ui/Segment'))
|
|
||||||
const Select = React.lazy(() => import('../ui/Select'))
|
|
||||||
const Skeleton = React.lazy(() => import('../ui/Skeleton'))
|
|
||||||
const Spinner = React.lazy(() => import('../ui/Spinner'))
|
|
||||||
const Steps = React.lazy(() => import('../ui/Steps'))
|
|
||||||
const Switcher = React.lazy(() => import('../ui/Switcher'))
|
|
||||||
const Table = React.lazy(() => import('../ui/Table'))
|
|
||||||
const Tabs = React.lazy(() => import('../ui/Tabs'))
|
|
||||||
const Tag = React.lazy(() => import('../ui/Tag'))
|
|
||||||
const TimeInput = React.lazy(() => import('../ui/TimeInput'))
|
|
||||||
const Timeline = React.lazy(() => import('../ui/Timeline'))
|
|
||||||
const Tooltip = React.lazy(() => import('../ui/Tooltip'))
|
|
||||||
const Upload = React.lazy(() => import('../ui/Upload'))
|
|
||||||
|
|
||||||
const [Component, setComponent] = useState<React.ComponentType<any> | null>(null)
|
const [Component, setComponent] = useState<React.ComponentType<any> | null>(null)
|
||||||
const { getComponentByName, components } = useComponents()
|
const { getComponentByName, components } = useComponents()
|
||||||
|
|
||||||
|
|
@ -105,7 +138,7 @@ const DynamicRenderer: React.FC<DynamicRendererProps> = ({
|
||||||
c.name,
|
c.name,
|
||||||
{
|
{
|
||||||
name: c.name,
|
name: c.name,
|
||||||
jsx_code: c.code,
|
jsx_code: getRuntimeCode(c),
|
||||||
dependencies: Array.isArray(componentDeps) ? componentDeps : [],
|
dependencies: Array.isArray(componentDeps) ? componentDeps : [],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
@ -114,47 +147,6 @@ const DynamicRenderer: React.FC<DynamicRendererProps> = ({
|
||||||
|
|
||||||
const compiled: Record<string, any> = {}
|
const compiled: Record<string, any> = {}
|
||||||
|
|
||||||
const staticComponents: Record<string, any> = {
|
|
||||||
Alert,
|
|
||||||
Avatar,
|
|
||||||
Badge,
|
|
||||||
Button,
|
|
||||||
Calendar,
|
|
||||||
Card,
|
|
||||||
Checkbox,
|
|
||||||
ConfigProvider,
|
|
||||||
DatePicker,
|
|
||||||
Dialog,
|
|
||||||
Drawer,
|
|
||||||
Dropdown,
|
|
||||||
FormItem,
|
|
||||||
FormContainer,
|
|
||||||
Input,
|
|
||||||
InputGroup,
|
|
||||||
Menu,
|
|
||||||
MenuItem,
|
|
||||||
Notification,
|
|
||||||
Pagination,
|
|
||||||
Progress,
|
|
||||||
Radio,
|
|
||||||
RangeCalendar,
|
|
||||||
ScrollBar,
|
|
||||||
Segment,
|
|
||||||
Select,
|
|
||||||
Skeleton,
|
|
||||||
Spinner,
|
|
||||||
Steps,
|
|
||||||
Switcher,
|
|
||||||
Table,
|
|
||||||
Tabs,
|
|
||||||
Tag,
|
|
||||||
TimeInput,
|
|
||||||
Timeline,
|
|
||||||
toast,
|
|
||||||
Tooltip,
|
|
||||||
Upload,
|
|
||||||
}
|
|
||||||
|
|
||||||
const compileWithDependencies = (name: string): any => {
|
const compileWithDependencies = (name: string): any => {
|
||||||
if (compiled[name]) return compiled[name]
|
if (compiled[name]) return compiled[name]
|
||||||
|
|
||||||
|
|
|
||||||
114
ui/src/components/componentEditor/PlatformViewHost.tsx
Normal file
114
ui/src/components/componentEditor/PlatformViewHost.tsx
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
import React, { Suspense, useCallback, useEffect, useState } from 'react'
|
||||||
|
import type { GridDto } from '@/proxy/form/models'
|
||||||
|
import { getList } from '@/services/form.service'
|
||||||
|
import { Loading } from '@/components/shared'
|
||||||
|
|
||||||
|
const Grid = React.lazy(() => import('@/views/list/Grid'))
|
||||||
|
const List = React.lazy(() => import('@/views/list/List'))
|
||||||
|
const Tree = React.lazy(() => import('@/views/list/Tree'))
|
||||||
|
const GanttView = React.lazy(() => import('@/views/list/GanttView'))
|
||||||
|
const TodoBoard = React.lazy(() => import('@/views/list/TodoBoard'))
|
||||||
|
const CardView = React.lazy(() => import('@/views/list/CardView'))
|
||||||
|
const SchedulerView = React.lazy(() => import('@/views/list/SchedulerView'))
|
||||||
|
const Pivot = React.lazy(() => import('@/views/list/Pivot'))
|
||||||
|
const Chart = React.lazy(() => import('@/views/list/Chart'))
|
||||||
|
|
||||||
|
export type PlatformViewName =
|
||||||
|
| 'List'
|
||||||
|
| 'Grid'
|
||||||
|
| 'Tree'
|
||||||
|
| 'GanttView'
|
||||||
|
| 'TodoBoard'
|
||||||
|
| 'CardView'
|
||||||
|
| 'SchedulerView'
|
||||||
|
| 'Pivot'
|
||||||
|
| 'Chart'
|
||||||
|
|
||||||
|
interface PlatformViewHostProps {
|
||||||
|
view: PlatformViewName
|
||||||
|
listFormCode: string
|
||||||
|
height?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const PlatformViewHost = ({ view, listFormCode, height = '520px' }: PlatformViewHostProps) => {
|
||||||
|
const [gridDto, setGridDto] = useState<GridDto>()
|
||||||
|
const [error, setError] = useState('')
|
||||||
|
|
||||||
|
const refreshGridDto = useCallback(async () => {
|
||||||
|
if (!listFormCode.trim()) return
|
||||||
|
try {
|
||||||
|
setError('')
|
||||||
|
const response = await getList({ listFormCode })
|
||||||
|
const nextGridDto = response?.data
|
||||||
|
|
||||||
|
if (!nextGridDto?.gridOptions) {
|
||||||
|
throw new Error(`List form "${listFormCode}" did not return a valid grid configuration.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
setGridDto(nextGridDto)
|
||||||
|
} catch (reason) {
|
||||||
|
console.error(`Platform view ${view} could not load:`, reason)
|
||||||
|
setGridDto(undefined)
|
||||||
|
setError(`"${listFormCode}" list form configuration could not be loaded.`)
|
||||||
|
}
|
||||||
|
}, [listFormCode, view])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setGridDto(undefined)
|
||||||
|
void refreshGridDto()
|
||||||
|
}, [refreshGridDto])
|
||||||
|
|
||||||
|
if (!listFormCode.trim()) {
|
||||||
|
return (
|
||||||
|
<div className="rounded-lg border border-dashed border-amber-300 bg-amber-50 p-5 text-sm text-amber-800">
|
||||||
|
{view} için listFormCode property’sini ayarlayın.
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (error) return <div className="rounded-lg bg-red-50 p-4 text-sm text-red-700">{error}</div>
|
||||||
|
if (!gridDto) return <Loading loading />
|
||||||
|
|
||||||
|
const sharedProps = { listFormCode, isSubForm: false, gridDto }
|
||||||
|
const content = (() => {
|
||||||
|
switch (view) {
|
||||||
|
case 'List':
|
||||||
|
return (
|
||||||
|
<List
|
||||||
|
gridDto={gridDto}
|
||||||
|
listFormCode={listFormCode}
|
||||||
|
onRefreshGridDto={refreshGridDto}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
case 'Tree':
|
||||||
|
return <Tree {...sharedProps} />
|
||||||
|
case 'GanttView':
|
||||||
|
return <GanttView {...sharedProps} />
|
||||||
|
case 'TodoBoard':
|
||||||
|
return <TodoBoard {...sharedProps} />
|
||||||
|
case 'CardView':
|
||||||
|
return <CardView {...sharedProps} />
|
||||||
|
case 'SchedulerView':
|
||||||
|
return <SchedulerView {...sharedProps} />
|
||||||
|
case 'Pivot':
|
||||||
|
return <Pivot {...sharedProps} refreshGridDto={refreshGridDto} />
|
||||||
|
case 'Chart':
|
||||||
|
return (
|
||||||
|
<Chart
|
||||||
|
{...sharedProps}
|
||||||
|
id={gridDto.gridOptions.id as string}
|
||||||
|
refreshGridDto={refreshGridDto}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return <Grid {...sharedProps} />
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="overflow-auto" style={{ minHeight: height }}>
|
||||||
|
<Suspense fallback={<Loading loading />}>{content}</Suspense>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PlatformViewHost
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
import { forwardRef } from 'react'
|
import { forwardRef, useEffect, useState } from 'react'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { MenuContextProvider } from './context/menuContext'
|
import { MenuContextProvider } from './context/menuContext'
|
||||||
import { useConfig } from '../ConfigProvider'
|
import { useConfig } from '../ConfigProvider'
|
||||||
import type { CommonProps, TypeAttributes } from '../@types/common'
|
import type { CommonProps, TypeAttributes } from '../@types/common'
|
||||||
|
|
||||||
|
const EMPTY_ACTIVE_KEYS: string[] = []
|
||||||
|
|
||||||
export interface MenuProps extends CommonProps {
|
export interface MenuProps extends CommonProps {
|
||||||
defaultActiveKeys?: Array<string>
|
defaultActiveKeys?: Array<string>
|
||||||
defaultExpandedKeys?: Array<string>
|
defaultExpandedKeys?: Array<string>
|
||||||
|
|
@ -18,18 +20,29 @@ const Menu = forwardRef<HTMLElement, MenuProps>((props, ref) => {
|
||||||
const {
|
const {
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
defaultActiveKeys = [],
|
defaultActiveKeys = EMPTY_ACTIVE_KEYS,
|
||||||
defaultExpandedKeys = [],
|
defaultExpandedKeys = [],
|
||||||
menuItemHeight = 30,
|
menuItemHeight = 30,
|
||||||
onSelect,
|
onSelect,
|
||||||
sideCollapsed = false,
|
sideCollapsed = false,
|
||||||
variant = 'light',
|
variant: variantProp,
|
||||||
...rest
|
...rest
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const menuDefaultClass = 'menu'
|
const menuDefaultClass = 'menu'
|
||||||
|
|
||||||
const { themeColor, primaryColorLevel } = useConfig()
|
const { themeColor, primaryColorLevel, mode } = useConfig()
|
||||||
|
const variant = variantProp ?? mode
|
||||||
|
const [activeKeys, setActiveKeys] = useState(defaultActiveKeys)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setActiveKeys(defaultActiveKeys)
|
||||||
|
}, [defaultActiveKeys])
|
||||||
|
|
||||||
|
const handleSelect = (eventKey: string, event: MouseEvent) => {
|
||||||
|
setActiveKeys([eventKey])
|
||||||
|
onSelect?.(eventKey, event)
|
||||||
|
}
|
||||||
|
|
||||||
const menuColor = () => {
|
const menuColor = () => {
|
||||||
if (variant === 'themed') {
|
if (variant === 'themed') {
|
||||||
|
|
@ -44,12 +57,12 @@ const Menu = forwardRef<HTMLElement, MenuProps>((props, ref) => {
|
||||||
<nav ref={ref} className={menuClass} {...rest}>
|
<nav ref={ref} className={menuClass} {...rest}>
|
||||||
<MenuContextProvider
|
<MenuContextProvider
|
||||||
value={{
|
value={{
|
||||||
onSelect,
|
onSelect: handleSelect,
|
||||||
menuItemHeight,
|
menuItemHeight,
|
||||||
variant,
|
variant,
|
||||||
sideCollapsed,
|
sideCollapsed,
|
||||||
defaultExpandedKeys,
|
defaultExpandedKeys,
|
||||||
defaultActiveKeys,
|
defaultActiveKeys: activeKeys,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,14 @@ import classNames from 'classnames'
|
||||||
import ReactSelect, { components as ReactSelectComponents } from 'react-select'
|
import ReactSelect, { components as ReactSelectComponents } from 'react-select'
|
||||||
import CreatableSelect from 'react-select/creatable'
|
import CreatableSelect from 'react-select/creatable'
|
||||||
import AsyncSelect from 'react-select/async'
|
import AsyncSelect from 'react-select/async'
|
||||||
import { TW_COLORS, BORDER_RADIUS, SPACING } from '@/utils/tailwind'
|
import { TW_COLORS, BORDER_RADIUS } from '@/utils/tailwind'
|
||||||
import isEmpty from 'lodash/isEmpty'
|
import isEmpty from 'lodash/isEmpty'
|
||||||
import get from 'lodash/get'
|
import get from 'lodash/get'
|
||||||
import { useConfig } from '../ConfigProvider'
|
import { useConfig } from '../ConfigProvider'
|
||||||
import { useForm } from '../Form/context'
|
import { useForm } from '../Form/context'
|
||||||
import { useInputGroup } from '../InputGroup/context'
|
import { useInputGroup } from '../InputGroup/context'
|
||||||
import { FaCheck, FaChevronDown, FaTimes } from 'react-icons/fa';
|
import { FaCheck, FaChevronDown, FaTimes } from 'react-icons/fa'
|
||||||
import Spinner from '../Spinner/Spinner'
|
import Spinner from '../Spinner/Spinner'
|
||||||
import { CONTROL_SIZES } from '../utils/constants'
|
|
||||||
import type { CommonProps, TypeAttributes } from '../@types/common'
|
import type { CommonProps, TypeAttributes } from '../@types/common'
|
||||||
import type {
|
import type {
|
||||||
ControlProps,
|
ControlProps,
|
||||||
|
|
@ -21,7 +20,7 @@ import type {
|
||||||
} from 'react-select'
|
} from 'react-select'
|
||||||
import type { AsyncProps } from 'react-select/async'
|
import type { AsyncProps } from 'react-select/async'
|
||||||
import type { CreatableProps } from 'react-select/creatable'
|
import type { CreatableProps } from 'react-select/creatable'
|
||||||
import type { ForwardedRef, ReactNode, Ref } from 'react'
|
import type { ForwardedRef, JSX, ReactNode, Ref } from 'react'
|
||||||
|
|
||||||
interface DefaultOptionProps {
|
interface DefaultOptionProps {
|
||||||
innerProps: JSX.IntrinsicElements['div']
|
innerProps: JSX.IntrinsicElements['div']
|
||||||
|
|
@ -33,6 +32,13 @@ interface DefaultOptionProps {
|
||||||
isFocused: boolean
|
isFocused: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SELECT_SIZE_METRICS = {
|
||||||
|
xs: { height: 24, fontSize: 12, horizontalPadding: 6 },
|
||||||
|
sm: { height: 32, fontSize: 14, horizontalPadding: 8 },
|
||||||
|
md: { height: 40, fontSize: 14, horizontalPadding: 10 },
|
||||||
|
lg: { height: 48, fontSize: 16, horizontalPadding: 12 },
|
||||||
|
} as const
|
||||||
|
|
||||||
const DefaultOption = ({
|
const DefaultOption = ({
|
||||||
innerProps,
|
innerProps,
|
||||||
label,
|
label,
|
||||||
|
|
@ -49,16 +55,12 @@ const DefaultOption = ({
|
||||||
'select-option',
|
'select-option',
|
||||||
isSelected && 'selected',
|
isSelected && 'selected',
|
||||||
isDisabled && 'disabled',
|
isDisabled && 'disabled',
|
||||||
isFocused && 'focused'
|
isFocused && 'focused',
|
||||||
)}
|
)}
|
||||||
{...innerProps}
|
{...innerProps}
|
||||||
>
|
>
|
||||||
<div className="ml-2 min-w-0">{children ?? label}</div>
|
<div className="ml-2 min-w-0">{children ?? label}</div>
|
||||||
{isSelected && (
|
{isSelected && <FaCheck className={`text-${themeColor} dark:text-white text-xl`} />}
|
||||||
<FaCheck
|
|
||||||
className={`text-${themeColor} dark:text-white text-xl`}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -66,9 +68,9 @@ const DefaultOption = ({
|
||||||
const DefaultMenuList = <
|
const DefaultMenuList = <
|
||||||
Option,
|
Option,
|
||||||
IsMulti extends boolean = false,
|
IsMulti extends boolean = false,
|
||||||
Group extends GroupBase<Option> = GroupBase<Option>
|
Group extends GroupBase<Option> = GroupBase<Option>,
|
||||||
>(
|
>(
|
||||||
props: MenuListProps<Option, IsMulti, Group>
|
props: MenuListProps<Option, IsMulti, Group>,
|
||||||
) => {
|
) => {
|
||||||
const menuListRef = useRef<HTMLDivElement | null>(null)
|
const menuListRef = useRef<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
|
|
@ -125,38 +127,34 @@ interface DefaultLoadingIndicatorProps {
|
||||||
selectProps: { themeColor?: string }
|
selectProps: { themeColor?: string }
|
||||||
}
|
}
|
||||||
|
|
||||||
const DefaultLoadingIndicator = ({
|
const DefaultLoadingIndicator = ({ selectProps }: DefaultLoadingIndicatorProps) => {
|
||||||
selectProps,
|
|
||||||
}: DefaultLoadingIndicatorProps) => {
|
|
||||||
const { themeColor } = selectProps
|
const { themeColor } = selectProps
|
||||||
return (
|
return <Spinner className={`select-loading-indicatior text-${themeColor}`} />
|
||||||
<Spinner className={`select-loading-indicatior text-${themeColor}`} />
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SelectProps<
|
export interface SelectProps<
|
||||||
Option,
|
Option,
|
||||||
IsMulti extends boolean = false,
|
IsMulti extends boolean = false,
|
||||||
Group extends GroupBase<Option> = GroupBase<Option>
|
Group extends GroupBase<Option> = GroupBase<Option>,
|
||||||
> extends CommonProps,
|
>
|
||||||
|
extends
|
||||||
|
CommonProps,
|
||||||
ReactSelectProps<Option, IsMulti, Group>,
|
ReactSelectProps<Option, IsMulti, Group>,
|
||||||
AsyncProps<Option, IsMulti, Group>,
|
AsyncProps<Option, IsMulti, Group>,
|
||||||
CreatableProps<Option, IsMulti, Group> {
|
CreatableProps<Option, IsMulti, Group> {
|
||||||
size?: TypeAttributes.ControlSize
|
size?: TypeAttributes.ControlSize
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
field?: any
|
field?: any
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
form?: any
|
form?: any
|
||||||
componentAs?: ReactSelect | CreatableSelect | AsyncSelect
|
componentAs?: ReactSelect | CreatableSelect | AsyncSelect
|
||||||
}
|
}
|
||||||
|
|
||||||
function _Select<
|
function SelectBase<
|
||||||
Option,
|
Option,
|
||||||
IsMulti extends boolean = false,
|
IsMulti extends boolean = false,
|
||||||
Group extends GroupBase<Option> = GroupBase<Option>
|
Group extends GroupBase<Option> = GroupBase<Option>,
|
||||||
>(
|
>(
|
||||||
props: SelectProps<Option, IsMulti, Group>,
|
props: SelectProps<Option, IsMulti, Group>,
|
||||||
ref: ForwardedRef<ReactSelect | CreatableSelect | AsyncSelect>
|
ref: ForwardedRef<ReactSelect | CreatableSelect | AsyncSelect>,
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
size,
|
size,
|
||||||
|
|
@ -174,6 +172,7 @@ function _Select<
|
||||||
const inputGroupSize = useInputGroup()?.size
|
const inputGroupSize = useInputGroup()?.size
|
||||||
|
|
||||||
const selectSize = size || inputGroupSize || formControlSize || controlSize
|
const selectSize = size || inputGroupSize || formControlSize || controlSize
|
||||||
|
const sizeMetrics = SELECT_SIZE_METRICS[selectSize]
|
||||||
|
|
||||||
const twColor = TW_COLORS as any
|
const twColor = TW_COLORS as any
|
||||||
|
|
||||||
|
|
@ -211,27 +210,26 @@ function _Select<
|
||||||
classNamePrefix={'select'}
|
classNamePrefix={'select'}
|
||||||
styles={{
|
styles={{
|
||||||
control: (provided, state) => {
|
control: (provided, state) => {
|
||||||
// Tailwind spacing karşılığı: CONTROL_SIZES -> '10' gibi anahtarlar dönüyorsa SPACING ile eşleşir
|
|
||||||
const sizeKey = String(CONTROL_SIZES[selectSize]) as keyof typeof SPACING
|
|
||||||
const ctlHeight = (SPACING as any)[sizeKey] as string | undefined
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...provided,
|
...provided,
|
||||||
...(ctlHeight ? { height: ctlHeight, minHeight: ctlHeight } : {}),
|
fontSize: sizeMetrics.fontSize,
|
||||||
|
height: sizeMetrics.height,
|
||||||
|
minHeight: sizeMetrics.height,
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
boxShadow: getBoxShadow(state),
|
boxShadow: getBoxShadow(state),
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
},
|
},
|
||||||
boxShadow: getBoxShadow(state),
|
boxShadow: getBoxShadow(state),
|
||||||
borderRadius: BORDER_RADIUS.md,
|
borderRadius: BORDER_RADIUS.md,
|
||||||
...(isInvalid
|
...(isInvalid ? { borderColor: twColor.red['500'] } : {}),
|
||||||
? { borderColor: twColor.red['500'] }
|
|
||||||
: {}),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
input: (css) => {
|
input: (css) => {
|
||||||
return {
|
return {
|
||||||
...css,
|
...css,
|
||||||
|
margin: 0,
|
||||||
|
paddingBottom: 0,
|
||||||
|
paddingTop: 0,
|
||||||
input: {
|
input: {
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
outlineOffset: 0,
|
outlineOffset: 0,
|
||||||
|
|
@ -239,6 +237,17 @@ function _Select<
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
valueContainer: (provided) => ({
|
||||||
|
...provided,
|
||||||
|
height: sizeMetrics.height - 2,
|
||||||
|
padding: `0 ${sizeMetrics.horizontalPadding}px`,
|
||||||
|
}),
|
||||||
|
indicatorsContainer: (provided) => ({
|
||||||
|
...provided,
|
||||||
|
height: sizeMetrics.height - 2,
|
||||||
|
}),
|
||||||
|
placeholder: (provided) => ({ ...provided, margin: 0 }),
|
||||||
|
singleValue: (provided) => ({ ...provided, margin: 0 }),
|
||||||
menu: (provided) => ({ ...provided, zIndex: 50 }),
|
menu: (provided) => ({ ...provided, zIndex: 50 }),
|
||||||
...style,
|
...style,
|
||||||
}}
|
}}
|
||||||
|
|
@ -246,25 +255,11 @@ function _Select<
|
||||||
...theme,
|
...theme,
|
||||||
colors: {
|
colors: {
|
||||||
...theme.colors,
|
...theme.colors,
|
||||||
neutral0: mode === 'dark'
|
neutral0: mode === 'dark' ? twColor.gray['800'] : (TW_COLORS as any).white,
|
||||||
? twColor.gray['800']
|
neutral20: mode === 'dark' ? twColor.gray['600'] : twColor.gray['300'],
|
||||||
: (TW_COLORS as any).white,
|
neutral30: mode === 'dark' ? twColor.gray['600'] : twColor.gray['300'],
|
||||||
neutral20:
|
neutral80: mode === 'dark' ? (TW_COLORS as any).white : twColor.gray['700'],
|
||||||
mode === 'dark'
|
neutral10: mode === 'dark' ? twColor.gray['600'] : twColor.gray['300'],
|
||||||
? twColor.gray['600']
|
|
||||||
: twColor.gray['300'],
|
|
||||||
neutral30:
|
|
||||||
mode === 'dark'
|
|
||||||
? twColor.gray['600']
|
|
||||||
: twColor.gray['300'],
|
|
||||||
neutral80:
|
|
||||||
mode === 'dark'
|
|
||||||
? (TW_COLORS as any).white
|
|
||||||
: twColor.gray['700'],
|
|
||||||
neutral10:
|
|
||||||
mode === 'dark'
|
|
||||||
? twColor.gray['600']
|
|
||||||
: twColor.gray['300'],
|
|
||||||
primary25: twColor[themeColor]['50'],
|
primary25: twColor[themeColor]['50'],
|
||||||
primary50: twColor[themeColor]['100'],
|
primary50: twColor[themeColor]['100'],
|
||||||
primary: twColor[themeColor][primaryColorLevel],
|
primary: twColor[themeColor][primaryColorLevel],
|
||||||
|
|
@ -286,14 +281,14 @@ function _Select<
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Select = forwardRef(_Select) as <
|
const Select = forwardRef(SelectBase) as <
|
||||||
Option,
|
Option,
|
||||||
IsMulti extends boolean = false,
|
IsMulti extends boolean = false,
|
||||||
Group extends GroupBase<Option> = GroupBase<Option>
|
Group extends GroupBase<Option> = GroupBase<Option>,
|
||||||
>(
|
>(
|
||||||
props: SelectProps<Option, IsMulti, Group> & {
|
props: SelectProps<Option, IsMulti, Group> & {
|
||||||
ref?: ForwardedRef<ReactSelect | CreatableSelect | AsyncSelect>
|
ref?: ForwardedRef<ReactSelect | CreatableSelect | AsyncSelect>
|
||||||
}
|
},
|
||||||
) => ReturnType<typeof _Select>
|
) => ReturnType<typeof SelectBase>
|
||||||
|
|
||||||
export default Select
|
export default Select
|
||||||
|
|
|
||||||
892
ui/src/components/visualDesigner/VisualCanvas.tsx
Normal file
892
ui/src/components/visualDesigner/VisualCanvas.tsx
Normal file
|
|
@ -0,0 +1,892 @@
|
||||||
|
import React from 'react'
|
||||||
|
import * as UiKit from '@/components/ui'
|
||||||
|
import PlatformViewHost, {
|
||||||
|
type PlatformViewName,
|
||||||
|
} from '@/components/componentEditor/PlatformViewHost'
|
||||||
|
import { FaArrowDown, FaArrowUp, FaClone, FaGripVertical, FaTrash } from 'react-icons/fa'
|
||||||
|
import {
|
||||||
|
getDesignerValueByPath,
|
||||||
|
normalizeDesignerKeyList,
|
||||||
|
type DesignerBinding,
|
||||||
|
type DesignerNode,
|
||||||
|
} from './types'
|
||||||
|
|
||||||
|
export const DESIGNER_DRAG_TYPE = 'application/x-sozsoft-designer'
|
||||||
|
|
||||||
|
interface VisualCanvasProps {
|
||||||
|
nodes: DesignerNode[]
|
||||||
|
selectedId: string | null
|
||||||
|
background: string
|
||||||
|
interactive?: boolean
|
||||||
|
onSelect?: (id: string) => void
|
||||||
|
onDropComponent?: (definitionName: string, parentId: string | null) => void
|
||||||
|
onMove?: (id: string, direction: -1 | 1) => void
|
||||||
|
onDuplicate?: (id: string) => void
|
||||||
|
onDelete?: (id: string) => void
|
||||||
|
renderCustomComponent?: (name: string, props?: Record<string, unknown>) => React.ReactNode
|
||||||
|
dataValues?: Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
class PreviewBoundary extends React.Component<
|
||||||
|
{ name: string; resetKey: string; children: React.ReactNode },
|
||||||
|
{ failed: boolean }
|
||||||
|
> {
|
||||||
|
state = { failed: false }
|
||||||
|
|
||||||
|
static getDerivedStateFromError() {
|
||||||
|
return { failed: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(previousProps: Readonly<{ resetKey: string }>) {
|
||||||
|
if (this.state.failed && previousProps.resetKey !== this.props.resetKey) {
|
||||||
|
this.setState({ failed: false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.failed) {
|
||||||
|
return (
|
||||||
|
<div className="rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-xs text-amber-800 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-200">
|
||||||
|
{this.props.name} önizlemesi için ek veri veya alt bileşen gerekiyor.
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return this.props.children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolveUiComponent = (name: string): React.ElementType | null => {
|
||||||
|
const parts = name.split('.')
|
||||||
|
let component: unknown = (UiKit as Record<string, unknown>)[parts[0]]
|
||||||
|
|
||||||
|
for (const part of parts.slice(1)) {
|
||||||
|
if ((typeof component !== 'object' && typeof component !== 'function') || !component) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
component = (component as Record<string, unknown>)[part]
|
||||||
|
}
|
||||||
|
|
||||||
|
return component ? (component as React.ElementType) : null
|
||||||
|
}
|
||||||
|
|
||||||
|
const executeEvent = (script: string, event: unknown, node: DesignerNode) => {
|
||||||
|
if (!script.trim()) return
|
||||||
|
try {
|
||||||
|
const run = new Function('event', 'component', 'props', script)
|
||||||
|
run(event, node, node.props)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Designer event error (${node.type}):`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getBindingValue = (
|
||||||
|
binding: DesignerBinding,
|
||||||
|
dataValues: Record<string, unknown>,
|
||||||
|
currentItem?: unknown,
|
||||||
|
) => {
|
||||||
|
const path = binding.path.trim()
|
||||||
|
if (currentItem !== undefined && (path === '$item' || path.startsWith('$item.'))) {
|
||||||
|
return getDesignerValueByPath(currentItem, path === '$item' ? '' : path.slice(6))
|
||||||
|
}
|
||||||
|
return getDesignerValueByPath(dataValues[binding.sourceId], path)
|
||||||
|
}
|
||||||
|
|
||||||
|
const toSelectOptions = (
|
||||||
|
value: unknown,
|
||||||
|
labelPath = '',
|
||||||
|
valuePath = '',
|
||||||
|
): Array<Record<string, unknown>> => {
|
||||||
|
if (!Array.isArray(value)) return []
|
||||||
|
return value.map((item, index) => {
|
||||||
|
if (item === null || typeof item !== 'object' || Array.isArray(item)) {
|
||||||
|
return { label: String(item ?? ''), value: item ?? index }
|
||||||
|
}
|
||||||
|
const record = item as Record<string, unknown>
|
||||||
|
const primitiveKeys = Object.keys(record).filter(
|
||||||
|
(key) => record[key] === null || ['string', 'number', 'boolean'].includes(typeof record[key]),
|
||||||
|
)
|
||||||
|
const labelKey = ['label', 'name', 'title', 'text', 'description'].find(
|
||||||
|
(key) => record[key] !== undefined,
|
||||||
|
)
|
||||||
|
const valueKey = ['value', 'id', 'code', 'key'].find((key) => record[key] !== undefined)
|
||||||
|
const labelValue = labelPath
|
||||||
|
? getDesignerValueByPath(record, labelPath)
|
||||||
|
: labelKey
|
||||||
|
? record[labelKey]
|
||||||
|
: record[primitiveKeys[0]]
|
||||||
|
const optionValue = valuePath
|
||||||
|
? getDesignerValueByPath(record, valuePath)
|
||||||
|
: valueKey
|
||||||
|
? record[valueKey]
|
||||||
|
: record[primitiveKeys[1] || primitiveKeys[0]]
|
||||||
|
return {
|
||||||
|
...record,
|
||||||
|
label: String(labelValue ?? `Seçenek ${index + 1}`),
|
||||||
|
value: optionValue ?? index,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPreviewProps = (
|
||||||
|
node: DesignerNode,
|
||||||
|
dataValues: Record<string, unknown>,
|
||||||
|
currentItem?: unknown,
|
||||||
|
) => {
|
||||||
|
const props: Record<string, unknown> = {}
|
||||||
|
Object.entries(node.props).forEach(([key, value]) => {
|
||||||
|
if (key === 'children' || key === 'html' || value === '') return
|
||||||
|
if (key.startsWith('on') && typeof value === 'string') return
|
||||||
|
props[key] = value
|
||||||
|
})
|
||||||
|
Object.entries(node.events).forEach(([name, script]) => {
|
||||||
|
if (!script.trim()) return
|
||||||
|
props[name] = (...args: unknown[]) => {
|
||||||
|
const event =
|
||||||
|
node.type === 'Checkbox' && name === 'onChange'
|
||||||
|
? {
|
||||||
|
checked: Boolean(args[0]),
|
||||||
|
originalEvent: args[1],
|
||||||
|
target:
|
||||||
|
args[1] && typeof args[1] === 'object' && 'target' in args[1]
|
||||||
|
? (args[1] as { target: unknown }).target
|
||||||
|
: undefined,
|
||||||
|
}
|
||||||
|
: args[0]
|
||||||
|
executeEvent(script, event, node)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Object.entries(node.bindings || {}).forEach(([propertyName, binding]) => {
|
||||||
|
if (propertyName !== 'children' && binding.sourceId) {
|
||||||
|
props[propertyName] = getBindingValue(binding, dataValues, currentItem)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (node.type === 'Select' || node.type === 'AutoComplete' || node.type === 'Menu') {
|
||||||
|
const collectionProperty = node.type === 'Menu' ? 'items' : 'options'
|
||||||
|
if (node.type === 'Select') {
|
||||||
|
const legacyAliases: Record<string, string> = {
|
||||||
|
clearable: 'isClearable',
|
||||||
|
disabled: 'isDisabled',
|
||||||
|
multiple: 'isMulti',
|
||||||
|
searchable: 'isSearchable',
|
||||||
|
}
|
||||||
|
Object.entries(legacyAliases).forEach(([legacyName, runtimeName]) => {
|
||||||
|
if (props[runtimeName] === undefined && props[legacyName] !== undefined) {
|
||||||
|
props[runtimeName] = props[legacyName]
|
||||||
|
}
|
||||||
|
delete props[legacyName]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const optionsBinding = node.bindings?.[collectionProperty]
|
||||||
|
props[collectionProperty] = toSelectOptions(
|
||||||
|
props[collectionProperty],
|
||||||
|
optionsBinding?.labelPath,
|
||||||
|
optionsBinding?.valuePath,
|
||||||
|
)
|
||||||
|
if (node.type === 'Select' && 'value' in props) {
|
||||||
|
const options = props.options as Array<Record<string, unknown>>
|
||||||
|
props.defaultValue = options.find((option) => option.value === props.value) || null
|
||||||
|
delete props.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node.type === 'Input' && 'value' in props) {
|
||||||
|
props.defaultValue = props.value
|
||||||
|
delete props.value
|
||||||
|
}
|
||||||
|
if (node.type === 'Checkbox' && 'checked' in props) {
|
||||||
|
props.defaultChecked = Boolean(props.checked)
|
||||||
|
delete props.checked
|
||||||
|
}
|
||||||
|
if ('value' in props && !props.onChange) props.onChange = () => undefined
|
||||||
|
if ('checked' in props && !props.onChange) props.onChange = () => undefined
|
||||||
|
props.key = JSON.stringify([node.props, node.bindings])
|
||||||
|
return props
|
||||||
|
}
|
||||||
|
|
||||||
|
const PlatformPlaceholder = ({ node }: { node: DesignerNode }) => (
|
||||||
|
<div
|
||||||
|
className="flex min-h-40 items-center justify-center rounded-lg border border-dashed border-sky-300 bg-gradient-to-br from-sky-50 to-indigo-50 p-6 text-center dark:border-sky-800 dark:from-slate-900 dark:to-sky-950"
|
||||||
|
style={{ minHeight: String(node.props.height || '320px') }}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div className="mx-auto mb-3 flex h-10 w-10 items-center justify-center rounded-lg bg-sky-600 font-bold text-white">
|
||||||
|
S
|
||||||
|
</div>
|
||||||
|
<div className="font-semibold text-slate-800 dark:text-slate-100">
|
||||||
|
{node.type.replace(/View$/, '')}
|
||||||
|
</div>
|
||||||
|
<div className="mt-1 text-xs text-slate-500">
|
||||||
|
{String(node.props.listFormCode || 'Property panelinden List Form Code seçin')}
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 text-[11px] uppercase tracking-wider text-sky-700 dark:text-sky-300">
|
||||||
|
Platform görünümü
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const PLATFORM_VIEW_NAMES: Record<string, PlatformViewName> = {
|
||||||
|
ListView: 'List',
|
||||||
|
DataGridView: 'Grid',
|
||||||
|
TreeView: 'Tree',
|
||||||
|
GanttView: 'GanttView',
|
||||||
|
TodoBoard: 'TodoBoard',
|
||||||
|
CardView: 'CardView',
|
||||||
|
SchedulerView: 'SchedulerView',
|
||||||
|
PivotView: 'Pivot',
|
||||||
|
ChartView: 'Chart',
|
||||||
|
}
|
||||||
|
|
||||||
|
const GridColumnHeaders = ({ columns }: { columns: string[] }) => (
|
||||||
|
<div className="min-w-0 overflow-auto rounded-md border border-slate-200 bg-slate-100 [grid-column:1/-1] dark:border-slate-700 dark:bg-slate-800">
|
||||||
|
{columns.length ? (
|
||||||
|
<div className="flex min-w-max divide-x divide-slate-200 dark:divide-slate-700">
|
||||||
|
{columns.map((column) => (
|
||||||
|
<div
|
||||||
|
key={column}
|
||||||
|
className="min-w-32 px-3 py-2 text-xs font-semibold text-slate-600 dark:text-slate-200"
|
||||||
|
>
|
||||||
|
{column}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="px-3 py-2 text-center text-xs text-slate-400">
|
||||||
|
Data panelinden gösterilecek sütunları seçin.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const getGridCellText = (value: unknown) =>
|
||||||
|
typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value ?? '') || '—'
|
||||||
|
|
||||||
|
const GridDataTablePreview = ({
|
||||||
|
borderlessRow = false,
|
||||||
|
compact = false,
|
||||||
|
hoverable = true,
|
||||||
|
items,
|
||||||
|
overflow = true,
|
||||||
|
selectedColumns,
|
||||||
|
}: {
|
||||||
|
borderlessRow?: boolean
|
||||||
|
compact?: boolean
|
||||||
|
hoverable?: boolean
|
||||||
|
items: unknown[]
|
||||||
|
overflow?: boolean
|
||||||
|
selectedColumns?: string[]
|
||||||
|
}) => {
|
||||||
|
const firstObject = items.find(
|
||||||
|
(item): item is Record<string, unknown> =>
|
||||||
|
Boolean(item) && typeof item === 'object' && !Array.isArray(item),
|
||||||
|
)
|
||||||
|
const columns = selectedColumns ?? (firstObject ? Object.keys(firstObject) : ['value'])
|
||||||
|
|
||||||
|
if (!columns.length) {
|
||||||
|
return (
|
||||||
|
<div className="rounded-lg border border-dashed border-slate-300 bg-slate-50 p-4 text-center text-xs text-slate-500 [grid-column:1/-1] dark:border-slate-700 dark:bg-slate-900 dark:text-slate-400">
|
||||||
|
Preview için en az bir sütun seçin.
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`min-w-0 rounded-lg border border-slate-200 bg-white shadow-sm [grid-column:1/-1] dark:border-slate-700 dark:bg-slate-900 ${overflow ? 'overflow-auto' : 'overflow-visible'}`}
|
||||||
|
>
|
||||||
|
<table className={`w-full border-collapse text-left ${compact ? 'text-[11px]' : 'text-xs'}`}>
|
||||||
|
<thead className="bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-200">
|
||||||
|
<tr>
|
||||||
|
{columns.map((column) => (
|
||||||
|
<th
|
||||||
|
key={column}
|
||||||
|
className={`whitespace-nowrap border-b border-slate-200 dark:border-slate-700 ${compact ? 'px-2 py-1' : 'px-3 py-2'}`}
|
||||||
|
>
|
||||||
|
{column === 'value' ? 'Value' : column}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{items.slice(0, 100).map((item, rowIndex) => (
|
||||||
|
<tr
|
||||||
|
key={rowIndex}
|
||||||
|
className={`${borderlessRow ? '' : 'border-b border-slate-100 last:border-b-0 dark:border-slate-800'} ${hoverable ? 'transition-colors hover:bg-slate-50 dark:hover:bg-slate-800/70' : ''}`}
|
||||||
|
>
|
||||||
|
{columns.map((column) => {
|
||||||
|
const value = column === 'value' ? item : getDesignerValueByPath(item, column)
|
||||||
|
const text = getGridCellText(value)
|
||||||
|
return (
|
||||||
|
<td
|
||||||
|
key={column}
|
||||||
|
className={`max-w-64 truncate text-slate-800 dark:text-slate-100 ${compact ? 'px-2 py-1' : 'px-3 py-2'}`}
|
||||||
|
title={text}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</td>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderElement = (
|
||||||
|
node: DesignerNode,
|
||||||
|
children: React.ReactNode,
|
||||||
|
dataValues: Record<string, unknown>,
|
||||||
|
currentItem: unknown,
|
||||||
|
interactive: boolean,
|
||||||
|
renderCustomComponent?: (name: string, props?: Record<string, unknown>) => React.ReactNode,
|
||||||
|
) => {
|
||||||
|
if (node.type === 'Spacer') {
|
||||||
|
return <div aria-hidden="true" style={{ height: Number(node.props.height) || 24 }} />
|
||||||
|
}
|
||||||
|
if (node.type === 'FlexRow') {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={String(node.props.className || '')}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: Number(node.props.gap) || 0,
|
||||||
|
flexWrap: node.props.wrap ? 'wrap' : 'nowrap',
|
||||||
|
alignItems: String(node.props.align || 'stretch'),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (node.type === 'PageContainer') {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={String(node.props.className || '')}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: Number(node.props.gap) || 0,
|
||||||
|
margin: '0 auto',
|
||||||
|
maxWidth: String(node.props.maxWidth || '1280px'),
|
||||||
|
padding: Number(node.props.padding) || 0,
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (node.type === 'TwoColumns' || node.type === 'SidebarContent') {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={String(node.props.className || '')}
|
||||||
|
style={{
|
||||||
|
display: 'grid',
|
||||||
|
gap: Number(node.props.gap) || 0,
|
||||||
|
gridTemplateColumns:
|
||||||
|
node.type === 'SidebarContent'
|
||||||
|
? `${String(node.props.sidebarWidth || '280px')} minmax(0, 1fr)`
|
||||||
|
: 'repeat(2, minmax(0, 1fr))',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (node.type === 'HeaderContent') {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={String(node.props.className || '')}
|
||||||
|
style={{ display: 'flex', flexDirection: 'column', gap: Number(node.props.gap) || 0 }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (node.type === 'Grid') {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={String(node.props.className || '')}
|
||||||
|
style={{
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: `repeat(${Number(node.props.cols) || 3}, minmax(0, 1fr))`,
|
||||||
|
gap: (Number(node.props.gap) || 0) * 4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (node.kind === 'platform') {
|
||||||
|
if (interactive) return <PlatformPlaceholder node={node} />
|
||||||
|
return (
|
||||||
|
<PlatformViewHost
|
||||||
|
height={String(node.props.height || '420px')}
|
||||||
|
listFormCode={String(node.props.listFormCode || '')}
|
||||||
|
view={PLATFORM_VIEW_NAMES[node.type] || (node.type as PlatformViewName)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (node.kind === 'custom') {
|
||||||
|
return renderCustomComponent?.(node.type, { ...node.props, children }) || null
|
||||||
|
}
|
||||||
|
|
||||||
|
const childrenBinding = node.bindings?.children
|
||||||
|
const boundChildren = childrenBinding?.sourceId
|
||||||
|
? getBindingValue(childrenBinding, dataValues, currentItem)
|
||||||
|
: undefined
|
||||||
|
const content: React.ReactNode =
|
||||||
|
React.Children.count(children) > 0
|
||||||
|
? children
|
||||||
|
: boundChildren === null || boundChildren === undefined
|
||||||
|
? String(node.props.children ?? '') || undefined
|
||||||
|
: React.isValidElement(boundChildren)
|
||||||
|
? boundChildren
|
||||||
|
: typeof boundChildren === 'object'
|
||||||
|
? JSON.stringify(boundChildren)
|
||||||
|
: String(boundChildren)
|
||||||
|
const props = getPreviewProps(node, dataValues, currentItem)
|
||||||
|
if (node.type === 'Table') {
|
||||||
|
const legacyData = Array.isArray(props.data) ? props.data : []
|
||||||
|
const items =
|
||||||
|
Array.isArray(props.items) && (props.items.length > 0 || !legacyData.length)
|
||||||
|
? props.items
|
||||||
|
: legacyData
|
||||||
|
const legacyColumns = Array.isArray(node.props.columns)
|
||||||
|
? node.props.columns
|
||||||
|
.map((column) => {
|
||||||
|
if (typeof column === 'string') return column
|
||||||
|
if (!column || typeof column !== 'object' || Array.isArray(column)) return ''
|
||||||
|
const record = column as Record<string, unknown>
|
||||||
|
return String(record.dataIndex ?? record.key ?? '')
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
: undefined
|
||||||
|
return (
|
||||||
|
<GridDataTablePreview
|
||||||
|
borderlessRow={Boolean(node.props.borderlessRow)}
|
||||||
|
compact={Boolean(node.props.compact)}
|
||||||
|
hoverable={node.props.hoverable !== false}
|
||||||
|
items={items}
|
||||||
|
overflow={node.props.overflow !== false}
|
||||||
|
selectedColumns={
|
||||||
|
Array.isArray(node.props.dataColumns)
|
||||||
|
? node.props.dataColumns.filter(
|
||||||
|
(column): column is string => typeof column === 'string',
|
||||||
|
)
|
||||||
|
: legacyColumns
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (node.type === 'Menu') {
|
||||||
|
const menuProps = { ...props }
|
||||||
|
const options = Array.isArray(menuProps.items)
|
||||||
|
? (menuProps.items as Array<Record<string, unknown>>)
|
||||||
|
: []
|
||||||
|
delete menuProps.items
|
||||||
|
delete menuProps.variant
|
||||||
|
menuProps.defaultActiveKeys = normalizeDesignerKeyList(menuProps.defaultActiveKeys)
|
||||||
|
menuProps.defaultExpandedKeys = normalizeDesignerKeyList(menuProps.defaultExpandedKeys)
|
||||||
|
return (
|
||||||
|
<UiKit.Menu {...(menuProps as React.ComponentProps<typeof UiKit.Menu>)}>
|
||||||
|
{options.map((option, index) => (
|
||||||
|
<UiKit.Menu.MenuItem
|
||||||
|
key={String(option.value ?? index)}
|
||||||
|
eventKey={String(option.value ?? index)}
|
||||||
|
>
|
||||||
|
{String(option.label ?? option.value ?? `Menü ${index + 1}`)}
|
||||||
|
</UiKit.Menu.MenuItem>
|
||||||
|
))}
|
||||||
|
{content}
|
||||||
|
</UiKit.Menu>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (node.type === 'checkbox') {
|
||||||
|
return <input {...(props as React.InputHTMLAttributes<HTMLInputElement>)} type="checkbox" />
|
||||||
|
}
|
||||||
|
if (node.kind === 'html') return React.createElement(node.type, props, content)
|
||||||
|
|
||||||
|
const Component = resolveUiComponent(node.type)
|
||||||
|
if (!Component) {
|
||||||
|
return (
|
||||||
|
<div className="rounded-md border border-slate-200 bg-slate-50 p-3 text-sm text-slate-600 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200">
|
||||||
|
{node.type}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return React.createElement(Component, props, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
const NodeView = ({
|
||||||
|
node,
|
||||||
|
index,
|
||||||
|
selectedId,
|
||||||
|
interactive,
|
||||||
|
onSelect,
|
||||||
|
onDropComponent,
|
||||||
|
onMove,
|
||||||
|
onDuplicate,
|
||||||
|
onDelete,
|
||||||
|
renderCustomComponent,
|
||||||
|
dataValues,
|
||||||
|
currentItem,
|
||||||
|
}: {
|
||||||
|
node: DesignerNode
|
||||||
|
index: number
|
||||||
|
selectedId: string | null
|
||||||
|
interactive: boolean
|
||||||
|
onSelect?: (id: string) => void
|
||||||
|
onDropComponent?: (definitionName: string, parentId: string | null) => void
|
||||||
|
onMove?: (id: string, direction: -1 | 1) => void
|
||||||
|
onDuplicate?: (id: string) => void
|
||||||
|
onDelete?: (id: string) => void
|
||||||
|
renderCustomComponent?: (name: string, props?: Record<string, unknown>) => React.ReactNode
|
||||||
|
dataValues: Record<string, unknown>
|
||||||
|
currentItem?: unknown
|
||||||
|
}) => {
|
||||||
|
const selected = interactive && selectedId === node.id
|
||||||
|
const acceptsDroppedChildren = [
|
||||||
|
'PageContainer',
|
||||||
|
'TwoColumns',
|
||||||
|
'SidebarContent',
|
||||||
|
'HeaderContent',
|
||||||
|
'FlexRow',
|
||||||
|
'div',
|
||||||
|
'Card',
|
||||||
|
'FormContainer',
|
||||||
|
].includes(node.type)
|
||||||
|
const itemsBinding = ['Grid', 'Table'].includes(node.type) ? node.bindings?.items : undefined
|
||||||
|
const boundItems = itemsBinding?.sourceId
|
||||||
|
? getBindingValue(itemsBinding, dataValues, currentItem)
|
||||||
|
: node.props.items
|
||||||
|
const sourceRootItems = itemsBinding?.sourceId ? dataValues[itemsBinding.sourceId] : undefined
|
||||||
|
const effectiveBoundItems =
|
||||||
|
Array.isArray(boundItems) || currentItem !== undefined
|
||||||
|
? boundItems
|
||||||
|
: Array.isArray(sourceRootItems)
|
||||||
|
? sourceRootItems
|
||||||
|
: boundItems
|
||||||
|
const repeatedItems = Array.isArray(effectiveBoundItems)
|
||||||
|
? interactive && !(node.type === 'Grid' && node.children.length === 0)
|
||||||
|
? effectiveBoundItems.slice(0, 1)
|
||||||
|
: effectiveBoundItems
|
||||||
|
: []
|
||||||
|
const childContexts = repeatedItems.length ? repeatedItems : [currentItem]
|
||||||
|
const children = childContexts.flatMap((childItem, itemIndex) =>
|
||||||
|
node.children.map((child, childIndex) => (
|
||||||
|
<NodeView
|
||||||
|
key={`${child.id}_${itemIndex}`}
|
||||||
|
node={child}
|
||||||
|
index={childIndex}
|
||||||
|
selectedId={selectedId}
|
||||||
|
interactive={interactive}
|
||||||
|
renderCustomComponent={renderCustomComponent}
|
||||||
|
dataValues={dataValues}
|
||||||
|
currentItem={childItem}
|
||||||
|
onSelect={onSelect}
|
||||||
|
onDropComponent={onDropComponent}
|
||||||
|
onMove={onMove}
|
||||||
|
onDuplicate={onDuplicate}
|
||||||
|
onDelete={onDelete}
|
||||||
|
/>
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
const renderedChildren =
|
||||||
|
node.type === 'Grid' && interactive && node.children.length === 0
|
||||||
|
? repeatedItems.length
|
||||||
|
? [
|
||||||
|
<GridDataTablePreview
|
||||||
|
key={`grid_data_${node.id}`}
|
||||||
|
borderlessRow={Boolean(node.props.borderlessRow)}
|
||||||
|
compact={Boolean(node.props.compact)}
|
||||||
|
hoverable={node.props.hoverable !== false}
|
||||||
|
items={repeatedItems}
|
||||||
|
overflow={node.props.overflow !== false}
|
||||||
|
selectedColumns={
|
||||||
|
Array.isArray(node.props.dataColumns)
|
||||||
|
? node.props.dataColumns.filter(
|
||||||
|
(column): column is string => typeof column === 'string',
|
||||||
|
)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>,
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
<GridColumnHeaders
|
||||||
|
key={`grid_headers_${node.id}`}
|
||||||
|
columns={
|
||||||
|
Array.isArray(node.props.dataColumns)
|
||||||
|
? node.props.dataColumns.filter(
|
||||||
|
(column): column is string => typeof column === 'string',
|
||||||
|
)
|
||||||
|
: []
|
||||||
|
}
|
||||||
|
/>,
|
||||||
|
]
|
||||||
|
: node.type === 'Grid' &&
|
||||||
|
!interactive &&
|
||||||
|
itemsBinding?.sourceId &&
|
||||||
|
effectiveBoundItems === undefined
|
||||||
|
? [
|
||||||
|
<div
|
||||||
|
key={`grid_loading_${node.id}`}
|
||||||
|
className="rounded-lg border border-slate-200 bg-slate-50 p-4 text-center text-xs text-slate-500 [grid-column:1/-1] dark:border-slate-700 dark:bg-slate-900 dark:text-slate-400"
|
||||||
|
>
|
||||||
|
Veriler yükleniyor…
|
||||||
|
</div>,
|
||||||
|
]
|
||||||
|
: node.type === 'Grid' &&
|
||||||
|
itemsBinding?.sourceId &&
|
||||||
|
effectiveBoundItems !== undefined &&
|
||||||
|
!Array.isArray(effectiveBoundItems)
|
||||||
|
? [
|
||||||
|
<div
|
||||||
|
key={`grid_binding_error_${node.id}`}
|
||||||
|
className="rounded-lg border border-amber-300 bg-amber-50 p-3 text-xs text-amber-800 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-200"
|
||||||
|
>
|
||||||
|
Grid items bağlantısı bir koleksiyon döndürmelidir. Seçili path:{' '}
|
||||||
|
<code>{itemsBinding.path || '(root)'}</code>
|
||||||
|
</div>,
|
||||||
|
]
|
||||||
|
: node.type === 'Grid' && node.children.length === 0 && repeatedItems.length
|
||||||
|
? [
|
||||||
|
<GridDataTablePreview
|
||||||
|
key={`grid_data_${node.id}`}
|
||||||
|
borderlessRow={Boolean(node.props.borderlessRow)}
|
||||||
|
compact={Boolean(node.props.compact)}
|
||||||
|
hoverable={node.props.hoverable !== false}
|
||||||
|
items={repeatedItems}
|
||||||
|
overflow={node.props.overflow !== false}
|
||||||
|
selectedColumns={
|
||||||
|
Array.isArray(node.props.dataColumns)
|
||||||
|
? node.props.dataColumns.filter(
|
||||||
|
(column): column is string => typeof column === 'string',
|
||||||
|
)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>,
|
||||||
|
]
|
||||||
|
: children
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`group/node relative min-h-[28px] rounded-md ${
|
||||||
|
interactive
|
||||||
|
? selected
|
||||||
|
? 'outline outline-2 outline-sky-500 outline-offset-2'
|
||||||
|
: 'outline outline-1 outline-transparent hover:outline-sky-300'
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
|
data-designer-node={node.id}
|
||||||
|
draggable={interactive}
|
||||||
|
onClickCapture={() => {
|
||||||
|
if (interactive) onSelect?.(node.id)
|
||||||
|
}}
|
||||||
|
onClick={(event) => {
|
||||||
|
if (!interactive) return
|
||||||
|
event.stopPropagation()
|
||||||
|
onSelect?.(node.id)
|
||||||
|
}}
|
||||||
|
onDragStart={(event) => {
|
||||||
|
if (!interactive) return
|
||||||
|
event.stopPropagation()
|
||||||
|
event.dataTransfer.setData(
|
||||||
|
DESIGNER_DRAG_TYPE,
|
||||||
|
JSON.stringify({ source: 'canvas', nodeId: node.id }),
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
onDragOver={(event) => {
|
||||||
|
if (!interactive || !acceptsDroppedChildren) return
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
event.dataTransfer.dropEffect = 'copy'
|
||||||
|
}}
|
||||||
|
onDrop={(event) => {
|
||||||
|
if (!interactive || !acceptsDroppedChildren) return
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
const raw =
|
||||||
|
event.dataTransfer.getData(DESIGNER_DRAG_TYPE) ||
|
||||||
|
event.dataTransfer.getData('text/plain')
|
||||||
|
if (!raw) return
|
||||||
|
const payload = JSON.parse(raw)
|
||||||
|
if (payload.source === 'library') onDropComponent?.(payload.name, node.id)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{interactive && (
|
||||||
|
<div
|
||||||
|
className={`absolute -top-7 right-0 z-20 items-center overflow-hidden rounded-md bg-sky-600 text-white shadow-md ${
|
||||||
|
selected ? 'flex' : 'hidden group-hover/node:flex'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span className="flex items-center gap-1 px-2 text-[10px] font-semibold">
|
||||||
|
<FaGripVertical /> {node.type}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
className="p-1.5 hover:bg-sky-700"
|
||||||
|
title="Yukarı taşı"
|
||||||
|
type="button"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
onMove?.(node.id, -1)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaArrowUp />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="p-1.5 hover:bg-sky-700"
|
||||||
|
title="Aşağı taşı"
|
||||||
|
type="button"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
onMove?.(node.id, 1)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaArrowDown />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="p-1.5 hover:bg-sky-700"
|
||||||
|
title="Çoğalt"
|
||||||
|
type="button"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
onDuplicate?.(node.id)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaClone />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="p-1.5 hover:bg-red-600"
|
||||||
|
title="Sil"
|
||||||
|
type="button"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
onDelete?.(node.id)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaTrash />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<PreviewBoundary
|
||||||
|
name={node.type}
|
||||||
|
resetKey={JSON.stringify([node.props, node.bindings, node.events])}
|
||||||
|
>
|
||||||
|
{renderElement(
|
||||||
|
node,
|
||||||
|
renderedChildren,
|
||||||
|
dataValues,
|
||||||
|
currentItem,
|
||||||
|
interactive,
|
||||||
|
renderCustomComponent,
|
||||||
|
)}
|
||||||
|
</PreviewBoundary>
|
||||||
|
{interactive &&
|
||||||
|
node.children.length === 0 &&
|
||||||
|
acceptsDroppedChildren && (
|
||||||
|
<div className="m-2 rounded border border-dashed border-slate-300 px-3 py-4 text-center text-xs text-slate-400">
|
||||||
|
Bileşeni buraya bırakın
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<span className="hidden">{index}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const VisualCanvas = ({
|
||||||
|
nodes,
|
||||||
|
selectedId,
|
||||||
|
background,
|
||||||
|
interactive = true,
|
||||||
|
onSelect,
|
||||||
|
onDropComponent,
|
||||||
|
onMove,
|
||||||
|
onDuplicate,
|
||||||
|
onDelete,
|
||||||
|
renderCustomComponent,
|
||||||
|
dataValues = {},
|
||||||
|
}: VisualCanvasProps) => {
|
||||||
|
const previewDataValues = dataValues
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="min-h-full p-8 text-slate-900 dark:text-slate-100"
|
||||||
|
style={{ background }}
|
||||||
|
onClick={() => interactive && onSelect?.('')}
|
||||||
|
onDragOver={(event) => interactive && event.preventDefault()}
|
||||||
|
onDrop={(event) => {
|
||||||
|
if (!interactive) return
|
||||||
|
event.preventDefault()
|
||||||
|
const raw =
|
||||||
|
event.dataTransfer.getData(DESIGNER_DRAG_TYPE) ||
|
||||||
|
event.dataTransfer.getData('text/plain')
|
||||||
|
if (!raw) return
|
||||||
|
const payload = JSON.parse(raw)
|
||||||
|
if (payload.source === 'library') onDropComponent?.(payload.name, null)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{nodes.length ? (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{nodes.map((node, index) => (
|
||||||
|
<NodeView
|
||||||
|
key={node.id}
|
||||||
|
node={node}
|
||||||
|
index={index}
|
||||||
|
selectedId={selectedId}
|
||||||
|
interactive={interactive}
|
||||||
|
renderCustomComponent={renderCustomComponent}
|
||||||
|
dataValues={previewDataValues}
|
||||||
|
onSelect={onSelect}
|
||||||
|
onDropComponent={onDropComponent}
|
||||||
|
onMove={onMove}
|
||||||
|
onDuplicate={onDuplicate}
|
||||||
|
onDelete={onDelete}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex min-h-[520px] items-center justify-center rounded-xl border-2 border-dashed border-slate-300 bg-white/70 text-center dark:border-slate-700 dark:bg-slate-900/70">
|
||||||
|
<div className="w-full max-w-2xl px-6">
|
||||||
|
<div className="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-2xl bg-sky-100 text-2xl text-sky-600">
|
||||||
|
+
|
||||||
|
</div>
|
||||||
|
<h3 className="font-semibold text-slate-700 dark:text-slate-200">
|
||||||
|
Sayfa layout’unu seçin
|
||||||
|
</h3>
|
||||||
|
<p className="mt-1 text-sm text-slate-500">
|
||||||
|
Hazır bir yerleşimle başlayın veya Toolbox’taki My Components grubundan kendi layout
|
||||||
|
komponentinizi kullanın.
|
||||||
|
</p>
|
||||||
|
{interactive && (
|
||||||
|
<div className="mt-5 grid grid-cols-2 gap-2 sm:grid-cols-4">
|
||||||
|
{[
|
||||||
|
['PageContainer', 'Sayfa alanı', '□'],
|
||||||
|
['TwoColumns', 'İki kolon', '▥'],
|
||||||
|
['SidebarContent', 'Sidebar + içerik', '◧'],
|
||||||
|
['HeaderContent', 'Üst alan + içerik', '⊟'],
|
||||||
|
].map(([name, label, icon]) => (
|
||||||
|
<button
|
||||||
|
key={name}
|
||||||
|
className="rounded-lg border border-slate-200 bg-white p-3 text-xs font-semibold text-slate-600 shadow-sm transition hover:border-sky-400 hover:text-sky-700 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300"
|
||||||
|
type="button"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
onDropComponent?.(name, null)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="mb-1 block text-xl text-sky-500">{icon}</span>
|
||||||
|
{label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VisualCanvas
|
||||||
475
ui/src/components/visualDesigner/catalog.ts
Normal file
475
ui/src/components/visualDesigner/catalog.ts
Normal file
|
|
@ -0,0 +1,475 @@
|
||||||
|
import { CUSTOM_COMPONENTS, HTML_ELEMENTS } from '@/components/codeLayout/data/componentDefinitions'
|
||||||
|
import generatedComponentProps from './generated/componentProps.json'
|
||||||
|
import type { DesignerComponentDefinition, DesignerNodeKind } from './types'
|
||||||
|
|
||||||
|
interface GeneratedProperty {
|
||||||
|
name: string
|
||||||
|
tsType: string
|
||||||
|
type: 'string' | 'boolean' | 'number' | 'function' | 'object' | 'array' | 'select'
|
||||||
|
required: boolean
|
||||||
|
options?: string[]
|
||||||
|
defaultValue?: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GeneratedComponentMetadata {
|
||||||
|
interfaceName: string
|
||||||
|
sourceFile: string
|
||||||
|
properties: GeneratedProperty[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const generatedMetadata = generatedComponentProps as Record<string, GeneratedComponentMetadata>
|
||||||
|
const CONTAINER_NAMES = new Set(['div', 'Card', 'FormContainer', 'Tabs', 'Timeline'])
|
||||||
|
const DATA_COMPONENT_NAMES = new Set(['AutoComplete', 'Grid', 'Menu', 'Pagination', 'Select', 'Table'])
|
||||||
|
const HTML_UI_DUPLICATES = new Set(['input', 'button', 'textarea', 'select', 'checkbox'])
|
||||||
|
const UI_PROPERTY_ALIASES: Record<string, Record<string, string>> = {
|
||||||
|
Select: {
|
||||||
|
clearable: 'isClearable',
|
||||||
|
disabled: 'isDisabled',
|
||||||
|
multiple: 'isMulti',
|
||||||
|
onSearch: 'onInputChange',
|
||||||
|
searchable: 'isSearchable',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
export const DESIGNER_PRIMARY_EVENTS: Record<string, string[]> = {
|
||||||
|
Button: ['onClick'],
|
||||||
|
Checkbox: ['onChange'],
|
||||||
|
Input: ['onChange'],
|
||||||
|
Menu: ['onSelect'],
|
||||||
|
Select: ['onChange'],
|
||||||
|
}
|
||||||
|
|
||||||
|
const DESIGNER_EVENT_SNIPPETS: Record<string, Record<string, string>> = {
|
||||||
|
Button: {
|
||||||
|
onClick: `// Butona tıklandığında çalışır.
|
||||||
|
console.log('Button clicked', event)`,
|
||||||
|
},
|
||||||
|
Checkbox: {
|
||||||
|
onChange: `// Checkbox bileşeninin yeni checked değeri.
|
||||||
|
const checked = event.checked
|
||||||
|
console.log('Checked:', checked)`,
|
||||||
|
},
|
||||||
|
Input: {
|
||||||
|
onChange: `// Input alanının güncel metin değeri.
|
||||||
|
const value = event.target.value
|
||||||
|
console.log('Value:', value)`,
|
||||||
|
},
|
||||||
|
Menu: {
|
||||||
|
onSelect: `// Seçilen menü öğesinin value değeridir.
|
||||||
|
console.log('Menu value:', event)`,
|
||||||
|
},
|
||||||
|
Select: {
|
||||||
|
onChange: `// Seçilen option nesnesi; temizlendiğinde null olur.
|
||||||
|
const selectedValue = event?.value ?? null
|
||||||
|
const selectedLabel = event?.label ?? ''
|
||||||
|
console.log({ selectedValue, selectedLabel })`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getDesignerEventSnippet = (componentName: string, eventName: string) =>
|
||||||
|
DESIGNER_EVENT_SNIPPETS[componentName]?.[eventName] ||
|
||||||
|
`// ${componentName}.${eventName}(event)\nconsole.log(event)`
|
||||||
|
const COLLECTION_ITEMS_PROPERTY = {
|
||||||
|
name: 'items',
|
||||||
|
type: 'array' as const,
|
||||||
|
value: [],
|
||||||
|
category: 'properties' as const,
|
||||||
|
description: 'Statik veya endpoint kaynaklı veri koleksiyonu',
|
||||||
|
tsType: 'unknown[]',
|
||||||
|
options: undefined,
|
||||||
|
required: false,
|
||||||
|
}
|
||||||
|
const GRID_COMPACT_PROPERTY = {
|
||||||
|
name: 'compact',
|
||||||
|
type: 'boolean' as const,
|
||||||
|
value: false,
|
||||||
|
category: 'properties' as const,
|
||||||
|
description: 'Satır ve hücre boşluklarını azaltır',
|
||||||
|
tsType: 'boolean',
|
||||||
|
options: undefined,
|
||||||
|
required: false,
|
||||||
|
}
|
||||||
|
const GRID_TABLE_BEHAVIOR_PROPERTIES = [
|
||||||
|
{
|
||||||
|
name: 'hoverable',
|
||||||
|
type: 'boolean' as const,
|
||||||
|
value: true,
|
||||||
|
category: 'properties' as const,
|
||||||
|
description: 'Satırların üzerine gelindiğinde vurgu rengi gösterir',
|
||||||
|
tsType: 'boolean',
|
||||||
|
options: undefined,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'overflow',
|
||||||
|
type: 'boolean' as const,
|
||||||
|
value: true,
|
||||||
|
category: 'properties' as const,
|
||||||
|
description: 'Geniş tablolarda yatay kaydırmayı etkinleştirir',
|
||||||
|
tsType: 'boolean',
|
||||||
|
options: undefined,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'borderlessRow',
|
||||||
|
type: 'boolean' as const,
|
||||||
|
value: false,
|
||||||
|
category: 'properties' as const,
|
||||||
|
description: 'Veri satırları arasındaki kenarlıkları kaldırır',
|
||||||
|
tsType: 'boolean',
|
||||||
|
options: undefined,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const SELECT_OPTIONS_PROPERTY = {
|
||||||
|
name: 'options',
|
||||||
|
type: 'array' as const,
|
||||||
|
value: [
|
||||||
|
{ label: 'Seçenek 1', value: '1' },
|
||||||
|
{ label: 'Seçenek 2', value: '2' },
|
||||||
|
],
|
||||||
|
category: 'properties' as const,
|
||||||
|
description: 'Select items; statik olarak veya endpoint koleksiyonundan beslenebilir',
|
||||||
|
tsType: 'Array<{ label: string; value: unknown }>',
|
||||||
|
options: undefined,
|
||||||
|
required: false,
|
||||||
|
}
|
||||||
|
const MENU_ITEMS_PROPERTY = {
|
||||||
|
...SELECT_OPTIONS_PROPERTY,
|
||||||
|
name: 'items',
|
||||||
|
description: 'Menu items; statik olarak veya endpoint koleksiyonundan beslenebilir',
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeDefinition = (
|
||||||
|
definition: (typeof CUSTOM_COMPONENTS)[number],
|
||||||
|
kind: DesignerNodeKind,
|
||||||
|
toolboxGroup: DesignerComponentDefinition['toolboxGroup'],
|
||||||
|
): DesignerComponentDefinition => {
|
||||||
|
const sourceProperties =
|
||||||
|
definition.name === 'Grid'
|
||||||
|
? definition.properties.filter((property) => property.name !== 'children')
|
||||||
|
: definition.properties
|
||||||
|
let properties =
|
||||||
|
['Grid', 'Table'].includes(definition.name) &&
|
||||||
|
!sourceProperties.some((property) => property.name === 'items')
|
||||||
|
? [COLLECTION_ITEMS_PROPERTY, ...sourceProperties]
|
||||||
|
: sourceProperties
|
||||||
|
if (definition.name === 'Grid' && !properties.some((property) => property.name === 'compact')) {
|
||||||
|
properties = [GRID_COMPACT_PROPERTY, ...properties]
|
||||||
|
}
|
||||||
|
if (definition.name === 'Grid') {
|
||||||
|
properties = [
|
||||||
|
...GRID_TABLE_BEHAVIOR_PROPERTIES.filter(
|
||||||
|
(candidate) => !properties.some((property) => property.name === candidate.name),
|
||||||
|
),
|
||||||
|
...properties,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...definition,
|
||||||
|
properties,
|
||||||
|
kind,
|
||||||
|
toolboxGroup,
|
||||||
|
acceptsChildren: CONTAINER_NAMES.has(definition.name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fallbackValue = (type: GeneratedProperty['type']) => {
|
||||||
|
if (type === 'boolean') return false
|
||||||
|
if (type === 'number') return 0
|
||||||
|
if (type === 'array') return []
|
||||||
|
if (type === 'object') return {}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const inferUiCategory = (name: string) => {
|
||||||
|
if (
|
||||||
|
new Set([
|
||||||
|
'AutoComplete',
|
||||||
|
'Checkbox',
|
||||||
|
'Chips',
|
||||||
|
'ColorPicker',
|
||||||
|
'DatePicker',
|
||||||
|
'Input',
|
||||||
|
'Knob',
|
||||||
|
'Radio',
|
||||||
|
'Rate',
|
||||||
|
'Select',
|
||||||
|
'Slider',
|
||||||
|
'Switcher',
|
||||||
|
'TimeInput',
|
||||||
|
'Upload',
|
||||||
|
]).has(name)
|
||||||
|
)
|
||||||
|
return 'form'
|
||||||
|
if (new Set(['Alert', 'Notification', 'Progress', 'Skeleton', 'Spinner']).has(name))
|
||||||
|
return 'feedback'
|
||||||
|
if (new Set(['Breadcrumb', 'Dropdown', 'Menu', 'Pagination', 'Steps', 'Tabs']).has(name))
|
||||||
|
return 'navigation'
|
||||||
|
return 'display'
|
||||||
|
}
|
||||||
|
|
||||||
|
const generatedPropertiesToDesigner = (
|
||||||
|
generated: GeneratedComponentMetadata,
|
||||||
|
existingProperties = new Map<string, (typeof CUSTOM_COMPONENTS)[number]['properties'][number]>(),
|
||||||
|
) =>
|
||||||
|
generated.properties.map((property) => {
|
||||||
|
const existing = existingProperties.get(property.name)
|
||||||
|
return {
|
||||||
|
name: property.name,
|
||||||
|
type: property.type,
|
||||||
|
value:
|
||||||
|
property.defaultValue !== undefined
|
||||||
|
? property.defaultValue
|
||||||
|
: existing?.value !== undefined
|
||||||
|
? existing.value
|
||||||
|
: fallbackValue(property.type),
|
||||||
|
options: property.options || existing?.options,
|
||||||
|
description: property.tsType,
|
||||||
|
category:
|
||||||
|
property.type === 'function'
|
||||||
|
? ('events' as const)
|
||||||
|
: existing?.category ||
|
||||||
|
(property.name === 'className' || property.name === 'style'
|
||||||
|
? ('styling' as const)
|
||||||
|
: ('properties' as const)),
|
||||||
|
tsType: property.tsType,
|
||||||
|
required: property.required,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const normalizeUiDefinition = (
|
||||||
|
definition: (typeof CUSTOM_COMPONENTS)[number],
|
||||||
|
): DesignerComponentDefinition => {
|
||||||
|
const generated = generatedMetadata[definition.name]
|
||||||
|
if (!generated) {
|
||||||
|
return normalizeDefinition(
|
||||||
|
definition,
|
||||||
|
'ui',
|
||||||
|
DATA_COMPONENT_NAMES.has(definition.name) ? 'data' : 'ui',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const propertyAliases = UI_PROPERTY_ALIASES[definition.name] || {}
|
||||||
|
const curatedProperties = definition.properties
|
||||||
|
.filter(
|
||||||
|
(property) =>
|
||||||
|
(definition.name !== 'Select' || property.name !== 'items') &&
|
||||||
|
(definition.name !== 'Menu' || property.name !== 'variant') &&
|
||||||
|
(definition.name !== 'Table' || !['columns', 'data'].includes(property.name)),
|
||||||
|
)
|
||||||
|
.map((property) => ({
|
||||||
|
...property,
|
||||||
|
name: propertyAliases[property.name] || property.name,
|
||||||
|
}))
|
||||||
|
const existingProperties = new Map(curatedProperties.map((property) => [property.name, property]))
|
||||||
|
const generatedProperties = generatedPropertiesToDesigner(generated, existingProperties)
|
||||||
|
if (
|
||||||
|
['Select', 'AutoComplete', 'Menu'].includes(definition.name) &&
|
||||||
|
!generatedProperties.some(
|
||||||
|
(property) => property.name === (definition.name === 'Menu' ? 'items' : 'options'),
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
generatedProperties.unshift(
|
||||||
|
definition.name === 'Menu' ? MENU_ITEMS_PROPERTY : SELECT_OPTIONS_PROPERTY,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
['Grid', 'Table'].includes(definition.name) &&
|
||||||
|
!generatedProperties.some((property) => property.name === 'items')
|
||||||
|
) {
|
||||||
|
generatedProperties.unshift(COLLECTION_ITEMS_PROPERTY)
|
||||||
|
}
|
||||||
|
const generatedPropertyNames = new Set(generatedProperties.map((property) => property.name))
|
||||||
|
const properties = [
|
||||||
|
...generatedProperties,
|
||||||
|
// Keep explicitly curated inherited React props such as Input.value and placeholder.
|
||||||
|
...curatedProperties.filter((property) => !generatedPropertyNames.has(property.name)),
|
||||||
|
].filter(
|
||||||
|
(property) =>
|
||||||
|
!(definition.name === 'Checkbox' && property.name === 'defaultChecked') &&
|
||||||
|
!(definition.name === 'Menu' && property.name === 'variant'),
|
||||||
|
)
|
||||||
|
const primaryEvents = DESIGNER_PRIMARY_EVENTS[definition.name]
|
||||||
|
return {
|
||||||
|
...definition,
|
||||||
|
kind: 'ui',
|
||||||
|
toolboxGroup: DATA_COMPONENT_NAMES.has(definition.name) ? 'data' : 'ui',
|
||||||
|
acceptsChildren: CONTAINER_NAMES.has(definition.name),
|
||||||
|
sourceFile: generated.sourceFile,
|
||||||
|
interfaceName: generated.interfaceName,
|
||||||
|
properties: primaryEvents
|
||||||
|
? properties.filter(
|
||||||
|
(property) =>
|
||||||
|
(property.category !== 'events' && property.type !== 'function') ||
|
||||||
|
primaryEvents.includes(property.name),
|
||||||
|
)
|
||||||
|
: properties,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const platformDefinition = (
|
||||||
|
name: string,
|
||||||
|
sourceName: string,
|
||||||
|
description: string,
|
||||||
|
height = '520px',
|
||||||
|
): DesignerComponentDefinition => ({
|
||||||
|
name,
|
||||||
|
sourceName,
|
||||||
|
icon: 'PanelsTopLeft',
|
||||||
|
category: 'platform',
|
||||||
|
kind: 'platform',
|
||||||
|
toolboxGroup: 'platform',
|
||||||
|
description,
|
||||||
|
properties: [
|
||||||
|
{ name: 'listFormCode', type: 'string', value: '', category: 'properties', required: true },
|
||||||
|
{ name: 'height', type: 'string', value: height, category: 'styling' },
|
||||||
|
],
|
||||||
|
hooks: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const PLATFORM_COMPONENTS: DesignerComponentDefinition[] = [
|
||||||
|
platformDefinition(
|
||||||
|
'ListView',
|
||||||
|
'List',
|
||||||
|
'Tüm etkin platform görünümlerini /admin/list/:listFormCode ile aynı şekilde yönetir',
|
||||||
|
),
|
||||||
|
platformDefinition('DataGridView', 'Grid', 'Sozsoft dinamik veri grid görünümü'),
|
||||||
|
platformDefinition('TreeView', 'Tree', 'Sozsoft dinamik ağaç görünümü'),
|
||||||
|
platformDefinition('GanttView', 'GanttView', 'Sozsoft dinamik Gantt görünümü'),
|
||||||
|
platformDefinition('TodoBoard', 'TodoBoard', 'Sozsoft dinamik iş panosu'),
|
||||||
|
platformDefinition('CardView', 'CardView', 'Sozsoft dinamik kart görünümü'),
|
||||||
|
platformDefinition('SchedulerView', 'SchedulerView', 'Sozsoft dinamik planlayıcı görünümü'),
|
||||||
|
platformDefinition('PivotView', 'Pivot', 'Sozsoft dinamik pivot görünümü'),
|
||||||
|
platformDefinition('ChartView', 'Chart', 'Sozsoft dinamik grafik görünümü', '420px'),
|
||||||
|
]
|
||||||
|
|
||||||
|
export const DESIGNER_EXTRAS: DesignerComponentDefinition[] = [
|
||||||
|
{
|
||||||
|
name: 'PageContainer',
|
||||||
|
icon: 'PanelTop',
|
||||||
|
category: 'layout',
|
||||||
|
kind: 'layout',
|
||||||
|
toolboxGroup: 'layout',
|
||||||
|
description: 'Intranet içeriği için ortalanmış sayfa alanı',
|
||||||
|
acceptsChildren: true,
|
||||||
|
properties: [
|
||||||
|
{ name: 'maxWidth', type: 'string', value: '1280px', category: 'styling' },
|
||||||
|
{ name: 'padding', type: 'number', value: 24, category: 'styling' },
|
||||||
|
{ name: 'gap', type: 'number', value: 16, category: 'styling' },
|
||||||
|
{ name: 'className', type: 'string', value: '', category: 'styling' },
|
||||||
|
],
|
||||||
|
hooks: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TwoColumns',
|
||||||
|
icon: 'Columns2',
|
||||||
|
category: 'layout',
|
||||||
|
kind: 'layout',
|
||||||
|
toolboxGroup: 'layout',
|
||||||
|
description: 'Sayfayı iki eşit kolona böler',
|
||||||
|
acceptsChildren: true,
|
||||||
|
properties: [
|
||||||
|
{ name: 'gap', type: 'number', value: 16, category: 'styling' },
|
||||||
|
{ name: 'className', type: 'string', value: '', category: 'styling' },
|
||||||
|
],
|
||||||
|
hooks: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'SidebarContent',
|
||||||
|
icon: 'PanelLeft',
|
||||||
|
category: 'layout',
|
||||||
|
kind: 'layout',
|
||||||
|
toolboxGroup: 'layout',
|
||||||
|
description: 'Dar sidebar ve geniş içerik alanı',
|
||||||
|
acceptsChildren: true,
|
||||||
|
properties: [
|
||||||
|
{ name: 'sidebarWidth', type: 'string', value: '280px', category: 'styling' },
|
||||||
|
{ name: 'gap', type: 'number', value: 16, category: 'styling' },
|
||||||
|
{ name: 'className', type: 'string', value: '', category: 'styling' },
|
||||||
|
],
|
||||||
|
hooks: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'HeaderContent',
|
||||||
|
icon: 'PanelTop',
|
||||||
|
category: 'layout',
|
||||||
|
kind: 'layout',
|
||||||
|
toolboxGroup: 'layout',
|
||||||
|
description: 'Üst alan ve alt içerik için dikey yerleşim',
|
||||||
|
acceptsChildren: true,
|
||||||
|
properties: [
|
||||||
|
{ name: 'gap', type: 'number', value: 16, category: 'styling' },
|
||||||
|
{ name: 'className', type: 'string', value: '', category: 'styling' },
|
||||||
|
],
|
||||||
|
hooks: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'FlexRow',
|
||||||
|
icon: 'Columns3',
|
||||||
|
category: 'layout',
|
||||||
|
kind: 'layout',
|
||||||
|
toolboxGroup: 'layout',
|
||||||
|
description: 'Yatay ve responsive yerleşim',
|
||||||
|
acceptsChildren: true,
|
||||||
|
properties: [
|
||||||
|
{ name: 'gap', type: 'number', value: 16, category: 'styling' },
|
||||||
|
{ name: 'wrap', type: 'boolean', value: true, category: 'styling' },
|
||||||
|
{
|
||||||
|
name: 'align',
|
||||||
|
type: 'select',
|
||||||
|
value: 'stretch',
|
||||||
|
options: ['stretch', 'start', 'center', 'end'],
|
||||||
|
category: 'styling',
|
||||||
|
},
|
||||||
|
{ name: 'className', type: 'string', value: '', category: 'styling' },
|
||||||
|
],
|
||||||
|
hooks: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Spacer',
|
||||||
|
icon: 'Space',
|
||||||
|
category: 'layout',
|
||||||
|
kind: 'layout',
|
||||||
|
toolboxGroup: 'layout',
|
||||||
|
description: 'Bileşenler arasında boşluk',
|
||||||
|
properties: [{ name: 'height', type: 'number', value: 24, category: 'styling' }],
|
||||||
|
hooks: [],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const getDesignerCatalog = (customNames: string[] = []): DesignerComponentDefinition[] => {
|
||||||
|
const html = HTML_ELEMENTS.filter((definition) => !HTML_UI_DUPLICATES.has(definition.name)).map(
|
||||||
|
(definition) => normalizeDefinition(definition, 'html', 'html'),
|
||||||
|
)
|
||||||
|
const ui = CUSTOM_COMPONENTS.map(normalizeUiDefinition)
|
||||||
|
const definedUiNames = new Set(CUSTOM_COMPONENTS.map((definition) => definition.name))
|
||||||
|
const generatedOnlyUi: DesignerComponentDefinition[] = Object.entries(generatedMetadata)
|
||||||
|
.filter(([name]) => !definedUiNames.has(name))
|
||||||
|
.map(([name, metadata]) => ({
|
||||||
|
name,
|
||||||
|
icon: 'Blocks',
|
||||||
|
category: inferUiCategory(name),
|
||||||
|
kind: 'ui',
|
||||||
|
toolboxGroup: DATA_COMPONENT_NAMES.has(name) ? 'data' : 'ui',
|
||||||
|
description: `${metadata.interfaceName} sözleşmesini kullanan Sozsoft UI component’i`,
|
||||||
|
sourceFile: metadata.sourceFile,
|
||||||
|
interfaceName: metadata.interfaceName,
|
||||||
|
acceptsChildren: CONTAINER_NAMES.has(name),
|
||||||
|
properties: generatedPropertiesToDesigner(metadata),
|
||||||
|
hooks: [],
|
||||||
|
}))
|
||||||
|
const custom: DesignerComponentDefinition[] = customNames.map((name) => ({
|
||||||
|
name,
|
||||||
|
icon: 'Blocks',
|
||||||
|
category: 'custom',
|
||||||
|
kind: 'custom',
|
||||||
|
toolboxGroup: 'custom',
|
||||||
|
description: 'Veritabanındaki yeniden kullanılabilir bileşen',
|
||||||
|
acceptsChildren: true,
|
||||||
|
properties: [{ name: 'className', type: 'string', value: '', category: 'styling' }],
|
||||||
|
hooks: [],
|
||||||
|
}))
|
||||||
|
|
||||||
|
return [...DESIGNER_EXTRAS, ...PLATFORM_COMPONENTS, ...html, ...ui, ...generatedOnlyUi, ...custom]
|
||||||
|
}
|
||||||
415
ui/src/components/visualDesigner/codeGenerator.ts
Normal file
415
ui/src/components/visualDesigner/codeGenerator.ts
Normal file
|
|
@ -0,0 +1,415 @@
|
||||||
|
import { normalizeDesignerKeyList, type DesignerDocument, type DesignerNode } from './types'
|
||||||
|
|
||||||
|
const EVENT_NAMES: Record<string, string> = {
|
||||||
|
onClick: 'event',
|
||||||
|
onChange: 'event',
|
||||||
|
onFocus: 'event',
|
||||||
|
onBlur: 'event',
|
||||||
|
onMouseEnter: 'event',
|
||||||
|
onMouseLeave: 'event',
|
||||||
|
}
|
||||||
|
|
||||||
|
const safeIdentifier = (value: string) => {
|
||||||
|
const cleaned = value.replace(/[^A-Za-z0-9_$]/g, '_')
|
||||||
|
return /^[A-Za-z_$]/.test(cleaned) ? cleaned : `Component_${cleaned}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const serializeValue = (value: unknown) => {
|
||||||
|
if (typeof value === 'string') return `{${JSON.stringify(value)}}`
|
||||||
|
if (typeof value === 'number' || typeof value === 'boolean') return `{${String(value)}}`
|
||||||
|
if (value === null) return '{null}'
|
||||||
|
if (Array.isArray(value) || typeof value === 'object') return `{${JSON.stringify(value)}}`
|
||||||
|
return JSON.stringify(String(value ?? ''))
|
||||||
|
}
|
||||||
|
|
||||||
|
const bindingExpression = (node: DesignerNode, propertyName: string, itemVariable?: string) => {
|
||||||
|
const binding = node.bindings?.[propertyName]
|
||||||
|
if (!binding?.sourceId) return ''
|
||||||
|
const path = binding.path.trim()
|
||||||
|
if (itemVariable && (path === '$item' || path.startsWith('$item.'))) {
|
||||||
|
return `getByPath(${itemVariable}, ${JSON.stringify(path === '$item' ? '' : path.slice(6))})`
|
||||||
|
}
|
||||||
|
return `getByPath(data_${safeIdentifier(binding.sourceId)}, ${JSON.stringify(binding.path || '')})`
|
||||||
|
}
|
||||||
|
|
||||||
|
const propsToCode = (node: DesignerNode, itemVariable?: string) => {
|
||||||
|
const hasBoundInputValue = node.type === 'Input' && Boolean(node.bindings?.value?.sourceId)
|
||||||
|
const hasInputChangeHandler = Boolean(node.events?.onChange?.trim())
|
||||||
|
const props = Object.entries(node.props)
|
||||||
|
.filter(
|
||||||
|
([key, value]) =>
|
||||||
|
key !== 'children' &&
|
||||||
|
!(node.type === 'Checkbox' && key === 'defaultChecked' && 'checked' in node.props) &&
|
||||||
|
!(hasBoundInputValue && !hasInputChangeHandler && key === 'readOnly') &&
|
||||||
|
!node.bindings?.[key]?.sourceId &&
|
||||||
|
value !== '' &&
|
||||||
|
value !== undefined,
|
||||||
|
)
|
||||||
|
.map(([key, value]) => {
|
||||||
|
if (node.type === 'Checkbox' && key === 'checked') {
|
||||||
|
return `defaultChecked=${serializeValue(value)}`
|
||||||
|
}
|
||||||
|
if (node.type === 'Input' && key === 'value') {
|
||||||
|
return `defaultValue=${serializeValue(value)}`
|
||||||
|
}
|
||||||
|
if (node.type === 'Select' && key === 'value') {
|
||||||
|
const staticOptions = Array.isArray(node.props.options) ? node.props.options : []
|
||||||
|
const selectedOption = staticOptions.find(
|
||||||
|
(option) =>
|
||||||
|
option &&
|
||||||
|
typeof option === 'object' &&
|
||||||
|
!Array.isArray(option) &&
|
||||||
|
(option as Record<string, unknown>).value === value,
|
||||||
|
)
|
||||||
|
return `defaultValue=${serializeValue(selectedOption ?? null)}`
|
||||||
|
}
|
||||||
|
return `${key}=${serializeValue(value)}`
|
||||||
|
})
|
||||||
|
|
||||||
|
Object.keys(node.bindings || {}).forEach((propertyName) => {
|
||||||
|
if (propertyName === 'children') return
|
||||||
|
const expression = bindingExpression(node, propertyName, itemVariable)
|
||||||
|
if (expression) {
|
||||||
|
const propertyExpression =
|
||||||
|
['Select', 'AutoComplete'].includes(node.type) && propertyName === 'options'
|
||||||
|
? `toSelectOptions(${expression}, ${JSON.stringify(node.bindings.options?.labelPath || '')}, ${JSON.stringify(node.bindings.options?.valuePath || '')})`
|
||||||
|
: expression
|
||||||
|
props.push(`${propertyName}={${propertyExpression}}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (hasBoundInputValue && !hasInputChangeHandler) props.push('readOnly={true}')
|
||||||
|
|
||||||
|
Object.entries(node.events)
|
||||||
|
.filter(([, script]) => script.trim())
|
||||||
|
.forEach(([eventName]) => {
|
||||||
|
props.push(`${eventName}={handle_${safeIdentifier(node.id)}_${eventName}}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
return props.length ? ` ${props.join(' ')}` : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const indent = (text: string, level: number) =>
|
||||||
|
text
|
||||||
|
.split('\n')
|
||||||
|
.map((line) => `${' '.repeat(level)}${line}`)
|
||||||
|
.join('\n')
|
||||||
|
|
||||||
|
const nodeToCode = (node: DesignerNode, level = 0, itemVariable?: string): string => {
|
||||||
|
if (node.type === 'Spacer') {
|
||||||
|
return indent(
|
||||||
|
`<div aria-hidden="true" style={{ height: ${Number(node.props.height) || 24} }} />`,
|
||||||
|
level,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.type === 'FlexRow') {
|
||||||
|
const style = `{ display: "flex", gap: ${Number(node.props.gap) || 0}, flexWrap: "${node.props.wrap ? 'wrap' : 'nowrap'}", alignItems: "${String(node.props.align || 'stretch')}" }`
|
||||||
|
const className = node.props.className
|
||||||
|
? ` className=${JSON.stringify(node.props.className)}`
|
||||||
|
: ''
|
||||||
|
const children = node.children
|
||||||
|
.map((child) => nodeToCode(child, level + 1, itemVariable))
|
||||||
|
.join('\n')
|
||||||
|
return `${indent(`<div${className} style={${style}}>`, level)}\n${children}\n${indent('</div>', level)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
node.type === 'PageContainer' ||
|
||||||
|
node.type === 'TwoColumns' ||
|
||||||
|
node.type === 'SidebarContent' ||
|
||||||
|
node.type === 'HeaderContent'
|
||||||
|
) {
|
||||||
|
const gap = Number(node.props.gap) || 0
|
||||||
|
const className = JSON.stringify(String(node.props.className || ''))
|
||||||
|
const style =
|
||||||
|
node.type === 'PageContainer'
|
||||||
|
? `{ display: "flex", flexDirection: "column", gap: ${gap}, margin: "0 auto", maxWidth: ${JSON.stringify(String(node.props.maxWidth || '1280px'))}, padding: ${Number(node.props.padding) || 0}, width: "100%" }`
|
||||||
|
: node.type === 'HeaderContent'
|
||||||
|
? `{ display: "flex", flexDirection: "column", gap: ${gap} }`
|
||||||
|
: `{ display: "grid", gap: ${gap}, gridTemplateColumns: ${JSON.stringify(node.type === 'SidebarContent' ? `${String(node.props.sidebarWidth || '280px')} minmax(0, 1fr)` : 'repeat(2, minmax(0, 1fr))')} }`
|
||||||
|
const children = node.children
|
||||||
|
.map((child) => nodeToCode(child, level + 1, itemVariable))
|
||||||
|
.join('\n')
|
||||||
|
return `${indent(`<div className=${className} style={${style}}>`, level)}\n${children}\n${indent('</div>', level)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.type === 'Table') {
|
||||||
|
const legacyData = Array.isArray(node.props.data) ? node.props.data : []
|
||||||
|
const staticItems =
|
||||||
|
Array.isArray(node.props.items) && (node.props.items.length > 0 || !legacyData.length)
|
||||||
|
? node.props.items
|
||||||
|
: legacyData
|
||||||
|
const itemsExpression =
|
||||||
|
bindingExpression(node, 'items', itemVariable) ||
|
||||||
|
bindingExpression(node, 'data', itemVariable) ||
|
||||||
|
JSON.stringify(staticItems)
|
||||||
|
const identifier = safeIdentifier(node.id)
|
||||||
|
const itemsVariable = `tableItems_${identifier}`
|
||||||
|
const columnsVariable = `tableColumns_${identifier}`
|
||||||
|
const rowVariable = `tableRow_${identifier}`
|
||||||
|
const configuredColumns = Array.isArray(node.props.dataColumns)
|
||||||
|
? node.props.dataColumns.filter((column): column is string => typeof column === 'string')
|
||||||
|
: Array.isArray(node.props.columns)
|
||||||
|
? node.props.columns
|
||||||
|
.map((column) => {
|
||||||
|
if (typeof column === 'string') return column
|
||||||
|
if (!column || typeof column !== 'object' || Array.isArray(column)) return ''
|
||||||
|
const record = column as Record<string, unknown>
|
||||||
|
return String(record.dataIndex ?? record.key ?? '')
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
: null
|
||||||
|
const columnsExpression = configuredColumns
|
||||||
|
? JSON.stringify(configuredColumns)
|
||||||
|
: `${itemsVariable}[0] && typeof ${itemsVariable}[0] === "object" && !Array.isArray(${itemsVariable}[0]) ? Object.keys(${itemsVariable}[0]) : ["value"]`
|
||||||
|
return `${indent('{(() => {', level)}
|
||||||
|
${indent(`const ${itemsVariable} = ${itemsExpression}`, level + 1)}
|
||||||
|
${indent(`if (!Array.isArray(${itemsVariable})) return <div className="rounded-lg border border-amber-300 bg-amber-50 p-3 text-xs text-amber-800 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-200">Table items bağlantısı bir koleksiyon döndürmelidir.</div>`, level + 1)}
|
||||||
|
${indent(`const ${columnsVariable} = ${columnsExpression}`, level + 1)}
|
||||||
|
${indent('return (', level + 1)}
|
||||||
|
${indent(`<UiKit.Table borderlessRow={${Boolean(node.props.borderlessRow)}} compact={${Boolean(node.props.compact)}} hoverable={${node.props.hoverable !== false}} overflow={${node.props.overflow !== false}}>`, level + 2)}
|
||||||
|
${indent(`<thead><tr>{${columnsVariable}.map((column) => <th key={column}>{column === "value" ? "Value" : column}</th>)}</tr></thead>`, level + 3)}
|
||||||
|
${indent(`<tbody>{${itemsVariable}.map((${rowVariable}, rowIndex) => <tr key={rowIndex}>{${columnsVariable}.map((column) => { const value = column === "value" ? ${rowVariable} : getByPath(${rowVariable}, column); return <td key={column}>{typeof value === "object" && value !== null ? JSON.stringify(value) : String(value ?? "")}</td> })}</tr>)}</tbody>`, level + 3)}
|
||||||
|
${indent('</UiKit.Table>', level + 2)}
|
||||||
|
${indent(')', level + 1)}
|
||||||
|
${indent('})()}', level)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.type === 'Grid') {
|
||||||
|
const columns = Number(node.props.cols) || 3
|
||||||
|
const gap = (Number(node.props.gap) || 0) * 4
|
||||||
|
const itemsExpression =
|
||||||
|
bindingExpression(node, 'items', itemVariable) ||
|
||||||
|
(Array.isArray(node.props.items) ? JSON.stringify(node.props.items) : '')
|
||||||
|
const itemsBinding = node.bindings?.items
|
||||||
|
const sourceRootExpression =
|
||||||
|
!itemVariable && itemsBinding?.sourceId ? `data_${safeIdentifier(itemsBinding.sourceId)}` : ''
|
||||||
|
const collectionExpression =
|
||||||
|
sourceRootExpression && sourceRootExpression !== itemsExpression
|
||||||
|
? `(Array.isArray(${itemsExpression}) ? ${itemsExpression} : Array.isArray(${sourceRootExpression}) ? ${sourceRootExpression} : null)`
|
||||||
|
: itemsExpression
|
||||||
|
const gridClassName = JSON.stringify(String(node.props.className || ''))
|
||||||
|
const gridStart = `<div className=${gridClassName} style={{ display: "grid", gridTemplateColumns: "repeat(${columns}, minmax(0, 1fr))", gap: ${gap} }}>`
|
||||||
|
|
||||||
|
if (collectionExpression && node.children.length === 0) {
|
||||||
|
const identifier = safeIdentifier(node.id)
|
||||||
|
const itemsVariable = `gridItems_${identifier}`
|
||||||
|
const columnsVariable = `gridColumns_${identifier}`
|
||||||
|
const itemVariable = `gridItem_${identifier}`
|
||||||
|
const valueVariable = `cellValue_${identifier}`
|
||||||
|
const configuredColumns = Array.isArray(node.props.dataColumns)
|
||||||
|
? node.props.dataColumns.filter((column): column is string => typeof column === 'string')
|
||||||
|
: null
|
||||||
|
const columnsExpression = configuredColumns
|
||||||
|
? JSON.stringify(configuredColumns)
|
||||||
|
: `${itemsVariable}[0] && typeof ${itemsVariable}[0] === "object" && !Array.isArray(${itemsVariable}[0]) ? Object.keys(${itemsVariable}[0]) : ["value"]`
|
||||||
|
const compact = Boolean(node.props.compact)
|
||||||
|
const borderlessRow = Boolean(node.props.borderlessRow)
|
||||||
|
const hoverable = node.props.hoverable !== false
|
||||||
|
const overflow = node.props.overflow !== false
|
||||||
|
const tableTextClass = compact ? 'text-[11px]' : 'text-xs'
|
||||||
|
const cellSpacingClass = compact ? 'px-2 py-1' : 'px-3 py-2'
|
||||||
|
const rowClass = [
|
||||||
|
borderlessRow ? '' : 'border-b border-slate-100 last:border-b-0 dark:border-slate-800',
|
||||||
|
hoverable ? 'transition-colors hover:bg-slate-50 dark:hover:bg-slate-800/70' : '',
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')
|
||||||
|
const overflowClass = overflow ? 'overflow-auto' : 'overflow-visible'
|
||||||
|
const table = `${indent('{(() => {', level + 1)}
|
||||||
|
${indent(`const ${itemsVariable} = ${collectionExpression}`, level + 2)}
|
||||||
|
${indent(`if (!Array.isArray(${itemsVariable})) return <div className="rounded-lg border border-amber-300 bg-amber-50 p-3 text-xs text-amber-800 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-200">Grid items bağlantısı bir koleksiyon döndürmelidir.</div>`, level + 2)}
|
||||||
|
${indent(`const ${columnsVariable} = ${columnsExpression}`, level + 2)}
|
||||||
|
${indent(`if (!${columnsVariable}.length) return <div className="rounded-lg border border-dashed border-slate-300 bg-slate-50 p-4 text-center text-xs text-slate-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-400">Preview için en az bir sütun seçin.</div>`, level + 2)}
|
||||||
|
${indent('return (', level + 2)}
|
||||||
|
${indent(`<div className="min-w-0 ${overflowClass} rounded-lg border border-slate-200 bg-white shadow-sm dark:border-slate-700 dark:bg-slate-900" style={{ gridColumn: "1 / -1" }}>`, level + 3)}
|
||||||
|
${indent(`<table className="w-full border-collapse text-left ${tableTextClass}">`, level + 4)}
|
||||||
|
${indent('<thead className="bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-200"><tr>', level + 5)}
|
||||||
|
${indent(`{${columnsVariable}.map((column) => <th key={column} className="whitespace-nowrap border-b border-slate-200 dark:border-slate-700 ${cellSpacingClass}">{column === "value" ? "Value" : column}</th>)}`, level + 6)}
|
||||||
|
${indent('</tr></thead>', level + 5)}
|
||||||
|
${indent('<tbody>', level + 5)}
|
||||||
|
${indent(`{${itemsVariable}.map((${itemVariable}, rowIndex) => (`, level + 6)}
|
||||||
|
${indent(`<tr key={rowIndex} className=${JSON.stringify(rowClass)}>`, level + 7)}
|
||||||
|
${indent(`{${columnsVariable}.map((column) => {`, level + 8)}
|
||||||
|
${indent(`const ${valueVariable} = column === "value" ? ${itemVariable} : getByPath(${itemVariable}, column)`, level + 9)}
|
||||||
|
${indent(`const text = typeof ${valueVariable} === "object" && ${valueVariable} !== null ? JSON.stringify(${valueVariable}) : String(${valueVariable} ?? "") || "—"`, level + 9)}
|
||||||
|
${indent(`return <td key={column} className="max-w-64 truncate text-slate-800 dark:text-slate-100 ${cellSpacingClass}" title={text}>{text}</td>`, level + 9)}
|
||||||
|
${indent('})}', level + 8)}
|
||||||
|
${indent('</tr>', level + 7)}
|
||||||
|
${indent('))}', level + 6)}
|
||||||
|
${indent('</tbody>', level + 5)}
|
||||||
|
${indent('</table>', level + 4)}
|
||||||
|
${indent('</div>', level + 3)}
|
||||||
|
${indent(')', level + 2)}
|
||||||
|
${indent('})()}', level + 1)}`
|
||||||
|
return `${indent(gridStart, level)}\n${table}\n${indent('</div>', level)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const repeatedItemVariable = `item_${safeIdentifier(node.id)}`
|
||||||
|
const staticChildren = node.children
|
||||||
|
.map((child) => nodeToCode(child, level + 1, itemVariable))
|
||||||
|
.join('\n')
|
||||||
|
const repeatedChildren = node.children.length
|
||||||
|
? node.children.map((child) => nodeToCode(child, level + 3, repeatedItemVariable)).join('\n')
|
||||||
|
: indent(
|
||||||
|
`<pre className="min-w-0 overflow-auto rounded-lg border border-slate-200 bg-white p-3 text-xs text-slate-800 shadow-sm dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100">{typeof ${repeatedItemVariable} === "string" ? ${repeatedItemVariable} : JSON.stringify(${repeatedItemVariable}, null, 2)}</pre>`,
|
||||||
|
level + 3,
|
||||||
|
)
|
||||||
|
const children = collectionExpression
|
||||||
|
? `${indent(`{Array.isArray(${collectionExpression}) ? ${collectionExpression}.map((${repeatedItemVariable}, index) => (`, level + 1)}
|
||||||
|
${indent(`<React.Fragment key={${repeatedItemVariable}?.id ?? index}>`, level + 2)}
|
||||||
|
${repeatedChildren}
|
||||||
|
${indent('</React.Fragment>', level + 2)}
|
||||||
|
${indent(')) : (', level + 1)}
|
||||||
|
${indent('<div className="rounded-lg border border-amber-300 bg-amber-50 p-3 text-xs text-amber-800 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-200">Grid items bağlantısı bir koleksiyon döndürmelidir.</div>', level + 2)}
|
||||||
|
${indent(')}', level + 1)}`
|
||||||
|
: staticChildren
|
||||||
|
return `${indent(gridStart, level)}\n${children}\n${indent('</div>', level)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.kind === 'platform') {
|
||||||
|
const viewNames: Record<string, string> = {
|
||||||
|
ListView: 'List',
|
||||||
|
DataGridView: 'Grid',
|
||||||
|
TreeView: 'Tree',
|
||||||
|
GanttView: 'GanttView',
|
||||||
|
TodoBoard: 'TodoBoard',
|
||||||
|
CardView: 'CardView',
|
||||||
|
SchedulerView: 'SchedulerView',
|
||||||
|
PivotView: 'Pivot',
|
||||||
|
ChartView: 'Chart',
|
||||||
|
}
|
||||||
|
return indent(
|
||||||
|
`<PlatformViewHost view=${JSON.stringify(viewNames[node.type] || node.type)} listFormCode=${JSON.stringify(String(node.props.listFormCode || ''))} height=${JSON.stringify(String(node.props.height || '420px'))} />`,
|
||||||
|
level,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.type === 'Menu') {
|
||||||
|
const { items: staticOptions = [], variant: _variant, ...menuProps } = node.props
|
||||||
|
menuProps.defaultActiveKeys = normalizeDesignerKeyList(menuProps.defaultActiveKeys)
|
||||||
|
menuProps.defaultExpandedKeys = normalizeDesignerKeyList(menuProps.defaultExpandedKeys)
|
||||||
|
const { items: optionsBinding, ...menuBindings } = node.bindings || {}
|
||||||
|
const menuNode = { ...node, props: menuProps, bindings: menuBindings }
|
||||||
|
const menuPropsCode = propsToCode(menuNode, itemVariable)
|
||||||
|
const binding = optionsBinding?.sourceId
|
||||||
|
? bindingExpression(node, 'items', itemVariable)
|
||||||
|
: ''
|
||||||
|
const optionsExpression = binding
|
||||||
|
? `toSelectOptions(${binding}, ${JSON.stringify(optionsBinding?.labelPath || '')}, ${JSON.stringify(optionsBinding?.valuePath || '')})`
|
||||||
|
: JSON.stringify(Array.isArray(staticOptions) ? staticOptions : [])
|
||||||
|
return `${indent(`<UiKit.Menu${menuPropsCode}>`, level)}\n${indent(`{${optionsExpression}.map((option, optionIndex) => <UiKit.Menu.MenuItem key={String(option.value ?? optionIndex)} eventKey={String(option.value ?? optionIndex)}>{String(option.label ?? option.value ?? \`Menü \${optionIndex + 1}\`)}</UiKit.Menu.MenuItem>)}`, level + 1)}\n${indent('</UiKit.Menu>', level)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const tag =
|
||||||
|
node.type === 'checkbox'
|
||||||
|
? 'input'
|
||||||
|
: node.kind === 'ui'
|
||||||
|
? `UiKit.${node.type}`
|
||||||
|
: node.kind === 'custom'
|
||||||
|
? safeIdentifier(node.type)
|
||||||
|
: node.type
|
||||||
|
const props = { ...node.props }
|
||||||
|
if (node.type === 'checkbox') props.type = 'checkbox'
|
||||||
|
const normalizedNode = { ...node, props }
|
||||||
|
const propCode = propsToCode(normalizedNode, itemVariable)
|
||||||
|
const childrenText = String(node.props.children ?? '')
|
||||||
|
const childrenBinding = bindingExpression(node, 'children', itemVariable)
|
||||||
|
const hasChildren = node.children.length > 0 || childrenText.length > 0 || !!childrenBinding
|
||||||
|
const voidElement = ['input', 'img'].includes(tag)
|
||||||
|
|
||||||
|
if (voidElement) return indent(`<${tag}${propCode} />`, level)
|
||||||
|
if (!hasChildren) return indent(`<${tag}${propCode} />`, level)
|
||||||
|
|
||||||
|
const nested = node.children.map((child) => nodeToCode(child, level + 1, itemVariable)).join('\n')
|
||||||
|
const text = childrenBinding
|
||||||
|
? indent(`{${childrenBinding}}`, level + 1)
|
||||||
|
: childrenText
|
||||||
|
? indent(`{${JSON.stringify(childrenText)}}`, level + 1)
|
||||||
|
: ''
|
||||||
|
const body = [text, nested].filter(Boolean).join('\n')
|
||||||
|
return `${indent(`<${tag}${propCode}>`, level)}\n${body}\n${indent(`</${tag}>`, level)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const generateDesignerCode = (name: string, document: DesignerDocument) => {
|
||||||
|
const componentName = safeIdentifier(name || 'VisualComponent')
|
||||||
|
const handlers: string[] = []
|
||||||
|
let hasSelect = false
|
||||||
|
let hasDataTable = false
|
||||||
|
|
||||||
|
const visit = (nodes: DesignerNode[]) => {
|
||||||
|
nodes.forEach((node) => {
|
||||||
|
if (node.type === 'Select' || node.type === 'AutoComplete' || node.type === 'Menu') {
|
||||||
|
hasSelect = true
|
||||||
|
}
|
||||||
|
if (node.type === 'Grid' || node.type === 'Table') hasDataTable = true
|
||||||
|
Object.entries(node.events).forEach(([eventName, script]) => {
|
||||||
|
if (!script.trim()) return
|
||||||
|
if (node.type === 'Checkbox' && eventName === 'onChange') {
|
||||||
|
handlers.push(
|
||||||
|
` const handle_${safeIdentifier(node.id)}_${eventName} = (checked, originalEvent) => {\n const event = { checked, originalEvent, target: originalEvent?.target }\n${indent(script, 2)}\n }`,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handlers.push(
|
||||||
|
` const handle_${safeIdentifier(node.id)}_${eventName} = (${EVENT_NAMES[eventName] || 'event'}) => {\n${indent(script, 2)}\n }`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
visit(node.children)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
visit(document.nodes)
|
||||||
|
|
||||||
|
const dataSources = document.dataSources || []
|
||||||
|
const selectHelpers = hasSelect
|
||||||
|
? ` const toSelectOptions = (value, labelPath = "", valuePath = "") => {
|
||||||
|
if (!Array.isArray(value)) return []
|
||||||
|
return value.map((item, index) => {
|
||||||
|
if (item === null || typeof item !== "object" || Array.isArray(item)) return { label: String(item ?? ""), value: item ?? index }
|
||||||
|
const primitiveKeys = Object.keys(item).filter((key) => item[key] === null || ["string", "number", "boolean"].includes(typeof item[key]))
|
||||||
|
const labelKey = ["label", "name", "title", "text", "description"].find((key) => item[key] !== undefined)
|
||||||
|
const valueKey = ["value", "id", "code", "key"].find((key) => item[key] !== undefined)
|
||||||
|
const optionLabel = labelPath ? getByPath(item, labelPath) : item[labelKey ?? primitiveKeys[0]]
|
||||||
|
const optionValue = valuePath ? getByPath(item, valuePath) : item[valueKey ?? primitiveKeys[1] ?? primitiveKeys[0]]
|
||||||
|
return { ...item, label: String(optionLabel ?? \`Seçenek \${index + 1}\`), value: optionValue ?? index }
|
||||||
|
})
|
||||||
|
}`
|
||||||
|
: ''
|
||||||
|
const dataHelpers =
|
||||||
|
dataSources.length || hasDataTable
|
||||||
|
? ` const getByPath = (value, path) => {
|
||||||
|
if (!path) return value
|
||||||
|
const readPath = (target, targetPath) => targetPath.split('.').filter(Boolean).reduce((current, key) => current?.[key], target)
|
||||||
|
const directValue = readPath(value, path)
|
||||||
|
if (directValue !== undefined) return directValue
|
||||||
|
if (path.startsWith('result.')) return readPath(value, path.slice('result.'.length))
|
||||||
|
return value?.result === undefined ? undefined : readPath(value.result, path)
|
||||||
|
}`
|
||||||
|
: ''
|
||||||
|
const dataHooks = dataSources
|
||||||
|
.map((source) => {
|
||||||
|
const identifier = safeIdentifier(source.id)
|
||||||
|
if (!source.url.trim().startsWith('/api/')) {
|
||||||
|
return ` const [data_${identifier}] = React.useState(null)`
|
||||||
|
}
|
||||||
|
return ` const [data_${identifier}, setData_${identifier}] = React.useState(null)
|
||||||
|
React.useEffect(() => {
|
||||||
|
let active = true
|
||||||
|
apiService.fetchData({ url: ${JSON.stringify(source.url)}, method: 'GET' }).then((response) => {
|
||||||
|
if (active) setData_${identifier}(getByPath(response.data, ${JSON.stringify(source.responsePath || '')}))
|
||||||
|
}).catch((error) => console.error(${JSON.stringify(`${source.name} endpoint error:`)}, error))
|
||||||
|
return () => { active = false }
|
||||||
|
}, [])`
|
||||||
|
})
|
||||||
|
.join('\n\n')
|
||||||
|
|
||||||
|
const mount = document.lifecycle.onMount.trim()
|
||||||
|
? ` React.useEffect(() => {\n${indent(document.lifecycle.onMount, 2)}\n }, [])`
|
||||||
|
: ''
|
||||||
|
const body = document.nodes.map((node) => nodeToCode(node, 3)).join('\n')
|
||||||
|
|
||||||
|
const designerBackup = encodeURIComponent(JSON.stringify(document))
|
||||||
|
|
||||||
|
return `/*__SOZSOFT_VISUAL_DESIGNER__${designerBackup}__*/\nconst ${componentName} = () => {\n${[dataHelpers, selectHelpers, dataHooks, mount, ...handlers].filter(Boolean).join('\n\n')}\n\n return (\n <>\n${body}\n </>\n )\n}\n\nexport default ${componentName}\n`
|
||||||
|
}
|
||||||
153
ui/src/components/visualDesigner/types.ts
Normal file
153
ui/src/components/visualDesigner/types.ts
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
import type { ComponentDefinition, PropertyInfo } from '@/proxy/developerKit/componentInfo'
|
||||||
|
|
||||||
|
export type DesignerNodeKind = 'html' | 'ui' | 'layout' | 'platform' | 'custom'
|
||||||
|
export type DesignerToolboxGroup = 'layout' | 'html' | 'platform' | 'data' | 'ui' | 'custom'
|
||||||
|
|
||||||
|
export interface DesignerBinding {
|
||||||
|
sourceId: string
|
||||||
|
path: string
|
||||||
|
labelPath?: string
|
||||||
|
valuePath?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DesignerDataSource {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
method: 'GET'
|
||||||
|
url: string
|
||||||
|
responsePath: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DesignerNode {
|
||||||
|
id: string
|
||||||
|
type: string
|
||||||
|
kind: DesignerNodeKind
|
||||||
|
props: Record<string, unknown>
|
||||||
|
events: Record<string, string>
|
||||||
|
bindings: Record<string, DesignerBinding>
|
||||||
|
children: DesignerNode[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DesignerDocument {
|
||||||
|
version: 1
|
||||||
|
sourceMode: 'visual' | 'code'
|
||||||
|
nodes: DesignerNode[]
|
||||||
|
canvas: {
|
||||||
|
background: string
|
||||||
|
width: 'responsive' | 'desktop' | 'tablet' | 'mobile'
|
||||||
|
}
|
||||||
|
lifecycle: {
|
||||||
|
onMount: string
|
||||||
|
}
|
||||||
|
dataSources: DesignerDataSource[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DesignerPropertyInfo extends PropertyInfo {
|
||||||
|
tsType?: string
|
||||||
|
required?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DesignerComponentDefinition extends Omit<ComponentDefinition, 'properties'> {
|
||||||
|
kind: DesignerNodeKind
|
||||||
|
toolboxGroup: DesignerToolboxGroup
|
||||||
|
properties: DesignerPropertyInfo[]
|
||||||
|
acceptsChildren?: boolean
|
||||||
|
sourceName?: string
|
||||||
|
sourceFile?: string
|
||||||
|
interfaceName?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createEmptyDesignerDocument = (
|
||||||
|
sourceMode: 'visual' | 'code' = 'visual',
|
||||||
|
): DesignerDocument => ({
|
||||||
|
version: 1,
|
||||||
|
sourceMode,
|
||||||
|
nodes: [],
|
||||||
|
canvas: {
|
||||||
|
background: '#f8fafc',
|
||||||
|
width: 'responsive',
|
||||||
|
},
|
||||||
|
lifecycle: {
|
||||||
|
onMount: '',
|
||||||
|
},
|
||||||
|
dataSources: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
export const createDesignerId = () =>
|
||||||
|
`cmp_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`
|
||||||
|
|
||||||
|
export const walkDesignerNodes = (
|
||||||
|
nodes: DesignerNode[],
|
||||||
|
callback: (node: DesignerNode, parentId: string | null) => void,
|
||||||
|
parentId: string | null = null,
|
||||||
|
) => {
|
||||||
|
nodes.forEach((node) => {
|
||||||
|
callback(node, parentId)
|
||||||
|
walkDesignerNodes(node.children, callback, node.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const findDesignerNode = (nodes: DesignerNode[], id: string | null): DesignerNode | null => {
|
||||||
|
if (!id) return null
|
||||||
|
for (const node of nodes) {
|
||||||
|
if (node.id === id) return node
|
||||||
|
const child = findDesignerNode(node.children, id)
|
||||||
|
if (child) return child
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getDesignerValueByPath = (value: unknown, path: string): unknown => {
|
||||||
|
if (!path.trim()) return value
|
||||||
|
return path
|
||||||
|
.split('.')
|
||||||
|
.filter(Boolean)
|
||||||
|
.reduce<unknown>((current, key) => {
|
||||||
|
if (current === null || current === undefined || typeof current !== 'object') return undefined
|
||||||
|
return (current as Record<string, unknown>)[key]
|
||||||
|
}, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const resolveDesignerResponse = (value: unknown, responsePath: string): unknown => {
|
||||||
|
if (!responsePath.trim()) return value
|
||||||
|
|
||||||
|
const directValue = getDesignerValueByPath(value, responsePath)
|
||||||
|
if (directValue !== undefined) return directValue
|
||||||
|
|
||||||
|
// Supports both ABP's older `result` envelope and current direct DTO responses.
|
||||||
|
if (responsePath.startsWith('result.')) {
|
||||||
|
return getDesignerValueByPath(value, responsePath.slice('result.'.length))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value && typeof value === 'object' && 'result' in value) {
|
||||||
|
return getDesignerValueByPath((value as Record<string, unknown>).result, responsePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export const normalizeDesignerKeyList = (value: unknown): string[] => {
|
||||||
|
let candidate = value
|
||||||
|
|
||||||
|
if (typeof candidate === 'string') {
|
||||||
|
const rawValue = candidate.trim()
|
||||||
|
const jsonValue = rawValue.startsWith('{') && rawValue.endsWith('}')
|
||||||
|
? rawValue.slice(1, -1).trim()
|
||||||
|
: rawValue
|
||||||
|
|
||||||
|
try {
|
||||||
|
candidate = JSON.parse(jsonValue)
|
||||||
|
} catch {
|
||||||
|
candidate = rawValue ? [rawValue] : []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(candidate)) return []
|
||||||
|
|
||||||
|
return candidate
|
||||||
|
.filter((item): item is string | number =>
|
||||||
|
typeof item === 'string' || typeof item === 'number',
|
||||||
|
)
|
||||||
|
.map(String)
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
|
|
@ -4,8 +4,20 @@ import type {
|
||||||
CustomComponentDto,
|
CustomComponentDto,
|
||||||
} from '@/proxy/developerKit/models'
|
} from '@/proxy/developerKit/models'
|
||||||
import { developerKitService } from '@/services/developerKit.service'
|
import { developerKitService } from '@/services/developerKit.service'
|
||||||
|
import apiService from '@/services/api.service'
|
||||||
|
import { generateDesignerCode } from '@/components/visualDesigner/codeGenerator'
|
||||||
|
import type { DesignerDocument } from '@/components/visualDesigner/types'
|
||||||
|
import DOMPurify from 'dompurify'
|
||||||
import { useStoreState } from '@/store/store'
|
import { useStoreState } from '@/store/store'
|
||||||
import React, { createContext, useContext, useState, useEffect, useCallback, useMemo } from 'react'
|
import React, {
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
useState,
|
||||||
|
useEffect,
|
||||||
|
useCallback,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
} from 'react'
|
||||||
|
|
||||||
interface ComponentProps {
|
interface ComponentProps {
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
|
|
@ -33,6 +45,65 @@ interface ComponentContextType {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ComponentContext = createContext<ComponentContextType | undefined>(undefined)
|
const ComponentContext = createContext<ComponentContextType | undefined>(undefined)
|
||||||
|
const COMPONENTS_CHANGED_STORAGE_KEY = 'sozsoft:custom-components:changed'
|
||||||
|
|
||||||
|
const notifyComponentsChanged = () => {
|
||||||
|
try {
|
||||||
|
window.localStorage.setItem(COMPONENTS_CHANGED_STORAGE_KEY, String(Date.now()))
|
||||||
|
} catch {
|
||||||
|
// Local state is already current; storage can be unavailable in restricted browsers.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const haveSameComponentRevision = (
|
||||||
|
current: CustomComponent[],
|
||||||
|
next: CustomComponent[],
|
||||||
|
) =>
|
||||||
|
current.length === next.length &&
|
||||||
|
current.every((component, index) => {
|
||||||
|
const candidate = next[index]
|
||||||
|
return (
|
||||||
|
component.id === candidate?.id &&
|
||||||
|
component.name === candidate.name &&
|
||||||
|
component.routePath === candidate.routePath &&
|
||||||
|
component.code === candidate.code &&
|
||||||
|
component.props === candidate.props &&
|
||||||
|
component.description === candidate.description &&
|
||||||
|
component.dependencies === candidate.dependencies &&
|
||||||
|
component.isActive === candidate.isActive &&
|
||||||
|
component.lastModificationTime === candidate.lastModificationTime
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const normalizeComponentCodeForBundle = (code: string) =>
|
||||||
|
code
|
||||||
|
// Stored components run inside a shared function scope. Imports are supplied
|
||||||
|
// by that scope and therefore cannot remain as nested ESM declarations.
|
||||||
|
.replace(/^\s*import\s+(?:(?:type\s+)?[\s\S]*?\s+from\s+)?['"][^'"\r\n]+['"]\s*;?\s*$/gm, '')
|
||||||
|
.replace(/\bexport\s+default\s+/g, '')
|
||||||
|
.trim()
|
||||||
|
|
||||||
|
const toJavaScriptIdentifier = (value: string) => {
|
||||||
|
const identifier = value.replace(/[^A-Za-z0-9_$]/g, '_')
|
||||||
|
return /^[A-Za-z_$]/.test(identifier) ? identifier : `Component_${identifier}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const getComponentRuntimeCode = (component: CustomComponent) => {
|
||||||
|
try {
|
||||||
|
const props = component.props ? JSON.parse(component.props) : null
|
||||||
|
const document = props?.visualDesigner as DesignerDocument | undefined
|
||||||
|
if (
|
||||||
|
document?.version === 1 &&
|
||||||
|
document.sourceMode === 'visual' &&
|
||||||
|
Array.isArray(document.nodes)
|
||||||
|
) {
|
||||||
|
return generateDesignerCode(component.name, document)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Visual designer schema could not be read for ${component.name}:`, error)
|
||||||
|
}
|
||||||
|
return component.code
|
||||||
|
}
|
||||||
|
|
||||||
const HelloWorldComponent: React.ComponentType<unknown> = () =>
|
const HelloWorldComponent: React.ComponentType<unknown> = () =>
|
||||||
React.createElement('div', { className: 'p-6 bg-blue-50 rounded-lg' }, [
|
React.createElement('div', { className: 'p-6 bg-blue-50 rounded-lg' }, [
|
||||||
|
|
@ -85,22 +156,50 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
||||||
const [compiledComponents, setCompiledComponents] = useState<
|
const [compiledComponents, setCompiledComponents] = useState<
|
||||||
Record<string, React.ComponentType<ComponentProps>>
|
Record<string, React.ComponentType<ComponentProps>>
|
||||||
>({})
|
>({})
|
||||||
|
const refreshInFlightRef = useRef<Promise<void> | null>(null)
|
||||||
|
|
||||||
const refreshComponents = useCallback(async () => {
|
const refreshComponents = useCallback(() => {
|
||||||
|
if (refreshInFlightRef.current) return refreshInFlightRef.current
|
||||||
|
|
||||||
|
const request = (async () => {
|
||||||
try {
|
try {
|
||||||
setError(null)
|
setError(null)
|
||||||
|
const result = await developerKitService.getCustomComponents()
|
||||||
const customComponents = extraProperties?.customComponents as CustomComponentDto[]
|
const nextComponents = result.items || []
|
||||||
setComponents(customComponents || [])
|
setComponents((current) =>
|
||||||
|
haveSameComponentRevision(current, nextComponents) ? current : nextComponents,
|
||||||
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : 'Failed to fetch components')
|
setError(err instanceof Error ? err.message : 'Failed to fetch components')
|
||||||
console.error('Failed to fetch components:', err)
|
console.error('Failed to fetch components:', err)
|
||||||
setComponents([])
|
const fallbackComponents = extraProperties?.customComponents as CustomComponentDto[]
|
||||||
|
setComponents((current) => (current.length ? current : fallbackComponents || []))
|
||||||
}
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
|
refreshInFlightRef.current = request
|
||||||
|
void request.finally(() => {
|
||||||
|
if (refreshInFlightRef.current === request) refreshInFlightRef.current = null
|
||||||
|
})
|
||||||
|
return request
|
||||||
}, [extraProperties])
|
}, [extraProperties])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refreshComponents()
|
void refreshComponents()
|
||||||
|
}, [refreshComponents])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleFocus = () => void refreshComponents()
|
||||||
|
const handleStorage = (event: StorageEvent) => {
|
||||||
|
if (event.key === COMPONENTS_CHANGED_STORAGE_KEY) void refreshComponents()
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('focus', handleFocus)
|
||||||
|
window.addEventListener('storage', handleStorage)
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('focus', handleFocus)
|
||||||
|
window.removeEventListener('storage', handleStorage)
|
||||||
|
}
|
||||||
}, [refreshComponents])
|
}, [refreshComponents])
|
||||||
|
|
||||||
const addComponent = useCallback(async (componentData: CreateUpdateCustomComponentDto) => {
|
const addComponent = useCallback(async (componentData: CreateUpdateCustomComponentDto) => {
|
||||||
|
|
@ -109,6 +208,7 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
||||||
setError(null)
|
setError(null)
|
||||||
const newComponent = await developerKitService.createCustomComponent(componentData)
|
const newComponent = await developerKitService.createCustomComponent(componentData)
|
||||||
setComponents((prev) => [...prev, newComponent])
|
setComponents((prev) => [...prev, newComponent])
|
||||||
|
notifyComponentsChanged()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : 'Failed to create component')
|
setError(err instanceof Error ? err.message : 'Failed to create component')
|
||||||
throw err
|
throw err
|
||||||
|
|
@ -117,10 +217,8 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const updateComponent = useCallback(async (
|
const updateComponent = useCallback(
|
||||||
id: string,
|
async (id: string, componentData: CreateUpdateCustomComponentDto) => {
|
||||||
componentData: CreateUpdateCustomComponentDto,
|
|
||||||
) => {
|
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
setError(null)
|
setError(null)
|
||||||
|
|
@ -128,13 +226,16 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
||||||
setComponents((prev) =>
|
setComponents((prev) =>
|
||||||
prev.map((component) => (component.id === id ? updatedComponent : component)),
|
prev.map((component) => (component.id === id ? updatedComponent : component)),
|
||||||
)
|
)
|
||||||
|
notifyComponentsChanged()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : 'Failed to update component')
|
setError(err instanceof Error ? err.message : 'Failed to update component')
|
||||||
throw err
|
throw err
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}, [])
|
},
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
|
||||||
const deleteComponent = useCallback(async (id: string) => {
|
const deleteComponent = useCallback(async (id: string) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -142,6 +243,7 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
||||||
setError(null)
|
setError(null)
|
||||||
await developerKitService.deleteCustomComponent(id)
|
await developerKitService.deleteCustomComponent(id)
|
||||||
setComponents((prev) => prev.filter((component) => component.id !== id))
|
setComponents((prev) => prev.filter((component) => component.id !== id))
|
||||||
|
notifyComponentsChanged()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : 'Failed to delete component')
|
setError(err instanceof Error ? err.message : 'Failed to delete component')
|
||||||
throw err
|
throw err
|
||||||
|
|
@ -159,7 +261,12 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
||||||
[components],
|
[components],
|
||||||
)
|
)
|
||||||
const activeComponentsByName = useMemo(
|
const activeComponentsByName = useMemo(
|
||||||
() => new Map(components.filter((component) => component.isActive).map((component) => [component.name, component])),
|
() =>
|
||||||
|
new Map(
|
||||||
|
components
|
||||||
|
.filter((component) => component.isActive)
|
||||||
|
.map((component) => [component.name, component]),
|
||||||
|
),
|
||||||
[components],
|
[components],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -170,14 +277,11 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
||||||
[componentsByName],
|
[componentsByName],
|
||||||
)
|
)
|
||||||
|
|
||||||
const registerComponent = useCallback(
|
const registerComponent = useCallback((name: string, component: React.ComponentType<unknown>) => {
|
||||||
(name: string, component: React.ComponentType<unknown>) => {
|
|
||||||
setRegisteredComponents((previous) =>
|
setRegisteredComponents((previous) =>
|
||||||
previous[name] === component ? previous : { ...previous, [name]: component },
|
previous[name] === component ? previous : { ...previous, [name]: component },
|
||||||
)
|
)
|
||||||
},
|
}, [])
|
||||||
[],
|
|
||||||
)
|
|
||||||
|
|
||||||
// Component compilation functions (moved from ComponentRegistryProvider)
|
// Component compilation functions (moved from ComponentRegistryProvider)
|
||||||
const extractComponentInfo = useCallback((code: string, defaultName = '') => {
|
const extractComponentInfo = useCallback((code: string, defaultName = '') => {
|
||||||
|
|
@ -223,35 +327,50 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const componentInfos = activeComponents.map((comp) => {
|
const componentInfos = activeComponents.map((comp, index) => {
|
||||||
const name = comp.name
|
const name = comp.name
|
||||||
const nameCapitalized = name.charAt(0).toUpperCase() + name.slice(1)
|
const nameCapitalized = name.charAt(0).toUpperCase() + name.slice(1)
|
||||||
|
const runtimeCode = getComponentRuntimeCode(comp)
|
||||||
|
const referenceName = toJavaScriptIdentifier(nameCapitalized)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: name,
|
name: name,
|
||||||
nameCapitalized: nameCapitalized,
|
nameCapitalized: nameCapitalized,
|
||||||
internalName: extractComponentInfo(comp.code, nameCapitalized),
|
referenceName,
|
||||||
code: comp.code
|
bundleVariable: `__component_${index}`,
|
||||||
.replace(/import\s+.*?;/g, '')
|
internalName: extractComponentInfo(runtimeCode, referenceName),
|
||||||
.replace(/export\s+default\s+/, '')
|
code: normalizeComponentCodeForBundle(runtimeCode),
|
||||||
.trim(),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Create cross-referencing bundle
|
// Create cross-referencing bundle
|
||||||
const componentDeclarations = componentInfos
|
const componentDeclarations = componentInfos
|
||||||
.map((info) => `let ${info.name}_Component;`)
|
.map((info) => `let ${info.bundleVariable};`)
|
||||||
.join('\n')
|
.join('\n')
|
||||||
|
|
||||||
const componentDefinitions = componentInfos
|
const componentDefinitions = componentInfos
|
||||||
.map((info) => {
|
.map((info) => {
|
||||||
|
const declaredReferences = new Set<string>()
|
||||||
const componentVariables = componentInfos
|
const componentVariables = componentInfos
|
||||||
.filter((other) => other.name !== info.name)
|
.filter((other) => other.name !== info.name)
|
||||||
.map((other) => `const ${other.name} = ${other.name}_Component;`)
|
.filter((other) => {
|
||||||
|
if (
|
||||||
|
other.referenceName === info.internalName ||
|
||||||
|
declaredReferences.has(other.referenceName)
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
declaredReferences.add(other.referenceName)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
.map(
|
||||||
|
(other) =>
|
||||||
|
`const ${other.referenceName} = (componentProps) => React.createElement(${other.bundleVariable}, componentProps);`,
|
||||||
|
)
|
||||||
.join('\n ')
|
.join('\n ')
|
||||||
|
|
||||||
return `
|
return `
|
||||||
${info.name}_Component = (function() {
|
${info.bundleVariable} = (function() {
|
||||||
${componentVariables}
|
${componentVariables}
|
||||||
${info.code}
|
${info.code}
|
||||||
return ${info.internalName};
|
return ${info.internalName};
|
||||||
|
|
@ -262,7 +381,7 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
||||||
const componentBundle = componentDeclarations + '\n' + componentDefinitions
|
const componentBundle = componentDeclarations + '\n' + componentDefinitions
|
||||||
|
|
||||||
const bundledCode = `
|
const bundledCode = `
|
||||||
(function(React, Alert, Avatar, Badge, Button, Calendar, Card, Checkbox, ConfigProvider, DatePicker, Dialog, Drawer, Dropdown, FormItem, FormContainer, Input, InputGroup, Menu, MenuItem, Notification, Pagination, Progress, Radio, RangeCalendar, ScrollBar, Segment, Select, Skeleton, Spinner, Steps, Switcher, Table, Tabs, Tag, TimeInput, Timeline, toast, Tooltip, Upload, axios) {
|
(function(React, AutoComplete, Alert, Avatar, Badge, Button, Calendar, Card, Checkbox, ConfigProvider, DatePicker, Dialog, Drawer, Dropdown, FormItem, FormContainer, Input, InputGroup, Menu, MenuItem, Notification, Pagination, Progress, Radio, RangeCalendar, ScrollBar, Segment, Select, Skeleton, Spinner, Steps, Switcher, Table, Tabs, Tag, TimeInput, Timeline, toast, Tooltip, Upload, PlatformViewHost, apiService, DOMPurify, axios, UiKit) {
|
||||||
const { useState, useEffect, useCallback, useMemo, useRef, createContext, useContext } = React;
|
const { useState, useEffect, useCallback, useMemo, useRef, createContext, useContext } = React;
|
||||||
const componentRegistry = {};
|
const componentRegistry = {};
|
||||||
|
|
||||||
|
|
@ -271,26 +390,28 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
||||||
${componentInfos
|
${componentInfos
|
||||||
.map(
|
.map(
|
||||||
(info) => `
|
(info) => `
|
||||||
componentRegistry["${info.name}"] = ${info.name}_Component;
|
componentRegistry[${JSON.stringify(info.name)}] = ${info.bundleVariable};
|
||||||
componentRegistry["${info.nameCapitalized}"] = ${info.name}_Component;
|
componentRegistry[${JSON.stringify(info.nameCapitalized)}] = ${info.bundleVariable};
|
||||||
`,
|
`,
|
||||||
)
|
)
|
||||||
.join('\n')}
|
.join('\n')}
|
||||||
|
|
||||||
return componentRegistry;
|
return componentRegistry;
|
||||||
})(React, Alert, Avatar, Badge, Button, Calendar, Card, Checkbox, ConfigProvider, DatePicker, Dialog, Drawer, Dropdown, FormItem, FormContainer, Input, InputGroup, Menu, MenuItem, Notification, Pagination, Progress, Radio, RangeCalendar, ScrollBar, Segment, Select, Skeleton, Spinner, Steps, Switcher, Table, Tabs, Tag, TimeInput, Timeline, toast, Tooltip, Upload, axios)
|
})(React, AutoComplete, Alert, Avatar, Badge, Button, Calendar, Card, Checkbox, ConfigProvider, DatePicker, Dialog, Drawer, Dropdown, FormItem, FormContainer, Input, InputGroup, Menu, MenuItem, Notification, Pagination, Progress, Radio, RangeCalendar, ScrollBar, Segment, Select, Skeleton, Spinner, Steps, Switcher, Table, Tabs, Tag, TimeInput, Timeline, toast, Tooltip, Upload, PlatformViewHost, apiService, DOMPurify, axios, UiKit)
|
||||||
`
|
`
|
||||||
|
|
||||||
// Babel is several megabytes and is only needed when an active runtime
|
// Babel is several megabytes and is only needed when an active runtime
|
||||||
// component exists. Keep it out of the application startup bundle.
|
// component exists. Keep it out of the application startup bundle.
|
||||||
const [Babel, ui, { default: axios }] = await Promise.all([
|
const [Babel, ui, { default: axios }, { default: PlatformViewHost }] = await Promise.all([
|
||||||
import('@babel/standalone'),
|
import('@babel/standalone'),
|
||||||
import('../components/ui'),
|
import('../components/ui'),
|
||||||
import('axios'),
|
import('axios'),
|
||||||
|
import('../components/componentEditor/PlatformViewHost'),
|
||||||
])
|
])
|
||||||
if (cancelled) return
|
if (cancelled) return
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
AutoComplete,
|
||||||
Alert,
|
Alert,
|
||||||
Avatar,
|
Avatar,
|
||||||
Badge,
|
Badge,
|
||||||
|
|
@ -342,6 +463,7 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
||||||
|
|
||||||
const componentsFactory = new Function(
|
const componentsFactory = new Function(
|
||||||
'React',
|
'React',
|
||||||
|
'AutoComplete',
|
||||||
'Alert',
|
'Alert',
|
||||||
'Avatar',
|
'Avatar',
|
||||||
'Badge',
|
'Badge',
|
||||||
|
|
@ -380,12 +502,17 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
||||||
'toast',
|
'toast',
|
||||||
'Tooltip',
|
'Tooltip',
|
||||||
'Upload',
|
'Upload',
|
||||||
|
'PlatformViewHost',
|
||||||
|
'apiService',
|
||||||
|
'DOMPurify',
|
||||||
'axios',
|
'axios',
|
||||||
|
'UiKit',
|
||||||
`return ${compiledBundle}`,
|
`return ${compiledBundle}`,
|
||||||
)
|
)
|
||||||
|
|
||||||
const compiledComponentsRegistry = componentsFactory(
|
const compiledComponentsRegistry = componentsFactory(
|
||||||
React,
|
React,
|
||||||
|
AutoComplete,
|
||||||
Alert,
|
Alert,
|
||||||
Avatar,
|
Avatar,
|
||||||
Badge,
|
Badge,
|
||||||
|
|
@ -424,7 +551,11 @@ export const ComponentProvider: React.FC<{ children: React.ReactNode }> = ({ chi
|
||||||
toast,
|
toast,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Upload,
|
Upload,
|
||||||
|
PlatformViewHost,
|
||||||
|
apiService,
|
||||||
|
DOMPurify,
|
||||||
axios,
|
axios,
|
||||||
|
ui,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!cancelled) setCompiledComponents(compiledComponentsRegistry)
|
if (!cancelled) setCompiledComponents(compiledComponentsRegistry)
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,3 @@
|
||||||
export interface ComponentInfo {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
props: Record<string, any>;
|
|
||||||
children?: ComponentInfo[] | string;
|
|
||||||
startLine: number;
|
|
||||||
endLine: number;
|
|
||||||
startColumn: number;
|
|
||||||
endColumn: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PropertyInfo {
|
export interface PropertyInfo {
|
||||||
name: string;
|
name: string;
|
||||||
type:
|
type:
|
||||||
|
|
@ -25,8 +13,7 @@ export interface PropertyInfo {
|
||||||
description?: string;
|
description?: string;
|
||||||
category: "properties" | "events" | "styling";
|
category: "properties" | "events" | "styling";
|
||||||
}
|
}
|
||||||
|
interface HookInfo {
|
||||||
export interface HookInfo {
|
|
||||||
name: string;
|
name: string;
|
||||||
type:
|
type:
|
||||||
| "useState"
|
| "useState"
|
||||||
|
|
@ -49,14 +36,3 @@ export interface ComponentDefinition {
|
||||||
hooks: HookInfo[];
|
hooks: HookInfo[];
|
||||||
defaultProps?: Record<string, any>;
|
defaultProps?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TailwindClass {
|
|
||||||
category: string;
|
|
||||||
classes: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EditorState {
|
|
||||||
code: string;
|
|
||||||
components: ComponentInfo[];
|
|
||||||
selectedComponentId: string | null;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ class DeveloperKitService {
|
||||||
const response = await apiService.fetchData<PagedResultDto<CustomComponentDto>>({
|
const response = await apiService.fetchData<PagedResultDto<CustomComponentDto>>({
|
||||||
url: '/api/app/custom-component',
|
url: '/api/app/custom-component',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
params: { maxResultCount: 1000 },
|
||||||
})
|
})
|
||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
@ -47,10 +48,14 @@ class DeveloperKitService {
|
||||||
id: string,
|
id: string,
|
||||||
component: CreateUpdateCustomComponentDto,
|
component: CreateUpdateCustomComponentDto,
|
||||||
): Promise<CustomComponent> {
|
): Promise<CustomComponent> {
|
||||||
|
const payload =
|
||||||
|
component.props === undefined
|
||||||
|
? { ...component, props: (await this.getCustomComponent(id)).props }
|
||||||
|
: component
|
||||||
const response = await apiService.fetchData<CustomComponent>({
|
const response = await apiService.fetchData<CustomComponent>({
|
||||||
url: `/api/app/custom-component/${id}`,
|
url: `/api/app/custom-component/${id}`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: component as any,
|
data: payload as any,
|
||||||
})
|
})
|
||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
@ -91,6 +96,7 @@ class DeveloperKitService {
|
||||||
const response = await apiService.fetchData<PagedResultDto<CrudEndpoint>>({
|
const response = await apiService.fetchData<PagedResultDto<CrudEndpoint>>({
|
||||||
url: '/api/app/crud-endpoint-generate',
|
url: '/api/app/crud-endpoint-generate',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
params: { maxResultCount: 1000 },
|
||||||
})
|
})
|
||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,799 +0,0 @@
|
||||||
import * as parser from "@babel/parser";
|
|
||||||
import traverse from "@babel/traverse";
|
|
||||||
import * as t from "@babel/types";
|
|
||||||
import generate from "@babel/generator";
|
|
||||||
import { ComponentInfo } from "../proxy/developerKit/componentInfo";
|
|
||||||
|
|
||||||
export interface ParsedComponent {
|
|
||||||
components: ComponentInfo[];
|
|
||||||
imports: string[];
|
|
||||||
hooks: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const generateUniqueId = (): string => {
|
|
||||||
// Include timestamp for better uniqueness
|
|
||||||
return (
|
|
||||||
"c_" +
|
|
||||||
Date.now().toString(36) +
|
|
||||||
"_" +
|
|
||||||
Math.random().toString(36).substring(2, 8)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const generateSingleComponentJSX = (
|
|
||||||
type: string,
|
|
||||||
props: Record<string, { type: string; value: any }>
|
|
||||||
): string => {
|
|
||||||
const attributes = Object.entries(props)
|
|
||||||
.filter(([key]) => key !== "children")
|
|
||||||
.map(([key, propInfo]) => {
|
|
||||||
const { type, value } = propInfo;
|
|
||||||
|
|
||||||
// null ve boş değerleri ekleme
|
|
||||||
if (value === null || value === "") return "";
|
|
||||||
|
|
||||||
// object tipindeki boş nesneleri atla
|
|
||||||
if (
|
|
||||||
type === "object" &&
|
|
||||||
(value === "" ||
|
|
||||||
(typeof value === "object" &&
|
|
||||||
value !== null &&
|
|
||||||
Object.keys(value).length === 0))
|
|
||||||
) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// false boolean'ları atla
|
|
||||||
if (type === "boolean" && value === false) return "";
|
|
||||||
|
|
||||||
// true boolean'ları yalnızca anahtar olarak ekle
|
|
||||||
if (type === "boolean" && value === true) return `${key}`;
|
|
||||||
|
|
||||||
// number her zaman eklenir
|
|
||||||
if (type === "number") return `${key}={${value}}`;
|
|
||||||
|
|
||||||
// Diğer her şey
|
|
||||||
return `${key}=${JSON.stringify(value)}`;
|
|
||||||
})
|
|
||||||
.filter(Boolean)
|
|
||||||
.join(" ");
|
|
||||||
|
|
||||||
const children = props.children?.value ?? "";
|
|
||||||
|
|
||||||
return `<${type}${attributes ? " " + attributes : ""}>${children}</${type}>`;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseReactCode = (code: string): ParsedComponent => {
|
|
||||||
try {
|
|
||||||
// Clean up duplicate imports before parsing
|
|
||||||
const cleanCode = cleanupDuplicateImports(code);
|
|
||||||
|
|
||||||
const ast = parser.parse(cleanCode, {
|
|
||||||
sourceType: "module",
|
|
||||||
plugins: ["jsx", "typescript"],
|
|
||||||
});
|
|
||||||
|
|
||||||
const components: ComponentInfo[] = [];
|
|
||||||
const imports: string[] = [];
|
|
||||||
const hooks: string[] = [];
|
|
||||||
|
|
||||||
traverse(ast, {
|
|
||||||
ImportDeclaration(path) {
|
|
||||||
if (path.node.source.value === "react") {
|
|
||||||
path.node.specifiers.forEach((spec) => {
|
|
||||||
if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
|
|
||||||
imports.push(spec.imported.name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
CallExpression(path) {
|
|
||||||
if (t.isIdentifier(path.node.callee)) {
|
|
||||||
const functionName = path.node.callee.name;
|
|
||||||
if (functionName.startsWith("use")) {
|
|
||||||
hooks.push(functionName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
JSXElement(path) {
|
|
||||||
const element = path.node;
|
|
||||||
if (t.isJSXIdentifier(element.openingElement.name)) {
|
|
||||||
const componentName = element.openingElement.name.name;
|
|
||||||
const props: Record<string, any> = {};
|
|
||||||
|
|
||||||
// Extract props
|
|
||||||
element.openingElement.attributes.forEach((attr) => {
|
|
||||||
if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name)) {
|
|
||||||
const propName = attr.name.name;
|
|
||||||
let propValue: any = "";
|
|
||||||
|
|
||||||
if (attr.value) {
|
|
||||||
if (t.isStringLiteral(attr.value)) {
|
|
||||||
propValue = attr.value.value;
|
|
||||||
} else if (t.isJSXExpressionContainer(attr.value)) {
|
|
||||||
if (t.isStringLiteral(attr.value.expression)) {
|
|
||||||
propValue = attr.value.expression.value;
|
|
||||||
} else if (t.isBooleanLiteral(attr.value.expression)) {
|
|
||||||
propValue = attr.value.expression.value;
|
|
||||||
} else if (t.isNumericLiteral(attr.value.expression)) {
|
|
||||||
propValue = attr.value.expression.value;
|
|
||||||
} else {
|
|
||||||
// For complex expressions, store as string
|
|
||||||
propValue = cleanCode.slice(
|
|
||||||
attr.value.expression.start!,
|
|
||||||
attr.value.expression.end!
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
propValue = true; // Boolean prop without value
|
|
||||||
}
|
|
||||||
|
|
||||||
props[propName] = propValue;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add unique ID if not present
|
|
||||||
if (!props.id || !props.id.startsWith("c_")) {
|
|
||||||
props.id = generateUniqueId();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract children content - only if it's simple text/expression content
|
|
||||||
const childrenContent = extractChildrenContent(element, cleanCode);
|
|
||||||
const hasNestedElements =
|
|
||||||
element.children &&
|
|
||||||
element.children.some(
|
|
||||||
(child) => t.isJSXElement(child) || t.isJSXFragment(child)
|
|
||||||
);
|
|
||||||
const component: ComponentInfo = {
|
|
||||||
id: props.id,
|
|
||||||
name: componentName,
|
|
||||||
type: componentName.toLowerCase(),
|
|
||||||
props,
|
|
||||||
children: hasNestedElements ? undefined : childrenContent,
|
|
||||||
startLine: element.loc?.start.line || 0,
|
|
||||||
endLine: element.loc?.end.line || 0,
|
|
||||||
startColumn: element.loc?.start.column || 0,
|
|
||||||
endColumn: element.loc?.end.column || 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
components.push(component);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return { components, imports, hooks };
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error parsing React code:", error);
|
|
||||||
return { components: [], imports: [], hooks: [] };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const extractChildrenContent = (element: any, code: string): string => {
|
|
||||||
if (!element.children || element.children.length === 0) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the content between opening and closing tags
|
|
||||||
const start = element.openingElement.end;
|
|
||||||
const end = element.closingElement
|
|
||||||
? element.closingElement.start
|
|
||||||
: element.end;
|
|
||||||
|
|
||||||
if (start && end && start < end) {
|
|
||||||
return code.slice(start, end).trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
};
|
|
||||||
|
|
||||||
export const generateHookVariableName = (
|
|
||||||
hookType: string,
|
|
||||||
componentId: string
|
|
||||||
): string => {
|
|
||||||
// Use the full componentId without shortening
|
|
||||||
const cleanId = componentId.replace("c_", ""); // Remove c_ prefix but keep the rest
|
|
||||||
|
|
||||||
switch (hookType) {
|
|
||||||
case "useState":
|
|
||||||
return `state_c_${cleanId}`;
|
|
||||||
case "useRef":
|
|
||||||
return `ref_c_${cleanId}`;
|
|
||||||
case "useEffect":
|
|
||||||
return `effect_c_${cleanId}`;
|
|
||||||
case "useCallback":
|
|
||||||
return `callback_c_${cleanId}`;
|
|
||||||
case "useMemo":
|
|
||||||
return `memo_c_${cleanId}`;
|
|
||||||
default:
|
|
||||||
return `hook_c_${cleanId}`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const removeHookFromCode = (
|
|
||||||
code: string,
|
|
||||||
hookType: string,
|
|
||||||
componentId: string
|
|
||||||
): string => {
|
|
||||||
const varName = generateHookVariableName(hookType, componentId);
|
|
||||||
// Sadece hook tanım satırını sil
|
|
||||||
// ^\s*const\s+\[?.*?varName.*?\]?\s*=\s*hookType\(.*?\);\s*$
|
|
||||||
// Bu satırı, başında ve sonunda yalnızca bir satırı siler şekilde tasarla
|
|
||||||
const hookLineRegex = new RegExp(
|
|
||||||
`^\\s*const\\s+(?:\\[.*?${varName}.*?\\]|${varName})\\s*=\\s*${hookType}\\([^)]*\\);?\\s*$`,
|
|
||||||
"gm"
|
|
||||||
);
|
|
||||||
code = code.replace(hookLineRegex, "");
|
|
||||||
|
|
||||||
// Import'tan kaldırma mantığı aynı kalabilir
|
|
||||||
const remainingHooks = code.match(new RegExp(`${hookType}\\(`, "g"));
|
|
||||||
if (!remainingHooks || remainingHooks.length === 0) {
|
|
||||||
const reactImportRegex =
|
|
||||||
// eslint-disable-next-line no-useless-escape
|
|
||||||
/import\s+(React\s*,\s*)?\{([^}]*)\}\s+from\s+['"]react['\"];?\s*/g;
|
|
||||||
let match;
|
|
||||||
let newCode = code;
|
|
||||||
while ((match = reactImportRegex.exec(code)) !== null) {
|
|
||||||
const importLine = match[0];
|
|
||||||
const hooks = match[2]
|
|
||||||
.split(",")
|
|
||||||
.map((h) => h.trim())
|
|
||||||
.filter((h) => h && h !== hookType);
|
|
||||||
let newImport = "";
|
|
||||||
if (hooks.length > 0) {
|
|
||||||
newImport = match[1]
|
|
||||||
? `import React, { ${hooks.join(", ")} } from 'react';\n`
|
|
||||||
: `import { ${hooks.join(", ")} } from 'react';\n`;
|
|
||||||
} else {
|
|
||||||
newImport = match[1] ? `import React from 'react';\n` : "";
|
|
||||||
}
|
|
||||||
newCode = newCode.replace(importLine, newImport);
|
|
||||||
}
|
|
||||||
code = newCode;
|
|
||||||
}
|
|
||||||
// Duplicate import cleanup aynı kalabilir
|
|
||||||
code = cleanupDuplicateImports(code);
|
|
||||||
return code;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const insertJSXAtPosition = (
|
|
||||||
code: string,
|
|
||||||
jsx: string,
|
|
||||||
position: { line: number; column: number }
|
|
||||||
): string => {
|
|
||||||
const lines = code.split("\n");
|
|
||||||
|
|
||||||
// JSX kodunu istenen satıra ekle
|
|
||||||
lines.splice(position.line, 0, jsx);
|
|
||||||
|
|
||||||
return lines.join("\n");
|
|
||||||
};
|
|
||||||
|
|
||||||
export const generateComponentJSX = (component: ComponentInfo): string => {
|
|
||||||
const { type, props } = component;
|
|
||||||
|
|
||||||
const propsString = Object.entries(props)
|
|
||||||
.filter(([key, val]) => {
|
|
||||||
if (
|
|
||||||
key === "children" ||
|
|
||||||
!val ||
|
|
||||||
typeof val !== "object" ||
|
|
||||||
!("value" in val)
|
|
||||||
)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const isFunction = val.type === "function";
|
|
||||||
const isObject = val.type === "object";
|
|
||||||
const isBoolean = typeof val.value === "boolean";
|
|
||||||
|
|
||||||
return (
|
|
||||||
val.value !== null &&
|
|
||||||
val.value !== undefined &&
|
|
||||||
(isFunction || isBoolean || val.value !== "") &&
|
|
||||||
(isObject || Object.keys(val.value).length > 0)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.map(([key, val]) => {
|
|
||||||
const propVal =
|
|
||||||
val && typeof val === "object" && "value" in val ? val.value : val;
|
|
||||||
|
|
||||||
if (typeof propVal === "string") return `${key}="${propVal}"`;
|
|
||||||
if (typeof propVal === "number") return `${key}={${propVal}}`;
|
|
||||||
if (typeof propVal === "boolean")
|
|
||||||
return propVal ? `${key}` : `${key}={false}`;
|
|
||||||
return `${key}={${JSON.stringify(propVal)}}`;
|
|
||||||
})
|
|
||||||
.join(" ");
|
|
||||||
|
|
||||||
console.log("📝 App: Generated props string:", propsString);
|
|
||||||
|
|
||||||
const children =
|
|
||||||
props.children &&
|
|
||||||
typeof props.children === "object" &&
|
|
||||||
"value" in props.children
|
|
||||||
? props.children.value
|
|
||||||
: "";
|
|
||||||
const hasProps = propsString.length > 0;
|
|
||||||
const hasChildren = children && children.length > 0;
|
|
||||||
|
|
||||||
if (hasChildren) {
|
|
||||||
return `<${type} id="${component.id}"${
|
|
||||||
hasProps ? " " + propsString : ""
|
|
||||||
}>${children}</${type}>`;
|
|
||||||
} else {
|
|
||||||
return `<${type} id="${component.id}"${
|
|
||||||
hasProps ? " " + propsString : ""
|
|
||||||
} />`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const generateHookCode = (
|
|
||||||
hookType: string,
|
|
||||||
componentId: string,
|
|
||||||
componentType: string,
|
|
||||||
initialValue?: any
|
|
||||||
): string => {
|
|
||||||
const varName = generateHookVariableName(hookType, componentId);
|
|
||||||
|
|
||||||
switch (hookType) {
|
|
||||||
case "useState":
|
|
||||||
const defaultValue = getDefaultValueForComponent(
|
|
||||||
componentType,
|
|
||||||
initialValue
|
|
||||||
);
|
|
||||||
// Generate proper camelCase setter name from state variable
|
|
||||||
const setterName = `set${
|
|
||||||
varName.charAt(0).toUpperCase() + varName.slice(1)
|
|
||||||
}`;
|
|
||||||
return `const [${varName}, ${setterName}] = useState(${defaultValue});`;
|
|
||||||
|
|
||||||
case "useRef":
|
|
||||||
return `const ${varName} = useRef(null);`;
|
|
||||||
|
|
||||||
case "useCallback":
|
|
||||||
return `const ${varName} = useCallback(() => {\n // Callback logic here\n }, []);`;
|
|
||||||
|
|
||||||
case "useMemo":
|
|
||||||
return `const ${varName} = useMemo(() => {\n // Memo logic here\n }, []);`;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return `const ${varName} = ${hookType}();`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getDefaultValueForComponent = (
|
|
||||||
componentType: string,
|
|
||||||
initialValue?: any
|
|
||||||
): string => {
|
|
||||||
if (initialValue !== undefined) {
|
|
||||||
return typeof initialValue === "string"
|
|
||||||
? `'${initialValue}'`
|
|
||||||
: String(initialValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (componentType) {
|
|
||||||
case "input":
|
|
||||||
case "textarea":
|
|
||||||
return "''";
|
|
||||||
case "checkbox":
|
|
||||||
return "false";
|
|
||||||
case "select":
|
|
||||||
return "''";
|
|
||||||
case "button":
|
|
||||||
return "false";
|
|
||||||
default:
|
|
||||||
return "''";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateComponentProp = (
|
|
||||||
code: string,
|
|
||||||
componentId: string,
|
|
||||||
propName: string,
|
|
||||||
propValue: any
|
|
||||||
): string => {
|
|
||||||
// Special handling for children
|
|
||||||
if (propName === "children") {
|
|
||||||
return updateComponentChildren(code, componentId, propValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("🔧 updateComponentProp called:", {
|
|
||||||
componentId,
|
|
||||||
propName,
|
|
||||||
propValue,
|
|
||||||
propType: typeof propValue,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clean up duplicate imports before parsing
|
|
||||||
const cleanCode = cleanupDuplicateImports(code);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const ast = parser.parse(cleanCode, {
|
|
||||||
sourceType: "module",
|
|
||||||
plugins: ["jsx", "typescript"],
|
|
||||||
});
|
|
||||||
|
|
||||||
let updatedCode = cleanCode;
|
|
||||||
let offset = 0;
|
|
||||||
|
|
||||||
traverse(ast, {
|
|
||||||
JSXElement(path) {
|
|
||||||
const element = path.node;
|
|
||||||
if (t.isJSXIdentifier(element.openingElement.name)) {
|
|
||||||
// Find the component with matching ID
|
|
||||||
const idAttr = element.openingElement.attributes.find(
|
|
||||||
(attr) =>
|
|
||||||
t.isJSXAttribute(attr) &&
|
|
||||||
t.isJSXIdentifier(attr.name) &&
|
|
||||||
attr.name.name === "id" &&
|
|
||||||
attr.value &&
|
|
||||||
t.isStringLiteral(attr.value) &&
|
|
||||||
attr.value.value === componentId
|
|
||||||
);
|
|
||||||
|
|
||||||
if (idAttr) {
|
|
||||||
// Find existing prop or add new one
|
|
||||||
const existingPropIndex =
|
|
||||||
element.openingElement.attributes.findIndex(
|
|
||||||
(attr) =>
|
|
||||||
t.isJSXAttribute(attr) &&
|
|
||||||
t.isJSXIdentifier(attr.name) &&
|
|
||||||
attr.name.name === propName
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log("📍 Existing prop index:", existingPropIndex);
|
|
||||||
// Generate proper JSX attribute string
|
|
||||||
let newPropString: string | null = null;
|
|
||||||
let shouldRemoveAttribute = false;
|
|
||||||
|
|
||||||
if (
|
|
||||||
propValue === null ||
|
|
||||||
propValue === undefined ||
|
|
||||||
propValue === ""
|
|
||||||
) {
|
|
||||||
shouldRemoveAttribute = true;
|
|
||||||
console.log("🗑️ Will remove attribute - value is:", propValue);
|
|
||||||
} else if (propValue === true) {
|
|
||||||
newPropString = propName; // Boolean true: just the attribute name
|
|
||||||
console.log("✅ Boolean true prop:", newPropString);
|
|
||||||
} else if (typeof propValue === "string") {
|
|
||||||
// Handle event props and JSX expressions
|
|
||||||
if (propName.startsWith("on") || propValue.startsWith("{")) {
|
|
||||||
// Event props or JSX expressions - ensure proper braces
|
|
||||||
if (propValue.startsWith("{") && propValue.endsWith("}")) {
|
|
||||||
newPropString = `${propName}=${propValue}`;
|
|
||||||
} else {
|
|
||||||
newPropString = `${propName}={${propValue}}`;
|
|
||||||
}
|
|
||||||
console.log("🎯 Event/JSX prop:", newPropString);
|
|
||||||
} else {
|
|
||||||
newPropString = `${propName}="${propValue}"`; // String literal
|
|
||||||
console.log("📝 String prop:", newPropString);
|
|
||||||
}
|
|
||||||
} else if (typeof propValue === "boolean") {
|
|
||||||
newPropString = `${propName}={${propValue}}`;
|
|
||||||
console.log("🔘 Boolean prop:", newPropString);
|
|
||||||
} else {
|
|
||||||
newPropString = `${propName}={${propValue}}`; // Other values as JSX expression
|
|
||||||
console.log("🔢 Other prop:", newPropString);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingPropIndex !== -1) {
|
|
||||||
// Update existing prop
|
|
||||||
const existingProp =
|
|
||||||
element.openingElement.attributes[existingPropIndex];
|
|
||||||
if (t.isJSXAttribute(existingProp)) {
|
|
||||||
const attrStart = existingProp.start! + offset;
|
|
||||||
const attrEnd = existingProp.end! + offset;
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
"🔄 Updating existing prop at position:",
|
|
||||||
attrStart,
|
|
||||||
"-",
|
|
||||||
attrEnd
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
"🔄 Old attribute:",
|
|
||||||
updatedCode.slice(attrStart, attrEnd)
|
|
||||||
);
|
|
||||||
if (shouldRemoveAttribute) {
|
|
||||||
// Remove the entire attribute with proper whitespace handling
|
|
||||||
let removeStart = attrStart;
|
|
||||||
const removeEnd = attrEnd;
|
|
||||||
|
|
||||||
// Look for whitespace/newline before the attribute
|
|
||||||
while (removeStart > 0) {
|
|
||||||
const char = updatedCode[removeStart - 1];
|
|
||||||
if (
|
|
||||||
char === " " ||
|
|
||||||
char === "\t" ||
|
|
||||||
char === "\n" ||
|
|
||||||
char === "\r"
|
|
||||||
) {
|
|
||||||
removeStart--;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we don't remove too much - keep at least one space if needed
|
|
||||||
const beforeChar =
|
|
||||||
removeStart > 0 ? updatedCode[removeStart - 1] : "";
|
|
||||||
const afterChar =
|
|
||||||
removeEnd < updatedCode.length
|
|
||||||
? updatedCode[removeEnd]
|
|
||||||
: "";
|
|
||||||
|
|
||||||
// If we're between two attributes or after tag name, ensure proper spacing
|
|
||||||
if (
|
|
||||||
beforeChar &&
|
|
||||||
beforeChar !== " " &&
|
|
||||||
beforeChar !== "\n" &&
|
|
||||||
beforeChar !== "\t" &&
|
|
||||||
afterChar &&
|
|
||||||
afterChar !== " " &&
|
|
||||||
afterChar !== "\n" &&
|
|
||||||
afterChar !== "\t" &&
|
|
||||||
afterChar !== ">" &&
|
|
||||||
afterChar !== "/"
|
|
||||||
) {
|
|
||||||
// Insert a space to prevent attributes from merging
|
|
||||||
updatedCode =
|
|
||||||
updatedCode.slice(0, removeStart) +
|
|
||||||
" " +
|
|
||||||
updatedCode.slice(removeEnd);
|
|
||||||
offset -= removeEnd - removeStart - 1; // -1 because we added a space
|
|
||||||
} else {
|
|
||||||
updatedCode =
|
|
||||||
updatedCode.slice(0, removeStart) +
|
|
||||||
updatedCode.slice(removeEnd);
|
|
||||||
offset -= removeEnd - removeStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
"🗑️ Removed attribute from",
|
|
||||||
removeStart,
|
|
||||||
"to",
|
|
||||||
removeEnd
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Replace the entire attribute
|
|
||||||
if (newPropString) {
|
|
||||||
console.log("🔄 Replacing with:", newPropString);
|
|
||||||
updatedCode =
|
|
||||||
updatedCode.slice(0, attrStart) +
|
|
||||||
newPropString +
|
|
||||||
updatedCode.slice(attrEnd);
|
|
||||||
offset += newPropString.length - (attrEnd - attrStart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop traversal after modifying the target component
|
|
||||||
path.stop();
|
|
||||||
} else if (!shouldRemoveAttribute && newPropString) {
|
|
||||||
// Add new prop
|
|
||||||
const insertPos = element.openingElement.name.end! + offset;
|
|
||||||
const propToInsert = ` ${newPropString}`;
|
|
||||||
console.log(
|
|
||||||
"➕ Adding new prop at position",
|
|
||||||
insertPos,
|
|
||||||
":",
|
|
||||||
propToInsert
|
|
||||||
);
|
|
||||||
updatedCode =
|
|
||||||
updatedCode.slice(0, insertPos) +
|
|
||||||
propToInsert +
|
|
||||||
updatedCode.slice(insertPos);
|
|
||||||
offset += propToInsert.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop traversal after processing the target component
|
|
||||||
path.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
"✅ updateComponentProp completed. Code changed:",
|
|
||||||
code !== updatedCode
|
|
||||||
);
|
|
||||||
return updatedCode;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error updating component prop:", error);
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const cleanupDuplicateImports = (code: string): string => {
|
|
||||||
// Remove duplicate React imports
|
|
||||||
const reactImportRegex =
|
|
||||||
/import\s+React(?:\s*,\s*\{[^}]*\})?\s+from\s+['"]react['"];\s*/g;
|
|
||||||
const reactImports = code.match(reactImportRegex);
|
|
||||||
|
|
||||||
if (reactImports && reactImports.length > 1) {
|
|
||||||
// Collect all hooks from all imports
|
|
||||||
const allHooks = new Set<string>();
|
|
||||||
reactImports.forEach((importLine) => {
|
|
||||||
const hooksMatch = importLine.match(/\{([^}]*)\}/);
|
|
||||||
if (hooksMatch) {
|
|
||||||
const hooks = hooksMatch[1]
|
|
||||||
.split(",")
|
|
||||||
.map((h) => h.trim())
|
|
||||||
.filter((h) => h);
|
|
||||||
hooks.forEach((hook) => allHooks.add(hook));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove all React imports
|
|
||||||
let cleanedCode = code.replace(reactImportRegex, "");
|
|
||||||
|
|
||||||
// Add single consolidated import
|
|
||||||
if (allHooks.size > 0) {
|
|
||||||
const consolidatedImport = `import React, { ${Array.from(allHooks).join(
|
|
||||||
", "
|
|
||||||
)} } from 'react';\n`;
|
|
||||||
cleanedCode = consolidatedImport + cleanedCode;
|
|
||||||
} // else: Hiç import ekleme
|
|
||||||
|
|
||||||
console.log("✅ Consolidated imports:", Array.from(allHooks));
|
|
||||||
return cleanedCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return code;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateComponentProps = (
|
|
||||||
code: string,
|
|
||||||
componentId: string,
|
|
||||||
updates: Record<string, any>
|
|
||||||
): string => {
|
|
||||||
console.log("🔄 updateComponentProps called:", { componentId, updates });
|
|
||||||
|
|
||||||
// Apply each update individually to avoid offset calculation issues
|
|
||||||
let updatedCode = code;
|
|
||||||
|
|
||||||
Object.entries(updates).forEach(([propName, propValue]) => {
|
|
||||||
console.log(`<EFBFBD> Processing update: ${propName} = ${propValue}`);
|
|
||||||
updatedCode = updateComponentProp(
|
|
||||||
updatedCode,
|
|
||||||
componentId,
|
|
||||||
propName,
|
|
||||||
propValue
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
"✅ updateComponentProps completed. Code changed:",
|
|
||||||
code !== updatedCode
|
|
||||||
);
|
|
||||||
return updatedCode;
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateComponentChildren = (
|
|
||||||
code: string,
|
|
||||||
componentId: string,
|
|
||||||
newChildren: string
|
|
||||||
): string => {
|
|
||||||
try {
|
|
||||||
const ast = parser.parse(code, {
|
|
||||||
sourceType: "module",
|
|
||||||
plugins: ["jsx", "typescript"],
|
|
||||||
});
|
|
||||||
|
|
||||||
let updatedCode = code;
|
|
||||||
let offset = 0;
|
|
||||||
|
|
||||||
traverse(ast, {
|
|
||||||
JSXElement(path) {
|
|
||||||
const element = path.node;
|
|
||||||
if (t.isJSXIdentifier(element.openingElement.name)) {
|
|
||||||
// Find the component with matching ID
|
|
||||||
const idAttr = element.openingElement.attributes.find(
|
|
||||||
(attr) =>
|
|
||||||
t.isJSXAttribute(attr) &&
|
|
||||||
t.isJSXIdentifier(attr.name) &&
|
|
||||||
attr.name.name === "id" &&
|
|
||||||
attr.value &&
|
|
||||||
t.isStringLiteral(attr.value) &&
|
|
||||||
attr.value.value === componentId
|
|
||||||
);
|
|
||||||
|
|
||||||
if (idAttr && element.closingElement) {
|
|
||||||
// Update children content
|
|
||||||
const start = element.openingElement.end! + offset;
|
|
||||||
const end = element.closingElement.start! + offset;
|
|
||||||
|
|
||||||
updatedCode =
|
|
||||||
updatedCode.slice(0, start) +
|
|
||||||
newChildren +
|
|
||||||
updatedCode.slice(end);
|
|
||||||
offset += newChildren.length - (end - start);
|
|
||||||
|
|
||||||
// Stop traversal after modifying the target component
|
|
||||||
path.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return updatedCode;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error updating component children:", error);
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export function removeComponentAndHooksFromCode(
|
|
||||||
code: string,
|
|
||||||
componentId: string
|
|
||||||
): string {
|
|
||||||
let ast;
|
|
||||||
try {
|
|
||||||
ast = parser.parse(code, {
|
|
||||||
sourceType: "module",
|
|
||||||
plugins: ["jsx", "typescript"],
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Parse error:", e);
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. JSX'i sil
|
|
||||||
traverse(ast, {
|
|
||||||
JSXElement(path) {
|
|
||||||
const el = path.node;
|
|
||||||
if (t.isJSXIdentifier(el.openingElement.name)) {
|
|
||||||
const idAttr = el.openingElement.attributes.find(
|
|
||||||
(attr) =>
|
|
||||||
t.isJSXAttribute(attr) &&
|
|
||||||
t.isJSXIdentifier(attr.name) &&
|
|
||||||
attr.name.name === "id" &&
|
|
||||||
attr.value &&
|
|
||||||
t.isStringLiteral(attr.value) &&
|
|
||||||
attr.value.value === componentId
|
|
||||||
);
|
|
||||||
if (idAttr) {
|
|
||||||
path.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 2. useState ve useRef hooklarını sil
|
|
||||||
traverse(ast, {
|
|
||||||
VariableDeclaration(path) {
|
|
||||||
const decl = path.node.declarations[0];
|
|
||||||
// useState
|
|
||||||
if (
|
|
||||||
t.isVariableDeclarator(decl) &&
|
|
||||||
t.isArrayPattern(decl.id) &&
|
|
||||||
t.isCallExpression(decl.init) &&
|
|
||||||
t.isIdentifier(decl.init.callee, { name: "useState" }) &&
|
|
||||||
decl.id.elements[0] &&
|
|
||||||
t.isIdentifier(decl.id.elements[0]) &&
|
|
||||||
decl.id.elements[0].name.includes(componentId)
|
|
||||||
) {
|
|
||||||
path.remove();
|
|
||||||
}
|
|
||||||
// useRef
|
|
||||||
if (
|
|
||||||
t.isVariableDeclarator(decl) &&
|
|
||||||
t.isIdentifier(decl.id) &&
|
|
||||||
t.isCallExpression(decl.init) &&
|
|
||||||
t.isIdentifier(decl.init.callee, { name: "useRef" }) &&
|
|
||||||
decl.id.name.includes(componentId)
|
|
||||||
) {
|
|
||||||
path.remove();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 3. Boş satırları temizle
|
|
||||||
let output = generate(ast, { retainLines: true }).code;
|
|
||||||
output = output.replace(/\n{3,}/g, "\n\n");
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
@ -1,675 +1,5 @@
|
||||||
import React, { useState, useEffect, useCallback, useRef } from 'react'
|
import VisualComponentDesigner from './VisualComponentDesigner'
|
||||||
import { FaThLarge } from 'react-icons/fa'
|
|
||||||
import {
|
|
||||||
parseReactCode,
|
|
||||||
updateComponentProp,
|
|
||||||
updateComponentProps,
|
|
||||||
generateHookCode,
|
|
||||||
generateHookVariableName,
|
|
||||||
removeHookFromCode,
|
|
||||||
removeComponentAndHooksFromCode,
|
|
||||||
} from '../../utils/codeParser'
|
|
||||||
import { ComponentLibrary } from '../../components/codeLayout/ComponentLibrary'
|
|
||||||
import { Splitter } from '../../components/codeLayout/Splitter'
|
|
||||||
import { PanelManager } from '../../components/codeLayout/PanelManager'
|
|
||||||
import { ComponentDefinition, EditorState } from '../../proxy/developerKit/componentInfo'
|
|
||||||
import PropertyPanel from '../../components/codeLayout/PropertyPanel'
|
|
||||||
import ComponentSelector from '../../components/codeLayout/ComponentSelector'
|
|
||||||
import { useParams } from 'react-router-dom'
|
|
||||||
import { useComponents } from '../../contexts/ComponentContext'
|
|
||||||
import { Button, toast } from '../../components/ui'
|
|
||||||
import Notification from '../../components/ui/Notification/Notification'
|
|
||||||
import { PanelState } from '../../components/codeLayout/data/componentDefinitions'
|
|
||||||
import { ComponentCodeEditor } from '@/components/codeLayout/ComponentCodeEditor'
|
|
||||||
|
|
||||||
const INITIAL_CODE = `const Component = () => {
|
// Kept as the route entry point for existing ABP menu records. The designer itself
|
||||||
return (
|
// lives in a separate component so the visual schema/runtime can evolve independently.
|
||||||
<>
|
export default VisualComponentDesigner
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Component
|
|
||||||
`
|
|
||||||
|
|
||||||
function ComponentCodeLayout() {
|
|
||||||
const { id } = useParams()
|
|
||||||
const { getComponent, updateComponent } = useComponents()
|
|
||||||
const [showPanelManager, setShowPanelManager] = useState(false)
|
|
||||||
const [panelState, setPanelState] = useState<PanelState>({
|
|
||||||
toolbox: true,
|
|
||||||
properties: true,
|
|
||||||
})
|
|
||||||
const [editorState, setEditorState] = useState<EditorState>({
|
|
||||||
code: INITIAL_CODE,
|
|
||||||
components: [],
|
|
||||||
selectedComponentId: null,
|
|
||||||
})
|
|
||||||
|
|
||||||
const isEditing = !!id
|
|
||||||
const [code, setCode] = useState<string>(INITIAL_CODE)
|
|
||||||
const [isLoaded, setIsLoaded] = useState(false)
|
|
||||||
const parseDebounceRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
||||||
const [name, setName] = useState('')
|
|
||||||
const [dependencies, setDependencies] = useState<string[]>([])
|
|
||||||
const [isActive, setIsActive] = useState(true)
|
|
||||||
|
|
||||||
const handleSave = async () => {
|
|
||||||
try {
|
|
||||||
const componentData = {
|
|
||||||
name: name.trim(),
|
|
||||||
routePath: id ? (getComponent(id)?.routePath ?? '') : '',
|
|
||||||
dependencies: JSON.stringify(dependencies), // Serialize dependencies to JSON string
|
|
||||||
code: code.trim(),
|
|
||||||
isActive,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEditing && id) {
|
|
||||||
updateComponent(id, componentData)
|
|
||||||
parseAndUpdateComponents(componentData.code)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error saving component:', error)
|
|
||||||
alert('Failed to save component. Please try again.')
|
|
||||||
} finally {
|
|
||||||
setIsLoaded(true)
|
|
||||||
|
|
||||||
toast.push(
|
|
||||||
<Notification type="success" duration={2000}>
|
|
||||||
Bileşen başarıyla kaydedildi.
|
|
||||||
</Notification>,
|
|
||||||
{
|
|
||||||
placement: 'bottom-end',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load existing component data - sadece edit modunda
|
|
||||||
useEffect(() => {
|
|
||||||
if (isEditing && id && !isLoaded) {
|
|
||||||
const component = getComponent(id)
|
|
||||||
if (component) {
|
|
||||||
setName(component.name)
|
|
||||||
// setDescription(component.description || "");
|
|
||||||
// Parse dependencies from JSON string
|
|
||||||
try {
|
|
||||||
const deps = component.dependencies ? JSON.parse(component.dependencies) : []
|
|
||||||
setDependencies(Array.isArray(deps) ? deps : [])
|
|
||||||
} catch {
|
|
||||||
setDependencies([])
|
|
||||||
}
|
|
||||||
setCode(component.code) // Mevcut kodu yükle
|
|
||||||
// Parse components from the loaded code
|
|
||||||
parseAndUpdateComponents(component.code)
|
|
||||||
setIsActive(component.isActive)
|
|
||||||
setIsLoaded(true)
|
|
||||||
}
|
|
||||||
} else if (!isEditing && !isLoaded) {
|
|
||||||
// Yeni komponent için boş başla - TEMPLATE YOK
|
|
||||||
setIsLoaded(true)
|
|
||||||
}
|
|
||||||
}, [id, isEditing, getComponent, isLoaded])
|
|
||||||
|
|
||||||
const handleAppDragOver = (e: React.DragEvent<HTMLDivElement>) => {
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseAndUpdateComponents = useCallback((code: string) => {
|
|
||||||
try {
|
|
||||||
const parsed = parseReactCode(code)
|
|
||||||
setEditorState((prev) => ({
|
|
||||||
...prev,
|
|
||||||
code,
|
|
||||||
components: parsed.components,
|
|
||||||
}))
|
|
||||||
} catch (error) {
|
|
||||||
const msg = error instanceof Error ? error.message : 'Koddan komponentler parse edilemedi.'
|
|
||||||
console.log('Parse error:', msg)
|
|
||||||
setEditorState((prev) => ({
|
|
||||||
...prev,
|
|
||||||
code, // Only update the code, keep existing components
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// Handle code changes from Monaco Editor
|
|
||||||
const handleCodeChange = useCallback(
|
|
||||||
(value: string | undefined) => {
|
|
||||||
if (value !== undefined) {
|
|
||||||
setCode(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[editorState.code],
|
|
||||||
)
|
|
||||||
|
|
||||||
// Apply code changes
|
|
||||||
const handleApplyCodeChanges = useCallback(() => {
|
|
||||||
parseAndUpdateComponents(code)
|
|
||||||
}, [parseAndUpdateComponents, code])
|
|
||||||
|
|
||||||
// Reset code changes
|
|
||||||
const handleResetCodeChanges = useCallback(() => {
|
|
||||||
setCode(editorState.code)
|
|
||||||
}, [editorState.code])
|
|
||||||
|
|
||||||
// Handle property changes from Property Panel
|
|
||||||
// Handle hook toggle
|
|
||||||
const handleHookToggle = useCallback(
|
|
||||||
(componentId: string, hookType: string, enabled: boolean) => {
|
|
||||||
console.log('🪝 App: handleHookToggle called:', {
|
|
||||||
componentId,
|
|
||||||
hookType,
|
|
||||||
enabled,
|
|
||||||
})
|
|
||||||
const selectedComponent = editorState.components?.find((c) => c.id === componentId)
|
|
||||||
if (!selectedComponent) return
|
|
||||||
|
|
||||||
// Use the most up-to-date code (pendingCode if available, otherwise editorState.code)
|
|
||||||
let updatedCode = code || editorState.code
|
|
||||||
console.log('🔍 App: Using code source:', code === editorState.code ? 'same' : 'pendingCode')
|
|
||||||
|
|
||||||
if (enabled) {
|
|
||||||
// Check if hook is already present - more specific check
|
|
||||||
const varName = generateHookVariableName(hookType, componentId)
|
|
||||||
console.log('🔍 App: Checking for existing hook variable:', varName)
|
|
||||||
|
|
||||||
// Create a more specific regex to check for actual hook declarations
|
|
||||||
const hookDeclarationRegex = new RegExp(
|
|
||||||
`const\\s+(?:\\[.*?${varName}.*?\\]|${varName})\\s*=\\s*${hookType}\\s*\\(`,
|
|
||||||
)
|
|
||||||
if (hookDeclarationRegex.test(updatedCode)) {
|
|
||||||
console.log('⚠️ App: Hook already exists, skipping')
|
|
||||||
return // Hook already exists
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle React imports - improved approach
|
|
||||||
const reactImportRegex = /^import\s+\{([^}]*)\}\s+from\s+['"]react['"]\s*;?/m
|
|
||||||
const reactMatch = reactImportRegex.exec(updatedCode)
|
|
||||||
|
|
||||||
if (reactMatch) {
|
|
||||||
// reactMatch[1] --> süslü parantez içi örn: "useState, useRef"
|
|
||||||
let existingHooks = reactMatch[1]
|
|
||||||
.split(',')
|
|
||||||
.map((h) => h.trim())
|
|
||||||
.filter((h) => h)
|
|
||||||
|
|
||||||
// Eğer hookType yoksa ekle
|
|
||||||
if (!existingHooks.includes(hookType)) {
|
|
||||||
existingHooks.push(hookType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tekrarları kaldır (güvenlik için)
|
|
||||||
existingHooks = [...new Set(existingHooks)]
|
|
||||||
|
|
||||||
const newImport = `import { ${existingHooks.join(', ')} } from 'react';\n`
|
|
||||||
updatedCode = updatedCode.replace(reactImportRegex, newImport)
|
|
||||||
|
|
||||||
console.log('🔄 App: Updated existing React import:', newImport.trim())
|
|
||||||
} else {
|
|
||||||
// React import satırı yoksa ekle
|
|
||||||
const importLine = `import { ${hookType} } from 'react';\n\n`
|
|
||||||
updatedCode = importLine + updatedCode
|
|
||||||
console.log('🔄 App: Added new React import:', importLine.trim())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add hook declaration
|
|
||||||
const hookCode = generateHookCode(hookType, componentId, selectedComponent.type)
|
|
||||||
console.log('🔍 App: Generated hook code:', hookCode)
|
|
||||||
|
|
||||||
// Try multiple patterns for function declaration
|
|
||||||
const functionPatterns = [
|
|
||||||
/function\s+Component\s*\([^)]*\)\s*\{/, // function Component() {
|
|
||||||
/function\s+\w+\s*\([^)]*\)\s*\{/, // function AnyName() {
|
|
||||||
/const\s+\w+\s*=\s*\([^)]*\)\s*=>\s*\{/, // const Component = () => {
|
|
||||||
/export\s+default\s+function\s*\([^)]*\)\s*\{/, // export default function() {
|
|
||||||
]
|
|
||||||
|
|
||||||
let match2 = null
|
|
||||||
for (const pattern of functionPatterns) {
|
|
||||||
match2 = updatedCode.match(pattern)
|
|
||||||
if (match2) {
|
|
||||||
console.log('🔍 App: Found function with pattern:', pattern)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('🔍 App: Function match result:', match2)
|
|
||||||
console.log('🔍 App: Updated code preview:', updatedCode.substring(0, 500))
|
|
||||||
|
|
||||||
if (match2 && typeof match2.index === 'number') {
|
|
||||||
const insertPosition = match2.index + match2[0].length
|
|
||||||
updatedCode =
|
|
||||||
updatedCode.slice(0, insertPosition) +
|
|
||||||
'\n ' +
|
|
||||||
hookCode +
|
|
||||||
'\n' +
|
|
||||||
updatedCode.slice(insertPosition)
|
|
||||||
console.log('✅ App: Hook code inserted successfully')
|
|
||||||
} else {
|
|
||||||
console.log('⚠️ App: Could not find function body to insert hook')
|
|
||||||
// Fallback: insert after the first opening brace
|
|
||||||
const firstBrace = updatedCode.indexOf('{')
|
|
||||||
if (firstBrace !== -1) {
|
|
||||||
updatedCode =
|
|
||||||
updatedCode.slice(0, firstBrace + 1) +
|
|
||||||
'\n ' +
|
|
||||||
hookCode +
|
|
||||||
'\n' +
|
|
||||||
updatedCode.slice(firstBrace + 1)
|
|
||||||
console.log('✅ App: Hook code inserted using fallback method')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update component properties if needed
|
|
||||||
if (hookType === 'useState') {
|
|
||||||
const setterName = `set${varName.charAt(0).toUpperCase() + varName.slice(1)}`
|
|
||||||
|
|
||||||
// Update component props based on type
|
|
||||||
if (selectedComponent.type === 'input') {
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'value', `{${varName}}`)
|
|
||||||
updatedCode = updateComponentProp(
|
|
||||||
updatedCode,
|
|
||||||
componentId,
|
|
||||||
'onChange',
|
|
||||||
`{(e) => ${setterName}(e.target.value)}`,
|
|
||||||
)
|
|
||||||
} else if (selectedComponent.type === 'button') {
|
|
||||||
updatedCode = updateComponentProp(
|
|
||||||
updatedCode,
|
|
||||||
componentId,
|
|
||||||
'onClick',
|
|
||||||
`{() => ${setterName}(!${varName})}`,
|
|
||||||
)
|
|
||||||
} else if (selectedComponent.type === 'checkbox') {
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'checked', `{${varName}}`)
|
|
||||||
updatedCode = updateComponentProp(
|
|
||||||
updatedCode,
|
|
||||||
componentId,
|
|
||||||
'onChange',
|
|
||||||
`{(val) => ${setterName}(val)}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else if (hookType === 'useRef') {
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'ref', `{${varName}}`)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Remove hook
|
|
||||||
updatedCode = removeHookFromCode(updatedCode, hookType, componentId)
|
|
||||||
|
|
||||||
// Remove related props
|
|
||||||
if (hookType === 'useState') {
|
|
||||||
if (selectedComponent.type === 'input') {
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'value', '')
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'onChange', null)
|
|
||||||
} else if (selectedComponent.type === 'button') {
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'onClick', null)
|
|
||||||
} else if (selectedComponent.type === 'checkbox') {
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'checked', false)
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'onChange', null)
|
|
||||||
}
|
|
||||||
} else if (hookType === 'useRef') {
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'ref', null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setEditorState((prev) => {
|
|
||||||
console.log('🔄 App: Final updatedCode before parsing:', updatedCode)
|
|
||||||
const parsed = parseReactCode(updatedCode)
|
|
||||||
console.log('🔄 App: Parsed components:', parsed.components?.length)
|
|
||||||
const newState = {
|
|
||||||
code: updatedCode,
|
|
||||||
components: parsed.components,
|
|
||||||
selectedComponentId: prev.selectedComponentId, // Preserve selection
|
|
||||||
}
|
|
||||||
console.log('🔄 App: New editor state code preview:', newState.code.substring(0, 300))
|
|
||||||
return newState
|
|
||||||
})
|
|
||||||
|
|
||||||
// Also update pending code to match
|
|
||||||
setCode(updatedCode)
|
|
||||||
},
|
|
||||||
[editorState.code, editorState.components, code],
|
|
||||||
)
|
|
||||||
|
|
||||||
// Zincirleme hook güncelleme fonksiyonu
|
|
||||||
const applyMultipleHookToggles = (
|
|
||||||
toggles: { componentId: string; hookType: string; enabled: boolean }[],
|
|
||||||
) => {
|
|
||||||
let updatedCode = code || editorState.code
|
|
||||||
// 1. Sadece kaldırılması gereken hook'ları kaldır
|
|
||||||
toggles
|
|
||||||
.filter((t) => t.enabled === false)
|
|
||||||
.forEach(({ hookType, componentId }) => {
|
|
||||||
const selectedComponent = editorState.components?.find((c) => c.id === componentId)
|
|
||||||
if (!selectedComponent) return
|
|
||||||
updatedCode = removeHookFromCode(updatedCode, hookType, componentId)
|
|
||||||
// Prop temizliği
|
|
||||||
if (hookType === 'useState') {
|
|
||||||
if (selectedComponent.type === 'input') {
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'value', '')
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'onChange', null)
|
|
||||||
} else if (selectedComponent.type === 'button') {
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'onClick', null)
|
|
||||||
} else if (selectedComponent.type === 'checkbox') {
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'checked', false)
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'onChange', null)
|
|
||||||
}
|
|
||||||
} else if (hookType === 'useRef') {
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'ref', null)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// 2. Eklenmesi gereken hook'ları ekle (veya zaten varsa dokunma)
|
|
||||||
toggles
|
|
||||||
.filter((t) => t.enabled === true)
|
|
||||||
.forEach(({ hookType, componentId }) => {
|
|
||||||
const selectedComponent = editorState.components?.find((c) => c.id === componentId)
|
|
||||||
if (!selectedComponent) return
|
|
||||||
const varName = generateHookVariableName(hookType, componentId)
|
|
||||||
// Hook kodu fonksiyon gövdesinde yoksa ekle
|
|
||||||
const hookDeclarationRegex = new RegExp(
|
|
||||||
`const\\s+(?:\\[.*?${varName}.*?\\]|${varName})\\s*=\\s*${hookType}\\s*\\(`,
|
|
||||||
)
|
|
||||||
if (!hookDeclarationRegex.test(updatedCode)) {
|
|
||||||
const reactImportRegex =
|
|
||||||
// eslint-disable-next-line no-useless-escape
|
|
||||||
/import\\s+React(?:\\s*,\\s*\\{([^}]*)\\})?\\s+from\\s+['\"]react['\"];?/
|
|
||||||
const importMatch = updatedCode.match(reactImportRegex)
|
|
||||||
const allHooks = new Set<string>()
|
|
||||||
if (importMatch && importMatch[1]) {
|
|
||||||
importMatch[1]
|
|
||||||
.split(',')
|
|
||||||
.map((h) => h.trim())
|
|
||||||
.filter((h) => h)
|
|
||||||
.forEach((h) => allHooks.add(h))
|
|
||||||
}
|
|
||||||
allHooks.add(hookType)
|
|
||||||
let newImport = ''
|
|
||||||
if (allHooks.size > 0) {
|
|
||||||
newImport = `import { ${Array.from(allHooks).join(', ')} } from 'react';\n`
|
|
||||||
}
|
|
||||||
if (importMatch) {
|
|
||||||
updatedCode = updatedCode.replace(reactImportRegex, newImport)
|
|
||||||
} else {
|
|
||||||
updatedCode = newImport + updatedCode
|
|
||||||
}
|
|
||||||
// Hook kodunu fonksiyon gövdesine ekle
|
|
||||||
const hookCode = generateHookCode(hookType, componentId, selectedComponent.type)
|
|
||||||
const functionPatterns = [
|
|
||||||
/function\s+Component\s*\([^)]*\)\s*\{/, // function Component() {
|
|
||||||
/function\s+\w+\s*\([^)]*\)\s*\{/, // function AnyName() {
|
|
||||||
/const\s+\w+\s*=\s*\([^)]*\)\s*=>\s*\{/, // const Component = () => {
|
|
||||||
/export\s+default\s+function\s*\([^)]*\)\s*\{/, // export default function() {
|
|
||||||
]
|
|
||||||
let match2 = null
|
|
||||||
for (const pattern of functionPatterns) {
|
|
||||||
match2 = updatedCode.match(pattern)
|
|
||||||
if (match2) break
|
|
||||||
}
|
|
||||||
if (match2 && typeof match2.index === 'number') {
|
|
||||||
const insertPosition = match2.index + match2[0].length
|
|
||||||
updatedCode =
|
|
||||||
updatedCode.slice(0, insertPosition) +
|
|
||||||
'\n ' +
|
|
||||||
hookCode +
|
|
||||||
'\n' +
|
|
||||||
updatedCode.slice(insertPosition)
|
|
||||||
} else {
|
|
||||||
const firstBrace = updatedCode.indexOf('{')
|
|
||||||
if (firstBrace !== -1) {
|
|
||||||
updatedCode =
|
|
||||||
updatedCode.slice(0, firstBrace + 1) +
|
|
||||||
'\n ' +
|
|
||||||
hookCode +
|
|
||||||
'\n' +
|
|
||||||
updatedCode.slice(firstBrace + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Prop güncellemeleri
|
|
||||||
if (hookType === 'useState') {
|
|
||||||
const setterName = `set${varName.charAt(0).toUpperCase() + varName.slice(1)}`
|
|
||||||
if (selectedComponent.type === 'input') {
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'value', `{${varName}}`)
|
|
||||||
updatedCode = updateComponentProp(
|
|
||||||
updatedCode,
|
|
||||||
componentId,
|
|
||||||
'onChange',
|
|
||||||
`{(e) => ${setterName}(e.target.value)}`,
|
|
||||||
)
|
|
||||||
} else if (selectedComponent.type === 'button') {
|
|
||||||
updatedCode = updateComponentProp(
|
|
||||||
updatedCode,
|
|
||||||
componentId,
|
|
||||||
'onClick',
|
|
||||||
`{() => ${setterName}(!${varName})}`,
|
|
||||||
)
|
|
||||||
} else if (selectedComponent.type === 'checkbox') {
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'checked', `{${varName}}`)
|
|
||||||
updatedCode = updateComponentProp(
|
|
||||||
updatedCode,
|
|
||||||
componentId,
|
|
||||||
'onChange',
|
|
||||||
`{(e) => ${setterName}(e.target.checked)}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else if (hookType === 'useRef') {
|
|
||||||
updatedCode = updateComponentProp(updatedCode, componentId, 'ref', `{${varName}}`)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
setEditorState((prev) => {
|
|
||||||
const parsed = parseReactCode(updatedCode)
|
|
||||||
return {
|
|
||||||
code: updatedCode,
|
|
||||||
components: parsed.components,
|
|
||||||
selectedComponentId: prev.selectedComponentId,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
setCode(updatedCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle multiple property changes at once
|
|
||||||
const handlePropertiesChange = useCallback(
|
|
||||||
(componentId: string, updates: Record<string, any>) => {
|
|
||||||
console.log('🔄 App: handlePropertiesChange called:', {
|
|
||||||
componentId,
|
|
||||||
updates,
|
|
||||||
})
|
|
||||||
|
|
||||||
const updatedCode = updateComponentProps(editorState.code, componentId, updates)
|
|
||||||
console.log('📝 App: Properties updated, code changed:', editorState.code !== updatedCode)
|
|
||||||
|
|
||||||
setEditorState((prev) => {
|
|
||||||
const parsed = parseReactCode(updatedCode)
|
|
||||||
return {
|
|
||||||
code: updatedCode,
|
|
||||||
components: parsed.components,
|
|
||||||
selectedComponentId: prev.selectedComponentId, // Preserve selection
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Update pending code to reflect changes
|
|
||||||
setCode(updatedCode)
|
|
||||||
},
|
|
||||||
[editorState.code],
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handle component list refresh
|
|
||||||
const handleRefreshComponents = useCallback(() => {
|
|
||||||
parseAndUpdateComponents(editorState.code)
|
|
||||||
}, [parseAndUpdateComponents, editorState.code])
|
|
||||||
|
|
||||||
// Handle component selection
|
|
||||||
const handleSelectComponent = useCallback((componentId: string | null) => {
|
|
||||||
setEditorState((prev) => ({
|
|
||||||
...prev,
|
|
||||||
selectedComponentId: componentId,
|
|
||||||
}))
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// Auto-parse ComponentSelector whenever code changes (debounced for live refresh)
|
|
||||||
useEffect(() => {
|
|
||||||
if (parseDebounceRef.current) {
|
|
||||||
clearTimeout(parseDebounceRef.current)
|
|
||||||
}
|
|
||||||
parseDebounceRef.current = setTimeout(() => {
|
|
||||||
parseAndUpdateComponents(code)
|
|
||||||
}, 500)
|
|
||||||
return () => {
|
|
||||||
if (parseDebounceRef.current) {
|
|
||||||
clearTimeout(parseDebounceRef.current)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [code, parseAndUpdateComponents])
|
|
||||||
|
|
||||||
const handleDragStart = (_componentDef: ComponentDefinition, e: React.DragEvent) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectedComponent =
|
|
||||||
editorState.components?.find((c) => c.id === editorState.selectedComponentId) || null
|
|
||||||
|
|
||||||
const renderLeftPanel = () => {
|
|
||||||
if (!panelState.toolbox) return null
|
|
||||||
return <ComponentLibrary onDragStart={handleDragStart} />
|
|
||||||
}
|
|
||||||
|
|
||||||
// Komponent ve ilgili hook'ları koddan silen fonksiyon
|
|
||||||
const handleDeleteComponent = (componentId: string) => {
|
|
||||||
// Koddan JSX ve hook'ları sil
|
|
||||||
const updatedCode = removeComponentAndHooksFromCode(code, componentId)
|
|
||||||
// Koddan parse edip state'i güncelle
|
|
||||||
parseAndUpdateComponents(updatedCode)
|
|
||||||
setCode(updatedCode)
|
|
||||||
// Seçili komponenti kaldır
|
|
||||||
setEditorState((prev) => ({
|
|
||||||
...prev,
|
|
||||||
selectedComponentId: null,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderRightPanel = () => {
|
|
||||||
if (!panelState.properties) return null
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col flex-1 min-h-0 h-full w-full">
|
|
||||||
<ComponentSelector
|
|
||||||
components={editorState.components}
|
|
||||||
selectedComponentId={editorState.selectedComponentId}
|
|
||||||
onSelectComponent={handleSelectComponent}
|
|
||||||
onRefresh={handleRefreshComponents}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PropertyPanel
|
|
||||||
selectedComponent={selectedComponent}
|
|
||||||
currentCode={editorState.code}
|
|
||||||
onPropertiesChange={handlePropertiesChange}
|
|
||||||
onHookToggle={handleHookToggle}
|
|
||||||
onMultipleHookToggle={applyMultipleHookToggles}
|
|
||||||
onDeleteComponent={handleDeleteComponent}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const mainContent = (
|
|
||||||
<div className="flex-1 flex flex-col min-h-0 h-full">
|
|
||||||
{/* Top Header */}
|
|
||||||
<div className="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700 px-3 py-3">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex items-center space-x-4">
|
|
||||||
<h1 className="text-lg font-semibold text-gray-900 dark:text-gray-100">{name}</h1>
|
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400">{dependencies.join(', ')}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
variant="default"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => setShowPanelManager(true)}
|
|
||||||
className="!inline-flex items-center gap-2 rounded-lg border border-gray-300 !bg-white transition-colors hover:!bg-gray-50 dark:border-gray-700 dark:!bg-gray-900 dark:hover:!bg-gray-800"
|
|
||||||
title="Panel Manager"
|
|
||||||
>
|
|
||||||
<FaThLarge className="text-gray-600 dark:text-gray-300" />
|
|
||||||
<span className="text-sm text-gray-600 dark:text-gray-300">Panels</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col flex-1 min-h-0 h-full">
|
|
||||||
<div className="flex-1 h-full min-h-0">
|
|
||||||
<ComponentCodeEditor
|
|
||||||
code={code}
|
|
||||||
onChange={handleCodeChange}
|
|
||||||
onApplyCodeChanges={handleApplyCodeChanges}
|
|
||||||
onResetCodeChanges={handleResetCodeChanges}
|
|
||||||
onComponentSave={handleSave}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="flex h-screen bg-gray-50 dark:bg-gray-950"
|
|
||||||
onDragOver={handleAppDragOver}
|
|
||||||
onDrop={(e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* Panel Yöneticisi Modal */}
|
|
||||||
{showPanelManager && (
|
|
||||||
<PanelManager
|
|
||||||
isOpen={showPanelManager}
|
|
||||||
onClose={() => setShowPanelManager(false)}
|
|
||||||
panelState={panelState}
|
|
||||||
onPanelToggle={(panel) => setPanelState((prev) => ({ ...prev, [panel]: !prev[panel] }))}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{/* Sol Sidebar ve ana içerik */}
|
|
||||||
{panelState.toolbox ? (
|
|
||||||
<Splitter direction="horizontal" initialSize={288} minSize={200} maxSize={400}>
|
|
||||||
{renderLeftPanel()}
|
|
||||||
<div className="flex flex-1">
|
|
||||||
{panelState.properties ? (
|
|
||||||
<Splitter
|
|
||||||
direction="horizontal"
|
|
||||||
initialSize={400}
|
|
||||||
minSize={300}
|
|
||||||
maxSize={window.innerWidth - 288 - 100}
|
|
||||||
reverse={true}
|
|
||||||
>
|
|
||||||
{mainContent}
|
|
||||||
{renderRightPanel()}
|
|
||||||
</Splitter>
|
|
||||||
) : (
|
|
||||||
mainContent
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Splitter>
|
|
||||||
) : (
|
|
||||||
<div className="flex flex-1">
|
|
||||||
{panelState.properties ? (
|
|
||||||
<Splitter
|
|
||||||
direction="horizontal"
|
|
||||||
initialSize={400}
|
|
||||||
minSize={300}
|
|
||||||
maxSize={window.innerWidth - 100}
|
|
||||||
reverse={true}
|
|
||||||
>
|
|
||||||
{mainContent}
|
|
||||||
{renderRightPanel()}
|
|
||||||
</Splitter>
|
|
||||||
) : (
|
|
||||||
mainContent
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ComponentCodeLayout
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,12 @@ import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||||
import { Formik, Form, Field } from 'formik'
|
import { Formik, Form, Field } from 'formik'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
import { Button, Checkbox, FormContainer, FormItem, Input } from '@/components/ui'
|
import { Button, Checkbox, FormContainer, FormItem, Input } from '@/components/ui'
|
||||||
|
import { generateDesignerCode } from '@/components/visualDesigner/codeGenerator'
|
||||||
|
import {
|
||||||
|
createDesignerId,
|
||||||
|
createEmptyDesignerDocument,
|
||||||
|
type DesignerDocument,
|
||||||
|
} from '@/components/visualDesigner/types'
|
||||||
|
|
||||||
// Validation schema
|
// Validation schema
|
||||||
const validationSchema = Yup.object({
|
const validationSchema = Yup.object({
|
||||||
|
|
@ -15,7 +21,6 @@ const validationSchema = Yup.object({
|
||||||
routePath: Yup.string().required().matches(/^\//, 'Route path must start with /'),
|
routePath: Yup.string().required().matches(/^\//, 'Route path must start with /'),
|
||||||
description: Yup.string(),
|
description: Yup.string(),
|
||||||
dependencies: Yup.array().of(Yup.string()),
|
dependencies: Yup.array().of(Yup.string()),
|
||||||
code: Yup.string(),
|
|
||||||
isActive: Yup.boolean(),
|
isActive: Yup.boolean(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -36,7 +41,6 @@ const ComponentEditor: React.FC = () => {
|
||||||
routePath: '',
|
routePath: '',
|
||||||
description: '',
|
description: '',
|
||||||
dependencies: [] as string[],
|
dependencies: [] as string[],
|
||||||
code: '',
|
|
||||||
isActive: true,
|
isActive: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -59,7 +63,6 @@ const ComponentEditor: React.FC = () => {
|
||||||
routePath: component.routePath,
|
routePath: component.routePath,
|
||||||
description: component.description || '',
|
description: component.description || '',
|
||||||
dependencies: deps,
|
dependencies: deps,
|
||||||
code: component.code,
|
|
||||||
isActive: component.isActive,
|
isActive: component.isActive,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,22 +75,20 @@ const ComponentEditor: React.FC = () => {
|
||||||
}
|
}
|
||||||
}, [id, isEditing, getComponent, isLoaded])
|
}, [id, isEditing, getComponent, isLoaded])
|
||||||
|
|
||||||
// Generate component template based on name
|
const createDefaultVisualDocument = (componentName: string): DesignerDocument => {
|
||||||
const generateComponentTemplate = (componentName: string): string => {
|
const document = createEmptyDesignerDocument('visual')
|
||||||
if (!componentName.trim()) return ''
|
document.nodes = [
|
||||||
|
{
|
||||||
// Convert component name to PascalCase if not already
|
id: createDesignerId(),
|
||||||
const pascalCaseName = componentName.charAt(0).toUpperCase() + componentName.slice(1)
|
type: 'span',
|
||||||
|
kind: 'html',
|
||||||
return `const ${pascalCaseName}Component = ({
|
props: { children: componentName.trim() },
|
||||||
title = "${pascalCaseName}"
|
events: {},
|
||||||
}) => {
|
bindings: {},
|
||||||
return (
|
children: [],
|
||||||
<span>{title}</span>
|
},
|
||||||
);
|
]
|
||||||
};
|
return document
|
||||||
|
|
||||||
export default ${pascalCaseName}Component;`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmit = async (values: typeof initialValues, { setSubmitting }: any) => {
|
const handleSubmit = async (values: typeof initialValues, { setSubmitting }: any) => {
|
||||||
|
|
@ -98,12 +99,19 @@ export default ${pascalCaseName}Component;`
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const existingComponent = isEditing && id ? getComponent(id) : undefined
|
||||||
|
const defaultDocument = createDefaultVisualDocument(values.name)
|
||||||
const componentData = {
|
const componentData = {
|
||||||
name: values.name.trim(),
|
name: values.name.trim(),
|
||||||
routePath: values.routePath.trim(),
|
routePath: values.routePath.trim(),
|
||||||
description: values.description.trim(),
|
description: values.description.trim(),
|
||||||
dependencies: JSON.stringify(values.dependencies), // Serialize dependencies to JSON string
|
dependencies: JSON.stringify(values.dependencies), // Serialize dependencies to JSON string
|
||||||
code: values.code.trim(),
|
code: existingComponent
|
||||||
|
? existingComponent.code
|
||||||
|
: generateDesignerCode(values.name, defaultDocument),
|
||||||
|
props: existingComponent
|
||||||
|
? existingComponent.props
|
||||||
|
: JSON.stringify({ visualDesigner: defaultDocument }),
|
||||||
isActive: values.isActive,
|
isActive: values.isActive,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,9 +196,7 @@ export default ${pascalCaseName}Component;`
|
||||||
onClick={submitForm}
|
onClick={submitForm}
|
||||||
>
|
>
|
||||||
<FaRegSave className="w-4 h-4" />
|
<FaRegSave className="w-4 h-4" />
|
||||||
{isSubmitting
|
{isSubmitting ? translate('::Saving') : translate('::Save')}
|
||||||
? translate('::Saving')
|
|
||||||
: translate('::Save')}
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -222,24 +228,7 @@ export default ${pascalCaseName}Component;`
|
||||||
component={Input}
|
component={Input}
|
||||||
placeholder="e.g., Button, Card, Modal"
|
placeholder="e.g., Button, Card, Modal"
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const newName = e.target.value
|
setFieldValue('name', e.target.value)
|
||||||
setFieldValue('name', newName)
|
|
||||||
|
|
||||||
// Auto-generate code template if this is a new component (not editing)
|
|
||||||
// and only if the code field is empty or contains the default template
|
|
||||||
if (!isEditing && newName.trim()) {
|
|
||||||
const currentCode = values.code.trim()
|
|
||||||
const isCodeEmpty = !currentCode
|
|
||||||
const isCodeDefaultTemplate =
|
|
||||||
currentCode.includes('Component = ({') &&
|
|
||||||
currentCode.includes('export default') &&
|
|
||||||
currentCode.includes('<span>{title}</span>')
|
|
||||||
|
|
||||||
if (isCodeEmpty || isCodeDefaultTemplate) {
|
|
||||||
const template = generateComponentTemplate(newName)
|
|
||||||
setFieldValue('code', template)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
@ -270,24 +259,6 @@ export default ${pascalCaseName}Component;`
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
<FormItem
|
|
||||||
label={translate('::App.Platform.Code')}
|
|
||||||
invalid={!!(errors.code && touched.code)}
|
|
||||||
errorMessage={errors.code as string}
|
|
||||||
>
|
|
||||||
<Field
|
|
||||||
name="code"
|
|
||||||
type="text"
|
|
||||||
component={Input}
|
|
||||||
placeholder="React component code goes here"
|
|
||||||
textArea={true}
|
|
||||||
rows={10}
|
|
||||||
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
||||||
setFieldValue('code', e.target.value)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
label={translate('::App.DeveloperKit.ComponentEditor.Dependencies')}
|
label={translate('::App.DeveloperKit.ComponentEditor.Dependencies')}
|
||||||
invalid={!!(errors.dependencies && touched.dependencies)}
|
invalid={!!(errors.dependencies && touched.dependencies)}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { Link, useNavigate } from 'react-router-dom'
|
import { Link, useNavigate } from 'react-router-dom'
|
||||||
import { useComponents } from '../../contexts/ComponentContext'
|
import { useComponents } from '../../contexts/ComponentContext'
|
||||||
import {
|
import {
|
||||||
|
|
@ -10,7 +10,8 @@ import {
|
||||||
FaEyeSlash,
|
FaEyeSlash,
|
||||||
FaFilter,
|
FaFilter,
|
||||||
FaCalendarAlt,
|
FaCalendarAlt,
|
||||||
FaExternalLinkAlt,
|
FaRegWindowMaximize,
|
||||||
|
FaSyncAlt,
|
||||||
} from 'react-icons/fa'
|
} from 'react-icons/fa'
|
||||||
import Widget from '@/components/common/Widget'
|
import Widget from '@/components/common/Widget'
|
||||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||||
|
|
@ -21,10 +22,12 @@ import { Helmet } from 'react-helmet'
|
||||||
import { Button } from '@/components/ui'
|
import { Button } from '@/components/ui'
|
||||||
|
|
||||||
const ComponentManager: React.FC = () => {
|
const ComponentManager: React.FC = () => {
|
||||||
const { components, loading, updateComponent, deleteComponent } = useComponents()
|
const { components, loading, updateComponent, deleteComponent, refreshComponents } =
|
||||||
|
useComponents()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const [searchTerm, setSearchTerm] = useState('')
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [filterActive, setFilterActive] = useState<'all' | 'active' | 'inactive'>('all')
|
const [filterActive, setFilterActive] = useState<'all' | 'active' | 'inactive'>('all')
|
||||||
|
const [isRefreshing, setIsRefreshing] = useState(false)
|
||||||
|
|
||||||
// Calculate statistics
|
// Calculate statistics
|
||||||
const totalComponents = components?.length || 0
|
const totalComponents = components?.length || 0
|
||||||
|
|
@ -32,6 +35,19 @@ const ComponentManager: React.FC = () => {
|
||||||
const inactiveComponents = totalComponents - activeComponents
|
const inactiveComponents = totalComponents - activeComponents
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
void refreshComponents()
|
||||||
|
}, [refreshComponents])
|
||||||
|
|
||||||
|
const handleRefresh = async () => {
|
||||||
|
setIsRefreshing(true)
|
||||||
|
try {
|
||||||
|
await refreshComponents()
|
||||||
|
} finally {
|
||||||
|
setIsRefreshing(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const filteredComponents = components?.filter((component) => {
|
const filteredComponents = components?.filter((component) => {
|
||||||
const matchesSearch =
|
const matchesSearch =
|
||||||
component.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
component.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
|
|
@ -68,7 +84,7 @@ const ComponentManager: React.FC = () => {
|
||||||
|
|
||||||
const handleOpenRoute = (routePath: string) => {
|
const handleOpenRoute = (routePath: string) => {
|
||||||
const normalizedPath = routePath.startsWith('/') ? routePath : `/${routePath}`
|
const normalizedPath = routePath.startsWith('/') ? routePath : `/${routePath}`
|
||||||
navigate(normalizedPath)
|
window.open(normalizedPath, '_blank', 'noopener,noreferrer')
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -114,16 +130,16 @@ const ComponentManager: React.FC = () => {
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={translate('::App.DeveloperKit.Component.SearchPlaceholder')}
|
placeholder={translate('::App.DeveloperKit.Component.SearchPlaceholder')}
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
|
||||||
className="w-full pl-10 pr-2 py-1 border border-slate-300 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
className="w-full pl-10 pr-2 py-1 border border-slate-300 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<FaFilter className="w-5 h-5 text-slate-500 dark:text-gray-400" />
|
<FaFilter className="w-5 h-5 text-slate-500 dark:text-gray-400" />
|
||||||
<select
|
<select
|
||||||
value={filterActive}
|
value={filterActive}
|
||||||
onChange={(e) => setFilterActive(e.target.value as 'all' | 'active' | 'inactive')}
|
|
||||||
className="px-2 py-1 border border-slate-300 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
className="px-2 py-1 border border-slate-300 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
onChange={(e) => setFilterActive(e.target.value as 'all' | 'active' | 'inactive')}
|
||||||
>
|
>
|
||||||
<option value="all">{translate('::App.DeveloperKit.Component.Filter.All')}</option>
|
<option value="all">{translate('::App.DeveloperKit.Component.Filter.All')}</option>
|
||||||
<option value="active">
|
<option value="active">
|
||||||
|
|
@ -134,6 +150,16 @@ const ComponentManager: React.FC = () => {
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<Button
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
disabled={loading || isRefreshing}
|
||||||
|
type="button"
|
||||||
|
variant="default"
|
||||||
|
onClick={() => void handleRefresh()}
|
||||||
|
>
|
||||||
|
<FaSyncAlt className={isRefreshing ? 'animate-spin' : ''} />
|
||||||
|
Yenile
|
||||||
|
</Button>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -211,18 +237,6 @@ const ComponentManager: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Props Preview */}
|
|
||||||
{component.props && (
|
|
||||||
<div className="mb-4">
|
|
||||||
<p className="text-xs font-medium text-slate-700 dark:text-gray-400 mb-1">
|
|
||||||
{translate('::App.DeveloperKit.Component.PropsLabel')}
|
|
||||||
</p>
|
|
||||||
<code className="text-xs bg-slate-100 dark:bg-gray-800 text-slate-600 dark:text-gray-300 px-2 py-1 rounded">
|
|
||||||
{component.props}
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Actions */}
|
{/* Actions */}
|
||||||
<div className="flex items-center justify-between pt-2 border-t border-slate-100 dark:border-gray-700">
|
<div className="flex items-center justify-between pt-2 border-t border-slate-100 dark:border-gray-700">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|
@ -230,8 +244,8 @@ const ComponentManager: React.FC = () => {
|
||||||
type="button"
|
type="button"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
shape="circle"
|
shape="circle"
|
||||||
onClick={() => handleToggleActive(component.id, !component.isActive)}
|
|
||||||
className={`!inline-flex !h-auto items-center gap-1 rounded !px-2 py-1 text-xs font-medium transition-colors`}
|
className={`!inline-flex !h-auto items-center gap-1 rounded !px-2 py-1 text-xs font-medium transition-colors`}
|
||||||
|
onClick={() => handleToggleActive(component.id, !component.isActive)}
|
||||||
>
|
>
|
||||||
{component.isActive ? (
|
{component.isActive ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -250,19 +264,8 @@ const ComponentManager: React.FC = () => {
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
shape="circle"
|
|
||||||
color="gray-600"
|
|
||||||
disabled={!component.isActive || !component.routePath?.trim()}
|
|
||||||
title={translate('::OpenUrl')}
|
|
||||||
onClick={() => handleOpenRoute(component.routePath)}
|
|
||||||
>
|
|
||||||
<FaExternalLinkAlt className="w-3 h-3" />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="solid"
|
|
||||||
shape="circle"
|
|
||||||
color="blue-600"
|
color="blue-600"
|
||||||
|
title={translate('::Edit')}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
window.open(
|
window.open(
|
||||||
ROUTES_ENUM.protected.saas.developerKit.componentsEdit.replace(
|
ROUTES_ENUM.protected.saas.developerKit.componentsEdit.replace(
|
||||||
|
|
@ -272,15 +275,24 @@ const ComponentManager: React.FC = () => {
|
||||||
'_blank',
|
'_blank',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
title={translate('::Edit')}
|
|
||||||
>
|
>
|
||||||
<FaRegEdit className="w-3 h-3" />
|
<FaRegEdit className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="solid"
|
||||||
|
color="gray-600"
|
||||||
|
disabled={!component.isActive || !component.routePath?.trim()}
|
||||||
|
title={translate('::OpenUrl')}
|
||||||
|
onClick={() => handleOpenRoute(component.routePath)}
|
||||||
|
>
|
||||||
|
<FaRegWindowMaximize className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
shape="circle"
|
|
||||||
color="green-600"
|
color="green-600"
|
||||||
|
title={translate('::App.Platform.View')}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
navigate(
|
navigate(
|
||||||
ROUTES_ENUM.protected.saas.developerKit.componentsView.replace(
|
ROUTES_ENUM.protected.saas.developerKit.componentsView.replace(
|
||||||
|
|
@ -289,19 +301,17 @@ const ComponentManager: React.FC = () => {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
title={translate('::App.Platform.View')}
|
|
||||||
>
|
>
|
||||||
<FaEye className="w-3 h-3" />
|
<FaEye className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
shape="circle"
|
|
||||||
color="red-600"
|
color="red-600"
|
||||||
onClick={() => handleDelete(component.id)}
|
|
||||||
title={translate('::Delete')}
|
title={translate('::Delete')}
|
||||||
|
onClick={() => handleDelete(component.id)}
|
||||||
>
|
>
|
||||||
<FaTrashAlt className="w-3 h-3" />
|
<FaTrashAlt className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
3423
ui/src/views/developerKit/VisualComponentDesigner.tsx
Normal file
3423
ui/src/views/developerKit/VisualComponentDesigner.tsx
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -37,10 +37,20 @@ import { useFilters } from './useFilters'
|
||||||
import GridFilterDialogs from './GridFilterDialogs'
|
import GridFilterDialogs from './GridFilterDialogs'
|
||||||
|
|
||||||
type GanttInstance = ReturnType<GanttRef['instance']>
|
type GanttInstance = ReturnType<GanttRef['instance']>
|
||||||
|
type GanttTreeListInstance = {
|
||||||
|
element: () => HTMLElement
|
||||||
|
getVisibleColumns: () => Array<Record<string, any>>
|
||||||
|
state: {
|
||||||
|
(): Record<string, any>
|
||||||
|
(value: Record<string, any> | null): void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const getGanttTreeList = (gantt: GanttInstance) => {
|
const getGanttTreeList = (gantt: GanttInstance) => {
|
||||||
const element = (gantt.element() as HTMLElement).querySelector('.dx-treelist')
|
const element = (gantt.element() as HTMLElement).querySelector('.dx-treelist')
|
||||||
return element ? TreeList.getInstance(element as HTMLElement) : undefined
|
return element
|
||||||
|
? (TreeList.getInstance(element as HTMLElement) as unknown as GanttTreeListInstance)
|
||||||
|
: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GanttViewProps {
|
interface GanttViewProps {
|
||||||
|
|
@ -161,7 +171,7 @@ const GanttView = (props: GanttViewProps) => {
|
||||||
)
|
)
|
||||||
|
|
||||||
setGanttDataSource(dataSource)
|
setGanttDataSource(dataSource)
|
||||||
}, [gridDto, searchParams, createSelectDataSource])
|
}, [createSelectDataSource, getBandedColumns, gridDto, layout, listFormCode, searchParams])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refListFormCode.current = listFormCode
|
refListFormCode.current = listFormCode
|
||||||
|
|
@ -193,7 +203,7 @@ const GanttView = (props: GanttViewProps) => {
|
||||||
ROUTES_ENUM.protected.saas.listFormManagement.edit.replace(':listFormCode', listFormCode),
|
ROUTES_ENUM.protected.saas.listFormManagement.edit.replace(':listFormCode', listFormCode),
|
||||||
isPwaMode ? '_self' : '_blank',
|
isPwaMode ? '_self' : '_blank',
|
||||||
)
|
)
|
||||||
}, [])
|
}, [isPwaMode, listFormCode])
|
||||||
|
|
||||||
const getSettingButtonOptions = useCallback(
|
const getSettingButtonOptions = useCallback(
|
||||||
() => ({
|
() => ({
|
||||||
|
|
@ -217,7 +227,7 @@ const GanttView = (props: GanttViewProps) => {
|
||||||
gridRef.current?.instance()?.refresh()
|
gridRef.current?.instance()?.refresh()
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[settingButtonClick],
|
[translate],
|
||||||
)
|
)
|
||||||
|
|
||||||
const getCurrentState = useCallback((gantt: GanttInstance) => {
|
const getCurrentState = useCallback((gantt: GanttInstance) => {
|
||||||
|
|
@ -248,7 +258,7 @@ const GanttView = (props: GanttViewProps) => {
|
||||||
showDependencies: gantt.option('showDependencies'),
|
showDependencies: gantt.option('showDependencies'),
|
||||||
showResources: gantt.option('showResources'),
|
showResources: gantt.option('showResources'),
|
||||||
taskListWidth: Math.round(
|
taskListWidth: Math.round(
|
||||||
taskListElement?.getBoundingClientRect().width ?? gantt.option('taskListWidth'),
|
taskListElement?.getBoundingClientRect().width ?? gantt.option('taskListWidth') ?? 500,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
|
||||||
|
|
@ -314,7 +314,7 @@ const Grid = (props: GridProps) => {
|
||||||
|
|
||||||
const { customSaveState, customLoadState } = useListFormStateStoring({
|
const { customSaveState, customLoadState } = useListFormStateStoring({
|
||||||
listFormCode,
|
listFormCode,
|
||||||
storageKey: gridDto?.gridOptions.stateStoringDto?.storageKey,
|
storageKey: gridDto?.gridOptions?.stateStoringDto?.storageKey,
|
||||||
filterPrefix: 'list',
|
filterPrefix: 'list',
|
||||||
skipSaveRef: isEditingRef,
|
skipSaveRef: isEditingRef,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,19 @@ function isLayoutValid(dto: GridDto, layout: ListViewLayoutType | undefined): bo
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const List: React.FC = () => {
|
interface ListProps {
|
||||||
const { listFormCode = '' } = useParams()
|
listFormCode?: string
|
||||||
|
gridDto?: GridDto
|
||||||
|
onRefreshGridDto?: () => Promise<void> | void
|
||||||
|
}
|
||||||
|
|
||||||
|
const List: React.FC<ListProps> = ({
|
||||||
|
listFormCode: externalListFormCode,
|
||||||
|
gridDto: externalGridDto,
|
||||||
|
onRefreshGridDto,
|
||||||
|
}) => {
|
||||||
|
const { listFormCode: routeListFormCode = '' } = useParams()
|
||||||
|
const listFormCode = externalListFormCode || routeListFormCode
|
||||||
const [searchParams] = useSearchParams()
|
const [searchParams] = useSearchParams()
|
||||||
|
|
||||||
const { translate } = useLocalization()
|
const { translate } = useLocalization()
|
||||||
|
|
@ -99,7 +110,9 @@ const List: React.FC = () => {
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
gridDto: GridDto | null
|
gridDto: GridDto | null
|
||||||
}>({ listFormCode: '', gridDto: null })
|
}>({ listFormCode: '', gridDto: null })
|
||||||
const gridDto = gridDtoState.listFormCode === listFormCode ? gridDtoState.gridDto : null
|
const gridDto =
|
||||||
|
externalGridDto ??
|
||||||
|
(gridDtoState.listFormCode === listFormCode ? gridDtoState.gridDto : null)
|
||||||
|
|
||||||
const [viewModeState, setViewModeState] = useState<{
|
const [viewModeState, setViewModeState] = useState<{
|
||||||
listFormCode: string
|
listFormCode: string
|
||||||
|
|
@ -112,6 +125,12 @@ const List: React.FC = () => {
|
||||||
======================= */
|
======================= */
|
||||||
const refreshGridDto = useCallback(async () => {
|
const refreshGridDto = useCallback(async () => {
|
||||||
if (!listFormCode) return
|
if (!listFormCode) return
|
||||||
|
|
||||||
|
if (onRefreshGridDto) {
|
||||||
|
await onRefreshGridDto()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const requestedListFormCode = listFormCode
|
const requestedListFormCode = listFormCode
|
||||||
try {
|
try {
|
||||||
const response = await getList({ listFormCode: requestedListFormCode })
|
const response = await getList({ listFormCode: requestedListFormCode })
|
||||||
|
|
@ -120,11 +139,11 @@ const List: React.FC = () => {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('GridDto load error:', err)
|
console.error('GridDto load error:', err)
|
||||||
}
|
}
|
||||||
}, [listFormCode])
|
}, [listFormCode, onRefreshGridDto])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refreshGridDto()
|
if (!externalGridDto) void refreshGridDto()
|
||||||
}, [refreshGridDto])
|
}, [externalGridDto, refreshGridDto])
|
||||||
|
|
||||||
/* =======================
|
/* =======================
|
||||||
🔹 INITIAL VIEW MODE
|
🔹 INITIAL VIEW MODE
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue