Add support for webhook triggers
This commit is contained in:
parent
73e67025e7
commit
9a01aefe17
9
Elsa.sln
9
Elsa.sln
|
|
@ -233,13 +233,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "blazor", "blazor", "{D86B94
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "aspnetcore", "aspnetcore", "{C869CC72-9A98-4246-9A76-6A50F48AFD85}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElsaDashboard.Samples.Monolith", "src\samples\dashboard\blazor\ElsaDashboard.Samples.Monolith\ElsaDashboard.Samples.Monolith.csproj", "{C0ECCB6E-B6FA-413E-B717-648E9CB71A97}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElsaDashboard.Samples.Server", "src\samples\dashboard\blazor\ElsaDashboard.Samples.Server\ElsaDashboard.Samples.Server.csproj", "{9B56C09A-E4D5-4FDD-AD41-EDD1F52B7B9B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElsaDashboard.Samples.WebAssembly", "src\samples\dashboard\blazor\ElsaDashboard.Samples.WebAssembly\ElsaDashboard.Samples.WebAssembly.csproj", "{84876EBC-8109-475E-8C4E-A5CB714DB429}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElsaDashboard.Samples.AspNetCore.Monolith", "src\samples\dashboard\aspnetcore\ElsaDashboard.Samples.AspNetCore.Monolith\ElsaDashboard.Samples.AspNetCore.Monolith.csproj", "{F1EF5639-021A-4D22-9D4A-73D57C88EBFA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElsaDashboard.Samples.AspNetCore.Monolith", "src\samples\dashboard\aspnetcore\ElsaDashboard.Samples.AspNetCore.Monolith\ElsaDashboard.Samples.AspNetCore.Monolith.csproj", "{F1EF5639-021A-4D22-9D4A-73D57C88EBFA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.Persistence.YesSql", "src\samples\persistence\Elsa.Samples.Persistence.YesSql\Elsa.Samples.Persistence.YesSql.csproj", "{3A30EFFE-32D8-40B8-9C62-427037BB5424}"
|
||||
|
|
@ -263,8 +258,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Samples.CustomActivity
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "aspnetcore", "aspnetcore", "{28C5C63F-2DCF-46BE-9576-1F50A7CFE8C4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElsaDashboard.Web", "src\dashboards\aspnetcore\ElsaDashboard\ElsaDashboard.Web.csproj", "{5F81C84A-4C04-48FF-981C-23E48A7ACA6C}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "github", "github", "{3BF2C5A9-AC69-4587-833F-C16CFC97E100}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.github\workflows\publish-latest-dashboard-and-server-docker.yml = .github\workflows\publish-latest-dashboard-and-server-docker.yml
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@ namespace Elsa.Activities.Http.Bookmarks
|
|||
public override async ValueTask<IEnumerable<BookmarkResult>> GetBookmarksAsync(BookmarkProviderContext<HttpEndpoint> context, CancellationToken cancellationToken)
|
||||
{
|
||||
var path = ToLower(await context.ReadActivityPropertyAsync(x => x.Path, cancellationToken))!;
|
||||
var correlationId = ToLower(context.ActivityExecutionContext.WorkflowExecutionContext.CorrelationId);
|
||||
var methods = (await context.ReadActivityPropertyAsync(x => x.Methods, cancellationToken))?.Select(x => x.ToLowerInvariant()) ?? Enumerable.Empty<string>();
|
||||
var methods = (await context.ReadActivityPropertyAsync(x => x.Methods, cancellationToken))?.Select(ToLower) ?? Enumerable.Empty<string>();
|
||||
|
||||
BookmarkResult CreateBookmark(string method) => Result(new(path, method));
|
||||
return methods.Select(CreateBookmark);
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ namespace Elsa.Activities.Http.Middleware
|
|||
{
|
||||
var pendingWorkflowInstance = pendingWorkflowInstances[pendingWorkflow.WorkflowInstanceId];
|
||||
var workflowBlueprintWrapper = workflowBlueprintWrappers[pendingWorkflowInstance.DefinitionId];
|
||||
var activityWrapper = workflowBlueprintWrapper.GetActivity<HttpEndpoint>(pendingWorkflow.ActivityId!);
|
||||
var activityWrapper = workflowBlueprintWrapper.GetUnfilteredActivity<HttpEndpoint>(pendingWorkflow.ActivityId!);
|
||||
var readContent = await activityWrapper!.EvaluatePropertyValueAsync(x => x.ReadContent, cancellationToken);
|
||||
var inputModel = commonInputModel;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ namespace Elsa.Activities.Webhooks.ActivityTypes
|
|||
{
|
||||
public class WebhookActivityTypeProvider : IActivityTypeProvider
|
||||
{
|
||||
public const string WebhookMarkerAttribute = "WebhookMarker";
|
||||
private const string WebhookActivityCategory = "Webhooks";
|
||||
|
||||
private readonly IWebhookDefinitionStore _webhookDefinitionStore;
|
||||
|
|
@ -59,17 +60,17 @@ namespace Elsa.Activities.Webhooks.ActivityTypes
|
|||
InputProperties = new[]
|
||||
{
|
||||
new ActivityInputDescriptor(
|
||||
nameof(HttpEndpoint.Methods),
|
||||
nameof(HttpEndpoint.Methods),
|
||||
typeof(HashSet<string>),
|
||||
ActivityInputUIHints.Dropdown,
|
||||
ActivityInputUIHints.CheckList,
|
||||
"Request Method",
|
||||
"Specify what request method this webhook should handle. Leave empty to handle both GET and POST requests",
|
||||
new HashSet<string> { "", "GET", "POST" },
|
||||
"Webhooks",
|
||||
new[] { "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD" },
|
||||
default,
|
||||
0,
|
||||
"POST",
|
||||
SyntaxNames.Literal,
|
||||
new[] { SyntaxNames.JavaScript, SyntaxNames.Liquid })
|
||||
SyntaxNames.Json,
|
||||
new[] { SyntaxNames.Json, SyntaxNames.JavaScript, SyntaxNames.Liquid })
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -78,18 +79,22 @@ namespace Elsa.Activities.Webhooks.ActivityTypes
|
|||
var activity = await _activityActivator.ActivateActivityAsync<HttpEndpoint>(context);
|
||||
|
||||
activity.Path = webhook.Path;
|
||||
activity.ReadContent = true;
|
||||
activity.TargetType = webhook.PayloadTypeName is not null and not "" ? Type.GetType(webhook.PayloadTypeName) : throw new Exception($"Type {webhook.PayloadTypeName} not found");
|
||||
return activity;
|
||||
activity.TargetType = !string.IsNullOrWhiteSpace(webhook.PayloadTypeName) ? Type.GetType(webhook.PayloadTypeName) ?? throw new Exception($"Type {webhook.PayloadTypeName} not found") : null;
|
||||
return activity;
|
||||
}
|
||||
|
||||
return new ActivityType
|
||||
{
|
||||
TypeName = webhook.Name,
|
||||
Type = typeof(HttpEndpoint),
|
||||
Description = webhook.Description is not null and not "" ? webhook.Description : $"A webhook at {webhook.Path}",
|
||||
Description = !string.IsNullOrWhiteSpace(webhook.Description) ? webhook.Description : $"A webhook at {webhook.Path}",
|
||||
DisplayName = webhook.Name,
|
||||
ActivateAsync = ActivateActivityAsync,
|
||||
Attributes = new Dictionary<string, object>
|
||||
{
|
||||
[WebhookMarkerAttribute] = true,
|
||||
["Path"] = webhook.Path,
|
||||
},
|
||||
Describe = () => descriptor
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Activities.Http;
|
||||
using Elsa.Activities.Http.Bookmarks;
|
||||
using Elsa.Activities.Webhooks.ActivityTypes;
|
||||
using Elsa.Bookmarks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Elsa.Activities.Webhooks.Bookmarks
|
||||
{
|
||||
public record WebhookBookmark(string WebhookActivityTypeName) : IBookmark;
|
||||
|
||||
public class WebhookBookmarkProvider : BookmarkProvider<HttpEndpointBookmark>
|
||||
{
|
||||
public override bool SupportsActivity(BookmarkProviderContext context)
|
||||
{
|
||||
var activityType = context.ActivityType;
|
||||
return activityType.Attributes.ContainsKey(WebhookActivityTypeProvider.WebhookMarkerAttribute);
|
||||
}
|
||||
|
||||
public override async ValueTask<IEnumerable<BookmarkResult>> GetBookmarksAsync(BookmarkProviderContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
var path = ToLower((string)context.ActivityType.Attributes["Path"])!;
|
||||
var methods = (await context.ReadActivityPropertyAsync<HttpEndpoint, HashSet<string>>(x => x.Methods, cancellationToken))?.Select(ToLower) ?? Enumerable.Empty<string>();
|
||||
|
||||
BookmarkResult CreateBookmark(string method) => Result(new(path, method), nameof(HttpEndpoint));
|
||||
return methods.Select(CreateBookmark);
|
||||
}
|
||||
|
||||
private static string? ToLower(string? s) => s?.ToLowerInvariant();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using Elsa.Activities.Webhooks.ActivityTypes;
|
||||
using Elsa.Activities.Webhooks.Bookmarks;
|
||||
using Elsa.Activities.Webhooks.Persistence.Decorators;
|
||||
using Elsa.Activities.Webhooks.Services;
|
||||
using Elsa.Services;
|
||||
|
|
@ -22,11 +23,11 @@ namespace Elsa.Activities.Webhooks.Extensions
|
|||
configure?.Invoke(webhookOptionsBuilder);
|
||||
|
||||
// Services.
|
||||
|
||||
services
|
||||
.AddScoped<IActivityTypeProvider, WebhookActivityTypeProvider>()
|
||||
.AddScoped(sp => webhookOptionsBuilder.WebhookOptions.WebhookDefinitionStoreFactory(sp))
|
||||
.AddScoped<IWebhookPublisher, WebhookPublisher>();
|
||||
.AddScoped<IWebhookPublisher, WebhookPublisher>()
|
||||
.AddBookmarkProvider<WebhookBookmarkProvider>();
|
||||
|
||||
services.Decorate<IWebhookDefinitionStore, InitializingWebhookDefinitionStore>();
|
||||
services.Decorate<IWebhookDefinitionStore, EventPublishingWebhookDefinitionStore>();
|
||||
|
|
|
|||
|
|
@ -20,17 +20,8 @@ namespace Elsa.Bookmarks
|
|||
public ActivityType ActivityType { get; }
|
||||
public BookmarkIndexingMode Mode { get; }
|
||||
public IActivityBlueprintWrapper<TActivity> GetActivity<TActivity>() where TActivity : IActivity => new ActivityBlueprintWrapper<TActivity>(ActivityExecutionContext);
|
||||
}
|
||||
|
||||
public class BookmarkProviderContext<TActivity> : BookmarkProviderContext where TActivity: IActivity
|
||||
{
|
||||
public BookmarkProviderContext(ActivityExecutionContext activityExecutionContext, ActivityType activityType, BookmarkIndexingMode mode) : base(activityExecutionContext, activityType, mode)
|
||||
{
|
||||
}
|
||||
|
||||
public IActivityBlueprintWrapper<TActivity> Activity => GetActivity<TActivity>();
|
||||
|
||||
public async ValueTask<T?> ReadActivityPropertyAsync<T>(Expression<Func<TActivity, T>> propertyExpression, CancellationToken cancellationToken = default)
|
||||
public async ValueTask<T?> ReadActivityPropertyAsync<TActivity, T>(Expression<Func<TActivity, T>> propertyExpression, CancellationToken cancellationToken = default) where TActivity : IActivity
|
||||
{
|
||||
var activityBlueprint = GetActivity<TActivity>();
|
||||
|
||||
|
|
@ -40,4 +31,15 @@ namespace Elsa.Bookmarks
|
|||
return activityBlueprint.GetPropertyValue(propertyExpression);
|
||||
}
|
||||
}
|
||||
|
||||
public class BookmarkProviderContext<TActivity> : BookmarkProviderContext where TActivity: IActivity
|
||||
{
|
||||
public BookmarkProviderContext(ActivityExecutionContext activityExecutionContext, ActivityType activityType, BookmarkIndexingMode mode) : base(activityExecutionContext, activityType, mode)
|
||||
{
|
||||
}
|
||||
|
||||
public IActivityBlueprintWrapper<TActivity> Activity => GetActivity<TActivity>();
|
||||
|
||||
public ValueTask<T?> ReadActivityPropertyAsync<T>(Expression<Func<TActivity, T>> propertyExpression, CancellationToken cancellationToken = default) => base.ReadActivityPropertyAsync(propertyExpression, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,11 @@ namespace Elsa
|
|||
}
|
||||
|
||||
public static IActivityBlueprintWrapper<TActivity>? FindActivity<TActivity>(this IWorkflowBlueprintWrapper workflowBlueprintWrapper, Func<IActivityBlueprintWrapper<TActivity>, bool>? predicate = default) where TActivity : IActivity => workflowBlueprintWrapper.Filter(predicate).FirstOrDefault();
|
||||
public static IActivityBlueprintWrapper<TActivity>? GetActivity<TActivity>(this IWorkflowBlueprintWrapper workflowBlueprintWrapper, string id) where TActivity : IActivity => workflowBlueprintWrapper.FindActivity<TActivity>(x => x.ActivityBlueprint.Id == id)!;
|
||||
public static IActivityBlueprintWrapper<TActivity> GetActivity<TActivity>(this IWorkflowBlueprintWrapper workflowBlueprintWrapper, string id) where TActivity : IActivity => workflowBlueprintWrapper.FindActivity<TActivity>(x => x.ActivityBlueprint.Id == id)!;
|
||||
public static IActivityBlueprintWrapper? GetActivity(this IWorkflowBlueprintWrapper workflowBlueprintWrapper, string id) => workflowBlueprintWrapper.Activities.FirstOrDefault(x => x.ActivityBlueprint.Id == id);
|
||||
|
||||
public static IActivityBlueprintWrapper<TActivity>? FindUnfilteredActivity<TActivity>(this IWorkflowBlueprintWrapper workflowBlueprintWrapper, Func<IActivityBlueprintWrapper<TActivity>, bool> predicate) where TActivity : IActivity => workflowBlueprintWrapper.Activities.Select(x => x.As<TActivity>()).Where(predicate).FirstOrDefault();
|
||||
|
||||
public static IActivityBlueprintWrapper<TActivity> GetUnfilteredActivity<TActivity>(this IWorkflowBlueprintWrapper workflowBlueprintWrapper, string id) where TActivity : IActivity => workflowBlueprintWrapper.FindUnfilteredActivity<TActivity>(x => x.ActivityBlueprint.Id == id)!;
|
||||
}
|
||||
}
|
||||
|
|
@ -87,7 +87,7 @@ namespace Elsa.Triggers
|
|||
{
|
||||
var bookmarkResults = (await provider.GetBookmarksAsync(context, cancellationToken)).ToList();
|
||||
return bookmarkResults
|
||||
.Select(x => new WorkflowTrigger(workflowBlueprint, activityBlueprint.Id, activityBlueprint.Type, _bookmarkHasher.Hash(x.Bookmark), x.Bookmark))
|
||||
.Select(x => new WorkflowTrigger(workflowBlueprint, activityBlueprint.Id, x.ActivityTypeName ?? activityBlueprint.Type, _bookmarkHasher.Hash(x.Bookmark), x.Bookmark))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1118,6 +1118,12 @@ select {
|
|||
margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
|
||||
}
|
||||
|
||||
.elsa-space-y-6 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
|
||||
}
|
||||
|
||||
.elsa-space-y-8 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
|
|
@ -2544,6 +2550,14 @@ select {
|
|||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.elsa-grid-cols-4 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.elsa-col-span-4 {
|
||||
grid-column: span 4 / span 4;
|
||||
}
|
||||
|
||||
.elsa-transform {
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
|
|
@ -3212,6 +3226,10 @@ svg.jtk-connector.jtk-hover path {
|
|||
border-color: rgba(229, 231, 235, var(--tw-divide-opacity));
|
||||
}
|
||||
|
||||
.sm\:elsa-rounded-md {
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.sm\:elsa-rounded-lg {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
|
@ -4357,11 +4375,26 @@ svg.jtk-connector.jtk-hover path {
|
|||
max-width: 65ch;
|
||||
}
|
||||
|
||||
.lg\:elsa-py-0 {
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
.lg\:elsa-px-0 {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.lg\:elsa-px-8 {
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
.lg\:elsa-py-12 {
|
||||
padding-top: 3rem;
|
||||
padding-bottom: 3rem;
|
||||
}
|
||||
|
||||
.lg\:elsa-pl-2 {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
|
@ -4374,13 +4407,26 @@ svg.jtk-connector.jtk-hover path {
|
|||
gap: 2rem;
|
||||
}
|
||||
|
||||
.lg\:elsa-gap-x-5 {
|
||||
-moz-column-gap: 1.25rem;
|
||||
column-gap: 1.25rem;
|
||||
}
|
||||
|
||||
.lg\:elsa-grid-cols-12 {
|
||||
grid-template-columns: repeat(12, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.lg\:elsa-col-span-2 {
|
||||
grid-column: span 2 / span 2;
|
||||
}
|
||||
|
||||
.lg\:elsa-col-span-6 {
|
||||
grid-column: span 6 / span 6;
|
||||
}
|
||||
|
||||
.lg\:elsa-col-span-9 {
|
||||
grid-column: span 9 / span 9;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
|
|
|
|||
|
|
@ -1118,6 +1118,12 @@ select {
|
|||
margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
|
||||
}
|
||||
|
||||
.elsa-space-y-6 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
|
||||
}
|
||||
|
||||
.elsa-space-y-8 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-y-reverse: 0;
|
||||
margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse)));
|
||||
|
|
@ -2544,6 +2550,14 @@ select {
|
|||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.elsa-grid-cols-4 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.elsa-col-span-4 {
|
||||
grid-column: span 4 / span 4;
|
||||
}
|
||||
|
||||
.elsa-transform {
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
|
|
@ -3212,6 +3226,10 @@ svg.jtk-connector.jtk-hover path {
|
|||
border-color: rgba(229, 231, 235, var(--tw-divide-opacity));
|
||||
}
|
||||
|
||||
.sm\:elsa-rounded-md {
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.sm\:elsa-rounded-lg {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
|
@ -4357,11 +4375,26 @@ svg.jtk-connector.jtk-hover path {
|
|||
max-width: 65ch;
|
||||
}
|
||||
|
||||
.lg\:elsa-py-0 {
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
.lg\:elsa-px-0 {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.lg\:elsa-px-8 {
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
.lg\:elsa-py-12 {
|
||||
padding-top: 3rem;
|
||||
padding-bottom: 3rem;
|
||||
}
|
||||
|
||||
.lg\:elsa-pl-2 {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
|
@ -4374,13 +4407,26 @@ svg.jtk-connector.jtk-hover path {
|
|||
gap: 2rem;
|
||||
}
|
||||
|
||||
.lg\:elsa-gap-x-5 {
|
||||
-moz-column-gap: 1.25rem;
|
||||
column-gap: 1.25rem;
|
||||
}
|
||||
|
||||
.lg\:elsa-grid-cols-12 {
|
||||
grid-template-columns: repeat(12, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.lg\:elsa-col-span-2 {
|
||||
grid-column: span 2 / span 2;
|
||||
}
|
||||
|
||||
.lg\:elsa-col-span-6 {
|
||||
grid-column: span 6 / span 6;
|
||||
}
|
||||
|
||||
.lg\:elsa-col-span-9 {
|
||||
grid-column: span 9 / span 9;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue