From 671372bfe80ff0b2a88996ebaecdc791ea0e80d9 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Thu, 20 Aug 2026 15:52:17 +0330 Subject: [PATCH] last ... --- .gitignore | 1 + .gitmodules | 3 + ...ashboard-xEventService-20260819_212912.txt | 2586 ++ ...ashboard-xEventService-20260819_221910.txt | 2586 ++ ...ashboard-xEventService-20260819_222228.txt | 2586 ++ ...ashboard-xEventService-20260819_223556.txt | 2610 ++ ...shboard-xFrameworkTest-20260819_225137.txt | 21434 ++++++++++++++++ Modules/xCommons | 2 +- Modules/xEventService | 2 +- Modules/xFrameworkComponentsHolder | 2 +- xClient | 2 +- xFrameworkTest | 1 + 12 files changed, 31811 insertions(+), 4 deletions(-) create mode 100644 Documents/GeneratedContext/xDashboard-xEventService-20260819_212912.txt create mode 100644 Documents/GeneratedContext/xDashboard-xEventService-20260819_221910.txt create mode 100644 Documents/GeneratedContext/xDashboard-xEventService-20260819_222228.txt create mode 100644 Documents/GeneratedContext/xDashboard-xEventService-20260819_223556.txt create mode 100644 Documents/GeneratedContext/xDashboard-xFrameworkTest-20260819_225137.txt create mode 160000 xFrameworkTest diff --git a/.gitignore b/.gitignore index d12d932..35701ef 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ xApi/* xIds/* xAiApi/* xClient/* +xFrameworkTest/* # Modules/* diff --git a/.gitmodules b/.gitmodules index 43d8df9..893c757 100644 --- a/.gitmodules +++ b/.gitmodules @@ -105,4 +105,7 @@ [submodule "xApi"] path = xApi url = https://git.saherelm.ir/saherelm/xSaherElmApi.git +[submodule "xFrameworkTest"] + path = xFrameworkTest + url = https://git.saherelm.ir/saherelm/xFrameworkTest.git diff --git a/Documents/GeneratedContext/xDashboard-xEventService-20260819_212912.txt b/Documents/GeneratedContext/xDashboard-xEventService-20260819_212912.txt new file mode 100644 index 0000000..6efbde2 --- /dev/null +++ b/Documents/GeneratedContext/xDashboard-xEventService-20260819_212912.txt @@ -0,0 +1,2586 @@ + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Configuration\XEventServiceConfiguration.cs + +using xEventService.Constants; +using xEventService.Models; + +namespace xEventService.Configuration +{ + /// + /// a configuration class for describing how to connect rabbit mq server + /// and how channels must be declared ... + /// + public class XEventServiceConfiguration + { + /// + /// Message Broker Address ... + /// + public string Url { get; set; } + + /// + /// Max Retries to Sending Message ... + /// + public int MaxRetries { get; set; } = 3; + + /// + /// Allowed Message Broker ... + /// + public XEventBroker Broker { get; set; } = XEventBroker.None; + + /// + /// ZeroMQ Specific Options ... + /// Only used when Broker is XZeroMQ ... + /// + public XZeroMQOptions ZeroMQ { get; set; } = new XZeroMQOptions(); + + /// + /// RabbitMQ Specific Options ... + /// Only used when Broker is XRabbitMQ ... + /// + public XRabbitMQOptions RabbitMQ { get; set; } = new XRabbitMQOptions(); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\ConfigurationNodeNames.cs + +namespace xEventService.Constants +{ + public partial struct ConfigurationNodeNames { + public const string EVENT_SERVICE_NODE_NAME = "EventServiceConfiguration"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\XEventBroker.cs + +using xExceptions.Attributes; + +namespace xEventService.Constants +{ + /// + /// Available Brokers ... + /// + public enum XEventBroker + { + [StringValue(XEventServiceConstants.None)] + None, + + [StringValue(XEventServiceConstants.XKafka)] + XKafka, + + [StringValue(XEventServiceConstants.XZeroMQ)] + XZeroMQ, + + [StringValue(XEventServiceConstants.XRabbitMQ)] + XRabbitMQ, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\XEventServiceConstants.cs + +namespace xEventService.Constants +{ + public struct XEventServiceConstants + { + // + // Service Extensions Log Tag ... + public const string XEventServiceDILogTag = "XEventService"; + + // + // Defaults ... + public const string XDefaultTopic = "XSaherelm"; + public const string XDefaultConsumerGroup = "XSaherElmGroup"; + + // + public const string None = "None"; + public const string XKafka = "XKafka"; + public const string PubSub = "PubSub"; + public const string XZeroMQ = "XZeroMQ"; + public const string PushPull = "PushPull"; + public const string XRabbitMQ = "XRabbitMQ"; + + // + public const string XWildcard = "*"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\XRabbitMQExchangeType.cs + +using xExceptions.Attributes; + +namespace xEventService.Constants +{ + /// + /// Available RabbitMQ Exchange Types ... + /// + public struct XRabbitMQExchangeTypes + { + public const string None = "none"; + public const string Direct = "direct"; + public const string Fanout = "fanout"; + public const string Topic = "topic"; + public const string Headers = "headers"; + } + + /// + /// RabbitMQ Exchange Type Enum ... + /// + public enum XRabbitMQExchangeType + { + [StringValue(XRabbitMQExchangeTypes.None)] + None, + + [StringValue(XRabbitMQExchangeTypes.Direct)] + Direct, + + [StringValue(XRabbitMQExchangeTypes.Fanout)] + Fanout, + + [StringValue(XRabbitMQExchangeTypes.Topic)] + Topic, + + [StringValue(XRabbitMQExchangeTypes.Headers)] + Headers, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\XZeroMQPattern.cs + +using xExceptions.Attributes; + +namespace xEventService.Constants +{ + public enum XZeroMQPattern + { + [StringValue(XEventServiceConstants.None)] + None, + + [StringValue(XEventServiceConstants.PubSub)] + PubSub, + + [StringValue(XEventServiceConstants.PushPull)] + PushPull + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\DI\XDIHelperExtension.cs + +using System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Providers; +using xExceptions.Constants; + +namespace xEventService.DI +{ + public static partial class XDIHelperExtension + { + /// + /// Extract EventService Configurations ... + /// + /// + /// + public static XEventServiceConfiguration GetXEventServiceConfiguration( + this IConfiguration source + ) + { + // + var configSection = source + .GetSection(ConfigurationNodeNames.EVENT_SERVICE_NODE_NAME); + var result = configSection.Get(); + + // + return result; + } + + /// + /// Register Event Service Configurations ... + /// + /// + /// + public static void AddXEventServiceConfiguration( + this IServiceCollection services, + IConfiguration configuration + ) + { + // + var config = configuration.GetXEventServiceConfiguration(); + services.AddXEventServiceConfiguration(config); + } + + /// + /// Register Event Service Configurations ... + /// + /// + /// + public static void AddXEventServiceConfiguration( + this IServiceCollection services, + XEventServiceConfiguration configuration + ) + { + // + // Validate ... + var isValid = configuration.IsValid() && + (configuration.Broker == XEventBroker.XKafka || + (configuration.Broker == XEventBroker.XZeroMQ && + configuration.ZeroMQ.IsValid()) || + (configuration.Broker == XEventBroker.XRabbitMQ && + configuration.RabbitMQ.IsValid())); + if (!isValid) + { + // + Log("Registration Failed, due Invalid Configuration ..."); + XException.InvalidConfiguration.Throw(); + } + + // + // Register Configuration as Singleton ... + services.AddSingleton(configuration); + } + + /// + /// Register Service ... + /// + /// + /// + /// + public static void AddXEventService( + this IServiceCollection source, + IConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + var config = configuration.GetXEventServiceConfiguration(); + source.AddEventService( + lifeTime: lifeTime, + configuration: config + ); + } + + /// + /// Register Service ... + /// + /// + /// + /// + public static void AddXEventService( + this IServiceCollection source, + XEventServiceConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + source.AddEventService( + lifeTime: lifeTime, + configuration: configuration + ); + } + + /// + /// Event Consumer Service Registration ... + /// + /// + /// + public static void AddXEventServiceConsumer( + this IServiceCollection source + ) where TConsumer : XBaseEventConsumerBackgroundService + { + // + var config = source.GetRegisteredService(); + if (config.IsNullOrDefault()) + { + // + Log("Consumer Registration Faild, due Invalid Configuration issue ..."); + XException.InvalidConfiguration.Throw(); + } + + // + var consumerType = typeof(TConsumer); + switch (config.Broker) + { + // + case XEventBroker.XKafka: + // + if (!typeof(XKafkaConsumerServiceBase).IsAssignableFrom(consumerType)) + { + // + Log($"Consumer Registration Failed. TConsumer must inherit from XKafkaConsumerServiceBase when Broker is {config.Broker.GetStringValue()} ..."); + XException.InvalidConfiguration.Throw(); + } + break; + + // + case XEventBroker.XZeroMQ: + if (!typeof(XZeroMQConsumerServiceBase).IsAssignableFrom(consumerType)) + { + Log($"Consumer Registration Failed. TConsumer must inherit from XZeroMQConsumerServiceBase when Broker is {config.Broker.GetStringValue()} ..."); + XException.InvalidConfiguration.Throw(); + } + break; + + // + case XEventBroker.XRabbitMQ: + if (!typeof(XRabbitMQConsumerServiceBase).IsAssignableFrom(consumerType)) + { + Log($"Consumer Registration Failed. TConsumer must inherit from XRabbitMQConsumerServiceBase when Broker is {config.Broker.GetStringValue()} ..."); + XException.InvalidConfiguration.Throw(); + } + break; + } + + // + source.AddXBackgroundService(); + + // + Log($"Consumer Registered Successfully: {consumerType.Name} for Broker: {config.Broker.GetStringValue()} ..."); + } + + // + #region Private ... + /// + /// a LogTag for Service ... + /// + private static string XLogTag = XEventServiceConstants.XEventServiceDILogTag; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + + /// + /// Register Event Service ... + /// + /// + /// + /// + private static void AddEventService( + this IServiceCollection source, + XEventServiceConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + // Register Configuration ... + source.AddXEventServiceConfiguration(configuration); + + // + // Register based on Broker ... + switch (configuration.Broker) + { + // + case XEventBroker.XKafka: + source.Add( + new ServiceDescriptor(typeof(IXKafkaProducerService), typeof(XKafkaProducerService), lifeTime) + ); + break; + + // + case XEventBroker.XZeroMQ: + source.Add( + new ServiceDescriptor(typeof(IXZeroMQProducerService), typeof(XZeroMQProducerService), lifeTime) + ); + break; + + // + case XEventBroker.XRabbitMQ: + source.Add( + new ServiceDescriptor(typeof(IXRabbitMQProducerService), typeof(XRabbitMQProducerService), lifeTime) + ); + break; + } + + // + source.Add( + new ServiceDescriptor(typeof(IXEventServiceProvider), typeof(XEventServiceProvider), lifeTime) + ); + + // + Log($"Service Registered Sucessfull, Provider: {configuration.Broker.GetStringValue()}"); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Extensions\XEventServiceExtensions.cs + +using System.Linq; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Extensions +{ + /// + /// Extensions for Event Service ... + /// + public static class XEventServiceExtensions + { + /// + /// Validate Event Broker ... + /// + /// + /// + public static bool IsValid(this XEventBroker source) + { + // + var result = + !source.IsNull() && + source != XEventBroker.None; + + // + return result; + } + + /// + /// Validate Event Service Configuration ... + /// + /// + /// + public static bool IsValid(this XEventServiceConfiguration source) + { + // + var result = + !source.IsNullOrDefault() && + source.Broker.IsValid() && + !source.Url.IsNullOrEmpty() && + source.Url.IsValidUrl(); + + // + return result; + } + + /// + /// Validate RabbitMQ Exchange Type ... + /// + /// + /// + public static bool IsValid(this XRabbitMQExchangeType source) + { + // + var result = source != XRabbitMQExchangeType.None; + + // + return result; + } + + /// + /// Validate RabbitMQ Configuration ... + /// + /// + /// + public static bool IsValid(this XRabbitMQOptions source) + { + // + var result = + !source.IsNullOrDefault() && + !source.Exchange.IsNullOrEmpty() && + source.ExchangeType.IsValid(); + + // + return result; + } + + /// + /// Validate an Event Message ... + /// + /// + /// + public static bool IsValid(this XEventMessage source) + { + // + var result = + !source.IsNullOrDefault() && + !source.Action.IsNullOrEmpty() && + !source.Sender.IsNullOrEmpty(); + + // + return result; + } + + /// + /// Validate an Event Request ... + /// + /// + /// + public static bool IsValid(this XEventRequest source) + { + // + var result = + !source.IsNullOrDefault() && + !source.Topic.IsNullOrEmpty() && + source.Message.IsValid(); + + // + return result; + } + + /// + /// Validate ZeroMQ Pattern ... + /// + /// + /// + public static bool IsValid(this XZeroMQPattern source) + { + // + var result = source != XZeroMQPattern.None; + + // + return result; + } + + /// + /// Validate ZeroMQ Options ... + /// + /// + /// + public static bool IsValid(this XZeroMQOptions source) + { + // + var result = + !source.IsNullOrDefault() && + source.Pattern.IsValid(); + + // + return result; + } + + /// + /// Validate an Array is Wildcard passed or not ... + /// + /// + /// + public static bool IsAllowedWildcard(this string[] source) + { + // + var result = + source is not null && + source.Length == 1 && + source.Any(s => + s == XEventServiceConstants.XWildcard); + + // + return result; + } + + /// + /// Validate a Collection is Valid for Kafka Consumer ... + /// + /// + /// + public static bool IsAllowedCollection( + this string[] source + ) + { + // + var result = source.HasChild(); + + // + return result; + } + + /// + /// Validate Sender ... + /// + /// + /// + /// + public static bool IsValidSender( + this XEventMessage source, + string[] allowedSenders + ) + { + // + // Validate Source and Collection ... + var result = + source.IsValid() && + allowedSenders + .IsAllowedCollection(); + if (!result) + { + return result; + } + + // + // Validate Sender ... + result = + allowedSenders.IsAllowedWildcard() || + allowedSenders.Any(x => + x.Equals( + source.Sender, + System.StringComparison.InvariantCultureIgnoreCase)); + + // + return result; + } + + /// + /// Validate Action ... + /// + /// + /// + /// + public static bool IsValidAction( + this XEventMessage source, + string[] allowedActions + ) + { + // + // Validate Source and Collection ... + var result = + source.IsValid() && + allowedActions + .IsAllowedCollection(); + if (!result) + { + return result; + } + + // + // Validate Action ... + result = + allowedActions.IsAllowedWildcard() || + allowedActions.Any(x => + x.Equals( + source.Action, + System.StringComparison.InvariantCultureIgnoreCase)); + + // + return result; + } + + /// + /// Completely Validate a Message, based on providing: + /// - Allowed Actions + /// - Allowed Senders + /// + /// + /// + /// + /// + public static bool IsValid( + this XEventMessage source, + string[] allowedSenders, + string[] allowedActions + ) + { + // + var result = + source.IsValid() && + source.IsValidAction(allowedActions) && + source.IsValidSender(allowedSenders); + + // + return result; + } + + /// + /// Parse Url and Extract Host address ... + /// + /// + /// + public static string GetRabbitMQHost(this XEventServiceConfiguration source) + { + // + var result = string.Empty; + + // + if (!source.IsValid()) + { + XException.InvalidConfiguration.Throw(); + } + + // + var parts = source.Url.Split(':'); + if (parts.Length != 2) + { + XException.InvalidConfiguration.Throw(); + } + + // + result = parts[0]; + if (result.IsNullOrEmpty() || + !result.IsValidUrl() + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + return result; + } + + /// + /// Parse Url and Extract Port Number ... + /// + /// + /// + public static int GetRabbitMQPort(this XEventServiceConfiguration source) + { + // + int result = -1; + + // + if (!source.IsValid()) + { + XException.InvalidConfiguration.Throw(); + } + + // + var parts = source.Url.Split(':'); + if (parts.Length != 2) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Try Parse Port as Integer ... + int.TryParse(parts[1], out result); + if (result <= 0) + { + XException.InvalidConfiguration.Throw(); + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXEventServiceProvider.cs + +namespace xEventService.Interfaces +{ + public interface IXEventServiceProvider : IXProducerServiceBase + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXKafkaProducerService.cs + +namespace xEventService.Interfaces +{ + /// + /// a Service for Produce Kafka Messages ... + /// + public interface IXKafkaProducerService : IXProducerServiceBase + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXProducerServiceBase.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using xEventService.Constants; +using xEventService.Models; + +namespace xEventService.Interfaces +{ + public interface IXProducerServiceBase : IDisposable + { + /// + /// Sending Message ... + /// + /// an Instance of to Provides Kafka Messaging requirement ... + /// + /// + Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ); + + /// + /// Sending a Message ... + /// + /// an Instance of to Provides Kafka Messaging requirement ... + /// Topic for Sending Message ... + /// + /// + Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXRabbitMQProducerService.cs + +using System.Threading; +using System.Threading.Tasks; +using xEventService.Models; + +namespace xEventService.Interfaces +{ + /// + /// RabbitMQ Producer Service Implementation ... + /// + public interface IXRabbitMQProducerService : IXProducerServiceBase + { + /// + /// Sending a Message to Default Exchange ... + /// + /// + /// an Instance of ... + /// + /// + /// Target Exchange Name. if empty, uses default from configuration ... + /// + /// + /// Routing Key for message routing ... + /// + /// + /// + Task SendMessageAsync( + XEventMessage message, + string exchange = null, + string routingKey = null, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXZeroMQProducerService.cs + +namespace xEventService.Interfaces +{ + public interface IXZeroMQProducerService : IXProducerServiceBase + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XEventMessage.cs + +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace xEventService.Models +{ + /// + /// a Message Descriptor for Events ... + /// + public class XEventMessage + { + /// + /// Specified Action for Fire ... + /// + [JsonRequired] + public string Action { get; set; } + + /// + /// Specified Action Sender ... + /// + [JsonRequired] + public string Sender { get; set; } + + /// + /// Sending Time ... + /// + [JsonRequired] + public DateTime Offset { get; set; } + + /// + /// Specified Message ... + /// + public string Message { get; set; } + + /// + /// Metadata for Message ... + /// + public IDictionary Payload { get; set; } = new Dictionary(); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XEventRequest.cs + +using xEventService.Constants; + +namespace xEventService.Models +{ + /// + /// a Request for Sending an Event ... + /// + public class XEventRequest + { + /// + /// Specified Which Kafka Topic ... + /// + public string Topic { get; set; } = XEventServiceConstants.XDefaultTopic; + + /// + /// Specified Kafka Meesage to Send ... + /// + public XEventMessage Message { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XRabbitMQOptions.cs + +using xEventService.Constants; + +namespace xEventService.Models +{ + /// + /// RabbitMQ-specific connection and messaging options ... + /// + public class XRabbitMQOptions + { + + /// + /// Connection Username ... + /// + public string Username { get; set; } = "guest"; + + /// + /// Connection Password ... + /// + public string Password { get; set; } = "guest"; + + /// + /// Virtual Host ... + /// + public string VirtualHost { get; set; } = "/"; + + /// + /// Default Exchange Name ... + /// + public string Exchange { get; set; } = "x.saherelm.events"; + + /// + /// Default Exchange Type (fanout, direct, topic, headers) ... + /// + public XRabbitMQExchangeType ExchangeType { get; set; } = XRabbitMQExchangeType.Fanout; + + /// + /// Default Queue Name ... + /// if empty, a dynamic queue will be generated ... + /// + public string Queue { get; set; } = string.Empty; + + /// + /// Default Routing Key ... + /// + public string RoutingKey { get; set; } = string.Empty; + + /// + /// Exchange Durable Flag ... + /// + public bool ExchangeDurable { get; set; } = true; + + /// + /// Exchange AutoDelete Flag ... + /// + public bool ExchangeAutoDelete { get; set; } = false; + + /// + /// Queue Durable Flag ... + /// + public bool QueueDurable { get; set; } = true; + + /// + /// Queue Exclusive Flag ... + /// + public bool QueueExclusive { get; set; } = false; + + /// + /// Queue AutoDelete Flag ... + /// + public bool QueueAutoDelete { get; set; } = false; + + /// + /// Enable Automatic Recovery on Connection Loss ... + /// + public bool AutomaticRecoveryEnabled { get; set; } = true; + + /// + /// Network Recovery Interval in Seconds ... + /// + public int NetworkRecoveryIntervalSeconds { get; set; } = 5; + + /// + /// Prefetch Count for Consumer ... + /// + public ushort PrefetchCount { get; set; } = 1; + + /// + /// Enable Manual Acknowledgement ... + /// + public bool ManualAck { get; set; } = true; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XZeroMQOptions.cs + +using xEventService.Constants; + +namespace xEventService.Models +{ + public class XZeroMQOptions + { + /// + /// Socket Pattern (PubSub, PushPull) ... + /// + public XZeroMQPattern Pattern { get; set; } = XZeroMQPattern.PubSub; + + /// + /// Bind or Connect ... + /// True for Server (Bind), False for Client (Connect) ... + /// + public bool IsServer { get; set; } = false; + + /// + /// High Water Mark for Sending ... + /// + public int SendHighWaterMark { get; set; } = 1000; + + /// + /// High Water Mark for Receiving ... + /// + public int ReceiveHighWaterMark { get; set; } = 1000; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XBaseEventConsumerBackgroundService.cs + +using System; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xCommons.Providers; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// Base Event Consumer Service ... + /// + public abstract class XBaseEventConsumerBackgroundService : XResumableBackgroundService + { + /// + /// Consumed Topic Messages ... + /// + protected readonly string topic; + + /// + /// Allowed Senders ... + /// + protected readonly string[] allowedSenders; + + /// + /// Allowed Actions ... + /// + protected readonly string[] allowedActions; + + /// + /// Configuration ... + /// + protected readonly XEventServiceConfiguration configuration; + + protected XBaseEventConsumerBackgroundService( + string[] allowedActions, + string[] allowedSenders, + IServiceProvider serviceProvider, + XEventServiceConfiguration configuration, + ILogger logger, + string topic = XEventServiceConstants.XDefaultTopic + ) : base(serviceProvider, logger) + { + // + // Validate Actions ... + if (!allowedActions + .IsAllowedCollection() || + !allowedSenders + .IsAllowedCollection() || + !configuration.IsValid() + ) + { + XException.InvalidArgs.Throw(); + } + + // + this.topic = topic; + this.configuration = configuration; + this.allowedActions = allowedActions; + this.allowedSenders = allowedSenders; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XBaseEventProducerService.cs + +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Extensions; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + public abstract class XBaseEventProducerService + { + protected readonly XEventServiceConfiguration configuration; + protected readonly ILogger logger; + + protected XBaseEventProducerService( + XEventServiceConfiguration configuration, + ILogger logger + ) + { + // + this.logger = logger; + this.configuration = configuration; + + // + // Validate Configurations ... + if (!configuration.IsValid()) + { + XException.InvalidConfiguration.Throw(); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XEventServiceProvider.cs + +using System.Threading; +using System.Threading.Tasks; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + public class XEventServiceProvider : IXEventServiceProvider + { + // + private readonly XEventServiceConfiguration configuration; + private readonly IXKafkaProducerService kafkaProducerService; + private readonly IXZeroMQProducerService zeroMQProducerService; + private readonly IXRabbitMQProducerService rabbitMQProducerService; + + public XEventServiceProvider( + XEventServiceConfiguration configuration, + IXKafkaProducerService kafkaProducerService = null, + IXZeroMQProducerService zeroMQProducerService = null, + IXRabbitMQProducerService rabbitMQProducerService = null + ) + { + // + this.configuration = configuration; + this.kafkaProducerService = kafkaProducerService; + this.zeroMQProducerService = zeroMQProducerService; + this.rabbitMQProducerService = rabbitMQProducerService; + + // + // Validate ... + var isValid = configuration.IsValid() && + configuration.Broker == XEventBroker.XKafka + ? kafkaProducerService != null + : configuration.Broker == XEventBroker.XZeroMQ + ? zeroMQProducerService != null + : configuration.Broker == XEventBroker.XRabbitMQ + ? rabbitMQProducerService != null + : false; + if (!isValid) + { + XException.InvalidConfiguration.Throw(); + } + } + + /// + /// Send Message to Broker Using XEventRequest instance ... + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + if (!request.IsValid()) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await SendMessageAsync( + topic: request.Topic, + message: request.Message, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Send Message to Broker Using XMessage instance ... + /// + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + if (!message.IsValid()) + { + XException.InvalidArgs.Throw(); + } + + // + var result = false; + switch (configuration.Broker) + { + // + case XEventBroker.XKafka: + result = await kafkaProducerService.SendMessageAsync( + topic: topic, + message: message, + cancellationToken: cancellationToken + ); + break; + + // + case XEventBroker.XZeroMQ: + result = await zeroMQProducerService.SendMessageAsync( + topic: topic, + message: message, + cancellationToken: cancellationToken + ); + break; + + // + case XEventBroker.XRabbitMQ: + result = await rabbitMQProducerService.SendMessageAsync( + topic: topic, + message: message, + cancellationToken: cancellationToken + ); + break; + } + + // + return result; + } + + /// + /// Dispose ... + /// + public void Dispose() + { + // + if (kafkaProducerService != null) + { + kafkaProducerService.Dispose(); + } + + // + if (zeroMQProducerService != null) + { + zeroMQProducerService.Dispose(); + } + + // + if (rabbitMQProducerService != null) + { + rabbitMQProducerService.Dispose(); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XKafkaConsumerServiceBase.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using Confluent.Kafka; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// an Abstract Kafka Consumer Service ... + /// + public abstract class XKafkaConsumerServiceBase : XBaseEventConsumerBackgroundService + { + /// + /// Kafka Consumer Class ... + /// + protected readonly IConsumer consumer; + + protected XKafkaConsumerServiceBase( + string[] allowedActions, + string[] allowedSenders, + IServiceProvider serviceProvider, + XEventServiceConfiguration configuration, + ILogger logger, + string topic = XEventServiceConstants.XDefaultTopic + ) : base( + topic: topic, + logger: logger, + configuration: configuration, + allowedActions: allowedActions, + allowedSenders: allowedSenders, + serviceProvider: serviceProvider + ) + { + // + // Validating Configuration ... + if (configuration.Broker != XEventBroker.XKafka) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Prepare Kafka Consumer Configuration ... + var config = new ConsumerConfig + { + EnableAutoCommit = false, + BootstrapServers = configuration.Url, + AutoOffsetReset = AutoOffsetReset.Earliest, + GroupId = XEventServiceConstants.XDefaultConsumerGroup, + }; + consumer = new ConsumerBuilder(config).Build(); + } + + /// + /// Default Action Execution for Background Services ... + /// + /// + /// + protected override async Task ExecuteAsync( + CancellationToken cancellationToken = default + ) + { + // + // For Fix Blocking Synchronous ... + await Task.Yield(); + using var cts = new CancellationTokenSource(); + + // + // Subscribe to Topic ... + consumer.Subscribe(topic); + + // + // Consuming ... + while (!cancellationToken.IsCancellationRequested) + { + try + { + // + // Consuming Topic ... + var consumeResult = consumer.Consume(cancellationToken); + if (!consumeResult.IsNullOrDefault() && + !consumeResult.Message.IsNullOrDefault() + ) + { + // + var message = consumeResult + .Message + .Value + .FromJSON(); + if (message.IsValid( + allowedSenders: allowedSenders, + allowedActions: allowedActions) + ) + { + // + await DoWorkAsync( + payload: consumeResult.Message, + cancellationToken: cancellationToken + ); + } + } + } + catch (ConsumeException) + { + await Task.Delay(1000, cancellationToken); + } + catch (Exception) + { + await Task.Delay(1000, cancellationToken); + } + } + } + + /// + /// Dispose Object ... + /// + public override void Dispose() + { + // + if (consumer != null) + { + consumer.Close(); + consumer.Dispose(); + } + + // + GC.SuppressFinalize(this); + base.Dispose(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XKafkaProducerService.cs + +using xCommons.Extensions; +using xExceptions.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Configuration; +using xEventService.Models; +using Confluent.Kafka; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; +using System.Threading; +using xEventService.Constants; +using System; + +namespace xEventService.Providers +{ + public class XKafkaProducerService : XBaseEventProducerService, IXKafkaProducerService + { + private readonly IProducer producer; + + public XKafkaProducerService( + ILogger logger, + XEventServiceConfiguration configuration + ) : base(configuration, logger) + { + // + // Validate Configurations ... + if (!configuration.IsValid() || + configuration.Broker != XEventBroker.XKafka + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Prepare Kafka Producer Configuration ... + var config = new ProducerConfig + { + Acks = Acks.All, + BootstrapServers = configuration.Url, + MessageSendMaxRetries = configuration.MaxRetries, + }; + producer = new ProducerBuilder(config).Build(); + } + + /// + /// Sending Message ... + /// + /// an Instance of to Provides Kafka Messaging requirement ... + /// + /// + public async Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = request.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + result = await SendMessageAsync( + message: request.Message, + topic: request.Topic, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Sending a Message ... + /// + /// an Instance of to Provides Kafka Messaging requirement ... + /// Topic for Sending Message ... + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = message.IsValid() && + !topic.IsNullOrEmpty(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + try + { + // + var response = await producer.ProduceAsync( + topic: topic, + message: new Message + { + Value = message + .ToJSON(camelCase: true) + }, + cancellationToken: cancellationToken + ); + + // + result = !response.IsNullOrDefault(); + } + catch + { + result = false; + } + + // + return result; + } + + /// + /// Dispose Required Objects ... + /// + public void Dispose() + { + // + if (producer != null) + { + producer.Dispose(); + } + + // + GC.SuppressFinalize(this); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XRabbitMQConsumerServiceBase.cs + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using RabbitMQ.Client; +using RabbitMQ.Client.Events; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// an Abstract Kafka Consumer Service ... + /// + public abstract class XRabbitMQConsumerServiceBase : XBaseEventConsumerBackgroundService + { + // + protected IModel channel; + protected IConnection connection; + protected AsyncEventingBasicConsumer consumer; + + protected XRabbitMQConsumerServiceBase( + string[] allowedActions, + string[] allowedSenders, + IServiceProvider serviceProvider, + XEventServiceConfiguration configuration, + ILogger logger, + string topic = XEventServiceConstants.XDefaultTopic + ) : base( + topic: topic, + logger: logger, + configuration: configuration, + allowedActions: allowedActions, + allowedSenders: allowedSenders, + serviceProvider: serviceProvider + ) + { + // + // Validating Configuration ... + if (!configuration.RabbitMQ.IsValid() || + configuration.Broker != XEventBroker.XRabbitMQ + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + SetupConnection(); + DeclareTopology(); + consumer = new AsyncEventingBasicConsumer(channel); + } + + /// + /// Default Action Execution for Background Services ... + /// + /// + /// + protected override async Task ExecuteAsync( + CancellationToken cancellationToken = default + ) + { + // + // For Fix Blocking Synchronous ... + await Task.Yield(); + using var cts = new CancellationTokenSource(); + + // + // Subscribe to Topic ... + consumer.Received += async (model, ea) => + { + // + try + { + // + var body = ea.Body.ToArray(); + var json = Encoding.UTF8.GetString(body); + var message = json.FromJSON(); + if (!message.IsNullOrDefault() && + message.IsValid( + allowedSenders: allowedSenders, + allowedActions: allowedActions) + ) + { + // + await DoWorkAsync( + payload: message, + cancellationToken: cancellationToken + ); + } + + // + if (configuration.RabbitMQ.ManualAck) + { + // + channel.BasicAck( + multiple: false, + deliveryTag: ea.DeliveryTag + ); + } + } + catch + { + // + if (configuration.RabbitMQ.ManualAck) + { + // + channel.BasicNack( + requeue: true, + multiple: false, + deliveryTag: ea.DeliveryTag + ); + } + } + }; + + // + consumer.Shutdown += (model, ea) => + { + return Task.CompletedTask; + }; + + // + channel.BasicQos( + prefetchSize: 0, + global: false, + prefetchCount: configuration.RabbitMQ.PrefetchCount + ); + + // + channel.BasicConsume( + queue: GetQueueName(), + consumer: consumer, + autoAck: !configuration.RabbitMQ.ManualAck + ); + + // + while (!cancellationToken.IsCancellationRequested) + { + await Task.Delay(1000, cancellationToken); + } + } + + /// + /// Overriding Stop Action ... + /// + /// + /// + public override async Task StopAsync(CancellationToken cancellationToken) + { + // + if (channel != null) + { + // + channel.Close(); + channel.Dispose(); + } + + // + if (connection != null) + { + // + connection.Close(); + connection.Dispose(); + } + + // + await base.StopAsync(cancellationToken); + } + + /// + /// Dispose Implementation ... + /// + public override void Dispose() + { + // + if (channel != null) + { + channel.Dispose(); + } + + // + if (connection != null) + { + connection.Dispose(); + } + + // + GC.SuppressFinalize(this); + base.Dispose(); + } + + // + #region Private ... + private string GetQueueName() + { + // + var result = string.Empty; + if (!configuration.RabbitMQ.Queue.IsNullOrEmpty()) + { + result = configuration.RabbitMQ.Queue; + } + + // + if (result.IsNullOrEmpty()) + { + // + var digits = Guid.NewGuid().ToString().GetDigits(); + result = $"{configuration.RabbitMQ.Exchange}[{digits}]"; + } + + // + return result; + } + + private void SetupConnection() + { + var factory = new ConnectionFactory + { + DispatchConsumersAsync = true, + Port = configuration.GetRabbitMQPort(), + HostName = configuration.GetRabbitMQHost(), + UserName = configuration.RabbitMQ.Username, + Password = configuration.RabbitMQ.Password, + NetworkRecoveryInterval = TimeSpan.FromSeconds( + configuration.RabbitMQ.NetworkRecoveryIntervalSeconds + ), + VirtualHost = configuration.RabbitMQ.VirtualHost, + AutomaticRecoveryEnabled = configuration.RabbitMQ.AutomaticRecoveryEnabled, + }; + + // + connection = factory.CreateConnection(); + channel = connection.CreateModel(); + } + + private void DeclareTopology() + { + // + channel.ExchangeDeclare( + arguments: null, + exchange: configuration.RabbitMQ.Exchange, + durable: configuration.RabbitMQ.ExchangeDurable, + autoDelete: configuration.RabbitMQ.ExchangeAutoDelete, + type: configuration.RabbitMQ.ExchangeType.GetStringValue() + ); + + // + var queueName = GetQueueName(); + + // + channel.QueueDeclare( + arguments: null, + queue: queueName, + durable: configuration.RabbitMQ.QueueDurable, + exclusive: configuration.RabbitMQ.QueueExclusive, + autoDelete: configuration.RabbitMQ.QueueAutoDelete + ); + + // + channel.QueueBind( + arguments: null, + queue: queueName, + exchange: configuration.RabbitMQ.Exchange, + routingKey: configuration.RabbitMQ.RoutingKey + ); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XRabbitMQProducerService.cs + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using RabbitMQ.Client; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// RabbitMQ Producer Service Implementation ... + /// + public class XRabbitMQProducerService : XBaseEventProducerService, IXRabbitMQProducerService + { + private IModel channel; + private IConnection connection; + private readonly object connectionLock = new(); + + public XRabbitMQProducerService( + ILogger logger, + XEventServiceConfiguration configuration + ) : base(configuration, logger) + { + // + // Validate Configurations ... + if (!configuration.IsValid() || + !configuration.RabbitMQ.IsValid() || + configuration.Broker != XEventBroker.XRabbitMQ + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Initialize Connection ... + CreateConnection(); + } + + /// + /// Sending Message with Full Request Descriptor ... + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = request.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + result = await SendMessageAsync( + message: request.Message, + exchange: request.Topic, + cancellationToken: cancellationToken, + routingKey: configuration.RabbitMQ.RoutingKey + ); + + // + return result; + } + + /// + /// Sending a Message to Topic ... + /// + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ) + { + // + var result = await SendMessageAsync( + request: new XEventRequest + { + Topic = topic, + Message = message + }, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Sending a Message to Exchange ... + /// + /// + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string exchange = null, + string routingKey = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = message.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize Exchange ... + if (exchange.IsNullOrEmpty()) + { + exchange = configuration.RabbitMQ.Exchange; + } + + // + // Normalize Routing Key ... + if (routingKey.IsNull()) + { + routingKey = configuration.RabbitMQ.RoutingKey; + } + + // + try + { + // + // Ensure Connection is Alive ... + EnsureChannelAsync(); + + // + // Serialize Message ... + var body = Encoding.UTF8.GetBytes( + message.ToJSON(camelCase: true) + ); + + // + // Prepare Message Properties ... + var properties = channel.CreateBasicProperties(); + properties.Persistent = true; + properties.ContentType = "application/json"; + properties.Timestamp = new AmqpTimestamp( + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + ); + properties.MessageId = Guid.NewGuid().ToString(); + properties.Type = message.Action; + properties.AppId = message.Sender; + + // + // Publish Message ... + await Task.Run(() => + { + // + channel.BasicPublish( + exchange: exchange, + routingKey: routingKey, + mandatory: false, + basicProperties: properties, + body: body + ); + }, + cancellationToken: cancellationToken); + + // + result = true; + } + catch + { + result = false; + } + + // + return result; + } + + /// + /// Dispose Object ... + /// + public void Dispose() + { + // + try + { + // + if (channel != null) + { + // + channel.Close(); + channel.Dispose(); + channel = null; + } + + // + if (connection != null) + { + // + connection.Close(); + connection.Dispose(); + connection = null; + } + } + catch + { } + + // + GC.SuppressFinalize(this); + } + + // + #region Private ... + /// + /// Create Connection to RabbitMQ Server ... + /// + private void CreateConnection() + { + // + var factory = new ConnectionFactory + { + Port = configuration.GetRabbitMQPort(), + HostName = configuration.GetRabbitMQHost(), + UserName = configuration.RabbitMQ.Username, + Password = configuration.RabbitMQ.Password, + VirtualHost = configuration.RabbitMQ.VirtualHost, + AutomaticRecoveryEnabled = configuration.RabbitMQ.AutomaticRecoveryEnabled, + NetworkRecoveryInterval = TimeSpan.FromSeconds( + configuration.RabbitMQ.NetworkRecoveryIntervalSeconds + ), + DispatchConsumersAsync = true + }; + + // + connection = factory.CreateConnection(); + } + + /// + /// Check Connection is Alive ... + /// + private bool IsConnected() + { + // + var result = + !connection.IsNullOrDefault() && + connection.IsOpen && + !channel.IsNull() && + channel.IsOpen; + + // + return result; + } + + /// + /// Ensure Channel is Open and Exchange/Queue Declared ... + /// + private void EnsureChannelAsync() + { + // + if (IsConnected()) + { + return; + } + + // + lock (connectionLock) + { + // + // Double Check after Lock ... + if (IsConnected()) + { + return; + } + + // + // Recreate Connection if Dead ... + if (connection is null || !connection.IsOpen) + { + CreateConnection(); + } + + // + // Create Channel ... + channel = connection.CreateModel(); + + // + // Declare Exchange ... + channel.ExchangeDeclare( + arguments: null, + exchange: configuration.RabbitMQ.Exchange, + durable: configuration.RabbitMQ.ExchangeDurable, + autoDelete: configuration.RabbitMQ.ExchangeAutoDelete, + type: configuration.RabbitMQ.ExchangeType.GetStringValue() + ); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XZeroMQConsumerServiceBase.cs + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using NetMQ; +using NetMQ.Sockets; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// an Abstract ZeroMQ Consumer Service ... + /// + public abstract class XZeroMQConsumerServiceBase : XBaseEventConsumerBackgroundService + { + // + private NetMQSocket socket; + + protected XZeroMQConsumerServiceBase( + string[] allowedActions, + string[] allowedSenders, + IServiceProvider serviceProvider, + XEventServiceConfiguration configuration, + ILogger logger, + string topic = XEventServiceConstants.XDefaultTopic + ) : base( + topic: topic, + logger: logger, + configuration: configuration, + allowedActions: allowedActions, + allowedSenders: allowedSenders, + serviceProvider: serviceProvider + ) + { + // + // Validating Configuration ... + if (!configuration.ZeroMQ.IsValid() || + configuration.Broker != XEventBroker.XZeroMQ) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Initialize ... + InitializeSocket(); + } + + /// + /// Default Action Execution for Background Services ... + /// + /// + /// + protected override async Task ExecuteAsync( + CancellationToken cancellationToken = default + ) + { + // + await Task.Yield(); + while (!cancellationToken.IsCancellationRequested) + { + // + try + { + // + string receivedTopic = string.Empty; + byte[] body = null; + + if (configuration.ZeroMQ.Pattern == XZeroMQPattern.PubSub) + { + // + if (socket.TryReceiveFrameString(TimeSpan.FromMilliseconds(100), out receivedTopic)) + { + // + if (socket.TryReceiveFrameBytes(TimeSpan.FromMilliseconds(100), out body)) + { + // Process + } + } + } + else + { + if (socket.TryReceiveFrameBytes(TimeSpan.FromMilliseconds(100), out body)) + { + // Process + } + } + + if (body != null && body.Length > 0) + { + // + var json = Encoding.UTF8.GetString(body); + var message = json.FromJSON(); + if (!message.IsNullOrDefault() && + message.IsValid( + allowedSenders: allowedSenders, + allowedActions: allowedActions) + ) + { + // + await DoWorkAsync( + payload: message, + cancellationToken: cancellationToken + ); + } + } + else + { + await Task.Delay(10, cancellationToken); + } + } + catch (Exception) + { + await Task.Delay(1000, cancellationToken); + } + } + } + + public override async Task StopAsync(CancellationToken cancellationToken) + { + // + if (socket != null) + { + socket.Close(); + } + + // + await base.StopAsync(cancellationToken); + } + + /// + /// Dispose Object ... + /// + public override void Dispose() + { + // + if (socket != null) + { + socket.Dispose(); + } + + // + GC.SuppressFinalize(this); + base.Dispose(); + } + + // + #region Private ... + private void InitializeSocket() + { + // + if (configuration.ZeroMQ.Pattern == XZeroMQPattern.PubSub) + { + // + socket = new SubscriberSocket(); + socket.Options.ReceiveHighWatermark = configuration.ZeroMQ.ReceiveHighWaterMark; + + // + // Subscribe to specific topic or all + if (topic.IsNullOrEmpty() || topic == XEventServiceConstants.XWildcard) + { + ((SubscriberSocket)socket).SubscribeToAnyTopic(); + } + else + { + ((SubscriberSocket)socket).Subscribe(topic); + } + } + else + { + // + socket = new PullSocket(); + socket.Options.ReceiveHighWatermark = configuration.ZeroMQ.ReceiveHighWaterMark; + } + + // + if (configuration.ZeroMQ.IsServer) + { + socket.Bind(configuration.Url); + } + else + { + socket.Connect(configuration.Url); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XZeroMQProducerService.cs + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using NetMQ; +using NetMQ.Sockets; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + public class XZeroMQProducerService : XBaseEventProducerService, IXZeroMQProducerService + { + // + private NetMQSocket socket; + private readonly object socketLock = new(); + + public XZeroMQProducerService( + XEventServiceConfiguration configuration, + ILogger logger + ) : base(configuration, logger) + { + // + // Validate Configurations ... + if (!configuration.IsValid() || + !configuration.ZeroMQ.IsValid() || + configuration.Broker != XEventBroker.XZeroMQ + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + InitializeSocket(); + } + + /// + /// Sending Message with Full Request Descriptor ... + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ) + { + // + var result = request.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + result = await SendMessageAsync( + message: request.Message, + topic: request.Topic, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Sending a Message to Topic ... + /// + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ) + { + // + var result = message.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize Topic ... + if (topic.IsNullOrEmpty()) + { + topic = XEventServiceConstants.XDefaultTopic; + } + + // + try + { + // + var json = message.ToJSON(camelCase: true); + var body = Encoding.UTF8.GetBytes(json); + await Task.Run(() => + { + // + lock (socketLock) + { + // + if (configuration.ZeroMQ.Pattern == XZeroMQPattern.PubSub) + { + socket.SendMoreFrame(topic).SendFrame(body); + } + else + { + socket.SendFrame(body); + } + } + }, cancellationToken); + + // + result = true; + } + catch (Exception) + { + result = false; + } + + // + return result; + } + + /// + /// Dispose Object ... + /// + public void Dispose() + { + // + try + { + // + lock (socketLock) + { + // + if (socket != null) + { + // + socket.Close(); + socket.Dispose(); + socket = null; + } + } + } + catch { } + + // + GC.SuppressFinalize(this); + } + + // + #region Private ... + /// + /// Initialize Socket ... + /// + private void InitializeSocket() + { + // + lock (socketLock) + { + // + if (configuration.ZeroMQ.Pattern == XZeroMQPattern.PubSub) + { + // + socket = new PublisherSocket(); + socket.Options.SendHighWatermark = configuration.ZeroMQ.SendHighWaterMark; + } + else + { + // + socket = new PushSocket(); + socket.Options.SendHighWatermark = configuration.ZeroMQ.SendHighWaterMark; + } + + // + if (configuration.ZeroMQ.IsServer) + { + socket.Bind(configuration.Url); + } + else + { + socket.Connect(configuration.Url); + } + } + } + #endregion + } +} diff --git a/Documents/GeneratedContext/xDashboard-xEventService-20260819_221910.txt b/Documents/GeneratedContext/xDashboard-xEventService-20260819_221910.txt new file mode 100644 index 0000000..79f2a87 --- /dev/null +++ b/Documents/GeneratedContext/xDashboard-xEventService-20260819_221910.txt @@ -0,0 +1,2586 @@ + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Configuration\XEventServiceConfiguration.cs + +using xEventService.Constants; +using xEventService.Models; + +namespace xEventService.Configuration +{ + /// + /// a configuration class for describing how to connect rabbit mq server + /// and how channels must be declared ... + /// + public class XEventServiceConfiguration + { + /// + /// Message Broker Address ... + /// + public string Url { get; set; } + + /// + /// Max Retries to Sending Message ... + /// + public int MaxRetries { get; set; } = 3; + + /// + /// Allowed Message Broker ... + /// + public XEventBroker Broker { get; set; } = XEventBroker.None; + + /// + /// ZeroMQ Specific Options ... + /// Only used when Broker is XZeroMQ ... + /// + public XZeroMQOptions ZeroMQ { get; set; } = new XZeroMQOptions(); + + /// + /// RabbitMQ Specific Options ... + /// Only used when Broker is XRabbitMQ ... + /// + public XRabbitMQOptions RabbitMQ { get; set; } = new XRabbitMQOptions(); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\ConfigurationNodeNames.cs + +namespace xEventService.Constants +{ + public partial struct ConfigurationNodeNames { + public const string EVENT_SERVICE_NODE_NAME = "EventServiceConfiguration"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\XEventBroker.cs + +using xExceptions.Attributes; + +namespace xEventService.Constants +{ + /// + /// Available Brokers ... + /// + public enum XEventBroker + { + [StringValue(XEventServiceConstants.None)] + None, + + [StringValue(XEventServiceConstants.XKafka)] + XKafka, + + [StringValue(XEventServiceConstants.XZeroMQ)] + XZeroMQ, + + [StringValue(XEventServiceConstants.XRabbitMQ)] + XRabbitMQ, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\XEventServiceConstants.cs + +namespace xEventService.Constants +{ + public struct XEventServiceConstants + { + // + // Service Extensions Log Tag ... + public const string XEventServiceDILogTag = "XEventService"; + + // + // Defaults ... + public const string XDefaultTopic = "XSaherelm"; + public const string XDefaultConsumerGroup = "XSaherElmGroup"; + + // + public const string None = "None"; + public const string XKafka = "XKafka"; + public const string PubSub = "PubSub"; + public const string XZeroMQ = "XZeroMQ"; + public const string PushPull = "PushPull"; + public const string XRabbitMQ = "XRabbitMQ"; + + // + public const string XWildcard = "*"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\XRabbitMQExchangeType.cs + +using xExceptions.Attributes; + +namespace xEventService.Constants +{ + /// + /// Available RabbitMQ Exchange Types ... + /// + public struct XRabbitMQExchangeTypes + { + public const string None = "none"; + public const string Direct = "direct"; + public const string Fanout = "fanout"; + public const string Topic = "topic"; + public const string Headers = "headers"; + } + + /// + /// RabbitMQ Exchange Type Enum ... + /// + public enum XRabbitMQExchangeType + { + [StringValue(XRabbitMQExchangeTypes.None)] + None, + + [StringValue(XRabbitMQExchangeTypes.Direct)] + Direct, + + [StringValue(XRabbitMQExchangeTypes.Fanout)] + Fanout, + + [StringValue(XRabbitMQExchangeTypes.Topic)] + Topic, + + [StringValue(XRabbitMQExchangeTypes.Headers)] + Headers, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\XZeroMQPattern.cs + +using xExceptions.Attributes; + +namespace xEventService.Constants +{ + public enum XZeroMQPattern + { + [StringValue(XEventServiceConstants.None)] + None, + + [StringValue(XEventServiceConstants.PubSub)] + PubSub, + + [StringValue(XEventServiceConstants.PushPull)] + PushPull + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\DI\XDIHelperExtension.cs + +using System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Providers; +using xExceptions.Constants; + +namespace xEventService.DI +{ + public static partial class XDIHelperExtension + { + /// + /// Extract EventService Configurations ... + /// + /// + /// + public static XEventServiceConfiguration GetXEventServiceConfiguration( + this IConfiguration source + ) + { + // + var configSection = source + .GetSection(ConfigurationNodeNames.EVENT_SERVICE_NODE_NAME); + var result = configSection.Get(); + + // + return result; + } + + /// + /// Register Event Service Configurations ... + /// + /// + /// + public static void AddXEventServiceConfiguration( + this IServiceCollection services, + IConfiguration configuration + ) + { + // + var config = configuration.GetXEventServiceConfiguration(); + services.AddXEventServiceConfiguration(config); + } + + /// + /// Register Event Service Configurations ... + /// + /// + /// + public static void AddXEventServiceConfiguration( + this IServiceCollection services, + XEventServiceConfiguration configuration + ) + { + // + // Validate ... + var isValid = configuration.IsValid() && + (configuration.Broker == XEventBroker.XKafka || + (configuration.Broker == XEventBroker.XZeroMQ && + configuration.ZeroMQ.IsValid()) || + (configuration.Broker == XEventBroker.XRabbitMQ && + configuration.RabbitMQ.IsValid())); + if (!isValid) + { + // + Log("Registration Failed, due Invalid Configuration ..."); + XException.InvalidConfiguration.Throw(); + } + + // + // Register Configuration as Singleton ... + services.AddSingleton(configuration); + } + + /// + /// Register Service ... + /// + /// + /// + /// + public static void AddXEventService( + this IServiceCollection source, + IConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + var config = configuration.GetXEventServiceConfiguration(); + source.AddEventService( + lifeTime: lifeTime, + configuration: config + ); + } + + /// + /// Register Service ... + /// + /// + /// + /// + public static void AddXEventService( + this IServiceCollection source, + XEventServiceConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + source.AddEventService( + lifeTime: lifeTime, + configuration: configuration + ); + } + + /// + /// Event Consumer Service Registration ... + /// + /// + /// + public static void AddXEventServiceConsumer( + this IServiceCollection source + ) where TConsumer : XBaseEventConsumerBackgroundService + { + // + var config = source.GetRegisteredService(); + if (config.IsNullOrDefault()) + { + // + Log("Consumer Registration Faild, due Invalid Configuration issue ..."); + XException.InvalidConfiguration.Throw(); + } + + // + var consumerType = typeof(TConsumer); + switch (config.Broker) + { + // + case XEventBroker.XKafka: + // + if (!typeof(XKafkaConsumerServiceBase).IsAssignableFrom(consumerType)) + { + // + Log($"Consumer Registration Failed. TConsumer must inherit from XKafkaConsumerServiceBase when Broker is {config.Broker.GetStringValue()} ..."); + XException.InvalidConfiguration.Throw(); + } + break; + + // + case XEventBroker.XZeroMQ: + if (!typeof(XZeroMQConsumerServiceBase).IsAssignableFrom(consumerType)) + { + Log($"Consumer Registration Failed. TConsumer must inherit from XZeroMQConsumerServiceBase when Broker is {config.Broker.GetStringValue()} ..."); + XException.InvalidConfiguration.Throw(); + } + break; + + // + case XEventBroker.XRabbitMQ: + if (!typeof(XRabbitMQConsumerServiceBase).IsAssignableFrom(consumerType)) + { + Log($"Consumer Registration Failed. TConsumer must inherit from XRabbitMQConsumerServiceBase when Broker is {config.Broker.GetStringValue()} ..."); + XException.InvalidConfiguration.Throw(); + } + break; + } + + // + source.AddXBackgroundService(); + + // + Log($"Consumer Registered Successfully: {consumerType.Name} for Broker: {config.Broker.GetStringValue()} ..."); + } + + // + #region Private ... + /// + /// a LogTag for Service ... + /// + private static string XLogTag = XEventServiceConstants.XEventServiceDILogTag; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + + /// + /// Register Event Service ... + /// + /// + /// + /// + private static void AddEventService( + this IServiceCollection source, + XEventServiceConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + // Register Configuration ... + source.AddXEventServiceConfiguration(configuration); + + // + // Register based on Broker ... + switch (configuration.Broker) + { + // + case XEventBroker.XKafka: + source.Add( + new ServiceDescriptor(typeof(IXKafkaProducerService), typeof(XKafkaProducerService), lifeTime) + ); + break; + + // + case XEventBroker.XZeroMQ: + source.Add( + new ServiceDescriptor(typeof(IXZeroMQProducerService), typeof(XZeroMQProducerService), lifeTime) + ); + break; + + // + case XEventBroker.XRabbitMQ: + source.Add( + new ServiceDescriptor(typeof(IXRabbitMQProducerService), typeof(XRabbitMQProducerService), lifeTime) + ); + break; + } + + // + source.Add( + new ServiceDescriptor(typeof(IXEventServiceProvider), typeof(XEventServiceProvider), lifeTime) + ); + + // + Log($"Service Registered Sucessfull, Provider: {configuration.Broker.GetStringValue()}"); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Extensions\XEventServiceExtensions.cs + +using System.Linq; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Extensions +{ + /// + /// Extensions for Event Service ... + /// + public static class XEventServiceExtensions + { + /// + /// Validate Event Broker ... + /// + /// + /// + public static bool IsValid(this XEventBroker source) + { + // + var result = + !source.IsNull() && + source != XEventBroker.None; + + // + return result; + } + + /// + /// Validate Event Service Configuration ... + /// + /// + /// + public static bool IsValid(this XEventServiceConfiguration source) + { + // + var result = + !source.IsNullOrDefault() && + source.Broker.IsValid() && + !source.Url.IsNullOrEmpty() && + source.Url.IsValidUrl(); + + // + return result; + } + + /// + /// Validate RabbitMQ Exchange Type ... + /// + /// + /// + public static bool IsValid(this XRabbitMQExchangeType source) + { + // + var result = source != XRabbitMQExchangeType.None; + + // + return result; + } + + /// + /// Validate RabbitMQ Configuration ... + /// + /// + /// + public static bool IsValid(this XRabbitMQOptions source) + { + // + var result = + !source.IsNullOrDefault() && + !source.Exchange.IsNullOrEmpty() && + source.ExchangeType.IsValid(); + + // + return result; + } + + /// + /// Validate an Event Message ... + /// + /// + /// + public static bool IsValid(this XEventMessage source) + { + // + var result = + !source.IsNullOrDefault() && + !source.Action.IsNullOrEmpty() && + !source.Sender.IsNullOrEmpty(); + + // + return result; + } + + /// + /// Validate an Event Request ... + /// + /// + /// + public static bool IsValid(this XEventRequest source) + { + // + var result = + !source.IsNullOrDefault() && + !source.Topic.IsNullOrEmpty() && + source.Message.IsValid(); + + // + return result; + } + + /// + /// Validate ZeroMQ Pattern ... + /// + /// + /// + public static bool IsValid(this XZeroMQPattern source) + { + // + var result = source != XZeroMQPattern.None; + + // + return result; + } + + /// + /// Validate ZeroMQ Options ... + /// + /// + /// + public static bool IsValid(this XZeroMQOptions source) + { + // + var result = + !source.IsNullOrDefault() && + source.Pattern.IsValid(); + + // + return result; + } + + /// + /// Validate an Array is Wildcard passed or not ... + /// + /// + /// + public static bool IsAllowedWildcard(this string[] source) + { + // + var result = + source is not null && + source.Length == 1 && + source.Any(s => + s == XEventServiceConstants.XWildcard); + + // + return result; + } + + /// + /// Validate a Collection is Valid for Kafka Consumer ... + /// + /// + /// + public static bool IsAllowedCollection( + this string[] source + ) + { + // + var result = source.HasChild(); + + // + return result; + } + + /// + /// Validate Sender ... + /// + /// + /// + /// + public static bool IsValidSender( + this XEventMessage source, + string[] allowedSenders + ) + { + // + // Validate Source and Collection ... + var result = + source.IsValid() && + allowedSenders + .IsAllowedCollection(); + if (!result) + { + return result; + } + + // + // Validate Sender ... + result = + allowedSenders.IsAllowedWildcard() || + allowedSenders.Any(x => + x.Equals( + source.Sender, + System.StringComparison.InvariantCultureIgnoreCase)); + + // + return result; + } + + /// + /// Validate Action ... + /// + /// + /// + /// + public static bool IsValidAction( + this XEventMessage source, + string[] allowedActions + ) + { + // + // Validate Source and Collection ... + var result = + source.IsValid() && + allowedActions + .IsAllowedCollection(); + if (!result) + { + return result; + } + + // + // Validate Action ... + result = + allowedActions.IsAllowedWildcard() || + allowedActions.Any(x => + x.Equals( + source.Action, + System.StringComparison.InvariantCultureIgnoreCase)); + + // + return result; + } + + /// + /// Completely Validate a Message, based on providing: + /// - Allowed Actions + /// - Allowed Senders + /// + /// + /// + /// + /// + public static bool IsValid( + this XEventMessage source, + string[] allowedSenders, + string[] allowedActions + ) + { + // + var result = + source.IsValid() && + source.IsValidAction(allowedActions) && + source.IsValidSender(allowedSenders); + + // + return result; + } + + /// + /// Parse Url and Extract Host address ... + /// + /// + /// + public static string GetRabbitMQHost(this XEventServiceConfiguration source) + { + // + var result = string.Empty; + + // + if (!source.IsValid()) + { + XException.InvalidConfiguration.Throw(); + } + + // + var parts = source.Url.Split(':'); + if (parts.Length != 2) + { + XException.InvalidConfiguration.Throw(); + } + + // + result = parts[0]; + if (result.IsNullOrEmpty() || + !result.IsValidUrl() + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + return result; + } + + /// + /// Parse Url and Extract Port Number ... + /// + /// + /// + public static int GetRabbitMQPort(this XEventServiceConfiguration source) + { + // + int result = -1; + + // + if (!source.IsValid()) + { + XException.InvalidConfiguration.Throw(); + } + + // + var parts = source.Url.Split(':'); + if (parts.Length != 2) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Try Parse Port as Integer ... + int.TryParse(parts[1], out result); + if (result <= 0) + { + XException.InvalidConfiguration.Throw(); + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXEventServiceProvider.cs + +namespace xEventService.Interfaces +{ + public interface IXEventServiceProvider : IXProducerServiceBase + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXKafkaProducerService.cs + +namespace xEventService.Interfaces +{ + /// + /// a Service for Produce Kafka Messages ... + /// + public interface IXKafkaProducerService : IXProducerServiceBase + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXProducerServiceBase.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using xEventService.Constants; +using xEventService.Models; + +namespace xEventService.Interfaces +{ + public interface IXProducerServiceBase : IDisposable + { + /// + /// Sending Message ... + /// + /// an Instance of to Provides Kafka Messaging requirement ... + /// + /// + Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ); + + /// + /// Sending a Message ... + /// + /// an Instance of to Provides Kafka Messaging requirement ... + /// Topic for Sending Message ... + /// + /// + Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXRabbitMQProducerService.cs + +using System.Threading; +using System.Threading.Tasks; +using xEventService.Models; + +namespace xEventService.Interfaces +{ + /// + /// RabbitMQ Producer Service Implementation ... + /// + public interface IXRabbitMQProducerService : IXProducerServiceBase + { + /// + /// Sending a Message to Default Exchange ... + /// + /// + /// an Instance of ... + /// + /// + /// Target Exchange Name. if empty, uses default from configuration ... + /// + /// + /// Routing Key for message routing ... + /// + /// + /// + Task SendMessageAsync( + XEventMessage message, + string exchange = null, + string routingKey = null, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXZeroMQProducerService.cs + +namespace xEventService.Interfaces +{ + public interface IXZeroMQProducerService : IXProducerServiceBase + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XEventMessage.cs + +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace xEventService.Models +{ + /// + /// a Message Descriptor for Events ... + /// + public class XEventMessage + { + /// + /// Specified Action for Fire ... + /// + [JsonRequired] + public string Action { get; set; } + + /// + /// Specified Action Sender ... + /// + [JsonRequired] + public string Sender { get; set; } + + /// + /// Sending Time ... + /// + [JsonRequired] + public DateTime Offset { get; set; } + + /// + /// Specified Message ... + /// + public string Message { get; set; } + + /// + /// Metadata for Message ... + /// + public IDictionary Payload { get; set; } = new Dictionary(); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XEventRequest.cs + +using xEventService.Constants; + +namespace xEventService.Models +{ + /// + /// a Request for Sending an Event ... + /// + public class XEventRequest + { + /// + /// Specified Which Kafka Topic ... + /// + public string Topic { get; set; } = XEventServiceConstants.XDefaultTopic; + + /// + /// Specified Kafka Meesage to Send ... + /// + public XEventMessage Message { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XRabbitMQOptions.cs + +using xEventService.Constants; + +namespace xEventService.Models +{ + /// + /// RabbitMQ-specific connection and messaging options ... + /// + public class XRabbitMQOptions + { + + /// + /// Connection Username ... + /// + public string Username { get; set; } = "guest"; + + /// + /// Connection Password ... + /// + public string Password { get; set; } = "guest"; + + /// + /// Virtual Host ... + /// + public string VirtualHost { get; set; } = "/"; + + /// + /// Default Exchange Name ... + /// + public string Exchange { get; set; } = "x.saherelm.events"; + + /// + /// Default Exchange Type (fanout, direct, topic, headers) ... + /// + public XRabbitMQExchangeType ExchangeType { get; set; } = XRabbitMQExchangeType.Fanout; + + /// + /// Default Queue Name ... + /// if empty, a dynamic queue will be generated ... + /// + public string Queue { get; set; } = string.Empty; + + /// + /// Default Routing Key ... + /// + public string RoutingKey { get; set; } = string.Empty; + + /// + /// Exchange Durable Flag ... + /// + public bool ExchangeDurable { get; set; } = true; + + /// + /// Exchange AutoDelete Flag ... + /// + public bool ExchangeAutoDelete { get; set; } = false; + + /// + /// Queue Durable Flag ... + /// + public bool QueueDurable { get; set; } = true; + + /// + /// Queue Exclusive Flag ... + /// + public bool QueueExclusive { get; set; } = false; + + /// + /// Queue AutoDelete Flag ... + /// + public bool QueueAutoDelete { get; set; } = false; + + /// + /// Enable Automatic Recovery on Connection Loss ... + /// + public bool AutomaticRecoveryEnabled { get; set; } = true; + + /// + /// Network Recovery Interval in Seconds ... + /// + public int NetworkRecoveryIntervalSeconds { get; set; } = 5; + + /// + /// Prefetch Count for Consumer ... + /// + public ushort PrefetchCount { get; set; } = 1; + + /// + /// Enable Manual Acknowledgement ... + /// + public bool ManualAck { get; set; } = true; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XZeroMQOptions.cs + +using xEventService.Constants; + +namespace xEventService.Models +{ + public class XZeroMQOptions + { + /// + /// Socket Pattern (PubSub, PushPull) ... + /// + public XZeroMQPattern Pattern { get; set; } = XZeroMQPattern.PubSub; + + /// + /// Bind or Connect ... + /// True for Server (Bind), False for Client (Connect) ... + /// + public bool IsServer { get; set; } = false; + + /// + /// High Water Mark for Sending ... + /// + public int SendHighWaterMark { get; set; } = 1000; + + /// + /// High Water Mark for Receiving ... + /// + public int ReceiveHighWaterMark { get; set; } = 1000; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XBaseEventConsumerBackgroundService.cs + +using System; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xCommons.Providers; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// Base Event Consumer Service ... + /// + public abstract class XBaseEventConsumerBackgroundService : XResumableBackgroundService + { + /// + /// Consumed Topic Messages ... + /// + protected readonly string topic; + + /// + /// Allowed Senders ... + /// + protected readonly string[] allowedSenders; + + /// + /// Allowed Actions ... + /// + protected readonly string[] allowedActions; + + /// + /// Configuration ... + /// + protected readonly XEventServiceConfiguration configuration; + + protected XBaseEventConsumerBackgroundService( + string[] allowedActions, + string[] allowedSenders, + IServiceProvider serviceProvider, + XEventServiceConfiguration configuration, + ILogger logger, + string topic = XEventServiceConstants.XDefaultTopic + ) : base(serviceProvider, logger) + { + // + // Validate Actions ... + if (!allowedActions + .IsAllowedCollection() || + !allowedSenders + .IsAllowedCollection() || + !configuration.IsValid() + ) + { + XException.InvalidArgs.Throw(); + } + + // + this.topic = topic; + this.configuration = configuration; + this.allowedActions = allowedActions; + this.allowedSenders = allowedSenders; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XBaseEventProducerService.cs + +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Extensions; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + public abstract class XBaseEventProducerService + { + protected readonly XEventServiceConfiguration configuration; + protected readonly ILogger logger; + + protected XBaseEventProducerService( + XEventServiceConfiguration configuration, + ILogger logger + ) + { + // + this.logger = logger; + this.configuration = configuration; + + // + // Validate Configurations ... + if (!configuration.IsValid()) + { + XException.InvalidConfiguration.Throw(); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XEventServiceProvider.cs + +using System.Threading; +using System.Threading.Tasks; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + public class XEventServiceProvider : IXEventServiceProvider + { + // + private readonly XEventServiceConfiguration configuration; + private readonly IXKafkaProducerService kafkaProducerService; + private readonly IXZeroMQProducerService zeroMQProducerService; + private readonly IXRabbitMQProducerService rabbitMQProducerService; + + public XEventServiceProvider( + XEventServiceConfiguration configuration, + IXKafkaProducerService kafkaProducerService = null, + IXZeroMQProducerService zeroMQProducerService = null, + IXRabbitMQProducerService rabbitMQProducerService = null + ) + { + // + this.configuration = configuration; + this.kafkaProducerService = kafkaProducerService; + this.zeroMQProducerService = zeroMQProducerService; + this.rabbitMQProducerService = rabbitMQProducerService; + + // + // Validate ... + var isValid = configuration.IsValid() && + configuration.Broker == XEventBroker.XKafka + ? kafkaProducerService != null + : configuration.Broker == XEventBroker.XZeroMQ + ? zeroMQProducerService != null + : configuration.Broker == XEventBroker.XRabbitMQ + ? rabbitMQProducerService != null + : false; + if (!isValid) + { + XException.InvalidConfiguration.Throw(); + } + } + + /// + /// Send Message to Broker Using XEventRequest instance ... + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + if (!request.IsValid()) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await SendMessageAsync( + topic: request.Topic, + message: request.Message, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Send Message to Broker Using XMessage instance ... + /// + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + if (!message.IsValid()) + { + XException.InvalidArgs.Throw(); + } + + // + var result = false; + switch (configuration.Broker) + { + // + case XEventBroker.XKafka: + result = await kafkaProducerService.SendMessageAsync( + topic: topic, + message: message, + cancellationToken: cancellationToken + ); + break; + + // + case XEventBroker.XZeroMQ: + result = await zeroMQProducerService.SendMessageAsync( + topic: topic, + message: message, + cancellationToken: cancellationToken + ); + break; + + // + case XEventBroker.XRabbitMQ: + result = await rabbitMQProducerService.SendMessageAsync( + topic: topic, + message: message, + cancellationToken: cancellationToken + ); + break; + } + + // + return result; + } + + /// + /// Dispose ... + /// + public void Dispose() + { + // + if (kafkaProducerService != null) + { + kafkaProducerService.Dispose(); + } + + // + if (zeroMQProducerService != null) + { + zeroMQProducerService.Dispose(); + } + + // + if (rabbitMQProducerService != null) + { + rabbitMQProducerService.Dispose(); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XKafkaConsumerServiceBase.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using Confluent.Kafka; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// an Abstract Kafka Consumer Service ... + /// + public abstract class XKafkaConsumerServiceBase : XBaseEventConsumerBackgroundService + { + /// + /// Kafka Consumer Class ... + /// + protected readonly IConsumer consumer; + + protected XKafkaConsumerServiceBase( + string[] allowedActions, + string[] allowedSenders, + IServiceProvider serviceProvider, + XEventServiceConfiguration configuration, + ILogger logger, + string topic = XEventServiceConstants.XDefaultTopic + ) : base( + topic: topic, + logger: logger, + configuration: configuration, + allowedActions: allowedActions, + allowedSenders: allowedSenders, + serviceProvider: serviceProvider + ) + { + // + // Validating Configuration ... + if (configuration.Broker != XEventBroker.XKafka) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Prepare Kafka Consumer Configuration ... + var config = new ConsumerConfig + { + EnableAutoCommit = false, + BootstrapServers = configuration.Url, + AutoOffsetReset = AutoOffsetReset.Earliest, + GroupId = XEventServiceConstants.XDefaultConsumerGroup, + }; + consumer = new ConsumerBuilder(config).Build(); + } + + /// + /// Default Action Execution for Background Services ... + /// + /// + /// + protected override async Task ExecuteAsync( + CancellationToken cancellationToken = default + ) + { + // + // For Fix Blocking Synchronous ... + await Task.Yield(); + using var cts = new CancellationTokenSource(); + + // + // Subscribe to Topic ... + consumer.Subscribe(topic); + + // + // Consuming ... + while (!cancellationToken.IsCancellationRequested) + { + try + { + // + // Consuming Topic ... + var consumeResult = consumer.Consume(cancellationToken); + if (!consumeResult.IsNullOrDefault() && + !consumeResult.Message.IsNullOrDefault() + ) + { + // + var message = consumeResult + .Message + .Value + .FromJSON(); + if (message.IsValid( + allowedSenders: allowedSenders, + allowedActions: allowedActions) + ) + { + // + await DoWorkAsync( + payload: consumeResult.Message, + cancellationToken: cancellationToken + ); + } + } + } + catch (ConsumeException) + { + await Task.Delay(1000, cancellationToken); + } + catch (Exception) + { + await Task.Delay(1000, cancellationToken); + } + } + } + + /// + /// Dispose Object ... + /// + public override void Dispose() + { + // + if (consumer != null) + { + consumer.Close(); + consumer.Dispose(); + } + + // + GC.SuppressFinalize(this); + base.Dispose(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XKafkaProducerService.cs + +using xCommons.Extensions; +using xExceptions.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Configuration; +using xEventService.Models; +using Confluent.Kafka; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; +using System.Threading; +using xEventService.Constants; +using System; + +namespace xEventService.Providers +{ + public class XKafkaProducerService : XBaseEventProducerService, IXKafkaProducerService + { + private readonly IProducer producer; + + public XKafkaProducerService( + ILogger logger, + XEventServiceConfiguration configuration + ) : base(configuration, logger) + { + // + // Validate Configurations ... + if (!configuration.IsValid() || + configuration.Broker != XEventBroker.XKafka + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Prepare Kafka Producer Configuration ... + var config = new ProducerConfig + { + Acks = Acks.All, + BootstrapServers = configuration.Url, + MessageSendMaxRetries = configuration.MaxRetries, + }; + producer = new ProducerBuilder(config).Build(); + } + + /// + /// Sending Message ... + /// + /// an Instance of to Provides Kafka Messaging requirement ... + /// + /// + public async Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = request.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + result = await SendMessageAsync( + message: request.Message, + topic: request.Topic, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Sending a Message ... + /// + /// an Instance of to Provides Kafka Messaging requirement ... + /// Topic for Sending Message ... + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = message.IsValid() && + !topic.IsNullOrEmpty(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + try + { + // + var response = await producer.ProduceAsync( + topic: topic, + message: new Message + { + Value = message + .ToJSON(camelCase: true) + }, + cancellationToken: cancellationToken + ); + + // + result = !response.IsNullOrDefault(); + } + catch + { + result = false; + } + + // + return result; + } + + /// + /// Dispose Required Objects ... + /// + public void Dispose() + { + // + if (producer != null) + { + producer.Dispose(); + } + + // + GC.SuppressFinalize(this); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XRabbitMQConsumerServiceBase.cs + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using RabbitMQ.Client; +using RabbitMQ.Client.Events; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// an Abstract Kafka Consumer Service ... + /// + public abstract class XRabbitMQConsumerServiceBase : XBaseEventConsumerBackgroundService + { + // + protected IModel channel; + protected IConnection connection; + protected AsyncEventingBasicConsumer consumer; + + protected XRabbitMQConsumerServiceBase( + string[] allowedActions, + string[] allowedSenders, + IServiceProvider serviceProvider, + XEventServiceConfiguration configuration, + ILogger logger, + string topic = XEventServiceConstants.XDefaultTopic + ) : base( + topic: topic, + logger: logger, + configuration: configuration, + allowedActions: allowedActions, + allowedSenders: allowedSenders, + serviceProvider: serviceProvider + ) + { + // + // Validating Configuration ... + if (!configuration.RabbitMQ.IsValid() || + configuration.Broker != XEventBroker.XRabbitMQ + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + SetupConnection(); + DeclareTopology(topic); + consumer = new AsyncEventingBasicConsumer(channel); + } + + /// + /// Default Action Execution for Background Services ... + /// + /// + /// + protected override async Task ExecuteAsync( + CancellationToken cancellationToken = default + ) + { + // + // For Fix Blocking Synchronous ... + await Task.Yield(); + using var cts = new CancellationTokenSource(); + + // + // Subscribe to Topic ... + consumer.Received += async (model, ea) => + { + // + try + { + // + var body = ea.Body.ToArray(); + var json = Encoding.UTF8.GetString(body); + var message = json.FromJSON(); + if (!message.IsNullOrDefault() && + message.IsValid( + allowedSenders: allowedSenders, + allowedActions: allowedActions) + ) + { + // + await DoWorkAsync( + payload: message, + cancellationToken: cancellationToken + ); + } + + // + if (configuration.RabbitMQ.ManualAck) + { + // + channel.BasicAck( + multiple: false, + deliveryTag: ea.DeliveryTag + ); + } + } + catch + { + // + if (configuration.RabbitMQ.ManualAck) + { + // + channel.BasicNack( + requeue: true, + multiple: false, + deliveryTag: ea.DeliveryTag + ); + } + } + }; + + // + consumer.Shutdown += (model, ea) => + { + return Task.CompletedTask; + }; + + // + channel.BasicQos( + prefetchSize: 0, + global: false, + prefetchCount: configuration.RabbitMQ.PrefetchCount + ); + + // + channel.BasicConsume( + queue: GetQueueName(), + consumer: consumer, + autoAck: !configuration.RabbitMQ.ManualAck + ); + + // + while (!cancellationToken.IsCancellationRequested) + { + await Task.Delay(1000, cancellationToken); + } + } + + /// + /// Overriding Stop Action ... + /// + /// + /// + public override async Task StopAsync(CancellationToken cancellationToken) + { + // + if (channel != null) + { + // + channel.Close(); + channel.Dispose(); + } + + // + if (connection != null) + { + // + connection.Close(); + connection.Dispose(); + } + + // + await base.StopAsync(cancellationToken); + } + + /// + /// Dispose Implementation ... + /// + public override void Dispose() + { + // + if (channel != null) + { + channel.Dispose(); + } + + // + if (connection != null) + { + connection.Dispose(); + } + + // + GC.SuppressFinalize(this); + base.Dispose(); + } + + // + #region Private ... + private string GetQueueName() + { + // + var result = string.Empty; + if (!configuration.RabbitMQ.Queue.IsNullOrEmpty()) + { + result = configuration.RabbitMQ.Queue; + } + + // + if (result.IsNullOrEmpty()) + { + // + var digits = Guid.NewGuid().ToString().GetDigits(); + result = $"{configuration.RabbitMQ.Exchange}[{digits}]"; + } + + // + return result; + } + + private void SetupConnection() + { + var factory = new ConnectionFactory + { + DispatchConsumersAsync = true, + Port = configuration.GetRabbitMQPort(), + HostName = configuration.GetRabbitMQHost(), + UserName = configuration.RabbitMQ.Username, + Password = configuration.RabbitMQ.Password, + NetworkRecoveryInterval = TimeSpan.FromSeconds( + configuration.RabbitMQ.NetworkRecoveryIntervalSeconds + ), + VirtualHost = configuration.RabbitMQ.VirtualHost, + AutomaticRecoveryEnabled = configuration.RabbitMQ.AutomaticRecoveryEnabled, + }; + + // + connection = factory.CreateConnection(); + channel = connection.CreateModel(); + } + + private void DeclareTopology(string topic) + { + // + channel.ExchangeDeclare( + arguments: null, + exchange: topic ?? configuration.RabbitMQ.Exchange, + durable: configuration.RabbitMQ.ExchangeDurable, + autoDelete: configuration.RabbitMQ.ExchangeAutoDelete, + type: configuration.RabbitMQ.ExchangeType.GetStringValue() + ); + + // + var queueName = GetQueueName(); + + // + channel.QueueDeclare( + arguments: null, + queue: queueName, + durable: configuration.RabbitMQ.QueueDurable, + exclusive: configuration.RabbitMQ.QueueExclusive, + autoDelete: configuration.RabbitMQ.QueueAutoDelete + ); + + // + channel.QueueBind( + arguments: null, + queue: queueName, + exchange: topic ?? configuration.RabbitMQ.Exchange, + routingKey: configuration.RabbitMQ.RoutingKey + ); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XRabbitMQProducerService.cs + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using RabbitMQ.Client; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// RabbitMQ Producer Service Implementation ... + /// + public class XRabbitMQProducerService : XBaseEventProducerService, IXRabbitMQProducerService + { + private IModel channel; + private IConnection connection; + private readonly object connectionLock = new(); + + public XRabbitMQProducerService( + ILogger logger, + XEventServiceConfiguration configuration + ) : base(configuration, logger) + { + // + // Validate Configurations ... + if (!configuration.IsValid() || + !configuration.RabbitMQ.IsValid() || + configuration.Broker != XEventBroker.XRabbitMQ + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Initialize Connection ... + CreateConnection(); + } + + /// + /// Sending Message with Full Request Descriptor ... + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = request.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + result = await SendMessageAsync( + message: request.Message, + exchange: request.Topic, + cancellationToken: cancellationToken, + routingKey: configuration.RabbitMQ.RoutingKey + ); + + // + return result; + } + + /// + /// Sending a Message to Topic ... + /// + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ) + { + // + var result = await SendMessageAsync( + request: new XEventRequest + { + Topic = topic, + Message = message + }, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Sending a Message to Exchange ... + /// + /// + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string exchange = null, + string routingKey = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = message.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize Exchange ... + if (exchange.IsNullOrEmpty()) + { + exchange = configuration.RabbitMQ.Exchange; + } + + // + // Normalize Routing Key ... + if (routingKey.IsNull()) + { + routingKey = configuration.RabbitMQ.RoutingKey; + } + + // + try + { + // + // Ensure Connection is Alive ... + EnsureChannelAsync(); + + // + // Serialize Message ... + var body = Encoding.UTF8.GetBytes( + message.ToJSON(camelCase: true) + ); + + // + // Prepare Message Properties ... + var properties = channel.CreateBasicProperties(); + properties.Persistent = true; + properties.ContentType = "application/json"; + properties.Timestamp = new AmqpTimestamp( + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + ); + properties.MessageId = Guid.NewGuid().ToString(); + properties.Type = message.Action; + properties.AppId = message.Sender; + + // + // Publish Message ... + await Task.Run(() => + { + // + channel.BasicPublish( + exchange: exchange, + routingKey: routingKey, + mandatory: false, + basicProperties: properties, + body: body + ); + }, + cancellationToken: cancellationToken); + + // + result = true; + } + catch + { + result = false; + } + + // + return result; + } + + /// + /// Dispose Object ... + /// + public void Dispose() + { + // + try + { + // + if (channel != null) + { + // + channel.Close(); + channel.Dispose(); + channel = null; + } + + // + if (connection != null) + { + // + connection.Close(); + connection.Dispose(); + connection = null; + } + } + catch + { } + + // + GC.SuppressFinalize(this); + } + + // + #region Private ... + /// + /// Create Connection to RabbitMQ Server ... + /// + private void CreateConnection() + { + // + var factory = new ConnectionFactory + { + Port = configuration.GetRabbitMQPort(), + HostName = configuration.GetRabbitMQHost(), + UserName = configuration.RabbitMQ.Username, + Password = configuration.RabbitMQ.Password, + VirtualHost = configuration.RabbitMQ.VirtualHost, + AutomaticRecoveryEnabled = configuration.RabbitMQ.AutomaticRecoveryEnabled, + NetworkRecoveryInterval = TimeSpan.FromSeconds( + configuration.RabbitMQ.NetworkRecoveryIntervalSeconds + ), + DispatchConsumersAsync = true + }; + + // + connection = factory.CreateConnection(); + } + + /// + /// Check Connection is Alive ... + /// + private bool IsConnected() + { + // + var result = + !connection.IsNullOrDefault() && + connection.IsOpen && + !channel.IsNull() && + channel.IsOpen; + + // + return result; + } + + /// + /// Ensure Channel is Open and Exchange/Queue Declared ... + /// + private void EnsureChannelAsync() + { + // + if (IsConnected()) + { + return; + } + + // + lock (connectionLock) + { + // + // Double Check after Lock ... + if (IsConnected()) + { + return; + } + + // + // Recreate Connection if Dead ... + if (connection is null || !connection.IsOpen) + { + CreateConnection(); + } + + // + // Create Channel ... + channel = connection.CreateModel(); + + // + // Declare Exchange ... + channel.ExchangeDeclare( + arguments: null, + exchange: configuration.RabbitMQ.Exchange, + durable: configuration.RabbitMQ.ExchangeDurable, + autoDelete: configuration.RabbitMQ.ExchangeAutoDelete, + type: configuration.RabbitMQ.ExchangeType.GetStringValue() + ); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XZeroMQConsumerServiceBase.cs + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using NetMQ; +using NetMQ.Sockets; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// an Abstract ZeroMQ Consumer Service ... + /// + public abstract class XZeroMQConsumerServiceBase : XBaseEventConsumerBackgroundService + { + // + private NetMQSocket socket; + + protected XZeroMQConsumerServiceBase( + string[] allowedActions, + string[] allowedSenders, + IServiceProvider serviceProvider, + XEventServiceConfiguration configuration, + ILogger logger, + string topic = XEventServiceConstants.XDefaultTopic + ) : base( + topic: topic, + logger: logger, + configuration: configuration, + allowedActions: allowedActions, + allowedSenders: allowedSenders, + serviceProvider: serviceProvider + ) + { + // + // Validating Configuration ... + if (!configuration.ZeroMQ.IsValid() || + configuration.Broker != XEventBroker.XZeroMQ) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Initialize ... + InitializeSocket(); + } + + /// + /// Default Action Execution for Background Services ... + /// + /// + /// + protected override async Task ExecuteAsync( + CancellationToken cancellationToken = default + ) + { + // + await Task.Yield(); + while (!cancellationToken.IsCancellationRequested) + { + // + try + { + // + string receivedTopic = string.Empty; + byte[] body = null; + + if (configuration.ZeroMQ.Pattern == XZeroMQPattern.PubSub) + { + // + if (socket.TryReceiveFrameString(TimeSpan.FromMilliseconds(100), out receivedTopic)) + { + // + if (socket.TryReceiveFrameBytes(TimeSpan.FromMilliseconds(100), out body)) + { + // Process + } + } + } + else + { + if (socket.TryReceiveFrameBytes(TimeSpan.FromMilliseconds(100), out body)) + { + // Process + } + } + + if (body != null && body.Length > 0) + { + // + var json = Encoding.UTF8.GetString(body); + var message = json.FromJSON(); + if (!message.IsNullOrDefault() && + message.IsValid( + allowedSenders: allowedSenders, + allowedActions: allowedActions) + ) + { + // + await DoWorkAsync( + payload: message, + cancellationToken: cancellationToken + ); + } + } + else + { + await Task.Delay(10, cancellationToken); + } + } + catch (Exception) + { + await Task.Delay(1000, cancellationToken); + } + } + } + + public override async Task StopAsync(CancellationToken cancellationToken) + { + // + if (socket != null) + { + socket.Close(); + } + + // + await base.StopAsync(cancellationToken); + } + + /// + /// Dispose Object ... + /// + public override void Dispose() + { + // + if (socket != null) + { + socket.Dispose(); + } + + // + GC.SuppressFinalize(this); + base.Dispose(); + } + + // + #region Private ... + private void InitializeSocket() + { + // + if (configuration.ZeroMQ.Pattern == XZeroMQPattern.PubSub) + { + // + socket = new SubscriberSocket(); + socket.Options.ReceiveHighWatermark = configuration.ZeroMQ.ReceiveHighWaterMark; + + // + // Subscribe to specific topic or all + if (topic.IsNullOrEmpty() || topic == XEventServiceConstants.XWildcard) + { + ((SubscriberSocket)socket).SubscribeToAnyTopic(); + } + else + { + ((SubscriberSocket)socket).Subscribe(topic); + } + } + else + { + // + socket = new PullSocket(); + socket.Options.ReceiveHighWatermark = configuration.ZeroMQ.ReceiveHighWaterMark; + } + + // + if (configuration.ZeroMQ.IsServer) + { + socket.Bind(configuration.Url); + } + else + { + socket.Connect(configuration.Url); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XZeroMQProducerService.cs + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using NetMQ; +using NetMQ.Sockets; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + public class XZeroMQProducerService : XBaseEventProducerService, IXZeroMQProducerService + { + // + private NetMQSocket socket; + private readonly object socketLock = new(); + + public XZeroMQProducerService( + XEventServiceConfiguration configuration, + ILogger logger + ) : base(configuration, logger) + { + // + // Validate Configurations ... + if (!configuration.IsValid() || + !configuration.ZeroMQ.IsValid() || + configuration.Broker != XEventBroker.XZeroMQ + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + InitializeSocket(); + } + + /// + /// Sending Message with Full Request Descriptor ... + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ) + { + // + var result = request.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + result = await SendMessageAsync( + message: request.Message, + topic: request.Topic, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Sending a Message to Topic ... + /// + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ) + { + // + var result = message.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize Topic ... + if (topic.IsNullOrEmpty()) + { + topic = XEventServiceConstants.XDefaultTopic; + } + + // + try + { + // + var json = message.ToJSON(camelCase: true); + var body = Encoding.UTF8.GetBytes(json); + await Task.Run(() => + { + // + lock (socketLock) + { + // + if (configuration.ZeroMQ.Pattern == XZeroMQPattern.PubSub) + { + socket.SendMoreFrame(topic).SendFrame(body); + } + else + { + socket.SendFrame(body); + } + } + }, cancellationToken); + + // + result = true; + } + catch (Exception) + { + result = false; + } + + // + return result; + } + + /// + /// Dispose Object ... + /// + public void Dispose() + { + // + try + { + // + lock (socketLock) + { + // + if (socket != null) + { + // + socket.Close(); + socket.Dispose(); + socket = null; + } + } + } + catch { } + + // + GC.SuppressFinalize(this); + } + + // + #region Private ... + /// + /// Initialize Socket ... + /// + private void InitializeSocket() + { + // + lock (socketLock) + { + // + if (configuration.ZeroMQ.Pattern == XZeroMQPattern.PubSub) + { + // + socket = new PublisherSocket(); + socket.Options.SendHighWatermark = configuration.ZeroMQ.SendHighWaterMark; + } + else + { + // + socket = new PushSocket(); + socket.Options.SendHighWatermark = configuration.ZeroMQ.SendHighWaterMark; + } + + // + if (configuration.ZeroMQ.IsServer) + { + socket.Bind(configuration.Url); + } + else + { + socket.Connect(configuration.Url); + } + } + } + #endregion + } +} diff --git a/Documents/GeneratedContext/xDashboard-xEventService-20260819_222228.txt b/Documents/GeneratedContext/xDashboard-xEventService-20260819_222228.txt new file mode 100644 index 0000000..995f75a --- /dev/null +++ b/Documents/GeneratedContext/xDashboard-xEventService-20260819_222228.txt @@ -0,0 +1,2586 @@ + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Configuration\XEventServiceConfiguration.cs + +using xEventService.Constants; +using xEventService.Models; + +namespace xEventService.Configuration +{ + /// + /// a configuration class for describing how to connect rabbit mq server + /// and how channels must be declared ... + /// + public class XEventServiceConfiguration + { + /// + /// Message Broker Address ... + /// + public string Url { get; set; } + + /// + /// Max Retries to Sending Message ... + /// + public int MaxRetries { get; set; } = 3; + + /// + /// Allowed Message Broker ... + /// + public XEventBroker Broker { get; set; } = XEventBroker.None; + + /// + /// ZeroMQ Specific Options ... + /// Only used when Broker is XZeroMQ ... + /// + public XZeroMQOptions ZeroMQ { get; set; } = new XZeroMQOptions(); + + /// + /// RabbitMQ Specific Options ... + /// Only used when Broker is XRabbitMQ ... + /// + public XRabbitMQOptions RabbitMQ { get; set; } = new XRabbitMQOptions(); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\ConfigurationNodeNames.cs + +namespace xEventService.Constants +{ + public partial struct ConfigurationNodeNames { + public const string EVENT_SERVICE_NODE_NAME = "EventServiceConfiguration"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\XEventBroker.cs + +using xExceptions.Attributes; + +namespace xEventService.Constants +{ + /// + /// Available Brokers ... + /// + public enum XEventBroker + { + [StringValue(XEventServiceConstants.None)] + None, + + [StringValue(XEventServiceConstants.XKafka)] + XKafka, + + [StringValue(XEventServiceConstants.XZeroMQ)] + XZeroMQ, + + [StringValue(XEventServiceConstants.XRabbitMQ)] + XRabbitMQ, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\XEventServiceConstants.cs + +namespace xEventService.Constants +{ + public struct XEventServiceConstants + { + // + // Service Extensions Log Tag ... + public const string XEventServiceDILogTag = "XEventService"; + + // + // Defaults ... + public const string XDefaultTopic = "XSaherelm"; + public const string XDefaultConsumerGroup = "XSaherElmGroup"; + + // + public const string None = "None"; + public const string XKafka = "XKafka"; + public const string PubSub = "PubSub"; + public const string XZeroMQ = "XZeroMQ"; + public const string PushPull = "PushPull"; + public const string XRabbitMQ = "XRabbitMQ"; + + // + public const string XWildcard = "*"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\XRabbitMQExchangeType.cs + +using xExceptions.Attributes; + +namespace xEventService.Constants +{ + /// + /// Available RabbitMQ Exchange Types ... + /// + public struct XRabbitMQExchangeTypes + { + public const string None = "none"; + public const string Direct = "direct"; + public const string Fanout = "fanout"; + public const string Topic = "topic"; + public const string Headers = "headers"; + } + + /// + /// RabbitMQ Exchange Type Enum ... + /// + public enum XRabbitMQExchangeType + { + [StringValue(XRabbitMQExchangeTypes.None)] + None, + + [StringValue(XRabbitMQExchangeTypes.Direct)] + Direct, + + [StringValue(XRabbitMQExchangeTypes.Fanout)] + Fanout, + + [StringValue(XRabbitMQExchangeTypes.Topic)] + Topic, + + [StringValue(XRabbitMQExchangeTypes.Headers)] + Headers, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\XZeroMQPattern.cs + +using xExceptions.Attributes; + +namespace xEventService.Constants +{ + public enum XZeroMQPattern + { + [StringValue(XEventServiceConstants.None)] + None, + + [StringValue(XEventServiceConstants.PubSub)] + PubSub, + + [StringValue(XEventServiceConstants.PushPull)] + PushPull + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\DI\XDIHelperExtension.cs + +using System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Providers; +using xExceptions.Constants; + +namespace xEventService.DI +{ + public static partial class XDIHelperExtension + { + /// + /// Extract EventService Configurations ... + /// + /// + /// + public static XEventServiceConfiguration GetXEventServiceConfiguration( + this IConfiguration source + ) + { + // + var configSection = source + .GetSection(ConfigurationNodeNames.EVENT_SERVICE_NODE_NAME); + var result = configSection.Get(); + + // + return result; + } + + /// + /// Register Event Service Configurations ... + /// + /// + /// + public static void AddXEventServiceConfiguration( + this IServiceCollection services, + IConfiguration configuration + ) + { + // + var config = configuration.GetXEventServiceConfiguration(); + services.AddXEventServiceConfiguration(config); + } + + /// + /// Register Event Service Configurations ... + /// + /// + /// + public static void AddXEventServiceConfiguration( + this IServiceCollection services, + XEventServiceConfiguration configuration + ) + { + // + // Validate ... + var isValid = configuration.IsValid() && + (configuration.Broker == XEventBroker.XKafka || + (configuration.Broker == XEventBroker.XZeroMQ && + configuration.ZeroMQ.IsValid()) || + (configuration.Broker == XEventBroker.XRabbitMQ && + configuration.RabbitMQ.IsValid())); + if (!isValid) + { + // + Log("Registration Failed, due Invalid Configuration ..."); + XException.InvalidConfiguration.Throw(); + } + + // + // Register Configuration as Singleton ... + services.AddSingleton(configuration); + } + + /// + /// Register Service ... + /// + /// + /// + /// + public static void AddXEventService( + this IServiceCollection source, + IConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + var config = configuration.GetXEventServiceConfiguration(); + source.AddEventService( + lifeTime: lifeTime, + configuration: config + ); + } + + /// + /// Register Service ... + /// + /// + /// + /// + public static void AddXEventService( + this IServiceCollection source, + XEventServiceConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + source.AddEventService( + lifeTime: lifeTime, + configuration: configuration + ); + } + + /// + /// Event Consumer Service Registration ... + /// + /// + /// + public static void AddXEventServiceConsumer( + this IServiceCollection source + ) where TConsumer : XBaseEventConsumerBackgroundService + { + // + var config = source.GetRegisteredService(); + if (config.IsNullOrDefault()) + { + // + Log("Consumer Registration Faild, due Invalid Configuration issue ..."); + XException.InvalidConfiguration.Throw(); + } + + // + var consumerType = typeof(TConsumer); + switch (config.Broker) + { + // + case XEventBroker.XKafka: + // + if (!typeof(XKafkaConsumerServiceBase).IsAssignableFrom(consumerType)) + { + // + Log($"Consumer Registration Failed. TConsumer must inherit from XKafkaConsumerServiceBase when Broker is {config.Broker.GetStringValue()} ..."); + XException.InvalidConfiguration.Throw(); + } + break; + + // + case XEventBroker.XZeroMQ: + if (!typeof(XZeroMQConsumerServiceBase).IsAssignableFrom(consumerType)) + { + Log($"Consumer Registration Failed. TConsumer must inherit from XZeroMQConsumerServiceBase when Broker is {config.Broker.GetStringValue()} ..."); + XException.InvalidConfiguration.Throw(); + } + break; + + // + case XEventBroker.XRabbitMQ: + if (!typeof(XRabbitMQConsumerServiceBase).IsAssignableFrom(consumerType)) + { + Log($"Consumer Registration Failed. TConsumer must inherit from XRabbitMQConsumerServiceBase when Broker is {config.Broker.GetStringValue()} ..."); + XException.InvalidConfiguration.Throw(); + } + break; + } + + // + source.AddXBackgroundService(); + + // + Log($"Consumer Registered Successfully: {consumerType.Name} for Broker: {config.Broker.GetStringValue()} ..."); + } + + // + #region Private ... + /// + /// a LogTag for Service ... + /// + private static string XLogTag = XEventServiceConstants.XEventServiceDILogTag; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + + /// + /// Register Event Service ... + /// + /// + /// + /// + private static void AddEventService( + this IServiceCollection source, + XEventServiceConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + // Register Configuration ... + source.AddXEventServiceConfiguration(configuration); + + // + // Register based on Broker ... + switch (configuration.Broker) + { + // + case XEventBroker.XKafka: + source.Add( + new ServiceDescriptor(typeof(IXKafkaProducerService), typeof(XKafkaProducerService), lifeTime) + ); + break; + + // + case XEventBroker.XZeroMQ: + source.Add( + new ServiceDescriptor(typeof(IXZeroMQProducerService), typeof(XZeroMQProducerService), lifeTime) + ); + break; + + // + case XEventBroker.XRabbitMQ: + source.Add( + new ServiceDescriptor(typeof(IXRabbitMQProducerService), typeof(XRabbitMQProducerService), lifeTime) + ); + break; + } + + // + source.Add( + new ServiceDescriptor(typeof(IXEventServiceProvider), typeof(XEventServiceProvider), lifeTime) + ); + + // + Log($"Service Registered Sucessfull, Provider: {configuration.Broker.GetStringValue()}"); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Extensions\XEventServiceExtensions.cs + +using System.Linq; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Extensions +{ + /// + /// Extensions for Event Service ... + /// + public static class XEventServiceExtensions + { + /// + /// Validate Event Broker ... + /// + /// + /// + public static bool IsValid(this XEventBroker source) + { + // + var result = + !source.IsNull() && + source != XEventBroker.None; + + // + return result; + } + + /// + /// Validate Event Service Configuration ... + /// + /// + /// + public static bool IsValid(this XEventServiceConfiguration source) + { + // + var result = + !source.IsNullOrDefault() && + source.Broker.IsValid() && + !source.Url.IsNullOrEmpty() && + source.Url.IsValidUrl(); + + // + return result; + } + + /// + /// Validate RabbitMQ Exchange Type ... + /// + /// + /// + public static bool IsValid(this XRabbitMQExchangeType source) + { + // + var result = source != XRabbitMQExchangeType.None; + + // + return result; + } + + /// + /// Validate RabbitMQ Configuration ... + /// + /// + /// + public static bool IsValid(this XRabbitMQOptions source) + { + // + var result = + !source.IsNullOrDefault() && + !source.Exchange.IsNullOrEmpty() && + source.ExchangeType.IsValid(); + + // + return result; + } + + /// + /// Validate an Event Message ... + /// + /// + /// + public static bool IsValid(this XEventMessage source) + { + // + var result = + !source.IsNullOrDefault() && + !source.Action.IsNullOrEmpty() && + !source.Sender.IsNullOrEmpty(); + + // + return result; + } + + /// + /// Validate an Event Request ... + /// + /// + /// + public static bool IsValid(this XEventRequest source) + { + // + var result = + !source.IsNullOrDefault() && + !source.Topic.IsNullOrEmpty() && + source.Message.IsValid(); + + // + return result; + } + + /// + /// Validate ZeroMQ Pattern ... + /// + /// + /// + public static bool IsValid(this XZeroMQPattern source) + { + // + var result = source != XZeroMQPattern.None; + + // + return result; + } + + /// + /// Validate ZeroMQ Options ... + /// + /// + /// + public static bool IsValid(this XZeroMQOptions source) + { + // + var result = + !source.IsNullOrDefault() && + source.Pattern.IsValid(); + + // + return result; + } + + /// + /// Validate an Array is Wildcard passed or not ... + /// + /// + /// + public static bool IsAllowedWildcard(this string[] source) + { + // + var result = + source is not null && + source.Length == 1 && + source.Any(s => + s == XEventServiceConstants.XWildcard); + + // + return result; + } + + /// + /// Validate a Collection is Valid for Kafka Consumer ... + /// + /// + /// + public static bool IsAllowedCollection( + this string[] source + ) + { + // + var result = source.HasChild(); + + // + return result; + } + + /// + /// Validate Sender ... + /// + /// + /// + /// + public static bool IsValidSender( + this XEventMessage source, + string[] allowedSenders + ) + { + // + // Validate Source and Collection ... + var result = + source.IsValid() && + allowedSenders + .IsAllowedCollection(); + if (!result) + { + return result; + } + + // + // Validate Sender ... + result = + allowedSenders.IsAllowedWildcard() || + allowedSenders.Any(x => + x.Equals( + source.Sender, + System.StringComparison.InvariantCultureIgnoreCase)); + + // + return result; + } + + /// + /// Validate Action ... + /// + /// + /// + /// + public static bool IsValidAction( + this XEventMessage source, + string[] allowedActions + ) + { + // + // Validate Source and Collection ... + var result = + source.IsValid() && + allowedActions + .IsAllowedCollection(); + if (!result) + { + return result; + } + + // + // Validate Action ... + result = + allowedActions.IsAllowedWildcard() || + allowedActions.Any(x => + x.Equals( + source.Action, + System.StringComparison.InvariantCultureIgnoreCase)); + + // + return result; + } + + /// + /// Completely Validate a Message, based on providing: + /// - Allowed Actions + /// - Allowed Senders + /// + /// + /// + /// + /// + public static bool IsValid( + this XEventMessage source, + string[] allowedSenders, + string[] allowedActions + ) + { + // + var result = + source.IsValid() && + source.IsValidAction(allowedActions) && + source.IsValidSender(allowedSenders); + + // + return result; + } + + /// + /// Parse Url and Extract Host address ... + /// + /// + /// + public static string GetRabbitMQHost(this XEventServiceConfiguration source) + { + // + var result = string.Empty; + + // + if (!source.IsValid()) + { + XException.InvalidConfiguration.Throw(); + } + + // + var parts = source.Url.Split(':'); + if (parts.Length != 2) + { + XException.InvalidConfiguration.Throw(); + } + + // + result = parts[0]; + if (result.IsNullOrEmpty() || + !result.IsValidUrl() + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + return result; + } + + /// + /// Parse Url and Extract Port Number ... + /// + /// + /// + public static int GetRabbitMQPort(this XEventServiceConfiguration source) + { + // + int result = -1; + + // + if (!source.IsValid()) + { + XException.InvalidConfiguration.Throw(); + } + + // + var parts = source.Url.Split(':'); + if (parts.Length != 2) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Try Parse Port as Integer ... + int.TryParse(parts[1], out result); + if (result <= 0) + { + XException.InvalidConfiguration.Throw(); + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXEventServiceProvider.cs + +namespace xEventService.Interfaces +{ + public interface IXEventServiceProvider : IXProducerServiceBase + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXKafkaProducerService.cs + +namespace xEventService.Interfaces +{ + /// + /// a Service for Produce Kafka Messages ... + /// + public interface IXKafkaProducerService : IXProducerServiceBase + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXProducerServiceBase.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using xEventService.Constants; +using xEventService.Models; + +namespace xEventService.Interfaces +{ + public interface IXProducerServiceBase : IDisposable + { + /// + /// Sending Message ... + /// + /// an Instance of to Provides Kafka Messaging requirement ... + /// + /// + Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ); + + /// + /// Sending a Message ... + /// + /// an Instance of to Provides Kafka Messaging requirement ... + /// Topic for Sending Message ... + /// + /// + Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXRabbitMQProducerService.cs + +using System.Threading; +using System.Threading.Tasks; +using xEventService.Models; + +namespace xEventService.Interfaces +{ + /// + /// RabbitMQ Producer Service Implementation ... + /// + public interface IXRabbitMQProducerService : IXProducerServiceBase + { + /// + /// Sending a Message to Default Exchange ... + /// + /// + /// an Instance of ... + /// + /// + /// Target Exchange Name. if empty, uses default from configuration ... + /// + /// + /// Routing Key for message routing ... + /// + /// + /// + Task SendMessageAsync( + XEventMessage message, + string exchange = null, + string routingKey = null, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXZeroMQProducerService.cs + +namespace xEventService.Interfaces +{ + public interface IXZeroMQProducerService : IXProducerServiceBase + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XEventMessage.cs + +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace xEventService.Models +{ + /// + /// a Message Descriptor for Events ... + /// + public class XEventMessage + { + /// + /// Specified Action for Fire ... + /// + [JsonRequired] + public string Action { get; set; } + + /// + /// Specified Action Sender ... + /// + [JsonRequired] + public string Sender { get; set; } + + /// + /// Sending Time ... + /// + [JsonRequired] + public DateTime Offset { get; set; } + + /// + /// Specified Message ... + /// + public string Message { get; set; } + + /// + /// Metadata for Message ... + /// + public IDictionary Payload { get; set; } = new Dictionary(); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XEventRequest.cs + +using xEventService.Constants; + +namespace xEventService.Models +{ + /// + /// a Request for Sending an Event ... + /// + public class XEventRequest + { + /// + /// Specified Which Kafka Topic ... + /// + public string Topic { get; set; } = XEventServiceConstants.XDefaultTopic; + + /// + /// Specified Kafka Meesage to Send ... + /// + public XEventMessage Message { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XRabbitMQOptions.cs + +using xEventService.Constants; + +namespace xEventService.Models +{ + /// + /// RabbitMQ-specific connection and messaging options ... + /// + public class XRabbitMQOptions + { + + /// + /// Connection Username ... + /// + public string Username { get; set; } = "guest"; + + /// + /// Connection Password ... + /// + public string Password { get; set; } = "guest"; + + /// + /// Virtual Host ... + /// + public string VirtualHost { get; set; } = "/"; + + /// + /// Default Exchange Name ... + /// + public string Exchange { get; set; } = "x.saherelm.events"; + + /// + /// Default Exchange Type (fanout, direct, topic, headers) ... + /// + public XRabbitMQExchangeType ExchangeType { get; set; } = XRabbitMQExchangeType.Fanout; + + /// + /// Default Queue Name ... + /// if empty, a dynamic queue will be generated ... + /// + public string Queue { get; set; } = string.Empty; + + /// + /// Default Routing Key ... + /// + public string RoutingKey { get; set; } = string.Empty; + + /// + /// Exchange Durable Flag ... + /// + public bool ExchangeDurable { get; set; } = true; + + /// + /// Exchange AutoDelete Flag ... + /// + public bool ExchangeAutoDelete { get; set; } = false; + + /// + /// Queue Durable Flag ... + /// + public bool QueueDurable { get; set; } = true; + + /// + /// Queue Exclusive Flag ... + /// + public bool QueueExclusive { get; set; } = false; + + /// + /// Queue AutoDelete Flag ... + /// + public bool QueueAutoDelete { get; set; } = false; + + /// + /// Enable Automatic Recovery on Connection Loss ... + /// + public bool AutomaticRecoveryEnabled { get; set; } = true; + + /// + /// Network Recovery Interval in Seconds ... + /// + public int NetworkRecoveryIntervalSeconds { get; set; } = 5; + + /// + /// Prefetch Count for Consumer ... + /// + public ushort PrefetchCount { get; set; } = 1; + + /// + /// Enable Manual Acknowledgement ... + /// + public bool ManualAck { get; set; } = true; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XZeroMQOptions.cs + +using xEventService.Constants; + +namespace xEventService.Models +{ + public class XZeroMQOptions + { + /// + /// Socket Pattern (PubSub, PushPull) ... + /// + public XZeroMQPattern Pattern { get; set; } = XZeroMQPattern.PubSub; + + /// + /// Bind or Connect ... + /// True for Server (Bind), False for Client (Connect) ... + /// + public bool IsServer { get; set; } = false; + + /// + /// High Water Mark for Sending ... + /// + public int SendHighWaterMark { get; set; } = 1000; + + /// + /// High Water Mark for Receiving ... + /// + public int ReceiveHighWaterMark { get; set; } = 1000; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XBaseEventConsumerBackgroundService.cs + +using System; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xCommons.Providers; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// Base Event Consumer Service ... + /// + public abstract class XBaseEventConsumerBackgroundService : XResumableBackgroundService + { + /// + /// Consumed Topic Messages ... + /// + protected readonly string topic; + + /// + /// Allowed Senders ... + /// + protected readonly string[] allowedSenders; + + /// + /// Allowed Actions ... + /// + protected readonly string[] allowedActions; + + /// + /// Configuration ... + /// + protected readonly XEventServiceConfiguration configuration; + + protected XBaseEventConsumerBackgroundService( + string[] allowedActions, + string[] allowedSenders, + IServiceProvider serviceProvider, + XEventServiceConfiguration configuration, + ILogger logger, + string topic = XEventServiceConstants.XDefaultTopic + ) : base(serviceProvider, logger) + { + // + // Validate Actions ... + if (!allowedActions + .IsAllowedCollection() || + !allowedSenders + .IsAllowedCollection() || + !configuration.IsValid() + ) + { + XException.InvalidArgs.Throw(); + } + + // + this.topic = topic; + this.configuration = configuration; + this.allowedActions = allowedActions; + this.allowedSenders = allowedSenders; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XBaseEventProducerService.cs + +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Extensions; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + public abstract class XBaseEventProducerService + { + protected readonly XEventServiceConfiguration configuration; + protected readonly ILogger logger; + + protected XBaseEventProducerService( + XEventServiceConfiguration configuration, + ILogger logger + ) + { + // + this.logger = logger; + this.configuration = configuration; + + // + // Validate Configurations ... + if (!configuration.IsValid()) + { + XException.InvalidConfiguration.Throw(); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XEventServiceProvider.cs + +using System.Threading; +using System.Threading.Tasks; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + public class XEventServiceProvider : IXEventServiceProvider + { + // + private readonly XEventServiceConfiguration configuration; + private readonly IXKafkaProducerService kafkaProducerService; + private readonly IXZeroMQProducerService zeroMQProducerService; + private readonly IXRabbitMQProducerService rabbitMQProducerService; + + public XEventServiceProvider( + XEventServiceConfiguration configuration, + IXKafkaProducerService kafkaProducerService = null, + IXZeroMQProducerService zeroMQProducerService = null, + IXRabbitMQProducerService rabbitMQProducerService = null + ) + { + // + this.configuration = configuration; + this.kafkaProducerService = kafkaProducerService; + this.zeroMQProducerService = zeroMQProducerService; + this.rabbitMQProducerService = rabbitMQProducerService; + + // + // Validate ... + var isValid = configuration.IsValid() && + configuration.Broker == XEventBroker.XKafka + ? kafkaProducerService != null + : configuration.Broker == XEventBroker.XZeroMQ + ? zeroMQProducerService != null + : configuration.Broker == XEventBroker.XRabbitMQ + ? rabbitMQProducerService != null + : false; + if (!isValid) + { + XException.InvalidConfiguration.Throw(); + } + } + + /// + /// Send Message to Broker Using XEventRequest instance ... + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + if (!request.IsValid()) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await SendMessageAsync( + topic: request.Topic, + message: request.Message, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Send Message to Broker Using XMessage instance ... + /// + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + if (!message.IsValid()) + { + XException.InvalidArgs.Throw(); + } + + // + var result = false; + switch (configuration.Broker) + { + // + case XEventBroker.XKafka: + result = await kafkaProducerService.SendMessageAsync( + topic: topic, + message: message, + cancellationToken: cancellationToken + ); + break; + + // + case XEventBroker.XZeroMQ: + result = await zeroMQProducerService.SendMessageAsync( + topic: topic, + message: message, + cancellationToken: cancellationToken + ); + break; + + // + case XEventBroker.XRabbitMQ: + result = await rabbitMQProducerService.SendMessageAsync( + topic: topic, + message: message, + cancellationToken: cancellationToken + ); + break; + } + + // + return result; + } + + /// + /// Dispose ... + /// + public void Dispose() + { + // + if (kafkaProducerService != null) + { + kafkaProducerService.Dispose(); + } + + // + if (zeroMQProducerService != null) + { + zeroMQProducerService.Dispose(); + } + + // + if (rabbitMQProducerService != null) + { + rabbitMQProducerService.Dispose(); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XKafkaConsumerServiceBase.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using Confluent.Kafka; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// an Abstract Kafka Consumer Service ... + /// + public abstract class XKafkaConsumerServiceBase : XBaseEventConsumerBackgroundService + { + /// + /// Kafka Consumer Class ... + /// + protected readonly IConsumer consumer; + + protected XKafkaConsumerServiceBase( + string[] allowedActions, + string[] allowedSenders, + IServiceProvider serviceProvider, + XEventServiceConfiguration configuration, + ILogger logger, + string topic = XEventServiceConstants.XDefaultTopic + ) : base( + topic: topic, + logger: logger, + configuration: configuration, + allowedActions: allowedActions, + allowedSenders: allowedSenders, + serviceProvider: serviceProvider + ) + { + // + // Validating Configuration ... + if (configuration.Broker != XEventBroker.XKafka) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Prepare Kafka Consumer Configuration ... + var config = new ConsumerConfig + { + EnableAutoCommit = false, + BootstrapServers = configuration.Url, + AutoOffsetReset = AutoOffsetReset.Earliest, + GroupId = XEventServiceConstants.XDefaultConsumerGroup, + }; + consumer = new ConsumerBuilder(config).Build(); + } + + /// + /// Default Action Execution for Background Services ... + /// + /// + /// + protected override async Task ExecuteAsync( + CancellationToken cancellationToken = default + ) + { + // + // For Fix Blocking Synchronous ... + await Task.Yield(); + using var cts = new CancellationTokenSource(); + + // + // Subscribe to Topic ... + consumer.Subscribe(topic); + + // + // Consuming ... + while (!cancellationToken.IsCancellationRequested) + { + try + { + // + // Consuming Topic ... + var consumeResult = consumer.Consume(cancellationToken); + if (!consumeResult.IsNullOrDefault() && + !consumeResult.Message.IsNullOrDefault() + ) + { + // + var message = consumeResult + .Message + .Value + .FromJSON(); + if (message.IsValid( + allowedSenders: allowedSenders, + allowedActions: allowedActions) + ) + { + // + await DoWorkAsync( + payload: message, + cancellationToken: cancellationToken + ); + } + } + } + catch (ConsumeException) + { + await Task.Delay(1000, cancellationToken); + } + catch (Exception) + { + await Task.Delay(1000, cancellationToken); + } + } + } + + /// + /// Dispose Object ... + /// + public override void Dispose() + { + // + if (consumer != null) + { + consumer.Close(); + consumer.Dispose(); + } + + // + GC.SuppressFinalize(this); + base.Dispose(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XKafkaProducerService.cs + +using xCommons.Extensions; +using xExceptions.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Configuration; +using xEventService.Models; +using Confluent.Kafka; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; +using System.Threading; +using xEventService.Constants; +using System; + +namespace xEventService.Providers +{ + public class XKafkaProducerService : XBaseEventProducerService, IXKafkaProducerService + { + private readonly IProducer producer; + + public XKafkaProducerService( + ILogger logger, + XEventServiceConfiguration configuration + ) : base(configuration, logger) + { + // + // Validate Configurations ... + if (!configuration.IsValid() || + configuration.Broker != XEventBroker.XKafka + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Prepare Kafka Producer Configuration ... + var config = new ProducerConfig + { + Acks = Acks.All, + BootstrapServers = configuration.Url, + MessageSendMaxRetries = configuration.MaxRetries, + }; + producer = new ProducerBuilder(config).Build(); + } + + /// + /// Sending Message ... + /// + /// an Instance of to Provides Kafka Messaging requirement ... + /// + /// + public async Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = request.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + result = await SendMessageAsync( + message: request.Message, + topic: request.Topic, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Sending a Message ... + /// + /// an Instance of to Provides Kafka Messaging requirement ... + /// Topic for Sending Message ... + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = message.IsValid() && + !topic.IsNullOrEmpty(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + try + { + // + var response = await producer.ProduceAsync( + topic: topic, + message: new Message + { + Value = message + .ToJSON(camelCase: true) + }, + cancellationToken: cancellationToken + ); + + // + result = !response.IsNullOrDefault(); + } + catch + { + result = false; + } + + // + return result; + } + + /// + /// Dispose Required Objects ... + /// + public void Dispose() + { + // + if (producer != null) + { + producer.Dispose(); + } + + // + GC.SuppressFinalize(this); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XRabbitMQConsumerServiceBase.cs + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using RabbitMQ.Client; +using RabbitMQ.Client.Events; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// an Abstract Kafka Consumer Service ... + /// + public abstract class XRabbitMQConsumerServiceBase : XBaseEventConsumerBackgroundService + { + // + protected IModel channel; + protected IConnection connection; + protected AsyncEventingBasicConsumer consumer; + + protected XRabbitMQConsumerServiceBase( + string[] allowedActions, + string[] allowedSenders, + IServiceProvider serviceProvider, + XEventServiceConfiguration configuration, + ILogger logger, + string topic = XEventServiceConstants.XDefaultTopic + ) : base( + topic: topic, + logger: logger, + configuration: configuration, + allowedActions: allowedActions, + allowedSenders: allowedSenders, + serviceProvider: serviceProvider + ) + { + // + // Validating Configuration ... + if (!configuration.RabbitMQ.IsValid() || + configuration.Broker != XEventBroker.XRabbitMQ + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + SetupConnection(); + DeclareTopology(topic); + consumer = new AsyncEventingBasicConsumer(channel); + } + + /// + /// Default Action Execution for Background Services ... + /// + /// + /// + protected override async Task ExecuteAsync( + CancellationToken cancellationToken = default + ) + { + // + // For Fix Blocking Synchronous ... + await Task.Yield(); + using var cts = new CancellationTokenSource(); + + // + // Subscribe to Topic ... + consumer.Received += async (model, ea) => + { + // + try + { + // + var body = ea.Body.ToArray(); + var json = Encoding.UTF8.GetString(body); + var message = json.FromJSON(); + if (!message.IsNullOrDefault() && + message.IsValid( + allowedSenders: allowedSenders, + allowedActions: allowedActions) + ) + { + // + await DoWorkAsync( + payload: message, + cancellationToken: cancellationToken + ); + } + + // + if (configuration.RabbitMQ.ManualAck) + { + // + channel.BasicAck( + multiple: false, + deliveryTag: ea.DeliveryTag + ); + } + } + catch + { + // + if (configuration.RabbitMQ.ManualAck) + { + // + channel.BasicNack( + requeue: true, + multiple: false, + deliveryTag: ea.DeliveryTag + ); + } + } + }; + + // + consumer.Shutdown += (model, ea) => + { + return Task.CompletedTask; + }; + + // + channel.BasicQos( + prefetchSize: 0, + global: false, + prefetchCount: configuration.RabbitMQ.PrefetchCount + ); + + // + channel.BasicConsume( + queue: GetQueueName(), + consumer: consumer, + autoAck: !configuration.RabbitMQ.ManualAck + ); + + // + while (!cancellationToken.IsCancellationRequested) + { + await Task.Delay(1000, cancellationToken); + } + } + + /// + /// Overriding Stop Action ... + /// + /// + /// + public override async Task StopAsync(CancellationToken cancellationToken) + { + // + if (channel != null) + { + // + channel.Close(); + channel.Dispose(); + } + + // + if (connection != null) + { + // + connection.Close(); + connection.Dispose(); + } + + // + await base.StopAsync(cancellationToken); + } + + /// + /// Dispose Implementation ... + /// + public override void Dispose() + { + // + if (channel != null) + { + channel.Dispose(); + } + + // + if (connection != null) + { + connection.Dispose(); + } + + // + GC.SuppressFinalize(this); + base.Dispose(); + } + + // + #region Private ... + private string GetQueueName() + { + // + var result = string.Empty; + if (!configuration.RabbitMQ.Queue.IsNullOrEmpty()) + { + result = configuration.RabbitMQ.Queue; + } + + // + if (result.IsNullOrEmpty()) + { + // + var digits = Guid.NewGuid().ToString().GetDigits(); + result = $"{configuration.RabbitMQ.Exchange}[{digits}]"; + } + + // + return result; + } + + private void SetupConnection() + { + var factory = new ConnectionFactory + { + DispatchConsumersAsync = true, + Port = configuration.GetRabbitMQPort(), + HostName = configuration.GetRabbitMQHost(), + UserName = configuration.RabbitMQ.Username, + Password = configuration.RabbitMQ.Password, + NetworkRecoveryInterval = TimeSpan.FromSeconds( + configuration.RabbitMQ.NetworkRecoveryIntervalSeconds + ), + VirtualHost = configuration.RabbitMQ.VirtualHost, + AutomaticRecoveryEnabled = configuration.RabbitMQ.AutomaticRecoveryEnabled, + }; + + // + connection = factory.CreateConnection(); + channel = connection.CreateModel(); + } + + private void DeclareTopology(string topic) + { + // + channel.ExchangeDeclare( + arguments: null, + exchange: topic ?? configuration.RabbitMQ.Exchange, + durable: configuration.RabbitMQ.ExchangeDurable, + autoDelete: configuration.RabbitMQ.ExchangeAutoDelete, + type: configuration.RabbitMQ.ExchangeType.GetStringValue() + ); + + // + var queueName = GetQueueName(); + + // + channel.QueueDeclare( + arguments: null, + queue: queueName, + durable: configuration.RabbitMQ.QueueDurable, + exclusive: configuration.RabbitMQ.QueueExclusive, + autoDelete: configuration.RabbitMQ.QueueAutoDelete + ); + + // + channel.QueueBind( + arguments: null, + queue: queueName, + exchange: topic ?? configuration.RabbitMQ.Exchange, + routingKey: configuration.RabbitMQ.RoutingKey + ); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XRabbitMQProducerService.cs + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using RabbitMQ.Client; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// RabbitMQ Producer Service Implementation ... + /// + public class XRabbitMQProducerService : XBaseEventProducerService, IXRabbitMQProducerService + { + private IModel channel; + private IConnection connection; + private readonly object connectionLock = new(); + + public XRabbitMQProducerService( + ILogger logger, + XEventServiceConfiguration configuration + ) : base(configuration, logger) + { + // + // Validate Configurations ... + if (!configuration.IsValid() || + !configuration.RabbitMQ.IsValid() || + configuration.Broker != XEventBroker.XRabbitMQ + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Initialize Connection ... + CreateConnection(); + } + + /// + /// Sending Message with Full Request Descriptor ... + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = request.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + result = await SendMessageAsync( + message: request.Message, + exchange: request.Topic, + cancellationToken: cancellationToken, + routingKey: configuration.RabbitMQ.RoutingKey + ); + + // + return result; + } + + /// + /// Sending a Message to Topic ... + /// + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ) + { + // + var result = await SendMessageAsync( + request: new XEventRequest + { + Topic = topic, + Message = message + }, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Sending a Message to Exchange ... + /// + /// + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string exchange = null, + string routingKey = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = message.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize Exchange ... + if (exchange.IsNullOrEmpty()) + { + exchange = configuration.RabbitMQ.Exchange; + } + + // + // Normalize Routing Key ... + if (routingKey.IsNull()) + { + routingKey = configuration.RabbitMQ.RoutingKey; + } + + // + try + { + // + // Ensure Connection is Alive ... + EnsureChannel(); + + // + // Serialize Message ... + var body = Encoding.UTF8.GetBytes( + message.ToJSON(camelCase: true) + ); + + // + // Prepare Message Properties ... + var properties = channel.CreateBasicProperties(); + properties.Persistent = true; + properties.ContentType = "application/json"; + properties.Timestamp = new AmqpTimestamp( + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + ); + properties.MessageId = Guid.NewGuid().ToString(); + properties.Type = message.Action; + properties.AppId = message.Sender; + + // + // Publish Message ... + await Task.Run(() => + { + // + channel.BasicPublish( + exchange: exchange, + routingKey: routingKey, + mandatory: false, + basicProperties: properties, + body: body + ); + }, + cancellationToken: cancellationToken); + + // + result = true; + } + catch + { + result = false; + } + + // + return result; + } + + /// + /// Dispose Object ... + /// + public void Dispose() + { + // + try + { + // + if (channel != null) + { + // + channel.Close(); + channel.Dispose(); + channel = null; + } + + // + if (connection != null) + { + // + connection.Close(); + connection.Dispose(); + connection = null; + } + } + catch + { } + + // + GC.SuppressFinalize(this); + } + + // + #region Private ... + /// + /// Create Connection to RabbitMQ Server ... + /// + private void CreateConnection() + { + // + var factory = new ConnectionFactory + { + Port = configuration.GetRabbitMQPort(), + HostName = configuration.GetRabbitMQHost(), + UserName = configuration.RabbitMQ.Username, + Password = configuration.RabbitMQ.Password, + VirtualHost = configuration.RabbitMQ.VirtualHost, + AutomaticRecoveryEnabled = configuration.RabbitMQ.AutomaticRecoveryEnabled, + NetworkRecoveryInterval = TimeSpan.FromSeconds( + configuration.RabbitMQ.NetworkRecoveryIntervalSeconds + ), + DispatchConsumersAsync = true + }; + + // + connection = factory.CreateConnection(); + } + + /// + /// Check Connection is Alive ... + /// + private bool IsConnected() + { + // + var result = + !connection.IsNullOrDefault() && + connection.IsOpen && + !channel.IsNull() && + channel.IsOpen; + + // + return result; + } + + /// + /// Ensure Channel is Open and Exchange/Queue Declared ... + /// + private void EnsureChannel() + { + // + if (IsConnected()) + { + return; + } + + // + lock (connectionLock) + { + // + // Double Check after Lock ... + if (IsConnected()) + { + return; + } + + // + // Recreate Connection if Dead ... + if (connection is null || !connection.IsOpen) + { + CreateConnection(); + } + + // + // Create Channel ... + channel = connection.CreateModel(); + + // + // Declare Exchange ... + channel.ExchangeDeclare( + arguments: null, + exchange: configuration.RabbitMQ.Exchange, + durable: configuration.RabbitMQ.ExchangeDurable, + autoDelete: configuration.RabbitMQ.ExchangeAutoDelete, + type: configuration.RabbitMQ.ExchangeType.GetStringValue() + ); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XZeroMQConsumerServiceBase.cs + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using NetMQ; +using NetMQ.Sockets; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// an Abstract ZeroMQ Consumer Service ... + /// + public abstract class XZeroMQConsumerServiceBase : XBaseEventConsumerBackgroundService + { + // + private NetMQSocket socket; + + protected XZeroMQConsumerServiceBase( + string[] allowedActions, + string[] allowedSenders, + IServiceProvider serviceProvider, + XEventServiceConfiguration configuration, + ILogger logger, + string topic = XEventServiceConstants.XDefaultTopic + ) : base( + topic: topic, + logger: logger, + configuration: configuration, + allowedActions: allowedActions, + allowedSenders: allowedSenders, + serviceProvider: serviceProvider + ) + { + // + // Validating Configuration ... + if (!configuration.ZeroMQ.IsValid() || + configuration.Broker != XEventBroker.XZeroMQ) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Initialize ... + InitializeSocket(); + } + + /// + /// Default Action Execution for Background Services ... + /// + /// + /// + protected override async Task ExecuteAsync( + CancellationToken cancellationToken = default + ) + { + // + await Task.Yield(); + while (!cancellationToken.IsCancellationRequested) + { + // + try + { + // + string receivedTopic = string.Empty; + byte[] body = null; + + if (configuration.ZeroMQ.Pattern == XZeroMQPattern.PubSub) + { + // + if (socket.TryReceiveFrameString(TimeSpan.FromMilliseconds(100), out receivedTopic)) + { + // + if (socket.TryReceiveFrameBytes(TimeSpan.FromMilliseconds(100), out body)) + { + // Process + } + } + } + else + { + if (socket.TryReceiveFrameBytes(TimeSpan.FromMilliseconds(100), out body)) + { + // Process + } + } + + if (body != null && body.Length > 0) + { + // + var json = Encoding.UTF8.GetString(body); + var message = json.FromJSON(); + if (!message.IsNullOrDefault() && + message.IsValid( + allowedSenders: allowedSenders, + allowedActions: allowedActions) + ) + { + // + await DoWorkAsync( + payload: message, + cancellationToken: cancellationToken + ); + } + } + else + { + await Task.Delay(10, cancellationToken); + } + } + catch (Exception) + { + await Task.Delay(1000, cancellationToken); + } + } + } + + public override async Task StopAsync(CancellationToken cancellationToken) + { + // + if (socket != null) + { + socket.Close(); + } + + // + await base.StopAsync(cancellationToken); + } + + /// + /// Dispose Object ... + /// + public override void Dispose() + { + // + if (socket != null) + { + socket.Dispose(); + } + + // + GC.SuppressFinalize(this); + base.Dispose(); + } + + // + #region Private ... + private void InitializeSocket() + { + // + if (configuration.ZeroMQ.Pattern == XZeroMQPattern.PubSub) + { + // + socket = new SubscriberSocket(); + socket.Options.ReceiveHighWatermark = configuration.ZeroMQ.ReceiveHighWaterMark; + + // + // Subscribe to specific topic or all + if (topic.IsNullOrEmpty() || topic == XEventServiceConstants.XWildcard) + { + ((SubscriberSocket)socket).SubscribeToAnyTopic(); + } + else + { + ((SubscriberSocket)socket).Subscribe(topic); + } + } + else + { + // + socket = new PullSocket(); + socket.Options.ReceiveHighWatermark = configuration.ZeroMQ.ReceiveHighWaterMark; + } + + // + if (configuration.ZeroMQ.IsServer) + { + socket.Bind(configuration.Url); + } + else + { + socket.Connect(configuration.Url); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XZeroMQProducerService.cs + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using NetMQ; +using NetMQ.Sockets; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + public class XZeroMQProducerService : XBaseEventProducerService, IXZeroMQProducerService + { + // + private NetMQSocket socket; + private readonly object socketLock = new(); + + public XZeroMQProducerService( + XEventServiceConfiguration configuration, + ILogger logger + ) : base(configuration, logger) + { + // + // Validate Configurations ... + if (!configuration.IsValid() || + !configuration.ZeroMQ.IsValid() || + configuration.Broker != XEventBroker.XZeroMQ + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + InitializeSocket(); + } + + /// + /// Sending Message with Full Request Descriptor ... + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ) + { + // + var result = request.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + result = await SendMessageAsync( + message: request.Message, + topic: request.Topic, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Sending a Message to Topic ... + /// + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ) + { + // + var result = message.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize Topic ... + if (topic.IsNullOrEmpty()) + { + topic = XEventServiceConstants.XDefaultTopic; + } + + // + try + { + // + var json = message.ToJSON(camelCase: true); + var body = Encoding.UTF8.GetBytes(json); + await Task.Run(() => + { + // + lock (socketLock) + { + // + if (configuration.ZeroMQ.Pattern == XZeroMQPattern.PubSub) + { + socket.SendMoreFrame(topic).SendFrame(body); + } + else + { + socket.SendFrame(body); + } + } + }, cancellationToken); + + // + result = true; + } + catch (Exception) + { + result = false; + } + + // + return result; + } + + /// + /// Dispose Object ... + /// + public void Dispose() + { + // + try + { + // + lock (socketLock) + { + // + if (socket != null) + { + // + socket.Close(); + socket.Dispose(); + socket = null; + } + } + } + catch { } + + // + GC.SuppressFinalize(this); + } + + // + #region Private ... + /// + /// Initialize Socket ... + /// + private void InitializeSocket() + { + // + lock (socketLock) + { + // + if (configuration.ZeroMQ.Pattern == XZeroMQPattern.PubSub) + { + // + socket = new PublisherSocket(); + socket.Options.SendHighWatermark = configuration.ZeroMQ.SendHighWaterMark; + } + else + { + // + socket = new PushSocket(); + socket.Options.SendHighWatermark = configuration.ZeroMQ.SendHighWaterMark; + } + + // + if (configuration.ZeroMQ.IsServer) + { + socket.Bind(configuration.Url); + } + else + { + socket.Connect(configuration.Url); + } + } + } + #endregion + } +} diff --git a/Documents/GeneratedContext/xDashboard-xEventService-20260819_223556.txt b/Documents/GeneratedContext/xDashboard-xEventService-20260819_223556.txt new file mode 100644 index 0000000..b4505b2 --- /dev/null +++ b/Documents/GeneratedContext/xDashboard-xEventService-20260819_223556.txt @@ -0,0 +1,2610 @@ + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Configuration\XEventServiceConfiguration.cs + +using xEventService.Constants; +using xEventService.Models; + +namespace xEventService.Configuration +{ + /// + /// a configuration class for describing how to connect rabbit mq server + /// and how channels must be declared ... + /// + public class XEventServiceConfiguration + { + /// + /// Message Broker Address ... + /// + public string Url { get; set; } + + /// + /// Max Retries to Sending Message ... + /// + public int MaxRetries { get; set; } = 3; + + /// + /// Allowed Message Broker ... + /// + public XEventBroker Broker { get; set; } = XEventBroker.None; + + /// + /// ZeroMQ Specific Options ... + /// Only used when Broker is XZeroMQ ... + /// + public XZeroMQOptions ZeroMQ { get; set; } = new XZeroMQOptions(); + + /// + /// RabbitMQ Specific Options ... + /// Only used when Broker is XRabbitMQ ... + /// + public XRabbitMQOptions RabbitMQ { get; set; } = new XRabbitMQOptions(); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\ConfigurationNodeNames.cs + +namespace xEventService.Constants +{ + public partial struct ConfigurationNodeNames { + public const string EVENT_SERVICE_NODE_NAME = "EventServiceConfiguration"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\XEventBroker.cs + +using xExceptions.Attributes; + +namespace xEventService.Constants +{ + /// + /// Available Brokers ... + /// + public enum XEventBroker + { + [StringValue(XEventServiceConstants.None)] + None, + + [StringValue(XEventServiceConstants.XKafka)] + XKafka, + + [StringValue(XEventServiceConstants.XZeroMQ)] + XZeroMQ, + + [StringValue(XEventServiceConstants.XRabbitMQ)] + XRabbitMQ, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\XEventServiceConstants.cs + +namespace xEventService.Constants +{ + public struct XEventServiceConstants + { + // + // Service Extensions Log Tag ... + public const string XEventServiceDILogTag = "XEventService"; + + // + // Defaults ... + public const string XDefaultTopic = "XSaherelm"; + public const string XDefaultConsumerGroup = "XSaherElmGroup"; + + // + public const string None = "None"; + public const string XKafka = "XKafka"; + public const string PubSub = "PubSub"; + public const string XZeroMQ = "XZeroMQ"; + public const string PushPull = "PushPull"; + public const string XRabbitMQ = "XRabbitMQ"; + + // + public const string XWildcard = "*"; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\XRabbitMQExchangeType.cs + +using xExceptions.Attributes; + +namespace xEventService.Constants +{ + /// + /// Available RabbitMQ Exchange Types ... + /// + public struct XRabbitMQExchangeTypes + { + public const string None = "none"; + public const string Direct = "direct"; + public const string Fanout = "fanout"; + public const string Topic = "topic"; + public const string Headers = "headers"; + } + + /// + /// RabbitMQ Exchange Type Enum ... + /// + public enum XRabbitMQExchangeType + { + [StringValue(XRabbitMQExchangeTypes.None)] + None, + + [StringValue(XRabbitMQExchangeTypes.Direct)] + Direct, + + [StringValue(XRabbitMQExchangeTypes.Fanout)] + Fanout, + + [StringValue(XRabbitMQExchangeTypes.Topic)] + Topic, + + [StringValue(XRabbitMQExchangeTypes.Headers)] + Headers, + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Constants\XZeroMQPattern.cs + +using xExceptions.Attributes; + +namespace xEventService.Constants +{ + public enum XZeroMQPattern + { + [StringValue(XEventServiceConstants.None)] + None, + + [StringValue(XEventServiceConstants.PubSub)] + PubSub, + + [StringValue(XEventServiceConstants.PushPull)] + PushPull + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\DI\XDIHelperExtension.cs + +using System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Providers; +using xExceptions.Constants; + +namespace xEventService.DI +{ + public static partial class XDIHelperExtension + { + /// + /// Extract EventService Configurations ... + /// + /// + /// + public static XEventServiceConfiguration GetXEventServiceConfiguration( + this IConfiguration source + ) + { + // + var configSection = source + .GetSection(ConfigurationNodeNames.EVENT_SERVICE_NODE_NAME); + var result = configSection.Get(); + + // + return result; + } + + /// + /// Register Event Service Configurations ... + /// + /// + /// + public static void AddXEventServiceConfiguration( + this IServiceCollection services, + IConfiguration configuration + ) + { + // + var config = configuration.GetXEventServiceConfiguration(); + services.AddXEventServiceConfiguration(config); + } + + /// + /// Register Event Service Configurations ... + /// + /// + /// + public static void AddXEventServiceConfiguration( + this IServiceCollection services, + XEventServiceConfiguration configuration + ) + { + // + // Validate ... + var isValid = configuration.IsValid() && + (configuration.Broker == XEventBroker.XKafka || + (configuration.Broker == XEventBroker.XZeroMQ && + configuration.ZeroMQ.IsValid()) || + (configuration.Broker == XEventBroker.XRabbitMQ && + configuration.RabbitMQ.IsValid())); + if (!isValid) + { + // + Log("Registration Failed, due Invalid Configuration ..."); + XException.InvalidConfiguration.Throw(); + } + + // + // Register Configuration as Singleton ... + services.AddSingleton(configuration); + } + + /// + /// Register Service ... + /// + /// + /// + /// + public static void AddXEventService( + this IServiceCollection source, + IConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + var config = configuration.GetXEventServiceConfiguration(); + source.AddEventService( + lifeTime: lifeTime, + configuration: config + ); + } + + /// + /// Register Service ... + /// + /// + /// + /// + public static void AddXEventService( + this IServiceCollection source, + XEventServiceConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + source.AddEventService( + lifeTime: lifeTime, + configuration: configuration + ); + } + + /// + /// Event Consumer Service Registration ... + /// + /// + /// + public static void AddXEventServiceConsumer( + this IServiceCollection source + ) where TConsumer : XBaseEventConsumerBackgroundService + { + // + var config = source.GetRegisteredService(); + if (config.IsNullOrDefault()) + { + // + Log("Consumer Registration Faild, due Invalid Configuration issue ..."); + XException.InvalidConfiguration.Throw(); + } + + // + var consumerType = typeof(TConsumer); + switch (config.Broker) + { + // + case XEventBroker.XKafka: + // + if (!typeof(XKafkaConsumerServiceBase).IsAssignableFrom(consumerType)) + { + // + Log($"Consumer Registration Failed. TConsumer must inherit from XKafkaConsumerServiceBase when Broker is {config.Broker.GetStringValue()} ..."); + XException.InvalidConfiguration.Throw(); + } + break; + + // + case XEventBroker.XZeroMQ: + if (!typeof(XZeroMQConsumerServiceBase).IsAssignableFrom(consumerType)) + { + Log($"Consumer Registration Failed. TConsumer must inherit from XZeroMQConsumerServiceBase when Broker is {config.Broker.GetStringValue()} ..."); + XException.InvalidConfiguration.Throw(); + } + break; + + // + case XEventBroker.XRabbitMQ: + if (!typeof(XRabbitMQConsumerServiceBase).IsAssignableFrom(consumerType)) + { + Log($"Consumer Registration Failed. TConsumer must inherit from XRabbitMQConsumerServiceBase when Broker is {config.Broker.GetStringValue()} ..."); + XException.InvalidConfiguration.Throw(); + } + break; + } + + // + source.AddXBackgroundService(); + + // + Log($"Consumer Registered Successfully: {consumerType.Name} for Broker: {config.Broker.GetStringValue()} ..."); + } + + // + #region Private ... + /// + /// a LogTag for Service ... + /// + private static string XLogTag = XEventServiceConstants.XEventServiceDILogTag; + + /// + /// print a log in Console ... + /// + /// + private static void Log(string message) + { + Console.WriteLine($"{XLogTag} => {message}"); + } + + /// + /// Register Event Service ... + /// + /// + /// + /// + private static void AddEventService( + this IServiceCollection source, + XEventServiceConfiguration configuration, + ServiceLifetime lifeTime = ServiceLifetime.Scoped + ) + { + // + // Register Configuration ... + source.AddXEventServiceConfiguration(configuration); + + // + // Register based on Broker ... + switch (configuration.Broker) + { + // + case XEventBroker.XKafka: + source.Add( + new ServiceDescriptor(typeof(IXKafkaProducerService), typeof(XKafkaProducerService), lifeTime) + ); + break; + + // + case XEventBroker.XZeroMQ: + source.Add( + new ServiceDescriptor(typeof(IXZeroMQProducerService), typeof(XZeroMQProducerService), lifeTime) + ); + break; + + // + case XEventBroker.XRabbitMQ: + source.Add( + new ServiceDescriptor(typeof(IXRabbitMQProducerService), typeof(XRabbitMQProducerService), lifeTime) + ); + break; + } + + // + source.Add( + new ServiceDescriptor(typeof(IXEventServiceProvider), typeof(XEventServiceProvider), lifeTime) + ); + + // + Log($"Service Registered Sucessfull, Provider: {configuration.Broker.GetStringValue()}"); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Extensions\XEventServiceExtensions.cs + +using System.Linq; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Extensions +{ + /// + /// Extensions for Event Service ... + /// + public static class XEventServiceExtensions + { + /// + /// Validate Event Broker ... + /// + /// + /// + public static bool IsValid(this XEventBroker source) + { + // + var result = + !source.IsNull() && + source != XEventBroker.None; + + // + return result; + } + + /// + /// Validate Event Service Configuration ... + /// + /// + /// + public static bool IsValid(this XEventServiceConfiguration source) + { + // + var result = + !source.IsNullOrDefault() && + source.Broker.IsValid() && + !source.Url.IsNullOrEmpty() && + source.Url.IsValidUrl(); + + // + return result; + } + + /// + /// Validate RabbitMQ Exchange Type ... + /// + /// + /// + public static bool IsValid(this XRabbitMQExchangeType source) + { + // + var result = source != XRabbitMQExchangeType.None; + + // + return result; + } + + /// + /// Validate RabbitMQ Configuration ... + /// + /// + /// + public static bool IsValid(this XRabbitMQOptions source) + { + // + var result = + !source.IsNullOrDefault() && + !source.Exchange.IsNullOrEmpty() && + source.ExchangeType.IsValid(); + + // + return result; + } + + /// + /// Validate an Event Message ... + /// + /// + /// + public static bool IsValid(this XEventMessage source) + { + // + var result = + !source.IsNullOrDefault() && + !source.Action.IsNullOrEmpty() && + !source.Sender.IsNullOrEmpty(); + + // + return result; + } + + /// + /// Validate an Event Request ... + /// + /// + /// + public static bool IsValid(this XEventRequest source) + { + // + var result = + !source.IsNullOrDefault() && + !source.Topic.IsNullOrEmpty() && + source.Message.IsValid(); + + // + return result; + } + + /// + /// Validate ZeroMQ Pattern ... + /// + /// + /// + public static bool IsValid(this XZeroMQPattern source) + { + // + var result = source != XZeroMQPattern.None; + + // + return result; + } + + /// + /// Validate ZeroMQ Options ... + /// + /// + /// + public static bool IsValid(this XZeroMQOptions source) + { + // + var result = + !source.IsNullOrDefault() && + source.Pattern.IsValid(); + + // + return result; + } + + /// + /// Validate an Array is Wildcard passed or not ... + /// + /// + /// + public static bool IsAllowedWildcard(this string[] source) + { + // + var result = + source is not null && + source.Length == 1 && + source.Any(s => + s == XEventServiceConstants.XWildcard); + + // + return result; + } + + /// + /// Validate a Collection is Valid for Kafka Consumer ... + /// + /// + /// + public static bool IsAllowedCollection( + this string[] source + ) + { + // + var result = source.HasChild(); + + // + return result; + } + + /// + /// Validate Sender ... + /// + /// + /// + /// + public static bool IsValidSender( + this XEventMessage source, + string[] allowedSenders + ) + { + // + // Validate Source and Collection ... + var result = + source.IsValid() && + allowedSenders + .IsAllowedCollection(); + if (!result) + { + return result; + } + + // + // Validate Sender ... + result = + allowedSenders.IsAllowedWildcard() || + allowedSenders.Any(x => + x.Equals( + source.Sender, + System.StringComparison.InvariantCultureIgnoreCase)); + + // + return result; + } + + /// + /// Validate Action ... + /// + /// + /// + /// + public static bool IsValidAction( + this XEventMessage source, + string[] allowedActions + ) + { + // + // Validate Source and Collection ... + var result = + source.IsValid() && + allowedActions + .IsAllowedCollection(); + if (!result) + { + return result; + } + + // + // Validate Action ... + result = + allowedActions.IsAllowedWildcard() || + allowedActions.Any(x => + x.Equals( + source.Action, + System.StringComparison.InvariantCultureIgnoreCase)); + + // + return result; + } + + /// + /// Completely Validate a Message, based on providing: + /// - Allowed Actions + /// - Allowed Senders + /// + /// + /// + /// + /// + public static bool IsValid( + this XEventMessage source, + string[] allowedSenders, + string[] allowedActions + ) + { + // + var result = + source.IsValid() && + source.IsValidAction(allowedActions) && + source.IsValidSender(allowedSenders); + + // + return result; + } + + /// + /// Parse Url and Extract Host address ... + /// + /// + /// + public static string GetRabbitMQHost(this XEventServiceConfiguration source) + { + // + var result = string.Empty; + + // + if (!source.IsValid()) + { + XException.InvalidConfiguration.Throw(); + } + + // + var parts = source.Url.Split(':'); + if (parts.Length != 2) + { + XException.InvalidConfiguration.Throw(); + } + + // + result = parts[0]; + if (result.IsNullOrEmpty() || + !result.IsValidUrl() + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + return result; + } + + /// + /// Parse Url and Extract Port Number ... + /// + /// + /// + public static int GetRabbitMQPort(this XEventServiceConfiguration source) + { + // + int result = -1; + + // + if (!source.IsValid()) + { + XException.InvalidConfiguration.Throw(); + } + + // + var parts = source.Url.Split(':'); + if (parts.Length != 2) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Try Parse Port as Integer ... + int.TryParse(parts[1], out result); + if (result <= 0) + { + XException.InvalidConfiguration.Throw(); + } + + // + return result; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXEventServiceProvider.cs + +namespace xEventService.Interfaces +{ + public interface IXEventServiceProvider : IXProducerServiceBase + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXKafkaProducerService.cs + +namespace xEventService.Interfaces +{ + /// + /// a Service for Produce Kafka Messages ... + /// + public interface IXKafkaProducerService : IXProducerServiceBase + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXProducerServiceBase.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using xEventService.Constants; +using xEventService.Models; + +namespace xEventService.Interfaces +{ + public interface IXProducerServiceBase : IDisposable + { + /// + /// Sending Message ... + /// + /// an Instance of to Provides Kafka Messaging requirement ... + /// + /// + Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ); + + /// + /// Sending a Message ... + /// + /// an Instance of to Provides Kafka Messaging requirement ... + /// Topic for Sending Message ... + /// + /// + Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXRabbitMQProducerService.cs + +using System.Threading; +using System.Threading.Tasks; +using xEventService.Models; + +namespace xEventService.Interfaces +{ + /// + /// RabbitMQ Producer Service Implementation ... + /// + public interface IXRabbitMQProducerService : IXProducerServiceBase + { + /// + /// Sending a Message to Default Exchange ... + /// + /// + /// an Instance of ... + /// + /// + /// Target Exchange Name. if empty, uses default from configuration ... + /// + /// + /// Routing Key for message routing ... + /// + /// + /// + Task SendMessageAsync( + XEventMessage message, + string exchange = null, + string routingKey = null, + CancellationToken cancellationToken = default + ); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Interfaces\IXZeroMQProducerService.cs + +namespace xEventService.Interfaces +{ + public interface IXZeroMQProducerService : IXProducerServiceBase + { } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XEventMessage.cs + +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace xEventService.Models +{ + /// + /// a Message Descriptor for Events ... + /// + public class XEventMessage + { + /// + /// Specified Action for Fire ... + /// + [JsonRequired] + public string Action { get; set; } + + /// + /// Specified Action Sender ... + /// + [JsonRequired] + public string Sender { get; set; } + + /// + /// Sending Time ... + /// + [JsonRequired] + public DateTime Offset { get; set; } + + /// + /// Specified Message ... + /// + public string Message { get; set; } + + /// + /// Metadata for Message ... + /// + public IDictionary Payload { get; set; } = new Dictionary(); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XEventRequest.cs + +using xEventService.Constants; + +namespace xEventService.Models +{ + /// + /// a Request for Sending an Event ... + /// + public class XEventRequest + { + /// + /// Specified Which Kafka Topic ... + /// + public string Topic { get; set; } = XEventServiceConstants.XDefaultTopic; + + /// + /// Specified Kafka Meesage to Send ... + /// + public XEventMessage Message { get; set; } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XRabbitMQOptions.cs + +using xEventService.Constants; + +namespace xEventService.Models +{ + /// + /// RabbitMQ-specific connection and messaging options ... + /// + public class XRabbitMQOptions + { + + /// + /// Connection Username ... + /// + public string Username { get; set; } = "guest"; + + /// + /// Connection Password ... + /// + public string Password { get; set; } = "guest"; + + /// + /// Virtual Host ... + /// + public string VirtualHost { get; set; } = "/"; + + /// + /// Default Exchange Name ... + /// + public string Exchange { get; set; } = "x.saherelm.events"; + + /// + /// Default Exchange Type (fanout, direct, topic, headers) ... + /// + public XRabbitMQExchangeType ExchangeType { get; set; } = XRabbitMQExchangeType.Fanout; + + /// + /// Default Queue Name ... + /// if empty, a dynamic queue will be generated ... + /// + public string Queue { get; set; } = string.Empty; + + /// + /// Default Routing Key ... + /// + public string RoutingKey { get; set; } = string.Empty; + + /// + /// Exchange Durable Flag ... + /// + public bool ExchangeDurable { get; set; } = true; + + /// + /// Exchange AutoDelete Flag ... + /// + public bool ExchangeAutoDelete { get; set; } = false; + + /// + /// Queue Durable Flag ... + /// + public bool QueueDurable { get; set; } = true; + + /// + /// Queue Exclusive Flag ... + /// + public bool QueueExclusive { get; set; } = false; + + /// + /// Queue AutoDelete Flag ... + /// + public bool QueueAutoDelete { get; set; } = false; + + /// + /// Enable Automatic Recovery on Connection Loss ... + /// + public bool AutomaticRecoveryEnabled { get; set; } = true; + + /// + /// Network Recovery Interval in Seconds ... + /// + public int NetworkRecoveryIntervalSeconds { get; set; } = 5; + + /// + /// Prefetch Count for Consumer ... + /// + public ushort PrefetchCount { get; set; } = 1; + + /// + /// Enable Manual Acknowledgement ... + /// + public bool ManualAck { get; set; } = true; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Models\XZeroMQOptions.cs + +using xEventService.Constants; + +namespace xEventService.Models +{ + public class XZeroMQOptions + { + /// + /// Socket Pattern (PubSub, PushPull) ... + /// + public XZeroMQPattern Pattern { get; set; } = XZeroMQPattern.PubSub; + + /// + /// Bind or Connect ... + /// True for Server (Bind), False for Client (Connect) ... + /// + public bool IsServer { get; set; } = false; + + /// + /// High Water Mark for Sending ... + /// + public int SendHighWaterMark { get; set; } = 1000; + + /// + /// High Water Mark for Receiving ... + /// + public int ReceiveHighWaterMark { get; set; } = 1000; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XBaseEventConsumerBackgroundService.cs + +using System; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xCommons.Providers; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// Base Event Consumer Service ... + /// + public abstract class XBaseEventConsumerBackgroundService : XResumableBackgroundService + { + /// + /// Consumed Topic Messages ... + /// + protected readonly string topic; + + /// + /// Allowed Senders ... + /// + protected readonly string[] allowedSenders; + + /// + /// Allowed Actions ... + /// + protected readonly string[] allowedActions; + + /// + /// Configuration ... + /// + protected readonly XEventServiceConfiguration configuration; + + protected XBaseEventConsumerBackgroundService( + string[] allowedActions, + string[] allowedSenders, + IServiceProvider serviceProvider, + XEventServiceConfiguration configuration, + ILogger logger, + string topic = XEventServiceConstants.XDefaultTopic + ) : base(serviceProvider, logger) + { + // + // Validate Actions ... + if (!allowedActions + .IsAllowedCollection() || + !allowedSenders + .IsAllowedCollection() || + !configuration.IsValid() + ) + { + XException.InvalidArgs.Throw(); + } + + // + this.topic = topic; + this.configuration = configuration; + this.allowedActions = allowedActions; + this.allowedSenders = allowedSenders; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XBaseEventProducerService.cs + +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Extensions; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + public abstract class XBaseEventProducerService + { + protected readonly XEventServiceConfiguration configuration; + protected readonly ILogger logger; + + protected XBaseEventProducerService( + XEventServiceConfiguration configuration, + ILogger logger + ) + { + // + this.logger = logger; + this.configuration = configuration; + + // + // Validate Configurations ... + if (!configuration.IsValid()) + { + XException.InvalidConfiguration.Throw(); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XEventServiceProvider.cs + +using System.Threading; +using System.Threading.Tasks; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + public class XEventServiceProvider : IXEventServiceProvider + { + // + private readonly XEventServiceConfiguration configuration; + private readonly IXKafkaProducerService kafkaProducerService; + private readonly IXZeroMQProducerService zeroMQProducerService; + private readonly IXRabbitMQProducerService rabbitMQProducerService; + + public XEventServiceProvider( + XEventServiceConfiguration configuration, + IXKafkaProducerService kafkaProducerService = null, + IXZeroMQProducerService zeroMQProducerService = null, + IXRabbitMQProducerService rabbitMQProducerService = null + ) + { + // + this.configuration = configuration; + this.kafkaProducerService = kafkaProducerService; + this.zeroMQProducerService = zeroMQProducerService; + this.rabbitMQProducerService = rabbitMQProducerService; + + // + // Validate ... + var isValid = configuration.IsValid() && + configuration.Broker == XEventBroker.XKafka + ? kafkaProducerService != null + : configuration.Broker == XEventBroker.XZeroMQ + ? zeroMQProducerService != null + : configuration.Broker == XEventBroker.XRabbitMQ + ? rabbitMQProducerService != null + : false; + if (!isValid) + { + XException.InvalidConfiguration.Throw(); + } + } + + /// + /// Send Message to Broker Using XEventRequest instance ... + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + if (!request.IsValid()) + { + XException.InvalidArgs.Throw(); + } + + // + var result = await SendMessageAsync( + topic: request.Topic, + message: request.Message, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Send Message to Broker Using XMessage instance ... + /// + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ) + { + // + // Validate Args ... + if (!message.IsValid()) + { + XException.InvalidArgs.Throw(); + } + + // + var result = false; + switch (configuration.Broker) + { + // + case XEventBroker.XKafka: + result = await kafkaProducerService.SendMessageAsync( + topic: topic, + message: message, + cancellationToken: cancellationToken + ); + break; + + // + case XEventBroker.XZeroMQ: + result = await zeroMQProducerService.SendMessageAsync( + topic: topic, + message: message, + cancellationToken: cancellationToken + ); + break; + + // + case XEventBroker.XRabbitMQ: + result = await rabbitMQProducerService.SendMessageAsync( + topic: topic, + message: message, + cancellationToken: cancellationToken + ); + break; + } + + // + return result; + } + + /// + /// Dispose ... + /// + public void Dispose() + { + // + if (kafkaProducerService != null) + { + kafkaProducerService.Dispose(); + } + + // + if (zeroMQProducerService != null) + { + zeroMQProducerService.Dispose(); + } + + // + if (rabbitMQProducerService != null) + { + rabbitMQProducerService.Dispose(); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XKafkaConsumerServiceBase.cs + +using System; +using System.Threading; +using System.Threading.Tasks; +using Confluent.Kafka; +using Microsoft.Extensions.Logging; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// an Abstract Kafka Consumer Service ... + /// + public abstract class XKafkaConsumerServiceBase : XBaseEventConsumerBackgroundService + { + /// + /// Kafka Consumer Class ... + /// + protected readonly IConsumer consumer; + + protected XKafkaConsumerServiceBase( + string[] allowedActions, + string[] allowedSenders, + IServiceProvider serviceProvider, + XEventServiceConfiguration configuration, + ILogger logger, + string topic = XEventServiceConstants.XDefaultTopic + ) : base( + topic: topic, + logger: logger, + configuration: configuration, + allowedActions: allowedActions, + allowedSenders: allowedSenders, + serviceProvider: serviceProvider + ) + { + // + // Validating Configuration ... + if (configuration.Broker != XEventBroker.XKafka) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Prepare Kafka Consumer Configuration ... + var config = new ConsumerConfig + { + EnableAutoCommit = false, + BootstrapServers = configuration.Url, + AutoOffsetReset = AutoOffsetReset.Earliest, + GroupId = XEventServiceConstants.XDefaultConsumerGroup, + }; + consumer = new ConsumerBuilder(config).Build(); + } + + /// + /// Default Action Execution for Background Services ... + /// + /// + /// + protected override async Task ExecuteAsync( + CancellationToken cancellationToken = default + ) + { + // + // For Fix Blocking Synchronous ... + await Task.Yield(); + using var cts = new CancellationTokenSource(); + + // + // Subscribe to Topic ... + consumer.Subscribe(topic); + logger.LogInformation($"Kafka Cosume Topic: {topic} ..."); + + // + // Consuming ... + while (!cancellationToken.IsCancellationRequested) + { + try + { + // + // Consuming Topic ... + var consumeResult = consumer.Consume(cancellationToken); + if (!consumeResult.IsNullOrDefault() && + !consumeResult.Message.IsNullOrDefault() + ) + { + // + var message = consumeResult + .Message + .Value + .FromJSON(); + if (message.IsValid( + allowedSenders: allowedSenders, + allowedActions: allowedActions) + ) + { + // + await DoWorkAsync( + payload: message, + cancellationToken: cancellationToken + ); + } + } + } + catch (ConsumeException ex) + { + // + logger.LogError($"Kafka Consumer Result Parsing Error: {ex.Message} ..."); + await Task.Delay(1000, cancellationToken); + } + catch (Exception ex) + { + // + logger.LogError($"Kafka Consumer Result Parsing Error: {ex.Message} ..."); + await Task.Delay(1000, cancellationToken); + } + } + } + + /// + /// Dispose Object ... + /// + public override void Dispose() + { + // + if (consumer != null) + { + consumer.Close(); + consumer.Dispose(); + } + + // + GC.SuppressFinalize(this); + base.Dispose(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XKafkaProducerService.cs + +using xCommons.Extensions; +using xExceptions.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Configuration; +using xEventService.Models; +using Confluent.Kafka; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; +using System.Threading; +using xEventService.Constants; +using System; + +namespace xEventService.Providers +{ + public class XKafkaProducerService : XBaseEventProducerService, IXKafkaProducerService + { + private readonly IProducer producer; + + public XKafkaProducerService( + ILogger logger, + XEventServiceConfiguration configuration + ) : base(configuration, logger) + { + // + // Validate Configurations ... + if (!configuration.IsValid() || + configuration.Broker != XEventBroker.XKafka + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Prepare Kafka Producer Configuration ... + var config = new ProducerConfig + { + Acks = Acks.All, + BootstrapServers = configuration.Url, + MessageSendMaxRetries = configuration.MaxRetries, + }; + producer = new ProducerBuilder(config).Build(); + } + + /// + /// Sending Message ... + /// + /// an Instance of to Provides Kafka Messaging requirement ... + /// + /// + public async Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = request.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + result = await SendMessageAsync( + topic: request.Topic, + message: request.Message, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Sending a Message ... + /// + /// an Instance of to Provides Kafka Messaging requirement ... + /// Topic for Sending Message ... + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = message.IsValid() && + !topic.IsNullOrEmpty(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + try + { + // + var response = await producer.ProduceAsync( + topic: topic, + message: new Message + { + Value = message + .ToJSON(camelCase: true) + }, + cancellationToken: cancellationToken + ); + + // + result = !response.IsNullOrDefault(); + } + catch (Exception ex) + { + // + logger.LogError($"Kafka Produce Error: {ex.Message} ..."); + result = false; + } + + // + return result; + } + + /// + /// Dispose Required Objects ... + /// + public void Dispose() + { + // + if (producer != null) + { + producer.Dispose(); + } + + // + GC.SuppressFinalize(this); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XRabbitMQConsumerServiceBase.cs + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using RabbitMQ.Client; +using RabbitMQ.Client.Events; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// an Abstract Kafka Consumer Service ... + /// + public abstract class XRabbitMQConsumerServiceBase : XBaseEventConsumerBackgroundService + { + // + protected IModel channel; + protected IConnection connection; + protected AsyncEventingBasicConsumer consumer; + + protected XRabbitMQConsumerServiceBase( + string[] allowedActions, + string[] allowedSenders, + IServiceProvider serviceProvider, + XEventServiceConfiguration configuration, + ILogger logger, + string topic = XEventServiceConstants.XDefaultTopic + ) : base( + topic: topic, + logger: logger, + configuration: configuration, + allowedActions: allowedActions, + allowedSenders: allowedSenders, + serviceProvider: serviceProvider + ) + { + // + // Validating Configuration ... + if (!configuration.RabbitMQ.IsValid() || + configuration.Broker != XEventBroker.XRabbitMQ + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + SetupConnection(); + DeclareTopology(topic); + consumer = new AsyncEventingBasicConsumer(channel); + } + + /// + /// Default Action Execution for Background Services ... + /// + /// + /// + protected override async Task ExecuteAsync( + CancellationToken cancellationToken = default + ) + { + // + // For Fix Blocking Synchronous ... + await Task.Yield(); + using var cts = new CancellationTokenSource(); + logger.LogInformation($"RabbitMQ Cosume Topic: {topic} ..."); + + // + // Subscribe to Topic ... + consumer.Received += async (model, ea) => + { + // + try + { + // + var body = ea.Body.ToArray(); + var json = Encoding.UTF8.GetString(body); + var message = json.FromJSON(); + if (!message.IsNullOrDefault() && + message.IsValid( + allowedSenders: allowedSenders, + allowedActions: allowedActions) + ) + { + // + await DoWorkAsync( + payload: message, + cancellationToken: cancellationToken + ); + } + + // + if (configuration.RabbitMQ.ManualAck) + { + // + channel.BasicAck( + multiple: false, + deliveryTag: ea.DeliveryTag + ); + } + } + catch (Exception ex) + { + // + logger.LogError($"RabbitMQ Cosume Error: {ex.Message} ..."); + if (configuration.RabbitMQ.ManualAck) + { + // + channel.BasicNack( + requeue: true, + multiple: false, + deliveryTag: ea.DeliveryTag + ); + } + } + }; + + // + consumer.Shutdown += (model, ea) => + { + return Task.CompletedTask; + }; + + // + channel.BasicQos( + prefetchSize: 0, + global: false, + prefetchCount: configuration.RabbitMQ.PrefetchCount + ); + + // + channel.BasicConsume( + queue: GetQueueName(), + consumer: consumer, + autoAck: !configuration.RabbitMQ.ManualAck + ); + + // + while (!cancellationToken.IsCancellationRequested) + { + await Task.Delay(1000, cancellationToken); + } + } + + /// + /// Overriding Stop Action ... + /// + /// + /// + public override async Task StopAsync(CancellationToken cancellationToken) + { + // + if (channel != null) + { + // + channel.Close(); + channel.Dispose(); + } + + // + if (connection != null) + { + // + connection.Close(); + connection.Dispose(); + } + + // + await base.StopAsync(cancellationToken); + } + + /// + /// Dispose Implementation ... + /// + public override void Dispose() + { + // + if (channel != null) + { + channel.Dispose(); + } + + // + if (connection != null) + { + connection.Dispose(); + } + + // + GC.SuppressFinalize(this); + base.Dispose(); + } + + // + #region Private ... + private string GetQueueName() + { + // + var result = string.Empty; + if (!configuration.RabbitMQ.Queue.IsNullOrEmpty()) + { + result = configuration.RabbitMQ.Queue; + } + + // + if (result.IsNullOrEmpty()) + { + // + var digits = Guid.NewGuid().ToString().GetDigits(); + result = $"{configuration.RabbitMQ.Exchange}[{digits}]"; + } + + // + return result; + } + + private void SetupConnection() + { + var factory = new ConnectionFactory + { + DispatchConsumersAsync = true, + Port = configuration.GetRabbitMQPort(), + HostName = configuration.GetRabbitMQHost(), + UserName = configuration.RabbitMQ.Username, + Password = configuration.RabbitMQ.Password, + NetworkRecoveryInterval = TimeSpan.FromSeconds( + configuration.RabbitMQ.NetworkRecoveryIntervalSeconds + ), + VirtualHost = configuration.RabbitMQ.VirtualHost, + AutomaticRecoveryEnabled = configuration.RabbitMQ.AutomaticRecoveryEnabled, + }; + + // + connection = factory.CreateConnection(); + channel = connection.CreateModel(); + } + + private void DeclareTopology(string topic) + { + // + channel.ExchangeDeclare( + arguments: null, + exchange: topic ?? configuration.RabbitMQ.Exchange, + durable: configuration.RabbitMQ.ExchangeDurable, + autoDelete: configuration.RabbitMQ.ExchangeAutoDelete, + type: configuration.RabbitMQ.ExchangeType.GetStringValue() + ); + + // + var queueName = GetQueueName(); + + // + channel.QueueDeclare( + arguments: null, + queue: queueName, + durable: configuration.RabbitMQ.QueueDurable, + exclusive: configuration.RabbitMQ.QueueExclusive, + autoDelete: configuration.RabbitMQ.QueueAutoDelete + ); + + // + channel.QueueBind( + arguments: null, + queue: queueName, + exchange: topic ?? configuration.RabbitMQ.Exchange, + routingKey: configuration.RabbitMQ.RoutingKey + ); + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XRabbitMQProducerService.cs + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using RabbitMQ.Client; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// RabbitMQ Producer Service Implementation ... + /// + public class XRabbitMQProducerService : XBaseEventProducerService, IXRabbitMQProducerService + { + private IModel channel; + private IConnection connection; + private readonly object connectionLock = new(); + + public XRabbitMQProducerService( + ILogger logger, + XEventServiceConfiguration configuration + ) : base(configuration, logger) + { + // + // Validate Configurations ... + if (!configuration.IsValid() || + !configuration.RabbitMQ.IsValid() || + configuration.Broker != XEventBroker.XRabbitMQ + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Initialize Connection ... + CreateConnection(); + } + + /// + /// Sending Message with Full Request Descriptor ... + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = request.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + result = await SendMessageAsync( + message: request.Message, + exchange: request.Topic, + cancellationToken: cancellationToken, + routingKey: configuration.RabbitMQ.RoutingKey + ); + + // + return result; + } + + /// + /// Sending a Message to Topic ... + /// + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ) + { + // + var result = await SendMessageAsync( + request: new XEventRequest + { + Topic = topic, + Message = message + }, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Sending a Message to Exchange ... + /// + /// + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string exchange = null, + string routingKey = null, + CancellationToken cancellationToken = default + ) + { + // + // Validate ... + var result = message.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize Exchange ... + if (exchange.IsNullOrEmpty()) + { + exchange = configuration.RabbitMQ.Exchange; + } + + // + // Normalize Routing Key ... + if (routingKey.IsNull()) + { + routingKey = configuration.RabbitMQ.RoutingKey; + } + + // + try + { + // + // Ensure Connection is Alive ... + EnsureChannel(); + + // + // Serialize Message ... + var body = Encoding.UTF8.GetBytes( + message.ToJSON(camelCase: true) + ); + + // + // Prepare Message Properties ... + var properties = channel.CreateBasicProperties(); + properties.Persistent = true; + properties.ContentType = "application/json"; + properties.Timestamp = new AmqpTimestamp( + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + ); + properties.MessageId = Guid.NewGuid().ToString(); + properties.Type = message.Action; + properties.AppId = message.Sender; + + // + // Publish Message ... + await Task.Run(() => + { + // + channel.BasicPublish( + exchange: exchange, + routingKey: routingKey, + mandatory: false, + basicProperties: properties, + body: body + ); + }, + cancellationToken: cancellationToken); + + // + result = true; + } + catch (Exception ex) + { + // + logger.LogError($"RabbitMQ Produce Error: {ex.Message} ..."); + result = false; + } + + // + return result; + } + + /// + /// Dispose Object ... + /// + public void Dispose() + { + // + try + { + // + if (channel != null) + { + // + channel.Close(); + channel.Dispose(); + channel = null; + } + + // + if (connection != null) + { + // + connection.Close(); + connection.Dispose(); + connection = null; + } + } + catch + { } + + // + GC.SuppressFinalize(this); + } + + // + #region Private ... + /// + /// Create Connection to RabbitMQ Server ... + /// + private void CreateConnection() + { + // + var factory = new ConnectionFactory + { + Port = configuration.GetRabbitMQPort(), + HostName = configuration.GetRabbitMQHost(), + UserName = configuration.RabbitMQ.Username, + Password = configuration.RabbitMQ.Password, + VirtualHost = configuration.RabbitMQ.VirtualHost, + AutomaticRecoveryEnabled = configuration.RabbitMQ.AutomaticRecoveryEnabled, + NetworkRecoveryInterval = TimeSpan.FromSeconds( + configuration.RabbitMQ.NetworkRecoveryIntervalSeconds + ), + DispatchConsumersAsync = true + }; + + // + connection = factory.CreateConnection(); + } + + /// + /// Check Connection is Alive ... + /// + private bool IsConnected() + { + // + var result = + !connection.IsNullOrDefault() && + connection.IsOpen && + !channel.IsNull() && + channel.IsOpen; + + // + return result; + } + + /// + /// Ensure Channel is Open and Exchange/Queue Declared ... + /// + private void EnsureChannel() + { + // + if (IsConnected()) + { + return; + } + + // + lock (connectionLock) + { + // + // Double Check after Lock ... + if (IsConnected()) + { + return; + } + + // + // Recreate Connection if Dead ... + if (connection is null || !connection.IsOpen) + { + CreateConnection(); + } + + // + // Create Channel ... + channel = connection.CreateModel(); + + // + // Declare Exchange ... + channel.ExchangeDeclare( + arguments: null, + exchange: configuration.RabbitMQ.Exchange, + durable: configuration.RabbitMQ.ExchangeDurable, + autoDelete: configuration.RabbitMQ.ExchangeAutoDelete, + type: configuration.RabbitMQ.ExchangeType.GetStringValue() + ); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XZeroMQConsumerServiceBase.cs + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using NetMQ; +using NetMQ.Sockets; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + /// + /// an Abstract ZeroMQ Consumer Service ... + /// + public abstract class XZeroMQConsumerServiceBase : XBaseEventConsumerBackgroundService + { + // + private NetMQSocket socket; + + protected XZeroMQConsumerServiceBase( + string[] allowedActions, + string[] allowedSenders, + IServiceProvider serviceProvider, + XEventServiceConfiguration configuration, + ILogger logger, + string topic = XEventServiceConstants.XDefaultTopic + ) : base( + topic: topic, + logger: logger, + configuration: configuration, + allowedActions: allowedActions, + allowedSenders: allowedSenders, + serviceProvider: serviceProvider + ) + { + // + // Validating Configuration ... + if (!configuration.ZeroMQ.IsValid() || + configuration.Broker != XEventBroker.XZeroMQ) + { + XException.InvalidConfiguration.Throw(); + } + + // + // Initialize ... + InitializeSocket(); + } + + /// + /// Default Action Execution for Background Services ... + /// + /// + /// + protected override async Task ExecuteAsync( + CancellationToken cancellationToken = default + ) + { + // + await Task.Yield(); + logger.LogInformation($"ZeroMQ Cosume Topic: {topic} ..."); + while (!cancellationToken.IsCancellationRequested) + { + // + try + { + // + string receivedTopic = string.Empty; + byte[] body = null; + + // + if (configuration.ZeroMQ.Pattern == XZeroMQPattern.PubSub) + { + // + if (socket.TryReceiveFrameString(TimeSpan.FromMilliseconds(100), out receivedTopic)) + { + // + if (socket.TryReceiveFrameBytes(TimeSpan.FromMilliseconds(100), out body)) + { + // Process + } + else + { + logger.LogError($"ZeroMQ Cosume Error ..."); + } + } + } + else + { + if (socket.TryReceiveFrameBytes(TimeSpan.FromMilliseconds(100), out body)) + { + // Process + } + else + { + logger.LogError($"ZeroMQ Cosume Error ..."); + } + } + + // + if (body != null && body.Length > 0) + { + // + var json = Encoding.UTF8.GetString(body); + var message = json.FromJSON(); + if (!message.IsNullOrDefault() && + message.IsValid( + allowedSenders: allowedSenders, + allowedActions: allowedActions) + ) + { + // + await DoWorkAsync( + payload: message, + cancellationToken: cancellationToken + ); + } + } + else + { + await Task.Delay(10, cancellationToken); + } + } + catch (Exception ex) + { + // + logger.LogError($"ZeroMQ Cosume Error: {ex.Message} ..."); + await Task.Delay(1000, cancellationToken); + } + } + } + + public override async Task StopAsync(CancellationToken cancellationToken) + { + // + if (socket != null) + { + socket.Close(); + } + + // + await base.StopAsync(cancellationToken); + } + + /// + /// Dispose Object ... + /// + public override void Dispose() + { + // + if (socket != null) + { + socket.Dispose(); + } + + // + GC.SuppressFinalize(this); + base.Dispose(); + } + + // + #region Private ... + private void InitializeSocket() + { + // + if (configuration.ZeroMQ.Pattern == XZeroMQPattern.PubSub) + { + // + socket = new SubscriberSocket(); + socket.Options.ReceiveHighWatermark = configuration.ZeroMQ.ReceiveHighWaterMark; + + // + // Subscribe to specific topic or all + if (topic.IsNullOrEmpty() || topic == XEventServiceConstants.XWildcard) + { + ((SubscriberSocket)socket).SubscribeToAnyTopic(); + } + else + { + ((SubscriberSocket)socket).Subscribe(topic); + } + } + else + { + // + socket = new PullSocket(); + socket.Options.ReceiveHighWatermark = configuration.ZeroMQ.ReceiveHighWaterMark; + } + + // + if (configuration.ZeroMQ.IsServer) + { + socket.Bind(configuration.Url); + } + else + { + socket.Connect(configuration.Url); + } + } + #endregion + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\Modules\xEventService\Providers\XZeroMQProducerService.cs + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using NetMQ; +using NetMQ.Sockets; +using xCommons.Extensions; +using xEventService.Configuration; +using xEventService.Constants; +using xEventService.Extensions; +using xEventService.Interfaces; +using xEventService.Models; +using xExceptions.Constants; + +namespace xEventService.Providers +{ + public class XZeroMQProducerService : XBaseEventProducerService, IXZeroMQProducerService + { + // + private NetMQSocket socket; + private readonly object socketLock = new(); + + public XZeroMQProducerService( + XEventServiceConfiguration configuration, + ILogger logger + ) : base(configuration, logger) + { + // + // Validate Configurations ... + if (!configuration.IsValid() || + !configuration.ZeroMQ.IsValid() || + configuration.Broker != XEventBroker.XZeroMQ + ) + { + XException.InvalidConfiguration.Throw(); + } + + // + InitializeSocket(); + } + + /// + /// Sending Message with Full Request Descriptor ... + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventRequest request, + CancellationToken cancellationToken = default + ) + { + // + var result = request.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + result = await SendMessageAsync( + message: request.Message, + topic: request.Topic, + cancellationToken: cancellationToken + ); + + // + return result; + } + + /// + /// Sending a Message to Topic ... + /// + /// + /// + /// + /// + public async Task SendMessageAsync( + XEventMessage message, + string topic = XEventServiceConstants.XDefaultTopic, + CancellationToken cancellationToken = default + ) + { + // + var result = message.IsValid(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Normalize Topic ... + if (topic.IsNullOrEmpty()) + { + topic = XEventServiceConstants.XDefaultTopic; + } + + // + try + { + // + var json = message.ToJSON(camelCase: true); + var body = Encoding.UTF8.GetBytes(json); + await Task.Run(() => + { + // + lock (socketLock) + { + // + if (configuration.ZeroMQ.Pattern == XZeroMQPattern.PubSub) + { + socket.SendMoreFrame(topic).SendFrame(body); + } + else + { + socket.SendFrame(body); + } + } + }, cancellationToken); + + // + result = true; + } + catch (Exception) + { + result = false; + } + + // + return result; + } + + /// + /// Dispose Object ... + /// + public void Dispose() + { + // + try + { + // + lock (socketLock) + { + // + if (socket != null) + { + // + socket.Close(); + socket.Dispose(); + socket = null; + } + } + } + catch { } + + // + GC.SuppressFinalize(this); + } + + // + #region Private ... + /// + /// Initialize Socket ... + /// + private void InitializeSocket() + { + // + lock (socketLock) + { + // + if (configuration.ZeroMQ.Pattern == XZeroMQPattern.PubSub) + { + // + socket = new PublisherSocket(); + socket.Options.SendHighWatermark = configuration.ZeroMQ.SendHighWaterMark; + } + else + { + // + socket = new PushSocket(); + socket.Options.SendHighWatermark = configuration.ZeroMQ.SendHighWaterMark; + } + + // + if (configuration.ZeroMQ.IsServer) + { + socket.Bind(configuration.Url); + } + else + { + socket.Connect(configuration.Url); + } + } + } + #endregion + } +} diff --git a/Documents/GeneratedContext/xDashboard-xFrameworkTest-20260819_225137.txt b/Documents/GeneratedContext/xDashboard-xFrameworkTest-20260819_225137.txt new file mode 100644 index 0000000..4cd05ca --- /dev/null +++ b/Documents/GeneratedContext/xDashboard-xFrameworkTest-20260819_225137.txt @@ -0,0 +1,21434 @@ + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\plugins\cordova-plugin-device\types\index.d.ts + +// Type definitions for Apache Cordova Device plugin +// Project: https://github.com/apache/cordova-plugin-device +// Definitions by: Microsoft Open Technologies Inc +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// +// Copyright (c) Microsoft Open Technologies Inc +// Licensed under the MIT license + +/** + * This plugin defines a global device object, which describes the device's hardware and software. + * Although the object is in the global scope, it is not available until after the deviceready event. + */ +interface Device { + /** Get the version of Cordova running on the device. */ + cordova: string; + /** Indicates that Cordova initialize successfully. */ + available: boolean; + /** + * The device.model returns the name of the device's model or product. The value is set + * by the device manufacturer and may be different across versions of the same product. + */ + model: string; + /** Get the device's operating system name. */ + platform: string; + /** Get the device's Universally Unique Identifier (UUID). */ + uuid: string; + /** Get the operating system version. */ + version: string; + /** Get the device's manufacturer. */ + manufacturer: string; + /** Whether the device is running on a simulator. */ + isVirtual: boolean; + /** Get the device hardware serial number. */ + serial: string;} + +declare var device: Device; + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\plugins\cordova-plugin-splashscreen\src\wp\ResolutionHelper.cs + +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using Microsoft.Phone.Info; +using System; +using System.Windows; + +namespace WPCordovaClassLib.Cordova.Commands +{ + public enum Resolutions { WVGA, WXGA, HD }; + + public static class ResolutionHelper + { + public static Resolutions CurrentResolution + { + get + { + switch (Application.Current.Host.Content.ScaleFactor) + { + case 100: return Resolutions.WVGA; + case 160: return Resolutions.WXGA; + case 150: return Resolutions.HD; + } + throw new InvalidOperationException("Unknown resolution"); + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\plugins\cordova-plugin-splashscreen\src\wp\SplashScreen.cs + +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using Microsoft.Phone.Info; +using System.Windows.Controls.Primitives; +using System.Diagnostics; +using System.Windows.Media.Imaging; +using System.Windows.Resources; +using System.IO; +using System.Xml.Linq; +using System.Linq; +using System.Windows.Threading; + +namespace WPCordovaClassLib.Cordova.Commands +{ + /// + /// Listens for changes to the state of the battery on the device. + /// Currently only the "isPlugged" parameter available via native APIs. + /// + public class SplashScreen : BaseCommand + { + private Popup popup; + + // Time until we dismiss the splashscreen + private int prefDelay = 3000; + + // Whether we hide it by default + private bool prefAutoHide = true; + + // Path to image to use + private string prefImagePath = "SplashScreenImage.jpg"; + + // static because autodismiss is only ever applied once, at app launch + // subsequent page loads should not cause the SplashScreen to be shown. + private static bool WasShown = false; + + public SplashScreen() + { + LoadConfigPrefs(); + + Image SplashScreen = new Image() + { + Height = Application.Current.Host.Content.ActualHeight, + Width = Application.Current.Host.Content.ActualWidth, + Stretch = Stretch.Fill + }; + + var imageResource = GetSplashScreenImageResource(); + if (imageResource != null) + { + BitmapImage splash_image = new BitmapImage(); + splash_image.SetSource(imageResource.Stream); + SplashScreen.Source = splash_image; + } + + // Instansiate the popup and set the Child property of Popup to SplashScreen + popup = new Popup() { IsOpen = false, + Child = SplashScreen, + HorizontalAlignment = HorizontalAlignment.Stretch, + VerticalAlignment = VerticalAlignment.Center + + }; + } + + public override void OnInit() + { + // we only want to autoload on the first page load. + // but OnInit is called for every page load. + if (!SplashScreen.WasShown) + { + SplashScreen.WasShown = true; + show(); + } + } + + private void LoadConfigPrefs() + { + StreamResourceInfo streamInfo = Application.GetResourceStream(new Uri("config.xml", UriKind.Relative)); + if (streamInfo != null) + { + using (StreamReader sr = new StreamReader(streamInfo.Stream)) + { + //This will Read Keys Collection for the xml file + XDocument configFile = XDocument.Parse(sr.ReadToEnd()); + + string configAutoHide = configFile.Descendants() + .Where(x => x.Name.LocalName == "preference") + .Where(x => (string)x.Attribute("name") == "AutoHideSplashScreen") + .Select(x => (string)x.Attribute("value")) + .FirstOrDefault(); + + bool bVal; + prefAutoHide = bool.TryParse(configAutoHide, out bVal) ? bVal : prefAutoHide; + + string configDelay = configFile.Descendants() + .Where(x => x.Name.LocalName == "preference") + .Where(x => (string)x.Attribute("name") == "SplashScreenDelay") + .Select(x => (string)x.Attribute("value")) + .FirstOrDefault(); + int nVal; + prefDelay = int.TryParse(configDelay, out nVal) ? nVal : prefDelay; + + string configImage = configFile.Descendants() + .Where(x => x.Name.LocalName == "preference") + .Where(x => (string)x.Attribute("name") == "SplashScreen") + .Select(x => (string)x.Attribute("value")) + .FirstOrDefault(); + + if (!String.IsNullOrEmpty(configImage)) + { + prefImagePath = configImage; + } + } + } + } + + private StreamResourceInfo GetSplashScreenImageResource() + { + // Get the base filename for the splash screen images + string imageName = System.IO.Path.GetFileNameWithoutExtension(prefImagePath); + Uri imageUri = null; + StreamResourceInfo imageResource = null; + + // First, try to get a resolution-specific splashscreen + try + { + // Determine the device's resolution + switch (ResolutionHelper.CurrentResolution) + { + case Resolutions.HD: + imageUri = new Uri(imageName + ".screen-720p.jpg", UriKind.Relative); + break; + + case Resolutions.WVGA: + imageUri = new Uri(imageName + ".screen-WVGA.jpg", UriKind.Relative); + break; + + case Resolutions.WXGA: + default: + imageUri = new Uri(imageName + ".screen-WXGA.jpg", UriKind.Relative); + break; + } + + imageResource = Application.GetResourceStream(imageUri); + } + catch (Exception) + { + // It's OK if we didn't get a resolution-specific image + } + + // Fallback to the default image name without decoration + if (imageResource == null) + { + imageUri = new Uri(prefImagePath, UriKind.Relative); + imageResource = Application.GetResourceStream(imageUri); + } + + if (imageUri != null) Debug.WriteLine("INFO :: SplashScreen: using image {0}", imageUri.OriginalString); + + return imageResource; + } + + public void show(string options = null) + { + Deployment.Current.Dispatcher.BeginInvoke(() => + { + if (!popup.IsOpen) + { + popup.Child.Opacity = 0; + + Storyboard story = new Storyboard(); + DoubleAnimation animation = new DoubleAnimation() + { + From = 0.0, + To = 1.0, + Duration = new Duration(TimeSpan.FromSeconds(0.2)) + }; + + Storyboard.SetTarget(animation, popup.Child); + Storyboard.SetTargetProperty(animation, new PropertyPath("Opacity")); + story.Children.Add(animation); + + story.Begin(); + + popup.IsOpen = true; + + if (prefAutoHide) + { + StartAutoHideTimer(); + } + } + }); + } + + public void hide(string options = null) + { + Deployment.Current.Dispatcher.BeginInvoke(() => + { + if (popup.IsOpen) + { + popup.Child.Opacity = 1.0; + + Storyboard story = new Storyboard(); + DoubleAnimation animation = new DoubleAnimation() + { + From = 1.0, + To = 0.0, + Duration = new Duration(TimeSpan.FromSeconds(0.4)) + }; + + Storyboard.SetTarget(animation, popup.Child); + Storyboard.SetTargetProperty(animation, new PropertyPath("Opacity")); + story.Children.Add(animation); + story.Completed += (object sender, EventArgs e) => + { + popup.IsOpen = false; + }; + story.Begin(); + } + }); + } + + private void StartAutoHideTimer() + { + var timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(prefDelay) }; + timer.Tick += (object sender, EventArgs e) => + { + hide(); + timer.Stop(); + }; + timer.Start(); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\plugins\cordova-plugin-splashscreen\types\index.d.ts + +// Type definitions for Apache Cordova Splashscreen plugin +// Project: https://github.com/apache/cordova-plugin-splashscreen +// Definitions by: Microsoft Open Technologies Inc +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// +// Copyright (c) Microsoft Open Technologies Inc +// Licensed under the MIT license. + +interface Navigator { + /** This plugin displays and hides a splash screen during application launch. */ + splashscreen: { + /** Dismiss the splash screen. */ + hide(): void; + /** Displays the splash screen. */ + show(): void; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\plugins\cordova-plugin-statusbar\src\wp\StatusBar.cs + +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + + +using Microsoft.Phone.Shell; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Threading; +using System.Windows; +using System.Windows.Media; +using System.Windows.Threading; + + +/* + * http://www.idev101.com/code/User_Interface/StatusBar.html + * https://developer.apple.com/library/ios/documentation/userexperience/conceptual/transitionguide/Bars.html + * https://developer.apple.com/library/ios/documentation/uikit/reference/UIApplication_Class/Reference/Reference.html#//apple_ref/c/econst/UIStatusBarStyleDefault + * */ + + +namespace WPCordovaClassLib.Cordova.Commands +{ + public class StatusBar : BaseCommand + { + + // returns an argb value, if the hex is only rgb, it will be full opacity + protected Color ColorFromHex(string hexString) + { + string cleanHex = hexString.Replace("#", "").Replace("0x", ""); + // turn #FFF into #FFFFFF + if (cleanHex.Length == 3) + { + cleanHex = "" + cleanHex[0] + cleanHex[0] + cleanHex[1] + cleanHex[1] + cleanHex[2] + cleanHex[2]; + } + // add an alpha 100% if it is missing + if (cleanHex.Length == 6) + { + cleanHex = "FF" + cleanHex; + } + int argb = Int32.Parse(cleanHex, NumberStyles.HexNumber); + Color clr = Color.FromArgb((byte)((argb & 0xff000000) >> 0x18), + (byte)((argb & 0xff0000) >> 0x10), + (byte)((argb & 0xff00) >> 8), + (byte)(argb & 0xff)); + return clr; + } + + public void _ready(string options) + { + Deployment.Current.Dispatcher.BeginInvoke(() => + { + bool isVis = SystemTray.IsVisible; + // TODO: pass this to JS + //Debug.WriteLine("Result::" + res); + DispatchCommandResult(new PluginResult(PluginResult.Status.OK, isVis)); + }); + } + + public void overlaysWebView(string options) + { //exec(null, null, "StatusBar", "overlaysWebView", [doOverlay]); + // string arg = JSON.JsonHelper.Deserialize(options)[0]; + } + + public void styleDefault(string options) + { //exec(null, null, "StatusBar", "styleDefault", []); + Deployment.Current.Dispatcher.BeginInvoke(() => + { + SystemTray.ForegroundColor = Colors.Black; + }); + } + + public void styleLightContent(string options) + { //exec(null, null, "StatusBar", "styleLightContent", []); + + Deployment.Current.Dispatcher.BeginInvoke(() => + { + SystemTray.ForegroundColor = Colors.White; + }); + } + + public void styleBlackTranslucent(string options) + { //exec(null, null, "StatusBar", "styleBlackTranslucent", []); + styleLightContent(options); + } + + public void styleBlackOpaque(string options) + { //exec(null, null, "StatusBar", "styleBlackOpaque", []); + styleLightContent(options); + } + + public void backgroundColorByName(string options) + { //exec(null, null, "StatusBar", "backgroundColorByName", [colorname]); + // this should NOT be called, js should now be using/converting color names to hex + } + + public void backgroundColorByHexString(string options) + { //exec(null, null, "StatusBar", "backgroundColorByHexString", [hexString]); + string argb = JSON.JsonHelper.Deserialize(options)[0]; + + Color clr = ColorFromHex(argb); + + Deployment.Current.Dispatcher.BeginInvoke(() => + { + SystemTray.Opacity = clr.A / 255.0d; + SystemTray.BackgroundColor = clr; + + }); + } + + public void hide(string options) + { //exec(null, null, "StatusBar", "hide", []); + Deployment.Current.Dispatcher.BeginInvoke(() => + { + SystemTray.IsVisible = false; + }); + + } + + public void show(string options) + { //exec(null, null, "StatusBar", "show", []); + Deployment.Current.Dispatcher.BeginInvoke(() => + { + SystemTray.IsVisible = true; + }); + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\plugins\cordova-plugin-statusbar\types\index.d.ts + +// Type definitions for Apache Cordova StatusBar plugin +// Project: https://github.com/apache/cordova-plugin-statusbar +// Definitions by: Xinkai Chen +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/** +* Global object StatusBar. +*/ +interface Window { + StatusBar: StatusBar; +} + + +/** +* The StatusBar object provides some functions to customize the iOS and Android StatusBar. +*/ +interface StatusBar { + /** + * On iOS 7, make the statusbar overlay or not overlay the WebView. + * @param isOverlay On iOS 7, set to false to make the statusbar appear like iOS 6. + * Set the style and background color to suit using the other functions. + */ + overlaysWebView: (isOverlay: boolean) => void; + + /** + * Use the default statusbar (dark text, for light backgrounds). + */ + styleDefault: () => void; + + /** + * Use the lightContent statusbar (light text, for dark backgrounds). + */ + styleLightContent: () => void; + + /** + * Use the blackTranslucent statusbar (light text, for dark backgrounds). + */ + styleBlackTranslucent: () => void; + + /** + * Use the blackOpaque statusbar (light text, for dark backgrounds). + */ + styleBlackOpaque: () => void; + + /** + * On iOS 7, when you set StatusBar.statusBarOverlaysWebView to false, + * you can set the background color of the statusbar by color name. + * @param color Supported color names are: + * black, darkGray, lightGray, white, gray, red, green, blue, cyan, yellow, magenta, orange, purple, brown + */ + backgroundColorByName: (color: string) => void; + + /** + * Sets the background color of the statusbar by a hex string. + * @param color CSS shorthand properties are also supported. + * On iOS 7, when you set StatusBar.statusBarOverlaysWebView to false, you can set the background color of the statusbar by a hex string (#RRGGBB). + * On WP7 and WP8 you can also specify values as #AARRGGBB, where AA is an alpha value + */ + backgroundColorByHexString: (color: string) => void; + + /** + * Hide the statusbar. + */ + hide: () => void; + + /** + * Show the statusbar. + */ + show: () => void; + + /** + * Read this property to see if the statusbar is visible or not. + */ + isVisible: boolean; +} + +declare var StatusBar: StatusBar; + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\index.html + + + + + + XFramework App + + + + + + + + + + + + + + + + + + + + + + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\main.ts + +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\polyfills.ts + +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/guide/browser-support + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** + * IE11 requires the following for NgClass support on SVG elements + */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** + * Web Animations `@angular/platform-browser/animations` + * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. + * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). + */ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + * because those flags need to be set before `zone.js` being loaded, and webpack + * will put import in the top of bundle, so user need to create a separate file + * in this directory (for example: zone-flags.ts), and put the following flags + * into that file, and then add the following code before importing zone.js. + * import './zone-flags'; + * + * The flags allowed in zone-flags.ts are listed here. + * + * The following flags will work for all browsers. + * + * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + * + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + * + * (window as any).__Zone_enable_cross_context_check = true; + * + */ + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js'; // Included with Angular CLI. + + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\app-routing.module.ts + +import { NgModule } from '@angular/core'; +import { Pages, BaseRoutes } from './config/page.config'; +import { PreloadAllModules, RouterModule, Routes } from '@angular/router'; + +const routes: Routes = [ + // + // Default ... + { + path: BaseRoutes.Default, + redirectTo: Pages.Home.baseRoute, + pathMatch: 'full', + }, + // + // Home ... + { + path: Pages.Home.baseRoute, + loadChildren: () => + import('./pages/home/home.module').then((m) => m.HomePageModule), + }, + // + // Core ... + { + path: Pages.Core.baseRoute, + loadChildren: () => + import('./pages/core/core.module').then((m) => m.CorePageModule), + }, + // + // Services ... + { + path: Pages.Services.baseRoute, + loadChildren: () => + import('./pages/services/services.module').then( + (m) => m.ServicesPageModule + ), + }, + // + // Components ... + { + path: Pages.Components.baseRoute, + loadChildren: () => + import('./pages/components/components.module').then( + (m) => m.ComponentsPageModule + ), + }, + // + // TabsNavs ... + { + path: Pages.TabsNav.baseRoute, + loadChildren: () => + import('./pages/tabs/tabs.module').then((m) => m.TabsPageModule), + }, + // + // Themes ... + { + path: Pages.Themes.baseRoute, + loadChildren: () => + import('./pages/themes/themes.module').then((m) => m.ThemesPageModule), + }, +]; + +@NgModule({ + imports: [ + RouterModule.forRoot(routes, { + preloadingStrategy: PreloadAllModules, + scrollPositionRestoration: 'enabled', + }), + ], + exports: [RouterModule], +}) +export class AppRoutingModule {} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\app.component.html + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\app.component.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\app.component.ts + +import { Component } from '@angular/core'; +import { Platform } from '@ionic/angular'; + +@Component({ + selector: 'app-root', + templateUrl: 'app.component.html', + styleUrls: ['app.component.scss'] +}) +export class AppComponent { + constructor(private platform: Platform) { + this.initializeApp(); + } + + initializeApp() { + this.platform.ready().then(() => {}); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\app.module.ts + +import { + XCanDeactivateGuard, + XFrameworkCoreModule, + X_FRAMEWORK_CORE_CONFIG, +} from 'x-framework-core'; +import { + XManagerService, + XHttpInterceptorService, + XFrameworkServicesModule, + X_FRAMEWORK_SERVICES_CONFIG, +} from 'x-framework-services'; +import { + XFrameworkComponentsModule, + X_FRAMEWORK_COMPONENTS_CONFIG, +} from 'x-framework-components'; +import { NgModule } from '@angular/core'; +import { IonicModule } from '@ionic/angular'; +import { AppComponent } from './app.component'; +import { ViewsModule } from './views/views.module'; +import { X_CONFIG, XCONFIG } from './config/x-config'; +import { AppRoutingModule } from './app-routing.module'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { BrowserModule } from '@angular/platform-browser'; +import { environment } from '../environments/environment'; +import { ServiceWorkerModule } from '@angular/service-worker'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; + +@NgModule({ + declarations: [AppComponent], + imports: [ + ViewsModule, + BrowserModule, + AppRoutingModule, + XFrameworkCoreModule, + IonicModule.forRoot(), + BrowserAnimationsModule, + XFrameworkServicesModule, + XFrameworkComponentsModule, + ServiceWorkerModule.register('ngsw-worker.js', { + enabled: environment.production, + }), + ], + providers: [ + // + // Manager Service ... + XManagerService, + // + // Provide it ... + XCanDeactivateGuard, + { + provide: X_FRAMEWORK_CORE_CONFIG, + useValue: XCONFIG, + }, + { + provide: X_FRAMEWORK_SERVICES_CONFIG, + useValue: XCONFIG, + }, + { + provide: X_FRAMEWORK_COMPONENTS_CONFIG, + useValue: XCONFIG, + }, + { + provide: X_CONFIG, + useValue: XCONFIG, + }, + // + // Register Http Interceptor ... + { + provide: HTTP_INTERCEPTORS, + useClass: XHttpInterceptorService, + multi: true, + }, + ], + bootstrap: [AppComponent], +}) +export class AppModule {} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\config\app-config.ts + +import { + DefaultLocale, + AvailableTranslationResources, +} from './localization.config'; +import { NotificationAudioSources } from './audio.config'; +import { XFrameworkCoreConfig, XPage } from 'x-framework-core'; +import { XFrameworkServicesConfig } from 'x-framework-services'; +import { XFrameworkComponentsConfig } from 'x-framework-components'; + +// tslint:disable-next-line:no-empty-interface +export interface AppConfig { + defaultLandingPage?: XPage; + defaultBlogPage?: XPage; +} + +export type XFrameworkCoreSharedConfig = Partial; +export type XFrameworkServicesSharedConfig = Partial; +export type XFrameworkComponentsSharedConfig = + Partial; +export type XAppSharedConfig = Partial; + +export type XConfig = XFrameworkCoreConfig & + XFrameworkServicesConfig & + XFrameworkComponentsConfig & + AppConfig; +export type XSharedConfig = Partial; + +export const xFrameworkCoreSharedConfig: XFrameworkCoreSharedConfig = { + // + // App Info ... + appInstanceName: 'xFrameworkTestApp', + appNameResource: 'app_name', + appCompanyResource: 'company', + appVersion: 'v1.1', + + // + // Audio Service Configurations ... + defaultNotificationDelay: 2000, + notificationAudioSources: NotificationAudioSources, + + // + // Language Configuration ... + availableLanguages: AvailableTranslationResources, + defaultLanguage: DefaultLocale, + + // + // Api Configuration ... + poweredValue: 'SaherElmITCenter', + + // + // Security Service Configurations ... + secretKey: 'SaherElm@09121694056@Hadi_Khazaee_Asl@yahoo.com', + + // + // Logging Configuration ... + baseLogTag: 'xFrameworkApp', + + // + // Image Related Configs ... + minAllowedImageSize: 512, + maxAllowedImageSize: 5242880, + minAllowedImageCroppableSize: 512, + allowedImageExtensions: ['.png', '.jpg', '.jpeg'], + + // + // Profile Image Configs ... + maxProfileImageSize: 5242880, + maxProfileImagesUploadFiles: 10, + + // + defaultCountryCode: 'IR', + + // + defaultPageSize: 20, + pageSizes: [10, 20, 40, 50, 100, 200], + + // + defaultActionDelay: 500, +}; + +export const xFrameworkServicesSharedConfig: XFrameworkServicesSharedConfig = { + geoLocationConfig: { + enableHighAccuracy: true, + timeout: 5000, + }, +}; + +export const xFrameworkComponentsSharedConfig: XFrameworkComponentsSharedConfig = + { + // + // File Config ... + minAllowedFileSize: 512, + maxAllowedFileSize: 20971520, + maxAllowedSize: 966367641, + maxUploadFiles: 10, + + // + defaultSearchDebounceTime: 500, + }; + +export const xAppSharedConfig: XAppSharedConfig = {}; + +export const xSharedConfig: XSharedConfig = { + ...xFrameworkCoreSharedConfig, + ...xFrameworkServicesSharedConfig, + ...xFrameworkComponentsSharedConfig, + ...xAppSharedConfig, +}; + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\config\app.localization.config.ts + +import { XTranslationItem } from 'x-framework-core'; + +export enum AppResourceIDs { + // + //#region Global ... + sample = 'sample', + temp_label = 'temp_label', + documentation = 'documentation', + default_loading = 'default_loading', + + // + english = 'english', + persian = 'persian', + + // + maintainer_title = 'maintainer_title', + maintainer_name = 'maintainer_name', + //#endregion + + // + //#region Landing ... + landing_page_title = 'landing_page_title', + landing_page_description = 'landing_page_description', + //#endregion + + // + //#region Home ... + home = 'home', + home_description = 'home_description', + + // + //#region Intro ... + home_introduction = 'home_introduction', + home_introduction_description = 'home_introduction_description', + //#endregion + //#endregion + + // + //#region Core ... + core = 'core', + core_description = 'core_description', + + // + //#region Intro ... + core_introduction = 'core_introduction', + core_introduction_description = 'core_introduction_description', + //#endregion + //#endregion + + // + //#region Services ... + services = 'services', + services_description = 'services_description', + + // + //#region Intro ... + services_introduction = 'services_introduction', + services_introduction_description = 'services_introduction_description', + //#endregion + + // + //#region User Device ... + user_device = 'user_device', + user_device_description = 'user_device_description', + //#endregion + //#endregion + + // + //#region Components ... + components = 'components', + components_description = 'components_description', + + // + //#region Intro ... + components_introduction = 'components_introduction', + components_introduction_description = 'components_introduction_description', + //#endregion + + // + //#region Small Components ... + small_components = 'small_components', + small_components_description = 'small_components_description', + + // + //#region Button ... + button = 'button', + button_description = 'button_description', + //#endregion + + // + //#region ActionBar ... + action_bar = 'action_bar', + action_bar_description = 'action_bar_description', + //#endregion + + // + //#region SearchBar ... + search_bar = 'search_bar', + search_bar_description = 'search_bar_description', + //#endregion + + // + //#region Alert ... + alert = 'alert', + alert_description = 'alert_description', + //#endregion + + // + //#region Notification ... + notification = 'notification', + notification_description = 'notification_description', + //#endregion + + // + //#region Spinner ... + spinner = 'spinner', + spinner_description = 'spinner_description', + //#endregion + + // + //#region Loading ... + loading = 'loading', + loading_description = 'loading_description', + //#endregion + + // + //#region Thumbnail ... + thumbnail_component = 'thumbnail_component', + thumbnail_description = 'thumbnail_description', + //#endregion + + // + //#region Prompt ... + prompt = 'prompt', + prompt_description = 'prompt_description', + //#endregion + + // + //#region Color ... + color = 'color', + color_description = 'color_description', + //#endregion + + // + //#region Picker ... + picker = 'picker', + picker_description = 'picker_description', + //#endregion + + // + //#region Slotter ... + slotter = 'slotter', + slotter_description = 'slotter_description', + + // + vertical = 'vertical', + horizontal = 'horizontal', + //#endregion + + // + //#region Empty Component ... + empty_component = 'empty_component', + empty_component_description = 'empty_component_description', + //#endregion + //#endregion + + // + //#region Card ... + card = 'card', + card_description = 'card_description', + + // + header = 'header', + actions = 'actions', + footer = 'footer', + //#endregion + + // + //#region Icon ... + icon = 'icon', + icon_description = 'icon_description', + + icon_list = 'icon_list', + //#endregion + + // + //#region Counter ... + counter = 'counter', + counter_description = 'counter_description', + + // + pause = 'pause', + //#endregion + + // + //#region ColorPicker ... + color_picker = 'color_picker', + color_picker_description = 'color_picker_description', + //#endregion + + // + //#region Fab ... + fab = 'fab', + fab_description = 'fab_description', + //#endregion + + // + //#region FileUpload ... + file_components = 'file_components', + file_components_description = 'file_components_description', + + // + image_cropper = 'image_cropper', + image_cropper_description = 'image_cropper_description', + + // + file_drop_area = 'file_drop_area', + file_drop_area_description = 'file_drop_area_description', + + // + file_upload = 'file_upload', + file_upload_description = 'file_upload_description', + //#endregion + + // + //#region Form ... + form = 'form', + form_description = 'form_description', + + // + first_name = 'first_name', + last_name = 'last_name', + + // + content = 'content', + content_type = 'content_type', + post = 'post', + news = 'news', + visibility = 'visibility', + visible = 'visible', + hidden = 'hidden', + + // + publish = 'publish', + brithDate = 'brithDate', + wedding_state = 'wedding_state', + num_of_childs = 'num_of_childs', + //#endregion + + // + //#region Grid ... + grid = 'grid', + grid_description = 'grid_description', + //#endregion + + // + //#region List ... + list = 'list', + list_description = 'list_description', + //#endregion + + // + //#region Map ... + map = 'map', + map_description = 'map_description', + //#endregion + + // + //#region Markdown ... + markdown = 'markdown', + markdown_description = 'markdown_description', + //#endregion + + // + //#region Tree ... + tree = 'tree', + tree_description = 'tree_description', + //#endregion + + // + //#region DescriptionList ... + description_list = 'description_list', + description_list_description = 'description_list_description', + //#endregion + + // + //#region ExpandableList ... + expandable_list = 'expandable_list', + expandable_list_description = 'expandable_list_description', + //#endregion + + // + //#region ExpandablePanel ... + expandable_panel = 'expandable_panel', + expandable_panel_description = 'expandable_panel_description', + //#endregion + + // + //#region ModelPresenter ... + model_presenter = 'model_presenter', + model_presenter_description = 'model_presenter_description', + //#endregion + + // + //#region Slider ... + slider = 'slider', + slider_description = 'slider_description', + //#endregion + + // + //#region Stepper ... + stepper = 'stepper', + stepper_description = 'stepper_description', + //#endregion + + // + //#region Tabs ... + tabs = 'tabs', + tabs_description = 'tabs_description', + //#endregion + + // + //#region Table ... + table = 'table', + table_description = 'table_description', + //#endregion + + // + //#region Table ... + page = 'page', + page_description = 'page_description', + //#endregion + + // + //#region TabNavigation ... + tab_navigator = 'tab_navigator', + tab_navigator_description = 'tab_navigator_description', + //#endregion + //#endregion + + // + //#region TabsNav ... + tab_page_1 = 'tab_page_1', + tab_page_2 = 'tab_page_2', + tab_page_3 = 'tab_page_3', + //#endregion + + // + //#region Theme ... + themes = 'themes', + themes_description = 'themes_description', + theme_manager = 'theme_manager', + theme_manager_description = 'theme_manager_description', + //#endregion +} + +// +export const FaCustomLocales: XTranslationItem[] = [ + // + //#region Global ... + { + id: 'sample', + value: 'نمونه', + }, + { + id: 'temp_label', + value: 'برچسب پیش فرض', + }, + { + id: 'documentation', + value: 'مستندات', + }, + { + id: 'default_loading', + value: 'در حال بارگزاری', + }, + + // + { + id: 'persian', + value: 'فارسی', + }, + { + id: 'english', + value: 'انگلیسی', + }, + + // + { + id: 'maintainer_title', + value: 'طراح و توسعه دهنده:', + }, + { + id: 'maintainer_name', + value: 'هادی خزاعی اصل', + }, + //#endregion + + // + //#region Landing ... + // + { + id: 'landing_page_title', + value: 'صفحه نخست', + }, + { + id: 'landing_page_description', + value: 'نخستین صفحه ای که نمایش داده میشود', + }, + //#endregion + + // + //#region Home ... + { + id: 'home', + value: 'خانه', + }, + { + id: 'home_description', + value: 'مرور کلیات', + }, + + // + //#region Intro ... + { + id: 'home_introduction', + value: 'مقدمه', + }, + { + id: 'home_introduction_description', + value: 'آشنایی با چارچوب ایکس', + }, + //#endregion + //#endregion + + // + //#region Core ... + { + id: 'core', + value: 'هسته', + }, + { + id: 'core_description', + value: 'مروری بر واحد هسته از چارچوب ایکس', + }, + + // + //#region Intro ... + { + id: 'core_introduction', + value: 'مقدمه', + }, + { + id: 'core_introduction_description', + value: 'آشنایی با واحد هسته از چارچوب ایکس', + }, + //#endregion + //#endregion + + // + //#region Services ... + { + id: 'services', + value: 'خدمات', + }, + { + id: 'services_description', + value: 'مروری بر واحد خدمات از چارچوب ایکس', + }, + + // + //#region Intro ... + { + id: 'services_introduction', + value: 'مقدمه', + }, + { + id: 'services_introduction_description', + value: 'آشنایی با واحد خدمات از چارچوب ایکس', + }, + //#endregion + + // + //#region User Device ... + { + id: 'user_device', + value: 'ابزار کاربر', + }, + { + id: 'user_device_description', + value: 'شناسایی ابزار مقصد کاربر', + }, + //#endregion + //#endregion + + // + //#region Components ... + // + { + id: 'components', + value: 'مولفه ها', + }, + { + id: 'components_description', + value: 'توضیح کامل مجموعه مولفه های ارائه شده توسط فریمورک ایکس', + }, + + // + //#region Intro ... + { + id: 'components_introduction', + value: 'مقدمه', + }, + { + id: 'components_introduction_description', + value: 'آشنایی با واحد مولفه ها', + }, + //#endregion + + // + //#region Small Components ... + { + id: 'small_components', + value: 'مولفه های جزئی', + }, + { + id: 'small_components_description', + value: 'ارائه مستندات مربوط به مولفه های جزئی', + }, + + // + //#region Button ... + { + id: 'button', + value: 'دکمه', + }, + { + id: 'button_description', + value: 'مستندات مربوط به مولفه دکمه', + }, + //#endregion + + // + //#region ActionBar ... + { + id: 'action_bar', + value: 'میله فرمان', + }, + { + id: 'action_bar_description', + value: 'مستندات مربوط به مولفه میله فرمان', + }, + //#endregion + + // + //#region SearchBar ... + { + id: 'search_bar', + value: 'میله جستجو', + }, + { + id: 'search_bar_description', + value: 'مستندات مربوط به مولفه میله جستجو', + }, + //#endregion + + // + //#region Alert ... + { + id: 'alert', + value: 'هشدار', + }, + { + id: 'alert_description', + value: 'مستندات مربوط به مولفه هشدار', + }, + //#endregion + + // + //#region Notification ... + { + id: 'notification', + value: 'اعلان', + }, + { + id: 'notification_description', + value: 'مستندات مربوط به مولفه اعلان', + }, + //#endregion + + // + //#region Spinner ... + { + id: 'spinner', + value: 'فرفره', + }, + { + id: 'spinner_description', + value: 'مستندات مربوط به مولفه فرفره', + }, + //#endregion + + // + //#region Loading ... + { + id: 'loading', + value: 'بارگزاری', + }, + { + id: 'loading_description', + value: 'مستندات مربوط به مولفه بارگزاری', + }, + //#endregion + + // + //#region Thumbnail ... + { + id: 'thumbnail_component', + value: 'تصویر بند انگشتی', + }, + { + id: 'thumbnail_description', + value: 'مستندات مربوط به مولفه بند انگشتی', + }, + //#endregion + + // + //#region Prompt ... + { + id: 'prompt', + value: 'سوال', + }, + { + id: 'prompt_description', + value: 'مستندات مربوط به مولفه سوال', + }, + //#endregion + + // + //#region Color ... + { + id: 'color', + value: 'رنگ', + }, + { + id: 'color_description', + value: 'مستندات مربوط به مولفه رنگ', + }, + //#endregion + + // + //#region Picker ... + { + id: 'picker', + value: 'انتخاب', + }, + { + id: 'picker_description', + value: 'مستندات مربوط به مولفه انتخاب', + }, + //#endregion + + // + //#region Slotter ... + { + id: 'slotter', + value: 'جورچین', + }, + { + id: 'slotter_description', + value: 'مستندات مربوط به مولفه جورچین', + }, + + // + { + id: 'vertical', + value: 'عمودی', + }, + { + id: 'horizontal', + value: 'افقی', + }, + //#endregion + + // + //#region Empty Component ... + { + id: 'empty_component', + value: 'محتوای خالی', + }, + { + id: 'empty_component_description', + value: 'مستندات مربوط به مولفه محتوای خالی', + }, + //#endregion + //#endregion + + // + //#region Card ... + { + id: 'card', + value: 'کارت', + }, + { + id: 'card_description', + value: 'مستندات مربوط به مولفه کارت', + }, + + // + { + id: 'header', + value: 'ناحیه فوقانی', + }, + { + id: 'actions', + value: 'فعالیت ها و فرمان ها', + }, + { + id: 'footer', + value: 'ناحیه تحتانی', + }, + //#endregion + + // + //#region Icon ... + { + id: 'icon', + value: 'شمایل', + }, + { + id: 'icon_description', + value: 'مستندات مربوط به مولفه شمایل', + }, + + // + { + id: 'icon_list', + value: 'فهرست شمایل های موجود', + }, + //#endregion + + // + //#region Counter ... + { + id: 'counter', + value: 'شمارنده', + }, + { + id: 'counter_description', + value: 'مستندات مربوط به مولفه شمارنده', + }, + + // + { + id: 'pause', + value: 'توقف', + }, + //#endregion + + // + //#region ColorPicker ... + { + id: 'color_picker', + value: 'انتخابگر رنگ', + }, + { + id: 'color_picker_description', + value: 'مستندات مربوط به مولفه انتخابگر رنگ', + }, + //#endregion + + // + //#region Fab ... + { + id: 'fab', + value: 'دکمه شناور', + }, + { + id: 'fab_description', + value: 'مستندات مربوط به مولفه دکمه شناور', + }, + //#endregion + + // + //#region FileUpload ... + { + id: 'file_components', + value: 'مولفه های مرتبط با فایل', + }, + { + id: 'file_components_description', + value: 'مستندات مربوط به مولفه های مرتبط با فایل', + }, + + // + { + id: 'image_cropper', + value: 'برشگر تصویر', + }, + { + id: 'image_cropper_description', + value: 'مستندات مربوط به برشگر تصویر', + }, + + // + { + id: 'file_drop_area', + value: 'جایگذاری فایل', + }, + { + id: 'file_drop_area_description', + value: 'مستندات مربوط به مولفه جایگذاری فایل', + }, + + // + { + id: 'file_upload', + value: 'بارگزاری فایل', + }, + { + id: 'file_upload_description', + value: 'مستندات مربوط به مولفه بارگزاری فایل', + }, + //#endregion + + // + //#region Form ... + { + id: 'form', + value: 'فرم', + }, + { + id: 'form_description', + value: 'مستندات مربوط به مولفه فرم', + }, + + // + { + id: 'first_name', + value: 'نام', + }, + { + id: 'last_name', + value: 'نام خانوادگی', + }, + // + { + id: 'content', + value: 'محتوا', + }, + { + id: 'content_type', + value: 'نوع محتوا', + }, + { + id: 'post', + value: 'مطلب', + }, + { + id: 'news', + value: 'خبر', + }, + // + { + id: 'visibility', + value: 'وضعیت نمایش', + }, + { + id: 'visible', + value: 'قابل رویت', + }, + { + id: 'hidden', + value: 'پنهان', + }, + // + { + id: 'publish', + value: 'منتشر شود', + }, + { + id: 'brithDate', + value: 'تاریخ تولد', + }, + { + id: 'wedding_state', + value: 'وضعیت تاهل', + }, + { + id: 'num_of_childs', + value: 'تعداد فرزندان', + }, + //#endregion + + // + //#region Grid ... + { + id: 'grid', + value: 'توری', + }, + { + id: 'grid_description', + value: 'مستندات مربوط به مولفه توری', + }, + //#endregion + + // + //#region List ... + { + id: 'list', + value: 'فهرست', + }, + { + id: 'list_description', + value: 'مستندات مربوط به مولفه فهرست', + }, + //#endregion + + // + //#region Map ... + { + id: 'map', + value: 'نقشه', + }, + { + id: 'map_description', + value: 'مستندات مربوط به مولفه نقشه', + }, + //#endregion + + // + //#region Markdown ... + { + id: 'markdown', + value: 'نشانه گزاری', + }, + { + id: 'markdown_description', + value: 'مستندات مربوط به مولفه نشانه گزاری', + }, + //#endregion + + // + //#region Tree ... + { + id: 'tree', + value: 'ساختار درختی', + }, + { + id: 'tree_description', + value: 'مستندات مربوط به مولفه ساختار درختی', + }, + //#endregion + + // + //#region DescriptionList ... + { + id: 'description_list', + value: 'لیست توضیحات', + }, + { + id: 'description_list_description', + value: 'مستندات مربوط به مولفه لیست توضیحات', + }, + //#endregion + + // + //#region ExpandableList ... + { + id: 'expandable_list', + value: 'فهرست باز شونده', + }, + { + id: 'expandable_list_description', + value: 'مستندات مربوط به مولفه فهرست باز شونده', + }, + //#endregion + + // + //#region ExpandablePanel ... + { + id: 'expandable_panel', + value: 'قطعه بازشونده', + }, + { + id: 'expandable_panel_description', + value: 'مستندات مربوط به مولفه قطعه بازشونده', + }, + //#endregion + + // + //#region ModelPresenter ... + { + id: 'model_presenter', + value: 'نمایشگر داده', + }, + { + id: 'model_presenter_description', + value: 'مستندات مربوط به نمایشگر داده', + }, + //#endregion + + // + //#region Slider ... + { + id: 'slider', + value: 'لغزنده', + }, + { + id: 'slider_description', + value: 'مستندات مربوط به مولفه لغزنده', + }, + //#endregion + + // + //#region Stepper ... + { + id: 'stepper', + value: 'پله ساز', + }, + { + id: 'stepper_description', + value: 'مستندات مربوط به مولفه پله ساز', + }, + //#endregion + + // + //#region Tabs ... + { + id: 'tabs', + value: 'برگه ها', + }, + { + id: 'tabs_description', + value: 'مستندات مربوط به مولفه برگه ها', + }, + //#endregion + + // + //#region Table ... + { + id: 'table', + value: 'جدول', + }, + { + id: 'table_description', + value: 'مستندات مربوط به مولفه جدول', + }, + //#endregion + + // + //#region Page ... + { + id: 'page', + value: 'صفحه', + }, + { + id: 'page_description', + value: 'مستندات مربوط به مولفه صفحه', + }, + //#endregion + + // + //#region TabNavigation ... + { + id: 'tab_navigator', + value: 'پیمایشگر برگه', + }, + { + id: 'tab_navigator_description', + value: 'مستندات مربوط به مولفه پیمایشگر برگه', + }, + //#endregion + //#endregion + + // + //#region TabsNav ... + { + id: 'tab_page_1', + value: 'برگه ۱', + }, + { + id: 'tab_page_2', + value: 'برگه ۲', + }, + { + id: 'tab_page_3', + value: 'برگه ۳', + }, + //#endregion + + // + //#region Theme ... + { + id: 'themes', + value: 'پوسته ها', + }, + { + id: 'themes_description', + value: 'دسترسی به تمام امکانات مدیریت پوسته ها', + }, + // + { + id: 'theme_manager', + value: 'مدیریت پوسته', + }, + { + id: 'theme_manager_description', + value: 'مدیریت و دستکاری پوسته های نمایشی برنامه', + }, + //#endregion +]; + +// +export const EnCustomLocales: XTranslationItem[] = [ + // + //#region Global ... + { + id: 'sample', + value: 'Sample', + }, + { + id: 'temp_label', + value: 'Default Label', + }, + { + id: 'documentation', + value: 'Documentation', + }, + { + id: 'default_loading', + value: 'Loading', + }, + + // + { + id: 'persian', + value: 'Persian', + }, + { + id: 'english', + value: 'English', + }, + + // + { + id: 'maintainer_title', + value: 'Design and Develop', + }, + { + id: 'maintainer_name', + value: 'Hadi Khazaee Asl', + }, + //#endregion + + // + //#region Landing ... + // + { + id: 'landing_page_title', + value: 'First Page', + }, + { + id: 'landing_page_description', + value: 'First Page was shown', + }, + //#endregion + + // + //#region Home ... + { + id: 'home', + value: 'Home', + }, + { + id: 'home_description', + value: 'Overview of xFramework', + }, + + // + //#region Intro ... + { + id: 'home_introduction', + value: 'Introduction', + }, + { + id: 'home_introduction_description', + value: 'Introduction to xFramework', + }, + //#endregion + //#endregion + + // + //#region Core ... + { + id: 'core', + value: 'Core Module', + }, + { + id: 'core_description', + value: 'Overview of xFramework Core Module', + }, + + // + //#region Intro ... + { + id: 'core_introduction', + value: 'Introduction', + }, + { + id: 'core_introduction_description', + value: 'Introduction to xFramework Core Module', + }, + //#endregion + //#endregion + + // + //#region Services ... + { + id: 'services', + value: 'Services Module', + }, + { + id: 'services_description', + value: 'Overview of xFramework Services Module', + }, + + // + //#region Intro ... + { + id: 'services_introduction', + value: 'Introduction', + }, + { + id: 'services_introduction_description', + value: 'Introduction to xFramework Services Module', + }, + //#endregion + + // + //#region User Device ... + { + id: 'user_device', + value: 'َUser Device', + }, + { + id: 'user_device_description', + value: 'User Device Detection', + }, + //#endregion + //#endregion + + // + //#region Components ... + { + id: 'components', + value: 'Components', + }, + { + id: 'components_description', + value: 'Describe all provided Components in XFramework', + }, + + // + //#region Intro ... + { + id: 'components_introduction', + value: 'Introduction', + }, + { + id: 'components_introduction_description', + value: 'Introduction to xFramework Components Module', + }, + //#endregion + + // + //#region Small Components ... + { + id: 'small_components', + value: 'Small Components', + }, + { + id: 'small_components_description', + value: 'Provide Small Components Documentation', + }, + + // + //#region Button ... + { + id: 'button', + value: 'Button', + }, + { + id: 'button_description', + value: 'Button Component Documentation', + }, + //#endregion + + // + //#region ActionBar ... + { + id: 'action_bar', + value: 'ActionBar', + }, + { + id: 'action_bar_description', + value: 'ActionBar Component Documentation', + }, + //#endregion + + // + //#region SearchBar ... + { + id: 'search_bar', + value: 'SearchBar', + }, + { + id: 'search_bar_description', + value: 'SearchBar Component Documentation', + }, + //#endregion + + // + //#region Alert ... + { + id: 'alert', + value: 'Alert', + }, + { + id: 'alert_description', + value: 'Alert Component Documentation', + }, + //#endregion + + // + //#region Notification ... + { + id: 'notification', + value: 'Notification', + }, + { + id: 'notification_description', + value: 'Notification Component Documentation', + }, + //#endregion + + // + //#region Spinner ... + { + id: 'spinner', + value: 'Spinner', + }, + { + id: 'spinner_description', + value: 'Spinner Component Documentation', + }, + //#endregion + + // + //#region Loading ... + { + id: 'loading', + value: 'Loading', + }, + { + id: 'loading_description', + value: 'Loading Component Documentation', + }, + //#endregion + + // + //#region Thumbnail ... + { + id: 'thumbnail_component', + value: 'Thumbnail', + }, + { + id: 'thumbnail_description', + value: 'Thumbnail Component Documentation', + }, + //#endregion + + // + //#region Prompt ... + { + id: 'prompt', + value: 'Prompt', + }, + { + id: 'prompt_description', + value: 'Prompt Component Documentation', + }, + //#endregion + + // + //#region Color ... + { + id: 'color', + value: 'Color', + }, + { + id: 'color_description', + value: 'Color Component Documentation', + }, + //#endregion + + // + //#region Picker ... + { + id: 'picker', + value: 'Picker', + }, + { + id: 'picker_description', + value: 'Picker Component Documentation', + }, + //#endregion + + // + //#region Slotter ... + { + id: 'slotter', + value: 'Slotter', + }, + { + id: 'slotter_description', + value: 'Slotter Component Documentation', + }, + + // + { + id: 'vertical', + value: 'Vertical', + }, + { + id: 'horizontal', + value: 'Horizontal', + }, + //#endregion + + // + //#region Empty Component ... + { + id: 'empty_component', + value: 'Empty', + }, + { + id: 'empty_component_description', + value: 'Empty Component Documentation', + }, + //#endregion + //#endregion + + // + //#region Card ... + { + id: 'card', + value: 'Card', + }, + { + id: 'card_description', + value: 'Card Component Documentation', + }, + + // + { + id: 'header', + value: 'Header', + }, + { + id: 'actions', + value: 'Actions', + }, + { + id: 'footer', + value: 'Footer', + }, + //#endregion + + // + //#region Icon ... + { + id: 'icon', + value: 'Icon', + }, + { + id: 'icon_description', + value: 'Icon Component Documentation', + }, + + // + { + id: 'icon_list', + value: 'Icons List', + }, + //#endregion + + // + //#region Counter ... + { + id: 'counter', + value: 'Counter', + }, + { + id: 'counter_description', + value: 'Counter Component Documentation', + }, + + // + { + id: 'pause', + value: 'Pause', + }, + //#endregion + + // + //#region ColorPicker ... + { + id: 'color_picker', + value: 'ColorPicker', + }, + { + id: 'color_picker_description', + value: 'ColorPicker Component Documentation', + }, + //#endregion + + // + //#region Fab ... + { + id: 'fab', + value: 'Floating Action Button', + }, + { + id: 'fab_description', + value: 'FAB Component Documentation', + }, + //#endregion + + // + //#region FileUpload ... + { + id: 'file_components', + value: 'Files Related Components', + }, + { + id: 'file_components_description', + value: 'Files Related Components Documentation', + }, + + // + { + id: 'image_cropper', + value: 'Image Cropper', + }, + { + id: 'image_cropper_description', + value: 'Image Cropper Documentation', + }, + + // + { + id: 'file_drop_area', + value: 'File Drop Area', + }, + { + id: 'file_drop_area_description', + value: 'File Drop Area Documentation', + }, + + // + { + id: 'file_upload', + value: 'FileUpload', + }, + { + id: 'file_upload_description', + value: 'FileUpload Component Documentation', + }, + //#endregion + + // + //#region Form ... + { + id: 'form', + value: 'Form', + }, + { + id: 'form_description', + value: 'Form Component Documentation', + }, + + // + { + id: 'first_name', + value: 'First Name', + }, + { + id: 'last_name', + value: 'Last Name', + }, + // + { + id: 'content', + value: 'Content', + }, + { + id: 'content_type', + value: 'Content Type', + }, + { + id: 'post', + value: 'Post', + }, + { + id: 'news', + value: 'News', + }, + // + { + id: 'visibility', + value: 'Visibility', + }, + { + id: 'visible', + value: 'Visible', + }, + { + id: 'hidden', + value: 'Hidden', + }, + // + { + id: 'publish', + value: 'Publish', + }, + { + id: 'brithDate', + value: 'BrithDate', + }, + { + id: 'wedding_state', + value: 'Wedding State', + }, + { + id: 'num_of_childs', + value: 'ٔNumber of Childs', + }, + //#endregion + + // + //#region Grid ... + { + id: 'grid', + value: 'Grid', + }, + { + id: 'grid_description', + value: 'Grid Component Documentation', + }, + //#endregion + + // + //#region List ... + { + id: 'list', + value: 'List', + }, + { + id: 'list_description', + value: 'List Component Documentation', + }, + //#endregion + + // + //#region Map ... + { + id: 'map', + value: 'Map', + }, + { + id: 'map_description', + value: 'Map Component Documentation', + }, + //#endregion + + // + //#region Markdown ... + { + id: 'markdown', + value: 'Markdown', + }, + { + id: 'markdown_description', + value: 'Markdown Component Documentation', + }, + //#endregion + + // + //#region Tree ... + { + id: 'tree', + value: 'Tree', + }, + { + id: 'tree_description', + value: 'Tree Component Documentation', + }, + //#endregion + + // + //#region DescriptionList ... + { + id: 'description_list', + value: 'ِDescription List', + }, + { + id: 'description_list_description', + value: 'Description List Component Documentation', + }, + //#endregion + + // + //#region ExpandableList ... + { + id: 'expandable_list', + value: 'Expandable List', + }, + { + id: 'expandable_list_description', + value: 'Expandable List Component Documentation', + }, + //#endregion + + // + //#region ExpandablePanel ... + { + id: 'expandable_panel', + value: 'Expandable Panel', + }, + { + id: 'expandable_panel_description', + value: 'Expandable Panel Component Documentation', + }, + //#endregion + + // + //#region ModelPresenter ... + { + id: 'model_presenter', + value: 'Model Presenter', + }, + { + id: 'model_presenter_description', + value: 'Model Presenter Component Documentation', + }, + //#endregion + + // + //#region Slider ... + { + id: 'slider', + value: 'Slider', + }, + { + id: 'slider_description', + value: 'Slider Component Documentation', + }, + //#endregion + + // + //#region Stepper ... + { + id: 'stepper', + value: 'Stepper', + }, + { + id: 'stepper_description', + value: 'Stepper Component Documentation', + }, + //#endregion + + // + //#region Tabs ... + { + id: 'tabs', + value: 'Tabs', + }, + { + id: 'tabs_description', + value: 'Tabs Component Documentation', + }, + //#endregion + + // + //#region Table ... + { + id: 'table', + value: 'Table', + }, + { + id: 'table_description', + value: 'Table Component Documentation', + }, + //#endregion + + // + //#region Page ... + { + id: 'page', + value: 'Page', + }, + { + id: 'page_description', + value: 'Page Component Documentation', + }, + //#endregion + + // + //#region TabNavigation ... + { + id: 'tab_navigator', + value: 'Tab Navigator', + }, + { + id: 'tab_navigator_description', + value: 'Tab Navigator Component Documentation', + }, + //#endregion + //#endregion + + // + //#region TabsNav ... + { + id: 'tab_page_1', + value: 'Page 1', + }, + { + id: 'tab_page_2', + value: 'Page 2', + }, + { + id: 'tab_page_3', + value: 'Page 3', + }, + //#endregion + + // + //#region Theme ... + { + id: 'theme', + value: 'Themes', + }, + { + id: 'themes_description', + value: 'Access All Theme Management tools', + }, + // + { + id: 'theme_manager', + value: 'Theme Manager', + }, + { + id: 'theme_manager_description', + value: 'add/remove and edit Application Themes', + }, + //#endregion +]; + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\config\audio.config.ts + +import { XNotificationSound, XActionType } from 'x-framework-core'; + +export const NotificationAudioSources: XNotificationSound[] = [ + { + type: XActionType.User, + resourcePath: './assets/audio/user_notification.wav', + }, + { + type: XActionType.System, + resourcePath: './assets/audio/system_notification.wav', + }, + { + type: XActionType.Capture, + resourcePath: './assets/audio/capture_notification.wav', + }, +]; + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\config\exceptions.config.ts + +import { XTranslationItem } from 'x-framework-core'; + +/** + * Persian Exception Translations ... + */ +export const XFaIRExceptions: XTranslationItem[] = [ + // + //#region Invalid Exceptions 1xx ... + { + id: 'ex_100', + value: 'داده نا معتبر', + }, + { + id: 'ex_101', + value: 'تاریخ نامعتبر', + }, + { + id: 'ex_102', + value: 'درخواست اشتباه', + }, + { + id: 'ex_103', + value: 'ورودی نامعتبر', + }, + { + id: 'ex_104', + value: 'پیام نامعتبر', + }, + { + id: 'ex_105', + value: 'نشانه نامعتبر', + }, + { + id: 'ex_106', + value: 'کد اععتبارسنجی نامعتبر', + }, + { + id: 'ex_107', + value: 'پست الکترونیک نامعتبر', + }, + { + id: 'ex_108', + value: 'شماره تلفن همراه نامعتبر', + }, + { + id: 'ex_109', + value: 'کلمه عبور نامعتبر', + }, + { + id: 'ex_110', + value: 'آدرس اینترنتی نامعتبر', + }, + { + id: 'ex_111', + value: 'دستگاه نامعتبر', + }, + { + id: 'ex_112', + value: 'تصویر پروفایل نامعتبر', + }, + { + id: 'ex_113', + value: 'فایل نامعتبر', + }, + { + id: 'ex_114', + value: 'نام کاربری نامعتبر', + }, + //#endregion + + // + //#region Not (...) Exceptions 2xx ... + { + id: 'ex_200', + value: 'یافت نشد', + }, + { + id: 'ex_201', + value: 'عدم تایید جواز', + }, + { + id: 'ex_202', + value: 'سطح دسترسی نامعتبر', + }, + { + id: 'ex_203', + value: 'غیر قابل نمایش', + }, + //#endregion + + // + //#region User/Registration/Account/Bann Exceptions 3xx ... + { + id: 'ex_300', + value: 'حساب کاربری قفل شده است', + }, + { + id: 'ex_301', + value: 'خطا در ورود', + }, + { + id: 'ex_302', + value: 'کاربر قبلا ثبت نام کرده است', + }, + { + id: 'ex_303', + value: 'کاربر غیرفعال است', + }, + { + id: 'ex_304', + value: 'دستگاه شما قفل شده است تا : (0)', + }, + { + id: 'ex_305', + value: 'امکان درخواست کد اعتبارسنجی وجود ندارد تا : (0)', + }, + { + id: 'ex_306', + value: 'کلمه عبورها یکسان هستند', + }, + { + id: 'ex_307', + value: 'شماره های تلفن یکسان هستند', + }, + { + id: 'ex_308', + value: 'آدرس های پست الکترونیک یکسان هستند', + }, + { + id: 'ex_309', + value: 'شماره تلفن قبلا ثبت شده است', + }, + { + id: 'ex_310', + value: 'پست الکترونیک قبلا ثبت شده است', + }, + { + id: 'ex_311', + value: 'تلفن همراه تایید نشده است', + }, + { + id: 'ex_312', + value: 'پست الکترونیک تایید نشده است', + }, + { + id: 'ex_313', + value: 'مطلب ممنوعه', + }, + { + id: 'ex_314', + value: 'کاربر ممنوعه', + }, + //#endregion + + // + //#region General Exceptions 4xx ... + { + id: 'ex_400', + value: 'خطای نا شناخته', + }, + { + id: 'ex_401', + value: 'مورد تکراری', + }, + { + id: 'ex_402', + value: 'شکست عملیات', + }, + { + id: 'ex_403', + value: 'تاریخ مصرف منقضی شده است', + }, + { + id: 'ex_404', + value: 'غیر قابل اسفاده', + }, + { + id: 'ex_405', + value: 'تعداد برچسب ها بیش از جد مجاز است', + }, + { + id: 'ex_406', + value: 'اتمام مهلت زمانی', + }, + //#endregion + + // + //#region File Exceptions 5xx ... + { + id: 'ex_500', + value: 'پرونده خالی است', + }, + { + id: 'ex_501', + value: 'اندازه بیش از حد مجاز', + }, + { + id: 'ex_502', + value: 'گونه نامعتبر فایل', + }, + { + id: 'ex_503', + value: 'تعداد رسانه ها بیش از حد مجاز است', + }, + //#endregion + + // + //#region Configuration Exceptions 6xx ... + { + id: 'ex_600', + value: 'پیکربندی اشتباه', + }, + { + id: 'ex_601', + value: 'خطا در پیکربندی سامانه ذخیره سازی', + }, + { + id: 'ex_602', + value: 'خطا در پیکربندی سامانه ذخیره سازی', + }, + //#endregion + + // + //#region xFramework Exceptions 9xx ... + { + id: 'ex_900', + value: 'تعداد فایل ها بیش از حد مجاز است', + }, + { + id: 'ex_901', + value: 'مجموع اندازه فایل ها کمتر از حد مجاز است', + }, + { + id: 'ex_902', + value: 'مجموع اندازه فایل ها بیش از حد مجاز است', + }, + { + id: 'ex_903', + value: 'شرایط و ضوابط باید پذیرفته شوند', + }, + { + id: 'ex_904', + value: 'عدم پشتیبانی از سیستم موقعیت یاب', + }, + { + id: 'ex_905', + value: 'مجوز دسترسی به موقعیت خود را صادر نکرده اید', + }, + { + id: 'ex_906', + value: 'دسترسی به موقعیت جغرافیایی امکان پذیر نیست', + }, + { + id: 'ex_907', + value: 'عدم پشتیبانی از سیستم ذخیره سازی', + }, + { + id: 'ex_908', + value: 'برای دسترسی به محتوا باید از سامانه خارج شوید', + }, + { + id: 'ex_909', + value: 'زبان انتخاب شده نامعتبر است', + }, + { + id: 'ex_910', + value: 'داده نامعتبر برای انتخاب کاربر', + }, + //#endregion +]; + +/** + * English Exception Translations ... + */ +export const XEnUSExceptions: XTranslationItem[] = [ + // + //#region Invalid Exceptions 1xx ... + { + id: 'ex_100', + value: 'Invalid Data ...', + }, + { + id: 'ex_101', + value: 'Invalid Date ...', + }, + { + id: 'ex_102', + value: 'Bad Request ...', + }, + { + id: 'ex_103', + value: 'Invalid Args ...', + }, + { + id: 'ex_104', + value: 'Invalid Message ...', + }, + { + id: 'ex_105', + value: 'Invalid Token ...', + }, + { + id: 'ex_106', + value: 'Invalid Verification Code ...', + }, + { + id: 'ex_107', + value: 'Invalid Email Address ...', + }, + { + id: 'ex_108', + value: 'Invalid Mobile Number ...', + }, + { + id: 'ex_109', + value: 'Invalid Password ...', + }, + { + id: 'ex_110', + value: 'Invalid URL ...', + }, + { + id: 'ex_111', + value: 'Invalid Device ...', + }, + { + id: 'ex_112', + value: 'Invalid Profile Image ...', + }, + { + id: 'ex_113', + value: 'Invalid File ...', + }, + { + id: 'ex_114', + value: 'Invalid Username ...', + }, + //#endregion + + // + //#region Not (...) Exceptions 2xx ... + { + id: 'ex_200', + value: 'Not Found ...', + }, + { + id: 'ex_201', + value: 'Not Allowed ...', + }, + { + id: 'ex_202', + value: 'Not Authorized ...', + }, + { + id: 'ex_203', + value: 'Not Visible ...', + }, + //#endregion + + // + //#region User/Registration/Account/Bann Exceptions 3xx ... + { + id: 'ex_300', + value: 'Account Locked out ...', + }, + { + id: 'ex_301', + value: 'Login Failed ...', + }, + { + id: 'ex_302', + value: 'User Registered Before ...', + }, + { + id: 'ex_303', + value: 'User Disabled ...', + }, + { + id: 'ex_304', + value: 'Device Banned Until:(0)', + }, + { + id: 'ex_305', + value: `You can't Request Verification code Until:(0)`, + }, + { + id: 'ex_306', + value: 'Passwords Same ...', + }, + { + id: 'ex_307', + value: 'Mobiles Same ...', + }, + { + id: 'ex_308', + value: 'Emails Same ...', + }, + { + id: 'ex_309', + value: 'Mobile In Used ...', + }, + { + id: 'ex_310', + value: 'Email In Used ...', + }, + { + id: 'ex_311', + value: 'Mobile Not Confirmed ...', + }, + { + id: 'ex_312', + value: 'Email Not Confirmed ...', + }, + { + id: 'ex_313', + value: 'Post Banned ...', + }, + { + id: 'ex_314', + value: 'User Banned ...', + }, + //#endregion + + // + //#region General Exceptions 4xx ... + { + id: 'ex_400', + value: 'Unknown Error ...', + }, + { + id: 'ex_401', + value: 'Duplicate ...', + }, + { + id: 'ex_402', + value: 'Action Failed ...', + }, + { + id: 'ex_403', + value: 'Expired Date ...', + }, + { + id: 'ex_404', + value: 'Unavailable ...', + }, + { + id: 'ex_405', + value: 'Max Allowed Tag Exceeded ...', + }, + { + id: 'ex_406', + value: 'Timeout ...', + }, + //#endregion + + // + //#region File Exceptions 5xx ... + { + id: 'ex_500', + value: 'Empty File ...', + }, + { + id: 'ex_501', + value: 'max File size exceeded ...', + }, + { + id: 'ex_502', + value: 'Unsupported File Type ...', + }, + { + id: 'ex_503', + value: 'Max Allowed Media Exceeded ...', + }, + //#endregion + + // + //#region Configuration Exceptions 6xx ... + { + id: 'ex_600', + value: 'Invalid Configuration ...', + }, + { + id: 'ex_601', + value: 'Storage Service Initiallization Failed ...', + }, + { + id: 'ex_602', + value: 'Message Service Initialization Failed ...', + }, + //#endregion + + // + //#region xFramework Exceptions 9xx ... + { + id: 'ex_900', + value: 'Max Allowed Files Exceeded ...', + }, + { + id: 'ex_901', + value: 'Min Files Size Exceeded ...', + }, + { + id: 'ex_902', + value: 'Max Files Size Exceeded ...', + }, + { + id: 'ex_903', + value: 'Must Accept Terms And Conditions ...', + }, + { + id: 'ex_904', + value: 'Browser does not support Location Services ...', + }, + { + id: 'ex_905', + value: 'You have rejected access to your Location ...', + }, + { + id: 'ex_906', + value: 'Unable to determine your Location ...', + }, + { + id: 'ex_907', + value: 'Browser does not support Storage Services ...', + }, + { + id: 'ex_908', + value: 'Must Logout to access content ...', + }, + { + id: 'ex_909', + value: 'Invalid Locale ...', + }, + { + id: 'ex_910', + value: 'Invalid User Selector ...', + }, + //#endregion +]; + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\config\localization.config.ts + +import { XLocale, XLanguageDescriptor } from 'x-framework-core'; +import { XFaIRExceptions, XEnUSExceptions } from './exceptions.config'; +import { EnCustomLocales, FaCustomLocales } from './app.localization.config'; + +export const DefaultLocale: XLocale = 'fa-IR'; + +export const AvailableTranslationResources: XLanguageDescriptor[] = [ + { + name: 'persian', + locale: 'fa-IR', + language: 'fa', + direction: 'rtl', + resources: [ + // + //#region App ... + // TODO: Customize this ... + { + id: 'app_name', + value: 'چارچوب ایکس', + }, + // + // TODO: Customize this ... + { + id: 'company', + value: 'فن آوران ساحر علم', + }, + //#endregion + + // + //#region Commons ... + { + id: 'id', + value: 'شناسه', + }, + { + id: 'file_name', + value: 'نام فایل', + }, + + // + { + id: 'error', + value: 'خطا', + }, + { + id: 'required', + value: 'الزامی است.', + }, + + // + { + id: 'loading', + value: 'بارگزاری', + }, + + // + { + id: 'username', + value: 'نام کاربری', + }, + { + id: 'password', + value: 'کلمه عبور', + }, + + // + { + id: 'first_name', + value: 'نام', + }, + { + id: 'last_name', + value: 'نام خانوادگی', + }, + + // + { + id: 'verified', + value: 'تایید شده', + }, + { + id: 'unverified', + value: 'تایید نشده', + }, + + // + { + id: 'thumbnail', + value: 'پیش نمایش', + }, + + // + { + id: 'language', + value: 'زبان', + }, + + // + { + id: 'take_screenshot', + value: 'تصویر برداری ازمحیط', + }, + { + id: 'enable_capture_mode', + value: 'فعالسازی حالت تصویر برداری', + }, + { + id: 'disable_capture_mode', + value: 'غیرفعالسازی حالت تصویر برداری', + }, + //#endregion + + // + //#region File Types ... + { + id: 'audio', + value: 'صوت', + }, + { + id: 'video', + value: 'فیلم', + }, + { + id: 'document', + value: 'سند', + }, + { + id: 'cover_image', + value: 'تصویر', + }, + { + id: 'image', + value: 'تصویر', + }, + { + id: 'avatar', + value: 'تصویر پروفایل', + }, + { + id: 'unknown', + value: 'ناشناس', + }, + { + id: 'location', + value: 'موقعیت جغرافیای', + }, + //#endregion + + // + //#region Messages ... + { + id: 'are_you_sure', + value: 'اطمینان دارید ؟', + }, + { + id: 'success_msg', + value: 'عملیات با موفقیت انجام شد', + }, + { + id: 'locked_msg', + value: + 'برای ترک این صفحه باید فعالیت جاری را لغو کرده و یا به اتمام برسانید', + }, + { + id: 'empty_content', + value: 'خالی است', + }, + { + id: 'no_item', + value: 'محتوایی ثبت نشده است', + }, + { + id: 'drag_drop_here', + value: 'محل رها کردن فایل', + }, + //#endregion + + // + //#region Components ... + { + id: 'map_dialog_title', + value: 'نمایش موقعیت مکانی', + }, + { + id: 'map_dialog_select_title', + value: 'انتخاب موقعیت جغرافیایی', + }, + { + id: 'color_picker_dialog_title', + value: 'انتخاب رنگ', + }, + //#endregion + + // + //#region Verbs ... + { + id: 'dismiss', + value: 'رد کردن', + }, + { + id: 'accept', + value: 'می پذیرم', + }, + { + id: 'ok', + value: 'بسیار خب', + }, + { + id: 'save', + value: 'ذخیره', + }, + { + id: 'cancel', + value: 'لغو', + }, + { + id: 'retry', + value: 'سعی مجدد', + }, + { + id: 'edit', + value: 'ویرایش', + }, + { + id: 'reset', + value: 'باز نشاندن', + }, + { + id: 'change', + value: 'تغییر', + }, + { + id: 'start', + value: 'آغاز', + }, + { + id: 'yes', + value: 'بله', + }, + { + id: 'no', + value: 'خیر', + }, + { + id: 'verify', + value: 'تایید کردن', + }, + { + id: 'confirm', + value: 'تاییدیه', + }, + { + id: 'crop', + value: 'برش', + }, + { + id: 'clear', + value: 'پاک کردن', + }, + // + { + id: 'set', + value: 'تنظیم', + }, + { + id: 'select', + value: 'انتخاب', + }, + { + id: 'send', + value: 'ارسال', + }, + { + id: 'show', + value: 'نمایش دادن', + }, + { + id: 'hide', + value: 'پنهان کردن', + }, + { + id: 'search', + value: 'جستجو', + }, + { + id: 'delete', + value: 'حذف', + }, + { + id: 'clone', + value: 'نمونه گیری', + }, + { + id: 'select_all', + value: 'انتخاب همه', + }, + { + id: 'select_file', + value: 'انتخاب فایل(ها)', + }, + { + id: 'select_all_group', + value: 'انتخاب همه موارد فیلتر شده', + }, + { + id: 'deselect_all', + value: 'لغو همه انتخاب ها', + }, + { + id: 'deselect_all_group', + value: 'لغو انتخاب همه موارد فیلتر شده', + }, + { + id: 'inverse_selection', + value: 'انتخاب معکوس', + }, + // + { + id: 'selected_location', + value: 'انتخاب محل', + }, + { + id: 'preset_colors', + value: 'رنگ های پیش فرض', + }, + { + id: 'copy_to_clipboard', + value: 'ثبت در حافظه موقت', + }, + { + id: 'color', + value: 'رنگ', + }, + { + id: 'colors', + value: 'رنگها', + }, + { + id: 'upload', + value: 'بارگزاری', + }, + { + id: 'import', + value: 'وارد کردن', + }, + { + id: 'continue', + value: 'ادامه', + }, + { + id: 'finish', + value: 'پایان', + }, + { + id: 'enable', + value: 'فعال', + }, + { + id: 'disable', + value: 'غیر فعال', + }, + { + id: 'all', + value: 'همه', + }, + { + id: 'new', + value: 'جدید', + }, + { + id: 'add', + value: 'افزودن', + }, + { + id: 'update', + value: 'بروز رسانی', + }, + { + id: 'details', + value: 'جزئیات', + }, + { + id: 'translation', + value: 'ترجمه', + }, + { + id: 'download', + value: 'دریافت', + }, + { + id: 'list', + value: 'فهرست', + }, + { + id: 'options', + value: 'انتخاب ها', + }, + { + id: 'descriptions', + value: 'توضیحات', + }, + + // + { + id: 'type', + value: 'نوع', + }, + // + { + id: 'media', + value: 'رسانه', + }, + { + id: 'medias', + value: 'رسانه ها', + }, + + // + { + id: 'go', + value: 'رفتن', + }, + { + id: 'input', + value: 'ورود', + }, + { + id: 'preview', + value: 'پیش نمایش', + }, + //#endregion + + // + //#region Pagination ... + { + id: 'first', + value: 'نخستین', + }, + { + id: 'last', + value: 'آخرین', + }, + { + id: 'previous', + value: 'قبلی', + }, + { + id: 'next', + value: 'بعدی', + }, + { + id: 'back', + value: 'بازگشت', + }, + //#endregion + + // + //#region Markdown ... + { + id: 'x_markdown_preview', + value: 'پیش نمایش', + }, + { + id: 'x_markdown_image', + value: 'تصویر', + }, + { + id: 'x_markdown_link', + value: 'پیوند', + }, + { + id: 'x_markdown_ol', + value: 'فهرست ترتیبی', + }, + { + id: 'x_markdown_ul', + value: 'فهرست بدون ترتیب', + }, + { + id: 'x_markdown_quote', + value: 'نقل قول', + }, + { + id: 'x_markdown_code', + value: 'کد منبع', + }, + { + id: 'x_markdown_heading', + value: 'عنوان', + }, + { + id: 'x_markdown_italic', + value: 'ایتالیک', + }, + { + id: 'x_markdown_bold', + value: 'پر رنگ', + }, + { + id: 'x_markdown_guide', + value: 'راهنمای نگارش', + }, + //#endregion + + // + //#region Theme Related ... + // + { + id: 'theme', + value: 'پوسته', + }, + { + id: 'theme_mode', + value: 'حالت نمایشی', + }, + // + { + id: 'theme_dark', + value: 'تیره', + }, + { + id: 'theme_light', + value: 'روشن', + }, + // + { + id: 'theme_name', + value: 'نام پوسته', + }, + { + id: 'theme_name_description', + value: 'نام پوسته را وارد کنید', + }, + // + { + id: 'theme_clone_title', + value: 'نمونه گیری از پوسته', + }, + //#endregion + + // + //#region Color Picker Related ... + { + id: 'input_color_title', + value: 'تعیین رنگ', + }, + { + id: 'input_color_description', + value: 'مقدار رنگ را وارد کنید', + }, + //#endregion + + // + // Custom Translations ... + ...FaCustomLocales, + + // + // Persian Translated Exceptions ... + ...XFaIRExceptions, + ], + }, + { + name: 'english', + locale: 'en-US', + language: 'en', + direction: 'ltr', + resources: [ + // + //#region App ... + // TODO: Customize this ... + { + id: 'app_name', + value: 'XFramework', + }, + // + // TODO: Customize this ... + { + id: 'company', + value: 'SaherElm IT Center', + }, + //#endregion + + // + //#region Commons ... + { + id: 'id', + value: 'ID', + }, + { + id: 'file_name', + value: 'File Name', + }, + + // + { + id: 'error', + value: 'Error', + }, + { + id: 'required', + value: 'required', + }, + + // + { + id: 'loading', + value: 'loading', + }, + + // + { + id: 'username', + value: 'Username', + }, + { + id: 'password', + value: 'Password', + }, + + // + { + id: 'first_name', + value: 'Firstname', + }, + { + id: 'last_name', + value: 'Lastname', + }, + + // + { + id: 'verified', + value: 'Verified', + }, + { + id: 'unverified', + value: 'Unverified', + }, + + // + { + id: 'thumbnail', + value: 'Thumbnail', + }, + + // + { + id: 'language', + value: 'Language', + }, + + // + { + id: 'take_screenshot', + value: 'Take Screenshot', + }, + { + id: 'enable_capture_mode', + value: 'Enable Capture Mode', + }, + { + id: 'disable_capture_mode', + value: 'Disable Capture Mode', + }, + //#endregion + + // + //#region File Types ... + { + id: 'audio', + value: 'Audio', + }, + { + id: 'video', + value: 'Video', + }, + { + id: 'document', + value: 'Document', + }, + { + id: 'cover_image', + value: 'Cover Image', + }, + { + id: 'image', + value: 'Image', + }, + { + id: 'avatar', + value: 'Avatar', + }, + { + id: 'unknown', + value: 'Unknown', + }, + { + id: 'location', + value: 'Location', + }, + //#endregion + + // + //#region Messages ... + { + id: 'are_you_sure', + value: 'Are You Sure ?', + }, + { + id: 'success_msg', + value: 'Operation done Successfully ...', + }, + { + id: 'locked_msg', + value: 'You have to finish/cancel current proccess for leaving', + }, + { + id: 'empty_content', + value: 'Empty', + }, + { + id: 'no_item', + value: 'there is no Content', + }, + { + id: 'drag_drop_here', + value: 'Drop File Here', + }, + //#endregion + + // + //#region Components ... + { + id: 'map_dialog_title', + value: 'Show Location', + }, + { + id: 'map_dialog_select_title', + value: 'Select Location', + }, + { + id: 'color_picker_dialog_title', + value: 'Select Color', + }, + //#endregion + + // + //#region Verbs ... + { + id: 'dismiss', + value: 'dismiss', + }, + { + id: 'accept', + value: 'Accept', + }, + { + id: 'ok', + value: 'Ok', + }, + { + id: 'save', + value: 'Save', + }, + { + id: 'cancel', + value: 'Cancel', + }, + { + id: 'retry', + value: 'Retry', + }, + { + id: 'edit', + value: 'Edit', + }, + { + id: 'reset', + value: 'Reset', + }, + { + id: 'change', + value: 'Change', + }, + { + id: 'start', + value: 'Start', + }, + { + id: 'yes', + value: 'Yes', + }, + { + id: 'no', + value: 'No', + }, + { + id: 'verify', + value: 'Verify', + }, + { + id: 'confirm', + value: 'Confirm', + }, + { + id: 'crop', + value: 'Crop', + }, + { + id: 'clear', + value: 'Clear', + }, + // + { + id: 'set', + value: 'Set', + }, + { + id: 'select', + value: 'Select', + }, + { + id: 'send', + value: 'Send', + }, + { + id: 'show', + value: 'Show', + }, + { + id: 'hide', + value: 'Hide', + }, + { + id: 'search', + value: 'Search', + }, + { + id: 'delete', + value: 'Delete', + }, + { + id: 'clone', + value: 'Clone', + }, + { + id: 'select_all', + value: 'Select All', + }, + { + id: 'select_file', + value: 'Select File(s)', + }, + { + id: 'select_all_group', + value: 'Select All Filtered Items', + }, + { + id: 'deselect_all', + value: 'Deselect All', + }, + { + id: 'deselect_all_group', + value: 'Deselect All Filtered Items', + }, + { + id: 'inverse_selection', + value: 'Inverse Selection', + }, + // + { + id: 'selected_location', + value: 'Selected Location', + }, + { + id: 'preset_colors', + value: 'Preset Colors', + }, + { + id: 'copy_to_clipboard', + value: 'Copy to Clipboard', + }, + { + id: 'color', + value: 'Color', + }, + { + id: 'colors', + value: 'Colors', + }, + // + { + id: 'upload', + value: 'Upload', + }, + { + id: 'import', + value: 'Import', + }, + { + id: 'continue', + value: 'Continue', + }, + { + id: 'finish', + value: 'Finish', + }, + { + id: 'enable', + value: 'Enable', + }, + { + id: 'disable', + value: 'Disable', + }, + { + id: 'all', + value: 'All', + }, + { + id: 'new', + value: 'New', + }, + { + id: 'add', + value: 'Add', + }, + { + id: 'update', + value: 'Update', + }, + { + id: 'details', + value: 'Details', + }, + { + id: 'translation', + value: 'Translation', + }, + { + id: 'download', + value: 'Download', + }, + { + id: 'list', + value: 'List', + }, + { + id: 'options', + value: 'Options', + }, + { + id: 'descriptions', + value: 'Description', + }, + + // + { + id: 'type', + value: 'Type', + }, + // + { + id: 'media', + value: 'Media', + }, + { + id: 'medias', + value: 'Medias', + }, + + // + { + id: 'go', + value: 'Go', + }, + { + id: 'input', + value: 'Input', + }, + { + id: 'preview', + value: 'Preview', + }, + //#endregion + + // + //#region Pagination ... + { + id: 'first', + value: 'First', + }, + { + id: 'last', + value: 'Last', + }, + { + id: 'previous', + value: 'Previous', + }, + { + id: 'next', + value: 'Next', + }, + { + id: 'back', + value: 'Back', + }, + //#endregion + + // + //#region Markdown ... + { + id: 'x_markdown_preview', + value: 'Preview', + }, + { + id: 'x_markdown_image', + value: 'Image', + }, + { + id: 'x_markdown_link', + value: 'Link', + }, + { + id: 'x_markdown_ol', + value: 'Ordered List', + }, + { + id: 'x_markdown_ul', + value: 'Unordered List', + }, + { + id: 'x_markdown_quote', + value: 'Quote', + }, + { + id: 'x_markdown_code', + value: 'Code', + }, + { + id: 'x_markdown_heading', + value: 'Heading', + }, + { + id: 'x_markdown_italic', + value: 'Italic', + }, + { + id: 'x_markdown_bold', + value: 'Bold', + }, + { + id: 'x_markdown_guide', + value: 'Markdown Guide', + }, + //#endregion + + // + //#region Theme Related ... + // + { + id: 'theme', + value: 'Theme', + }, + { + id: 'theme_mode', + value: 'Theme Mode', + }, + // + { + id: 'theme_dark', + value: 'Dark', + }, + { + id: 'theme_light', + value: 'Light', + }, + // + { + id: 'theme_name', + value: 'Theme Name', + }, + { + id: 'theme_name_description', + value: 'Enter Theme name', + }, + // + { + id: 'theme_clone_title', + value: 'Theme Cloning', + }, + //#endregion + + // + //#region Color Picker Related ... + { + id: 'input_color_title', + value: 'Color Value', + }, + { + id: 'input_color_description', + value: 'input color value', + }, + //#endregion + + // + // Custom Translations ... + ...EnCustomLocales, + + // + // Persian Translated Exceptions ... + ...XEnUSExceptions, + ], + }, +]; + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\config\page.config.ts + +import { + XIconNames, + XTabNavigatorItem, + XNavigatorListItem, + toTabNavigatiorItem, + toNavigatiorListItem, +} from 'x-framework-components'; +import { XPage } from 'x-framework-core'; +import { AppResourceIDs } from './app.localization.config'; + +// +//#region Route Config ... +export enum BaseRoutes { + Default = '', + Unknown = '**', +} + +export enum HomeRoutes { + Default = 'home', + Home = 'home', + Introduction = 'introduction', +} + +export enum CoreRoutes { + Default = 'core', + Core = 'core', + Introduction = 'introduction', +} + +export enum ServicesRoutes { + Default = 'services', + Services = 'services', + Introduction = 'introduction', + UserDevice = 'user_device', +} + +export enum ComponentsRoutes { + Default = 'components', + Components = 'components', + Introduction = 'introduction', + SmallComponents = 'small-components', + Card = 'card', + Icon = 'icon', + Counter = 'counter', + ColorPicker = 'color-picker', + Fab = 'fab', + FileComponents = 'file-components', + Form = 'form', + Grid = 'grid', + List = 'list', + Map = 'map', + MarkDown = 'markdown', + Tree = 'tree', + DescriptionList = 'description-list', + ExpandableList = 'expandable-list', + ExpandablePanel = 'expandable-panel', + ModelPresenter = 'model-presenter', + Slider = 'slider', + Stepper = 'stepper', + Tabs = 'tabs', + Table = 'table', + Page = 'page', + TabNavigator = 'tab-navigator', +} + +export enum TabsRoutes { + Default = 'tabs', + Tabs = 'tabs', + TabPage1 = 'tab-page-1', + TabPage2 = 'tab-page-2', + TabPage3 = 'tab-page-3', +} + +export enum ThemesRoutes { + Default = 'themes', + Themes = 'themes', + ThemeManager = 'manager', +} + +export enum AppRoutes { + Default = BaseRoutes.Default, + Home = HomeRoutes.Default, + Core = CoreRoutes.Default, + Services = ServicesRoutes.Default, + Components = ComponentsRoutes.Default, + TabsNav = TabsRoutes.Default, + Themes = ThemesRoutes.Default, + Unknown = BaseRoutes.Unknown, +} +//#endregion + +// +//#region Page Configs ... +export enum PageName { + // + // Home ... + Home = AppRoutes.Home, + HomeIntoduction = HomeRoutes.Introduction, + + // + // Core ... + Core = AppRoutes.Core, + CoreIntoduction = CoreRoutes.Introduction, + + // + // Services ... + Services = AppRoutes.Services, + ServicesIntoduction = ServicesRoutes.Introduction, + UserDevice = ServicesRoutes.UserDevice, + + // + // Components ... + Components = AppRoutes.Components, + ComponentsIntoduction = ComponentsRoutes.Introduction, + SmallComponents = ComponentsRoutes.SmallComponents, + Card = ComponentsRoutes.Card, + Icon = ComponentsRoutes.Icon, + Counter = ComponentsRoutes.Counter, + ColorPicker = ComponentsRoutes.ColorPicker, + Fab = ComponentsRoutes.Fab, + FileComponents = ComponentsRoutes.FileComponents, + Form = ComponentsRoutes.Form, + Grid = ComponentsRoutes.Grid, + List = ComponentsRoutes.List, + Map = ComponentsRoutes.Map, + MarkDown = ComponentsRoutes.MarkDown, + Tree = ComponentsRoutes.Tree, + DescriptionList = ComponentsRoutes.DescriptionList, + ExpandableList = ComponentsRoutes.ExpandableList, + ExpandablePanel = ComponentsRoutes.ExpandablePanel, + ModelPresenter = ComponentsRoutes.ModelPresenter, + Slider = ComponentsRoutes.Slider, + Stepper = ComponentsRoutes.Stepper, + Tabs = ComponentsRoutes.Tabs, + Table = ComponentsRoutes.Table, + Page = ComponentsRoutes.Page, + TabNavigator = ComponentsRoutes.TabNavigator, + + // + // Tabs Navigation ... + TabsNav = AppRoutes.TabsNav, + TabPage1 = TabsRoutes.TabPage1, + TabPage2 = TabsRoutes.TabPage2, + TabPage3 = TabsRoutes.TabPage3, + + // + // Themes ... + Themes = AppRoutes.Themes, + ThemeManager = ThemesRoutes.ThemeManager, +} + +export type PageNames = keyof typeof PageName; + +export type PageNameIdentifier = PageName | PageNames | string; + +export type PageIndexType = { + [key in PageNames]?: XPage; +}; + +export const PageIndex: PageIndexType = { + // + Home: { + icon: XIconNames.home, + id: `${PageName.Home}`, + name: `${PageName.Home}`, + title: AppResourceIDs.home, + baseRoute: `${AppRoutes.Home}`, + route: ['/', `${AppRoutes.Home}`], + description: AppResourceIDs.home_description, + childs: [ + // + // Intoduction ... + { + icon: XIconNames.guide, + id: `${PageName.HomeIntoduction}`, + name: `${PageName.HomeIntoduction}`, + title: AppResourceIDs.home_introduction, + baseRoute: `${HomeRoutes.Introduction}`, + description: AppResourceIDs.home_introduction_description, + route: ['/', `${AppRoutes.Home}`, `${HomeRoutes.Introduction}`], + }, + ], + }, + + // + Core: { + icon: XIconNames.event, + id: `${PageName.Core}`, + name: `${PageName.Core}`, + title: AppResourceIDs.core, + baseRoute: `${AppRoutes.Core}`, + route: ['/', `${AppRoutes.Core}`], + description: AppResourceIDs.core_description, + childs: [ + // + // Intoduction ... + { + icon: XIconNames.guide, + id: `${PageName.CoreIntoduction}`, + name: `${PageName.CoreIntoduction}`, + title: AppResourceIDs.core_introduction, + baseRoute: `${CoreRoutes.Introduction}`, + description: AppResourceIDs.core_introduction_description, + route: ['/', `${AppRoutes.Core}`, `${CoreRoutes.Introduction}`], + }, + ], + }, + + // + Services: { + icon: XIconNames.job, + id: `${PageName.Services}`, + name: `${PageName.Services}`, + title: AppResourceIDs.services, + baseRoute: `${AppRoutes.Services}`, + route: ['/', `${AppRoutes.Services}`], + description: AppResourceIDs.services_description, + childs: [ + // + // Intoduction ... + { + icon: XIconNames.guide, + id: `${PageName.ServicesIntoduction}`, + name: `${PageName.ServicesIntoduction}`, + title: AppResourceIDs.services_introduction, + baseRoute: `${ServicesRoutes.Introduction}`, + description: AppResourceIDs.services_introduction_description, + route: ['/', `${AppRoutes.Services}`, `${ServicesRoutes.Introduction}`], + }, + // + // UserDevice ... + { + icon: XIconNames.guide, + id: `${PageName.UserDevice}`, + name: `${PageName.UserDevice}`, + title: AppResourceIDs.user_device, + baseRoute: `${ServicesRoutes.UserDevice}`, + description: AppResourceIDs.user_device_description, + route: ['/', `${AppRoutes.Services}`, `${ServicesRoutes.UserDevice}`], + }, + ], + }, + + // + Components: { + icon: XIconNames.explorer, + id: `${PageName.Components}`, + name: `${PageName.Components}`, + title: AppResourceIDs.components, + baseRoute: `${AppRoutes.Components}`, + route: ['/', `${AppRoutes.Components}`], + description: AppResourceIDs.components_description, + childs: [ + // + // Intoduction ... + { + icon: XIconNames.guide, + id: `${PageName.ComponentsIntoduction}`, + name: `${PageName.ComponentsIntoduction}`, + title: AppResourceIDs.components_introduction, + baseRoute: `${ComponentsRoutes.Introduction}`, + description: AppResourceIDs.components_introduction_description, + route: [ + '/', + `${AppRoutes.Components}`, + `${ComponentsRoutes.Introduction}`, + ], + }, + // + // SmallComponents ... + { + icon: XIconNames.list, + id: `${PageName.SmallComponents}`, + name: `${PageName.SmallComponents}`, + title: AppResourceIDs.small_components, + baseRoute: `${ComponentsRoutes.SmallComponents}`, + description: AppResourceIDs.small_components_description, + route: [ + '/', + `${AppRoutes.Components}`, + `${ComponentsRoutes.SmallComponents}`, + ], + }, + // + // Card ... + { + icon: XIconNames.list, + id: `${PageName.Card}`, + name: `${PageName.Card}`, + title: AppResourceIDs.card, + baseRoute: `${ComponentsRoutes.Card}`, + description: AppResourceIDs.card_description, + route: ['/', `${AppRoutes.Components}`, `${ComponentsRoutes.Card}`], + }, + // + // Icon ... + { + icon: XIconNames.list, + id: `${PageName.Icon}`, + name: `${PageName.Icon}`, + title: AppResourceIDs.icon, + baseRoute: `${ComponentsRoutes.Icon}`, + description: AppResourceIDs.icon_description, + route: ['/', `${AppRoutes.Components}`, `${ComponentsRoutes.Icon}`], + }, + // + // Counter ... + { + icon: XIconNames.list, + id: `${PageName.Counter}`, + name: `${PageName.Counter}`, + title: AppResourceIDs.counter, + baseRoute: `${ComponentsRoutes.Counter}`, + description: AppResourceIDs.counter_description, + route: ['/', `${AppRoutes.Components}`, `${ComponentsRoutes.Counter}`], + }, + // + // ColorPicker ... + { + icon: XIconNames.list, + id: `${PageName.ColorPicker}`, + name: `${PageName.ColorPicker}`, + title: AppResourceIDs.color_picker, + baseRoute: `${ComponentsRoutes.ColorPicker}`, + description: AppResourceIDs.color_picker_description, + route: [ + '/', + `${AppRoutes.Components}`, + `${ComponentsRoutes.ColorPicker}`, + ], + }, + // + // Fab ... + { + id: `${PageName.Fab}`, + icon: XIconNames.list, + name: `${PageName.Fab}`, + title: AppResourceIDs.fab, + baseRoute: `${ComponentsRoutes.Fab}`, + description: AppResourceIDs.fab_description, + route: ['/', `${AppRoutes.Components}`, `${ComponentsRoutes.Fab}`], + }, + // + // FileComponents ... + { + icon: XIconNames.list, + id: `${PageName.FileComponents}`, + name: `${PageName.FileComponents}`, + title: AppResourceIDs.file_components, + baseRoute: `${ComponentsRoutes.FileComponents}`, + description: AppResourceIDs.file_components_description, + route: [ + '/', + `${AppRoutes.Components}`, + `${ComponentsRoutes.FileComponents}`, + ], + }, + // + // Form ... + { + icon: XIconNames.list, + id: `${PageName.Form}`, + name: `${PageName.Form}`, + title: AppResourceIDs.form, + baseRoute: `${ComponentsRoutes.Form}`, + description: AppResourceIDs.form_description, + route: ['/', `${AppRoutes.Components}`, `${ComponentsRoutes.Form}`], + }, + // + // Grid ... + { + icon: XIconNames.list, + id: `${PageName.Grid}`, + name: `${PageName.Grid}`, + title: AppResourceIDs.grid, + baseRoute: `${ComponentsRoutes.Grid}`, + description: AppResourceIDs.grid_description, + route: ['/', `${AppRoutes.Components}`, `${ComponentsRoutes.Grid}`], + }, + // + // List ... + { + icon: XIconNames.list, + id: `${PageName.List}`, + name: `${PageName.List}`, + title: AppResourceIDs.list, + baseRoute: `${ComponentsRoutes.List}`, + description: AppResourceIDs.list_description, + route: ['/', `${AppRoutes.Components}`, `${ComponentsRoutes.List}`], + }, + // + // Map ... + { + icon: XIconNames.list, + id: `${PageName.Map}`, + name: `${PageName.Map}`, + title: AppResourceIDs.map, + baseRoute: `${ComponentsRoutes.Map}`, + description: AppResourceIDs.map_description, + route: ['/', `${AppRoutes.Components}`, `${ComponentsRoutes.Map}`], + }, + // + // MarkDown ... + { + icon: XIconNames.list, + id: `${PageName.MarkDown}`, + name: `${PageName.MarkDown}`, + title: AppResourceIDs.markdown, + baseRoute: `${ComponentsRoutes.MarkDown}`, + description: AppResourceIDs.markdown_description, + route: ['/', `${AppRoutes.Components}`, `${ComponentsRoutes.MarkDown}`], + }, + // + // Tree ... + // TODO: Complete this ... + { + icon: XIconNames.ul, + id: `${PageName.Tree}`, + name: `${PageName.Tree}`, + title: AppResourceIDs.tree, + baseRoute: `${ComponentsRoutes.Tree}`, + description: AppResourceIDs.tree_description, + route: ['/', `${AppRoutes.Components}`, `${ComponentsRoutes.Tree}`], + }, + // + // DescriptionList ... + { + icon: XIconNames.list, + id: `${PageName.DescriptionList}`, + name: `${PageName.DescriptionList}`, + title: AppResourceIDs.description_list, + baseRoute: `${ComponentsRoutes.DescriptionList}`, + description: AppResourceIDs.description_list_description, + route: [ + '/', + `${AppRoutes.Components}`, + `${ComponentsRoutes.DescriptionList}`, + ], + }, + // + // ExpandableList ... + { + icon: XIconNames.list, + id: `${PageName.ExpandableList}`, + name: `${PageName.ExpandableList}`, + title: AppResourceIDs.expandable_list, + baseRoute: `${ComponentsRoutes.ExpandableList}`, + description: AppResourceIDs.expandable_list_description, + route: [ + '/', + `${AppRoutes.Components}`, + `${ComponentsRoutes.ExpandableList}`, + ], + }, + // + // ExpandablePanel ... + { + icon: XIconNames.list, + id: `${PageName.ExpandablePanel}`, + name: `${PageName.ExpandablePanel}`, + title: AppResourceIDs.expandable_panel, + baseRoute: `${ComponentsRoutes.ExpandablePanel}`, + description: AppResourceIDs.expandable_panel_description, + route: [ + '/', + `${AppRoutes.Components}`, + `${ComponentsRoutes.ExpandablePanel}`, + ], + }, + // + // ModelPresenter ... + { + icon: XIconNames.list, + id: `${PageName.ModelPresenter}`, + name: `${PageName.ModelPresenter}`, + title: AppResourceIDs.model_presenter, + baseRoute: `${ComponentsRoutes.ModelPresenter}`, + description: AppResourceIDs.model_presenter_description, + route: [ + '/', + `${AppRoutes.Components}`, + `${ComponentsRoutes.ModelPresenter}`, + ], + }, + // + // Slider ... + { + icon: XIconNames.list, + id: `${PageName.Slider}`, + name: `${PageName.Slider}`, + title: AppResourceIDs.slider, + baseRoute: `${ComponentsRoutes.Slider}`, + description: AppResourceIDs.slider_description, + route: ['/', `${AppRoutes.Components}`, `${ComponentsRoutes.Slider}`], + }, + // + // Stepper ... + { + icon: XIconNames.list, + id: `${PageName.Stepper}`, + name: `${PageName.Stepper}`, + title: AppResourceIDs.stepper, + baseRoute: `${ComponentsRoutes.Stepper}`, + description: AppResourceIDs.stepper_description, + route: ['/', `${AppRoutes.Components}`, `${ComponentsRoutes.Stepper}`], + }, + // + // Tabs ... + { + icon: XIconNames.list, + id: `${PageName.Tabs}`, + name: `${PageName.Tabs}`, + title: AppResourceIDs.tabs, + baseRoute: `${ComponentsRoutes.Tabs}`, + description: AppResourceIDs.tabs_description, + route: ['/', `${AppRoutes.Components}`, `${ComponentsRoutes.Tabs}`], + }, + // + // Table ... + { + icon: XIconNames.list, + id: `${PageName.Table}`, + name: `${PageName.Table}`, + title: AppResourceIDs.table, + baseRoute: `${ComponentsRoutes.Table}`, + description: AppResourceIDs.tabs_description, + route: ['/', `${AppRoutes.Components}`, `${ComponentsRoutes.Table}`], + }, + // + // Page ... + { + icon: XIconNames.list, + id: `${PageName.Page}`, + name: `${PageName.Page}`, + title: AppResourceIDs.page, + baseRoute: `${ComponentsRoutes.Page}`, + description: AppResourceIDs.page_description, + route: ['/', `${AppRoutes.Components}`, `${ComponentsRoutes.Page}`], + }, + // + // TabNavigator ... + { + icon: XIconNames.list, + id: `${PageName.TabNavigator}`, + name: `${PageName.TabNavigator}`, + title: AppResourceIDs.tab_navigator, + baseRoute: `${ComponentsRoutes.TabNavigator}`, + description: AppResourceIDs.tab_navigator_description, + route: [ + '/', + `${AppRoutes.Components}`, + `${ComponentsRoutes.TabNavigator}`, + ], + }, + ], + }, + + // + TabsNav: { + icon: XIconNames.list, + id: `${PageName.TabsNav}`, + name: `${PageName.TabsNav}`, + title: AppResourceIDs.tab_navigator, + baseRoute: `${AppRoutes.TabsNav}`, + route: ['/', `${AppRoutes.TabsNav}`], + description: AppResourceIDs.tab_navigator_description, + childs: [ + // + // TabPage 1 ... + { + icon: XIconNames.list, + id: `${PageName.TabPage1}`, + name: `${PageName.TabPage1}`, + title: AppResourceIDs.tab_page_1, + baseRoute: `${TabsRoutes.TabPage1}`, + description: AppResourceIDs.tab_navigator_description, + route: ['/', `${AppRoutes.TabsNav}`, `${TabsRoutes.TabPage1}`], + }, + // + // TabPage 2 ... + { + icon: XIconNames.list, + id: `${PageName.TabPage2}`, + name: `${PageName.TabPage2}`, + title: AppResourceIDs.tab_page_2, + baseRoute: `${TabsRoutes.TabPage2}`, + description: AppResourceIDs.tab_navigator_description, + route: ['/', `${AppRoutes.TabsNav}`, `${TabsRoutes.TabPage2}`], + }, + // + // TabPage 3 ... + { + icon: XIconNames.list, + id: `${PageName.TabPage3}`, + name: `${PageName.TabPage3}`, + title: AppResourceIDs.tab_page_3, + baseRoute: `${TabsRoutes.TabPage3}`, + description: AppResourceIDs.tab_navigator_description, + route: ['/', `${AppRoutes.TabsNav}`, `${TabsRoutes.TabPage3}`], + }, + ], + }, + + // + Themes: { + icon: XIconNames.color_palette, + id: `${PageName.Themes}`, + name: `${PageName.Themes}`, + title: AppResourceIDs.themes, + baseRoute: `${AppRoutes.Themes}`, + route: ['/', `${AppRoutes.Themes}`], + description: AppResourceIDs.themes_description, + childs: [ + // + // ThemeManager ... + { + icon: XIconNames.color_palette, + id: `${PageName.ThemeManager}`, + name: `${PageName.ThemeManager}`, + title: AppResourceIDs.theme_manager, + baseRoute: `${ThemesRoutes.ThemeManager}`, + description: AppResourceIDs.theme_manager_description, + route: ['/', `${AppRoutes.Themes}`, `${ThemesRoutes.ThemeManager}`], + }, + ], + }, +}; + +// +// a Const of Available Pages ... +export const Pages = { + // + Home: PageIndex.Home, + HomeIntroduction: PageIndex.Home.childs[0], + + // + Core: PageIndex.Core, + CoreIntroduction: PageIndex.Core.childs[0], + + // + Services: PageIndex.Services, + ServicesIntroduction: PageIndex.Services.childs[0], + + // + Components: PageIndex.Components, + ComponentsIntroduction: PageIndex.Components.childs[0], + SmallComponents: PageIndex.Components.childs[1], + Card: PageIndex.Components.childs[2], + Icon: PageIndex.Components.childs[3], + Counter: PageIndex.Components.childs[4], + ColorPicker: PageIndex.Components.childs[5], + Fab: PageIndex.Components.childs[6], + FileUpload: PageIndex.Components.childs[7], + Form: PageIndex.Components.childs[8], + Grid: PageIndex.Components.childs[9], + List: PageIndex.Components.childs[10], + Map: PageIndex.Components.childs[11], + MarkDown: PageIndex.Components.childs[12], + Tree: PageIndex.Components.childs[13], + DescriptionList: PageIndex.Components.childs[14], + ExpandableList: PageIndex.Components.childs[15], + ExpandablePanel: PageIndex.Components.childs[16], + Slider: PageIndex.Components.childs[17], + Stepper: PageIndex.Components.childs[18], + Tabs: PageIndex.Components.childs[19], + Table: PageIndex.Components.childs[20], + Page: PageIndex.Components.childs[21], + TabNavigator: PageIndex.Components.childs[22], + + // + TabsNav: PageIndex.TabsNav, + TabPage1: PageIndex.TabsNav.childs[0], + TabPage2: PageIndex.TabsNav.childs[1], + TabPage3: PageIndex.TabsNav.childs[2], + + // + Themes: PageIndex.Themes, + ThemeManager: PageIndex.Themes.childs[0], +}; + +// +// Admin Navigator Pages ... +export const NavPageItems: XNavigatorListItem[] = [ + // + //#region Home ... + { + ...toNavigatiorListItem(PageIndex.Home), + }, + //#endregion + + // + //#region Core ... + { + ...toNavigatiorListItem(PageIndex.Core), + }, + //#endregion + + // + //#region Services ... + { + ...toNavigatiorListItem(PageIndex.Services), + }, + //#endregion + + // + //#region Components ... + { + ...toNavigatiorListItem(PageIndex.Components), + }, + //#endregion +]; + +// +// Tabs Navigators Pages ... +export const TabNavsItems: XTabNavigatorItem[] = [ + { + ...toTabNavigatiorItem(Pages.TabPage1), + }, + { + ...toTabNavigatiorItem(Pages.TabPage2), + }, + { + ...toTabNavigatiorItem(Pages.TabPage3), + }, +]; +//#endregion + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\config\x-config.ts + +import { XConfig } from './app-config'; +import { environment } from '../../environments/environment'; +import { InjectionToken } from '@angular/core'; + +export const XCONFIG: XConfig = JSON.parse( + JSON.stringify(environment.configurations) +); + +export const X_CONFIG = new InjectionToken('x_config'); + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\components.module.ts + +import { NgModule } from '@angular/core'; +import { FabPage } from './fab/fab.page'; +import { MapPage } from './map/map.page'; +import { FormPage } from './form/form.page'; +import { GridPage } from './grid/grid.page'; +import { ListPage } from './list/list.page'; +import { IconPage } from './icon/icon.page'; +import { CardPage } from './card/card.page'; +import { PagePage } from './page/page.page'; +import { TabsPage } from './tabs/tabs.page'; +import { RouterModule } from '@angular/router'; +import { CommonModule } from '@angular/common'; +import { TablePage } from './table/table.page'; +import { SliderPage } from './slider/slider.page'; +import { CounterPage } from './counter/counter.page'; +import { StepperPage } from './stepper/stepper.page'; +import { MarkdownPage } from './markdown/markdown.page'; +import { ViewsModule } from 'src/app/views/views.module'; +import { ColorPickerPage } from './color-picker/color-picker.page'; +import { IntroductionPage } from './introduction/introduction.page'; +import { TabNavigatorPage } from './tab-navigator/tab-navigator.page'; +import { BaseRoutes, ComponentsRoutes } from 'src/app/config/page.config'; +import { FileComponentsPage } from './file-components/file-components.page'; +import { ExpandableListPage } from './expandable-list/expandable-list.page'; +import { ModelPresenterPage } from './model-presenter/model-presenter.page'; +import { DescriptionListPage } from './description-list/description-list.page'; +import { SmallComponentsPage } from './small-components/small-components.page'; +import { ExpandablePanelPage } from './expandable-panel/expandable-panel.page'; +import { SampleModalComponent } from './small-components/sample-modal/sample-modal.component'; +import { TreePage } from './tree/tree.page'; + +@NgModule({ + imports: [ + ViewsModule, + CommonModule, + RouterModule.forChild([ + { + path: BaseRoutes.Default, + redirectTo: ComponentsRoutes.Introduction, + pathMatch: 'full', + }, + { + path: ComponentsRoutes.Introduction, + component: IntroductionPage, + }, + { + path: ComponentsRoutes.SmallComponents, + component: SmallComponentsPage, + }, + { + path: ComponentsRoutes.Card, + component: CardPage, + }, + { + path: ComponentsRoutes.Icon, + component: IconPage, + }, + { + path: ComponentsRoutes.Counter, + component: CounterPage, + }, + { + path: ComponentsRoutes.ColorPicker, + component: ColorPickerPage, + }, + { + path: ComponentsRoutes.Fab, + component: FabPage, + }, + { + path: ComponentsRoutes.FileComponents, + component: FileComponentsPage, + }, + { + path: ComponentsRoutes.Form, + component: FormPage, + }, + { + path: ComponentsRoutes.Grid, + component: GridPage, + }, + { + path: ComponentsRoutes.List, + component: ListPage, + }, + { + path: ComponentsRoutes.Map, + component: MapPage, + }, + { + path: ComponentsRoutes.MarkDown, + component: MarkdownPage, + }, + { + path: ComponentsRoutes.Tree, + component: TreePage, + }, + { + path: ComponentsRoutes.DescriptionList, + component: DescriptionListPage, + }, + { + path: ComponentsRoutes.ExpandableList, + component: ExpandableListPage, + }, + { + path: ComponentsRoutes.ExpandablePanel, + component: ExpandablePanelPage, + }, + { + path: ComponentsRoutes.ModelPresenter, + component: ModelPresenterPage, + }, + { + path: ComponentsRoutes.Slider, + component: SliderPage, + }, + { + path: ComponentsRoutes.Stepper, + component: StepperPage, + }, + { + path: ComponentsRoutes.Tabs, + component: TabsPage, + }, + { + path: ComponentsRoutes.Table, + component: TablePage, + }, + { + path: ComponentsRoutes.Page, + component: PagePage, + }, + { + path: ComponentsRoutes.TabNavigator, + component: TabNavigatorPage, + }, + ]), + ], + declarations: [ + FabPage, + MapPage, + CardPage, + IconPage, + FormPage, + GridPage, + PagePage, + ListPage, + TabsPage, + TreePage, + TablePage, + SliderPage, + StepperPage, + CounterPage, + MarkdownPage, + ColorPickerPage, + IntroductionPage, + TabNavigatorPage, + ExpandableListPage, + FileComponentsPage, + ModelPresenterPage, + SmallComponentsPage, + ExpandablePanelPage, + DescriptionListPage, + + // + SampleModalComponent, + ], + entryComponents: [SampleModalComponent], +}) +export class ComponentsPageModule {} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\card\card.page.html + + + + + + + + + + + + + + + + +
+ {{resourceProvider(ResourceIDs.header)}} +
+ +
+ {{resourceProvider(ResourceIDs.temp_label)}} +
+ +
+ {{resourceProvider(ResourceIDs.actions)}} +
+ +
+ {{resourceProvider(ResourceIDs.footer)}} +
+
+ + + + +
+ {{resourceProvider(ResourceIDs.temp_label)}} +
+
+
+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\card\card.page.scss + +.content-container { + margin: 24px; +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\card\card.page.ts + +import { Component } from '@angular/core'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; +import { XResourceIDs, XColor, isNullOrEmptyString } from 'x-framework-core'; + +@Component({ + selector: 'app-card', + templateUrl: './card.page.html', + styleUrls: ['./card.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class CardPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.card; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider(AppResourceIDs.card_description); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} +این مولفه جهت نمایش یک جزء از اجزای تشکیل دهنده یک صفحه بکار میرود. + + این مولفه می تواند بسته به ابعاد مختلف صفحه واکنش نشان دهد و خود را مطابق کند. + `; + readonly contentEn = ` + # ${this.toolbarTitle} +this component use to present a part of a page. + + this component can adapt itself based on screen size. + `; + + // + readonly sample1 = + '```html' + + ` + +
+ {{resourceProvider( + ResourceIDs.header)}} +
+ +
+ {{resourceProvider( + ResourceIDs.temp_label)}} +
+ +
+ {{resourceProvider( + ResourceIDs.actions)}} +
+ +
+ {{resourceProvider( + ResourceIDs.footer)}} +
+
+` + + '```'; + + readonly sample2 = + '```html' + + ` + +
+ {{resourceProvider( + ResourceIDs.temp_label)}} +
+
+ ` + + '```'; + + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\color-picker\color-picker.page.html + + + + + + + + + + + + + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\color-picker\color-picker.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\color-picker\color-picker.page.ts + +import { + XColorPickerOutputFormat, + XColorPickerOutputFormatIdentifier, +} from 'x-framework-components'; +import { Component } from '@angular/core'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; +import { XColor, XResourceIDs, isNullOrEmptyString } from 'x-framework-core'; + +@Component({ + selector: 'app-color-picker', + templateUrl: './color-picker.page.html', + styleUrls: ['./color-picker.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class ColorPickerPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.color_picker; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider( + AppResourceIDs.color_picker_description + ); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} +`; + + readonly contentEn = ` + # ${this.toolbarTitle} +`; + + // + readonly sample1 = + '```html' + + ` +` + + '```'; + + // + readonly presetColorsLable = 'PresetColors'; + + // + readonly presetColors = [ + '#fff', + '#000', + '#2889e9', + '#e920e9', + '#fff500', + 'rgb(236,64,64)', + ]; + + readonly outputFormat = XColorPickerOutputFormat.XHEX; + + readonly representFormats: XColorPickerOutputFormatIdentifier[] = [ + XColorPickerOutputFormat.XHEX, + XColorPickerOutputFormat.XRGBA, + // XColorPickerOutputFormat.XHSLA, + ]; + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + //#endregion + + handleColorSelected(color: string) { + console.log('Color Selected From ColorPicker Component: ', color); + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\counter\counter.page.html + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+
+
+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\counter\counter.page.scss + +.content-container { + margin: 24px; +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\counter\counter.page.ts + +import { + XColor, + toTimeFormat, + XResourceIDs, + toPersianNumbers, + isNullOrEmptyString, +} from 'x-framework-core'; +import { + XSlotName, + XSlotLayout, + XCounterType, + XCounterTick, +} from 'x-framework-components'; +import { Component } from '@angular/core'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +@Component({ + selector: 'app-counter', + templateUrl: './counter.page.html', + styleUrls: ['./counter.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class CounterPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.counter; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider(AppResourceIDs.counter_description); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + // Counter Types ... + readonly CounterTypes = Object.assign({}, XCounterType); + + // + // Slotter ... + readonly SlotNames = Object.assign({}, XSlotName); + readonly SlotLayout = Object.assign({}, XSlotLayout); + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} + + این مولفه در مواردی که قصد داریم مدت زمان معینی را برای ادامه یا انجام کاری در نظر بگیریم مورد استفاده قرار می گیرد. + `; + readonly contentEn = ` + # ${this.toolbarTitle} + this component used when we need to wait specific time for continue or doing an action. + `; + + // + readonly sample1 = + '```html' + + ` + +` + + '```'; + + // + readonly sample2 = + '```html' + + ` + +` + + '```'; + + // + readonly sample3 = + '```html' + + ` + +` + + '```'; + + // + readonly sample4 = + '```html' + + ` + +` + + '```'; + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + + getCounterMessage(start: number, stop: number, current: number): string { + // + let steps = stop - start; + if (steps < 0) { + steps *= -1; + } + + // + const ellapsed = steps - current; + const remained = steps - ellapsed; + + // + const minutes = Math.trunc(remained / 60); + const seconds = remained - minutes * 60; + + // + const message = `${toTimeFormat(minutes)}:${toTimeFormat(seconds)}`; + + // + const locale = this.managerService.currentLocale; + + // + if (locale === 'fa-IR') { + return toPersianNumbers(message); + } else { + return message; + } + } + //#endregion + + // + //#region UI Handlers ... + handleCounterStop(event: XCounterTick) {} + + handleCounterStart(event: XCounterTick) {} + + handleCounterTick(event: XCounterTick) {} + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\description-list\description-list.page.html + + + + + + + + + + + + + + + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\description-list\description-list.page.scss + +.content-container { + margin: 24px; +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\description-list\description-list.page.ts + +import { Component } from '@angular/core'; +import { XResourceIDs, isNullOrEmptyString } from 'x-framework-core'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +export interface ItemType { + first_name: string; + last_name: string; + gender: string; + mobile: string; + email: string; +} + +@Component({ + selector: 'app-description-list', + templateUrl: './description-list.page.html', + styleUrls: ['./description-list.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class DescriptionListPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.description_list; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider( + AppResourceIDs.description_list_description + ); + toolbarShowSubTitle = true; + //#endregion + + // + item: ItemType = { + first_name: 'Hadi', + last_name: 'Khazaee Asl', + gender: 'male', + mobile: '09121694056', + email: 'hadi_khazaee_asl@yahoo.com', + }; + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} +این مولفه جهت نمایش یک مدل داده ای در غالبی ساختار یافته بکار میرود + `; + readonly contentEn = ` + # ${this.toolbarTitle} +this component used to show a data model in an structural template + `; + + // + readonly sample1 = + '```html' + + ` + + +` + + '```'; + + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\expandable-list\expandable-list.page.html + + + + + + + + + + + + + + + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\expandable-list\expandable-list.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\expandable-list\expandable-list.page.ts + +import { + XColor, + random, + XResourceIDs, + isNullOrEmptyString, +} from 'x-framework-core'; +import { Component } from '@angular/core'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { XIconNames, XExpandableListItem } from 'x-framework-components'; + +@Component({ + selector: 'app-expandable-list', + templateUrl: './expandable-list.page.html', + styleUrls: ['./expandable-list.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class ExpandableListPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.expandable_list; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider( + AppResourceIDs.expandable_list_description + ); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + readonly IconNames = Object.assign({}, XIconNames); + //#endregion + + // + tempList = Array.from(Array(5).keys()); + tempChilds = Array.from(Array(2).keys()); + listItems: XExpandableListItem[] = this.tempList.map((i) => { + // + const li: XExpandableListItem = { + ...this.toListItem(i), + childs: + random(0, 1) === 0 + ? undefined + : this.tempChilds.map((c) => { + // + const cLi: XExpandableListItem = { + ...this.toListItem(c, i.toString()), + }; + + // + return cLi; + }), + }; + + // + return li; + }); + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} + از این مولفه در مواقعی استفاده می شود که قصد داریم مجموعه هایی از اطلاعات را در گروه های مختلف دسته بندی کرده و نمایش دهیم. + `; + + readonly contentEn = ` + # ${this.toolbarTitle} + this component used to show a collection of data sets in a grouped structure. + `; + + // + readonly sample1 = + '```html' + + ` + + +` + + '```'; + + // + readonly sample2 = + '```typescript' + + ` +async handleItemSelected(item: XExpandableListItem) { + // + if (!item) { + return; + } + + // + await this.managerService.notificationService.presentSuccessNotification({ + message: \`item \${item.id} Selected ...\`, + dissmissable: true, + }); +} + ` + + '```'; + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + //#endregion + + async handleItemSelected(item: XExpandableListItem) { + // + if (!item) { + return; + } + + // + await this.managerService.notificationService.presentSuccessNotification({ + message: `item ${item.id} Selected ...`, + dissmissable: true, + }); + } + + private toListItem(data: any, prefix?: string) { + return { + id: `${prefix ? prefix + '_' : ''}${data}`, + data: `Item ${prefix ? prefix + '_' : ''}${data}`, + opened: random(0, 1) === 0 ? true : false, + title: `Title ${prefix ? prefix + '_' : ''}${data}`, + description: `Description ${prefix ? prefix + '_' : ''}${data}`, + } as XExpandableListItem; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\expandable-panel\expandable-panel.page.html + + + + + + + + + + + + + + + + + + +
+ Title 1 +
+ +
+ Description 1 +
+ + Content 1 + +
+ Custom Actions 1 +
+
+ + +
+ Title 2 +
+ +
+ Description 2 +
+ + Content 2 +
+ + +
+ Title 3 +
+ +
+ Description 3 +
+ + Content 3 +
+
+
+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\expandable-panel\expandable-panel.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\expandable-panel\expandable-panel.page.ts + +import { Component } from '@angular/core'; +import { XIconNames } from 'x-framework-components'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; +import { XResourceIDs, isNullOrEmptyString, XColor } from 'x-framework-core'; + +@Component({ + selector: 'app-expandable-panel', + templateUrl: './expandable-panel.page.html', + styleUrls: ['./expandable-panel.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class ExpandablePanelPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.expandable_panel; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider( + AppResourceIDs.expandable_panel_description + ); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + readonly IconNames = Object.assign({}, XIconNames); + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} + این مولفه جهت گروه بندی محتوای قابل ارائه در یک نمایه بکار می رود. + `; + + readonly contentEn = ` + # ${this.toolbarTitle} + this component used to Grouped representable contents in a View. + `; + + // + readonly sample1 = + '```html' + + ` + + +
+ Title 1 +
+ +
+ Description 1 +
+ + Content 1 + +
+ Custom Actions 1 +
+
+ + +
+ Title 2 +
+ +
+ Description 2 +
+ + Content 2 +
+ + +
+ Title 3 +
+ +
+ Description 3 +
+ + Content 3 +
+
+` + + '```'; + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\fab\fab.page.html + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+ + +
+ + + + + + + + + + + + + + +
+ + + + + +

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quisquam + inventore, optio obcaecati, harum temporibus accusamus voluptatem, + sapiente corporis expedita ipsum quos enim odio adipisci! At praesentium + hic quaerat ad mollitia. +

+ + +
+ + + + + + + + + +
+
+
+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\fab\fab.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\fab\fab.page.ts + +import { + XFabItem, + XIconNames, + XButtonType, + XFabButtonSize, + XFabButtonSide, + XFabVerticalSlot, + XFabHorizontalSlot, +} from 'x-framework-components'; +import { Component } from '@angular/core'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; +import { XResourceIDs, XColor, isNullOrEmptyString } from 'x-framework-core'; + +@Component({ + selector: 'app-fab', + templateUrl: './fab.page.html', + styleUrls: ['./fab.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class FabPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + toolbarShowSubTitle = true; + titleRes = AppResourceIDs.fab; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider(AppResourceIDs.fab_description); + + // + ids = Array.from(Array(25).keys()); + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + readonly IconNames = Object.assign({}, XIconNames); + readonly ButtonTypes = Object.assign({}, XButtonType); + + // + readonly FabButtonSizes = Object.assign({}, XFabButtonSize); + readonly FabButtonSides = Object.assign({}, XFabButtonSide); + readonly FabVerticalSlots = Object.assign({}, XFabVerticalSlot); + readonly FabHorizontalSlots = Object.assign({}, XFabHorizontalSlot); + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} + + این مولفه جهت ارائه مجموعه ای از فعالیت های مرتبط با یکدیگر در موارد لزوم مورد استفاده قرار می گیرد. + `; + readonly contentEn = ` + # ${this.toolbarTitle} + + this components usually used for present a group of related actions together in where we want. + `; + + // + readonly sample1 = + '```html' + + ` + +` + + '```'; + + // + readonly sample2 = + '```typescript' + + ` +fabHandler = async (id?: string) => { + await this.handleNotifyFab(id); +}; +` + + '```'; + + // + readonly descriptionsFa = ` + در ادامه نمونه ای از بکارگیری دکمه های شناور ارائه می گردد که در مولفه صفحه و بعنوان یکی از اجزاء آن ارائه شده است. + برای آشنایی بیشتر می توانید به مستندات مولفه صفحه مراجعه کنید. + `; + + readonly descriptionsEn = ` + in the following example you will see an implementation of Fab component which provides as a part of Page component. + for more information you can see Page component documentations. + `; + + // + actions: XFabItem = { + // + id: 'edit_profile', + + // + fixed: true, + + // + color: XColor.Warning, + + // + icon: XIconNames.edit, + + // + tooltip: AppResourceIDs.temp_label, + + // + vertical: XFabVerticalSlot.BOTTOM, + horizontal: XFabHorizontalSlot.END, + + // + disabled: this.uiDisabled, + + // + handler: async (id?: string) => { + await this.handleNotifyFab(id); + }, + }; + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + + fabHandler = async (id?: string) => { + await this.handleNotifyFab(id); + }; + //#endregion + + // + //#region UI Handlers ... + async handleNotifyFab(id?: string) { + await this.managerService.notificationService.presentInfoNotification({ + message: `Clicked Fab Id: ${id}`, + dissmissable: true, + }); + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\file-components\file-components.page.html + + + + + + + + + + +
+ {{ resourceProvider(ResourceIDs.thumbnail_component) }} +
+ + +
+ + + + + + + + + + + + + + + + +
+ + + + +
+ {{ resourceProvider(ResourceIDs.file_drop_area) }} +
+ + +
+ + + + + + + + + + + +
+ + + + +
{{ resourceProvider(ResourceIDs.image_cropper) }}
+ + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + +
{{ resourceProvider(ResourceIDs.file_upload) }}
+ + +
+ + + + + + + + + + + +
+
+
+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\file-components\file-components.page.scss + +// +$size: 32px !important; +$thumbSize: 64px !important; + +.thumbnail-wrapper { + margin-top: 6px; + height: $thumbSize; +} + +x-thumbnail { + --thumbnail-size: #{$size}; +} + +// :host ::ng-deep .thumbnail, +// :host ::ng-deep .x-item { +// width: $size; +// min-width: $size; +// max-width: $size; + +// height: $size; +// min-height: $size; +// max-height: $size; +// } + +:host ::ng-deep .thumbnail { + img { + border-radius: 50% !important; + } +} + +.avatar-actions { + // + @extend .thumbnail-wrapper; + + // + display: flex; + align-items: center; + flex-direction: row; + + // + * { + flex: auto; + margin: { + top: auto; + bottom: auto; + } + + // + * { + margin: { + left: 3px; + right: 3px; + } + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\file-components\file-components.page.ts + +import { + Inject, + NgZone, + Component, + ViewChild, + Renderer2, + ElementRef, + ChangeDetectorRef, +} from '@angular/core'; +import { + XColor, + toArray, + isArray, + hasChild, + getArrayOf, + XResourceIDs, + XExceptionIDs, + isExtensionOk, + XDialogResult, + XOneOrManyType, + getImageDimensions, + isNullOrEmptyString, +} from 'x-framework-core'; +import { + XSlotName, + XIconNames, + XSlotLayout, + XFileStatus, + XButtonType, + XThumbnailType, + XPageContainer, + XPopoverService, + XImageCropperService, + XActionBarActionFiredModel, +} from 'x-framework-components'; +import { MenuController } from '@ionic/angular'; +import { X_CONFIG } from 'src/app/config/x-config'; +import { XConfig } from 'src/app/config/app-config'; +import { ViewportRuler } from '@angular/cdk/overlay'; +import { XManagerService } from 'x-framework-services'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +@Component({ + selector: 'app-file-components', + templateUrl: './file-components.page.html', + styleUrls: ['./file-components.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class FileComponentsPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.file_components; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider( + AppResourceIDs.file_components_description + ); + toolbarShowSubTitle = true; + //#endregion + + // + //#region image cropper props ... + readonly SlotNames = Object.assign({}, XSlotName); + readonly SlotLayout = Object.assign({}, XSlotLayout); + readonly ButtonTypes = Object.assign({}, XButtonType); + + // + @ViewChild('fileInput', { static: false }) + private fileInputRef!: ElementRef; + + // + //#region Selected File ... + private SELECTED_FILE: File; + + set file(v: File) { + // + this.SELECTED_FILE = v; + + // + if (!v) { + return; + } + + // + getImageDimensions(v).then((dimension) => { + if (dimension) { + // + this.selectedFileWidth = dimension.width; + this.selectedFileHeight = dimension.height; + + // + this.detectChanges(); + } + }); + } + + get file() { + return this.SELECTED_FILE; + } + + // + selectedFileWidth = 0; + selectedFileHeight = 0; + + // + preparedMaxAllowedFileSize = this.config.maxProfileImageSize; + preparedAllowedExtensions = this.config.allowedImageExtensions; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + readonly IconNames = Object.assign({}, XIconNames); + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.resourceProvider(this.ResourceIDs.file_components)} + در این کتابخانه مجموعه ای از مولفه ها جهت سهولت کار با فایل ها ارائه شده است که در ادامه به بررسی هر یک بتفصیل پرداخته خواهد شد: +`; + + readonly contentEn = ` + # ${this.resourceProvider(this.ResourceIDs.file_components)} + this framework provides several components to make working with files so easy. in the following section they will be documented: + `; + + // + // Thumbnail ... + readonly ThumbnailTypes = Object.assign({}, XThumbnailType); + + readonly thumbnailContentFa = ` + این مولفه جهت نمایش تصاویر بند انگشتی و یا تعیین نوع فایل در حالت عمومی کاربرد دارد. + `; + readonly thumbnailContentEn = ` + this component used to show a thumbnail of an image or file general types. + `; + + readonly fileUrl = this.managerService.getFullUrl('/assets/icon/favicon.png'); + + readonly thumbnailSample1 = + '```html' + + ` + + +` + + '```'; + + readonly thumbnailSample2 = + '```html' + + ` + + +` + + '```'; + + // + // File Drop Area ... + readonly fileDropAreaContentFa = ` + این مولفه جهت فراهم کردن فضایی برای کشیدن فایل ها و رها کردن آن ها در فضای مناسب مورد استفاده قرار می گیرد. + `; + readonly fileDropAreaContentEn = ` + this component use to provide an area for dropping file(s) + `; + + // + readonly fileDropAreaSample1 = + '```html' + + ` + +` + + '```'; + + readonly imageCropperContentFa = ` + این مولفه جهت فراهم کردن امکانات بریدن تصاویر کاربرد دارد که مهمترین مورد کاربرد آن در تصاویر پروفایل است. + `; + readonly imageCropperContentEn = ` + this component use to provide a way to cropping images before upload them, usually used for avatars. + `; + + // + readonly imageCropperSample1 = + '```html' + + ` +
+ + + + + +
+ + +
+ + +
+
+ + +
+
+ + + + + + + + +
+
+
+
+
+
+` + + '```'; + + // + // File Upload ... + readonly fileUploadContentFa = ` + از این مولفه جهت مدیریت بارگزاری فایل ها استفاده می شود + `; + + readonly fileUploadContentEn = ` + use this component to manage how files uploaded. + `; + + readonly fileUploadSample1 = + '```html' + + ` + +` + + '```'; + + // + //#region Constructor ... + constructor( + public zone: NgZone, + public element: ElementRef, + public renderer: Renderer2, + public ruler: ViewportRuler, + public menuController: MenuController, + public managerService: XManagerService, + public changeDetector: ChangeDetectorRef, + @Inject(X_CONFIG) public config: XConfig, + public imageCropperService: XImageCropperService, + protected popoverService: XPopoverService, + protected propertyProvider: XPageContainer, + ) { + super( + zone, + config, + element, + renderer, + ruler, + menuController, + managerService, + changeDetector, + popoverService, + propertyProvider + ); + } + //#endregion + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + + croppableImage() { + return ( + this.file && + this.selectedFileWidth > 0 && + this.selectedFileHeight > 0 && + this.selectedFileWidth >= this.config.minAllowedImageSize && + this.selectedFileHeight >= this.config.minAllowedImageSize + ); + } + //#endregion + + // + //#region UI Handlers ... + // + async handleFilesChanged(files: File | File[]) { + // + console.log('files changed: ', files); + + // + await this.managerService.notificationService.presentInfoNotification({ + message: `Selected File(s): ${toArray(files).length}`, + dissmissable: true, + }); + } + + // + handleFilesSelected() { + // + if (!this.fileInputRef) { + return; + } + + // + const fileInput = this.fileInputRef.nativeElement; + if (!fileInput || !fileInput.files) { + return; + } + + // + const files = getArrayOf(fileInput.files); + + // + if (!files || files.length === 0) { + return; + } + + // + const file = files[0]; + if (!file) { + return; + } + + // + fileInput.value = null; + + // + this.handleFileDropped(files); + } + + // + async handleFileDropped(files: File | File[]) { + // + if (!files) { + return; + } + + // + this.loading = true; + + // + let file: File; + if (!isArray(files) && files instanceof File) { + file = files as File; + } else { + // + const filesArr = files as File[]; + if (hasChild(filesArr)) { + file = filesArr[0]; + } + } + + // + this.loading = false; + + // + if (!file) { + return; + } + + // + const status = this.getFileStatus(file); + if (status !== XFileStatus.OK) { + this.loading = false; + this.handleFileStatus(status); + return; + } + + // + this.file = file; + } + + // + handleLoadCropper() { + this.imageCropperService.showCropper(this.file); + + // + const s = this.applyTakeUntilWrapper( + this.imageCropperService.onResult.asObservable() + ).subscribe((res) => { + // + if (res.result === XDialogResult.Ok && res.file) { + // + this.file = null; + setTimeout(() => { + this.file = res.file; + }, 100); + } + + // + s.unsubscribe(); + }); + } + + // + handleClearFile() { + // + this.file = null; + this.selectedFileWidth = 0; + this.selectedFileHeight = 0; + } + + selectFile() { + // + if (!this.fileInputRef || !this.fileInputRef.nativeElement) { + return; + } + + // + this.fileInputRef.nativeElement.click(); + } + + // + // File Upload ... + async handleFileChanged(files: XOneOrManyType) { + console.log('files: ', files); + } + + // + async handleActionFired(event: XActionBarActionFiredModel) { + // + const name = event.action; + await this.managerService.notificationService.presentXNotification({ + message: `Action Fired: ${name}`, + dissmissable: true, + opt: { + color: XColor.Dark, + }, + }); + } + //#endregion + + // + //#region Private ... + // + //#region File Selection Policies ... + private isExtensionOk(file: File) { + // + if (!file) { + return false; + } + + // + if (!hasChild(this.preparedAllowedExtensions)) { + return true; + } + + // + const result = isExtensionOk(file, this.preparedAllowedExtensions); + return result; + } + + private isMaxAllowedFileSizeOk(file: File) { + // + const result = + this.preparedMaxAllowedFileSize && this.preparedMaxAllowedFileSize > 0 + ? file.size <= this.preparedMaxAllowedFileSize + : true; + + // + return result; + } + + private getFileStatus(file: File) { + // + // Check Not Empty File ... + if (!file || file.size === 0) { + return XFileStatus.EMPTY; + } + + // + // Check File Extension Validation ... + if (!this.isExtensionOk(file)) { + return XFileStatus.NOT_ALLOWED_TYPE; + } + + // + // Check File Size Ok ... + if (!this.isMaxAllowedFileSizeOk(file)) { + return XFileStatus.SIZE_EXCEEDED; + } + + // + // Everythings Ok ... + return XFileStatus.OK; + } + + private handleFileStatus(status: XFileStatus) { + // + let exception: XExceptionIDs = null; + switch (status) { + // + case XFileStatus.EMPTY: + exception = this.ExceptionIDs.EmptyFile; + break; + + // + case XFileStatus.NOT_ALLOWED_TYPE: + exception = this.ExceptionIDs.InavlidFile; + break; + + // + case XFileStatus.SIZE_EXCEEDED: + exception = this.ExceptionIDs.MaxFileSizeExceeded; + break; + + // + case XFileStatus.MAX_ALLOWED_SIZE_REACHED: + exception = this.ExceptionIDs.MaxFilesSizeExceeded; + break; + + // + case XFileStatus.MAX_ALLOWED_FILES_REACHED: + exception = this.ExceptionIDs.MaxAllowedFilesExceeded; + break; + } + + // + if (exception) { + this.managerService.notificationService.presentErrorNotification({ + exception, + dissmissable: true, + }); + } + } + //#endregion + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\form\form.page.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ item.data }} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\form\form.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\form\form.page.ts + +import { + keys, + nameof, + getKeys, + hasChild, + nthItems, + toBoolean, + XResourceIDs, + toNormalString, + XOneOrManyType, + XColorIdentifier, + isNullOrEmptyString, + XColorWithBrightness, + XBaseDataPagerQueryResult, +} from 'x-framework-core'; +import { Subject } from 'rxjs'; +import { + XListItem, + XSlotName, + XIconNames, + XSlotLayout, + XButtonType, + XFormConfig, + XFormUpdateOn, + XMarkdownMode, + XFormBaseAction, + XFormActionModel, + XFormControlType, + XFormControlConfig, + XFormIconDescriptor, + XFormMapControlConfig, + XFormControlAppearance, + XControlIconDescriptor, + XFormRadioControlConfig, + XFormSelectControlConfig, + XFormSelectControlOption, + XFormSliderControlConfig, + XFormMarkdownControlConfig, + XFormMapControlPresentType, + XFormCheckBoxControlConfig, + XFormControlIconDescriptor, + XFormSelectorControlConfig, + XFormValueChangeEventModel, + XBaseFormControlActionModel, + XFormControlEventsDescriptor, + XFormDatePickerControlConfig, + XFormColorPickerControlConfig, + XFormControlAutoCompleteConfig, + XFormAvatarUploadControlConfig, + XFormControlActionProviderModel, + XFormControlAppearanceIdentifier, + XFormColorPickerControlPresentType, + XFormDatePickerControlPickerPosition, +} from 'x-framework-components'; +import { Validators } from '@angular/forms'; +import { Component, TemplateRef, ViewChild } from '@angular/core'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +enum ContentType { + POST = 'post', + NEWS = 'news', +} + +enum ContentVisibility { + VISIBLE = 'visible', + HIDDEN = 'hidden', +} + +enum Car { + GMC = 'gmc', + BMD = 'bmw', + JAC = 'jac', + TOYOTA = 'toyota', + NISSAN = 'nissan', +} + +enum XFormFileds { + FirstName, + LastName, + Content, + ContentType, + ContentVisibility, + Publish, + BrithDate, + WeddingState, + NumOfChilds, + Avatar, + Map, + ColorPicker, + Selector, +} + +interface XFormModel { + firstName: string; + lastName: string; + content: string; + contentType: string[]; + visibility: ContentVisibility; + publish: boolean; + brithDate: Date; + weddingState: boolean; + numberOfChilds: number; + avatar: File; + latLong: string; + color: string; + phoneNumber: string; + medias: XOneOrManyType; + car: Car; +} + +interface XFormControlModel { + formFields: string[]; + hasIcon: boolean; + showHint: boolean; + showTooltip: boolean; + disabledByDefault: boolean; + hasRequiredValidator: boolean; + applyIconsStateColor: boolean; + applyStatusOnControls: boolean; + iconsDefaultColor: XColorIdentifier; + regularColor: XColorIdentifier; + validColor: XColorIdentifier; + hintColor: XColorIdentifier; + invalidColor: XColorIdentifier; + activeColor: XColorIdentifier; + pendingColor: XColorIdentifier; + disabledColor: XColorIdentifier; + accentColor: XColorIdentifier; + fillColor: XColorIdentifier; + disabledFillColor: XColorIdentifier; + formControlAppearance: XFormControlAppearanceIdentifier; +} + +@Component({ + selector: 'app-form', + templateUrl: './form.page.html', + styleUrls: ['./form.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class FormPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.form; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider(AppResourceIDs.form_description); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs: any = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ); + + // + readonly ColorNames = Object.assign({}, XColorWithBrightness); + readonly IconNames = Object.assign({}, XIconNames); + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} + فرم ها بستر دریافت اطلاعات از کاربر هستند. + این مولفه امکانات لازم برای تسهیل فرایند ایجاد فرم ها را فراهم می کند. + `; + + readonly contentEn = ` + # ${this.toolbarTitle} + Forms used to get data from users. + this Component provides usefull tools to make form creation easy. + `; + + // + readonly sample1 = + '```html' + + ` + + ` + + '```'; + + // + private NAME_AUTO_CONMPLETE_REF: TemplateRef; + + @ViewChild('nameAutoCompleteRef') + set nameAutoCompleteRef(v: TemplateRef) { + // + if (!v) { + return; + } + + // + this.NAME_AUTO_CONMPLETE_REF = v; + this.prepareFormConfig(); + this.detectChanges(); + } + + get nameAutoCompleteRef() { + return this.NAME_AUTO_CONMPLETE_REF; + } + + // + readonly SlotNames = Object.assign({}, XSlotName); + readonly SlotLayouts = Object.assign({}, XSlotLayout); + readonly ButtonTypes = Object.assign({}, XButtonType); + + // + readonly Cars = Object.assign({}, Car); + readonly FormFields = Object.assign({}, XFormFileds); + readonly ContentTypes = Object.assign({}, ContentType); + readonly ContentVisibilities = Object.assign({}, ContentVisibility); + + // + readonly formActionProvider = new Subject(); + + // + private firstNameAutoCompleteIsOpened = false; + private firstNameAutoCompleteQuery = ''; + private firstNameAutoCompleteItems: XListItem[] = + this.getFirstNameAutoCompleteItems(); + + // + private readonly FormControlAppearances = Object.assign( + {}, + XFormControlAppearance + ); + + // + applyStatusOnControls = false; + + // + // Last Form Value ... + lastFormValue: Partial; + + // + // Available Form Fields ... + hasFirstName = false; + hasLastName = false; + hasContent = false; + hasContentType = true; + hasContentVisibility = false; + hasPublish = false; + hasBrithDate = false; + hasWeddingState = false; + hasNumOfChilds = false; + hasAvatar = false; + hasMap = false; + hasColorPicker = false; + hasSelector = false; + + // + // Form Related Color Values ... + formRegularColor: XColorIdentifier; + formValidColor: XColorIdentifier; + formHintColor: XColorIdentifier; + formInvalidColor: XColorIdentifier; + formActiveColor: XColorIdentifier; + formPendingColor: XColorIdentifier; + formDisabledColor: XColorIdentifier; + formAccentColor: XColorIdentifier; + formFillColor: XColorIdentifier; + formDisabledFillColor: XColorIdentifier; + + // + private nameStrings: string[] = [ + 'Hadi', + 'Hamid', + 'Hesam', + 'Mehdi', + 'Kourosh', + 'Daruosh', + 'Raymond Reed', + 'Willie Ward', + 'Stephen Edwards', + 'Albert Johnson', + 'Scott Long', + 'Samuel Bailey', + 'Louis Ramirez', + 'Philip Henderson', + 'Jacqueline Bennett', + 'Sharon Hernandez', + 'Karen Morris', + 'Theresa Parker', + 'Judith Coleman', + 'Martha White', + 'Jose Mitchell', + 'Carl Powell', + 'Sandra Lopez', + 'Douglas Wright', + 'Helen Young', + 'Kathryn Hughes', + 'Kathy Bell', + 'Jonathan Price', + 'Paul Morgan', + 'Deborah Roberts', + 'Teresa Martinez', + 'Brenda Williams', + 'Todd Phillips', + 'Clarence Collins', + 'Alan Butler', + 'Donna Torres', + 'Mildred Perry', + 'Katherine Walker', + 'William Flores', + 'Marilyn Brooks', + 'Timothy Clark', + 'Ronald Cook', + 'Frank Hall', + 'Pamela Rogers', + 'Harold Sanders', + 'Harry Hill', + 'Evelyn Thompson', + 'Charles Jenkins', + 'Andrew Murphy', + 'Juan Simmons', + 'Patricia Diaz', + 'Doris Smith', + 'Janice Turner', + 'Matthew Rivera', + 'Joyce Adams', + 'Joe Moore', + 'Nancy Kelly', + 'Peter Garcia', + 'Virginia Ross', + 'Rose James', + 'Donald Nelson', + 'Joan Lewis', + 'Julia Perez', + 'Gerald Washington', + 'Irene Allen', + 'Brandon Green', + 'Melissa Cooper', + 'Sara Taylor', + 'Kelly Martin', + 'Daniel Brown', + 'Robert Evans', + 'Susan Carter', + 'James Thomas', + 'Jack Bryant', + 'Eugene Sanchez', + 'Shirley Davis', + 'Carol Wilson', + 'Ryan Robinson', + 'Roy Griffin', + 'Phillip Alexander', + 'Thomas Miller', + 'Keith Russell', + 'Frances Rodriguez', + 'Diana Wood', + 'Brian Jones', + 'Gregory Jackson', + 'Kenneth Patterson', + 'Anne Stewart', + 'Justin King', + 'Benjamin Scott', + 'Judy Watson', + 'Ruth Gonzalez', + 'Walter Anderson', + 'Steven Peterson', + 'Heather Gonzales', + 'Billy Lee', + 'Betty Campbell', + 'Carolyn Richardson', + 'Jesse Gray', + 'Kevin Cox', + 'Denise Foster', + 'Fred Barnes', + 'Nicholas Howard', + 'Christina Baker', + 'Laura Harris', + 'Jennifer', + ]; + + // + showFormControlCenter = true; + allAvailableFormFileds = getKeys(this.FormFields); + controlFormActionProvider = new Subject(); + formConfigModel: XFormControlModel = { + // + formFields: ['BrithDate'], + // + hasIcon: true, + showHint: true, + showTooltip: true, + disabledByDefault: false, + hasRequiredValidator: true, + applyIconsStateColor: true, + applyStatusOnControls: true, + // + iconsDefaultColor: XColorWithBrightness.WarningShade, + // + regularColor: XColorWithBrightness.PrimaryTint, + validColor: XColorWithBrightness.Success, + hintColor: XColorWithBrightness.Tertiary, + invalidColor: XColorWithBrightness.Danger, + activeColor: XColorWithBrightness.Tertiary, + pendingColor: XColorWithBrightness.Warning, + disabledColor: XColorWithBrightness.Medium, + accentColor: XColorWithBrightness.Secondary, + fillColor: XColorWithBrightness.TertiaryShade, + disabledFillColor: XColorWithBrightness.LightShade, + // + formControlAppearance: XFormControlAppearance.Outline, + }; + xFormControlConfig: XFormConfig = { + name: 'FormControlModel', + model: { + ...this.formConfigModel, + }, + controls: [ + // + // FormFileds ... + { + index: 0, + propName: 'formFields', + type: { + type: XFormControlType.Select, + config: { + multiple: true, + options: this.allAvailableFormFileds.map((k) => { + return { + value: k, + viewValue: k, + } as XFormSelectControlOption; + }), + } as XFormSelectControlConfig, + }, + appearance: { + label: 'Form Fields', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // Form Regular Color ... + { + index: 1, + propName: 'regularColor', + type: { + type: XFormControlType.Select, + config: { + options: keys(this.ColorNames).map((k) => { + return { + value: this.ColorNames[k], + viewValue: this.resourceProvider(this.ColorNames[k]), + } as XFormSelectControlOption; + }), + } as XFormSelectControlConfig, + }, + appearance: { + label: 'Form Regular Color', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // Form Valid Color ... + { + index: 2, + propName: 'validColor', + type: { + type: XFormControlType.Select, + config: { + options: keys(this.ColorNames).map((k) => { + return { + value: this.ColorNames[k], + viewValue: this.resourceProvider(this.ColorNames[k]), + } as XFormSelectControlOption; + }), + } as XFormSelectControlConfig, + }, + appearance: { + label: 'Form Valid Color', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // Form Hint Color ... + { + index: 3, + propName: 'hintColor', + type: { + type: XFormControlType.Select, + config: { + options: keys(this.ColorNames).map((k) => { + return { + value: this.ColorNames[k], + viewValue: this.resourceProvider(this.ColorNames[k]), + } as XFormSelectControlOption; + }), + } as XFormSelectControlConfig, + }, + appearance: { + label: 'Form Hint Color', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // Form Invalid Color ... + { + index: 4, + propName: 'invalidColor', + type: { + type: XFormControlType.Select, + config: { + options: keys(this.ColorNames).map((k) => { + return { + value: this.ColorNames[k], + viewValue: this.resourceProvider(this.ColorNames[k]), + } as XFormSelectControlOption; + }), + } as XFormSelectControlConfig, + }, + appearance: { + label: 'Form Invalid Color', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // Form Active Color ... + { + index: 5, + propName: 'activeColor', + type: { + type: XFormControlType.Select, + config: { + options: keys(this.ColorNames).map((k) => { + return { + value: this.ColorNames[k], + viewValue: this.resourceProvider(this.ColorNames[k]), + } as XFormSelectControlOption; + }), + } as XFormSelectControlConfig, + }, + appearance: { + label: 'Form Active Color', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // Form Pending Color ... + { + index: 6, + propName: 'pendingColor', + type: { + type: XFormControlType.Select, + config: { + options: keys(this.ColorNames).map((k) => { + return { + value: this.ColorNames[k], + viewValue: this.resourceProvider(this.ColorNames[k]), + } as XFormSelectControlOption; + }), + } as XFormSelectControlConfig, + }, + appearance: { + label: 'Form Pending Color', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // Form Disabled Color ... + { + index: 7, + propName: 'disabledColor', + type: { + type: XFormControlType.Select, + config: { + options: keys(this.ColorNames).map((k) => { + return { + value: this.ColorNames[k], + viewValue: this.resourceProvider(this.ColorNames[k]), + } as XFormSelectControlOption; + }), + } as XFormSelectControlConfig, + }, + appearance: { + label: 'Form Disabled Color', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // Form Accent Color ... + { + index: 8, + propName: 'accentColor', + type: { + type: XFormControlType.Select, + config: { + options: keys(this.ColorNames).map((k) => { + return { + value: this.ColorNames[k], + viewValue: this.resourceProvider(this.ColorNames[k]), + } as XFormSelectControlOption; + }), + } as XFormSelectControlConfig, + }, + appearance: { + label: 'Form Accent Color', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // Form Fill Color ... + { + index: 9, + propName: 'fillColor', + type: { + type: XFormControlType.Select, + config: { + options: keys(this.ColorNames).map((k) => { + return { + value: this.ColorNames[k], + viewValue: this.resourceProvider(this.ColorNames[k]), + } as XFormSelectControlOption; + }), + } as XFormSelectControlConfig, + }, + appearance: { + label: 'Form Fill Color', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // Form Disabled Fill Color ... + { + index: 10, + propName: 'disabledFillColor', + type: { + type: XFormControlType.Select, + config: { + options: keys(this.ColorNames).map((k) => { + return { + value: this.ColorNames[k], + viewValue: this.resourceProvider(this.ColorNames[k]), + } as XFormSelectControlOption; + }), + } as XFormSelectControlConfig, + }, + appearance: { + label: 'Form Disabled Fill Color', + appearance: XFormControlAppearance.Outline, + }, + }, + + // + // HasIcon ... + { + index: 11, + propName: 'hasIcon', + type: { + type: XFormControlType.CheckBox, + }, + appearance: { + label: 'HasIcon', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // ShowHint ... + { + index: 12, + propName: 'showHint', + type: { + type: XFormControlType.CheckBox, + }, + appearance: { + label: 'ShowHint', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // ShowTooltip ... + { + index: 13, + propName: 'showTooltip', + type: { + type: XFormControlType.CheckBox, + }, + appearance: { + label: 'ShowTooltip', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // Disabled Controls ... + { + index: 14, + propName: 'disabledByDefault', + type: { + type: XFormControlType.CheckBox, + }, + appearance: { + label: 'Disabled Controls', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // Has Required Validator ... + { + index: 15, + propName: 'hasRequiredValidator', + type: { + type: XFormControlType.CheckBox, + }, + appearance: { + label: 'Has Required Validator', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // Apply Icon State Color ... + { + index: 16, + propName: 'applyIconsStateColor', + type: { + type: XFormControlType.CheckBox, + }, + appearance: { + label: 'Apply Icon State Color', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // Apply Status On Controls ... + { + index: 17, + propName: 'applyStatusOnControls', + type: { + type: XFormControlType.CheckBox, + }, + appearance: { + label: 'Apply Status On Controls', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // Controls Appearance ... + { + index: 18, + propName: 'formControlAppearance', + type: { + type: XFormControlType.Select, + config: { + options: keys(this.FormControlAppearances).map((k) => { + return { + value: this.FormControlAppearances[k], + viewValue: this.resourceProvider( + this.FormControlAppearances[k] + ), + } as XFormSelectControlOption; + }), + } as XFormSelectControlConfig, + }, + appearance: { + label: 'Controls Appearance', + appearance: XFormControlAppearance.Outline, + }, + }, + // + // Icons Default Color ... + { + index: 19, + propName: 'iconsDefaultColor', + type: { + type: XFormControlType.Select, + config: { + options: keys(this.ColorNames).map((k) => { + return { + value: this.ColorNames[k], + viewValue: this.resourceProvider(this.ColorNames[k]), + } as XFormSelectControlOption; + }), + } as XFormSelectControlConfig, + }, + appearance: { + label: 'Icons Default Color', + appearance: XFormControlAppearance.Outline, + }, + }, + ], + }; + + // + initialFormValue: Partial = { + firstName: 'Hadi', + lastName: 'Khazaee Asl', + weddingState: true, + numberOfChilds: 0, + contentType: ['news'], + latLong: '51.0025424,35.7866258', + color: '#9847a6ff', + car: Car.BMD, + }; + xFormConfig: XFormConfig; + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + //#endregion + + // + //#region UI Handlers ... + public async handleFormValueChanged(value: XFormValueChangeEventModel) { + // + console.log('handleFormValueChanged: ', value.value); + + // + this.lastFormValue = { + ...value.value, + }; + } + + public async handleFormControlCenterValueChanged( + event: XFormValueChangeEventModel + ) { + // + if (event && event.value) { + // + this.formConfigModel = { + ...this.formConfigModel, + ...event.value, + }; + + // + await this.clearAndApplyFormModel(); + } + + // + this.detectChanges(); + } + + public async handleShowAllFormFileds() { + // + this.controlFormActionProvider.next({ + action: XFormBaseAction.SetValue, + name: nameof('formFields'), + payload: [...this.allAvailableFormFileds], + }); + } + + public async handleClearAllFormFileds() { + // + this.managerService.dialogService.presentAreYouSureYesNoDialog(() => { + // + this.controlFormActionProvider.next({ + action: XFormBaseAction.SetValue, + name: nameof('formFields'), + payload: [], + }); + }); + } + //#endregion + + // + //#region Private ... + private prepareFormConfig() { + // + this.xFormConfig = { + name: 'PersonalForm', + model: { + // + ...this.initialFormValue, + // + ...this.lastFormValue, + }, + updateOn: XFormUpdateOn.CHANGE, + controls: [], + }; + + // + let cIdx = 0; + + // + const hasIcon = this.formConfigModel.hasIcon; + const showHint = this.formConfigModel.showHint; + const showTooltip = this.formConfigModel.showTooltip; + const disabledByDefault = this.formConfigModel.disabledByDefault; + const iconsDefaultColor = this.formConfigModel.iconsDefaultColor; + const hasRequiredValidator = this.formConfigModel.hasRequiredValidator; + const applyIconsStateColor = this.formConfigModel.applyIconsStateColor; + this.applyStatusOnControls = this.formConfigModel.applyStatusOnControls; + const FormControlAppearance = this.formConfigModel.formControlAppearance; + + // + //#region Form Colors ... + this.formRegularColor = this.formConfigModel.regularColor; + this.formValidColor = this.formConfigModel.validColor; + this.formHintColor = this.formConfigModel.hintColor; + this.formInvalidColor = this.formConfigModel.invalidColor; + this.formActiveColor = this.formConfigModel.activeColor; + this.formPendingColor = this.formConfigModel.pendingColor; + this.formDisabledColor = this.formConfigModel.disabledColor; + this.formAccentColor = this.formConfigModel.accentColor; + this.formFillColor = this.formConfigModel.fillColor; + this.formDisabledFillColor = this.formConfigModel.disabledFillColor; + //#endregion + + // + //#region Available Form Fields ... + // + const formFieldKeys = getKeys(this.FormFields); + + // + this.hasFirstName = this.formConfigModel.formFields.includes( + formFieldKeys[this.FormFields.FirstName] + ); + this.hasLastName = this.formConfigModel.formFields.includes( + formFieldKeys[this.FormFields.LastName] + ); + this.hasContent = this.formConfigModel.formFields.includes( + formFieldKeys[this.FormFields.Content] + ); + this.hasContentType = this.formConfigModel.formFields.includes( + formFieldKeys[this.FormFields.ContentType] + ); + this.hasContentVisibility = this.formConfigModel.formFields.includes( + formFieldKeys[this.FormFields.ContentVisibility] + ); + this.hasPublish = this.formConfigModel.formFields.includes( + formFieldKeys[this.FormFields.Publish] + ); + this.hasBrithDate = this.formConfigModel.formFields.includes( + formFieldKeys[this.FormFields.BrithDate] + ); + this.hasWeddingState = this.formConfigModel.formFields.includes( + formFieldKeys[this.FormFields.WeddingState] + ); + this.hasNumOfChilds = this.formConfigModel.formFields.includes( + formFieldKeys[this.FormFields.NumOfChilds] + ); + this.hasAvatar = this.formConfigModel.formFields.includes( + formFieldKeys[this.FormFields.Avatar] + ); + this.hasMap = this.formConfigModel.formFields.includes( + formFieldKeys[this.FormFields.Map] + ); + this.hasColorPicker = this.formConfigModel.formFields.includes( + formFieldKeys[this.FormFields.ColorPicker] + ); + this.hasSelector = this.formConfigModel.formFields.includes( + formFieldKeys[this.FormFields.Selector] + ); + //#endregion + + // + const isPrefixIcon = true; + const defaultIconObject = { + applyStateColor: applyIconsStateColor || false, + name: this.IconNames.first_name, + color: iconsDefaultColor, + } as XFormIconDescriptor; + const defaultIcon: XFormControlIconDescriptor = hasIcon + ? { + prefix: isPrefixIcon ? defaultIconObject : undefined, + suffix: !isPrefixIcon ? defaultIconObject : undefined, + } + : undefined; + const clearIcon: XControlIconDescriptor = { + applyStateColor: false, //applyIconsStateColor || false, + name: this.IconNames.clear, + color: this.ColorNames.Danger, + }; + const selectIcon: XControlIconDescriptor = { + applyStateColor: false, // applyIconsStateColor || false, + name: this.IconNames.touch, + color: this.ColorNames.SuccessShade, + }; + const eventHandlers: XFormControlEventsDescriptor = { + onBlured: (model: XBaseFormControlActionModel) => + console.log('blured: ', model), + onFocused: (model: XBaseFormControlActionModel) => + console.log('focused: ', model), + valueChanged: (event) => + console.log(event.name.toString(), ' value changed: ', event.value), + statusChanged: (event) => + console.log( + event.name.toString(), + ' status changed: ', + event.status.toString() + ), + }; + + // + //#region First Name ... + if (this.hasFirstName) { + // + this.xFormConfig.controls[cIdx] = { + index: cIdx, + propName: 'firstName', + type: { + type: XFormControlType.Text, + }, + appearance: { + disabled: disabledByDefault || false, + appearance: FormControlAppearance, + hint: showHint ? this.ResourceIDs.first_name : undefined, + tooltip: showTooltip ? this.ResourceIDs.first_name : undefined, + label: this.ResourceIDs.first_name, + icons: defaultIcon, + }, + validators: hasRequiredValidator + ? { + validators: [Validators.required], + } + : undefined, + errorHandlers: [ + { + errorName: 'required', + errorMessage: 'Required Field ...', + }, + ], + eventHandlers: { + ...eventHandlers, + onBlured: (name) => { + // + eventHandlers.onBlured(name); + this.prepareFirstNameAutoComplete({ opened: false }); + }, + valueChanged: async (event) => { + // + eventHandlers.valueChanged(event); + await this.handleFilterAutoCompleteFirstName(event.value); + }, + }, + } as XFormControlConfig; + + // + cIdx++; + + // + this.prepareFirstNameAutoComplete(); + } + //#endregion + + // + //#region Last Name ... + if (this.hasLastName) { + // + this.xFormConfig.controls[cIdx] = { + index: cIdx, + propName: 'lastName', + type: { + type: XFormControlType.Text, + }, + appearance: { + disabled: disabledByDefault || false, + label: this.ResourceIDs.last_name, + appearance: FormControlAppearance, + hint: showHint ? this.ResourceIDs.last_name : undefined, + tooltip: showTooltip ? this.ResourceIDs.last_name : undefined, + icons: defaultIcon, + }, + validators: hasRequiredValidator + ? { + validators: [Validators.required], + } + : undefined, + errorHandlers: [ + { + errorName: 'required', + errorMessage: 'Required Field ...', + }, + ], + eventHandlers, + } as XFormControlConfig; + + // + cIdx++; + } + //#endregion + + // + //#region Content ... + if (this.hasContent) { + // + this.xFormConfig.controls[cIdx] = { + index: cIdx, + propName: 'content', + type: { + type: XFormControlType.MarkDown, + config: { + hasToolbar: true, + mode: XMarkdownMode.BOTH, + toolbarColor: this.ColorNames.Light, + contentChanged: (content: string) => { + console.log('markdown new content: ', content); + }, + } as XFormMarkdownControlConfig, + }, + appearance: { + label: AppResourceIDs.content, + hint: showHint ? AppResourceIDs.content : undefined, + tooltip: showTooltip ? AppResourceIDs.content : undefined, + appearance: FormControlAppearance, + disabled: disabledByDefault || false, + icons: defaultIcon, + }, + eventHandlers, + } as XFormControlConfig; + + // + cIdx++; + } + //#endregion + + // + //#region Content Type ... + if (this.hasContentType) { + // + this.xFormConfig.controls[cIdx] = { + index: cIdx, + propName: 'contentType', + type: { + type: XFormControlType.Select, + config: { + multiple: true, + options: keys(this.ContentTypes).map((k) => { + return { + value: this.ContentTypes[k], + viewValue: this.resourceProvider(this.ContentTypes[k]), + } as XFormSelectControlOption; + }), + } as XFormSelectControlConfig, + }, + appearance: { + disabled: disabledByDefault || false, + label: AppResourceIDs.content_type, + hint: showHint ? AppResourceIDs.content_type : undefined, + tooltip: showTooltip ? AppResourceIDs.content_type : undefined, + placeholder: AppResourceIDs.content_type, + appearance: FormControlAppearance, + icons: defaultIcon, + }, + eventHandlers, + validators: hasRequiredValidator + ? { + validators: [Validators.required], + } + : undefined, + errorHandlers: [ + { + errorName: 'required', + errorMessage: 'Required Field ...', + }, + ], + } as XFormControlConfig; + + // + cIdx++; + } + //#endregion + + // + //#region Content Visibility ... + if (this.hasContentVisibility) { + // + this.xFormConfig.controls[cIdx] = { + index: cIdx, + propName: 'visibility', + type: { + type: XFormControlType.Radio, + config: { + options: keys(this.ContentVisibilities).map((k) => { + return { + value: this.ContentVisibilities[k], + viewValue: this.resourceProvider(this.ContentVisibilities[k]), + } as XFormSelectControlOption; + }), + } as XFormRadioControlConfig, + }, + appearance: { + label: AppResourceIDs.visibility, + hint: showHint ? AppResourceIDs.visibility : undefined, + tooltip: showTooltip ? AppResourceIDs.visibility : undefined, + disabled: disabledByDefault || false, + appearance: FormControlAppearance, + icons: defaultIcon, + }, + validators: hasRequiredValidator + ? { + validators: [Validators.required], + } + : undefined, + errorHandlers: [ + { + errorName: 'required', + errorMessage: 'Required Field ...', + }, + ], + eventHandlers, + } as XFormControlConfig; + + // + cIdx++; + } + //#endregion + + // + //#region Publish ... + if (this.hasPublish) { + // + this.xFormConfig.controls[cIdx] = { + index: cIdx, + propName: 'publish', + type: { + type: XFormControlType.CheckBox, + config: { + checkedChanged: (checked: boolean) => { + console.log('checked change: ', checked); + }, + } as XFormCheckBoxControlConfig, + }, + appearance: { + label: this.ResourceIDs.publish, + hint: showHint ? this.ResourceIDs.publish : undefined, + tooltip: showTooltip ? this.ResourceIDs.publish : undefined, + disabled: disabledByDefault || false, + appearance: FormControlAppearance, + icons: defaultIcon, + }, + validators: hasRequiredValidator + ? { + validators: [Validators.requiredTrue], + } + : undefined, + errorHandlers: [ + { + errorName: 'requiredTrue', + errorMessage: 'Required Field ...', + }, + ], + eventHandlers, + } as XFormControlConfig; + + // + cIdx++; + } + //#endregion + + // + //#region BrithDate ... + if (this.hasBrithDate) { + // + this.xFormConfig.controls[cIdx] = { + index: cIdx, + propName: 'brithDate', + type: { + type: XFormControlType.Date, + config: { + iconColor: iconsDefaultColor, + toggleIcon: XIconNames.calendar, + applyStateColor: applyIconsStateColor || false, + datePickerPosition: XFormDatePickerControlPickerPosition.Prefix, + } as XFormDatePickerControlConfig, + }, + appearance: { + label: this.ResourceIDs.brithDate, + hint: showHint ? this.ResourceIDs.brithDate : undefined, + tooltip: showTooltip ? this.ResourceIDs.brithDate : undefined, + disabled: disabledByDefault || false, + appearance: FormControlAppearance, + icons: defaultIcon, + }, + validators: hasRequiredValidator + ? { + validators: [Validators.required], + } + : undefined, + errorHandlers: [ + { + errorName: 'required', + errorMessage: 'Required Field ...', + }, + ], + eventHandlers, + } as XFormControlConfig; + + // + cIdx++; + } + //#endregion + + // + //#region Wedding State ... + if (this.hasWeddingState || this.hasNumOfChilds) { + // + this.xFormConfig.controls[cIdx] = { + index: cIdx, + propName: 'weddingState', + type: { + type: XFormControlType.Toggle, + }, + appearance: { + label: this.ResourceIDs.wedding_state, + hint: showHint ? this.ResourceIDs.wedding_state : undefined, + tooltip: showTooltip ? this.ResourceIDs.wedding_state : undefined, + disabled: disabledByDefault || false, + appearance: FormControlAppearance, + icons: defaultIcon, + }, + validators: hasRequiredValidator + ? { + validators: [Validators.requiredTrue], + } + : undefined, + errorHandlers: [ + { + errorName: 'requiredTrue', + errorMessage: 'Required Field ...', + }, + ], + eventHandlers: { + ...eventHandlers, + valueChanged: (model) => { + eventHandlers.valueChanged(model); + this.handleHasNumberOfChild(toBoolean(model.value)); + }, + }, + } as XFormControlConfig; + + // + cIdx++; + } + //#endregion + + // + //#region Number Of Childs ... + if (this.hasNumOfChilds || this.hasWeddingState) { + // + this.xFormConfig.controls[cIdx] = { + index: cIdx, + propName: 'numberOfChilds', + type: { + type: XFormControlType.Hidden, + config: { + min: 0, + max: 15, + step: 1, + vertical: false, + thumbLabel: true, + formatter: (value) => this.applyLocale(value), + } as XFormSliderControlConfig, + }, + appearance: { + label: this.ResourceIDs.num_of_childs, + hint: showHint ? this.ResourceIDs.num_of_childs : undefined, + tooltip: showTooltip ? this.ResourceIDs.num_of_childs : undefined, + disabled: disabledByDefault || false, + appearance: FormControlAppearance, + icons: defaultIcon, + }, + validators: hasRequiredValidator + ? { + validators: [Validators.min(1)], + } + : undefined, + errorHandlers: [ + { + errorName: 'required', + errorMessage: 'Required Field ...', + }, + ], + eventHandlers, + } as XFormControlConfig; + + // + cIdx++; + } + //#endregion + + // + //#region Avatar ... + if (this.hasAvatar) { + // + this.xFormConfig.controls[cIdx] = { + index: cIdx, + propName: 'avatar', + type: { + type: XFormControlType.AvatarUpload, + config: { + canDrop: true, + color: this.ColorNames.Light, + fileDropAreaColor: this.ColorNames.Light, + placeHolderAvatar: './assets/image/default-user-image.png', + } as XFormAvatarUploadControlConfig, + }, + appearance: { + label: this.ResourceIDs.avatar, + hint: showHint ? this.ResourceIDs.avatar : undefined, + tooltip: showTooltip ? this.ResourceIDs.avatar : undefined, + disabled: disabledByDefault || false, + appearance: FormControlAppearance, + }, + validators: hasRequiredValidator + ? { + validators: [Validators.required], + } + : undefined, + errorHandlers: [ + { + errorName: 'required', + errorMessage: 'Required Field ...', + }, + ], + eventHandlers, + } as XFormControlConfig; + + // + cIdx++; + } + //#endregion + + // + //#region Map ... + if (this.hasMap) { + // + this.xFormConfig.controls[cIdx] = { + index: cIdx, + propName: 'latLong', + type: { + type: XFormControlType.Map, + config: { + zoom: 12, + clearIcon, + selectIcon, + showZoom: true, + canSelect: true, + showRotate: true, + showLocate: true, + disableInput: true, + showSearchBar: true, + showClearMarker: true, + centerAfterInit: false, + showGoMarkedPlace: true, + showSelectedPosition: true, + inputCssClass: 'ion-text-end', + searchBarColor: XColorWithBrightness.Primary, + progressBarColor: XColorWithBrightness.Warning, + presentType: XFormMapControlPresentType.WithoutDialog, + } as XFormMapControlConfig, + }, + appearance: { + label: this.ResourceIDs.location, + hint: showHint ? this.ResourceIDs.location : undefined, + tooltip: showTooltip ? this.ResourceIDs.location : undefined, + disabled: disabledByDefault || false, + appearance: FormControlAppearance, + placeholder: this.ResourceIDs.location, + icons: defaultIcon, + }, + validators: hasRequiredValidator + ? { + validators: [Validators.required], + } + : undefined, + errorHandlers: [ + { + errorName: 'required', + errorMessage: 'Required Field ...', + }, + ], + eventHandlers, + } as XFormControlConfig; + + // + cIdx++; + } + //#endregion + + // + //#region Color Picker ... + if (this.hasColorPicker) { + // + this.xFormConfig.controls[cIdx] = { + index: cIdx, + propName: 'color', + type: { + type: XFormControlType.ColorPicker, + config: { + clearIcon, + selectIcon, + showCopyToClipboard: true, + inputCssClass: 'ion-text-end', + presentType: XFormColorPickerControlPresentType.WithoutDialog, + } as XFormColorPickerControlConfig, + }, + appearance: { + label: this.ResourceIDs.color, + hint: showHint ? this.ResourceIDs.color : undefined, + tooltip: showTooltip ? this.ResourceIDs.color : undefined, + disabled: disabledByDefault || false, + appearance: FormControlAppearance, + placeholder: this.ResourceIDs.color, + icons: defaultIcon, + }, + validators: hasRequiredValidator + ? { + validators: [Validators.required], + } + : undefined, + errorHandlers: [ + { + errorName: 'required', + errorMessage: 'Required Field ...', + }, + ], + eventHandlers, + } as XFormControlConfig; + + // + cIdx++; + } + //#endregion + + // + //#region Selector ... + if (this.hasSelector) { + // + this.xFormConfig.controls[cIdx] = { + index: cIdx, + propName: 'car', + type: { + type: XFormControlType.Selector, + config: { + clearIcon, + selectIcon, + title: 'Select Car', + showPaginator: true, + taskName: 'load-cars', + queryFetcher: (query) => { + // + const result: XBaseDataPagerQueryResult = { + count: keys(this.Cars).length, + items: keys(this.Cars).map((k) => k as Car), + }; + + // + return this.getValue(result); + }, + formatSelectedItem: (item: Car) => this.resourceProvider(item), + onTaskFinished: (name) => { + console.log('Task Finished: ', name); + }, + } as XFormSelectorControlConfig, + }, + appearance: { + label: this.ResourceIDs.color, + hint: showHint ? this.ResourceIDs.color : undefined, + tooltip: showTooltip ? this.ResourceIDs.color : undefined, + disabled: disabledByDefault || false, + appearance: FormControlAppearance, + placeholder: this.ResourceIDs.color, + icons: defaultIcon, + }, + validators: hasRequiredValidator + ? { + validators: [Validators.required], + } + : undefined, + errorHandlers: [ + { + errorName: 'required', + errorMessage: 'Required Field ...', + }, + ], + eventHandlers, + } as XFormControlConfig; + + // + cIdx++; + } + //#endregion + + // + this.detectChanges(); + } + + private prepareFirstNameAutoComplete( + dataProvider?: Partial + ) { + // + if (!this.xFormConfig || !hasChild(this.xFormConfig.controls)) { + return; + } + + // + this.xFormConfig.controls[0].appearance.autoComplete = { + ...this.xFormConfig.controls[0].appearance.autoComplete, + // + opened: + dataProvider && dataProvider.opened + ? dataProvider.opened + : this.firstNameAutoCompleteIsOpened, + itemTemplate: + dataProvider && dataProvider.itemTemplate + ? dataProvider.itemTemplate + : this.NAME_AUTO_CONMPLETE_REF, + color: + dataProvider && dataProvider.color + ? dataProvider.color + : XColorWithBrightness.Tertiary, + items: + dataProvider && dataProvider.items + ? dataProvider.items + : () => { + return this.firstNameAutoCompleteItems; + }, + onSelect: + dataProvider && dataProvider.onSelect + ? dataProvider.onSelect + : (item: string) => { + console.log('auto complete selected: ', item); + }, + cssClass: + dataProvider && dataProvider.cssClass + ? dataProvider.cssClass + : ['x-auto-complete-margin'], + }; + + // + this.detectChanges(); + } + + private async handleFilterAutoCompleteFirstName(value: string) { + // + if (toNormalString(value) === this.firstNameAutoCompleteQuery) { + // + if (hasChild(this.firstNameAutoCompleteItems)) { + this.firstNameAutoCompleteIsOpened = true; + } else { + this.firstNameAutoCompleteIsOpened = false; + } + + // + this.prepareFirstNameAutoComplete(); + return; + } + + // + this.firstNameAutoCompleteQuery = toNormalString(value); + this.firstNameAutoCompleteItems = this.getFirstNameAutoCompleteItems(); + + // + this.firstNameAutoCompleteIsOpened = hasChild( + this.firstNameAutoCompleteItems + ); + + // + this.prepareFirstNameAutoComplete(); + } + + private getFirstNameAutoCompleteItems(): XListItem[] { + // + if (isNullOrEmptyString(this.firstNameAutoCompleteQuery)) { + return []; + } + + // + // Select Top 5 result of filtered Names ... + const selectedItems = nthItems( + 0, + 5, + this.nameStrings.filter( + (n) => + toNormalString(n).includes(this.firstNameAutoCompleteQuery) && + toNormalString(n) !== this.firstNameAutoCompleteQuery + ) + ); + if (!hasChild(selectedItems)) { + return []; + } + + return selectedItems.map((i) => { + return { + data: i, + } as XListItem; + }); + } + + private handleHasNumberOfChild(isMarried?: boolean) { + // + if (!this.xFormConfig.controls[8]) { + return; + } + + // + const hasChildCount = toBoolean(isMarried); + if (!hasChildCount) { + // + this.xFormConfig.controls[8].type = { + ...this.xFormConfig.controls[8].type, + type: XFormControlType.Hidden, + }; + } else { + // + this.xFormConfig.controls[8].type = { + ...this.xFormConfig.controls[8].type, + type: XFormControlType.Slider, + }; + } + + // + this.detectChanges(); + } + + private async clearAndApplyFormModel() { + // + this.xFormConfig = null; + + // + await this.runTimedOut(() => { + this.prepareFormConfig(); + }); + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\grid\grid.page.html + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {{ item.data }} + +
+
+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\grid\grid.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\grid\grid.page.ts + +import { + XGridItem, + XIconNames, + XGridPresentType, + defaultGridOptions, + XStaggedGridOptions, +} from 'x-framework-components'; +import { Component } from '@angular/core'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; +import { XResourceIDs, isNullOrEmptyString, XColor } from 'x-framework-core'; + +@Component({ + selector: 'app-grid', + templateUrl: './grid.page.html', + styleUrls: ['./grid.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class GridPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.grid; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider(AppResourceIDs.grid_description); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + readonly IconNames = Object.assign({}, XIconNames); + //#endregion + + // + // Content ... + readonly contentFa = ` +# ${this.toolbarTitle} +این مولفه نمایش مجموعه ای از عناصر مرتبط با هم بکار می رود. +`; + + readonly contentEn = ` +# ${this.toolbarTitle} +this component used to show a collection of related items. +`; + + // + readonly sample0 = + '```typescript' + + ` +gridOption: XStaggedGridOptions = { + ...defaultGridOptions, +}; +tempList = Array.from(Array(25).keys()); +templGridItems: XGridItem[] = this.tempList.map((i) => { + // + const li: XGridItem = { + data: \`Item \${i.toString()}\`, + }; + + // + return li; +}); +` + + '```'; + + // + readonly sample1 = + '```html' + + ` + + +` + + '```'; + + readonly sample2 = + '```html' + + ` + + +` + + '```'; + + // + readonly GridPresentTypes = Object.assign({}, XGridPresentType); + + // + gridOption: XStaggedGridOptions = { + ...defaultGridOptions, + }; + tempList = Array.from(Array(25).keys()); + templGridItems: XGridItem[] = this.tempList.map((i) => { + // + const li: XGridItem = { + data: `Item ${i.toString()}`, + }; + + // + return li; + }); + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + + // + handleGridItemSelected(item: XGridItem) { + console.log('handleGridItemSelected: ', item.data); + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\icon\icon.page.html + + + + + + + + + + + + + + + + + + + +
+
+ + + + + +
+ + + + + + + +
+ + + +
+ + {{ item.data }} +
+
+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\icon\icon.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\icon\icon.page.ts + +import { + XSpinner, + hasChild, + XResourceIDs, + isNullOrEmptyString, + XColorWithBrightness, +} from 'x-framework-core'; +import { + XIconNames, + XListItem, + XFormConfig, + XFormControlType, +} from 'x-framework-components'; +import { Component } from '@angular/core'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +interface IconsListPresentation { + show: boolean; +} + +@Component({ + selector: 'app-icon', + templateUrl: './icon.page.html', + styleUrls: ['./icon.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class IconPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.icon; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider(AppResourceIDs.icon_description); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + // Color Names ... + readonly ColorNames = Object.assign({}, XColorWithBrightness); + + // + // Spinner Names ... + readonly SpinnerNames = Object.assign({}, XSpinner); + + // + IconNames = Object.assign({}, XIconNames); + iconList = Object.keys(this.IconNames); + iconListItems: XListItem[] = []; + //#endregion + + readonly contentFa = ` + # ${this.toolbarTitle} + + این مولفه جهت نمایش شمایل های مختلف بکار میرود. + `; + readonly contentEn = ` + # ${this.toolbarTitle} + + this Component used for show an icon. + `; + + readonly sample1 = + '```html' + + ` + + +` + + '```'; + + readonly sample2 = + '```html' + + ` + + +` + + '```'; + + // + showIconList = false; + iconListFormConfig: XFormConfig = { + name: 'IconListForm', + controls: [ + { + index: 5, + propName: 'show', + type: { + type: XFormControlType.CheckBox, + config: { + checkedChanged: async (checked: boolean) => { + // + if (!hasChild(this.iconListItems)) { + // + const loading = await this.managerService.dialogService.presentLoading( + { + message: this.resourceProvider( + AppResourceIDs.default_loading + ), + spinner: this.SpinnerNames.LinesSmall, + } + ); + + // + await this.prepareIconListItems(); + + // + await loading.dismiss(); + } + + // + this.showIconList = checked; + }, + }, + }, + appearance: { + label: `${this.resourceProvider( + this.ResourceIDs.show + )} ${this.resourceProvider(this.ResourceIDs.icon_list)}`, + }, + }, + ], + }; + + // + //#region LifeCycle ... + //#endregion + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + + canShowIconList() { + return hasChild(this.iconListItems); + } + //#endregion + + // + //#region UI Handlers ... + async handleIconSelected(item: XListItem) { + // + // text: this.resourceProvider(XResourceIDs.dismiss), + const iconText = `<x-icon name='${item.data}'></x-icon>`; + const iconCode = ``; + await this.managerService.notificationService.presentInfoNotification({ + message: iconText, + dissmissable: true, + opt: { + duration: 4000, + buttons: [ + { + icon: this.IconNames.clipboard, + side: 'end', + role: 'set', + handler: () => { + // + console.log(iconCode); + const isSaved = this.managerService.saveToClipboard(iconCode); + if (isSaved) { + this.managerService.notificationService.presentDefaultSuccessNotification(); + } + }, + }, + { + icon: this.IconNames.close, + side: 'end', + role: 'cancel', + }, + ], + }, + }); + } + //#endregion + + // + //#region Private ... + private async prepareIconListItems() { + // + this.iconListItems = this.iconList.map((i) => { + // + const li: XListItem = { + data: i, + }; + + // + return li; + }); + this.detectChanges(); + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\introduction\introduction.page.html + + + + + + + + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\introduction\introduction.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\introduction\introduction.page.ts + +import { + XResourceIDs, + isNullOrEmptyString, + XColorWithBrightness, +} from 'x-framework-core'; +import { Component } from '@angular/core'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +@Component({ + selector: 'app-components-introduction', + templateUrl: './introduction.page.html', + styleUrls: ['./introduction.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class IntroductionPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.components_introduction; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider( + AppResourceIDs.components_introduction_description + ); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + // Color Names ... + readonly ColorNames = Object.assign({}, XColorWithBrightness); + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} + +این واحد در زمره اصلی ترین اجزاء چارچوب ایفای نقش می کند و شامل کلیه مولفه های بصری قابل استفاده در طراحی و پیاده سازی برنامه های کاربردی است. + +شناخت عمیق کلیه اجزاء این واحد در تسریع فرآیند توسعه بسیار لازم و ضروری است. + `; + + readonly contentEn = ` + # ${this.toolbarTitle} + +this module is one of most important modules of xFramework and contains all provided UI Components which used to design and develop Applications. + +getting deep dive into all components of this module is very usefull to make easier the Design and Develop proccess of Applications. + `; + //#endregion + + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\list\list.page.html + + + + + + + + + + + + + + + + + + + + + + + + {{item.data}} + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\list\list.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\list\list.page.ts + +import { + XColor, + XResourceIDs, + isNullOrEmptyString, + XBaseRangeRequestDto, + XStandardType, + XOneOrManyType, +} from 'x-framework-core'; +import { Component } from '@angular/core'; +import { XListItem, XIconNames } from 'x-framework-components'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +@Component({ + selector: 'app-list', + templateUrl: './list.page.html', + styleUrls: ['./list.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class ListPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.list; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider(AppResourceIDs.list_description); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + readonly IconNames = Object.assign({}, XIconNames); + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} + این مولفه جهت نمایش فهرستی از موارد و فراهم کردن ابزار مدیریت آن بکار می رود. + `; + + readonly contentEn = ` + # ${this.toolbarTitle} + this component used to provide a way to show items in a list and manage items. + `; + + // + readonly sample1 = + '```typescript' + + ` +tempList = Array + .from(Array(25).keys()); + +templListItems: XListItem[] = + this.tempList.map((i) => { + const li: XListItem = { + data: \`Item \${i.toString()}\`, + slideOptions: [ + { + id: 'remove', + icon: this.IconNames.remove, + color: this.ColorNames.Danger, + onlyIcon: true, + slot: 'end', + handler: () => { + // + this.managerService + .notificationService + .presentDangerNotification({ + message: \`remove: \${i}\`, + dissmissable: true, + }); + }, + }, + { + id: 'edit', + icon: this.IconNames.edit, + color: this.ColorNames.Warning, + onlyIcon: true, + slot: 'start', + handler: () => { + // + this.managerService + .notificationService + .presentInfoNotification({ + message: \`edit: \${i}\`, + dissmissable: true, + }); + }, + }, + ], + }; + + return li; +}); +` + + '```'; + + // + readonly sample2 = + '```html' + + ` + + +` + + '```'; + + readonly sample3 = + '```typescript' + + ` +handleRefreshList() { + this.prepareListItems(); +} + +handleRemoveItems(request: + XBaseRangeRequestDto) { + // + this.templListItems = this + .templListItems.filter( + (li) => !request.items + .includes(li.data) + ); +} + +private prepareListItems() { + this.templListItems = this + .tempList.map((i) => { + // + const li: XListItem = { + data: \`Item $\{i.toString()}\`, + slideOptions: [ + { + id: 'remove', + icon: this.IconNames.remove, + color: this.ColorNames.Danger, + onlyIcon: true, + slot: 'end', + handler: () => { + // + this.managerService + .notificationService + .presentDangerNotification({ + message: \`remove: \${i}\`, + dissmissable: true, + }); + }, + }, + { + id: 'edit', + icon: this.IconNames.edit, + color: this.ColorNames.Warning, + onlyIcon: true, + slot: 'start', + handler: () => { + // + this.managerService + .notificationService + .presentInfoNotification({ + message: \`edit: \${i}\`, + dissmissable: true, + }); + }, + }, + ], + }; + + // + return li; + }); +} +` + + '```'; + + // + tempList = Array.from(Array(25).keys()); + templListItems: XListItem[] = this.tempList.map((i) => { + // + const li: XListItem = { + data: `Item ${i.toString()}`, + slideOptions: [ + { + id: 'remove', + icon: this.IconNames.remove, + color: this.ColorNames.Danger, + onlyIcon: true, + slot: 'end', + handler: () => { + // + this.managerService.dialogService.presentAreYouSureYesNoDialog( + () => { + this.handleRemoveItems({ items: [li.data] }); + } + ); + }, + }, + { + id: 'edit', + icon: this.IconNames.edit, + color: this.ColorNames.Warning, + onlyIcon: true, + slot: 'start', + handler: () => { + // + this.managerService.notificationService.presentInfoNotification({ + message: `edit: ${i}`, + dissmissable: true, + }); + }, + }, + ], + }; + + // + return li; + }); + + // + hideActions: XStandardType> = []; // [DefaultActionBarItemIds.Remove]; + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + //#endregion + + // + //#region UI Handlers ... + // + handleRefreshList() { + this.prepareListItems(); + } + + // + handleRemoveItems(request: XBaseRangeRequestDto) { + // + this.templListItems = this.templListItems.filter( + (li) => !request.items.includes(li.data) + ); + + // + this.managerService.notificationService.presentSuccessNotification({ + message: `${request.items.length} item(s) removed ...`, + dissmissable: true, + }); + } + //#endregion + + // + //#region Private ... + // + private prepareListItems() { + this.templListItems = this.tempList.map((i) => { + // + const li: XListItem = { + data: `Item ${i.toString()}`, + slideOptions: [ + { + id: 'remove', + icon: this.IconNames.remove, + color: this.ColorNames.Danger, + onlyIcon: true, + slot: 'end', + handler: () => { + // + this.managerService.notificationService.presentDangerNotification( + { + message: `remove: ${i}`, + dissmissable: true, + } + ); + }, + }, + { + id: 'edit', + icon: this.IconNames.edit, + color: this.ColorNames.Warning, + onlyIcon: true, + slot: 'start', + handler: () => { + // + this.managerService.notificationService.presentInfoNotification({ + message: `edit: ${i}`, + dissmissable: true, + }); + }, + }, + ], + }; + + // + return li; + }); + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\map\map.page.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\map\map.page.scss + +x-map { + --map-height: 500px; +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\map\map.page.ts + +import { + Inject, + NgZone, + Component, + Renderer2, + ElementRef, + ChangeDetectorRef, +} from '@angular/core'; +import { Subject } from 'rxjs'; +import { + XIconNames, + XMapAction, + XButtonType, + XMapLocation, + XMapComponent, + XPageContainer, + XMapActionModel, + XPopoverService, + XMapDialogService, +} from 'x-framework-components'; +import { MenuController } from '@ionic/angular'; +import { X_CONFIG } from 'src/app/config/x-config'; +import { XConfig } from 'src/app/config/app-config'; +import { ViewportRuler } from '@angular/cdk/overlay'; +import { XManagerService } from 'x-framework-services'; +import { XDialogResult, isNullOrEmptyString } from 'x-framework-core'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +@Component({ + selector: 'app-map', + templateUrl: './map.page.html', + styleUrls: ['./map.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class MapPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.map; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider(AppResourceIDs.map_description); + toolbarShowSubTitle = true; + //#endregion + + // + readonly IconNames = Object.assign({}, XIconNames); + readonly ButtonTypes = Object.assign({}, XButtonType); + readonly mapActionProvider = new Subject(); + //#endregion + + // + //#region Constructor ... + public constructor( + public zone: NgZone, + @Inject(X_CONFIG) + public config: XConfig, + public element: ElementRef, + public renderer: Renderer2, + public ruler: ViewportRuler, + public menuController: MenuController, + public managerService: XManagerService, + public changeDetector: ChangeDetectorRef, + protected popoverService: XPopoverService, + protected propertyProvider: XPageContainer, + public mapDialogService: XMapDialogService + ) { + super( + zone, + config, + element, + renderer, + ruler, + menuController, + managerService, + changeDetector, + popoverService, + propertyProvider + ); + } + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} + این مولفه جهت نمایش نقشه و مکان یابی بکار می رود + `; + + readonly contentEn = ` + # ${this.toolbarTitle} + this component used to show map and locating map. + `; + + // + readonly sample1 = + '```html' + + ` + +` + + '```'; + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + //#endregion + + // + //#region UI Handlers ... + async handleSetZoom(map?: XMapComponent) { + // + if (map) { + // + await map.setZoom(12); + return; + } + + // + this.mapActionProvider.next({ + action: XMapAction.SetZoom, + payload: 12, + }); + } + + async handleSetCenter(map?: XMapComponent) { + // + if (map) { + // + await map.setCenter(35, 35, 6); + return; + } + + // + this.mapActionProvider.next({ + action: XMapAction.SetCenter, + payload: { + zoom: '6', + latitude: '35', + longitude: '35', + } as XMapLocation, + }); + } + + async handleAddMarker(map?: XMapComponent) { + // + if (map) { + // + await map.addMarker(35, 35); + return; + } + + // + this.mapActionProvider.next({ + action: XMapAction.AddMarker, + payload: { + latitude: '35', + longitude: '35', + } as XMapLocation, + }); + } + + async handleClearMarker(map?: XMapComponent) { + // + if (map) { + // + await map.clearMarker(); + return; + } + + // + this.mapActionProvider.next({ + action: XMapAction.ClearMarker, + }); + } + + async handleGoMarkedPlace(map?: XMapComponent) { + // + if (map) { + // + await map.goMarkedPlace(); + return; + } + + // + this.mapActionProvider.next({ + action: XMapAction.GoMarkedPlace, + }); + } + + async handleLocate(map?: XMapComponent) { + // + if (map) { + // + await map.locate(); + return; + } + + // + this.mapActionProvider.next({ + action: XMapAction.Locate, + }); + } + + async handleMapDialog() { + // + this.mapDialogService.openDialog({ + latitude: 35.814738589583044, + longitude: 50.97853161365292, + zoom: 14, + }); + + // + const resSubscription = this.mapDialogService.onResult.subscribe( + (result) => { + // + if (!result || result.result !== XDialogResult.Ok) { + // + console.log('empty result'); + resSubscription.unsubscribe(); + return; + } + + // + console.log('selected Location: ', result.payload); + resSubscription.unsubscribe(); + } + ); + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\markdown\markdown.page.html + + + + + + + + + + + + + + + + + + + + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\markdown\markdown.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\markdown\markdown.page.ts + +import { Component } from '@angular/core'; +import { XIconNames } from 'x-framework-components'; +import { XMarkdownMode } from 'x-framework-components'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; +import { XResourceIDs, isNullOrEmptyString, XColor } from 'x-framework-core'; + +@Component({ + selector: 'app-markdown', + templateUrl: './markdown.page.html', + styleUrls: ['./markdown.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class MarkdownPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.markdown; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider(AppResourceIDs.markdown_description); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + readonly IconNames = Object.assign({}, XIconNames); + //#endregion + + // + readonly MarkdownModes = Object.assign({}, XMarkdownMode); + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} + این مولفه جهت دریافت و نمایش اطلاعات مبتنی بر شیوه نشانه گذاری داده ها موسوم به مارک داون (MarkDown) کاربرد دارد. + `; + + readonly contentEn = ` + # ${this.toolbarTitle} + this component used to get and show MarkDown based Data's. + `; + + // + readonly sample1 = + '```html' + + ` + +` + + '```'; + readonly sample2 = + '```html' + + ` + +` + + '```'; + + // + sampleContent1 = ` + ## Todo example + + - Variable binding + - Code refactor + - Write more unit tests + - Module configuration for markdown settings + - Module configuration for prismjs settings + `; + + readonly sampleContent2 = ` + ## Todo example + this is only a preview sample of markdown. + `; + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\model-presenter\model-presenter.page.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\model-presenter\model-presenter.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\model-presenter\model-presenter.page.ts + +import { + nameof, + assign, + XParam, + isFunction, + XResourceIDs, + XModelProperty, + XModalButtonRole, + XModelKeyProvider, + isNullOrUndefined, + isNullOrEmptyString, +} from 'x-framework-core'; +import { Subject } from 'rxjs'; +import { + XListItem, + XIconNames, + XPopoverComponent, + XListItemSlideOption, + XModelPresenterAction, + XModelPresenterActionModel, +} from 'x-framework-components'; +import { Component } from '@angular/core'; +import { XMarkdownMode } from 'x-framework-components'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +export interface DataMODEL { + firstName: string; + lastName: string; + email: string; + contacts: Array<{ + type: 'mobile' | 'phone'; + title: string; + number: string; + }>; + checkList: { + todo1: boolean; + todo2: boolean; + todo3: boolean; + todo4: Array<{ + action: string; + dueData: Date; + }>; + }; +} + +@Component({ + selector: 'app-model-presenter', + templateUrl: './model-presenter.page.html', + styleUrls: ['./model-presenter.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class ModelPresenterPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.model_presenter; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider( + AppResourceIDs.model_presenter_description + ); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + readonly IconNames = Object.assign({}, XIconNames); + //#endregion + + // + readonly MarkdownModes = Object.assign({}, XMarkdownMode); + + // + //#region Model Presenter ... + dataModel: DataMODEL = { + firstName: 'Hadi', + lastName: 'Khazaee Asl', + email: 'hadi_khazaee_asl@yahoo.com', + contacts: [ + { + type: 'mobile', + title: 'Personal', + number: '09121694056', + }, + { + type: 'phone', + title: 'saherelm office', + number: '02632757987', + }, + ], + checkList: { + todo1: true, + todo2: false, + todo3: true, + todo4: [ + { + action: 'start', + dueData: new Date(), + }, + { + action: 'end', + dueData: new Date(), + }, + ], + }, + }; + + actionProvider = new Subject(); + + availableModelKeys: string[] = [ + nameof('lastName'), + nameof('firstName'), + nameof('email'), + nameof('contacts'), + nameof('checkList'), + + // + // Contacts ... + // `${nameof('contacts')}.type`, + `${nameof('contacts')}.title`, + // `${nameof('contacts')}.number`, + + // + // Checklist ... + `${nameof('checkList')}.todo1`, + `${nameof('checkList')}.todo2`, + `${nameof('checkList')}.todo3`, + `${nameof('checkList')}.todo4`, + + // + `${nameof('checkList')}.todo4.action`, + `${nameof('checkList')}.todo4.dueData`, + ]; + + hiddenModelKeys: string[] = [`${nameof('contacts')}.type`]; + + hiddenLabelModelKeys: string[] = [`${nameof('contacts')}.number`]; + + // + sortOrederModelKeys: string[] = [ + nameof('firstName'), + nameof('lastName'), + nameof('email'), + nameof('contacts'), + nameof('checkList'), + + // + // Contacts ... + `${nameof('contacts')}.number`, + `${nameof('contacts')}.title`, + + // + // Checklist ... + `${nameof('checkList')}.todo4.dueData`, + `${nameof('checkList')}.todo4.action`, + ]; + + // + modelKeyProvider: XModelKeyProvider = (key) => { + // + let result = key as string; + + // + switch (key) { + // + case nameof('firstName'): + result = this.resourceProvider(this.AppResourceIDs.first_name); + break; + + // + case nameof('lastName'): + result = this.resourceProvider(this.AppResourceIDs.last_name); + break; + + // + case nameof('contacts'): + result = this.resourceProvider(this.AppResourceIDs.content); + break; + + // + default: + result = key as string; + break; + } + + // + return result; + }; + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} + این مولفه جهت نمایش و ویرایش یک شی یا مدل در نمایی ساختار یافته بکار می رود. + `; + + readonly contentEn = ` + # ${this.toolbarTitle} + this component used to view and edit data models in an structural view. + `; + + // + readonly sample1 = + '```html' + + ` + +` + + '```'; + + readonly sample2 = + '```html' + + ` + + + + + + + + + + + + + + + + + + ` + + '```'; + + readonly sample3 = + '```javascript' + + ` +export interface DataMODEL { + firstName: string; + lastName: string; + email: string; + contacts: Array<{ + type: 'mobile' | 'phone'; + title: string; + number: string; + }>; + checkList: { + todo1: boolean; + todo2: boolean; + todo3: boolean; + todo4: Array<{ + action: string; + dueData: Date; + }>; + }; +} + ` + + '```'; + + readonly sample4 = + '```javascript' + + ` +dataModel: DataMODEL = { + firstName: 'Hadi', + lastName: 'Khazaee Asl', + email: 'hadi_khazaee_asl@yahoo.com', + contacts: [ + { + type: 'mobile', + title: 'Personal', + number: '09121694056', + }, + { + type: 'phone', + title: 'saherelm office', + number: '02632757987', + }, + ], + checkList: { + todo1: true, + todo2: false, + todo3: true, + todo4: [ + { + action: 'start', + dueData: new Date(), + }, + { + action: 'end', + dueData: new Date(), + }, + ], + }, +}; + +actionProvider = new Subject(); + +availableModelKeys: string[] = [ + nameof('lastName'), + nameof('firstName'), + nameof('email'), + nameof('contacts'), + nameof('checkList'), + + // + // Contacts ... + \`\${nameof('contacts')}.type\`, + \`\${nameof('contacts')}.title\`, + \`\${nameof('contacts')}.number\`, + + // + // Checklist ... + \`\${nameof('checkList')}.todo1\`, + \`\${nameof('checkList')}.todo2\`, + \`\${nameof('checkList')}.todo3\`, + \`\${nameof('checkList')}.todo4\`, + + // + \`\${nameof('checkList')}.todo4.action\`, + \`\${nameof('checkList')}.todo4.dueData\`, +]; + +hiddenModelKeys: string[] = [\`\${nameof('contacts')}.type\`]; + +hiddenLabelModelKeys: string[] = [\`\${nameof('contacts')}.number\`]; + +// +sortOrederModelKeys: string[] = [ + nameof('firstName'), + nameof('lastName'), + nameof('email'), + nameof('contacts'), + nameof('checkList'), + + // + // Contacts ... + \`\${nameof('contacts')}.number\`, + \`\${nameof('contacts')}.title\`, + + // + // Checklist ... + \`\${nameof('checkList')}.todo4.dueData\`, + \`\${nameof('checkList')}.todo4.action\`, +]; + +// +modelKeyProvider: XModelPresenterModelKeyProvider = (key) => { + // + let result = key as string; + + // + switch (key) { + // + case nameof('firstName'): + result = this.resourceProvider(this.AppResourceIDs.first_name); + break; + + // + case nameof('lastName'): + result = this.resourceProvider(this.AppResourceIDs.last_name); + break; + + // + case nameof('contacts'): + result = this.resourceProvider(this.AppResourceIDs.content); + break; + + // + default: + result = key as string; + break; + } + + // + return result; +}; + ` + + '```'; + + readonly sample5 = + '```javascript' + + ` +async handlePropertyActionMenuClicked( + key: string, + prefix: string, + value: any +) { + // + if (isNullOrEmptyString(key)) { + return; + } + + // + const onlyIcon = false; + const slideOptions: XListItemSlideOption[] = []; + + // + // Edit ... + slideOptions.push({ + id: 'property_edit', + icon: this.IconNames.edit, + title: this.ResourceIDs.edit, + color: isNullOrUndefined(value) + ? this.ColorNames.Danger + : this.ColorNames.Tertiary, + slot: 'start', + onlyIcon, + handler: async () => { + await this.handlePropertyEdit(key, prefix, value); + }, + }); + + // + // Clear ... + slideOptions.push({ + id: 'property_clear', + icon: this.IconNames.clear, + title: this.ResourceIDs.clear, + color: this.ColorNames.Danger, + slot: 'start', + disabled: isNullOrUndefined(value), + onlyIcon, + handler: async () => { + await this.handlePropertyClear(key, prefix, value); + }, + }); + + // + // Menu Items ... + const item: XListItem = { + data: '', + slideOptions, + }; + + // + const popOver = await this.managerService.dialogService.presentPopover({ + component: XPopoverComponent, + componentProps: assign({}, { key: XParam.Item, value: item }), + translucent: true, + showBackdrop: true, + event, + }); + + // + const { data } = await popOver.onWillDismiss(); + if (!data || !data.id || !data.data) { + return; + } + + // + const optionId = data.id.toString() as string; + const option = + item && item.slideOptions + ? item.slideOptions.find((so) => so.id === optionId) + : null; + if (!option) { + return; + } + + // + const handler = option.handler; + if (!handler || !isFunction(handler)) { + return; + } + + // + // Run Handler ... + await handler(); +} + +async handlePropertyEdit(key: string, prefix: string, value: any) { + // + if (isNullOrEmptyString(key)) { + return; + } + + // + await this.managerService.dialogService.presentAlert({ + header: 'Change Value:', + message: 'Enter new Value:', + inputs: [ + { + type: 'text', + name: 'value', + label: 'Value', + placeholder: isNullOrUndefined(value) ? '' : \`\${value.toString()}\`, + }, + ], + buttons: [ + { + text: 'Change', + role: XModalButtonRole.Selected, + cssClass: ['btn-selected'], + handler: async (value) => { + // + let newValue = ''; + if (value && value.value) { + newValue = value.value; + } + + // + console.log('new value to set: ', newValue); + + // + this.actionProvider.next({ + action: XModelPresenterAction.SetProperty, + payload: { + key: key, + value: newValue, + keyPrefix: prefix, + } as XModelPresenterProperty, + }); + }, + }, + { + text: 'Cancel', + role: XModalButtonRole.Cancel, + cssClass: ['btn-cancel'], + }, + ], + }); +} + +async handlePropertyClear(key: string, prefix: string, value: any) { + // + if (isNullOrEmptyString(key) || isNullOrUndefined(value)) { + return; + } + + // + await this.managerService.dialogService.presentAreYouSureYesNoDialog(() => { + // + this.actionProvider.next({ + action: XModelPresenterAction.ClearProperty, + payload: { + key, + keyPrefix: prefix, + } as XModelPresenterProperty, + }); + }); +} + +async handleModelChanged(event: any) { + console.log('handle model changed: ', event); +} + ` + + '```'; + + // readonly sample3 = + // '```html' + + // ` + // ` + + // '```'; + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + //#endregion + + // + //#region UI Handlers ... + // + //#region Model Presenter Handlers ... + async handlePropertyActionMenuClicked( + key: string, + prefix: string, + value: any + ) { + // + if (isNullOrEmptyString(key)) { + return; + } + + // + const onlyIcon = false; + const slideOptions: XListItemSlideOption[] = []; + + // + // Edit ... + slideOptions.push({ + id: 'property_edit', + icon: this.IconNames.edit, + title: this.ResourceIDs.edit, + color: isNullOrUndefined(value) + ? this.ColorNames.Danger + : this.ColorNames.Tertiary, + slot: 'start', + onlyIcon, + handler: async () => { + await this.handlePropertyEdit(key, prefix, value); + }, + }); + + // + // Clear ... + slideOptions.push({ + id: 'property_clear', + icon: this.IconNames.clear, + title: this.ResourceIDs.clear, + color: this.ColorNames.Danger, + slot: 'start', + disabled: isNullOrUndefined(value), + onlyIcon, + handler: async () => { + await this.handlePropertyClear(key, prefix, value); + }, + }); + + // + // Menu Items ... + const item: XListItem = { + data: '', + slideOptions, + }; + + // + const popOver = await this.managerService.dialogService.presentPopover({ + component: XPopoverComponent, + componentProps: assign({}, { key: XParam.Item, value: item }), + translucent: true, + showBackdrop: true, + event, + }); + + // + const { data } = await popOver.onWillDismiss(); + if (!data || !data.id || !data.data) { + return; + } + + // + const optionId = data.id.toString() as string; + const option = + item && item.slideOptions + ? item.slideOptions.find((so) => so.id === optionId) + : null; + if (!option) { + return; + } + + // + const handler = option.handler; + if (!handler || !isFunction(handler)) { + return; + } + + // + // Run Handler ... + await handler(); + } + + async handlePropertyEdit(key: string, prefix: string, value: any) { + // + if (isNullOrEmptyString(key)) { + return; + } + + // + await this.managerService.dialogService.presentAlert({ + header: 'Change Value:', + message: 'Enter new Value:', + inputs: [ + { + type: 'text', + name: 'value', + label: 'Value', + placeholder: isNullOrUndefined(value) ? '' : `${value.toString()}`, + }, + ], + buttons: [ + { + text: 'Change', + role: XModalButtonRole.Selected, + cssClass: ['btn-selected'], + handler: async (value) => { + // + let newValue = ''; + if (value && value.value) { + newValue = value.value; + } + + // + console.log('new value to set: ', newValue); + + // + this.actionProvider.next({ + action: XModelPresenterAction.SetProperty, + payload: { + prefix, + key: key, + value: newValue, + } as XModelProperty, + }); + }, + }, + { + text: 'Cancel', + role: XModalButtonRole.Cancel, + cssClass: ['btn-cancel'], + }, + ], + }); + } + + async handlePropertyClear(key: string, prefix: string, value: any) { + // + if (isNullOrEmptyString(key) || isNullOrUndefined(value)) { + return; + } + + // + await this.managerService.dialogService.presentAreYouSureYesNoDialog(() => { + // + this.actionProvider.next({ + action: XModelPresenterAction.ClearProperty, + payload: { + key, + prefix, + } as XModelProperty, + }); + }); + } + + async handleModelChanged(event: any) { + console.log('handle model changed: ', event); + } + //#endregion + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\page\page.page.html + + + + + + + + + + + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\page\page.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\page\page.page.ts + +import { Component } from '@angular/core'; +import { XIconNames } from 'x-framework-components'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; +import { XResourceIDs, isNullOrEmptyString, XColor } from 'x-framework-core'; + +@Component({ + selector: 'app-page', + templateUrl: './page.page.html', + styleUrls: ['./page.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class PagePage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.page; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider(AppResourceIDs.page_description); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + readonly IconNames = Object.assign({}, XIconNames); + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} + این مولفه یکی از پرکاربرد ترین مولفه های ارائه شده توسط این چارچوب است و برای ارائه یک صفحه مجزا در یک برنامه کاربردی کاربرد دارد. + + یک صفحه از ترکیب چهار مولفه مجزا تشکیل میگردد، که عبارتند از: + + - **XToolbarComponent**: +این مولفه میتواند در ناحیه فوقانی یک صفحه قرار بگیرد و امکانات مختلفی را فراهم کند. + + - **XFooterComponent**: +این مولفه میتواند در ناحیه تحتانی یک صفحه قرار بگیرد. + + - **XNavigatorComponent**: +این مولفه می تواند در ناحیه کناری صفحه قرار گرفته و گزینه های مختلفی را بعنوان نوار پیمایش در صفحات مختلف را ارئه کند. + + - **XPageComponent**: +این مولفه از هر سه مولفه فوق استفاده می کند و امکانات لازم جهت چینش اجزاء مختلف در صفحه و مدیریت نحوه نمایش آن را فراهم می کند. + +صفحه حاضر و تمام صفحاتی که در این مستند مرور کرده اید با بهره گیری از این مولفه ایجاد شده اند. + `; + + readonly contentEn = ` + # ${this.toolbarTitle} +this is a most common component's of this Framework and used to provide an stand alone Page in an Application. + +a page presented by combining 4 unique components, which are: + + - **XToolbarComponent**: +this component can show in top area of a page and provide several options. + + - **XFooterComponent**: +this component can show in bottom area of a page. + + - **XNavigatorComponent**: +this component can show in side area of a page and provide several options as a navigation bar. + + - **XPageComponent**: +this component use all of 3 above components and provide options to manage their usage and how to looks like a page. + +this page and all pages you explored until now created useing this Component. + `; + + // + readonly sample1 = + '```html' + + ` + + + ... + +` + + '```'; + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\slider\slider.page.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Lorem ipsum dolor sit, amet consectetur adipisicing elit. Quas eum odit + labore magnam accusantium assumenda error voluptatem ad cumque velit? Eos + expedita minus doloremque error iste nam commodi aspernatur repellendus. +
+
+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\slider\slider.page.scss + +.slide-content { + direction: ltr; + text-align: justify; + padding: { + top: 24px; + bottom: 24px; + left: 16px; + right: 16px; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\slider\slider.page.ts + +import { Component } from '@angular/core'; +import { XIconNames } from 'x-framework-components'; +import { XSliderAnimation } from 'x-framework-components'; +import { XResourceIDs, isNullOrEmptyString } from 'x-framework-core'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +@Component({ + selector: 'app-slider', + templateUrl: './slider.page.html', + styleUrls: ['./slider.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class SliderPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.slider; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider(AppResourceIDs.slider_description); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + readonly IconNames = Object.assign({}, XIconNames); + //#endregion + + // + readonly SliderAnimations = Object.assign({}, XSliderAnimation); + + // + // Content ... + readonly contentFa = ` +# ${this.toolbarTitle} +این مولفه جهت نمایش محتوا بصورت کادر های لغزنده بکار می رود. +`; + + readonly contentEn = ` +# ${this.toolbarTitle} +this Component used to provide Slideable Content's. +`; + + // + readonly sample1 = + '```html' + + ` + + + + + + + + + + + + + + + + + + + + + + + + + + +` + + '```'; + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + //#endregion + + // + //#region UI Handlers ... + handleOnSlideChange(index: number) { + console.log('handleOnSlideChange: ', index); + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\small-components\small-components.page.html + + + + + + + + + + +
{{ resourceProvider(ResourceIDs.button) }}
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
{{ resourceProvider(ResourceIDs.action_bar) }}
+ + +
+ + + + + + + + + + +
+ + + + +
{{ resourceProvider(ResourceIDs.search_bar) }}
+ + +
+ + + + + + + + + + + +
+ + + + +
{{ resourceProvider(ResourceIDs.alert) }}
+ + +
+ + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + +
+
+ + + + + + + + +
+
+ + + + + + + + + + +
+ + + + +
{{ resourceProvider(ResourceIDs.notification) }}
+ + +
+ + + + + + + + + + +
+ + + + +
{{ resourceProvider(ResourceIDs.spinner) }}
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
{{ resourceProvider(ResourceIDs.loading) }}
+ + +
+ + + + + + + + + + +
+ + + + +
{{ resourceProvider(ResourceIDs.color) }}
+ + +
+ + + + + + + + + + + +
+ + + + +
{{ resourceProvider(ResourceIDs.picker) }}
+ + +
+ + + + + + + + + + +
+ + + + +
{{ resourceProvider(ResourceIDs.slotter) }}
+ + +
+ + + + +
+
+

{{ resourceProvider(ResourceIDs.horizontal) }}

+ + + +
+
+ + + Start + + + Center + + + End + + + +
+
+ +

{{resourceProvider(ResourceIDs.vertical)}}

+ + + +
+
+ + + Top + + + Center + + + Bottom + + + + + +
+ + + + +
+ {{ resourceProvider(ResourceIDs.empty_component) }} +
+ + +
+ + + + + + + + + + + +
+
+
+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\small-components\small-components.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\small-components\small-components.page.ts + +import { + XColor, + XSpinner, + XActionType, + XResourceIDs, + XModalButtonRole, + isNullOrEmptyString, +} from 'x-framework-core'; +import { + XSlotName, + XButtonType, + XSlotLayout, + XActionBarItem, + XActionBarActionFiredModel, + defaultActionBarRemoveItem, + defaultActionBarSelectAllItem, + defaultActionBarDeSelectAllItem, + defaultActionBarInverseSelectionItem, +} from 'x-framework-components'; +import { Component } from '@angular/core'; +import { + XModalContext, + SampleModalComponent, +} from './sample-modal/sample-modal.component'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +@Component({ + selector: 'app-small-components', + templateUrl: './small-components.page.html', + styleUrls: ['./small-components.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class SmallComponentsPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.small_components; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider( + AppResourceIDs.small_components_description + ); + toolbarShowSubTitle = true; + //#endregion + + // + //#region Contents Info ... + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + // Content ... + readonly contentFa = ` + # مولفه های جزئی + + مولفه هایی هستند که بسیار جزئی اما پرکاربرد هستند و شامل پیکربندی های پیچیده نیستند. + این مولفه ها را بسادگی می توان مورد بهره برداری قرار داد. + + در ادامه فهرستی از این مولفه ها بهمراه مستندات مربوط به هریک به تفکیک ارائه می گردد: + `; + readonly contentEn = ` + # Small Components + + a collection of usefull and common Components which not so complicated to use them. + these Components can be implement easily. + + the following list contains documentation and examples of how to use these Components: + `; + + // + // Button Content ... + readonly ButtonTypes = Object.assign({}, XButtonType); + readonly buttonContentFa = ` + دکمه ها در زمره پرکاربرد ترین مولفه ها در برنامه های کاربردی هستند. + + در این چارچوب مجموعه ای از دکمه ها قابل بهره برداری هستند که در ادامه به آنها اشاره خواهد شد. + `; + readonly buttonContentEn = ` + Buttons are most common Components which used in Applications. + + this Framework provide a collection of Buttons which can be used. following are some examples. + `; + + // + // Button Sample 1 ... + readonly buttonSample1 = + '```html' + + ` + + + ` + + '```'; + + // + // Button Sample 1 ... + readonly buttonSample2 = + '```html' + + ` + + + + + + ` + + '```'; + + // + // ActionBar Content ... + actionBarItems: XActionBarItem[] = [ + // + // Remove ... + { + // + ...defaultActionBarRemoveItem, + + // + index: 0, + }, + // + // Select All ... + { + // + ...defaultActionBarSelectAllItem, + + // + index: 1, + }, + // + // Inverse Selection ... + { + // + ...defaultActionBarInverseSelectionItem, + + // + index: 2, + }, + // + // De Select All ... + { + // + ...defaultActionBarDeSelectAllItem, + + // + index: 3, + }, + ]; + + readonly actionBarContentFa = ` +این مولفه جهت ارائه مجموعه ای از فرمان های عملیاتی در یک واسط کاربری یکپارچه بکار می رود +`; + readonly actionBarContentEn = ` +this Component used to represent a Collection of Action Buttons in an integrated interface. +`; + + readonly actionBarSample1 = + '```html' + + ` + + +` + + '```'; + + // + // SearchBar Content ... + readonly searchBarContentFa = ` +این مولفه جهت ارائه امکانات جستجو در اطلاعات بکار می رود. +`; + readonly searchBarContentEn = ` +this component provide a Search interface. +`; + + readonly searchBarSample1 = + '```html' + + ` + + +` + + '```'; + + // + // Alert Content ... + readonly alertContentFa = ` + این مولفه زمانی کاربرد دارد که قصد داریم پیام خاصی را به کاربر نمایش دهیم. + + گونه های مختلفی از هشدارهای از پیش تعریف شده جهت سهولت استفاده فراهم شده اند که با مطالعه مستندات می توانید آن ها را مورد بهره برداری قرار دهید. + + برای نمایش اعلان باید بصورت زیر عمل کنیم: + + `; + readonly alertContentEn = ` + this Component used when we need to show an Special kind of messages to user. + + there are some predefined of this Components, you can read the documentaion for How to use and get more info about them. + + for show an Alert you must act like this: + `; + + readonly alertSample1 = + '```typescript' + + ` +// +await this.managerService +.dialogService +.presentAlert({ + header: AppResourceIDs.alert, + subHeader: 'sub title', + message: 'message', + buttons: [ + { + text: 'OK', + role: XModalButtonRole + .Selected, + handler: () => {}, + }, + ], +}); + ` + + '```'; + + readonly alertSample2 = + '```typescript' + + ` +this.managerService + .dialogService + .nativeAlert(this + .ResourceIDs.temp_label); +` + + '```'; + + // + // Prompt Content ... + readonly promptContentFa = ` + در برخی موارد احتیاج داریم تا اطلاعات خاصی را از کاربر تحت عنوان ورودی دریافت کنیم: + `; + readonly promptContentEn = ` + in some used cases we need to give som information from user through an Alert. + `; + + readonly promptSample1 = + '```typescript' + + ` +// +await this.managerService +.dialogService +.presentAlert({ + header: 'عنوان', + subHeader: 'توضیحات', + message: 'نام شما چیست ؟', + inputs: [ + { + type: 'text', + name: 'name', + label: 'نام', + placeholder: + 'نام و نام خانوادگی ...', + }, + ], + buttons: [ + { + text: 'ثبت', + role: XModalButtonRole + .Selected, + cssClass: ['btn-selected'], + handler: async (value) => { + // + console.log( + 'OK Clicked ...', value + ); + + // + if (value && + value.name && + !isNullOrEmptyString( + value.name)) { + await this.managerService + .notificationService + .presentInfoNotification( + { + message: value.name, + dissmissable: true, + } + ); + } + }, + }, + { + text: 'لغو', + role: XModalButtonRole.Cancel, + cssClass: ['btn-cancel'], + handler: () => { + console.log( + 'Cancel Clicked ...' + ); + }, + }, + ], +}); + ` + + '```'; + + readonly promptSample2 = + '```typescript' + + ` + this.managerService.dialogService + .nativeConfirm( + this.ResourceIDs.temp_label) + .subscribe((res) => { + this.managerService + .notificationService + .presentInfoNotification({ + message: \`\${res}\`, + dissmissable: true, + }); + }); +` + + '```'; + + readonly promptSample3 = + '```typescript' + + ` +// +await this.managerService + .dialogService + .presentActionModal({ + type: XActionType.System, + playSound: true, + header: XResourceIDs.are_you_sure, + buttons: [ + { + text: XResourceIDs.yes, + role: XModalButtonRole + .Selected, + handler: async () => { + await this.managerService + .notificationService + .presentSuccessNotification( + { + message: + 'Yes Selected ...', + dissmissable: true, + } + ); + }, + }, + { + text: XResourceIDs.no, + role: XModalButtonRole.Cancel, + handler: async () => { + await this.managerService + .notificationService + .presentDangerNotification( + { + message: + 'No Selected ...', + dissmissable: true, + } + ); + }, + }, + ], +}); +` + + '```'; + + readonly colorContentFa = 'این مولفه جهت نمایش رنگ بکار می رود'; + + readonly colorContentEn = 'this component used to represent a color'; + + readonly colorSample1 = + '```typescript' + + ` + + +` + + '```'; + + readonly modalContentFa = ` +برخی اوقات احتیاج داریم که یک مولفه مجزا در انگولار را بعنوان یک کادر محاوره به کاربر نمایش دهیم. +`; + + readonly modalContentEn = ` +some times we had to show an angular component as a dialog to user. +`; + + readonly modalSample1 = + '```typescript' + + ` +const modal = await this + .managerService + .dialogService + .presentModal({ + component: + SampleModalComponent, + componentProps: { + context: 'Show Modal', + }, +}); +` + + '```'; + + readonly popOverContentFa = ` +برخی اوقات احتیاج داریم که یک مولفه مجزا در انگولار را بعنوان منوی زمینه به کاربر نمایش دهیم. +`; + readonly popOverContentEn = ` +some times we had to show an angular component as a context menu to user. +`; + + readonly popOverSample1 = + '```typescript' + + ` +const popOver = await this + .managerService + .dialogService + .presentPopover({ + event, + component: + SampleModalComponent, + componentProps: { + context: 'Show PopOver', + }, +}); +` + + '```'; + + // + // Notification Content ... + readonly notificationContentFa = ` + اعلان ها اغلب جهت اطلاع از نتیجه انجام یک عملیات به کار می روند. + `; + readonly notificationContentEn = ` + Notifications almost used to notify a process/action result to user. + `; + + readonly notificationSample1 = + '```typescript' + + ` +// +await this.managerService +.notificationService +.presentDangerNotification({ + message: XResourceIDs.error, + dissmissable: true, +}); +` + + '```'; + + // + // Spinner Content ... + readonly SpinnerNames = Object.assign({}, XSpinner); + readonly spinnerContentFa = `این مولفه اغلب جهت نمایش وضعیت در حال انجام کار در سایر مولفه ها کاربرد دارد.`; + readonly spinnerContentEn = `this Components used to act as indicator of another Components`; + + readonly spinnerSample1 = + '```html' + + ` + + + +` + + '```'; + + // + // Loading Content ... + readonly loadingContentFa = ` + این مولفه در مواردی کاربرد دارد که قصد داریم روال اجرای برنامه را تا اتمام یک فرآیند خاص متوقف کنیم. + `; + readonly loadingContentEn = `this Component used when we have to stop application process until an action finished.`; + readonly loadingSample1 = + '```typescript' + + ` +// +const loading = await this +.managerService +.dialogService +.presentLoading({ + message: this + .resourceProvider( + AppResourceIDs.temp_label + ), + spinner: this + .SpinnerNames.LinesSmall, +}); + +// +// TODO: Do Actions ... + +// +// This is for Sample tests ... +loading.dismiss(); +` + + '```'; + + // + // Picker Content ... + readonly pickerContentFa = ` + انتخاب یک یا چند گزینه از میان مجموعه ای از انتخاب ها مهمترین کاربرد این مولفه است. + `; + readonly pickerContentEn = `select on or more item from a collection of items is the main used case of this Component.`; + + readonly pickerSample1 = + '```typescript' + + ` +// +await this +.managerService +.dialogService +.presentPicker({ + buttons: [ + { + text: 'Ok', + role: XModalButtonRole + .Selected, + cssClass: ['btn-selected'], + handler: (value) => { + console.log( + 'OK Clicked ...', value + ); + }, + }, + { + text: 'Cancel', + role: XModalButtonRole + .Cancel, + cssClass: ['btn-cancel'], + handler: () => { + console.log( + 'Cancel Clicked ...' + ); + }, + }, + ], + columns: [ + { + name: 'Col_1', + options: [ + { + text: 'S 1', + value: 1, + }, + { + text: 'S 2', + value: 2, + }, + { + text: 'S 3', + value: 3, + }, + { + text: 'S 4', + value: 4, + }, + { + text: 'S 5', + value: 5, + }, + { + text: 'S 6', + value: 6, + }, + { + text: 'S 7', + value: 7, + }, + { + text: 'S 8', + value: 8, + }, + { + text: 'S 9', + value: 9, + }, + { + text: 'S 10', + value: 10, + }, + ], + }, + ], +}); +` + + '```'; + + // + // Slotter Content ... + readonly SlotNames = Object.assign({}, XSlotName); + readonly SlotLayout = Object.assign({}, XSlotLayout); + readonly slotterContentFa = ` + این مولفه جهت تقسیم فضای در دسترس بکار میرود. + + توسط بکارگری این مولفه می توان چینش محتوا را مدیریت کرد + `; + readonly slotterContentEn = ` + this Component used to divide available screen space. + + with help of this Component you can manage how Components appear in available space. + `; + + readonly slotterSample1 = + '```html' + + ` + + + Start + + + Center + + + End + + +` + + '```'; + + readonly slotterSample2 = + '```html' + + ` + + + Top + + + Center + + + Bottom + + +` + + '```'; + + // + // Empty Component ... + readonly emptyContentFa = ` +از این مولفه جهت اطلاع رسانی در خصوص عدم وجود مورد یا مواردی که کاربر از منابع درخواست کرده است استفاده می گیرد. +`; + readonly emptyContentEn = ` +this component used when user requet item(s) from resources and the result is empty. +`; + + readonly emptySample1 = + '```html' + + ` + +` + + '```'; + + //#endregion + //#endregion + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + //#endregion + + // + //#region UI Handlers ... + // + async handleButtonClicked() { + await this.managerService.notificationService.presentSuccessNotification({ + message: this.resourceProvider(AppResourceIDs.temp_label), + dissmissable: true, + }); + } + + // + async handleActionFired(event: XActionBarActionFiredModel) { + // + const name = event.action; + await this.managerService.notificationService.presentXNotification({ + message: `Action Fired: ${name}`, + dissmissable: true, + opt: { + color: XColor.Dark, + }, + }); + } + + // + async handleShowAlert() { + // + await this.managerService.dialogService.presentAlert({ + header: AppResourceIDs.alert, + subHeader: 'sub title', + message: 'message', + buttons: [ + { + text: 'OK', + role: XModalButtonRole.Selected, + handler: () => {}, + }, + ], + }); + } + + handleShowNativeAlert() { + this.managerService.dialogService.nativeAlert(this.ResourceIDs.temp_label); + } + + async handleShowPrompt() { + // + await this.managerService.dialogService.presentAlert({ + header: 'عنوان', + subHeader: 'توضیحات', + message: 'نام شما چیست ؟', + inputs: [ + { + type: 'text', + name: 'name', + label: 'نام', + placeholder: 'نام و نام خانوادگی ...', + }, + ], + buttons: [ + { + text: 'ثبت', + role: XModalButtonRole.Selected, + cssClass: ['btn-selected'], + handler: async (value) => { + // + console.log('OK Clicked ...', value); + + // + if (value && value.name && !isNullOrEmptyString(value.name)) { + await this.managerService.notificationService.presentInfoNotification( + { + message: value.name, + dissmissable: true, + } + ); + } + }, + }, + { + text: 'لغو', + role: XModalButtonRole.Cancel, + cssClass: ['btn-cancel'], + handler: () => { + console.log('Cancel Clicked ...'); + }, + }, + ], + }); + } + + handleShowNativePrompt() { + // + this.managerService.dialogService + .nativeConfirm(this.ResourceIDs.temp_label) + .subscribe((res) => { + this.managerService.notificationService.presentInfoNotification({ + message: `${res}`, + dissmissable: true, + }); + }); + } + + async handleShowActionModal() { + // + await this.managerService.dialogService.presentActionModal({ + type: XActionType.System, + playSound: true, + header: XResourceIDs.are_you_sure, + buttons: [ + { + text: XResourceIDs.yes, + role: XModalButtonRole.Selected, + handler: async () => { + await this.managerService.notificationService.presentSuccessNotification( + { + message: 'Yes Selected ...', + dissmissable: true, + } + ); + }, + }, + { + text: XResourceIDs.no, + role: XModalButtonRole.Cancel, + handler: async () => { + await this.managerService.notificationService.presentDangerNotification( + { + message: 'No Selected ...', + dissmissable: true, + } + ); + }, + }, + ], + }); + } + + async handleShowModal() { + // + const modal = await this.managerService.dialogService.presentModal({ + component: SampleModalComponent, + componentProps: { + context: XModalContext.Modal, + }, + }); + } + + async handleShowPopOver(event: any) { + // + const popOver = await this.managerService.dialogService.presentPopover({ + event, + component: SampleModalComponent, + componentProps: { + context: XModalContext.PopOver, + }, + }); + } + + async handleShowNotification() { + // + await this.managerService.notificationService.presentDangerNotification({ + message: XResourceIDs.error, + dissmissable: true, + }); + } + + async handleShowLoading() { + // + const loading = await this.managerService.dialogService.presentLoading({ + message: this.resourceProvider(AppResourceIDs.default_loading), + spinner: this.SpinnerNames.LinesSmall, + }); + + // + // TODO: Do Actions ... + + // + // This is for Sample tests ... + setTimeout(() => { + loading.dismiss(); + }, 2000); + } + + async handleShowSelect() { + // + await this.managerService.dialogService.presentPicker({ + buttons: [ + { + text: 'Ok', + role: XModalButtonRole.Selected, + cssClass: ['btn-selected'], + handler: (value) => { + console.log('OK Clicked ...', value); + }, + }, + { + text: 'Cancel', + role: XModalButtonRole.Cancel, + cssClass: ['btn-cancel'], + handler: () => { + console.log('Cancel Clicked ...'); + }, + }, + ], + columns: [ + { + name: 'Col_1', + options: [ + { + text: 'S 1', + value: 1, + }, + { + text: 'S 2', + value: 2, + }, + { + text: 'S 3', + value: 3, + }, + { + text: 'S 4', + value: 4, + }, + { + text: 'S 5', + value: 5, + }, + { + text: 'S 6', + value: 6, + }, + { + text: 'S 7', + value: 7, + }, + { + text: 'S 8', + value: 8, + }, + { + text: 'S 9', + value: 9, + }, + { + text: 'S 10', + value: 10, + }, + ], + }, + ], + }); + } + + async handleQueryChanged(query: string) { + // + if (isNullOrEmptyString(query)) { + return; + } + + // + await this.managerService.notificationService.presentSuccessNotification({ + message: `Search Query: ${query}`, + dissmissable: true, + }); + } + + async handleRefreshEmpty() { + await this.managerService.notificationService.presentWarningNotification({ + message: 'Refresh in Empty Clicked ...', + dissmissable: true, + }); + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\small-components\sample-modal\sample-modal.component.html + +
+

Sample Modal Component

+

this is sample modal component content ...

+ + Binded Context: {{ context }} + + CLOSE +
+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\small-components\sample-modal\sample-modal.component.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\small-components\sample-modal\sample-modal.component.ts + +import { Component, Input } from '@angular/core'; +import { ModalController, PopoverController } from '@ionic/angular'; + +export enum XModalContext { + Modal = 'modal', + PopOver = 'pop_over', +} + +@Component({ + selector: 'app-sample-modal', + templateUrl: './sample-modal.component.html', + styleUrls: ['./sample-modal.component.scss'], +}) +export class SampleModalComponent { + @Input() + context: XModalContext; + + constructor( + private modalController: ModalController, + private popOverController: PopoverController + ) {} + + handleClosePopup() { + // + if (!this.context) { + return; + } + + // + switch (this.context) { + // + case XModalContext.Modal: + this.modalController.dismiss(); + break; + + // + case XModalContext.PopOver: + this.popOverController.dismiss(); + break; + } + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\stepper\stepper.page.html + + + + + + + + + + + + + + + + + + + + + +
Step ONe
+ + +
+ + + +
Step TWo
+ + +
+
+
+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\stepper\stepper.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\stepper\stepper.page.ts + +import { + XIconNames, + XFormConfig, + XStepperType, + XFormComponent, + XFormControlType, + XStepperTypeIdentifier, + XFormSelectControlOption, + XFormStatusChangeEventModel, +} from 'x-framework-components'; +import { Component } from '@angular/core'; +import { Validators } from '@angular/forms'; +import { XResourceIDs, isNullOrEmptyString } from 'x-framework-core'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +interface StepOne { + id: number; + name: string; + family: string; +} + +interface StepTwo { + id: number; + userName: string; + password: string; +} + +interface StepperPresentationType { + type: XStepperTypeIdentifier; +} + +@Component({ + selector: 'app-stepper', + templateUrl: './stepper.page.html', + styleUrls: ['./stepper.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class StepperPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.stepper; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider(AppResourceIDs.stepper_description); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + readonly IconNames = Object.assign({}, XIconNames); + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} + از این مولفه جهت دریافت یا نمایش اطلاعات در گام های مختلف از کاربر استفاده می گردد. + `; + + readonly contentEn = ` + # ${this.toolbarTitle} + this component used to get or show data to user in several steps. + `; + + // + readonly sample1 = + '```html' + + ` + + + + +
+ Step ONe +
+ + +
+ + + +
+ Step TWo +
+ + +
+
+` + + '```'; + + // + currentStepperType: XStepperTypeIdentifier; + stepperPresentationTypes = [ + `${XStepperType.VERTICAL}`, + `${XStepperType.HORIZONTAL}`, + ]; + stepperPresentationTypeFormConfig: XFormConfig = { + name: 'Steper_Present_Type', + model: { + type: XStepperType.HORIZONTAL, + }, + controls: [ + // + // Id ... + { + index: 0, + propName: 'type', + type: { + type: XFormControlType.Select, + config: { + multiple: false, + options: this.stepperPresentationTypes.map((k) => { + return { + value: k, + viewValue: this.resourceProvider(k), + } as XFormSelectControlOption; + }), + }, + }, + appearance: { + label: this.ResourceIDs.type, + }, + eventHandlers: { + valueChanged: (model) => { + // + if (!model || !model.value) { + return; + } + + // + const type = model.value as XStepperTypeIdentifier; + if (!type) { + return; + } + + // + this.currentStepperType = type; + this.detectChanges(); + }, + }, + }, + ], + }; + + // + stepOneModelConfig: XFormConfig = { + name: 'StepOneForm', + controls: [ + // + // Id ... + { + index: 0, + propName: 'id', + type: { + type: XFormControlType.Number, + }, + appearance: { + label: this.ResourceIDs.id, + }, + validators: { + validators: [Validators.required], + }, + }, + // + // Name ... + { + index: 1, + propName: 'name', + type: { + type: XFormControlType.Text, + }, + appearance: { + label: this.ResourceIDs.first_name, + }, + validators: { + validators: [Validators.required], + }, + }, + // + // Family ... + { + index: 2, + propName: 'family', + type: { + type: XFormControlType.Text, + }, + appearance: { + label: this.ResourceIDs.last_name, + }, + validators: { + validators: [Validators.required], + }, + }, + ], + }; + + // + stepTwoModelConfig: XFormConfig = { + name: 'StepTwoForm', + controls: [ + // + // Id ... + { + index: 0, + propName: 'id', + type: { + type: XFormControlType.Number, + }, + appearance: { + label: this.ResourceIDs.id, + }, + validators: { + validators: [Validators.required], + }, + }, + // + // Username ... + { + index: 1, + propName: 'userName', + type: { + type: XFormControlType.Text, + }, + appearance: { + label: this.ResourceIDs.username, + }, + validators: { + validators: [Validators.required], + }, + }, + // + // Password ... + { + index: 2, + propName: 'password', + type: { + type: XFormControlType.Password, + }, + appearance: { + label: this.ResourceIDs.password, + }, + validators: { + validators: [Validators.required], + }, + }, + ], + }; + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + + handleFormOneStatusChanged( + event: XFormStatusChangeEventModel, + form: XFormComponent + ) { + console.log('status changed: ', event, form.isStatusValid); + } + + handleResetStepper(id: string) { + // + console.log('stepper resetted at: ', id); + + // + this.stepOneModelConfig = { + ...this.stepOneModelConfig, + model: null, + }; + + // + this.stepTwoModelConfig = { + ...this.stepTwoModelConfig, + model: null, + }; + + // + this.detectChanges(); + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\tab-navigator\tab-navigator.page.html + + + + + + + + + + + + {{ resourceProvider(ResourceIDs.preview) }} + + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\tab-navigator\tab-navigator.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\tab-navigator\tab-navigator.page.ts + +import { Component } from '@angular/core'; +import { Pages } from 'src/app/config/page.config'; +import { XIconNames } from 'x-framework-components'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; +import { XColor, XResourceIDs, isNullOrEmptyString } from 'x-framework-core'; + +@Component({ + selector: 'app-tab-navigator', + templateUrl: './tab-navigator.page.html', + styleUrls: ['./tab-navigator.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class TabNavigatorPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.tab_navigator; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider( + AppResourceIDs.tab_navigator_description + ); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + readonly IconNames = Object.assign({}, XIconNames); + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} + این مولفه زمانی کاربرد دارد که قصد داریم صفحاتی را در غالب نمایه ای مبتنی بر برگه ها دسته بندی کرده و نمایش دهیم. + `; + + readonly contentEn = ` + # ${this.toolbarTitle} + this component used to group Pages in a Tabbed template and show them. + `; + + // + readonly sample1 = + '```html' + + ` + +` + + '```'; + + // + tabsHref = Pages.TabsNav.baseRoute; + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\table\table.page.html + + + + + + + + + + + + + + + + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\table\table.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\table\table.page.ts + +import { + XResourceIDs, + isNullOrEmptyString, + XColorWithBrightness, +} from 'x-framework-core'; +import { + XIconNames, + XTableConfig, + XTableItemPosition, +} from 'x-framework-components'; +import { Component } from '@angular/core'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +interface Temp { + id?: string; + name: string; + family: string; + phoneNumber: string; + value: string; + slided: boolean; + checked: boolean; + level: number; + person: string; + dob: Date; +} + +@Component({ + selector: 'app-table', + templateUrl: './table.page.html', + styleUrls: ['./table.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class TablePage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.table; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider(AppResourceIDs.table_description); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + readonly IconNames = Object.assign({}, XIconNames); + readonly ColorNames = Object.assign({}, XColorWithBrightness); + //#endregion + + // + //#region Data Preparation For Table ... + // + readonly TableItemPositions = Object.assign({}, XTableItemPosition); + + // + tempItems: Partial[] = [ + { + id: '71f14439-262f-44bf-8dc8-ee85929c349c', + name: 'Deidre', + family: 'Thomas', + phoneNumber: '+98 (864) 472-3812', + }, + { + id: 'caa22853-dd2d-4669-8b63-b1fe4a38ea07', + name: 'Hubbard', + family: 'Preston', + phoneNumber: '+98 (899) 452-3720', + }, + { + id: 'ccb62dc4-a06b-412d-800d-02d18f54a0e0', + name: 'Cooley', + family: 'Nixon', + phoneNumber: '+98 (926) 459-2360', + }, + { + id: '6a946279-fe01-4e17-bb2c-a1931780ce9d', + name: 'Becky', + family: 'Wallace', + phoneNumber: '+98 (914) 519-3589', + }, + { + id: '8601242a-f2ff-465f-8275-5b991b4a8ade', + name: 'Kidd', + family: 'Shelton', + phoneNumber: '+98 (848) 570-3661', + }, + { + id: '1f4bd5e8-d7be-460d-9bfb-f0738957bc4b', + name: 'Caitlin', + family: 'Camacho', + phoneNumber: '+98 (959) 453-2722', + }, + { + id: '18a874dc-1f3d-4cdc-a3c7-69d2f417e18e', + name: 'Estes', + family: 'Rivera', + phoneNumber: '+98 (917) 436-3237', + }, + { + id: '5a5f2a8e-d20e-4fe9-ae6f-84dde7602e9f', + name: 'Ines', + family: 'Powell', + phoneNumber: '+98 (917) 539-3108', + }, + { + id: '663c8403-e525-4b51-b262-e876e97d4532', + name: 'Carrillo', + family: 'Acevedo', + phoneNumber: '+98 (828) 556-3742', + }, + { + id: '7e6d0bb6-4180-4329-96cc-364a9a534a04', + name: 'Ina', + family: 'Wilkinson', + phoneNumber: '+98 (933) 560-3834', + }, + { + id: '19c21deb-f8b7-40c7-a0bb-da203193c114', + name: 'Mercer', + family: 'Adkins', + phoneNumber: '+98 (981) 480-3459', + }, + { + id: 'c2e4de31-d406-4611-9dbe-cd7bdefdc5bc', + name: 'Donaldson', + family: 'Vega', + phoneNumber: '+98 (986) 554-2571', + }, + { + id: 'cf846f56-b381-447b-96a1-0d7c29736f4f', + name: 'Natalie', + family: 'Cantu', + phoneNumber: '+98 (857) 445-2595', + }, + { + id: 'c5252e01-8a26-44f1-846d-28c8810b10b8', + name: 'Christa', + family: 'Higgins', + phoneNumber: '+98 (983) 498-3785', + }, + { + id: 'e7e5b97a-67f8-4bff-8c8c-b68cf6a24b2f', + name: 'Kelley', + family: 'Pitts', + phoneNumber: '+98 (948) 588-3437', + }, + { + id: '9db9d0ab-a2c4-4edb-8d0b-e664c86c4e55', + name: 'Shelby', + family: 'Macdonald', + phoneNumber: '+98 (882) 401-2642', + }, + { + id: '41305b10-aa7a-415d-a819-5e6ab05e0a49', + name: 'Preston', + family: 'Patton', + phoneNumber: '+98 (923) 509-3278', + }, + { + id: '35acdcfe-7015-4a27-a5de-0f5c701a9152', + name: 'Allen', + family: 'Flores', + phoneNumber: '+98 (957) 587-3471', + }, + { + id: '31f568b8-272a-43c5-bb7a-bf25c2fa6da9', + name: 'Margaret', + family: 'Neal', + phoneNumber: '+98 (870) 480-3528', + }, + { + id: '5ef4be35-3d02-45f7-9c25-bf828b4a3845', + name: 'Kirkland', + family: 'Hewitt', + phoneNumber: '+98 (958) 466-2957', + }, + { + id: 'e581d671-5d35-4c21-af77-71f881f8d764', + name: 'Herminia', + family: 'Silva', + phoneNumber: '+98 (925) 546-3981', + }, + { + id: '4242bc21-945e-4cbe-b73c-bacc0c8b2013', + name: 'Galloway', + family: 'Tyler', + phoneNumber: '+98 (971) 468-2144', + }, + { + id: '58f91369-8718-4c01-aff8-8a5d65527948', + name: 'Hull', + family: 'Rojas', + phoneNumber: '+98 (950) 439-3702', + }, + { + id: 'e6d25e18-f272-4ac3-b7f6-54c8bae985b5', + name: 'Sandoval', + family: 'Norton', + phoneNumber: '+98 (920) 439-2511', + }, + { + id: 'bff9bdad-2947-4fd9-a18a-92388e32aa0b', + name: 'Alba', + family: 'Collins', + phoneNumber: '+98 (957) 445-2714', + }, + { + id: '8272763a-6a67-41da-924e-0f417fb259da', + name: 'Montoya', + family: 'Stein', + phoneNumber: '+98 (889) 585-3578', + }, + { + id: '29a6ff2c-746e-4214-b28f-073ac201ea96', + name: 'Grant', + family: 'Gilliam', + phoneNumber: '+98 (814) 562-3575', + }, + { + id: '416cb3f4-30ae-4874-a7c3-ffd401cf6c3f', + name: 'Meadows', + family: 'Mcknight', + phoneNumber: '+98 (994) 589-2830', + }, + { + id: 'd061721e-ca9c-4054-9aa1-1196a51a0256', + name: 'Harris', + family: 'Rhodes', + phoneNumber: '+98 (853) 489-2436', + }, + { + id: 'd01323d5-5126-4c8a-ad89-0298acf4d82d', + name: 'Frieda', + family: 'Hayden', + phoneNumber: '+98 (930) 436-2770', + }, + { + id: '34225f99-0986-4294-95f2-3c8961b15036', + name: 'Rasmussen', + family: 'Ross', + phoneNumber: '+98 (821) 599-2812', + }, + { + id: '6f7a24d4-8b82-45c4-8b5b-a3053c5a7023', + name: 'Mitzi', + family: 'Gregory', + phoneNumber: '+98 (838) 404-3840', + }, + { + id: 'acba244a-12b5-4291-beb2-66a64127121d', + name: 'Washington', + family: 'Cruz', + phoneNumber: '+98 (975) 465-2025', + }, + { + id: '2c70c776-80dd-4be9-b32f-6af7d6cea5b0', + name: 'Jewell', + family: 'Rios', + phoneNumber: '+98 (909) 453-3851', + }, + { + id: '5f98d7a6-c98b-4dc7-bf90-4df04074b57e', + name: 'Heath', + family: 'Bush', + phoneNumber: '+98 (976) 516-3200', + }, + { + id: 'bc51bdc5-47f6-45b4-8c51-6d86324b90d2', + name: 'Laura', + family: 'Edwards', + phoneNumber: '+98 (966) 546-3720', + }, + { + id: 'f1f47337-cc3f-49e4-acb7-5989f5eb1a30', + name: 'Harriett', + family: 'Sanders', + phoneNumber: '+98 (816) 579-3042', + }, + { + id: '2fce8cd5-398c-4d73-8151-3d0fb375ff49', + name: 'Milagros', + family: 'Vazquez', + phoneNumber: '+98 (941) 437-2907', + }, + { + id: 'b725ce24-b08d-4c8e-95ec-5926804ccde3', + name: 'Sheila', + family: 'Aguilar', + phoneNumber: '+98 (855) 509-3995', + }, + { + id: 'f7d64d31-43d2-416f-ba19-397b407afeba', + name: 'Eunice', + family: 'Langley', + phoneNumber: '+98 (959) 564-2615', + }, + { + id: '6f73fd66-2931-4f1f-9cd1-95803724ff38', + name: 'Valerie', + family: 'House', + phoneNumber: '+98 (985) 550-3203', + }, + { + id: '17fab0f9-f37d-4ed0-8656-98b9f9c65333', + name: 'Carmella', + family: 'Young', + phoneNumber: '+98 (964) 548-3625', + }, + { + id: '7e9e3057-34df-4727-8a7e-750e876a7ca8', + name: 'Brittany', + family: 'Pennington', + phoneNumber: '+98 (840) 499-3543', + }, + { + id: '4ca1aebb-8d6f-4c62-9633-353abf1cab06', + name: 'Polly', + family: 'Roberts', + phoneNumber: '+98 (821) 554-2791', + }, + { + id: '147c9f63-800a-4600-94dd-7fa39e233cd7', + name: 'Estrada', + family: 'Tate', + phoneNumber: '+98 (844) 586-3191', + }, + { + id: 'decca57e-7d69-4152-99f0-3a56262db788', + name: 'Aimee', + family: 'Morrow', + phoneNumber: '+98 (920) 522-2471', + }, + { + id: '1b7d57b2-0169-4949-a1e9-42137085dcda', + name: 'Elinor', + family: 'Gill', + phoneNumber: '+98 (981) 525-3212', + }, + { + id: 'db3e2d62-d8cf-4809-9226-49a34a4fbea1', + name: 'Velasquez', + family: 'Taylor', + phoneNumber: '+98 (879) 440-2756', + }, + { + id: 'a3f6bda0-0975-4e4e-8bcd-1a9be84777ac', + name: 'Hardin', + family: 'Rowe', + phoneNumber: '+98 (945) 564-3793', + }, + { + id: '17c0c061-3215-429d-9172-be3d8ca69119', + name: 'Miranda', + family: 'Hendrix', + phoneNumber: '+98 (804) 519-2572', + }, + { + id: '634862e6-09bf-41c3-8553-c8807aab2b22', + name: 'Kristin', + family: 'Mcgowan', + phoneNumber: '+98 (974) 533-3607', + }, + { + id: '4214933e-a57c-495c-8c32-ddb9960556f6', + name: 'Ila', + family: 'Gutierrez', + phoneNumber: '+98 (814) 546-2444', + }, + { + id: '74f62419-a4eb-414f-9a84-d82dfd117917', + name: 'Sophie', + family: 'Pena', + phoneNumber: '+98 (962) 458-3451', + }, + { + id: 'c3cc6ff3-d0ef-45fb-b6a7-b499ff13fedf', + name: 'Sara', + family: 'Burton', + phoneNumber: '+98 (937) 516-2702', + }, + { + id: '969ca3e7-78ad-4fbe-b413-0e12021d07b9', + name: 'Valeria', + family: 'Spence', + phoneNumber: '+98 (968) 403-2227', + }, + { + id: 'e73ba5bb-adb7-423e-a6e9-22be1501987c', + name: 'Stefanie', + family: 'Erickson', + phoneNumber: '+98 (950) 545-2095', + }, + { + id: '83ae1198-c533-43dd-ae1a-85bc5f503293', + name: 'Bean', + family: 'Newman', + phoneNumber: '+98 (824) 504-2755', + }, + { + id: 'a2f3949a-7d24-4c42-ac6d-573c11c903fd', + name: 'Tracie', + family: 'Keith', + phoneNumber: '+98 (981) 499-3453', + }, + { + id: '7f52c00e-4ccb-4458-ad0c-bb7d9ff759a4', + name: 'Cardenas', + family: 'Trevino', + phoneNumber: '+98 (942) 476-2939', + }, + { + id: '48f9d38e-9211-420f-968e-098f7a3dfeaf', + name: 'Yolanda', + family: 'Hamilton', + phoneNumber: '+98 (936) 418-3047', + }, + { + id: '0f739ca9-1a2a-41f1-b703-9ddc98e7b328', + name: 'Mendoza', + family: 'Cunningham', + phoneNumber: '+98 (980) 547-3912', + }, + { + id: '3e3e68cc-053a-4811-9987-7c700fcc32ee', + name: 'Singleton', + family: 'Harmon', + phoneNumber: '+98 (975) 564-3890', + }, + { + id: 'd7d6e72b-5a63-440d-bdf0-125bd9d8d4dd', + name: 'Witt', + family: 'Huffman', + phoneNumber: '+98 (867) 475-3332', + }, + { + id: '46c951e3-ab31-44ca-b5eb-2f988dd57562', + name: 'James', + family: 'Mccullough', + phoneNumber: '+98 (895) 507-2233', + }, + { + id: '9b7b2396-5758-44be-9d5d-4eef04742a53', + name: 'Stevenson', + family: 'Robles', + phoneNumber: '+98 (847) 587-3623', + }, + { + id: 'a0db4093-ec6b-41ed-8ca5-a9b43ee302b7', + name: 'Mcknight', + family: 'Perry', + phoneNumber: '+98 (881) 443-2991', + }, + { + id: '1ad02420-9ac0-4250-940c-fc0ea24964a8', + name: 'Flowers', + family: 'Emerson', + phoneNumber: '+98 (933) 514-3019', + }, + { + id: 'b2ffa8f4-4f95-4b9d-a219-780d5f4dbd69', + name: 'Karin', + family: 'James', + phoneNumber: '+98 (958) 486-2623', + }, + { + id: 'd87e51b3-80db-4e7c-b0d0-9f39c633b70a', + name: 'Marisol', + family: 'William', + phoneNumber: '+98 (961) 533-3778', + }, + { + id: '1ce985cc-f810-4972-8e9b-59dfb67b161c', + name: 'Emilia', + family: 'Black', + phoneNumber: '+98 (984) 592-2589', + }, + { + id: '4b5be713-cb7e-46cb-b72b-e8c4f644693a', + name: 'Irene', + family: 'Miles', + phoneNumber: '+98 (950) 431-2351', + }, + { + id: 'c1ff3dfd-21b4-4824-aeef-d3b6a36ba3ab', + name: 'Tonia', + family: 'Serrano', + phoneNumber: '+98 (955) 511-2933', + }, + { + id: '97eec6ce-ccb0-4050-b413-96a0a4afce7a', + name: 'Thelma', + family: 'Odom', + phoneNumber: '+98 (844) 524-2142', + }, + { + id: 'e9111905-fc22-4e35-a3a6-4a4c0b6bc0c0', + name: 'Kerry', + family: 'Rose', + phoneNumber: '+98 (947) 581-2952', + }, + { + id: '9129d569-5acd-43b7-8eac-de7a9f4f7336', + name: 'Mcgee', + family: 'Hernandez', + phoneNumber: '+98 (865) 521-3785', + }, + { + id: '221e68ec-0462-4418-88c9-d433f1d60ae1', + name: 'Shepard', + family: 'Santiago', + phoneNumber: '+98 (892) 446-3690', + }, + { + id: '429b8bcc-028d-4989-89d9-5519dc6fec0f', + name: 'Emily', + family: 'French', + phoneNumber: '+98 (836) 537-3964', + }, + { + id: 'c750ebd9-9aa0-426f-b668-5702500f7178', + name: 'Clements', + family: 'Carson', + phoneNumber: '+98 (916) 460-3168', + }, + { + id: '2f75f87c-65f9-479b-bec4-5c83fbbfb96a', + name: 'Gibbs', + family: 'Burch', + phoneNumber: '+98 (950) 543-2339', + }, + { + id: '6dbf72ff-5761-4c0b-9781-93cd4606fd4b', + name: 'Pearson', + family: 'Petersen', + phoneNumber: '+98 (889) 591-2938', + }, + { + id: '8944a741-1f18-4d30-8536-a306ab1b5b8f', + name: 'Stout', + family: 'Castillo', + phoneNumber: '+98 (938) 578-3360', + }, + { + id: 'aca707ac-97fe-4f7f-8665-ab34d863bc3a', + name: 'Leonard', + family: 'Finch', + phoneNumber: '+98 (821) 441-3242', + }, + { + id: '43cc621c-17fb-4196-a28e-658f1fc4752e', + name: 'Nelson', + family: 'Kirkland', + phoneNumber: '+98 (869) 431-2288', + }, + { + id: '63292aab-ddc3-4dd8-851a-6430c85f3ad3', + name: 'Olson', + family: 'Sherman', + phoneNumber: '+98 (865) 446-3664', + }, + { + id: '40f28107-6a4d-4c1c-a506-a8dc4d65c6e4', + name: 'Bray', + family: 'Hansen', + phoneNumber: '+98 (808) 532-3820', + }, + { + id: '02ab1c0b-654f-4277-92dd-1a0219bda442', + name: 'Antonia', + family: 'Bell', + phoneNumber: '+98 (802) 572-3045', + }, + { + id: '15019624-9683-4977-a2d8-dc47e434f086', + name: 'Cecile', + family: 'Maxwell', + phoneNumber: '+98 (804) 452-2908', + }, + ]; + + // + tempTableProps: XTableConfig> = { + items: [...this.tempItems], + columns: [ + { + name: 'name', + title: 'Name', + sortable: true, + description: 'Name of Person', + }, + { + name: 'family', + title: 'Family', + sortable: true, + description: 'LastName of Person', + // hidden: this.managerService.isMobileUi$ + }, + { + name: 'phoneNumber', + title: 'Phone Number', + description: 'Phone Number of Person', + // hidden: this.managerService.isMobileUi$ + }, + { + customColumn: true, + name: 'Actions', + title: 'Actions', + description: 'row Action', + }, + ], + }; + //#endregion + + // + // Content ... + readonly contentFa = ` +# ${this.toolbarTitle} +این مولفه جهت نمایش داده های مرتبط بصورت جدول بکار می رود. +`; + + readonly contentEn = ` +# ${this.toolbarTitle} +this Component used to show a collection of related Data in a Table. +`; + + // + readonly sample1 = + '```typescript' + + ` + // + interface Temp { + id?: string; + name: string; + family: string; + phoneNumber: string; + value: string; + slided: boolean; + checked: boolean; + level: number; + person: string; + dob: Date; + } + + // + tempTableProps: XTableProps> = { + items: [ + { + id: ' + 71f14439-262f-44bf-8dc8-ee85929c349c', + name: 'Deidre', + family: 'Thomas', + phoneNumber: '+98 (864) 472-3812' + }, + { + id: ' + caa22853-dd2d-4669-8b63-b1fe4a38ea07', + name: 'Hubbard', + family: 'Preston', + phoneNumber: '+98 (899) 452-3720' + }, + { + id: ' + ccb62dc4-a06b-412d-800d-02d18f54a0e0', + name: 'Cooley', + family: 'Nixon', + phoneNumber: '+98 (926) 459-2360' + }, + { + ... + }, + ... + ], + columns: [ + { + name: 'name', + title: 'Name', + sortable: true, + description: 'Name of Person' + }, + { + name: 'family', + title: 'Family', + sortable: true, + description: 'LastName of Person' + }, + { + name: 'phoneNumber', + title: 'Phone Number', + description: 'Phone Number of Person' + }, + { + customColumn: true, + name: 'Actions', + title: 'Actions', + description: 'row Action' + } + ] + }; +` + + '```'; + + // + readonly sample2 = + '```html' + + ` + + +` + + '```'; + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + + // + handleRowSelected(row: any) { + console.log('handleRowSelected: ', row); + } + + // + handleRowSelectionChanged(selectedRows: any[]) { + console.log('handleRowSelectionChanged: ', selectedRows); + } + + // + async handleRemoveItems(items: Partial[]) { + // + console.log('Remove Items: ', items); + if (!this.hasChild(items)) { + return; + } + + // + const removedItems = this.tempItems.filter((i) => !items.includes(i)); + console.log('num of items before remove: ', this.tempItems.length); + console.log('num of items after remove: ', removedItems.length); + + // + const newTableProps = { + ...this.tempTableProps, + items: [...removedItems], + }; + + // + this.tempTableProps = { + ...newTableProps, + }; + + // + this.detectChanges(); + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\tabs\tabs.page.html + + + + + + + + + + + + + + + + + +
+ Tab 1 +
+
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Omnis similique + reiciendis vero vel. Et, in nemo. Sapiente doloremque provident ex + dolore, expedita totam ipsam architecto vero mollitia magni. Corporis, + dolore. +
+
+ + +
+ Tab 2 +
+ +
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Omnis similique + reiciendis vero vel. Et, in nemo. Sapiente doloremque provident ex + dolore, expedita totam ipsam architecto vero mollitia magni. Corporis, + dolore. +
+
+ + +
+ Tab 3 +
+ +
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Omnis similique + reiciendis vero vel. Et, in nemo. Sapiente doloremque provident ex + dolore, expedita totam ipsam architecto vero mollitia magni. Corporis, + dolore. +
+
+ + +
+ Tab 4 +
+ +
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Omnis similique + reiciendis vero vel. Et, in nemo. Sapiente doloremque provident ex + dolore, expedita totam ipsam architecto vero mollitia magni. Corporis, + dolore. +
+
+
+
+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\tabs\tabs.page.scss + +.tab-wrapper { + padding: { + top: 24px; + left: 6px; + right: 6px; + bottom: 24px; + }; +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\tabs\tabs.page.ts + +import { + XTabAlign, + XIconNames, + XTabsHeaderPosition, +} from 'x-framework-components'; +import { Component } from '@angular/core'; +import { VPageComponent } from '../../../views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; +import { XResourceIDs, isNullOrEmptyString, XColor } from 'x-framework-core'; + +@Component({ + selector: 'app-tabs', + templateUrl: './tabs.page.html', + styleUrls: ['./tabs.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class TabsPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.tabs; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider(AppResourceIDs.tabs_description); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + readonly IconNames = Object.assign({}, XIconNames); + //#endregion + + // + readonly TabAligns = Object.assign({}, XTabAlign); + readonly TabsHeaderPositions = Object.assign({}, XTabsHeaderPosition); + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} + این مولفه جهت نمایش محتواهای مختلف در غالب برگه ها و دسته بندی آن ها بکار می رود + `; + + readonly contentEn = ` + # ${this.toolbarTitle} + this component used to group several contents and show them in tabs area. + `; + + // + readonly sample1 = + '```html' + + ` + + +
+ Tab 1 +
+
+ Lorem ipsum dolor sit amet + consectetur adipisicing elit. + Omnis similique reiciendis vero + vel. Et, in nemo. Sapiente + doloremque provident ex dolore, + expedita totam ipsam architecto + vero mollitia magni. Corporis, dolore. +
+
+ + +
+ Tab 2 +
+ +
+ Lorem ipsum dolor sit amet + consectetur adipisicing elit. + Omnis similique reiciendis vero + vel. Et, in nemo. Sapiente + doloremque provident ex dolore, + expedita totam ipsam architecto + vero mollitia magni. Corporis, dolore. +
+
+ + +
+ Tab 3 +
+ +
+ Lorem ipsum dolor sit amet + consectetur adipisicing elit. + Omnis similique reiciendis vero + vel. Et, in nemo. Sapiente + doloremque provident ex dolore, + expedita totam ipsam architecto + vero mollitia magni. Corporis, dolore. +
+
+ + +
+ Tab 4 +
+ +
+ Lorem ipsum dolor sit amet + consectetur adipisicing elit. + Omnis similique reiciendis vero + vel. Et, in nemo. Sapiente + doloremque provident ex dolore, + expedita totam ipsam architecto + vero mollitia magni. Corporis, dolore. +
+
+
+` + + '```'; + + // + //#region UI Providers ... + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\tree\tree.page.html + + + + + + + + + + + + + + + + + + + + + + + + +
{{ nodeId }}
+ + + +
+
Name: {{ data.name }}
+ +
Description: {{ data.description }}
+
+
+
+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\tree\tree.page.scss + +:host(::ng-deep.mat-tree-node) { + align-items: baseline !important; +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\components\tree\tree.page.ts + +import { Subject } from 'rxjs'; +import { + XTreeNode, + XIconNames, + XButtonType, + XTreeAction, + XTreeNodeToggle, + XTreeActionModel, +} from 'x-framework-components'; +import { Component } from '@angular/core'; +import { XResourceIDs } from 'x-framework-core'; +import { VPageComponent } from 'src/app/views/v-page/v-page.component'; + +interface ProgrammingTech { + name: string; + description: string; +} + +@Component({ + selector: 'app-tree', + templateUrl: './tree.page.html', + styleUrls: ['./tree.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class TreePage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = this.AppResourceIDs.tree; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider(this.AppResourceIDs.tree_description); + toolbarShowSubTitle = true; + + // + treeActionProvider = new Subject(); + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + this.AppResourceIDs + ) as any; + + // + readonly IconNames = Object.assign({}, XIconNames); + readonly ButtonTypes = Object.assign({}, XButtonType); + //#endregion + + // + XProgrammingTech: ProgrammingTech; + treeNodes = this.getTreeNodes(); + + // + // TODO: Complete this ... + handleTreeNodeToggled(event: XTreeNodeToggle) { + console.log('handleTreeNodeToggled: ', event); + } + + handleTreeExpand(id: number) { + console.log('handleTreeExpand: ', id); + + // + this.treeActionProvider.next({ + action: XTreeAction.Expand, + payload: id, + }); + } + + handleTreeCollapse(id: number) { + console.log('handleTreeCollapse: ', id); + + // + this.treeActionProvider.next({ + action: XTreeAction.Collapse, + payload: id, + }); + } + + handleTreeExpandAll() { + console.log('handleTreeExpandAll'); + + // + this.treeActionProvider.next({ + action: XTreeAction.ExpandAll, + }); + } + + handleTreeCollapseAll() { + console.log('handleTreeCollapseAll'); + + // + this.treeActionProvider.next({ + action: XTreeAction.CollapseAll, + }); + } + + // + //#region XTree ... + private getTreeNodes() { + return [ + // + // Frontend ... + { + id: '1', + tooltip: 'Frontend Development Technologies', + data: { + name: 'Frontend', + description: 'Frontend Development Technologies', + }, + childs: [ + // + // Angular ... + { + id: '1/1', + tooltip: 'Angular Framework', + data: { + name: 'Angular', + description: 'Angular Framework', + }, + childs: [ + // + // Components ... + { + id: '1/1/1', + data: { + name: 'Components', + description: + "Components is an important part of Angular's building blocks, since it is on top of Component based architecture", + }, + childs: [], + } as XTreeNode, + ], + } as XTreeNode, + // + // React ... + { + id: '1/2', + data: { + name: 'React', + description: + 'a Collection of Web tools to create Web client apps', + }, + childs: [], + } as XTreeNode, + // + // Vue ... + { + id: '1/3', + data: { + name: 'Vue', + description: 'a Frontend Development Frameworks', + }, + childs: [], + } as XTreeNode, + ], + } as XTreeNode, + // + // Desktop ... + { + id: '2', + data: { + name: 'Desktop', + description: 'Desktop Development Technologies', + }, + childs: [ + // + // .Net ... + { + id: '2/1', + data: { + name: '.Net', + description: 'a collction of languages for creating Desktop Apps', + }, + childs: [], + } as XTreeNode, + // + // Electron ... + { + id: '2/2', + data: { + name: 'Electron', + description: + 'a collection of tools to Convert a Web app into Desktop Apps', + }, + childs: [], + } as XTreeNode, + ], + } as XTreeNode, + // + // Mobile ... + { + id: '3', + data: { + name: 'Mobile', + description: 'Mobile Development Technologies', + }, + childs: [ + // + // J2ME ... + { + id: '3/1', + data: { + name: 'J2ME', + description: + 'Java Mobile edition, using JAVA language to creating java based Mobile Applications', + }, + childs: [], + } as XTreeNode, + // + // CBuilder ... + { + id: '3/2', + data: { + name: 'CBuilder', + description: + 'using c++ to creating Symbian based Mobile Applications', + }, + childs: [], + } as XTreeNode, + // + // Android SDK ... + { + id: '3/3', + data: { + name: 'Android', + description: + 'using Android SDK to create Android OS based Mobile Applications by help of Java/Kotlin', + }, + childs: [], + } as XTreeNode, + // + // Swift ... + { + id: '3/4', + data: { + name: 'Swift', + description: 'create iOs based Mobile Applications', + }, + childs: [], + } as XTreeNode, + ], + } as XTreeNode, + // + // Backend ... + { + id: '4', + data: { + name: 'Backend', + description: 'Backend Development Technologies', + }, + childs: [ + // + // Php ... + { + id: '4/1', + data: { + name: 'Php', + description: 'one of most popular backend development languages', + }, + childs: [], + } as XTreeNode, + // + // Python ... + { + id: '4/2', + data: { + name: 'Python', + description: 'a multi usage language to develop applications', + }, + childs: [], + } as XTreeNode, + // + // NodeJS ... + { + id: '4/3', + data: { + name: 'Node JS', + description: + 'a chrome engin based tool to create multi purpose applications', + }, + childs: [], + } as XTreeNode, + // + // .Net Core ... + { + id: '4/4', + data: { + name: '.net core', + description: + 'a lightweigt, fast and cross platform version of .Net which provided by Microsoft to Develop Applications', + }, + childs: [], + } as XTreeNode, + ], + } as XTreeNode, + ]; + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\core\core.module.ts + +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { CommonModule } from '@angular/common'; +import { ViewsModule } from '../../views/views.module'; +import { IntroductionPage } from './introduction/introduction.page'; +import { BaseRoutes, ServicesRoutes } from 'src/app/config/page.config'; + +@NgModule({ + imports: [ + ViewsModule, + CommonModule, + RouterModule.forChild([ + { + path: BaseRoutes.Default, + redirectTo: ServicesRoutes.Introduction, + pathMatch: 'full', + }, + { + path: ServicesRoutes.Introduction, + component: IntroductionPage, + }, + ]), + ], + declarations: [IntroductionPage], +}) +export class CorePageModule {} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\core\introduction\introduction.page.html + + + + + + + + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\core\introduction\introduction.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\core\introduction\introduction.page.ts + +import { + XResourceIDs, + isNullOrEmptyString, + XColorWithBrightness, +} from 'x-framework-core'; +import { Component } from '@angular/core'; +import { VPageComponent } from 'src/app/views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +@Component({ + selector: 'app-components-introduction', + templateUrl: './introduction.page.html', + styleUrls: ['./introduction.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class IntroductionPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.core_introduction; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider( + AppResourceIDs.core_introduction_description + ); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + // Color Names ... + readonly ColorNames = Object.assign({}, XColorWithBrightness); + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} +این واحد شامل کلیه نیازمندی های پایه چارچوب است. + +برای آشنایی با اجزاء این واحد‌‌ مستندات آن را مورد مطالعه قرار دهید. + `; + + readonly contentEn = ` + # ${this.toolbarTitle} +this module contains all required dependencies for xFramework. + +for complete descriptions please read this module documentaions. + `; + //#endregion + + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\home\home.module.ts + +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { CommonModule } from '@angular/common'; +import { ViewsModule } from '../../views/views.module'; +import { BaseRoutes, HomeRoutes } from 'src/app/config/page.config'; +import { IntroductionPage } from './introduction/introduction.page'; + +@NgModule({ + imports: [ + ViewsModule, + CommonModule, + RouterModule.forChild([ + { + path: BaseRoutes.Default, + redirectTo: HomeRoutes.Introduction, + pathMatch: 'full', + }, + { + path: HomeRoutes.Introduction, + component: IntroductionPage, + }, + ]), + ], + declarations: [IntroductionPage], +}) +export class HomePageModule {} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\home\introduction\introduction.page.html + + + + + + + + + + + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\home\introduction\introduction.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\home\introduction\introduction.page.ts + +import { Component } from '@angular/core'; +import { VPageComponent } from 'src/app/views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; +import { isNullOrEmptyString, XColorWithBrightness } from 'x-framework-core'; + +@Component({ + selector: 'app-components-introduction', + templateUrl: './introduction.page.html', + styleUrls: ['./introduction.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class IntroductionPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.home_introduction; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider( + AppResourceIDs.home_introduction_description + ); + toolbarShowSubTitle = true; + //#endregion + + // + // Color Names ... + readonly ColorNames = Object.assign({}, XColorWithBrightness); + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} +یک چارچوب طراحی و توسعه ظاهر برنامه های کاربردی است که با بهره گیری از توانمندی های انگولار/آیونیک و کوردوا ایجاد شده است تا فرآیند طراحی و توسعه ظاهر برنامه های کاربردی را سهل تر کند. + +این چارچوب بر سه واحد مستقل استوار است که عبارتند از: + +- **واحد هسته**: +شامل اصلی ترین نیازمندی های پایه است. + +- **واحد خدمات**: +شامل مجموعه ای از خدمات پرکاربرد است که چارچوب از آن ها برای فراهم کردن امکانات مختلف استفاده می کند. + +- **واحد مولفه ها**: +مجموعه ای مولفه های بصری که بر پایه استاندارد متریال دیزاین طراحی و توسعه یافته اند و امکانات مختلفی را در اختیار کاربر قرار می دهند. + `; + + readonly contentEn = ` + # ${this.toolbarTitle} + + a Frontend framework which combined and implement several building blocks to provide an easy to use interface for creating Web/Mobile/Desktop Apps by help of Angular/Ionic and Cordova Powers. + + XFramework built on top of 3 main and unique modules: + + - **Core Module**: + conatins core features and dependencies. + + - **Services Module**: + contains some sort of usefull services which this framework used them to provide different options. + + - **Components Module**: + contains a collection of UI Components which designed and develop based on Material Design and provide different options. + `; + //#endregion + + // + async afterViewInit() { + await super.afterViewInit(); + + // + // console.log('sortOrder: ', this.sortOrederModelKeys); + } + + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\services\services.module.ts + +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { CommonModule } from '@angular/common'; +import { ViewsModule } from '../../views/views.module'; +import { IntroductionPage } from './introduction/introduction.page'; +import { BaseRoutes, ServicesRoutes } from 'src/app/config/page.config'; +import { UserDevicePage } from './user-device/user-device.page'; + +@NgModule({ + imports: [ + ViewsModule, + CommonModule, + RouterModule.forChild([ + { + path: BaseRoutes.Default, + redirectTo: ServicesRoutes.Introduction, + pathMatch: 'full', + }, + { + path: ServicesRoutes.Introduction, + component: IntroductionPage, + }, + { + path: ServicesRoutes.UserDevice, + component: UserDevicePage, + }, + ]), + ], + declarations: [UserDevicePage, IntroductionPage], +}) +export class ServicesPageModule {} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\services\introduction\introduction.page.html + + + + + + + + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\services\introduction\introduction.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\services\introduction\introduction.page.ts + +import { + XResourceIDs, + isNullOrEmptyString, + XColorWithBrightness, +} from 'x-framework-core'; +import { Component } from '@angular/core'; +import { VPageComponent } from 'src/app/views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +@Component({ + selector: 'app-components-introduction', + templateUrl: './introduction.page.html', + styleUrls: ['./introduction.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class IntroductionPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.services_introduction; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider( + AppResourceIDs.services_introduction_description + ); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + // Color Names ... + readonly ColorNames = Object.assign({}, XColorWithBrightness); + //#endregion + + // + // Content ... + readonly contentFa = ` + # ${this.toolbarTitle} + +این واحد شامل کلیه خدمات قابل استفاده در چارچوب است که هر یک امکانات خاصی را در اختیار قرار می دهد، که اهم آن ها عبارتند از: + +- **XDialogService**: +قابلیت نمایش و استفاده از کادر های مختلف محاوره ای را فراهم می کند. + +- **XGeolocationService**: +قابلیت مکان یابی را فراهم می کند. + +- **XHistoryService**: +قابلیت مدیریت و دستکاری تاریخچه مرور صفحات در مرورگر را فراهم می کند. + +- **XLocalizationService**: +قابلیت استفاده از منابع در ازای محتوای مستقیم را جهت پشتیبانی از زبان های مختلف را فراهم می کند. + +- **XNotificationService**: +قابلیت نمایش اعلان های مختلف را فراهم می کند. + +- **XStorageService**: +قابلیت استفاده از حافطه را جهت ذخیره و بازیابی موارد مختلف فراهم می کند. + +- **XSecureStorageService**: +قابلیت استفاده از حافظه بصورت رمزنگاری شده را فراهم می کند. + +- **XSecurityService**: +قابلیت رمزنگاری و رمزگشایی اطلاعات و تولید امضاهای دیجیتال را فراهم می کند. + +- **XSettingsService**: +قابلیت مدیریت تنظیمات چارچوب را فراهم می کند. + +- **XTaskManagerService**: +قابلیت اجرای وظایف و اولویت بندی آن ها را فراهم می کند. + +- **XManagerService**: +جهت سهولت دستری به خدمات موجود این خدمت بعنوان در برگیرنده کلیه خدمات لازم در چارچوب، مورد بهره برداری قرار می گیرد. + +برای آشنایی بیشتر لطفا به مستندات مراجعه فرمایید. + `; + + readonly contentEn = ` + # ${this.toolbarTitle} + +this module contains all useable services inside the framework which each one provide some capabilities, such as: + +- **XDialogService**: +provide the ability of use several types of Dialogs. + +- **XGeolocationService**: +provide Geo Location abilities. + +- **XHistoryService**: +provide the ability of managing and manipulating Browser History. + +- **XLocalizationService**: +provide ability of Localization and Internationalization. + +- **XNotificationService**: +provide ability of using several Notifications. + +- **XStorageService**: +provide ability of Storage used to Save and Retrieve data. + +- **XSecureStorageService**: +provide a Secure Storage to Save and Retrieve data privately. + +- **XSecurityService**: +provide ability of Encrypt/Decrypt data and also Generate Checksub Digest of data. + +- **XSettingsService**: +provide ability of Managing xFramework Settings. + +- **XTaskManagerService**: +provide ability to Queue Tasks and run them based on their priority. + +- **XManagerService**: +for better access all services, this Service acts as a Container of all required important above services. + +for more information, please read documentation. + `; + //#endregion + + // + // Provide content based on current locale ... + getContent(title: string) { + // + if (isNullOrEmptyString(title)) { + return ''; + } + + // + const currentLocale = this.managerService.currentLocale; + + // + const varName = + title + + (currentLocale === 'en-US' + ? 'En' + : currentLocale === 'fa-IR' + ? 'Fa' + : ''); + + const result = this[`${varName}`]; + + // + return result || ''; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\services\user-device\user-device.page.html + + + + +
{{ device | json }}
+
+
+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\services\user-device\user-device.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\services\user-device\user-device.page.ts + +import { Component } from '@angular/core'; +import { XResourceIDs, XColorWithBrightness } from 'x-framework-core'; +import { VPageComponent } from 'src/app/views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +@Component({ + selector: 'app-services-user-device', + templateUrl: './user-device.page.html', + styleUrls: ['./user-device.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class UserDevicePage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.user_device; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider( + AppResourceIDs.user_device_description + ); + toolbarShowSubTitle = true; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + // Color Names ... + readonly ColorNames = Object.assign({}, XColorWithBrightness); + //#endregion + + // + device = this.managerService.settingsService.device; +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\tabs\tabs.module.ts + +import { TabsPage } from './tabs.page'; +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { CommonModule } from '@angular/common'; +import { ViewsModule } from '../../views/views.module'; +import { TabPage1Page } from './tab-page-1/tab-page-1.page'; +import { TabPage2Page } from './tab-page-2/tab-page-2.page'; +import { TabPage3Page } from './tab-page-3/tab-page-3.page'; +import { Pages, BaseRoutes } from 'src/app/config/page.config'; + +@NgModule({ + imports: [ + ViewsModule, + CommonModule, + RouterModule.forChild([ + { + path: BaseRoutes.Default, + component: TabsPage, + children: [ + { + path: BaseRoutes.Default, + redirectTo: Pages.TabPage1.baseRoute, + pathMatch: 'full', + }, + { + path: Pages.TabPage1.baseRoute, + component: TabPage1Page, + }, + { + path: Pages.TabPage2.baseRoute, + component: TabPage2Page, + }, + { + path: Pages.TabPage3.baseRoute, + component: TabPage3Page, + }, + ], + }, + ]), + ], + declarations: [TabsPage, TabPage1Page, TabPage2Page, TabPage3Page], +}) +export class TabsPageModule {} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\tabs\tabs.page.ts + +import { Component } from '@angular/core'; +import { XColorWithBrightness } from 'x-framework-core'; +import { TabNavsItems } from 'src/app/config/page.config'; +import { VPageComponent } from 'src/app/views/v-page/v-page.component'; + +@Component({ + selector: 'app-components-tabs', + template: ` + + `, + styles: [``], +}) +// tslint:disable-next-line:component-class-suffix +export class TabsPage extends VPageComponent { + // + //#region Props ... + tabItems = TabNavsItems; + + readonly ColorNames = Object.assign({}, XColorWithBrightness); + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\tabs\tab-page-1\tab-page-1.page.html + +

Tab Page 1

+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\tabs\tab-page-1\tab-page-1.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\tabs\tab-page-1\tab-page-1.page.ts + +import { Component } from '@angular/core'; +import { VPageComponent } from 'src/app/views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; +import { XResourceIDs, XColorWithBrightness } from 'x-framework-core'; + +@Component({ + selector: 'app-tab-page-1', + templateUrl: './tab-page-1.page.html', + styleUrls: ['./tab-page-1.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class TabPage1Page extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.tab_page_1; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + // Color Names ... + readonly ColorNames = Object.assign({}, XColorWithBrightness); + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\tabs\tab-page-2\tab-page-2.page.html + +

Tab Page 2

+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\tabs\tab-page-2\tab-page-2.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\tabs\tab-page-2\tab-page-2.page.ts + +import { Component } from '@angular/core'; +import { VPageComponent } from 'src/app/views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; +import { XResourceIDs, XColorWithBrightness } from 'x-framework-core'; + +@Component({ + selector: 'app-tab-page-2', + templateUrl: './tab-page-2.page.html', + styleUrls: ['./tab-page-2.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class TabPage2Page extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.tab_page_2; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + // Color Names ... + readonly ColorNames = Object.assign({}, XColorWithBrightness); + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\tabs\tab-page-3\tab-page-3.page.html + +

Tab Page 3

+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\tabs\tab-page-3\tab-page-3.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\tabs\tab-page-3\tab-page-3.page.ts + +import { Component } from '@angular/core'; +import { VPageComponent } from 'src/app/views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; +import { XResourceIDs, XColorWithBrightness } from 'x-framework-core'; + +@Component({ + selector: 'app-tab-page-3', + templateUrl: './tab-page-3.page.html', + styleUrls: ['./tab-page-3.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class TabPage3Page extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.tab_page_3; + //#endregion + + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + + // + // Color Names ... + readonly ColorNames = Object.assign({}, XColorWithBrightness); + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\themes\themes.module.ts + +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { CommonModule } from '@angular/common'; +import { ViewsModule } from '../../views/views.module'; +import { XCanDeactivateGuard } from 'x-framework-core'; +import { BaseRoutes, ThemesRoutes } from 'src/app/config/page.config'; +import { ThemeManagerPage } from './theme-manager/theme-manager.page'; + +@NgModule({ + imports: [ + ViewsModule, + CommonModule, + RouterModule.forChild([ + { + path: BaseRoutes.Default, + redirectTo: ThemesRoutes.ThemeManager, + pathMatch: 'full', + }, + { + path: ThemesRoutes.ThemeManager, + component: ThemeManagerPage, + canDeactivate: [XCanDeactivateGuard] + }, + ]), + ], + declarations: [ThemeManagerPage], +}) +export class ThemesPageModule {} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\themes\theme-manager\theme-manager.page.html + + + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\themes\theme-manager\theme-manager.page.scss + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\pages\themes\theme-manager\theme-manager.page.ts + +import { + XThemePack, + XThemeType, + XStandardType, + XColorWithBrightness, +} from 'x-framework-core'; +import { Subject } from 'rxjs'; +import { + XTabsActionModel, + getEmptyThemePack, + XThemeManagerTabs, + XThemeManagerAction, + XFormStatusIdentifier, + XThemeManagerActionModel, +} from 'x-framework-components'; +import { Component } from '@angular/core'; +import { VPageComponent } from 'src/app/views/v-page/v-page.component'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +@Component({ + selector: 'app-theme-manager', + templateUrl: './theme-manager.page.html', + styleUrls: ['./theme-manager.page.scss'], +}) +// tslint:disable-next-line:component-class-suffix +export class ThemeManagerPage extends VPageComponent { + // + //#region Props ... + // + //#region Page Props ... + titleRes = AppResourceIDs.theme_manager; + toolbarTitle = this.resourceProvider(this.titleRes); + toolbarSubTitle = this.resourceProvider( + AppResourceIDs.theme_manager_description + ); + toolbarShowSubTitle = true; + + // + toolbarBackHandler: () => XStandardType = async () => { + await this.backHandler(); + }; + //#endregion + + // + // Color Names ... + readonly ColorNames = Object.assign({}, XColorWithBrightness); + + // + // Tabs ... + currentTab: number = XThemeManagerTabs.List; + tabsActionProvider = new Subject(); + + // + // Theme Types ... + readonly ThemeTypes = Object.assign({}, XThemeType); + + // + themes: XThemePack[]; + themeNames: string[]; + selectedTheme: XThemePack; + + // + formProvidedTheme: XThemePack; + formStatus: XFormStatusIdentifier; + emptyThemePack = getEmptyThemePack(); + + // + // Theme Maker Action Provider ... + themeManagerActionProvider = new Subject(); + //#endregion + + // + //#region LifeCycles ... + async onInit() { + super.onInit(); + } + + async onBack() { + super.onBack(); + + // + if (this.currentTab !== XThemeManagerTabs.List) { + await this.backHandler(); + } + } + //#endregion + + // + //#region UI Providers ... + //#endregion + + // + //#region UI Handlers ... + async handleTabChange(event: XStandardType) { + // + const eventTab = (await this.getValueAsync(event)) || 0; + + // + switch (eventTab) { + // + case XThemeManagerTabs.List: + // + this.preventBack = false; + this.toolbarHasBack = false; + this.toolbarShowBack = false; + break; + + // + case XThemeManagerTabs.Edit: + case XThemeManagerTabs.Import: + case XThemeManagerTabs.Details: + // + this.preventBack = true; + this.toolbarHasBack = true; + this.toolbarShowBack = true; + break; + } + + // + this.currentTab = eventTab; + await this.prepareActions(this.currentTab); + + // + this.detectChanges(); + } + + async handleThemeChange(event: boolean) { + // + if ((!event && !this.isLocked) || (event && this.isLocked)) { + return; + } + // + this.lock = event; + this.detectChanges(); + } + //#endregion + + // + //#region Actions ... + //#endregion + + // + //#region Private ... + private async prepareActions(tab = XThemeManagerTabs.List) { + // + this.actions = null; + this.detectChanges(); + } + + private async backHandler() { + // + if (this.currentTab === XThemeManagerTabs.List) { + // + this.lock = false; + this.managerService.historyService.back(); + } else { + // + if (this.isLocked) { + await this.presentDeactiveGuardDialog(() => { + this.issueThemeManagerComponentBackAction(); + }); + } else { + this.issueThemeManagerComponentBackAction(); + } + } + } + + private issueThemeManagerComponentBackAction() { + // + this.themeManagerActionProvider.next({ + action: XThemeManagerAction.Back, + }); + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\public-api.ts + +// +// Tools ... +export * from './tools/task.tools'; +export * from './tools/v-base.tools'; +export * from './tools/v-data-pager.tools'; + +// +// Typings ... +export * from './typings/v-base-form.typings'; + +// +// Classes ... +export * from './classes/v-base.component'; +export * from './classes/v-base-tabs.page'; +export * from './classes/v-base-form-view'; +export * from './classes/v-base-data-pager.page'; +export * from './classes/v-base-data-scroller.page'; + +// +// VMaintainer ... +export * from './v-maintainer/v-maintainer.component'; + +// +// VPage ... +export * from './v-page/v-page.component'; + +// +// Module ... +export * from './views.module'; + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\views.module.ts + +import { + XFrameworkCoreModule, + XDirectivesModule as XFrameworkCoreDirectivesModule, +} from 'x-framework-core'; +import { + XManagerService, + XHttpInterceptorService, + XFrameworkServicesModule, + XDirectivesModule as XFrameworkServicesDirectivesModule, +} from 'x-framework-services'; +import { + XFabModule, + XMapModule, + XTabsModule, + XPageModule, + XCardModule, + XIconModule, + XListModule, + XGridModule, + XFormModule, + XTreeModule, + XTableModule, + XThemeModule, + XEmptyModule, + XColorModule, + XButtonModule, + XSliderModule, + XSpinnerModule, + XSlotterModule, + XCounterModule, + XStepperModule, + XMarkdownModule, + XSearchBarModule, + XThumbnailModule, + XActionBarModule, + XDataPagerModule, + XFileUploadModule, + XColorPickerModule, + XTabNavigatorModule, + XFileDropAreaModule, + XAvatarUploadModule, + XImageCropperModule, + XDataScrollerModule, + XBaseComponentModule, + XExpandableListModule, + XModelPresenterModule, + XExpandablePanelModule, + XDescriptionListModule, + XFrameworkComponentsModule, + XDialogsModule, +} from 'x-framework-components'; +import { NgModule } from '@angular/core'; +import { IonicModule } from '@ionic/angular'; +import { CommonModule } from '@angular/common'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { VPageComponent } from './v-page/v-page.component'; +import { VMaintainerComponent } from './v-maintainer/v-maintainer.component'; + +@NgModule({ + declarations: [VPageComponent, VMaintainerComponent], + providers: [ + XManagerService, + // + // Register Http Interceptor ... + { + provide: HTTP_INTERCEPTORS, + useClass: XHttpInterceptorService, + multi: true, + }, + ], + imports: [ + // + IonicModule, + CommonModule, + + // + XFrameworkCoreModule, + XFrameworkCoreDirectivesModule, + + // + XFrameworkServicesModule, + XFrameworkServicesDirectivesModule, + + // + XEmptyModule, + XBaseComponentModule, + XFrameworkComponentsModule, + + // + XFabModule, + XMapModule, + XPageModule, + XCardModule, + XIconModule, + XFormModule, + XTabsModule, + XListModule, + XGridModule, + XTreeModule, + XColorModule, + XTableModule, + XThemeModule, + XButtonModule, + XSliderModule, + XSpinnerModule, + XStepperModule, + XSlotterModule, + XCounterModule, + XMarkdownModule, + XDataPagerModule, + XSearchBarModule, + XThumbnailModule, + XActionBarModule, + XFileUploadModule, + XColorPickerModule, + XImageCropperModule, + XFileDropAreaModule, + XDataScrollerModule, + XTabNavigatorModule, + XAvatarUploadModule, + XExpandableListModule, + XModelPresenterModule, + XExpandablePanelModule, + XDescriptionListModule, + ], + exports: [ + // + IonicModule, + CommonModule, + + // + XFrameworkCoreModule, + XFrameworkCoreDirectivesModule, + + // + XFrameworkServicesModule, + XFrameworkServicesDirectivesModule, + + // + XEmptyModule, + XDialogsModule, + XBaseComponentModule, + + // + XFabModule, + XMapModule, + XPageModule, + XCardModule, + XIconModule, + XFormModule, + XTabsModule, + XListModule, + XGridModule, + XTreeModule, + XColorModule, + XTableModule, + XThemeModule, + XButtonModule, + XSliderModule, + XSpinnerModule, + XStepperModule, + XSlotterModule, + XCounterModule, + XMarkdownModule, + XDataPagerModule, + XSearchBarModule, + XThumbnailModule, + XActionBarModule, + XFileUploadModule, + XColorPickerModule, + XImageCropperModule, + XFileDropAreaModule, + XDataScrollerModule, + XTabNavigatorModule, + XAvatarUploadModule, + XExpandableListModule, + XModelPresenterModule, + XExpandablePanelModule, + XDescriptionListModule, + + // + VPageComponent, + VMaintainerComponent, + ], +}) +export class ViewsModule {} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\classes\v-base-data-pager.page.ts + +import { + isSameObject, + XModelKeyProvider, + XModelValueProvider, + isNullOrEmptyString, + XBaseDataPagerQuery, + XPartialModelFieldNames, + XBaseDataPagerQueryResult, +} from 'x-framework-core'; +import { + XListItem, + XListItemSlideOption, + XDataPagerPresentType, + XDataPagerPaginatorPosition, +} from 'x-framework-components'; +import { map } from 'rxjs/operators'; +import { + VBaseFormViewAction, + VBaseFormViewActionModel, + VBaseFormViewPresentType, +} from '../typings/v-base-form.typings'; +import { Component } from '@angular/core'; +import { Observable, Subject } from 'rxjs'; +import { getTaskName } from '../tools/task.tools'; +import { VBaseTabsPage } from './v-base-tabs.page'; +import { XQueryDto, XQueryResultDto } from '../models/x-query.dto'; + +@Component({ + template: '', +}) +// tslint:disable-next-line: component-class-suffix +export abstract class VBaseDataPagerPage extends VBaseTabsPage { + // + //#region Props ... + // + // XQueryDto ... + filter: string; + canSearch: boolean; + isAscending = true; + enableTracking = false; + containsDetail = false; + sortOrder: keyof Partial; + + // + readonly DataPagerPaginatorPositions = Object.assign( + {}, + XDataPagerPaginatorPosition + ); + readonly ModelPresentTypes = Object.assign({}, VBaseFormViewPresentType); + readonly DataPagerPresentTypes = Object.assign({}, XDataPagerPresentType); + + // + showDataPagerPaginator: boolean; + dataPagerQuery: XBaseDataPagerQuery = { + filter: '', + pageNumber: 1, + rowsInPage: this.config.defaultPageSize, + }; + dataPagerResultListItems: XListItem[]; + dataPagerResult: XBaseDataPagerQueryResult; + + // + listTabIndex: number; + + // + selectedModel: T; + canAddOrUpdate: boolean; + hiddenModelKeys: XPartialModelFieldNames; + availableModelKeys: XPartialModelFieldNames; + sortOrderModelKeys: XPartialModelFieldNames; + hiddenLabelModelKeys: XPartialModelFieldNames; + modelActionProvider = new Subject(); + + // + //#region Default Slide Options ... + // + // Details ... + readonly defaultDetailsSlideOption = { + id: 'Details', + icon: this.IconNames.visible, + title: this.ResourceIDs.details, + color: this.ColorNames.Secondary, + onlyIcon: true, + slot: 'start', + disabled: this.isLoading || this.uiDisabled, + } as XListItemSlideOption; + + // + // Edit ... + readonly defaultEditSlideOption = { + id: 'Edit', + icon: this.IconNames.edit, + title: this.ResourceIDs.edit, + color: this.ColorNames.Tertiary, + onlyIcon: true, + slot: 'start', + disabled: this.isLoading || this.uiDisabled, + } as XListItemSlideOption; + + // + // Download ... + readonly defaultDownloadSlideOption = { + id: 'Download', + icon: this.IconNames.download, + color: this.ColorNames.Success, + title: this.ResourceIDs.download, + onlyIcon: true, + slot: 'start', + disabled: this.isLoading || this.uiDisabled, + } as XListItemSlideOption; + + // + // Remove ... + readonly defaultRemoveSlideOption = { + id: 'Remove', + icon: this.IconNames.remove, + title: this.ResourceIDs.delete, + color: this.ColorNames.Danger, + onlyIcon: true, + slot: 'end', + disabled: this.isLoading || this.uiDisabled, + } as XListItemSlideOption; + //#endregion + //#endregion + + // + //#region Abstract ... + abstract modelKeyProvider: XModelKeyProvider; + + abstract modelValueProvider: XModelValueProvider; + + abstract getAddOrUpdateAvailableModelKeys(): XPartialModelFieldNames; + + abstract provideSlideOptions(item: T): Promise; + + abstract isSameAsDefault(item: T): boolean; + + abstract getTaskNames(): string[]; + + abstract queryFetcherTask(query: XQueryDto): Observable>; + + abstract issueDataPagerTask( + task: Observable> + ): Promise; + + abstract issueAddModelTask(model: T): Promise; + + abstract issueUpdateModelTask(model: T): Promise; + + abstract issueRemoveModelTask(model: T): Promise; + + abstract handlePresentDetails(model: T): Promise; + + abstract handlePresentAddForm(): Promise; + + abstract handlePresentEditForm(model: T): Promise; + + abstract modelFieldPreparer(model: T): T; + //#endregion + + // + //#region Constructor ... + //#endregion + + // + //#region LifeCycles ... + //#endregion + + // + //#region Register Handlers ... + //#endregion + + // + //#region Task Handlers ... + //#endregion + + // + //#region UI Providers ... + queryFetcher = (q: XBaseDataPagerQuery) => { + // + const apiQuery = this.toXQuery(q); + return this.applyToDataPagerQueryResult(this.queryFetcherTask(apiQuery)); + // tslint:disable-next-line: semicolon + }; + + isSameAsSelectedModel(item: T) { + return isSameObject(item as any, this.selectedModel as any); + } + //#endregion + + // + //#region UI Handlers ... + public async handleDataPagerTaskReady( + task: Observable> + ) { + // + // XBaseDataPagerQueryResult ... + if (!task || this.uiDisabled || this.isLoading) { + return; + } + + // + await this.issueDataPagerTask(task); + } + + public async handleDataPagerTaskFinished( + result: XBaseDataPagerQueryResult + ) { + // + this.dataPagerResult = null; + this.dataPagerResultListItems = null; + this.detectChanges(); + + // + if (!result) { + return; + } + + // + this.dataPagerResult = { + ...result, + }; + + // + this.canSearch = + (this.hasChild(this.dataPagerResult.items) && + this.dataPagerResult.items.length > 1) || + !isNullOrEmptyString(this.dataPagerQuery.filter); + + // + this.showDataPagerPaginator = + this.hasChild(this.dataPagerResult.items) && + this.dataPagerResult.items.length > 1; + + // + this.dataPagerResultListItems = await this.prepareListItems(); + await this.changeTab(this.listTabIndex); + + // + this.detectChanges(); + } + + public async handleAddOrUpdateModelTaskFinished(model: T) { + // + if (!model) { + return; + } + + // + this.lock = false; + + // + this.handleResetDataPagerQuery(); + } + + public async handleAddOrUpdateFormModelChanged(model: T) { + // + if (!model || !this.selectedModel) { + return; + } + + // + const isSameAsDefault = this.isSameAsDefault(model); + const isSameAsSelectedModel = this.isSameAsSelectedModel(model); + + // + this.lock = !isSameAsDefault && !isSameAsSelectedModel; + this.canAddOrUpdate = !isSameAsSelectedModel; + + // + this.detectChanges(); + } + + public async handleSubmitAddOrUpdateForm(model: T) { + // + if (!model || this.isLoading || this.uiDisabled || !this.canAddOrUpdate) { + return; + } + + // + const isAdd = this.isSameAsDefault(this.selectedModel); + if (isAdd) { + await this.issueAddModelTask(model); + } else { + await this.issueUpdateModelTask(model); + } + } + + public async handleResetAddOrUpdateForm(model: T) { + // + if (!model) { + return; + } + + // + await this.managerService.dialogService.presentAreYouSureYesNoDialog(() => { + // + this.lock = false; + this.canAddOrUpdate = false; + + // + this.modelActionProvider.next({ + action: VBaseFormViewAction.ResetForm, + }); + }); + } + + public async handleCancelAddOrUpdateForm() { + // + if (this.canAddOrUpdate || this.isLocked) { + // + await this.managerService.dialogService.presentAreYouSureYesNoDialog( + async () => { + // + this.lock = false; + await this.toolbarBackHandler(); + } + ); + + // + return; + } + + // + this.lock = false; + await this.toolbarBackHandler(); + } + //#endregion + + // + //#region Actions ... + async prepareListItems() { + // + if (!this.dataPagerResult || !this.hasChild(this.dataPagerResult.items)) { + return; + } + + // + const result = await Promise.all( + this.dataPagerResult.items.map(async (i) => { + // + const slideOptions = await this.provideSlideOptions( + this.modelFieldPreparer(i) + ); + + // + return { + data: i, + slideOptions, + } as XListItem; + }) + ); + + // + return result; + } + + async handleResetDataPagerQuery() { + // + this.dataPagerQuery = { + filter: '', + pageNumber: 1, + rowsInPage: this.config.defaultPageSize, + }; + + // + this.selectedModel = null; + await this.changeTab(this.listTabIndex); + + // + this.detectChanges(); + } + + async handleSearchbarQuryChanged(query: string) { + // + this.dataPagerQuery = { + ...this.dataPagerQuery, + filter: query, + }; + + // + this.detectChanges(); + } + + async handleDataPagerQueryChanged(query: XBaseDataPagerQuery) { + // + this.dataPagerQuery = { + ...query, + }; + } + + async handlePerformRemove(model: T) { + // + if (!model) { + return; + } + + // + await this.managerService.dialogService.presentAreYouSureYesNoDialog( + async () => { + await this.issueRemoveModelTask(model); + } + ); + } + + isPageTask(key: string | any) { + // + const pageTasks = this.getTaskNames(); + const mTaskKey = getTaskName(key); + const result = pageTasks.includes(mTaskKey); + + // + return result; + } + //#endregion + + // + //#region Private ... + private toXQuery(q: XBaseDataPagerQuery) { + return { + filter: q.filter, + containsDetail: this.containsDetail, + enableTracking: this.enableTracking, + sortBy: this.sortOrder, + isAscending: this.isAscending, + page: q.pageNumber, + pageSize: q.rowsInPage, + } as XQueryDto; + } + + private applyToDataPagerQueryResult(result: Observable>) { + return result.pipe( + // tslint:disable-next-line: no-shadowed-variable + map((result) => { + return { + count: result.totalItems || 0, + items: result.items || [], + } as XBaseDataPagerQueryResult; + }) + ); + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\classes\v-base-data-scroller.page.ts + +import { + XTask, + toBoolean, + isNullOrUndefined, + XModelKeyProvider, + XModelValueProvider, + isNullOrEmptyString, + XBaseDataPagerQuery, + XBaseDataPagerQueryResult, +} from 'x-framework-core'; +import { + XPageLoadingEvent, + XDataScrollerAction, + XDataScrollerActionModel, +} from 'x-framework-components'; +import { map } from 'rxjs/operators'; +import { Component } from '@angular/core'; +import { Observable, Subject } from 'rxjs'; +import { getTaskName } from '../tools/task.tools'; +import { VPageComponent } from '../v-page/v-page.component'; +import { XQueryDto, XQueryResultDto } from '../models/x-query.dto'; + +@Component({ + template: '', +}) +// tslint:disable-next-line: component-class-suffix +export abstract class VBaseDataScrollerPage extends VPageComponent { + // + //#region Props ... + private loaderEvent: any; + + // + canNext: boolean; + abstract showSearch: boolean; + + // + showPageRefresher = true; + showPageInfinityScroller = true; + + // + // XQueryDto ... + canSearch: boolean; + isAscending = true; + enableTracking = false; + containsDetail = false; + sortOrder: keyof Partial; + + // + dataScrollerQuery: XBaseDataPagerQuery = { + filter: '', + pageNumber: 1, + rowsInPage: this.config.defaultPageSize, + }; + dataScrollerResult: XBaseDataPagerQueryResult; + dataScrollerActionProvider = new Subject(); + //#endregion + + // + //#region Abstract ... + // + abstract DataScrollerTaskName: string; + abstract hiddenModelKeys: Array>; + abstract availableModelKeys: Array>; + abstract sortOrderModelKeys: Array>; + + // + abstract modelKeyProvider: XModelKeyProvider; + abstract modelValueProvider: XModelValueProvider; + + // + abstract getTaskNames(): string[]; + abstract queryFetcherTask(query: XQueryDto): Observable>; + //#endregion + + // + //#region Constructor ... + //#endregion + + // + //#region Register Handlers ... + registerViewHandlers() { + // + // Register on DataScroller Action provider ... + this.applyTakeUntilWrapper( + this.dataScrollerActionProvider.asObservable() + // tslint:disable-next-line: deprecation + ).subscribe((model) => { + // + if (!model) { + return; + } + + // + if (model.action === XDataScrollerAction.CanNext) { + // + this.canNext = toBoolean(model.payload); + this.detectChanges(); + } + }); + } + //#endregion + + // + //#region Task Handlers ... + async onTaskStart(name: string, task: XTask) { + super.onTaskStart(name, task); + console.log('onTaskStart: ', name, task); + + // + if (this.isPageTask(name)) { + await this.setLoadingState(true); + } + } + + async onTaskFinished(name: string, task: XTask) { + super.onTaskFinished(name, task); + console.log('onTaskFinished: ', name, task); + + // + if (this.isPageTask(name)) { + // + await this.setLoadingState(false); + + // + if (name !== this.DataScrollerTaskName) { + this.showSuccess(); + } + + // + // Load All Models ... + if (name === this.DataScrollerTaskName) { + await this.handleDataScrollerTaskFinished(task.result); + } + } + } + + async onTaskFailed(name: string, task: XTask) { + super.onTaskFailed(name, task); + console.log('onTaskFailed: ', name, task); + + // + if (this.isPageTask(name)) { + // + await this.setLoadingState(false); + this.showError(task.error); + } + } + //#endregion + + // + //#region UI Providers ... + queryFetcher = (q: XBaseDataPagerQuery) => { + // + const apiQuery = this.toXQuery(q); + return this.applyToDataScrollerQueryResult(this.queryFetcherTask(apiQuery)); + // tslint:disable-next-line: semicolon + }; + + trackByFn(index: number, item: T) { + return index; + } + //#endregion + + // + //#region UI Handlers ... + handleLoadMorePage(event: XPageLoadingEvent) { + // + if (!this.canNext) { + // + this.loaderEvent = null; + this.cancelLoading(event.event); + return; + } + + // + this.loaderEvent = event.event; + this.dataScrollerActionProvider.next({ + action: XDataScrollerAction.LoadMore, + }); + } + + handleResetDataScrollerQuery() { + // + this.dataScrollerQuery = { + filter: '', + pageNumber: 1, + rowsInPage: this.config.defaultPageSize, + }; + + // + this.dataScrollerActionProvider.next({ + action: XDataScrollerAction.ClearItems, + }); + + // + this.detectChanges(); + } + + handleRefreshPage(event: XPageLoadingEvent) { + // + this.loaderEvent = event.event; + this.handleResetDataScrollerQuery(); + } + + handleDataScrollerQueryChanged(query: XBaseDataPagerQuery) { + // + this.dataScrollerQuery = { + ...query, + }; + } + + handleSearchbarQuryChanged(query: string) { + // + this.dataScrollerQuery = { + ...this.dataScrollerQuery, + filter: query, + }; + + // + this.detectChanges(); + } + + async handleDataScrollerTaskReady( + task: Observable> + ) { + // + // XBaseDataPagerQueryResult ... + if (!task || this.uiDisabled || this.isLoading) { + return; + } + + // + await this.issueDataScrollerTask(task); + } + //#endregion + + // + //#region Actions ... + protected isPageTask(key: string | any) { + // + const pageTasks = this.getTaskNames(); + const mTaskKey = getTaskName(key); + const result = pageTasks.includes(mTaskKey); + + // + return result; + } + //#endregion + + // + //#region Private ... + private toXQuery(q: XBaseDataPagerQuery) { + return { + filter: q.filter, + containsDetail: this.containsDetail, + enableTracking: this.enableTracking, + sortBy: this.sortOrder, + isAscending: this.isAscending, + page: q.pageNumber, + pageSize: q.rowsInPage, + } as XQueryDto; + } + + private applyToDataScrollerQueryResult( + result: Observable> + ) { + return result.pipe( + // tslint:disable-next-line: no-shadowed-variable + map((result) => { + return { + count: result.totalItems || 0, + items: result.items || [], + } as XBaseDataPagerQueryResult; + }) + ); + } + + private async issueDataScrollerTask( + task: Observable> + ): Promise { + // + const mTaskName = this.DataScrollerTaskName; + const mTask = task; + + // + await this.addOrUpdateTask({ + priority: 0, + affectLoading: true, + name: mTaskName, + task: mTask, + }); + + // + await this.dispatchTask(mTaskName); + + // + if (!isNullOrUndefined(this.loaderEvent)) { + this.cancelLoading(this.loaderEvent); + } + } + + private async handleDataScrollerTaskFinished( + result: XBaseDataPagerQueryResult + ) { + // + this.dataScrollerResult = null; + this.detectChanges(); + + // + if (!result) { + return; + } + + // + this.dataScrollerResult = { + ...result, + }; + + // + this.canSearch = + (this.hasChild(this.dataScrollerResult.items) && + this.dataScrollerResult.items.length > 1) || + !isNullOrEmptyString(this.dataScrollerQuery.filter); + + // + this.detectChanges(); + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\classes\v-base-form-view.ts + +import { + nameof, + toArray, + isFunction, + isSameObject, + XStandardType, + XOneOrManyType, + XColorIdentifier, + isNullOrUndefined, + XModelKeyProvider, + XModelValueProvider, + XResourceIdentifier, + isNullOrEmptyString, + XColorWithBrightness, + XModelFieldTypeIndex, + XPartialModelFieldNames, +} from 'x-framework-core'; +import { + XFormConfig, + XFormStatus, + XFormActionModel, + XFormStatusIdentifier, + XFormValueChangeEventModel, + XFormStatusChangeEventModel, + XDescriptionListActionModel, +} from 'x-framework-components'; +import { + VBaseFormViewAction, + VBaseFormViewActionModel, + VBaseFormViewPresentType, + VBaseFormViewPresentTypeIdentifier, +} from '../typings/v-base-form.typings'; +import { Subject, Subscription } from 'rxjs'; +import { concatMap, map } from 'rxjs/operators'; +import { VBaseComponent } from './v-base.component'; +import { Input, Output, Component, EventEmitter } from '@angular/core'; + +@Component({ + template: '', +}) +export abstract class VBaseFormViewComponent extends VBaseComponent { + // + //#region Props ... + isValid: boolean; + validationError: string; + hasValidationError = false; + isSameAsDefaultModel: boolean; + status: XFormStatusIdentifier; + + // + @Input() + model: T; + baseModel: T; + defaultModel: T; + + // + @Input() + hideOptionalValues = false; + + @Input() + availableModelKeys: XPartialModelFieldNames; + + @Input() + sortOrderModelKeys: XPartialModelFieldNames; + + @Input() + hiddenModelKeys: XPartialModelFieldNames; + + @Input() + hiddenLabelModelKeys: XPartialModelFieldNames; + + @Input() + modelKeyProvider: XModelKeyProvider; + + @Input() + modelValueProvider: XModelValueProvider; + + // + readonly PresentTypes = Object.assign({}, VBaseFormViewPresentType); + + // + @Input() + presentType: VBaseFormViewPresentTypeIdentifier = + VBaseFormViewPresentType.AsForm; + + // + formConfig: XFormConfig; + + // + //#region Form Props ... + @Input() + formRegularColor: XStandardType = XColorWithBrightness.Dark; + + @Input() + formActiveColor: XStandardType = + XColorWithBrightness.Tertiary; + + @Input() + formInvalidColor: XStandardType = + XColorWithBrightness.Danger; + + @Input() + formPendingColor: XStandardType = + XColorWithBrightness.Warning; + + @Input() + formFillColor: XStandardType = + XColorWithBrightness.TertiaryShade; + + @Input() + formDisabledColor: XStandardType = + XColorWithBrightness.LightTint; + + @Input() + formDisabledFillColor: XStandardType = + XColorWithBrightness.LightShade; + //#endregion + + // + //#region Card Props ... + @Input() + isInPage: XStandardType = false; + + @Input() + wrapWithCard: XStandardType = false; + + @Input() + showCardFooter: XStandardType = false; + + @Input() + cardColor: XStandardType = XColorWithBrightness.Light; + + @Input() + cardForegroundColor: XStandardType = false; + + @Input() + cardSubTitle: XStandardType; + //#endregion + + // + //#region Description List Props ... + @Input() + cssClass: XOneOrManyType; + + @Input() + showLabels = true; + + @Input() + color: XColorIdentifier = XColorWithBrightness.Light; + + @Input() + foregroundColor = false; + + @Input() + labelColor: XColorIdentifier = XColorWithBrightness.TertiaryShade; + + @Input() + contentColor: XColorIdentifier = XColorWithBrightness.Dark; + //#endregion + + // + //#region ActionProvider ... + private ACTION_PROVIDER_SUBSCRIPTION: Subscription; + + @Input() + actionProvider: Subject; + + @Input() + formActionProvider = new Subject(); + + @Input() + descriptionListActionProvider = new Subject(); + //#endregion + + // + //#region Events ... + @Output() + modelChanged = new EventEmitter(); + + @Output() + statusChanged = new EventEmitter(); + //#endregion + //#endregion + + // + //#region Abstract ... + abstract title: string; + abstract FieldNames: XModelFieldTypeIndex; + abstract modelFieldPreparer: (model: T) => T; + abstract prepareFormConfig(): Promise; + abstract defaultHiddenModelKeys(): XPartialModelFieldNames; + abstract defaultSortOrderModelKeys(): XPartialModelFieldNames; + abstract defaultHiddenLabelModelKeys(): XPartialModelFieldNames; + abstract defaultModelKeyProvider(): XModelKeyProvider; + abstract defaultModelValueProvider(): XModelValueProvider; + //#endregion + + // + //#region Constructor ... + //#endregion + + // + //#region LifeCycles ... + async onInit() { + super.onInit(); + + // + await this.prepareModelProviders(); + + // + this.checkSameAsDefault(); + + // + if (!isNullOrUndefined(this.model)) { + // + this.baseModel = { + ...this.model, + }; + await this.configureForm(); + } + } + + async onChange(changeKeys: string[]) { + super.onChange(changeKeys); + + // + //#region Model Changed ... + const isModelChanged = changeKeys.includes( + nameof>('model') + ); + const isDefaultModelChanged = changeKeys.includes( + nameof>('defaultModel') + ); + + // + // Hnadle Model Changed ... + if (isModelChanged) { + // + this.baseModel = { + ...this.model, + }; + await this.handleModelChaned(); + } + + // + // Handle Default Model Check ... + if (isModelChanged || isDefaultModelChanged) { + this.checkSameAsDefault(); + } + //#endregion + + // + //#region Action Providers ... + const isActionProviderChanged = changeKeys.includes( + nameof>('actionProvider') + ); + if (isActionProviderChanged) { + this.registerActionProvider(); + } + + // + const isFormActionProviderChanged = changeKeys.includes( + nameof>('formActionProvider') + ); + if (isFormActionProviderChanged) { + if (!this.formActionProvider) { + this.formActionProvider = new Subject(); + } + } + + // + const isDescriptionListActionProviderChanged = changeKeys.includes( + nameof>('descriptionListActionProvider') + ); + if (isDescriptionListActionProviderChanged) { + if (!this.descriptionListActionProvider) { + this.descriptionListActionProvider = + new Subject(); + } + } + + // + if (isFormActionProviderChanged || isDescriptionListActionProviderChanged) { + this.detectChanges(); + } + //#endregion + + // + //#region Color Changed ... + const isColorChanged = changeKeys.includes( + nameof>('color') + ); + if (isColorChanged) { + // + this.getValueAsync(this.color).then((color) => { + this.color = !isNullOrEmptyString(color) + ? color + : XColorWithBrightness.Light; + }); + } + //#endregion + + // + //#region ShowLabels Changed ... + const isShowLabelsChanged = changeKeys.includes( + nameof>('showLabels') + ); + if (isShowLabelsChanged) { + if (isNullOrUndefined(this.showLabels)) { + this.showLabels = true; + } + } + //#endregion + + // + //#region CardColor Changed ... + const isCardColorChanged = changeKeys.includes( + nameof>('cardColor') + ); + if (isCardColorChanged) { + if (isNullOrUndefined(this.cardColor)) { + this.cardColor = this.ColorNames.Light; + } + } + //#endregion + + // + //#region SortOrderModelKeys Changed ... + const isSortOrderModelKeysChanged = changeKeys.includes( + nameof>('sortOrderModelKeys') + ); + if (isSortOrderModelKeysChanged) { + this.prepareSortOrderModelKeys(); + } + //#endregion + + // + //#region HiddenModelKeys Changed ... + const isHiddenModelKeysChanged = changeKeys.includes( + nameof>('hiddenModelKeys') + ); + if (isHiddenModelKeysChanged) { + this.prepareHiddenModelKeys(); + } + //#endregion + + // + //#region HiddenLabelModelKeys Changed ... + const isHiddenLabelModelKeysChanged = changeKeys.includes( + nameof>('hiddenLabelModelKeys') + ); + if (isHiddenLabelModelKeysChanged) { + this.prepareHiddenLabelModelKeys(); + } + //#endregion + + // + //#region ModelKeyProvider Changed ... + const isModelKeyProviderChanged = changeKeys.includes( + nameof>('modelKeyProvider') + ); + if (isModelKeyProviderChanged) { + this.prepareModelKeyProvider(); + } + //#endregion + + // + //#region ModelValueProvider Changed ... + const isModelValueProviderChanged = changeKeys.includes( + nameof>('modelValueProvider') + ); + if (isModelValueProviderChanged) { + this.prepareModelValueProvider(); + } + //#endregion + } + + onDestroy() { + super.onDestroy(); + + // + this.unregisterActionProvider(); + } + //#endregion + + // + //#region Register Handlers ... + registerInstanceHandlers() { + super.registerInstanceHandlers(); + + // + this.registerActionProvider(); + } + //#endregion + + // + //#region UI Handlers ... + handleFormChange(event: XFormValueChangeEventModel) { + // + if (isNullOrUndefined(event) || isNullOrUndefined(event.value)) { + return; + } + + // + const eventModel = + this.modelFieldPreparer && isFunction(this.modelFieldPreparer) + ? this.modelFieldPreparer(event.value) + : (event.value as T); + + // + this.model = { + ...eventModel, + }; + + // + this.checkSameAsDefault(); + this.detectChanges(); + + // + this.modelChanged.emit(this.model); + } + + handleFormStatusChanged(event: XFormStatusChangeEventModel) { + // + if (isNullOrUndefined(event) || isNullOrEmptyString(event.status)) { + return; + } + + // + this.status = event.status; + this.isValid = this.status === XFormStatus.Valid; + + // + this.detectChanges(); + this.statusChanged.emit(this.isValid); + } + //#endregion + + // + //#region UI Providers ... + public canShowKey(key: string) { + return this.getArrayValue(this.availableModelKeys).pipe( + concatMap((availableModelKeys) => + this.getArrayValue(this.hiddenModelKeys).pipe( + map((hiddenModelKeys) => { + return { + availableModelKeys, + hiddenModelKeys, + }; + }) + ) + ), + map((provider) => { + // + const hasChildHiddens = this.hasChild(provider.hiddenModelKeys); + const hasChildAvailables = this.hasChild(provider.availableModelKeys); + + // + if (!hasChildAvailables && !hasChildHiddens) { + return true; + } + + // + const isInHiddens = provider.hiddenModelKeys.includes(key as any); + const isInAvailables = provider.availableModelKeys.includes(key as any); + + // + if ( + (!hasChildAvailables && !isInHiddens) || + (isInAvailables && !hasChildHiddens) || + (isInAvailables && !isInHiddens) + ) { + return true; + } + + // + return false; + }) + ); + } + + public canShowLabelKey(key: string) { + return this.getArrayValue(this.hiddenLabelModelKeys).pipe( + map((hiddenLabelsModelKeys) => { + // + const hasChildHiddenLabels = this.hasChild(hiddenLabelsModelKeys); + + // + if (!hasChildHiddenLabels) { + return true; + } + + // + const isInHiddens = hiddenLabelsModelKeys.includes(key as any); + return hasChildHiddenLabels && !isInHiddens; + }) + ); + } + //#endregion + + // + //#region Actions ... + public setValidationError(errorMsg: string) { + // + if (isNullOrEmptyString(errorMsg)) { + return; + } + + // + this.hasValidationError = true; + this.validationError = errorMsg; + + // + this.detectChanges(); + } + + public clearValidationErrors() { + // + this.clearError(); + this.hasValidationError = false; + this.validationError = null; + + // + this.detectChanges(); + } + + public async clearForm() { + // + this.model = { + ...this.defaultModel, + }; + + // + this.checkSameAsDefault(); + await this.prepareFormConfig(); + + // + await this.onFormCleared(); + this.detectChanges(); + } + + public async resetForm() { + // + this.model = { + ...this.baseModel, + }; + + // + this.checkSameAsDefault(); + await this.prepareFormConfig(); + + // + await this.onFormResetted(); + this.detectChanges(); + } + + public async configureForm() { + // + await this.getValueAsync(this.prepareFormConfig()); + this.detectChanges(); + } + //#endregion + + // + //#region Protected Actions ... + protected registerActionProvider() { + // + this.unregisterActionProvider(); + + // + if (!this.actionProvider) { + this.actionProvider = new Subject(); + } + + // + this.ACTION_PROVIDER_SUBSCRIPTION = this.actionProvider + .asObservable() + // tslint:disable-next-line: deprecation + .subscribe(async (model) => { + // + if (!model) { + return; + } + + // + switch (model.action) { + // + case VBaseFormViewAction.Refresh: + this.detectChanges(); + break; + + // + case VBaseFormViewAction.SetError: + if (model.payload) { + this.setValidationError(model.payload); + } + break; + + // + case VBaseFormViewAction.ClearError: + this.clearValidationErrors(); + break; + + // + case VBaseFormViewAction.ClearForm: + await this.clearForm(); + break; + + // + case VBaseFormViewAction.ResetForm: + await this.resetForm(); + break; + } + }); + } + + protected unregisterActionProvider() { + // + if (!this.ACTION_PROVIDER_SUBSCRIPTION) { + return; + } + + // + this.ACTION_PROVIDER_SUBSCRIPTION.unsubscribe(); + this.ACTION_PROVIDER_SUBSCRIPTION = null; + } + + protected async handleModelChaned() { + await this.configureForm(); + } + + protected checkSameAsDefault() { + // + this.isSameAsDefaultModel = false; + if ( + !isNullOrUndefined(this.model) && + !isNullOrUndefined(this.defaultModel) + ) { + this.isSameAsDefaultModel = isSameObject( + this.modelFieldPreparer(this.model) as any, + this.modelFieldPreparer(this.defaultModel) as any + ); + } + + // + this.detectChanges(); + } + + protected async onFormCleared(): Promise {} + + protected async onFormResetted(): Promise {} + + protected async onModelProvidersPrepared(): Promise {} + + protected async onSortOrderModelKeysPrepared(): Promise {} + + protected async onHiddenModelKeysPrepared(): Promise {} + + protected async onHiddenLabelModelKeysPrepared(): Promise {} + + protected async onModelKeyProviderPrepared(): Promise {} + + protected async onModelValueProviderPrepared(): Promise {} + //#endregion + + // + //#region Private ... + private async prepareModelProviders() { + // + await this.prepareSortOrderModelKeys(); + await this.prepareHiddenModelKeys(); + await this.prepareHiddenLabelModelKeys(); + await this.prepareModelKeyProvider(); + await this.prepareModelValueProvider(); + + // + await this.onModelProvidersPrepared(); + } + + private async prepareSortOrderModelKeys() { + // + if (isNullOrUndefined(this.sortOrderModelKeys)) { + // + this.sortOrderModelKeys = [...toArray(this.defaultSortOrderModelKeys())]; + + // + await this.onSortOrderModelKeysPrepared(); + + // + this.detectChanges(); + } else { + await this.onSortOrderModelKeysPrepared(); + } + } + + private async prepareHiddenModelKeys() { + // + if (isNullOrUndefined(this.hiddenModelKeys)) { + // + this.hiddenModelKeys = [...toArray(this.defaultHiddenModelKeys())]; + + // + await this.onHiddenModelKeysPrepared(); + + // + this.detectChanges(); + } else { + await this.onHiddenModelKeysPrepared(); + } + } + + private async prepareHiddenLabelModelKeys() { + // + if (isNullOrUndefined(this.hiddenLabelModelKeys)) { + // + this.hiddenLabelModelKeys = this.defaultHiddenLabelModelKeys(); + + // + await this.onHiddenLabelModelKeysPrepared(); + + // + this.detectChanges(); + } else { + await this.onHiddenLabelModelKeysPrepared(); + } + } + + private async prepareModelKeyProvider() { + // + if (isNullOrUndefined(this.modelKeyProvider)) { + // + this.modelKeyProvider = this.defaultModelKeyProvider(); + + // + await this.onModelKeyProviderPrepared(); + + // + this.detectChanges(); + } else { + await this.onModelKeyProviderPrepared(); + } + } + + private async prepareModelValueProvider() { + // + if (isNullOrUndefined(this.modelValueProvider)) { + // + this.modelValueProvider = this.defaultModelValueProvider(); + + // + await this.onModelValueProviderPrepared(); + + // + this.detectChanges(); + } else { + await this.onModelValueProviderPrepared(); + } + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\classes\v-base-tabs.page.ts + +import { Subject } from 'rxjs'; +import { + XSlotName, + XFabButton, + XButtonType, + XTabsAction, + XSlotLayout, + XFabButtonSize, + XFabButtonSide, + XTabsActionModel, +} from 'x-framework-components'; +import { Component } from '@angular/core'; +import { VPageComponent } from '../v-page/v-page.component'; + +@Component({ + template: '', +}) +// tslint:disable-next-line: component-class-suffix +export abstract class VBaseTabsPage extends VPageComponent { + // + //#region Props ... + // + readonly SlotNames = Object.assign({}, XSlotName); + readonly SlotLayouts = Object.assign({}, XSlotLayout); + readonly ButtonTypes = Object.assign({}, XButtonType); + + // + // Tabs Configs ... + lastTab: number; + selectedTab: number; + tabsActionProvider = new Subject(); + + // + //#region Default Fab Buttons ... + // + // Edit ... + readonly defaultEditFab: XFabButton = { + id: 'Edit', + index: 1, + icon: this.IconNames.edit, + tooltip: this.ResourceIDs.edit, + size: XFabButtonSize.MINI, + side: XFabButtonSide.TOP, + disabled: this.isLoading || this.uiDisabled, + color: this.ColorNames.Tertiary, + }; + + // + // Remove ... + readonly defaultRemoveFab: XFabButton = { + id: 'Remove', + index: 1, + icon: this.IconNames.remove, + tooltip: this.ResourceIDs.delete, + size: XFabButtonSize.MINI, + side: XFabButtonSide.TOP, + disabled: this.isLoading || this.uiDisabled, + color: this.ColorNames.Danger, + }; + + // + // Add ... + readonly defaultAddFab: XFabButton = { + id: 'Add', + index: 1, + icon: this.IconNames.add, + size: XFabButtonSize.REGULAR, + color: this.ColorNames.Primary, + tooltip: this.ResourceIDs.add, + side: XFabButtonSide.TOP, + disabled: this.isLoading || this.uiDisabled, + }; + + // + // Upload ... + readonly defaultUploadFab: XFabButton = { + id: 'Upload', + index: 1, + icon: this.IconNames.upload, + size: XFabButtonSize.REGULAR, + color: this.ColorNames.Primary, + tooltip: this.ResourceIDs.upload, + side: XFabButtonSide.TOP, + disabled: this.isLoading || this.uiDisabled, + }; + + // + // Download ... + readonly defaultDownloadFab: XFabButton = { + id: 'Download', + index: 1, + icon: this.IconNames.download, + size: XFabButtonSize.REGULAR, + color: this.ColorNames.Success, + tooltip: this.ResourceIDs.download, + side: XFabButtonSide.TOP, + disabled: this.isLoading || this.uiDisabled, + }; + + // + // Select ... + readonly defaultSelectFilesFab: XFabButton = { + index: 1, + id: 'SelectFiles', + side: XFabButtonSide.TOP, + icon: this.IconNames.touch, + size: XFabButtonSize.REGULAR, + color: this.ColorNames.Primary, + tooltip: this.ResourceIDs.select, + disabled: this.isLoading || this.uiDisabled, + }; + + // + // More ... + readonly defaultMoreFab: XFabButton = { + id: 'More', + index: 1, + side: XFabButtonSide.TOP, + size: XFabButtonSize.REGULAR, + color: this.ColorNames.Primary, + tooltip: this.ResourceIDs.options, + icon: this.IconNames.menu_vertical, + disabled: this.isLoading || this.uiDisabled, + }; + //#endregion + //#endregion + + // + //#region Constructor ... + //#endregion + + // + //#region LifeCycles ... + toolbarBackHandler = async () => { + // + if (this.isLoading || this.uiDisabled || this.isLocked) { + return; + } + + // + await this.handleToolbarBackPressed(); + }; + //#endregion + + // + //#region Register Handlers ... + //#endregion + + // + //#region Task Handlers ... + //#endregion + + // + //#region UI Providers ... + //#endregion + + // + //#region UI Handlers ... + //#endregion + + // + //#region Actions ... + async changeTab(tab: number) { + // + if (tab === this.selectedTab || this.isLocked) { + return; + } + + // + this.selectedTab = tab; + this.tabsActionProvider.next({ + action: XTabsAction.TO, + payload: tab, + }); + + // + await this.prepareStates(); + } + //#endregion + + // + //#region Abstracted ... + abstract prepareStates(): Promise; + + abstract handleToolbarBackPressed(): Promise; + //#endregion + + // + //#region Private ... + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\classes\v-base.component.ts + +import { + Inject, + NgZone, + Renderer2, + Component, + ElementRef, + ChangeDetectorRef, +} from '@angular/core'; +import { X_CONFIG } from '../../config/x-config'; +import { XConfig } from '../../config/app-config'; +import { ViewportRuler } from '@angular/cdk/overlay'; +import { XManagerService } from 'x-framework-services'; +import { XColorWithBrightness } from 'x-framework-core'; +import { XBaseComponent, XIconNames } from 'x-framework-components'; +import { AppResourceIDs } from '../../config/app.localization.config'; + +@Component({ + template: '', +}) +export abstract class VBaseComponent extends XBaseComponent { + // + //#region Props ... + // + readonly IconNames = Object.assign({}, XIconNames); + readonly AppResourceIDs = Object.assign({}, AppResourceIDs); + readonly ColorNames = Object.assign({}, XColorWithBrightness); + + // + isMobileUi$ = this.managerService.isMobileUi$; + isNotMobileUi$ = this.managerService.isNotMobileUi$; + //#endregion + + // + //#region Constructor ... + public constructor( + public zone: NgZone, + @Inject(X_CONFIG) + public config: XConfig, + public element: ElementRef, + public renderer: Renderer2, + public ruler: ViewportRuler, + public managerService: XManagerService, + public changeDetector: ChangeDetectorRef + ) { + super( + config, + managerService, + element, + renderer, + ruler, + zone, + changeDetector + ); + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\models\x-query.dto.ts + +export interface XQueryDto { + filter?: string; + containsDetail?: boolean; + enableTracking?: boolean; + sortBy?: string; + isAscending?: boolean; + page?: number; + pageSize?: number; +} + +export interface XQueryResultDto { + items?: Array; + page?: number; + pageSize?: number; + totalPages?: number; + totalFilteredItems?: number; + totalItems?: number; +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\tools\task.tools.ts + +export const taskIdStartKey = '_['; +export const taskIdEndKey = ']'; + +/** + * check if task name contains id ... + * + * @param key the task name ... + */ +export function hasTaskId(key: string | any) { + // + if (!key) { + return false; + } + + // + const mKey = key.toString() as string; + return mKey.includes(taskIdStartKey) && mKey.includes(taskIdEndKey); +} + +/** + * retrieve and extract the raw task name from a task name ... + * + * @param key the task name ... + */ +export function getTaskName(key: string | any) { + // + if (!key) { + return ''; + } + + // + const mKey = key.toString() as string; + + // + if (!hasTaskId(mKey)) { + return mKey; + } + + // + const taskId = getTaskId(mKey); + const taskIdString = `${taskIdStartKey}${taskId}${taskIdEndKey}`; + const result = mKey.replace(taskIdString, ''); + + // + return result; +} + +/** + * retrieve and extract the id inside a task name ... + * + * @param key the task name ... + */ +export function getTaskId(key: string | any) { + // + // Validate Args ... + if (!key || !hasTaskId(key)) { + return ''; + } + + // + // Task Id ... + let result = key.toString() as string; + + // + // Validating Info ... + let index = result.indexOf(taskIdStartKey); + + // + result = result.substr(index, result.length); + result = result.replace(taskIdStartKey, ''); + + // + index = result.indexOf(taskIdEndKey); + result = result.substring(0, index); + + // + // Return Result ... + return result; +} + +/** + * prepare a Task With ID and return ... + * + * @param key the Task Name ... + * @param id th ID ... + */ +export function setTaskId(key: string | any, id: string | any) { + return `${key.toString()}${taskIdStartKey}${id.toString()}${taskIdEndKey}`; +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\tools\v-base.tools.ts + +import { + isValidEmail, + isValidPhoneNumber, + isNullOrEmptyString, + } from 'x-framework-core'; + + /** + * generate an href string for linking to email addresses ... + * + * @param email the propper email address ... + */ + export function getEmailHref(email: string) { + // + if (isNullOrEmptyString(email) || !isValidEmail(email)) { + return ''; + } + + // + return `mailto:${email}`; + } + + /** + * generate an href string for linking to phoneNumber ... + * + * @param phoneNumber the propper phone number ... + */ + export function getPhoneNumberHref(phoneNumber: string) { + // + if (isNullOrEmptyString(phoneNumber) || !isValidPhoneNumber(phoneNumber)) { + return ''; + } + + // + return `tel:${phoneNumber}`; + } + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\tools\v-data-pager.tools.ts + +import { + XBaseDataPagerQuery, + XBaseDataPagerQueryResult, + } from 'x-framework-core'; + import { Observable } from 'rxjs'; + import { map } from 'rxjs/operators'; + import { XQueryDto, XQueryResultDto } from '../models/x-query.dto'; + + export function toXQuery( + q: XBaseDataPagerQuery, + containsDetail = true, + enableTracking = false, + sortBy = 'id', + isAscending = true + ) { + return { + filter: q.filter, + containsDetail, + enableTracking, + sortBy, + isAscending, + page: q.pageNumber, + pageSize: q.rowsInPage, + } as XQueryDto; + } + + export function applyToDataPagerQueryResult( + result: Observable> + ) { + return result.pipe( + map((queryResult) => { + return { + count: queryResult.totalItems || 0, + items: queryResult.items || [], + } as XBaseDataPagerQueryResult; + }) + ); + } + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\typings\v-base-form.typings.ts + +import { XBaseActionModel } from 'x-framework-components'; + +export enum VBaseFormViewPresentType { + AsForm = 'as_form', + AsDescriptionList = 'as_description_list', +} + +export type VBaseFormViewPresentTypeIdentifier = + | VBaseFormViewPresentType + | string; + +export enum VBaseFormViewAction { + // + Refresh = 'refresh', + SetError = 'set_error', + ClearError = 'clear_error', + ClearForm = 'clear_form', + ResetForm = 'reset_form', +} + +export type VBaseFormViewActionIdentifier = VBaseFormViewAction | string; + +export interface VBaseFormViewActionModel + extends XBaseActionModel {} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\v-maintainer\v-maintainer.component.html + +
+

+ {{ getResourceValue(ResourceIDs.maintainer_title) | async }} +

+

+ {{ getResourceValue(ResourceIDs.maintainer_name) | async }} +

+
+ + + + + + + + + + + + + + + + + + + + +
+
+ +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\v-maintainer\v-maintainer.component.scss + +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0; +} + +.socials { + margin-top: 6px; +} + +.social { + display: inline-block; + margin: 0 2px; +} + +:host ::ng-deep .x-icon { + &:hover { + color: var(--x-color-tertiary) !important; + } +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\v-maintainer\v-maintainer.component.ts + +import { + Inject, + NgZone, + Renderer2, + Component, + ElementRef, + ChangeDetectorRef, + ChangeDetectionStrategy, +} from '@angular/core'; +import { XResourceIDs } from 'x-framework-core'; +import { X_CONFIG } from '../../config/x-config'; +import { XConfig } from '../../config/app-config'; +import { ViewportRuler } from '@angular/cdk/overlay'; +import { XManagerService } from 'x-framework-services'; +import { XBaseComponent } from 'x-framework-components'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +@Component({ + // tslint:disable-next-line:component-selector + selector: 'v-maintainer', + templateUrl: './v-maintainer.component.html', + styleUrls: ['./v-maintainer.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class VMaintainerComponent extends XBaseComponent { + // + //#region Props ... + // + // Prepare All Resource IDs ... + readonly ResourceIDs = Object.assign( + Object.assign({}, XResourceIDs), + AppResourceIDs + ) as any; + //#endregion + + // + //#region Constructor ... + public constructor( + @Inject(X_CONFIG) public config: XConfig, + public managerService: XManagerService, + public element: ElementRef, + public renderer: Renderer2, + public ruler: ViewportRuler, + public zone: NgZone, + public changeDetector: ChangeDetectorRef + ) { + super( + config, + managerService, + element, + renderer, + ruler, + zone, + changeDetector + ); + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\v-page\v-page.component.html + + + +
+ +
+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\v-page\v-page.component.scss + +.x-footer-content { + margin: 0; + padding: 0; +} + +:host::ng-deep { + .x-toolbar-end-slot { + flex-shrink: 0 !important; + justify-content: flex-end; + } + + .x-toolbar-start-slot { + flex-shrink: 1 !important; + flex-grow: 7 !important; + justify-content: flex-start; + overflow: hidden; + text-overflow: ellipsis; + } +} + +x-page { + // --content-offset-top: -56px !important; + // --content-offset-bottom: -56px !important; + + // --side-menu-content-offset-top: -54px !important; + // --side-menu-content-offset-bottom: -44px !important; +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\app\views\v-page\v-page.component.ts + +import { + Input, + Inject, + NgZone, + Renderer2, + Component, + ViewChild, + ElementRef, + TemplateRef, + EventEmitter, + ChangeDetectorRef, + ChangeDetectionStrategy, +} from '@angular/core'; +import { + XColor, + nameof, + XLocale, + notValue, + XResourceIDs, + XPickerColumn, + XStandardType, + XOneOrManyType, + XColorIdentifier, + XModalButtonRole, + XPickerColumnOption, + XColorWithBrightness, +} from 'x-framework-core'; +import { + XMenuSlot, + XLogoSlot, + XBackSlot, + XSideType, + XSideSlot, + XTitleSlot, + XIconNames, + XMenuState, + XContentSlot, + XPageComponent, + XPageContainer, + XPopoverOption, + XPopoverService, + XNavigatorListItem, + XBackSlotIdentifier, + XLogoSlotIdentifier, + XMenuSlotIdentifier, + XPageSizeIdentifier, + XSideSlotIdentifier, + XSideTypeIdentifier, + XTitleSlotIdentifier, + XContentSlotIdentifier, +} from 'x-framework-components'; +import { map } from 'rxjs/operators'; +import { MenuController } from '@ionic/angular'; +import { X_CONFIG } from 'src/app/config/x-config'; +import { Pages } from 'src/app/config/page.config'; +import { XConfig } from 'src/app/config/app-config'; +import { ViewportRuler } from '@angular/cdk/overlay'; +import { XManagerService } from 'x-framework-services'; +import { NavPageItems } from 'src/app/config/page.config'; +import { isNullOrUndefined, hasChild } from 'x-framework-core'; +import { AppResourceIDs } from 'src/app/config/app.localization.config'; + +@Component({ + // tslint:disable-next-line:component-selector + selector: 'v-page', + templateUrl: './v-page.component.html', + styleUrls: ['./v-page.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class VPageComponent extends XPageComponent { + // + //#region Props ... + // + isMobileUi$ = this.managerService.isMobileUi$; + isNotMobileUi$ = this.managerService.isNotMobileUi$; + + // + readonly IconNames = Object.assign({}, XIconNames); + readonly AppResourceIDs = Object.assign({}, AppResourceIDs); + readonly ColorNames = Object.assign({}, XColorWithBrightness); + + // + navPages = NavPageItems; + + // + //#region CaptureMode ... + enableCaptureMode: XStandardType = false; + downloadCapturedImage: XStandardType = true; + captureTopOffset: XStandardType = 0; + captureBottomOffset: XStandardType = 0; + imageCaptured = new EventEmitter(); + //#endregion + + // + //#region Page Props ... + // + //#region Toolbar Content ... + @Input() + showToolbarContent = true; + private TOOLBAR_CONTENT_TEMPLATE: TemplateRef; + + /** + * the toolbar content ... + */ + @ViewChild('toolbarContentTemplateRef', { static: false }) + set toolbarContentTemplateRef(v: TemplateRef) { + // + if (isNullOrUndefined(v)) { + return; + } + + // + this.TOOLBAR_CONTENT_TEMPLATE = v; + } + + get toolbarContentTemplateRef() { + return this.TOOLBAR_CONTENT_TEMPLATE; + } + //#endregion + + // + //#region Toolbar End Slot ... + @Input() + showToolbarEndSlot = true; + //#endregion + + // + color: XStandardType = 'light-shade'; + cssClass: XStandardType>; + //#endregion + + // + //#region Toolbar Props ... + // + //#region Toolbar Boundary ... + // + showToolbar: XStandardType = true; + + // + toolbarColor: XStandardType = XColor.Primary; + //#endregion + + // + //#region Toolbar ProgressBar ... + toolbarProgressBarColor: XStandardType = XColor.Warning; + //#endregion + + // + //#region Toolbar Title and SubTitle ... + // + // Toolbar Title ... + toolbarTitleSlot: XStandardType = XTitleSlot.End; + toolbarShowTitle: XStandardType = true; + toolbarShowSubTitle: XStandardType = false; + toolbarTitle: XStandardType = super.resourceProvider( + this.ResourceIDs.app_name + ); + //#endregion + + // + //#region Toolbar Logo ... + toolbarLogoSlot: XStandardType = XLogoSlot.End; + toolbarShowLogo: XStandardType = true; + toolbarLogoUrl: XStandardType = './assets/image/logo.png'; + //#endregion + + // + //#region Toolbar Back ... + toolbarBackSlot: XStandardType = XBackSlot.Start; + toolbarShowBack: XStandardType = false; + toolbarHasBack: XStandardType = true; + toolbarDefaultHref: string = ''; + //#endregion + + // + //#region Toolbar Menu ... + toolbarMenuSlot: XStandardType = XMenuSlot.Start; + + // + menuState: XStandardType = XMenuState.WillClose; + + // + toolbarShowMenu: XStandardType = true; + + // + toolbarHasMenu: XStandardType = true; + //#endregion + + // + //#region Toolbar Templates ... + toolbarContentSlot: XStandardType = + XContentSlot.Start; + //#endregion + //#endregion + + // + //#region Scrolling Handlers ... + provideScrollEvents: XStandardType = true; + scrollBehaviour: XOneOrManyType = ['x-fab']; + scrollStartClasses: XOneOrManyType = 'start-scroll'; + scrollEndClasses: XOneOrManyType = 'end-scroll'; + //#endregion + + // + //#region Footer Props ... + // + footerColor: XStandardType = XColor.Primary; + //#endregion + + // + //#region Side Props ... + // + toggleMenuWhen: XStandardType = 'md'; + hasSide: XStandardType = true; + sideSlot: XStandardType = XSideSlot.Start; + sideType: XStandardType = XSideType.Overlay; + sideColor: XStandardType = XColor.Primary; + //#endregion + //#endregion + + // + //#region Constructor ... + public constructor( + public zone: NgZone, + @Inject(X_CONFIG) + public config: XConfig, + public element: ElementRef, + public renderer: Renderer2, + public ruler: ViewportRuler, + public menuController: MenuController, + public managerService: XManagerService, + public changeDetector: ChangeDetectorRef, + protected popoverService: XPopoverService, + protected propertyProvider: XPageContainer + ) { + super( + config, + managerService, + element, + renderer, + ruler, + zone, + changeDetector, + menuController, + propertyProvider + ); + + // + this.isMobileUi$ = this.managerService.isMobileUi$; + this.isNotMobileUi$ = notValue(this.isMobileUi$); + } + //#endregion + + // + //#region LifeCycle ... + onInit() { + super.onInit(); + + // + this.menuState = this.isMobileUi$.pipe( + map((isMobile) => (isMobile ? XMenuState.WillClose : XMenuState.Opened)) + ); + } + + async afterViewInit() { + super.afterViewInit(); + + // + const showToolbar = await this.getValueAsync(this.showToolbar); + if (showToolbar) { + this.detectChanges(); + } + + // + this.managerService.dispatchResizeEvent(); + } + + afterViewChecked() { + super.afterViewChecked(); + + // + this.changeDetector.detectChanges(); + } + + async onChange(changeKeys: string[]) { + super.onChange(changeKeys); + + // + const isHasSideChanged = changeKeys.includes( + nameof('hasSide') + ); + if (isHasSideChanged) { + await this.handleHasSideEffect(); + } + + // + const isToolbarHasMenuChanged = changeKeys.includes( + nameof('toolbarHasMenu') + ); + const isToolbarShowMenuChanged = changeKeys.includes( + nameof('toolbarShowMenu') + ); + if (isToolbarHasMenuChanged || isToolbarShowMenuChanged) { + this.detectChanges(); + } + } + //#endregion + + // + //#region Register Handlers ... + //#endregion + + // + //#region Handlers ... + //#endregion + + // + //#region UI Providers ... + //#endregion + + // + //#region UI Handlers ... + async handleNavigatorItemSelected(event: XNavigatorListItem) { + // + if (!event) { + return; + } + + // + // TODO: Complete Special Actions on NavItems ... + } + + async handleMoreButtonClicked(event: any) { + // + const onlyIcon = false; + + // + const options: XPopoverOption[] = []; + + // + // Home Page ... + options.push({ + id: 'go_home', + icon: XIconNames.home, + title: AppResourceIDs.home, + color: XColor.Tertiary, + slot: 'start', + onlyIcon, + handler: async () => { + await this.managerService.navigateByPageReplace(Pages.Home); + }, + }); + + // + // Change Language ... + options.push({ + id: 'change_language', + icon: XIconNames.language, + title: XResourceIDs.language, + color: XColor.Tertiary, + slot: 'start', + onlyIcon, + handler: async () => { + await this.handleChangeLocale(); + }, + }); + + // + // CaptureMode ... + const isCaptureModeEnabled = await this.getValueAsync( + this.enableCaptureMode + ); + if (isCaptureModeEnabled) { + // + // Capture Mode ... + options.push({ + id: 'disable_capture_mode', + icon: XIconNames.crop, + title: XResourceIDs.disable_capture_mode, + color: XColor.Danger, + slot: 'start', + onlyIcon, + handler: async () => { + // + this.enableCaptureMode = false; + this.detectChanges(); + }, + }); + } else { + // + // Capture Mode ... + options.push({ + id: 'enable_capture_mode', + icon: XIconNames.crop, + title: XResourceIDs.enable_capture_mode, + color: XColor.Tertiary, + slot: 'start', + onlyIcon, + handler: async () => { + // + this.enableCaptureMode = true; + this.detectChanges(); + }, + }); + } + + // + // Theme Manager ... + options.push({ + id: 'theme_manager', + icon: XIconNames.color_palette, + title: AppResourceIDs.theme_manager, + color: XColor.Tertiary, + slot: 'start', + onlyIcon, + handler: async () => { + await this.managerService.navigateByPageReplace(Pages.ThemeManager); + }, + }); + + // + // Present Context Menu ... + await this.popoverService.presentContextMenu(options, event); + } + + async handleImageCaptured(image: string | Blob) { + // + if (image) { + this.imageCaptured.emit(image); + } + + // + this.enableCaptureMode = false; + this.detectChanges(); + } + //#endregion + + // + //#region Provided Actions ... + //#endregion + + // + //#region Private ... + private async handleHasSideEffect() { + // + const hasSide = await this.getValueAsync(this.hasSide); + if (!hasSide) { + // + this.toolbarHasMenu = false; + this.toolbarShowMenu = false; + this.toggleMenuWhen = ''; + } + } + + private async handleChangeLocale() { + // + const currentLocale = this.managerService.currentLocale; + const availableLanguages = this.config.availableLanguages.map((ai) => { + return { + name: ai.name, + locale: ai.locale, + }; + }); + if (!hasChild(availableLanguages)) { + return; + } + + // + const localOptions: XPickerColumnOption[] = availableLanguages.map((al) => { + // + const op: XPickerColumnOption = { + text: al.name, + value: al.locale, + disabled: al.locale === currentLocale, + }; + + // + return op; + }); + const localColumn: XPickerColumn = { + name: 'locales', + options: localOptions, + }; + + // + await this.managerService.dialogService.presentPicker({ + buttons: [ + { + text: XResourceIDs.ok, + role: XModalButtonRole.Selected, + cssClass: ['btn-selected'], + handler: async (value) => { + // + const selectedLocale: XLocale = value?.locales?.value; + if (!selectedLocale) { + return; + } + + // + await this.managerService.settingsService.changeLocale( + selectedLocale, + true + ); + }, + }, + { + text: XResourceIDs.cancel, + role: XModalButtonRole.Cancel, + cssClass: ['btn-cancel'], + handler: () => {}, + }, + ], + columns: [localColumn], + }); + } + //#endregion +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\environments\environment.prod.ts + +import { XConfig, xSharedConfig } from '../app/config/app-config'; + +export const environment = { + production: true, + configurations: { + ...xSharedConfig, + + // + isProd: true + } as XConfig +}; + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\environments\environment.ts + +import { XConfig, xSharedConfig } from '../app/config/app-config'; + +export const environment = { + production: false, + configurations: { + ...xSharedConfig, + + // + isProd: false + } as XConfig +}; + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\src\theme\theme.scss + +// +// XFramework Color Mechanism ... +:root { + /** Primary **/ + --x-color-primary: #3880ff; + --x-color-primary-shade: #3171e0; + --x-color-primary-tint: #4c8dff; + + --x-color-primary-contrast: #ffffff; + --x-color-primary-shade-contrast: #ffffff; + --x-color-primary-tint-contrast: #ffffff; + + /** Secondary **/ + --x-color-secondary: #0cd1e8; + --x-color-secondary-shade: #0bb8cc; + --x-color-secondary-tint: #24d6ea; + + --x-color-secondary-contrast: #ffffff; + --x-color-secondary-shade-contrast: #ffffff; + --x-color-secondary-tint-contrast: #ffffff; + + /** Tertiary **/ + --x-color-tertiary: #7044ff; + --x-color-tertiary-shade: #633ce0; + --x-color-tertiary-tint: #7e57ff; + + --x-color-tertiary-contrast: #ffffff; + --x-color-tertiary-shade-contrast: #ffffff; + --x-color-tertiary-tint-contrast: #ffffff; + + /** Success **/ + --x-color-success: #10dc60; + --x-color-success-shade: #0ec254; + --x-color-success-tint: #28e070; + + --x-color-success-contrast: #ffffff; + --x-color-success-shade-contrast: #ffffff; + --x-color-success-tint-contrast: #ffffff; + + /** Warning **/ + --x-color-warning: #ffce00; + --x-color-warning-shade: #e0b500; + --x-color-warning-tint: #ffd31a; + + --x-color-warning-contrast: #ffffff; + --x-color-warning-shade-contrast: #ffffff; + --x-color-warning-tint-contrast: #ffffff; + + /** Danger **/ + --x-color-danger: #f04141; + --x-color-danger-shade: #d33939; + --x-color-danger-tint: #f25454; + + --x-color-danger-contrast: #ffffff; + --x-color-danger-shade-contrast: #ffffff; + --x-color-danger-tint-contrast: #ffffff; + + /** Dark **/ + --x-color-dark: #222428; + --x-color-dark-shade: #1e2023; + --x-color-dark-tint: #383a3e; + + --x-color-dark-contrast: #ffffff; + --x-color-dark-shade-contrast: #ffffff; + --x-color-dark-tint-contrast: #ffffff; + + /** Medium **/ + --x-color-medium: #989aa2; + --x-color-medium-shade: #86888f; + --x-color-medium-tint: #a2a4ab; + + --x-color-medium-contrast: #ffffff; + --x-color-medium-shade-contrast: #ffffff; + --x-color-medium-tint-contrast: #ffffff; + + /** Light **/ + --x-color-light: #f4f5f8; + --x-color-light-shade: #d7d8da; + --x-color-light-tint: #f5f6f9; + + --x-color-light-contrast: #000000; + --x-color-light-shade-contrast: #000000; + --x-color-light-tint-contrast: #000000; +} +/* Core CSS required for Ionic components to work properly */ +@import '~@ionic/angular/css/core.css'; + +/* Basic CSS for apps built with Ionic */ +@import '~@ionic/angular/css/normalize.css'; +@import '~@ionic/angular/css/structure.css'; +@import '~@ionic/angular/css/typography.css'; +@import '~@ionic/angular/css/display.css'; + +/* Optional CSS utils that can be commented out */ +@import '~@ionic/angular/css/padding.css'; +@import '~@ionic/angular/css/float-elements.css'; +@import '~@ionic/angular/css/text-alignment.css'; +@import '~@ionic/angular/css/text-transformation.css'; +@import '~@ionic/angular/css/flex-utils.css'; + +/* XFramework Theme */ +@import '~x-framework-components/theme/theme.scss'; + +/* Override Default XFramework's Body Background Color */ +body { + background-color: #ffffff; +} + +.code-block { + direction: ltr; + color: white; + margin: { + top: 6px; + bottom: 8px; + } + // white-space: pre; + word-spacing: normal; + word-break: normal; + background-color: black; + + // + overflow: hidden; + + // + .x-header { + direction: var(--ui-direction) !important; + } +} + +// +//#region Card .. +$border-radius: 8px; + +x-card.v-card, +.v-card > x-card { + .x-title { + margin-bottom: 8px; + font-size: 16px !important; + + color: var(--x-color-primary-tint-contrast); + } + + .x-sub-title { + margin-bottom: 0; + font-size: 12px !important; + + color: var(--x-color-primary-tint-contrast); + } + + mat-card { + border-top-left-radius: $border-radius; + border-top-right-radius: $border-radius; + } + + mat-card.mobile { + .mat-card-header { + border-top-left-radius: 0; + border-top-right-radius: 0; + } + } + + mat-card-header { + @include transition-duration(); + + // + background-color: var(--x-color-primary-tint); + + // + border-top-left-radius: $border-radius; + border-top-right-radius: $border-radius; + } + + mat-card-header:hover { + background-color: var(--x-color-primary-shade); + } +} + +x-card.v-card.v-no-margin-card, +.v-card.v-no-margin-card > x-card { + mat-card-content { + padding: 0 !important; + } +} +//#endregion + +.x-auto-complete-margin { + margin-top: -1.34375em; +} + +### FILE: C:\Users\SaherElm\Documents\Projects\xSaherelmWorkspace\xFrameworkTest\www\index.html + + + + + + XFramework App + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Modules/xCommons b/Modules/xCommons index 4697937..c87f80c 160000 --- a/Modules/xCommons +++ b/Modules/xCommons @@ -1 +1 @@ -Subproject commit 469793770977fbb6f8c58e09c8c7a30781056d0b +Subproject commit c87f80c5876e5a402619edf818d708c69bbd31d1 diff --git a/Modules/xEventService b/Modules/xEventService index e05420b..079048d 160000 --- a/Modules/xEventService +++ b/Modules/xEventService @@ -1 +1 @@ -Subproject commit e05420b1aaa49654b281d52004f1de78a99bcded +Subproject commit 079048dff51ac04e75a654279f459ad1cf937982 diff --git a/Modules/xFrameworkComponentsHolder b/Modules/xFrameworkComponentsHolder index 8ab1322..203c09a 160000 --- a/Modules/xFrameworkComponentsHolder +++ b/Modules/xFrameworkComponentsHolder @@ -1 +1 @@ -Subproject commit 8ab13222229f1220bf1a2950ed367c627bd6e16c +Subproject commit 203c09a0e80ac0a132158b9b08dee576d9937c21 diff --git a/xClient b/xClient index 16696d0..6209853 160000 --- a/xClient +++ b/xClient @@ -1 +1 @@ -Subproject commit 16696d08d0635c3844a731da1ce3d791df70b1ba +Subproject commit 62098538f5da68a5119f07bea73519e5f4c692c9 diff --git a/xFrameworkTest b/xFrameworkTest new file mode 160000 index 0000000..fac270d --- /dev/null +++ b/xFrameworkTest @@ -0,0 +1 @@ +Subproject commit fac270d30140c00d61f98206c9d68def3bbe094c